Skip to content

Commit 5795ba5

Browse files
committed
fix: do not assume container 0 is the function container
Add a utility method for correctly finding the index of the function Container inside a Deployment spec. Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
1 parent bdff0c4 commit 5795ba5

File tree

3 files changed

+61
-17
lines changed

3 files changed

+61
-17
lines changed

pkg/handlers/update.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,19 @@ func updateDeploymentSpec(
9191
}
9292

9393
if len(deployment.Spec.Template.Spec.Containers) > 0 {
94-
deployment.Spec.Template.Spec.Containers[0].Image = request.Image
94+
idx, _ := k8s.FunctionContainer(*deployment)
95+
if idx < 0 {
96+
return fmt.Errorf("deployment is not a valid function spec"), http.StatusBadRequest
97+
}
98+
99+
deployment.Spec.Template.Spec.Containers[idx].Image = request.Image
95100

96101
// Disabling update support to prevent unexpected mutations of deployed functions,
97102
// since imagePullPolicy is now configurable. This could be reconsidered later depending
98103
// on desired behavior, but will need to be updated to take config.
99-
//deployment.Spec.Template.Spec.Containers[0].ImagePullPolicy = v1.PullAlways
104+
//deployment.Spec.Template.Spec.Containers[idx].ImagePullPolicy = v1.PullAlways
100105

101-
deployment.Spec.Template.Spec.Containers[0].Env = buildEnvVars(&request)
106+
deployment.Spec.Template.Spec.Containers[idx].Env = buildEnvVars(&request)
102107

103108
factory.ConfigureReadOnlyRootFilesystem(request, deployment)
104109
factory.ConfigureContainerUserID(deployment)
@@ -135,7 +140,7 @@ func updateDeploymentSpec(
135140
return resourceErr, http.StatusBadRequest
136141
}
137142

138-
deployment.Spec.Template.Spec.Containers[0].Resources = *resources
143+
deployment.Spec.Template.Spec.Containers[idx].Resources = *resources
139144

140145
secrets := k8s.NewSecretsClient(factory.Client)
141146
existingSecrets, err := secrets.GetSecrets(functionNamespace, request.Secrets)
@@ -154,8 +159,8 @@ func updateDeploymentSpec(
154159
return err, http.StatusBadRequest
155160
}
156161

157-
deployment.Spec.Template.Spec.Containers[0].LivenessProbe = probes.Liveness
158-
deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = probes.Readiness
162+
deployment.Spec.Template.Spec.Containers[idx].LivenessProbe = probes.Liveness
163+
deployment.Spec.Template.Spec.Containers[idx].ReadinessProbe = probes.Readiness
159164

160165
// compare the annotations from args to the cache copy of the deployment annotations
161166
// at this point we have already updated the annotations to the new value, if we

pkg/k8s/function_status.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package k8s
66
import (
77
types "github.com/openfaas/faas-provider/types"
88
appsv1 "k8s.io/api/apps/v1"
9+
corev1 "k8s.io/api/core/v1"
910
)
1011

1112
// EnvProcessName is the name of the env variable containing the function process
@@ -19,7 +20,7 @@ func AsFunctionStatus(item appsv1.Deployment) *types.FunctionStatus {
1920
replicas = uint64(*item.Spec.Replicas)
2021
}
2122

22-
functionContainer := item.Spec.Template.Spec.Containers[0]
23+
_, functionContainer := FunctionContainer(item)
2324

2425
labels := item.Spec.Template.Labels
2526
function := types.FunctionStatus{
@@ -69,7 +70,8 @@ func nodeSelectorToConstraint(deploy appsv1.Deployment) []string {
6970
}
7071

7172
func hasReadOnlyRootFilesystem(function appsv1.Deployment) bool {
72-
securityContext := function.Spec.Template.Spec.Containers[0].SecurityContext
73+
_, c := FunctionContainer(function)
74+
securityContext := c.SecurityContext
7375
if securityContext == nil {
7476
return false
7577
}
@@ -80,3 +82,18 @@ func hasReadOnlyRootFilesystem(function appsv1.Deployment) bool {
8082

8183
return *securityContext.ReadOnlyRootFilesystem
8284
}
85+
86+
// FunctionContainer returns the index and spec of the OpenFaaS function container
87+
// in the deployment. Use this method to safely retrieve the container spec, it protects
88+
// the controller from potential changes in the deployment spec by other Operators and
89+
// Admission webhooks in the cluster.
90+
//
91+
// idx will be -1 if the function container is not found.
92+
func FunctionContainer(function appsv1.Deployment) (idx int, c corev1.Container) {
93+
for idx, container := range function.Spec.Template.Spec.Containers {
94+
if container.Name == function.Name {
95+
return idx, container
96+
}
97+
}
98+
return -1, c
99+
}

pkg/k8s/securityContext.go

+31-9
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,22 @@ func (f *FunctionFactory) ConfigureContainerUserID(deployment *appsv1.Deployment
2323
functionUser = &userID
2424
}
2525

26-
if deployment.Spec.Template.Spec.Containers[0].SecurityContext == nil {
27-
deployment.Spec.Template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{}
26+
if deployment == nil {
27+
return
2828
}
2929

30-
deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser = functionUser
30+
idx, container := FunctionContainer(*deployment)
31+
if idx < 0 {
32+
// function container not found
33+
// and there is nothing we can do at this point
34+
return
35+
}
36+
37+
if container.SecurityContext == nil {
38+
deployment.Spec.Template.Spec.Containers[idx].SecurityContext = &corev1.SecurityContext{}
39+
}
40+
41+
deployment.Spec.Template.Spec.Containers[idx].SecurityContext.RunAsUser = functionUser
3142
}
3243

3344
// ConfigureReadOnlyRootFilesystem will create or update the required settings and mounts to ensure
@@ -39,19 +50,30 @@ func (f *FunctionFactory) ConfigureContainerUserID(deployment *appsv1.Deployment
3950
//
4051
// This method is safe for both create and update operations.
4152
func (f *FunctionFactory) ConfigureReadOnlyRootFilesystem(request types.FunctionDeployment, deployment *appsv1.Deployment) {
42-
if deployment.Spec.Template.Spec.Containers[0].SecurityContext != nil {
43-
deployment.Spec.Template.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &request.ReadOnlyRootFilesystem
53+
if deployment == nil {
54+
return
55+
}
56+
57+
idx, container := FunctionContainer(*deployment)
58+
if idx < 0 {
59+
// function container not found
60+
// and there is nothing we can do at this point
61+
return
62+
}
63+
64+
if container.SecurityContext != nil {
65+
deployment.Spec.Template.Spec.Containers[idx].SecurityContext.ReadOnlyRootFilesystem = &request.ReadOnlyRootFilesystem
4466
} else {
45-
deployment.Spec.Template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
67+
deployment.Spec.Template.Spec.Containers[idx].SecurityContext = &corev1.SecurityContext{
4668
ReadOnlyRootFilesystem: &request.ReadOnlyRootFilesystem,
4769
}
4870
}
4971

5072
existingVolumes := removeVolume("temp", deployment.Spec.Template.Spec.Volumes)
5173
deployment.Spec.Template.Spec.Volumes = existingVolumes
5274

53-
existingMounts := removeVolumeMount("temp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts)
54-
deployment.Spec.Template.Spec.Containers[0].VolumeMounts = existingMounts
75+
existingMounts := removeVolumeMount("temp", container.VolumeMounts)
76+
deployment.Spec.Template.Spec.Containers[idx].VolumeMounts = existingMounts
5577

5678
if request.ReadOnlyRootFilesystem {
5779
deployment.Spec.Template.Spec.Volumes = append(
@@ -64,7 +86,7 @@ func (f *FunctionFactory) ConfigureReadOnlyRootFilesystem(request types.Function
6486
},
6587
)
6688

67-
deployment.Spec.Template.Spec.Containers[0].VolumeMounts = append(
89+
deployment.Spec.Template.Spec.Containers[idx].VolumeMounts = append(
6890
existingMounts,
6991
corev1.VolumeMount{
7092
Name: "temp",

0 commit comments

Comments
 (0)