@@ -23,7 +23,6 @@ import (
23
23
24
24
"github.com/kcp-dev/logicalcluster/v3"
25
25
26
- appsv1 "k8s.io/api/apps/v1"
27
26
corev1 "k8s.io/api/core/v1"
28
27
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29
28
"k8s.io/apimachinery/pkg/runtime"
@@ -38,7 +37,7 @@ import (
38
37
39
38
type ListSecretFunc func (clusterName logicalcluster.Name , namespace string ) ([]runtime.Object , error )
40
39
41
- type DeploymentMutator struct {
40
+ type PodSpecableMutator struct {
42
41
upstreamURL * url.URL
43
42
listSecrets ListSecretFunc
44
43
serviceLister listerscorev1.ServiceLister
@@ -49,18 +48,30 @@ type DeploymentMutator struct {
49
48
upsyncPods bool
50
49
}
51
50
52
- func (dm * DeploymentMutator ) GVR () schema.GroupVersionResource {
53
- return schema.GroupVersionResource {
54
- Group : "apps" ,
55
- Version : "v1" ,
56
- Resource : "deployments" ,
51
+ func (dm * PodSpecableMutator ) GVRs () []schema.GroupVersionResource {
52
+ return []schema.GroupVersionResource {
53
+ {
54
+ Group : "apps" ,
55
+ Version : "v1" ,
56
+ Resource : "deployments" ,
57
+ },
58
+ {
59
+ Group : "apps" ,
60
+ Version : "v1" ,
61
+ Resource : "statefulsets" ,
62
+ },
63
+ {
64
+ Group : "apps" ,
65
+ Version : "v1" ,
66
+ Resource : "replicasets" ,
67
+ },
57
68
}
58
69
}
59
70
60
- func NewDeploymentMutator (upstreamURL * url.URL , secretLister ListSecretFunc , serviceLister listerscorev1.ServiceLister ,
71
+ func NewPodspecableMutator (upstreamURL * url.URL , secretLister ListSecretFunc , serviceLister listerscorev1.ServiceLister ,
61
72
syncTargetClusterName logicalcluster.Name ,
62
- syncTargetUID types.UID , syncTargetName , dnsNamespace string , upsyncPods bool ) * DeploymentMutator {
63
- return & DeploymentMutator {
73
+ syncTargetUID types.UID , syncTargetName , dnsNamespace string , upsyncPods bool ) * PodSpecableMutator {
74
+ return & PodSpecableMutator {
64
75
upstreamURL : upstreamURL ,
65
76
listSecrets : secretLister ,
66
77
serviceLister : serviceLister ,
@@ -73,24 +84,30 @@ func NewDeploymentMutator(upstreamURL *url.URL, secretLister ListSecretFunc, ser
73
84
}
74
85
75
86
// Mutate applies the mutator changes to the object.
76
- func (dm * DeploymentMutator ) Mutate (obj * unstructured.Unstructured ) error {
77
- var deployment appsv1.Deployment
78
- err := runtime .DefaultUnstructuredConverter .FromUnstructured (
79
- obj .UnstructuredContent (),
80
- & deployment )
87
+ func (dm * PodSpecableMutator ) Mutate (obj * unstructured.Unstructured ) error {
88
+ podTemplateUnstr , ok , err := unstructured .NestedMap (obj .UnstructuredContent (), "spec" , "template" )
81
89
if err != nil {
82
90
return err
83
91
}
84
- upstreamLogicalName := logicalcluster .From (obj )
92
+ if ! ok {
93
+ return fmt .Errorf ("object should have a PodTemplate.Spec 'spec.template', but doesn't: %v" , obj )
94
+ }
85
95
86
- templateSpec := & deployment .Spec .Template .Spec
96
+ podTemplate := & corev1.PodTemplateSpec {}
97
+ err = runtime .DefaultUnstructuredConverter .FromUnstructured (
98
+ podTemplateUnstr ,
99
+ & podTemplate )
100
+ if err != nil {
101
+ return err
102
+ }
103
+ upstreamLogicalName := logicalcluster .From (obj )
87
104
88
105
desiredServiceAccountName := "default"
89
- if templateSpec . ServiceAccountName != "" && templateSpec .ServiceAccountName != "default" {
90
- desiredServiceAccountName = templateSpec .ServiceAccountName
106
+ if podTemplate . Spec . ServiceAccountName != "" && podTemplate . Spec .ServiceAccountName != "default" {
107
+ desiredServiceAccountName = podTemplate . Spec .ServiceAccountName
91
108
}
92
109
93
- rawSecretList , err := dm .listSecrets (upstreamLogicalName , deployment . Namespace )
110
+ rawSecretList , err := dm .listSecrets (upstreamLogicalName , obj . GetNamespace () )
94
111
if err != nil {
95
112
return fmt .Errorf ("error listing secrets for workspace %s: %w" , upstreamLogicalName .String (), err )
96
113
}
@@ -123,14 +140,14 @@ func (dm *DeploymentMutator) Mutate(obj *unstructured.Unstructured) error {
123
140
}
124
141
125
142
if desiredSecretName == "" {
126
- return fmt .Errorf ("couldn't find a token upstream for the serviceaccount %s/%s in workspace %s" , desiredServiceAccountName , deployment . Namespace , upstreamLogicalName .String ())
143
+ return fmt .Errorf ("couldn't find a token upstream for the serviceaccount %s/%s in workspace %s" , desiredServiceAccountName , obj . GetNamespace () , upstreamLogicalName .String ())
127
144
}
128
145
129
146
// Setting AutomountServiceAccountToken to false allow us to control the ServiceAccount
130
147
// VolumeMount and Volume definitions.
131
- templateSpec .AutomountServiceAccountToken = utilspointer .BoolPtr (false )
148
+ podTemplate . Spec .AutomountServiceAccountToken = utilspointer .BoolPtr (false )
132
149
// Set to empty the serviceAccountName on podTemplate as we are not syncing the serviceAccount down to the workload cluster.
133
- templateSpec .ServiceAccountName = ""
150
+ podTemplate . Spec .ServiceAccountName = ""
134
151
135
152
kcpExternalHost := dm .upstreamURL .Hostname ()
136
153
kcpExternalPort := dm .upstreamURL .Port ()
@@ -193,42 +210,42 @@ func (dm *DeploymentMutator) Mutate(obj *unstructured.Unstructured) error {
193
210
}
194
211
195
212
// Override Envs, resolve downwardAPI FieldRef and add the VolumeMount to all the containers
196
- for i := range deployment . Spec . Template .Spec .Containers {
213
+ for i := range podTemplate .Spec .Containers {
197
214
for _ , overrideEnv := range overrideEnvs {
198
- templateSpec . Containers [i ].Env = updateEnv (templateSpec .Containers [i ].Env , overrideEnv )
215
+ podTemplate . Spec . Containers [i ].Env = updateEnv (podTemplate . Spec .Containers [i ].Env , overrideEnv )
199
216
}
200
- templateSpec . Containers [i ].Env = resolveDownwardAPIFieldRefEnv (templateSpec . Containers [i ].Env , deployment )
201
- templateSpec . Containers [i ].VolumeMounts = updateVolumeMount (templateSpec .Containers [i ].VolumeMounts , serviceAccountMount )
217
+ podTemplate . Spec . Containers [i ].Env = resolveDownwardAPIFieldRefEnv (podTemplate . Spec . Containers [i ].Env , obj )
218
+ podTemplate . Spec . Containers [i ].VolumeMounts = updateVolumeMount (podTemplate . Spec .Containers [i ].VolumeMounts , serviceAccountMount )
202
219
}
203
220
204
221
// Override Envs, resolve downwardAPI FieldRef and add the VolumeMount to all the Init containers
205
- for i := range templateSpec .InitContainers {
222
+ for i := range podTemplate . Spec .InitContainers {
206
223
for _ , overrideEnv := range overrideEnvs {
207
- templateSpec . InitContainers [i ].Env = updateEnv (templateSpec .InitContainers [i ].Env , overrideEnv )
224
+ podTemplate . Spec . InitContainers [i ].Env = updateEnv (podTemplate . Spec .InitContainers [i ].Env , overrideEnv )
208
225
}
209
- templateSpec . InitContainers [i ].Env = resolveDownwardAPIFieldRefEnv (templateSpec . InitContainers [i ].Env , deployment )
210
- templateSpec . InitContainers [i ].VolumeMounts = updateVolumeMount (templateSpec .InitContainers [i ].VolumeMounts , serviceAccountMount )
226
+ podTemplate . Spec . InitContainers [i ].Env = resolveDownwardAPIFieldRefEnv (podTemplate . Spec . InitContainers [i ].Env , obj )
227
+ podTemplate . Spec . InitContainers [i ].VolumeMounts = updateVolumeMount (podTemplate . Spec .InitContainers [i ].VolumeMounts , serviceAccountMount )
211
228
}
212
229
213
230
// Override Envs, resolve downwardAPI FieldRef and add the VolumeMount to all the Ephemeral containers
214
- for i := range templateSpec .EphemeralContainers {
231
+ for i := range podTemplate . Spec .EphemeralContainers {
215
232
for _ , overrideEnv := range overrideEnvs {
216
- templateSpec . EphemeralContainers [i ].Env = updateEnv (templateSpec .EphemeralContainers [i ].Env , overrideEnv )
233
+ podTemplate . Spec . EphemeralContainers [i ].Env = updateEnv (podTemplate . Spec .EphemeralContainers [i ].Env , overrideEnv )
217
234
}
218
- templateSpec . EphemeralContainers [i ].Env = resolveDownwardAPIFieldRefEnv (templateSpec . EphemeralContainers [i ].Env , deployment )
219
- templateSpec . EphemeralContainers [i ].VolumeMounts = updateVolumeMount (templateSpec .EphemeralContainers [i ].VolumeMounts , serviceAccountMount )
235
+ podTemplate . Spec . EphemeralContainers [i ].Env = resolveDownwardAPIFieldRefEnv (podTemplate . Spec . EphemeralContainers [i ].Env , obj )
236
+ podTemplate . Spec . EphemeralContainers [i ].VolumeMounts = updateVolumeMount (podTemplate . Spec .EphemeralContainers [i ].VolumeMounts , serviceAccountMount )
220
237
}
221
238
222
239
// Add the ServiceAccount volume with our overrides.
223
240
found := false
224
- for i := range templateSpec .Volumes {
225
- if templateSpec .Volumes [i ].Name == "kcp-api-access" {
226
- templateSpec .Volumes [i ] = serviceAccountVolume
241
+ for i := range podTemplate . Spec .Volumes {
242
+ if podTemplate . Spec .Volumes [i ].Name == "kcp-api-access" {
243
+ podTemplate . Spec .Volumes [i ] = serviceAccountVolume
227
244
found = true
228
245
}
229
246
}
230
247
if ! found {
231
- templateSpec . Volumes = append (templateSpec .Volumes , serviceAccountVolume )
248
+ podTemplate . Spec . Volumes = append (podTemplate . Spec .Volumes , serviceAccountVolume )
232
249
}
233
250
234
251
// Overrides DNS to point to the workspace DNS
@@ -238,8 +255,8 @@ func (dm *DeploymentMutator) Mutate(obj *unstructured.Unstructured) error {
238
255
return err // retry
239
256
}
240
257
241
- deployment . Spec . Template .Spec .DNSPolicy = corev1 .DNSNone
242
- deployment . Spec . Template .Spec .DNSConfig = & corev1.PodDNSConfig {
258
+ podTemplate .Spec .DNSPolicy = corev1 .DNSNone
259
+ podTemplate .Spec .DNSConfig = & corev1.PodDNSConfig {
243
260
Nameservers : []string {dnsIP },
244
261
Searches : []string { // TODO(LV): from /etc/resolv.conf
245
262
obj .GetNamespace () + ".svc.cluster.local" ,
@@ -256,31 +273,29 @@ func (dm *DeploymentMutator) Mutate(obj *unstructured.Unstructured) error {
256
273
257
274
if dm .upsyncPods {
258
275
syncTargetKey := workloadv1alpha1 .ToSyncTargetKey (dm .syncTargetClusterName , dm .syncTargetName )
259
- labels := deployment . Spec . Template .Labels
276
+ labels := podTemplate .Labels
260
277
if labels == nil {
261
278
labels = map [string ]string {}
262
279
}
263
280
labels [workloadv1alpha1 .ClusterResourceStateLabelPrefix + syncTargetKey ] = string (workloadv1alpha1 .ResourceStateUpsync )
264
281
labels [workloadv1alpha1 .InternalDownstreamClusterLabel ] = syncTargetKey
265
- deployment . Spec . Template .Labels = labels
282
+ podTemplate .Labels = labels
266
283
267
284
// TODO(davidfestal): In the future we could add a diff annotation to transform the resource while upsyncing:
268
285
// - remove unnecessary fields we don't want leaking to upstream
269
286
// - add an owner reference to the upstream deployment
270
287
}
271
288
272
- unstructured , err := runtime .DefaultUnstructuredConverter .ToUnstructured (& deployment )
289
+ newPodTemplateUnstr , err := runtime .DefaultUnstructuredConverter .ToUnstructured (podTemplate )
273
290
if err != nil {
274
291
return err
275
292
}
276
293
277
294
// Set the changes back into the obj.
278
- obj .SetUnstructuredContent (unstructured )
279
-
280
- return nil
295
+ return unstructured .SetNestedMap (obj .Object , newPodTemplateUnstr , "spec" , "template" )
281
296
}
282
297
283
- func (dm * DeploymentMutator ) getDNSIPForWorkspace (workspace logicalcluster.Name ) (string , error ) {
298
+ func (dm * PodSpecableMutator ) getDNSIPForWorkspace (workspace logicalcluster.Name ) (string , error ) {
284
299
// Retrieve the DNS IP associated to the workspace
285
300
dnsServiceName := shared .GetDNSID (workspace , dm .syncTargetUID , dm .syncTargetName )
286
301
@@ -299,13 +314,13 @@ func (dm *DeploymentMutator) getDNSIPForWorkspace(workspace logicalcluster.Name)
299
314
}
300
315
301
316
// resolveDownwardAPIFieldRefEnv replaces the downwardAPI FieldRef EnvVars with the value from the deployment, right now it only replaces the metadata.namespace.
302
- func resolveDownwardAPIFieldRefEnv (envs []corev1.EnvVar , deployment appsv1. Deployment ) []corev1.EnvVar {
317
+ func resolveDownwardAPIFieldRefEnv (envs []corev1.EnvVar , podspecable * unstructured. Unstructured ) []corev1.EnvVar {
303
318
var result []corev1.EnvVar
304
319
for _ , env := range envs {
305
320
if env .ValueFrom != nil && env .ValueFrom .FieldRef != nil && env .ValueFrom .FieldRef .FieldPath == "metadata.namespace" {
306
321
result = append (result , corev1.EnvVar {
307
322
Name : env .Name ,
308
- Value : deployment . Namespace ,
323
+ Value : podspecable . GetNamespace () ,
309
324
})
310
325
} else {
311
326
result = append (result , env )
0 commit comments