Skip to content

Commit 81ff7b1

Browse files
authored
Merge pull request #232 from joelsmith/kedamain
Add caConfigMaps field to KedaOperatorSpec
2 parents 8370e2d + f423f32 commit 81ff7b1

File tree

9 files changed

+190
-40
lines changed

9 files changed

+190
-40
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ spec:
111111
# default value: rfc3339
112112
# logTimeEncoding: rfc3339
113113
114+
## CA Certificate ConfigMap Names
115+
# ConfigMaps containing PEM-encoded trusted certificate authorities (CAs).
116+
# The files from the ConfigMaps will be loaded by the KEDA operator during
117+
# start-up and will be used by scalers to authenticate TLS-enabled metrics
118+
# data sources.
119+
# default value: []
120+
# caConfigMaps: []
121+
114122
## Arbitrary arguments
115123
# Define any argument with possibility to override already existing ones.
116124
# Array of strings (format is either with prefix '--key=value' or just 'value')

apis/keda/v1alpha1/kedacontroller_types.go

+7
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ type KedaOperatorSpec struct {
9393
// 'argument=value' or just 'value'. Ex.: '--v=0' or 'ENV_ARGUMENT'
9494
// +optional
9595
Args []string `json:"args,omitempty"`
96+
97+
// ConfigMaps containing PEM-encoded trusted certificate authorities (CAs).
98+
// The files from the ConfigMaps will be loaded by the KEDA operator during
99+
// start-up and will be used by scalers to authenticate TLS-enabled metrics
100+
// data sources.
101+
// +optional
102+
CAConfigMaps []string `json:"caConfigMaps,omitempty"`
96103
}
97104

98105
type KedaMetricsServerSpec struct {

apis/keda/v1alpha1/zz_generated.deepcopy.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle/manifests/keda.sh_kedacontrollers.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -3597,6 +3597,14 @@ spec:
35973597
items:
35983598
type: string
35993599
type: array
3600+
caConfigMaps:
3601+
description: ConfigMaps containing PEM-encoded trusted certificate
3602+
authorities (CAs). The files from the ConfigMaps will be loaded
3603+
by the KEDA operator during start-up and will be used by scalers
3604+
to authenticate TLS-enabled metrics data sources.
3605+
items:
3606+
type: string
3607+
type: array
36003608
deploymentAnnotations:
36013609
additionalProperties:
36023610
type: string

config/crd/bases/keda.sh_kedacontrollers.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -3594,6 +3594,14 @@ spec:
35943594
items:
35953595
type: string
35963596
type: array
3597+
caConfigMaps:
3598+
description: ConfigMaps containing PEM-encoded trusted certificate
3599+
authorities (CAs). The files from the ConfigMaps will be loaded
3600+
by the KEDA operator during start-up and will be used by scalers
3601+
to authenticate TLS-enabled metrics data sources.
3602+
items:
3603+
type: string
3604+
type: array
35973605
deploymentAnnotations:
35983606
additionalProperties:
35993607
type: string

config/samples/keda_v1alpha1_kedacontroller.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ spec:
3030
# default value: rfc3339
3131
# logTimeEncoding: rfc3339
3232

33+
## CA Certificate ConfigMap Names
34+
# ConfigMaps containing PEM-encoded trusted certificate authorities (CAs).
35+
# The files from the ConfigMaps will be loaded by the KEDA operator during
36+
# start-up and will be used by scalers to authenticate TLS-enabled metrics
37+
# data sources.
38+
# default value: []
39+
# caConfigMaps: []
40+
3341
## Arbitrary arguments
3442
# Define any argument with possibility to override already existing ones
3543
# array of strings (format is either with prefix '--key=value' or just 'value')

controllers/keda/kedacontroller_controller.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,30 @@ func (r *KedaControllerReconciler) installController(ctx context.Context, logger
371371

372372
runningOnOpenshift := util.RunningOnOpenshift(ctx, logger, r.Client)
373373

374+
caConfigMaps := instance.Spec.Operator.CAConfigMaps
375+
if runningOnOpenshift {
376+
found := false
377+
for _, cmName := range caConfigMaps {
378+
if cmName == caBundleConfigMapName {
379+
found = true
380+
break
381+
}
382+
}
383+
if !found {
384+
// prepend it
385+
caConfigMaps = append([]string{caBundleConfigMapName}, caConfigMaps...)
386+
}
387+
}
388+
389+
transforms = append(transforms, transform.EnsureCACertsForOperatorDeployment(caConfigMaps, r.Scheme, logger)...)
390+
374391
if runningOnOpenshift {
375392
// certificates rotation works only on Openshift due to openshift/service-ca-operator
376393
serviceName := "keda-operator"
377394
certsSecretName := serviceName + "-certs"
378395
transforms = append(transforms,
379396
transform.EnsureCertInjectionForService(serviceName, servingCertsAnnotation, certsSecretName),
380397
transform.KedaOperatorEnsureCertificatesVolume(certsSecretName, grpcClientCertsSecretName, r.Scheme),
381-
transform.EnsureOpenshiftCABundleForOperatorDeployment(caBundleConfigMapName, r.Scheme),
382398
transform.SetOperatorCertRotation(false, r.Scheme, logger), // don't use KEDA operator's built-in cert rotation when on OpenShift
383399
)
384400
// on OpenShift 4.10 (kube 1.23) and earlier, the RuntimeDefault SeccompProfile won't validate against any SCC

controllers/keda/transform/transform.go

+121-39
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const (
3434
TLSCertFile Prefix = "--tls-cert-file="
3535
TLSPrivateKeyFile Prefix = "--tls-private-key-file="
3636
CertRotation Prefix = "--enable-cert-rotation="
37+
CADir Prefix = "--ca-dir="
3738
)
3839

3940
func (p Prefix) String() string {
@@ -45,6 +46,7 @@ const (
4546
containerNameKedaOperator = "keda-operator"
4647
containerNameMetricsServer = "keda-metrics-apiserver"
4748
containerNameAdmissionWebhooks = "keda-admission-webhooks"
49+
caCertVolPrefix = "cabundle"
4850
)
4951

5052
// ReplaceAllNamespaces returns a transformer which will traverse the unstructured content looking for map entries with
@@ -406,7 +408,7 @@ func ensureCertificatesVolumeForDeployment(containerName, configMapName, secretN
406408
var cabundleVolume corev1.Volume
407409
if containerName == containerNameKedaOperator {
408410
cabundleVolume = corev1.Volume{
409-
Name: "cabundle",
411+
Name: caCertVolPrefix,
410412
VolumeSource: corev1.VolumeSource{
411413
ConfigMap: &corev1.ConfigMapVolumeSource{
412414
LocalObjectReference: corev1.LocalObjectReference{
@@ -462,7 +464,7 @@ func ensureCertificatesVolumeForDeployment(containerName, configMapName, secretN
462464
certificatesVolumeFound := false
463465
for i := range volumes {
464466
if containerName == containerNameKedaOperator {
465-
if volumes[i].Name == "cabundle" {
467+
if volumes[i].Name == caCertVolPrefix {
466468
volumes[i] = cabundleVolume
467469
cabundleVolumeFound = true
468470
}
@@ -486,7 +488,7 @@ func ensureCertificatesVolumeForDeployment(containerName, configMapName, secretN
486488
var cabundleVolumeMount corev1.VolumeMount
487489
if containerName == containerNameKedaOperator {
488490
cabundleVolumeMount = corev1.VolumeMount{
489-
Name: "cabundle",
491+
Name: caCertVolPrefix,
490492
MountPath: "/custom/ca",
491493
}
492494
}
@@ -504,7 +506,7 @@ func ensureCertificatesVolumeForDeployment(containerName, configMapName, secretN
504506
for j := range volumeMounts {
505507
// add custom CA to Operator
506508
if containerName == containerNameKedaOperator {
507-
if volumeMounts[j].Name == "cabundle" {
509+
if volumeMounts[j].Name == caCertVolPrefix {
508510
volumeMounts[j] = cabundleVolumeMount
509511
cabundleVolumeMountFound = true
510512
}
@@ -534,59 +536,83 @@ func ensureCertificatesVolumeForDeployment(containerName, configMapName, secretN
534536
}
535537
}
536538

537-
//nolint:dupl
538-
func EnsureOpenshiftCABundleForOperatorDeployment(configMapName string, scheme *runtime.Scheme) mf.Transformer {
539-
return func(u *unstructured.Unstructured) error {
539+
// Add configmap volumes for configMapNames named cabundle0, cabundle1, etc. as /custom/ca0, /custom/ca1, etc. with
540+
// container args --ca-dir=/custom/ca0, --ca-dir=/custom/ca1, etc.
541+
func EnsureCACertsForOperatorDeployment(configMapNames []string, scheme *runtime.Scheme, logger logr.Logger) []mf.Transformer {
542+
var retval []mf.Transformer
543+
544+
var caDirs []string
545+
for i := range configMapNames {
546+
caDirs = append(caDirs, "/custom/ca"+strconv.Itoa(i))
547+
}
548+
retval = append(retval, replaceContainerArgs(caDirs, CADir, containerNameKedaOperator, scheme, logger))
549+
550+
retval = append(retval, func(u *unstructured.Unstructured) error {
540551
if u.GetKind() == "Deployment" {
541552
deploy := &appsv1.Deployment{}
542553
if err := scheme.Convert(u, deploy, nil); err != nil {
543554
return err
544555
}
545556

546557
// add Volumes referencing certs in ConfigMap
547-
cabundleVolume := corev1.Volume{
548-
Name: "cabundle",
549-
VolumeSource: corev1.VolumeSource{
550-
ConfigMap: &corev1.ConfigMapVolumeSource{
551-
LocalObjectReference: corev1.LocalObjectReference{
552-
Name: configMapName,
558+
cabundleVolumes := map[string]corev1.Volume{}
559+
cabundleVolumeMounts := map[string]corev1.VolumeMount{}
560+
for i, configMapName := range configMapNames {
561+
cabundleVolumes[configMapName] = corev1.Volume{
562+
Name: caCertVolPrefix + strconv.Itoa(i),
563+
VolumeSource: corev1.VolumeSource{
564+
ConfigMap: &corev1.ConfigMapVolumeSource{
565+
LocalObjectReference: corev1.LocalObjectReference{
566+
Name: configMapName,
567+
},
553568
},
554569
},
555-
},
556-
}
570+
}
557571

558-
volumes := deploy.Spec.Template.Spec.Volumes
559-
cabundleVolumeFound := false
560-
for i := range volumes {
561-
if volumes[i].Name == "cabundle" {
562-
volumes[i] = cabundleVolume
563-
cabundleVolumeFound = true
572+
cabundleVolumeMounts[caCertVolPrefix+strconv.Itoa(i)] = corev1.VolumeMount{
573+
Name: caCertVolPrefix + strconv.Itoa(i),
574+
MountPath: "/custom/ca" + strconv.Itoa(i),
564575
}
565576
}
566-
if !cabundleVolumeFound {
567-
deploy.Spec.Template.Spec.Volumes = append(deploy.Spec.Template.Spec.Volumes, cabundleVolume)
577+
578+
cabundleVolumeFound := map[string]bool{}
579+
var volumes []corev1.Volume
580+
for _, vol := range deploy.Spec.Template.Spec.Volumes {
581+
if caVol, ok := cabundleVolumes[vol.Name]; ok {
582+
volumes = append(volumes, caVol)
583+
cabundleVolumeFound[vol.Name] = true
584+
} else if !strings.HasPrefix(vol.Name, caCertVolPrefix) {
585+
volumes = append(volumes, vol)
586+
} // else don't copy it over since it shouldn't be there
568587
}
588+
589+
for name, vol := range cabundleVolumes {
590+
if !cabundleVolumeFound[name] {
591+
volumes = append(volumes, vol)
592+
}
593+
}
594+
deploy.Spec.Template.Spec.Volumes = volumes
595+
569596
containers := deploy.Spec.Template.Spec.Containers
570597
for i := range containers {
571598
if containers[i].Name == containerNameKedaOperator {
572599
// mount Volumes referencing certs in ConfigMap
573-
cabundleVolumeMount := corev1.VolumeMount{
574-
Name: "cabundle",
575-
MountPath: "/custom/ca",
600+
var volumeMounts []corev1.VolumeMount
601+
cabundleVolumeMountFound := map[string]bool{}
602+
for _, volMount := range containers[i].VolumeMounts {
603+
if caVolMount, ok := cabundleVolumeMounts[volMount.Name]; ok {
604+
volumeMounts = append(volumeMounts, caVolMount)
605+
cabundleVolumeMountFound[volMount.Name] = true
606+
} else if !strings.HasPrefix(volMount.Name, caCertVolPrefix) {
607+
volumeMounts = append(volumeMounts, volMount)
608+
} // else don't copy it over since it shouldn't be there
576609
}
577-
578-
volumeMounts := containers[i].VolumeMounts
579-
cabundleVolumeMountFound := false
580-
for j := range volumeMounts {
581-
if volumeMounts[j].Name == "cabundle" {
582-
volumeMounts[j] = cabundleVolumeMount
583-
cabundleVolumeMountFound = true
610+
for name, volmount := range cabundleVolumeMounts {
611+
if !cabundleVolumeMountFound[name] {
612+
volumeMounts = append(volumeMounts, volmount)
584613
}
585614
}
586-
if !cabundleVolumeMountFound {
587-
containers[i].VolumeMounts = append(containers[i].VolumeMounts, cabundleVolumeMount)
588-
}
589-
615+
containers[i].VolumeMounts = volumeMounts
590616
break
591617
}
592618
}
@@ -596,7 +622,8 @@ func EnsureOpenshiftCABundleForOperatorDeployment(configMapName string, scheme *
596622
}
597623
}
598624
return nil
599-
}
625+
})
626+
return retval
600627
}
601628

602629
func EnsurePathsToCertsInDeployment(values []string, prefixes []Prefix, scheme *runtime.Scheme, logger logr.Logger) []mf.Transformer {
@@ -607,7 +634,6 @@ func EnsurePathsToCertsInDeployment(values []string, prefixes []Prefix, scheme *
607634
return transforms
608635
}
609636

610-
//nolint:dupl
611637
func EnsureAuditPolicyConfigMapMountsVolume(configMapName string, scheme *runtime.Scheme) mf.Transformer {
612638
return func(u *unstructured.Unstructured) error {
613639
if u.GetKind() == "Deployment" {
@@ -936,6 +962,62 @@ func replaceContainerArg(value string, prefix Prefix, containerName string, sche
936962
}
937963
}
938964

965+
func replaceContainerArgs(values []string, prefix Prefix, containerName string, scheme *runtime.Scheme, logger logr.Logger) mf.Transformer {
966+
return func(u *unstructured.Unstructured) error {
967+
// this function only supports flags with a prefix
968+
if prefix == "" {
969+
return nil
970+
}
971+
changed := false
972+
if u.GetKind() == "Deployment" {
973+
deploy := &appsv1.Deployment{}
974+
if err := scheme.Convert(u, deploy, nil); err != nil {
975+
return err
976+
}
977+
containers := deploy.Spec.Template.Spec.Containers
978+
for i, container := range containers {
979+
if container.Name == containerName {
980+
argFound := false
981+
var newArgs []string
982+
for _, arg := range container.Args {
983+
if !strings.HasPrefix(arg, prefix.String()) {
984+
newArgs = append(newArgs, arg)
985+
} else {
986+
if argFound {
987+
continue
988+
}
989+
argFound = true
990+
for _, value := range values {
991+
newArgs = append(newArgs, prefix.String()+value)
992+
}
993+
}
994+
}
995+
if argFound {
996+
changed = !reflect.DeepEqual(containers[i].Args, newArgs)
997+
if changed {
998+
logger.Info("Updating args", "deployment", container.Name, "prefix", prefix.String(), "values", values)
999+
containers[i].Args = newArgs
1000+
}
1001+
} else if len(values) > 0 {
1002+
logger.Info("Adding args", "deployment", container.Name, "prefix", prefix.String(), "value", values)
1003+
for _, value := range values {
1004+
containers[i].Args = append(containers[i].Args, prefix.String()+value)
1005+
}
1006+
changed = true
1007+
}
1008+
break
1009+
}
1010+
}
1011+
if changed {
1012+
if err := scheme.Convert(deploy, u, nil); err != nil {
1013+
return err
1014+
}
1015+
}
1016+
}
1017+
return nil
1018+
}
1019+
}
1020+
9391021
func AddServiceAccountAnnotations(annotations map[string]string, scheme *runtime.Scheme) mf.Transformer {
9401022
return func(u *unstructured.Unstructured) error {
9411023
if u.GetKind() == "ServiceAccount" {

hack/kedacontroller.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ spec:
3030
# default value: rfc3339
3131
# logTimeEncoding: rfc3339
3232

33+
## CA Certificate ConfigMap Names
34+
# ConfigMaps containing PEM-encoded trusted certificate authorities (CAs).
35+
# The files from the ConfigMaps will be loaded by the KEDA operator during
36+
# start-up and will be used by scalers to authenticate TLS-enabled metrics
37+
# data sources.
38+
# default value: []
39+
# caConfigMaps: []
40+
3341
## Arbitrary arguments
3442
# Define any argument with possibility to override already existing ones.
3543
# Array of strings (format is either with prefix '--key=value' or just 'value')

0 commit comments

Comments
 (0)