Skip to content

Commit 2cfa610

Browse files
authored
Merge pull request #2690 from vmware/configurable_kube_cert_agent_strategy_type
allow the kube cert agent deployment's strategy type to be configured
2 parents 6e87caa + fa5f754 commit 2cfa610

File tree

10 files changed

+228
-29
lines changed

10 files changed

+228
-29
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require (
3434
github.com/gofrs/flock v0.13.0
3535
github.com/google/cel-go v0.26.1
3636
github.com/google/go-cmp v0.7.0
37-
github.com/google/go-github/v74 v74.0.0
37+
github.com/google/go-github/v75 v75.0.0
3838
github.com/google/gofuzz v1.2.0
3939
github.com/google/uuid v1.6.0
4040
github.com/gorilla/securecookie v1.1.2
@@ -49,7 +49,7 @@ require (
4949
github.com/spf13/cobra v1.10.1
5050
github.com/spf13/pflag v1.0.10
5151
github.com/stretchr/testify v1.11.1
52-
github.com/tdewolff/minify/v2 v2.24.4
52+
github.com/tdewolff/minify/v2 v2.24.5
5353
go.uber.org/mock v0.6.0
5454
go.uber.org/zap v1.27.0
5555
golang.org/x/crypto v0.43.0
@@ -147,7 +147,7 @@ require (
147147
github.com/spf13/viper v1.16.0 // indirect
148148
github.com/stoewer/go-strcase v1.3.0 // indirect
149149
github.com/subosito/gotenv v1.4.2 // indirect
150-
github.com/tdewolff/parse/v2 v2.8.4 // indirect
150+
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a // indirect
151151
github.com/x448/float16 v0.8.4 // indirect
152152
go.etcd.io/etcd/api/v3 v3.5.21 // indirect
153153
go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
247247
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
248248
github.com/google/go-github/v73 v73.0.0 h1:aR+Utnh+Y4mMkS+2qLQwcQ/cF9mOTpdwnzlaw//rG24=
249249
github.com/google/go-github/v73 v73.0.0/go.mod h1:fa6w8+/V+edSU0muqdhCVY7Beh1M8F1IlQPZIANKIYw=
250-
github.com/google/go-github/v74 v74.0.0 h1:yZcddTUn8DPbj11GxnMrNiAnXH14gNs559AsUpNpPgM=
251-
github.com/google/go-github/v74 v74.0.0/go.mod h1:ubn/YdyftV80VPSI26nSJvaEsTOnsjrxG3o9kJhcyak=
250+
github.com/google/go-github/v75 v75.0.0 h1:k7q8Bvg+W5KxRl9Tjq16a9XEgVY1pwuiG5sIL7435Ic=
251+
github.com/google/go-github/v75 v75.0.0/go.mod h1:H3LUJEA1TCrzuUqtdAQniBNwuKiQIqdGKgBo1/M/uqI=
252252
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
253253
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
254254
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -564,10 +564,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
564564
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
565565
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
566566
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
567-
github.com/tdewolff/minify/v2 v2.24.4 h1:pQyr6eWDa+RXtAoZg+6wurh0jB9ojqw/qc5LlU7/z6c=
568-
github.com/tdewolff/minify/v2 v2.24.4/go.mod h1:iD9Qn7/brhKY9d0KLKMkZrqS8/bqxSxRKruBi7V6m+w=
569-
github.com/tdewolff/parse/v2 v2.8.4 h1:A6slgBLGGDPBMGA28KQZfHpaKffuNvhOe7zSag+x/rw=
570-
github.com/tdewolff/parse/v2 v2.8.4/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
567+
github.com/tdewolff/minify/v2 v2.24.5 h1:ytxthX3xSxrK3Xx5B38flg5moCKs/dB8VwiD/RzJViU=
568+
github.com/tdewolff/minify/v2 v2.24.5/go.mod h1:q09KtNnVai7TyEzGEZeWPAnK+c8Z+NI8prCXZW652bo=
569+
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a h1:Rmq+utdraciok/97XHRweYdsAo/M4LOswpCboo3yvN4=
570+
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
571571
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
572572
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
573573
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=

internal/config/concierge/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"os"
1212
"strings"
1313

14+
appsv1 "k8s.io/api/apps/v1"
15+
"k8s.io/apimachinery/pkg/util/sets"
1416
"k8s.io/apimachinery/pkg/util/validation"
1517
"k8s.io/utils/ptr"
1618
"sigs.k8s.io/yaml"
@@ -200,6 +202,12 @@ func validateKubeCertAgent(agentConfig *KubeCertAgentSpec) error {
200202
return constable.Error(fmt.Sprintf("runAsGroup must be 0 or greater (instead of %d)", *agentConfig.RunAsGroup))
201203
}
202204

205+
allowedStrategyTypes := sets.New(appsv1.RecreateDeploymentStrategyType, appsv1.RollingUpdateDeploymentStrategyType)
206+
if agentConfig.DeploymentStrategyType != nil && !allowedStrategyTypes.Has(*agentConfig.DeploymentStrategyType) {
207+
return constable.Error(fmt.Sprintf("deploymentStrategyType must be one of %s (instead of %s)",
208+
sets.List(allowedStrategyTypes), *agentConfig.DeploymentStrategyType))
209+
}
210+
203211
if len(agentConfig.PriorityClassName) == 0 {
204212
// Optional, so empty is valid.
205213
return nil

internal/config/concierge/config_test.go

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"testing"
1212

1313
"github.com/stretchr/testify/require"
14+
appsv1 "k8s.io/api/apps/v1"
1415
"k8s.io/utils/ptr"
1516

1617
"go.pinniped.dev/internal/here"
@@ -71,6 +72,7 @@ func TestFromPath(t *testing.T) {
7172
priorityClassName: %s
7273
runAsUser: 1
7374
runAsGroup: 2
75+
deploymentStrategyType: Recreate
7476
log:
7577
level: debug
7678
tls:
@@ -119,12 +121,13 @@ func TestFromPath(t *testing.T) {
119121
"myLabelKey2": "myLabelValue2",
120122
},
121123
KubeCertAgentConfig: KubeCertAgentSpec{
122-
NamePrefix: ptr.To("kube-cert-agent-name-prefix-"),
123-
Image: ptr.To("kube-cert-agent-image"),
124-
ImagePullSecrets: []string{"kube-cert-agent-image-pull-secret"},
125-
PriorityClassName: stringOfLength253,
126-
RunAsUser: ptr.To(int64(1)),
127-
RunAsGroup: ptr.To(int64(2)),
124+
NamePrefix: ptr.To("kube-cert-agent-name-prefix-"),
125+
Image: ptr.To("kube-cert-agent-image"),
126+
ImagePullSecrets: []string{"kube-cert-agent-image-pull-secret"},
127+
PriorityClassName: stringOfLength253,
128+
RunAsUser: ptr.To(int64(1)),
129+
RunAsGroup: ptr.To(int64(2)),
130+
DeploymentStrategyType: ptr.To(appsv1.RecreateDeploymentStrategyType),
128131
},
129132
Log: plog.LogSpec{
130133
Level: plog.LevelDebug,
@@ -184,6 +187,9 @@ func TestFromPath(t *testing.T) {
184187
image: kube-cert-agent-image
185188
imagePullSecrets: [kube-cert-agent-image-pull-secret]
186189
priorityClassName: kube-cert-agent-priority-class-name
190+
runAsUser: 1
191+
runAsGroup: 2
192+
deploymentStrategyType: RollingUpdate
187193
log:
188194
level: all
189195
format: json
@@ -227,10 +233,13 @@ func TestFromPath(t *testing.T) {
227233
"myLabelKey2": "myLabelValue2",
228234
},
229235
KubeCertAgentConfig: KubeCertAgentSpec{
230-
NamePrefix: ptr.To("kube-cert-agent-name-prefix-"),
231-
Image: ptr.To("kube-cert-agent-image"),
232-
ImagePullSecrets: []string{"kube-cert-agent-image-pull-secret"},
233-
PriorityClassName: "kube-cert-agent-priority-class-name",
236+
NamePrefix: ptr.To("kube-cert-agent-name-prefix-"),
237+
Image: ptr.To("kube-cert-agent-image"),
238+
ImagePullSecrets: []string{"kube-cert-agent-image-pull-secret"},
239+
PriorityClassName: "kube-cert-agent-priority-class-name",
240+
RunAsUser: ptr.To(int64(1)),
241+
RunAsGroup: ptr.To(int64(2)),
242+
DeploymentStrategyType: ptr.To(appsv1.RollingUpdateDeploymentStrategyType),
234243
},
235244
Log: plog.LogSpec{
236245
Level: plog.LevelAll,
@@ -801,6 +810,27 @@ func TestFromPath(t *testing.T) {
801810
`),
802811
wantError: `validate kubeCertAgent: runAsGroup must be 0 or greater (instead of -1)`,
803812
},
813+
{
814+
name: "invalid deploymentStrategyType",
815+
yaml: here.Doc(`
816+
---
817+
names:
818+
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
819+
credentialIssuer: pinniped-config
820+
apiService: pinniped-api
821+
impersonationLoadBalancerService: impersonationLoadBalancerService-value
822+
impersonationClusterIPService: impersonationClusterIPService-value
823+
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
824+
impersonationCACertificateSecret: impersonationCACertificateSecret-value
825+
impersonationSignerSecret: impersonationSignerSecret-value
826+
agentServiceAccount: agentServiceAccount-value
827+
impersonationProxyServiceAccount: impersonationProxyServiceAccount-value
828+
impersonationProxyLegacySecret: impersonationProxyLegacySecret-value
829+
kubeCertAgent:
830+
deploymentStrategyType: thisIsInvalid
831+
`),
832+
wantError: `validate kubeCertAgent: deploymentStrategyType must be one of [Recreate RollingUpdate] (instead of thisIsInvalid)`,
833+
},
804834
}
805835
for _, test := range tests {
806836
t.Run(test.name, func(t *testing.T) {

internal/config/concierge/types.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
package concierge
55

6-
import "go.pinniped.dev/internal/plog"
6+
import (
7+
appsv1 "k8s.io/api/apps/v1"
8+
9+
"go.pinniped.dev/internal/plog"
10+
)
711

812
const (
913
Enabled = "enabled"
@@ -120,4 +124,9 @@ type KubeCertAgentSpec struct {
120124
// The GID to run the entrypoint of the kube-cert-agent container.
121125
// Defaults to 0 (root). No validation is performed on this value.
122126
RunAsGroup *int64 `json:"runAsGroup"`
127+
128+
// DeploymentStrategyType will be set as the agent Deployment's deployment strategy type.
129+
// When nil, the Deployment will not specify any deployment strategy type, and will therefore have its
130+
// deployment strategy type set by Kubernetes default behavior (currently RollingUpdate).
131+
DeploymentStrategyType *appsv1.DeploymentStrategyType `json:"deploymentStrategyType"`
123132
}

internal/controller/kubecertagent/kubecertagent.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,16 @@ type AgentConfig struct {
105105
// PriorityClassName optionally sets the PriorityClassName for the agent's pod.
106106
PriorityClassName string
107107

108-
// RunAsUser is the UID to run the entrypoint of the container process
108+
// RunAsUser is the UID to run the entrypoint of the container process.
109109
RunAsUser *int64
110110

111-
// RunAsGroup is the GID to run the entrypoint of the container process
111+
// RunAsGroup is the GID to run the entrypoint of the container process.
112112
RunAsGroup *int64
113+
114+
// DeploymentStrategyType will be set as the agent Deployment's deployment strategy type.
115+
// When nil, the Deployment will not specify any deployment strategy type, and will therefore have its
116+
// deployment strategy type set by Kubernetes default behavior (currently RollingUpdate).
117+
DeploymentStrategyType *appsv1.DeploymentStrategyType
113118
}
114119

115120
// Only select using the unique label which will not match the pods of any other Deployment.
@@ -440,12 +445,14 @@ func (c *agentController) createOrUpdateDeployment(ctx context.Context, newestCo
440445
desireTemplateLabelsUpdate := !apiequality.Semantic.DeepEqual(updatedDeployment.Spec.Template.Labels, existingDeployment.Spec.Template.Labels)
441446
// The user might want to set PriorityClassName back to the default value of empty string. DeepDerivative() won't detect this case below.
442447
desirePriorityClassNameUpdate := updatedDeployment.Spec.Template.Spec.PriorityClassName != existingDeployment.Spec.Template.Spec.PriorityClassName
448+
// The user might want to set deploymentStrategyType back to the default value. DeepDerivative() won't detect this case below.
449+
desireDeploymentStrategyTypeUpdate := updatedDeployment.Spec.Strategy.Type != existingDeployment.Spec.Strategy.Type
443450

444451
// If the existing Deployment already matches our desired spec, we're done.
445452
if apiequality.Semantic.DeepDerivative(updatedDeployment, existingDeployment) {
446453
// DeepDerivative allows the map fields of updatedDeployment to be a subset of existingDeployment,
447454
// but we want to check that certain of those map fields are exactly equal before deciding to skip the update.
448-
if !desireSelectorUpdate && !desireTemplateLabelsUpdate && !desirePriorityClassNameUpdate {
455+
if !desireSelectorUpdate && !desireTemplateLabelsUpdate && !desirePriorityClassNameUpdate && !desireDeploymentStrategyTypeUpdate {
449456
return nil // already equal enough, so skip update
450457
}
451458
}
@@ -614,6 +621,14 @@ func (c *agentController) getPodSecurityContext() *corev1.PodSecurityContext {
614621
return podSecurityContext
615622
}
616623

624+
func (c *agentController) getDeploymentStrategy() appsv1.DeploymentStrategy {
625+
s := appsv1.DeploymentStrategy{}
626+
if c.cfg.DeploymentStrategyType != nil {
627+
s.Type = *c.cfg.DeploymentStrategyType
628+
}
629+
return s
630+
}
631+
617632
func (c *agentController) newAgentDeployment(controllerManagerPod *corev1.Pod) *appsv1.Deployment {
618633
var volumeMounts []corev1.VolumeMount
619634
if len(controllerManagerPod.Spec.Containers) > 0 {
@@ -699,6 +714,9 @@ func (c *agentController) newAgentDeployment(controllerManagerPod *corev1.Pod) *
699714

700715
// Setting MinReadySeconds prevents the agent pods from being churned too quickly by the deployments controller.
701716
MinReadySeconds: 10,
717+
718+
// Allow the user to optionally configure the deployment strategy type.
719+
Strategy: c.getDeploymentStrategy(),
702720
},
703721
}
704722
}

0 commit comments

Comments
 (0)