Skip to content
This repository was archived by the owner on Jul 30, 2021. It is now read-only.

Commit 04d3b92

Browse files
authored
Add recovery support for ConfigMaps. (#497)
1 parent 6f39a81 commit 04d3b92

File tree

3 files changed

+114
-41
lines changed

3 files changed

+114
-41
lines changed

pkg/recovery/etcd.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,18 @@ func (s *etcdBackend) read(ctx context.Context) (*controlPlane, error) {
3636
cp := &controlPlane{}
3737
for _, r := range []struct {
3838
etcdKeyName string
39-
yamlName string
4039
obj runtime.Object
4140
}{{
4241
etcdKeyName: "configmaps",
43-
yamlName: "config-map",
4442
obj: &cp.configMaps,
4543
}, {
4644
etcdKeyName: "daemonsets",
47-
yamlName: "daemonset",
4845
obj: &cp.daemonSets,
4946
}, {
5047
etcdKeyName: "deployments",
51-
yamlName: "deployment",
5248
obj: &cp.deployments,
5349
}, {
5450
etcdKeyName: "secrets",
55-
yamlName: "secret",
5651
obj: &cp.secrets,
5752
}} {
5853
if err := s.list(ctx, r.etcdKeyName, r.obj); err != nil {

pkg/recovery/recover.go

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,17 @@ func (cp *controlPlane) renderBootstrap() (asset.Assets, error) {
111111
if err != nil {
112112
return nil, err
113113
}
114-
requiredSecrets, err := fixUpBootstrapPods(pods)
114+
requiredConfigMaps, requiredSecrets := fixUpBootstrapPods(pods)
115+
as, err := outputBootstrapPods(pods)
115116
if err != nil {
116117
return nil, err
117118
}
118-
as, err := outputBootstrapPods(pods)
119+
configMaps, err := outputBootstrapConfigMaps(cp.configMaps, requiredConfigMaps)
119120
if err != nil {
120121
return nil, err
121122
}
122-
secrets, err := outputBootstrapSecrets(cp.secrets.Items, requiredSecrets)
123+
as = append(as, configMaps...)
124+
secrets, err := outputBootstrapSecrets(cp.secrets, requiredSecrets)
123125
if err != nil {
124126
return nil, err
125127
}
@@ -176,23 +178,27 @@ func setBootstrapPodMetadata(pod *v1.Pod, parent metav1.ObjectMeta) error {
176178
return nil
177179
}
178180

179-
// fixUpBootstrapPods modifies extracted bootstrap pod specs to have correct metadata and
180-
// point to filesystem-mount-based secrets. It returns a set of secrets that must also be rendered
181-
// in order for the bootstrap pods to be functional.
182-
// TODO(diegs): also output the set of reqiured configMaps.
183-
func fixUpBootstrapPods(pods []v1.Pod) (map[string]struct{}, error) {
184-
requiredSecrets := make(map[string]struct{})
181+
// fixUpBootstrapPods modifies extracted bootstrap pod specs to have correct metadata and point to
182+
// filesystem-mount-based secrets. It returns mappings from configMap and secret names to output
183+
// paths that must also be rendered in order for the bootstrap pods to be functional.
184+
func fixUpBootstrapPods(pods []v1.Pod) (requiredConfigMaps, requiredSecrets map[string]string) {
185+
requiredConfigMaps, requiredSecrets = make(map[string]string), make(map[string]string)
185186
for i := range pods {
186187
pod := &pods[i]
187188

188189
// Change secret volumes to point to file mounts.
189190
for i := range pod.Spec.Volumes {
190191
vol := &pod.Spec.Volumes[i]
191192
if vol.Secret != nil {
192-
requiredSecrets[vol.Secret.SecretName] = struct{}{}
193-
secretPath := path.Join(asset.BootstrapSecretsDir, vol.Secret.SecretName)
194-
vol.HostPath = &v1.HostPathVolumeSource{Path: secretPath}
193+
pathPrefix := path.Join(asset.BootstrapSecretsDir, "secrets", vol.Secret.SecretName)
194+
requiredSecrets[vol.Secret.SecretName] = pathPrefix
195+
vol.HostPath = &v1.HostPathVolumeSource{Path: pathPrefix}
195196
vol.Secret = nil
197+
} else if vol.ConfigMap != nil {
198+
pathPrefix := path.Join(asset.BootstrapSecretsDir, "config-maps", vol.ConfigMap.Name)
199+
requiredConfigMaps[vol.ConfigMap.Name] = pathPrefix
200+
vol.HostPath = &v1.HostPathVolumeSource{Path: pathPrefix}
201+
vol.ConfigMap = nil
196202
}
197203
}
198204

@@ -216,7 +222,7 @@ func fixUpBootstrapPods(pods []v1.Pod) (map[string]struct{}, error) {
216222
Name: "kubeconfig",
217223
})
218224
}
219-
return requiredSecrets, nil
225+
return
220226
}
221227

222228
// outputBootstrapPods outputs the bootstrap pod definitions.
@@ -232,27 +238,62 @@ func outputBootstrapPods(pods []v1.Pod) (asset.Assets, error) {
232238
return as, nil
233239
}
234240

241+
// outputBootstrapConfigMaps creates assets for all the configMap names in the requiredConfigMaps
242+
// set. It returns an error if any configMap cannot be found in the provided configMaps list.
243+
func outputBootstrapConfigMaps(configMaps v1.ConfigMapList, requiredConfigMaps map[string]string) (asset.Assets, error) {
244+
return outputKeyValueData(&configMaps, requiredConfigMaps, func(obj runtime.Object) map[string][]byte {
245+
configMap, ok := obj.(*v1.ConfigMap)
246+
if !ok || configMap == nil {
247+
return nil
248+
}
249+
output := make(map[string][]byte)
250+
for k, v := range configMap.Data {
251+
output[k] = []byte(v)
252+
}
253+
return output
254+
})
255+
}
256+
235257
// outputBootstrapSecrets creates assets for all the secret names in the requiredSecrets set. It
236258
// returns an error if any secret cannot be found in the provided secrets list.
237-
func outputBootstrapSecrets(secrets []v1.Secret, requiredSecrets map[string]struct{}) (asset.Assets, error) {
259+
func outputBootstrapSecrets(secrets v1.SecretList, requiredSecrets map[string]string) (asset.Assets, error) {
260+
return outputKeyValueData(&secrets, requiredSecrets, func(obj runtime.Object) map[string][]byte {
261+
if secret, ok := obj.(*v1.Secret); ok && secret != nil {
262+
return secret.Data
263+
}
264+
return nil
265+
})
266+
}
267+
268+
// outputKeyValueData takes a key-value object (such as a Secret or ConfigMap) and outputs assets
269+
// for each key-value pair. See outputBootstrapConfigMaps or outputBootstrapSecrets for usage.
270+
func outputKeyValueData(objList runtime.Object, requiredObjs map[string]string, extractData func(runtime.Object) map[string][]byte) (asset.Assets, error) {
238271
var as asset.Assets
239-
for _, secret := range secrets {
240-
if _, ok := requiredSecrets[secret.Name]; ok {
241-
for key, data := range secret.Data {
272+
objs, err := meta.ExtractList(objList)
273+
if err != nil {
274+
return nil, err
275+
}
276+
for _, obj := range objs {
277+
name, err := metaAccessor.Name(obj)
278+
if err != nil {
279+
return nil, err
280+
}
281+
if namePrefix, ok := requiredObjs[name]; ok {
282+
for key, data := range extractData(obj) {
242283
as = append(as, asset.Asset{
243-
Name: path.Join(asset.AssetPathSecrets, secret.Name, key),
284+
Name: path.Join(namePrefix, key),
244285
Data: data,
245286
})
246287
}
247-
delete(requiredSecrets, secret.Name)
288+
delete(requiredObjs, name)
248289
}
249290
}
250-
if len(requiredSecrets) > 0 {
251-
var missingSecrets []string
252-
for secret := range requiredSecrets {
253-
missingSecrets = append(missingSecrets, secret)
291+
if len(requiredObjs) > 0 {
292+
var missingObjs []string
293+
for obj := range requiredObjs {
294+
missingObjs = append(missingObjs, obj)
254295
}
255-
return nil, fmt.Errorf("failed to extract some required bootstrap secrets: %v", missingSecrets)
296+
return nil, fmt.Errorf("failed to extract some required objects: %v", missingObjs)
256297
}
257298
return as, nil
258299
}

pkg/recovery/recover_test.go

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@ var (
1616
secretData = []byte("this is very secret")
1717

1818
cp = &controlPlane{
19-
configMaps: v1.ConfigMapList{},
19+
configMaps: v1.ConfigMapList{
20+
Items: []v1.ConfigMap{{
21+
ObjectMeta: metav1.ObjectMeta{
22+
Name: "kube-apiserver",
23+
Namespace: "kube-system",
24+
},
25+
Data: map[string]string{"key": "value"},
26+
}},
27+
},
2028
daemonSets: v1beta1.DaemonSetList{
2129
Items: []v1beta1.DaemonSet{{
2230
ObjectMeta: metav1.ObjectMeta{
@@ -168,6 +176,10 @@ func TestFixUpBootstrapPods(t *testing.T) {
168176
Name: "ssl-certs-host",
169177
MountPath: "/etc/ssl/certs",
170178
ReadOnly: true,
179+
}, {
180+
Name: "configs",
181+
MountPath: "/etc/kubernetes/config-maps",
182+
ReadOnly: true,
171183
}, {
172184
Name: "secrets",
173185
MountPath: "/etc/kubernetes/secrets",
@@ -177,6 +189,9 @@ func TestFixUpBootstrapPods(t *testing.T) {
177189
Volumes: []v1.Volume{{
178190
Name: "ssl-certs-host",
179191
VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/usr/share/ca-certificates"}},
192+
}, {
193+
Name: "configs",
194+
VolumeSource: v1.VolumeSource{ConfigMap: &v1.ConfigMapVolumeSource{LocalObjectReference: v1.LocalObjectReference{Name: "kube-apiserver"}}},
180195
}, {
181196
Name: "secrets",
182197
VolumeSource: v1.VolumeSource{Secret: &v1.SecretVolumeSource{SecretName: "kube-apiserver"}},
@@ -217,6 +232,10 @@ func TestFixUpBootstrapPods(t *testing.T) {
217232
Name: "ssl-certs-host",
218233
MountPath: "/etc/ssl/certs",
219234
ReadOnly: true,
235+
}, {
236+
Name: "configs",
237+
MountPath: "/etc/kubernetes/config-maps",
238+
ReadOnly: true,
220239
}, {
221240
Name: "secrets",
222241
MountPath: "/etc/kubernetes/secrets",
@@ -226,9 +245,12 @@ func TestFixUpBootstrapPods(t *testing.T) {
226245
Volumes: []v1.Volume{{
227246
Name: "ssl-certs-host",
228247
VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/usr/share/ca-certificates"}},
248+
}, {
249+
Name: "configs",
250+
VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/etc/kubernetes/bootstrap-secrets/config-maps/kube-apiserver"}},
229251
}, {
230252
Name: "secrets",
231-
VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/etc/kubernetes/bootstrap-secrets/kube-apiserver"}},
253+
VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/etc/kubernetes/bootstrap-secrets/secrets/kube-apiserver"}},
232254
}, {
233255
Name: "kubeconfig",
234256
VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/etc/kubernetes/kubeconfig"}},
@@ -260,32 +282,47 @@ func TestFixUpBootstrapPods(t *testing.T) {
260282
}},
261283
},
262284
}}
263-
wantSecrets := map[string]struct{}{"kube-apiserver": {}}
264-
gotSecrets, err := fixUpBootstrapPods(pods)
265-
if err != nil || !reflect.DeepEqual(gotSecrets, wantSecrets) {
266-
t.Errorf("fixUpBootstrapPods(%v) = %v, %v, want: %v, %v", pods, gotSecrets, err, wantSecrets, nil)
285+
wantConfigMaps := map[string]string{"kube-apiserver": "/etc/kubernetes/bootstrap-secrets/config-maps/kube-apiserver"}
286+
wantSecrets := map[string]string{"kube-apiserver": "/etc/kubernetes/bootstrap-secrets/secrets/kube-apiserver"}
287+
gotConfigMaps, gotSecrets := fixUpBootstrapPods(pods)
288+
if !reflect.DeepEqual(gotSecrets, wantSecrets) || !reflect.DeepEqual(gotConfigMaps, wantConfigMaps) {
289+
t.Errorf("fixUpBootstrapPods(%v) = %v, %v, want: %v, %v", pods, gotConfigMaps, gotSecrets, wantConfigMaps, wantSecrets)
267290
} else if !reflect.DeepEqual(pods, wantPods) {
268291
t.Errorf("fixUpBootstrapPods(%v) = %v, want: %v", pods, pods, wantPods)
269292
}
270293
}
271294

295+
func TestOutputConfigMaps(t *testing.T) {
296+
requiredSecrets := map[string]string{"kube-apiserver": "tls/kube-apiserver"}
297+
want := asset.Assets{{
298+
Name: "tls/kube-apiserver/apiserver.crt",
299+
Data: secretData,
300+
}}
301+
if got, err := outputBootstrapSecrets(cp.secrets, requiredSecrets); err != nil {
302+
t.Errorf("outputBootstrapSecrets(%v, %v) = %v, want: nil", cp.secrets.Items, requiredSecrets, err)
303+
} else if !reflect.DeepEqual(got, want) {
304+
t.Errorf("outputBootstrapSecrets(%v, %v) = %v, want: %v", cp.secrets.Items, requiredSecrets, got, want)
305+
}
306+
}
307+
272308
func TestOutputBootstrapSecrets(t *testing.T) {
273-
requiredSecrets := map[string]struct{}{"kube-apiserver": {}}
309+
requiredSecrets := map[string]string{"kube-apiserver": "tls/kube-apiserver"}
274310
want := asset.Assets{{
275311
Name: "tls/kube-apiserver/apiserver.crt",
276312
Data: secretData,
277313
}}
278-
if got, err := outputBootstrapSecrets(cp.secrets.Items, requiredSecrets); err != nil {
314+
if got, err := outputBootstrapSecrets(cp.secrets, requiredSecrets); err != nil {
279315
t.Errorf("outputBootstrapSecrets(%v, %v) = %v, want: nil", cp.secrets.Items, requiredSecrets, err)
280316
} else if !reflect.DeepEqual(got, want) {
281317
t.Errorf("outputBootstrapSecrets(%v, %v) = %v, want: %v", cp.secrets.Items, requiredSecrets, got, want)
282318
}
283319
}
284320

285-
func TestOutputBootstrapSecretsMissing(t *testing.T) {
286-
requiredSecrets := map[string]struct{}{"missing-secret": {}}
287-
if as, err := outputBootstrapSecrets(cp.secrets.Items, requiredSecrets); err == nil {
288-
t.Errorf("outputBootstrapSecrets(%v, %v) = %v, %v, want: nil, non-nil", cp.secrets.Items, requiredSecrets, as, err)
321+
func TestOutputKeyValueDataKeyMissing(t *testing.T) {
322+
objList := &v1.SecretList{}
323+
requiredObjs := map[string]string{"missing-key": "some-path"}
324+
if as, err := outputKeyValueData(objList, requiredObjs, func(obj runtime.Object) map[string][]byte { return obj.(*v1.Secret).Data }); err == nil {
325+
t.Errorf("outputKeyValueData(%v, %v) = %v, %v, want: nil, non-nil", objList, requiredObjs, as, err)
289326
}
290327
}
291328

0 commit comments

Comments
 (0)