Skip to content

Commit e7c43ed

Browse files
author
Samu
authored
[v0.34.x] codegen: allow setting Strategy and PodSecurityContext (#546)
* codegen: allow setting Strategy and PodSecurityContext (#538)
1 parent 02dae3c commit e7c43ed

File tree

26 files changed

+907
-5
lines changed

26 files changed

+907
-5
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
changelog:
2+
- type: NEW_FEATURE
3+
issueLink: https://github.com/solo-io/gloo-mesh-enterprise/issues/15710
4+
resolvesIssue: false
5+
description: |
6+
Add support for configuring custom strategy and pod-level security context for operator deployments.
7+
skipCI: false

codegen/cmd_test.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
goyaml "gopkg.in/yaml.v3"
1414
rbacv1 "k8s.io/api/rbac/v1"
1515
v12 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
16+
"k8s.io/utils/pointer"
1617

1718
. "github.com/onsi/ginkgo/v2"
1819
. "github.com/onsi/gomega"
@@ -1836,6 +1837,189 @@ roleRef:
18361837
[]v1.EnvVar{{Name: "FOO", ValueFrom: &v1.EnvVarSource{SecretKeyRef: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "bar"}, Key: "baz"}}}}),
18371838
)
18381839

1840+
DescribeTable("rendering deployment strategy",
1841+
func(deploymentStrategy *appsv1.DeploymentStrategy) {
1842+
cmd := &Command{
1843+
Chart: &Chart{
1844+
Operators: []Operator{
1845+
{
1846+
Name: "painter",
1847+
Deployment: Deployment{
1848+
Strategy: deploymentStrategy,
1849+
Container: Container{
1850+
Image: Image{
1851+
Tag: "v0.0.0",
1852+
Repository: "painter",
1853+
Registry: "quay.io/solo-io",
1854+
PullPolicy: "IfNotPresent",
1855+
},
1856+
},
1857+
},
1858+
},
1859+
},
1860+
1861+
Values: nil,
1862+
Data: Data{
1863+
ApiVersion: "v1",
1864+
Description: "",
1865+
Name: "Painting Operator",
1866+
Version: "v0.0.1",
1867+
Home: "https://docs.solo.io/skv2/latest",
1868+
Sources: []string{
1869+
"https://github.com/solo-io/skv2",
1870+
},
1871+
},
1872+
},
1873+
1874+
ManifestRoot: "codegen/test/chart-deployment-strategy",
1875+
}
1876+
1877+
err := cmd.Execute()
1878+
Expect(err).NotTo(HaveOccurred())
1879+
1880+
values := map[string]interface{}{}
1881+
helmValues := map[string]interface{}{"painter": values}
1882+
1883+
renderedManifests := helmTemplate("codegen/test/chart-deployment-strategy", helmValues)
1884+
1885+
var renderedDeployment *appsv1.Deployment
1886+
decoder := kubeyaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(renderedManifests), 4096)
1887+
for {
1888+
obj := &unstructured.Unstructured{}
1889+
err := decoder.Decode(obj)
1890+
if err != nil {
1891+
break
1892+
}
1893+
if obj.GetName() != "painter" || obj.GetKind() != "Deployment" {
1894+
continue
1895+
}
1896+
1897+
bytes, err := obj.MarshalJSON()
1898+
Expect(err).NotTo(HaveOccurred())
1899+
renderedDeployment = &appsv1.Deployment{}
1900+
err = json.Unmarshal(bytes, renderedDeployment)
1901+
Expect(err).NotTo(HaveOccurred())
1902+
}
1903+
Expect(renderedDeployment).NotTo(BeNil())
1904+
renderedDeploymentStrategy := renderedDeployment.Spec.Strategy
1905+
if deploymentStrategy == nil {
1906+
Expect(renderedDeploymentStrategy).To(Equal(appsv1.DeploymentStrategy{}))
1907+
} else {
1908+
Expect(renderedDeploymentStrategy).To(Equal(*deploymentStrategy))
1909+
}
1910+
},
1911+
Entry("when the deployment strategy is not defined",
1912+
nil),
1913+
Entry("when the deployment strategy is configured to recreate",
1914+
&appsv1.DeploymentStrategy{
1915+
Type: appsv1.RecreateDeploymentStrategyType,
1916+
}),
1917+
Entry("when the deployment strategy is configured to rolling update",
1918+
&appsv1.DeploymentStrategy{
1919+
Type: appsv1.RollingUpdateDeploymentStrategyType,
1920+
RollingUpdate: &appsv1.RollingUpdateDeployment{
1921+
MaxUnavailable: &intstr.IntOrString{
1922+
IntVal: 1,
1923+
},
1924+
},
1925+
}),
1926+
)
1927+
1928+
DescribeTable("rendering pod security context",
1929+
func(podSecurityContextValues map[string]interface{}, podSecurityContext *v1.PodSecurityContext, expectedPodSecurityContext *v1.PodSecurityContext) {
1930+
cmd := &Command{
1931+
Chart: &Chart{
1932+
Operators: []Operator{
1933+
{
1934+
Name: "painter",
1935+
Deployment: Deployment{
1936+
Container: Container{
1937+
Image: Image{
1938+
Tag: "v0.0.0",
1939+
Repository: "painter",
1940+
Registry: "quay.io/solo-io",
1941+
PullPolicy: "IfNotPresent",
1942+
},
1943+
},
1944+
PodSecurityContext: podSecurityContext,
1945+
},
1946+
},
1947+
},
1948+
1949+
Values: nil,
1950+
Data: Data{
1951+
ApiVersion: "v1",
1952+
Description: "",
1953+
Name: "Painting Operator",
1954+
Version: "v0.0.1",
1955+
Home: "https://docs.solo.io/skv2/latest",
1956+
Sources: []string{
1957+
"https://github.com/solo-io/skv2",
1958+
},
1959+
},
1960+
},
1961+
1962+
ManifestRoot: "codegen/test/chart-pod-security-context",
1963+
}
1964+
1965+
err := cmd.Execute()
1966+
Expect(err).NotTo(HaveOccurred())
1967+
1968+
values := map[string]interface{}{"podSecurityContext": podSecurityContextValues}
1969+
helmValues := map[string]interface{}{"painter": values}
1970+
1971+
renderedManifests := helmTemplate("codegen/test/chart-pod-security-context", helmValues)
1972+
1973+
var renderedDeployment *appsv1.Deployment
1974+
decoder := kubeyaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(renderedManifests), 4096)
1975+
for {
1976+
obj := &unstructured.Unstructured{}
1977+
err := decoder.Decode(obj)
1978+
if err != nil {
1979+
break
1980+
}
1981+
if obj.GetName() != "painter" || obj.GetKind() != "Deployment" {
1982+
continue
1983+
}
1984+
1985+
bytes, err := obj.MarshalJSON()
1986+
Expect(err).NotTo(HaveOccurred())
1987+
renderedDeployment = &appsv1.Deployment{}
1988+
err = json.Unmarshal(bytes, renderedDeployment)
1989+
Expect(err).NotTo(HaveOccurred())
1990+
}
1991+
Expect(renderedDeployment).NotTo(BeNil())
1992+
renderedPodSecurityContext := renderedDeployment.Spec.Template.Spec.SecurityContext
1993+
Expect(renderedPodSecurityContext).To(Equal(expectedPodSecurityContext))
1994+
},
1995+
Entry("when PodSecurityContext is neither defined in values nor in the operator",
1996+
nil,
1997+
nil,
1998+
nil),
1999+
Entry("when PodSecurityContext is defined only in values",
2000+
map[string]interface{}{"fsGroup": 1000},
2001+
nil,
2002+
&v1.PodSecurityContext{
2003+
FSGroup: pointer.Int64(1000),
2004+
}),
2005+
Entry("when PodSecurityContext is defined only in the operator",
2006+
nil,
2007+
&v1.PodSecurityContext{
2008+
FSGroup: pointer.Int64(1000),
2009+
},
2010+
&v1.PodSecurityContext{
2011+
FSGroup: pointer.Int64(1000),
2012+
}),
2013+
Entry("when PodSecurityContext is defined in both values and the operator",
2014+
map[string]interface{}{"fsGroup": 1024},
2015+
&v1.PodSecurityContext{
2016+
FSGroup: pointer.Int64(1000),
2017+
},
2018+
&v1.PodSecurityContext{
2019+
FSGroup: pointer.Int64(1024), // should override the value defined in the operator
2020+
}),
2021+
)
2022+
18392023
Describe("rendering template env vars", func() {
18402024
var tmpDir string
18412025

codegen/model/chart.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/iancoleman/strcase"
1111
"github.com/solo-io/skv2/codegen/doc"
12+
appsv1 "k8s.io/api/apps/v1"
1213
corev1 "k8s.io/api/core/v1"
1314
rbacv1 "k8s.io/api/rbac/v1"
1415
)
@@ -112,7 +113,9 @@ type Deployment struct {
112113
// TODO support use of a DaemonSet instead of a Deployment
113114
UseDaemonSet bool
114115
Container
116+
Strategy *appsv1.DeploymentStrategy
115117
Sidecars []Sidecar
118+
PodSecurityContext *corev1.PodSecurityContext
116119
Volumes []corev1.Volume
117120
ConditionalVolumes []ConditionalVolume
118121
CustomPodLabels map[string]string

codegen/model/values/helm_chart_values.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ type UserValues struct {
5656
UserContainerValues `json:",inline"`
5757

5858
// Required to have an interface value in order to use the `index` function in the template
59-
Sidecars map[string]UserContainerValues `json:"sidecars" desc:"Optional configuration for the deployed containers."`
60-
FloatingUserID bool `json:"floatingUserId" desc:"Allow the pod to be assigned a dynamic user ID. Required for OpenShift installations."`
61-
RunAsUser uint32 `json:"runAsUser" desc:"Static user ID to run the containers as. Unused if floatingUserId is 'true'."`
62-
ServiceType v1.ServiceType `json:"serviceType" desc:"Kubernetes service type. Can be either \"ClusterIP\", \"NodePort\", \"LoadBalancer\", or \"ExternalName\"."`
63-
ServicePorts map[string]uint32 `json:"ports" desc:"Service ports as a map from port name to port number."`
59+
Sidecars map[string]UserContainerValues `json:"sidecars" desc:"Optional configuration for the deployed containers."`
60+
FloatingUserID bool `json:"floatingUserId" desc:"Allow the pod to be assigned a dynamic user ID. Required for OpenShift installations."`
61+
RunAsUser uint32 `json:"runAsUser" desc:"Static user ID to run the containers as. Unused if floatingUserId is 'true'."`
62+
ServiceType v1.ServiceType `json:"serviceType" desc:"Kubernetes service type. Can be either \"ClusterIP\", \"NodePort\", \"LoadBalancer\", or \"ExternalName\"."`
63+
ServicePorts map[string]uint32 `json:"ports" desc:"Service ports as a map from port name to port number."`
64+
PodSecurityContext *v1.PodSecurityContext `json:"podSecurityContext,omitempty" desc:"Pod-level security context. For more info, see the [Kubernetes documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#podsecuritycontext-v1-core)." omitChildren:"true"`
6465

6566
// Overrides which can be set by the user
6667
DeploymentOverrides *appsv1.Deployment `json:"deploymentOverrides" desc:"Arbitrary overrides for the component's [deployment template](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/)." omitChildren:"true"`

codegen/templates/chart/operator-deployment.yamltmpl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Expressions evaluating SKv2 Config use "[[" and "]]"
99

1010
[[/* custom values defined in codegen model */]]
1111
[[- $containers := containerConfigs $operator -]]
12+
[[- $deploymentStrategy := $operator.Deployment.Strategy -]]
13+
[[- $podSecurityContext := $operator.Deployment.PodSecurityContext -]]
1214
[[- $volumes := $operator.Deployment.Volumes -]]
1315
[[- $conditionalVolumes := $operator.Deployment.ConditionalVolumes -]]
1416
[[- $customPodLabels := $operator.Deployment.CustomPodLabels -]]
@@ -58,6 +60,16 @@ spec:
5860
[[- range $key, $value := $customPodLabels ]]
5961
[[ $key ]]: [[ $value ]]
6062
[[- end ]]
63+
[[- if $deploymentStrategy ]]
64+
strategy:
65+
[[- if $deploymentStrategy.Type ]]
66+
type: [[ $deploymentStrategy.Type ]]
67+
[[- end ]]
68+
[[- if $deploymentStrategy.RollingUpdate ]]
69+
rollingUpdate:
70+
[[ toYaml $deploymentStrategy.RollingUpdate | indent 6 ]]
71+
[[- end ]]
72+
[[- end ]]
6173
template:
6274
metadata:
6375
labels:
@@ -72,6 +84,16 @@ spec:
7284
[[- end ]]
7385
spec:
7486
serviceAccountName: [[ $operator.Name ]]
87+
{{- /* Override the default podSecurityContext config if it is set. */}}
88+
{{- if or ([[ (opVar $operator) ]].podSecurityContext) (eq "map[]" (printf "%v" [[ (opVar $operator) ]].podSecurityContext)) }}
89+
securityContext:
90+
{{ toYaml [[ (opVar $operator) ]].podSecurityContext | indent 8 }}
91+
[[- if $podSecurityContext ]]
92+
{{- else}}
93+
securityContext:
94+
[[ toYaml $podSecurityContext | indent 8 ]]
95+
[[- end ]]
96+
{{- end }}
7597
[[- if $volumes ]]
7698
volumes:
7799
[[ toYaml $volumes | indent 6 ]]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Code generated by skv2. DO NOT EDIT.
2+
3+
apiVersion: v1
4+
home: https://docs.solo.io/skv2/latest
5+
name: Painting Operator
6+
sources:
7+
- https://github.com/solo-io/skv2
8+
version: v0.0.1
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Code generated by skv2. DO NOT EDIT.
2+
3+
4+
5+
{{/* Below are library functions provided by skv2 */}}
6+
7+
{{- /*
8+
9+
"skv2.utils.merge" takes an array of three values:
10+
- the top context
11+
- the yaml block that will be merged in (override)
12+
- the name of the base template (source)
13+
14+
note: the source must be a named template (helm partial). This is necessary for the merging logic.
15+
16+
The behaviour is as follows, to align with already existing helm behaviour:
17+
- If no source is found (template is empty), the merged output will be empty
18+
- If no overrides are specified, the source is rendered as is
19+
- If overrides are specified and source is not empty, overrides will be merged in to the source.
20+
21+
Overrides can replace / add to deeply nested dictionaries, but will completely replace lists.
22+
Examples:
23+
24+
┌─────────────────────┬───────────────────────┬────────────────────────┐
25+
│ Source (template) │ Overrides │ Result │
26+
├─────────────────────┼───────────────────────┼────────────────────────┤
27+
│ metadata: │ metadata: │ metadata: │
28+
│ labels: │ labels: │ labels: │
29+
│ app: gloo │ app: gloo1 │ app: gloo1 │
30+
│ cluster: useast │ author: infra-team │ author: infra-team │
31+
│ │ │ cluster: useast │
32+
├─────────────────────┼───────────────────────┼────────────────────────┤
33+
│ lists: │ lists: │ lists: │
34+
│ groceries: │ groceries: │ groceries: │
35+
│ - apple │ - grapes │ - grapes │
36+
│ - banana │ │ │
37+
└─────────────────────┴───────────────────────┴────────────────────────┘
38+
39+
skv2.utils.merge is a fork of a helm library chart function (https://github.com/helm/charts/blob/master/incubator/common/templates/_util.tpl).
40+
This includes some optimizations to speed up chart rendering time, and merges in a value (overrides) with a named template, unlike the upstream
41+
version, which merges two named templates.
42+
43+
*/ -}}
44+
{{- define "skv2.utils.merge" -}}
45+
{{- $top := first . -}}
46+
{{- $overrides := (index . 1) -}}
47+
{{- $tpl := fromYaml (include (index . 2) $top) -}}
48+
{{- if or (empty $overrides) (empty $tpl) -}}
49+
{{ include (index . 2) $top }} {{/* render source as is */}}
50+
{{- else -}}
51+
{{- $merged := merge $overrides $tpl -}}
52+
{{- toYaml $merged -}} {{/* render source with overrides as YAML */}}
53+
{{- end -}}
54+
{{- end -}}

0 commit comments

Comments
 (0)