Skip to content
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

[v0.37.x] codegen: support for conditional deployment strategy #551

Merged
merged 3 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog/v0.37.3/conditional-strategy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
changelog:
- type: NEW_FEATURE
issueLink: https://github.com/solo-io/gloo-mesh-enterprise/issues/15710
resolvesIssue: false
description: |
Adds support for conditional deployment strategy.
skipCI: false
112 changes: 112 additions & 0 deletions codegen/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,118 @@ roleRef:
}),
)

DescribeTable("rendering conditional deployment strategy",
func(values map[string]any, conditionalStrategy []model.ConditionalStrategy, expectedStrategy appsv1.DeploymentStrategy) {
cmd := &Command{
Chart: &Chart{
Operators: []Operator{
{
Name: "painter",
Deployment: Deployment{
ConditionalStrategy: conditionalStrategy,
Container: Container{
Image: Image{
Tag: "v0.0.0",
Repository: "painter",
Registry: "quay.io/solo-io",
PullPolicy: "IfNotPresent",
},
},
},
},
},

Values: nil,
Data: Data{
ApiVersion: "v1",
Description: "",
Name: "Painting Operator",
Version: "v0.0.1",
Home: "https://docs.solo.io/skv2/latest",
Sources: []string{
"https://github.com/solo-io/skv2",
},
},
},

ManifestRoot: "codegen/test/chart-conditional-deployment-strategy",
}

err := cmd.Execute()
Expect(err).NotTo(HaveOccurred())

helmValues := map[string]interface{}{"painter": values}
renderedManifests := helmTemplate("codegen/test/chart-conditional-deployment-strategy", helmValues)

var renderedDeployment *appsv1.Deployment
decoder := kubeyaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(renderedManifests), 4096)
for {
obj := &unstructured.Unstructured{}
err := decoder.Decode(obj)
if err != nil {
break
}
if obj.GetName() != "painter" || obj.GetKind() != "Deployment" {
continue
}

bytes, err := obj.MarshalJSON()
Expect(err).NotTo(HaveOccurred())
renderedDeployment = &appsv1.Deployment{}
err = json.Unmarshal(bytes, renderedDeployment)
Expect(err).NotTo(HaveOccurred())
}
Expect(renderedDeployment).NotTo(BeNil())
renderedDeploymentStrategy := renderedDeployment.Spec.Strategy
Expect(renderedDeploymentStrategy).To(Equal(expectedStrategy))
},
Entry("when the conditional strategy is not defined",
map[string]any{"enabled": true},
nil,
appsv1.DeploymentStrategy{},
),
Entry("when the condition is true",
map[string]any{"enabled": true, "condition": true},
[]model.ConditionalStrategy{
{
Condition: "$.Values.painter.condition",
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
},
{
Condition: "not $.Values.painter.condition",
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RecreateDeploymentStrategyType,
},
},
},
appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
),
Entry("when the condition is false",
map[string]any{"enabled": true, "condition": false},
[]model.ConditionalStrategy{
{
Condition: "$.Values.painter.condition",
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
},
{
Condition: "not $.Values.painter.condition",
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RecreateDeploymentStrategyType,
},
},
},
appsv1.DeploymentStrategy{
Type: appsv1.RecreateDeploymentStrategyType,
},
),
)

DescribeTable("rendering pod security context",
func(podSecurityContextValues map[string]interface{}, podSecurityContext *v1.PodSecurityContext, expectedPodSecurityContext *v1.PodSecurityContext) {
cmd := &Command{
Expand Down
6 changes: 6 additions & 0 deletions codegen/model/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ type Deployment struct {
UseDaemonSet bool
Container
Strategy *appsv1.DeploymentStrategy
ConditionalStrategy []ConditionalStrategy
Sidecars []Sidecar
PodSecurityContext *corev1.PodSecurityContext
Volumes []corev1.Volume
Expand All @@ -124,6 +125,11 @@ type Deployment struct {
CustomDeploymentAnnotations map[string]string
}

type ConditionalStrategy struct {
Condition string
Strategy appsv1.DeploymentStrategy
}

type ConditionalVolume struct {
Condition string
Volume corev1.Volume
Expand Down
18 changes: 10 additions & 8 deletions codegen/templates/chart/operator-deployment.yamltmpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Expressions evaluating SKv2 Config use "[[" and "]]"

[[/* custom values defined in codegen model */]]
[[- $containers := containerConfigs $operator -]]
[[- $deploymentStrategy := $operator.Deployment.Strategy -]]
[[- $strategy := $operator.Deployment.Strategy -]]
[[- $conditionalStrategy := $operator.Deployment.ConditionalStrategy -]]
[[- $podSecurityContext := $operator.Deployment.PodSecurityContext -]]
[[- $volumes := $operator.Deployment.Volumes -]]
[[- $conditionalVolumes := $operator.Deployment.ConditionalVolumes -]]
Expand Down Expand Up @@ -60,14 +61,15 @@ spec:
[[- range $key, $value := $customPodLabels ]]
[[ $key ]]: [[ $value ]]
[[- end ]]
[[- if $deploymentStrategy ]]
[[- if $strategy ]]
strategy:
[[- if $deploymentStrategy.Type ]]
type: [[ $deploymentStrategy.Type ]]
[[- end ]]
[[- if $deploymentStrategy.RollingUpdate ]]
rollingUpdate:
[[ toYaml $deploymentStrategy.RollingUpdate | indent 6 ]]
[[ toYaml $strategy | indent 4 ]]
[[- else if $conditionalStrategy ]]
[[- range $s := $conditionalStrategy ]]
{{- if [[ $s.Condition ]] }}
strategy:
[[ toYaml $s.Strategy | indent 4 ]]
{{- end }}
[[- end ]]
[[- end ]]
template:
Expand Down
8 changes: 8 additions & 0 deletions codegen/test/chart-conditional-deployment-strategy/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Code generated by skv2. DO NOT EDIT.

apiVersion: v1
home: https://docs.solo.io/skv2/latest
name: Painting Operator
sources:
- https://github.com/solo-io/skv2
version: v0.0.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Code generated by skv2. DO NOT EDIT.



{{/* Below are library functions provided by skv2 */}}

{{- /*

"skv2.utils.merge" takes an array of three values:
- the top context
- the yaml block that will be merged in (override)
- the name of the base template (source)

note: the source must be a named template (helm partial). This is necessary for the merging logic.

The behaviour is as follows, to align with already existing helm behaviour:
- If no source is found (template is empty), the merged output will be empty
- If no overrides are specified, the source is rendered as is
- If overrides are specified and source is not empty, overrides will be merged in to the source.

Overrides can replace / add to deeply nested dictionaries, but will completely replace lists.
Examples:

┌─────────────────────┬───────────────────────┬────────────────────────┐
│ Source (template) │ Overrides │ Result │
├─────────────────────┼───────────────────────┼────────────────────────┤
│ metadata: │ metadata: │ metadata: │
│ labels: │ labels: │ labels: │
│ app: gloo │ app: gloo1 │ app: gloo1 │
│ cluster: useast │ author: infra-team │ author: infra-team │
│ │ │ cluster: useast │
├─────────────────────┼───────────────────────┼────────────────────────┤
│ lists: │ lists: │ lists: │
│ groceries: │ groceries: │ groceries: │
│ - apple │ - grapes │ - grapes │
│ - banana │ │ │
└─────────────────────┴───────────────────────┴────────────────────────┘

skv2.utils.merge is a fork of a helm library chart function (https://github.com/helm/charts/blob/master/incubator/common/templates/_util.tpl).
This includes some optimizations to speed up chart rendering time, and merges in a value (overrides) with a named template, unlike the upstream
version, which merges two named templates.

*/ -}}
{{- define "skv2.utils.merge" -}}
{{- $top := first . -}}
{{- $overrides := (index . 1) -}}
{{- $tpl := fromYaml (include (index . 2) $top) -}}
{{- if or (empty $overrides) (empty $tpl) -}}
{{ include (index . 2) $top }} {{/* render source as is */}}
{{- else -}}
{{- $merged := merge $overrides $tpl -}}
{{- toYaml $merged -}} {{/* render source with overrides as YAML */}}
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Code generated by skv2. DO NOT EDIT.



{{- $painter := $.Values.painter }}
---

{{- define "painter.deploymentSpec" }}
# Deployment manifest for painter

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: painter
annotations:
app.kubernetes.io/name: painter
name: painter
namespace: {{ default .Release.Namespace $.Values.painter.namespace }}
spec:
selector:
matchLabels:
app: painter
{{- if $.Values.painter.condition }}
strategy:
type: RollingUpdate
{{- end }}
{{- if not $.Values.painter.condition }}
strategy:
type: Recreate
{{- end }}
template:
metadata:
labels:
app: painter
annotations:
app.kubernetes.io/name: painter
spec:
serviceAccountName: painter
{{- /* Override the default podSecurityContext config if it is set. */}}
{{- if or ($.Values.painter.podSecurityContext) (eq "map[]" (printf "%v" $.Values.painter.podSecurityContext)) }}
securityContext:
{{ toYaml $.Values.painter.podSecurityContext | indent 8 }}
{{- end }}
containers:
{{- $painter := $.Values.painter }}
{{- $painterImage := $painter.image }}
- name: painter
image: {{ $painterImage.registry }}/{{ $painterImage.repository }}:{{ $painterImage.tag }}
imagePullPolicy: {{ $painterImage.pullPolicy }}
{{- if $painter.env }}
env:
{{ toYaml $painter.env | indent 10 }}
{{- else if $painter.extraEnvs }}
env:
{{- end }}
{{- range $name, $item := $painter.extraEnvs }}
- name: {{ $name }}
{{- $item | toYaml | nindent 12 }}
{{- end }}
resources:
{{- if $painter.resources }}
{{ toYaml $painter.resources | indent 10}}
{{- else}}
requests:
cpu: 500m
memory: 256Mi
{{- end }}
{{- /*
Render securityContext configs if it is set.
If securityContext is not set, render the default securityContext.
If securityContext is set to 'false', render an empty map.
*/}}
securityContext:
{{- if or ($painter.securityContext) (eq "map[]" (printf "%v" $painter.securityContext)) }}
{{ toYaml $painter.securityContext | indent 10}}
{{/* Because securityContext is nil by default we can only perform following conversion if it is a boolean. Skip conditional otherwise. */}}
{{- else if eq (ternary $painter.securityContext true (eq "bool" (printf "%T" $painter.securityContext))) false }}
{}
{{- else}}
runAsNonRoot: true
{{- if not $painter.floatingUserId }}
runAsUser: {{ printf "%.0f" (float64 $painter.runAsUser) }}
{{- end }}
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
{{- end }}
{{- if $painterImage.pullSecret }}
imagePullSecrets:
- name: {{ $painterImage.pullSecret }}
{{- end}}
{{- end }} {{/* define "painter.deploymentSpec" */}}

{{/* Render painter deployment template with overrides from values*/}}
{{ if $painter.enabled }}
{{- $painterDeploymentOverrides := dict }}
{{- if $painter.deploymentOverrides }}
{{- $painterDeploymentOverrides = $painter.deploymentOverrides }}
{{- end }}
---
{{ include "skv2.utils.merge" (list . $painterDeploymentOverrides "painter.deploymentSpec") }}
{{- end }}
---
{{ if $painter.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: painter
{{- if $painter.serviceAccount}}
{{- if $painter.serviceAccount.extraAnnotations }}
annotations:
{{- range $key, $value := $painter.serviceAccount.extraAnnotations }}
{{ $key }}: {{ $value }}
{{- end }}
{{- end }}
{{- end}}
name: painter
namespace: {{ default .Release.Namespace $.Values.painter.namespace }}
{{- end }}


{{- define "painter.serviceSpec"}}

{{- end }} {{/* define "painter.serviceSpec" */}}
{{ if $painter.enabled }}
{{/* Render painter service template with overrides from values*/}}
{{- $painterServiceOverrides := dict }}
{{- if $painter.serviceOverrides }}
{{- $painterServiceOverrides = $painter.serviceOverrides }}
{{- end }}

---

{{ include "skv2.utils.merge" (list . $painterServiceOverrides "painter.serviceSpec") }}
{{- end }}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Code generated by skv2. DO NOT EDIT.

Loading
Loading