-
Notifications
You must be signed in to change notification settings - Fork 560
Support envfrom #3191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support envfrom #3191
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -62,6 +62,81 @@ func mergeEnvVars(containerEnvVars []corev1.EnvVar, newEnvVars []corev1.EnvVar) | |||||
return merged | ||||||
} | ||||||
|
||||||
// InjectEnvFromIntoDeployment injects the envFrom variables | ||||||
// into the container(s) of the given PodSpec. | ||||||
// | ||||||
// If any Container in PodSpec already defines an envFrom variable | ||||||
// as any of the provided envFrom then it will be overwritten. | ||||||
func InjectEnvFromIntoDeployment(podSpec *corev1.PodSpec, envFromVars []corev1.EnvFromSource) error { | ||||||
if podSpec == nil { | ||||||
return errors.New("no pod spec provided") | ||||||
} | ||||||
|
||||||
for i := range podSpec.Containers { | ||||||
container := &podSpec.Containers[i] | ||||||
container.EnvFrom = mergeEnvFromVars(container.EnvFrom, envFromVars) | ||||||
} | ||||||
|
||||||
return nil | ||||||
} | ||||||
|
||||||
func mergeEnvFromVars(containerEnvFromVars []corev1.EnvFromSource, newEnvFromVars []corev1.EnvFromSource) (merged []corev1.EnvFromSource) { | ||||||
merged = containerEnvFromVars | ||||||
|
||||||
for _, newEnvFromVar := range newEnvFromVars { | ||||||
found := findEnvFromVar(containerEnvFromVars, newEnvFromVar) | ||||||
if found { | ||||||
continue | ||||||
} | ||||||
|
||||||
merged = append(merged, newEnvFromVar) | ||||||
} | ||||||
|
||||||
return | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: If you're going to use a named return, do you really need the |
||||||
} | ||||||
|
||||||
func findEnvFromVar(EnvFromVar []corev1.EnvFromSource, newEnvFromVar corev1.EnvFromSource) (found bool) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Looks like some mixed case variables here. for consistency:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also Nit: named returns; there is a single |
||||||
for i := range EnvFromVar { | ||||||
if compareEnvFromVar(EnvFromVar[i], newEnvFromVar) { | ||||||
found = true | ||||||
break | ||||||
} | ||||||
} | ||||||
|
||||||
return | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: If you're going to use a named return, do you really need the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please no named returns! |
||||||
} | ||||||
|
||||||
func compareEnvFromVar(envFromVar corev1.EnvFromSource, newEnvFromVar corev1.EnvFromSource) (found bool) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Named return, but the return name is never assigned. The return value is explicitly specified. |
||||||
compareprefix := newEnvFromVar.Prefix == envFromVar.Prefix | ||||||
var compareConfigMap, compareSecret bool | ||||||
|
||||||
// Compare ConfigMapRef | ||||||
if newEnvFromVar.ConfigMapRef == nil && envFromVar.ConfigMapRef == nil { | ||||||
compareConfigMap = true | ||||||
} else if newEnvFromVar.ConfigMapRef != nil && envFromVar.ConfigMapRef != nil { | ||||||
if newEnvFromVar.ConfigMapRef.Optional == nil && envFromVar.ConfigMapRef.Optional == nil { | ||||||
compareConfigMap = newEnvFromVar.ConfigMapRef.LocalObjectReference == envFromVar.ConfigMapRef.LocalObjectReference | ||||||
} else if newEnvFromVar.ConfigMapRef.Optional != nil && envFromVar.ConfigMapRef.Optional != nil { | ||||||
compareConfigMap = newEnvFromVar.ConfigMapRef.LocalObjectReference == envFromVar.ConfigMapRef.LocalObjectReference && *newEnvFromVar.ConfigMapRef.Optional == *envFromVar.ConfigMapRef.Optional | ||||||
} else { | ||||||
compareConfigMap = false | ||||||
} | ||||||
Comment on lines
+121
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not strictly necessary, |
||||||
} | ||||||
Comment on lines
+114
to
+124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block of conditionals looks pretty complicated and is a bit difficult to read. I wonder if something like this might be a bit easier to grok: if newEnvFromVar.ConfigMapRef == nil && envFromVar.ConfigMapRef == nil {
compareConfigMap = true
} else if newEnvFromVar.ConfigMapRef != nil && envFromVar.ConfigMapRef != nil {
compareConfigMap = newEnvFromVar.ConfigMapRef.LocalObjectReference == envFromVar.ConfigMapRef.LocalObjectReference
if newEnvFromVar.ConfigMapRef.Optional != nil && envFromVar.ConfigMapRef.Optional != nil {
compareConfigMap = compareConfigMap && *newEnvFromVar.ConfigMapRef.Optional == *envFromVar.ConfigMapRef.Optional
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this does the same thing but reduces the overall number of conditionals and is IMO a bit easier to read. I am also willing to concede that the GH PR review UI is making this harder to read than it normally would be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another thing that could be done, if constant time checks are not necessary, is to exit early from the function on any false condition. Right away, I could see this:
etc. |
||||||
// Compare SecretRef | ||||||
if newEnvFromVar.SecretRef == nil && envFromVar.SecretRef == nil { | ||||||
compareSecret = true | ||||||
} else if newEnvFromVar.SecretRef != nil && envFromVar.SecretRef != nil { | ||||||
if newEnvFromVar.SecretRef.Optional == nil && envFromVar.SecretRef.Optional == nil { | ||||||
compareSecret = newEnvFromVar.SecretRef.LocalObjectReference == envFromVar.SecretRef.LocalObjectReference | ||||||
} else if newEnvFromVar.SecretRef.Optional != nil && envFromVar.SecretRef.Optional != nil { | ||||||
compareSecret = newEnvFromVar.SecretRef.LocalObjectReference == envFromVar.SecretRef.LocalObjectReference && *newEnvFromVar.SecretRef.Optional == *envFromVar.ConfigMapRef.Optional | ||||||
} else { | ||||||
compareSecret = false | ||||||
} | ||||||
Comment on lines
+133
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not strictly necessary, |
||||||
} | ||||||
return compareprefix && compareConfigMap && compareSecret | ||||||
} | ||||||
|
||||||
// InjectVolumesIntoDeployment injects the provided Volumes | ||||||
// into the container(s) of the given PodSpec. | ||||||
// | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -30,6 +30,26 @@ var ( | |||||||||||
}, | ||||||||||||
} | ||||||||||||
|
||||||||||||
defaultEnvFromVars = []corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "test", | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
ConfigMapRef: &corev1.ConfigMapEnvSource{ | ||||||||||||
LocalObjectReference: corev1.LocalObjectReference{ | ||||||||||||
Name: "configmapForTest", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
SecretRef: &corev1.SecretEnvSource{ | ||||||||||||
LocalObjectReference: corev1.LocalObjectReference{ | ||||||||||||
Name: "secretForTest", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
} | ||||||||||||
|
||||||||||||
defaultVolumeMounts = []corev1.VolumeMount{ | ||||||||||||
{ | ||||||||||||
Name: "foo", | ||||||||||||
|
@@ -526,6 +546,175 @@ func TestInjectEnvIntoDeployment(t *testing.T) { | |||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
func TestInjectEnvFromIntoDeployment(t *testing.T) { | ||||||||||||
tests := []struct { | ||||||||||||
name string | ||||||||||||
podSpec *corev1.PodSpec | ||||||||||||
envFromVar []corev1.EnvFromSource | ||||||||||||
expected *corev1.PodSpec | ||||||||||||
}{ | ||||||||||||
{ | ||||||||||||
// PodSpec has one container and `EnvFrom` is empty. | ||||||||||||
// Expected: All env variable(s) specified are injected. | ||||||||||||
name: "WithContainerHasNoEnvFromVar", | ||||||||||||
podSpec: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
envFromVar: defaultEnvFromVars, | ||||||||||||
expected: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{ | ||||||||||||
EnvFrom: defaultEnvFromVars, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
// PodSpec has one container and it has overlapping envFrom var(s). | ||||||||||||
// Expected: existing duplicate env vars won't be appended in the envFrom. | ||||||||||||
name: "WithContainerHasOverlappingEnvFromVar", | ||||||||||||
podSpec: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{ | ||||||||||||
EnvFrom: []corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "test", | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
ConfigMapRef: &corev1.ConfigMapEnvSource{ | ||||||||||||
LocalObjectReference: corev1.LocalObjectReference{ | ||||||||||||
Name: "configmapForTest", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
SecretRef: &corev1.SecretEnvSource{ | ||||||||||||
LocalObjectReference: corev1.LocalObjectReference{ | ||||||||||||
Name: "secretForTest", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
envFromVar: defaultEnvFromVars, | ||||||||||||
expected: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{ | ||||||||||||
EnvFrom: []corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "test", | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
ConfigMapRef: &corev1.ConfigMapEnvSource{ | ||||||||||||
LocalObjectReference: corev1.LocalObjectReference{ | ||||||||||||
Name: "configmapForTest", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
SecretRef: &corev1.SecretEnvSource{ | ||||||||||||
LocalObjectReference: corev1.LocalObjectReference{ | ||||||||||||
Name: "secretForTest", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
// PodSpec has one container and it has non overlapping envFrom var(s). | ||||||||||||
// Expected: existing non overlapping env vars are intact. | ||||||||||||
name: "WithContainerHasNonOverlappingEnvFromVar", | ||||||||||||
podSpec: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{ | ||||||||||||
EnvFrom: []corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "foo", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
envFromVar: defaultEnvFromVars, | ||||||||||||
expected: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{ | ||||||||||||
EnvFrom: append([]corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "foo", | ||||||||||||
}, | ||||||||||||
}, defaultEnvFromVars...), | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
// PodSpec has more than one container(s) | ||||||||||||
// Expected: All container(s) should be updated as expected. | ||||||||||||
name: "WithMultipleContainers", | ||||||||||||
podSpec: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{}, | ||||||||||||
{ | ||||||||||||
EnvFrom: []corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "foo", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
EnvFrom: []corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "bar", | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
envFromVar: defaultEnvFromVars, | ||||||||||||
expected: &corev1.PodSpec{ | ||||||||||||
Containers: []corev1.Container{ | ||||||||||||
{ | ||||||||||||
EnvFrom: defaultEnvFromVars, | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
EnvFrom: append([]corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "foo", | ||||||||||||
}, | ||||||||||||
}, defaultEnvFromVars...), | ||||||||||||
}, | ||||||||||||
{ | ||||||||||||
EnvFrom: append([]corev1.EnvFromSource{ | ||||||||||||
{ | ||||||||||||
Prefix: "bar", | ||||||||||||
}, | ||||||||||||
}, defaultEnvFromVars...), | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
} | ||||||||||||
|
||||||||||||
for _, tt := range tests { | ||||||||||||
t.Run(tt.name, func(t *testing.T) { | ||||||||||||
inject.InjectEnvFromIntoDeployment(tt.podSpec, tt.envFromVar) | ||||||||||||
|
||||||||||||
podSpecWant := tt.expected | ||||||||||||
podSpecGot := tt.podSpec | ||||||||||||
|
||||||||||||
assert.Equal(t, podSpecWant, podSpecGot) | ||||||||||||
Comment on lines
+710
to
+713
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't need the temporary variables. |
||||||||||||
}) | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
func TestInjectTolerationsIntoDeployment(t *testing.T) { | ||||||||||||
tests := []struct { | ||||||||||||
name string | ||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: named returns. There is a single
return
statement here, and it could easily be changed toreturn merged