-
Notifications
You must be signed in to change notification settings - Fork 89
Add support for injecting CA certificates into Secrets #265
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| package cabundleinjector | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" | ||
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| kcoreclient "k8s.io/client-go/kubernetes/typed/core/v1" | ||
| listers "k8s.io/client-go/listers/core/v1" | ||
| "k8s.io/klog/v2" | ||
|
|
||
| apiannotations "github.com/openshift/api/annotations" | ||
| "github.com/openshift/library-go/pkg/controller/factory" | ||
| "github.com/openshift/service-ca-operator/pkg/controller/api" | ||
| ) | ||
|
|
||
| type secretCABundleInjector struct { | ||
| client kcoreclient.SecretsGetter | ||
| lister listers.SecretLister | ||
| caBundle string | ||
|
|
||
| filterFn func(secret *corev1.Secret) bool | ||
| } | ||
|
|
||
| func newSecretInjectorConfig(config *caBundleInjectorConfig) controllerConfig { | ||
| informer := config.kubeInformers.Core().V1().Secrets() | ||
|
|
||
| syncer := &secretCABundleInjector{ | ||
| client: config.kubeClient.CoreV1(), | ||
| lister: informer.Lister(), | ||
| caBundle: string(config.caBundle), | ||
| } | ||
|
|
||
| return controllerConfig{ | ||
| name: "SecretCABundleInjector", | ||
| sync: syncer.Sync, | ||
| informer: informer.Informer(), | ||
| annotationsChecker: annotationsChecker( | ||
| api.InjectCABundleAnnotationName, | ||
| ), | ||
| namespaced: true, | ||
| } | ||
| } | ||
|
|
||
| func (bi *secretCABundleInjector) Sync(ctx context.Context, syncCtx factory.SyncContext) error { | ||
| namespace, name := namespacedObjectFromQueueKey(syncCtx.QueueKey()) | ||
|
|
||
| secret, err := bi.lister.Secrets(namespace).Get(name) | ||
| if apierrors.IsNotFound(err) { | ||
| return nil | ||
| } else if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if bi.filterFn != nil && !bi.filterFn(secret) { | ||
| return nil | ||
| } | ||
|
|
||
| // skip updating when the CA bundle is already there | ||
| if data, ok := secret.Data[api.InjectionDataKey]; ok && | ||
| string(data) == bi.caBundle && len(secret.Data) == 1 { | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| klog.Infof("updating secret %s/%s with the service signing CA bundle", secret.Namespace, secret.Name) | ||
|
|
||
| // make a copy to avoid mutating cache state | ||
| secretCopy := secret.DeepCopy() | ||
| secretCopy.Data = map[string][]byte{api.InjectionDataKey: []byte(bi.caBundle)} | ||
| // set the owning-component unless someone else has claimed it. | ||
| if len(secretCopy.Annotations[apiannotations.OpenShiftComponent]) == 0 { | ||
| secretCopy.Annotations[apiannotations.OpenShiftComponent] = api.OwningJiraComponent | ||
| secretCopy.Annotations[apiannotations.OpenShiftDescription] = fmt.Sprintf("Secret is added/updated with a data item containing the CA signing bundle that can be used to verify service-serving certificates") | ||
| } | ||
|
|
||
| _, err = bi.client.Secrets(secretCopy.Namespace).Update(ctx, secretCopy, metav1.UpdateOptions{}) | ||
| return err | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -106,6 +106,18 @@ var _ = g.Describe("[sig-service-ca] service-ca-operator", func() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| g.Context("ca-bundle-injection-secret", func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| g.It("[Operator][Serial] should inject CA bundle into annotated secrets", func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| testCABundleInjectionSecret(g.GinkgoTB()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| g.Context("ca-bundle-injection-secret-update", func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| g.It("[Operator][Serial] should stomp on updated data in CA bundle injection secrets", func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| testCABundleInjectionSecretUpdate(g.GinkgoTB()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| g.Context("vulnerable-legacy-ca-bundle-injection-configmap", func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| g.It("[Operator][Serial] should only inject CA bundle for specific configmap names with legacy annotation", func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| testVulnerableLegacyCABundleInjectionConfigMap(g.GinkgoTB()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -756,6 +768,149 @@ func pollForConfigMapChange(t testing.TB, client *kubernetes.Clientset, compareC | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func testCABundleInjectionSecret(t testing.TB) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adminClient, err := getKubeClient() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error getting kube client: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ns, cleanup, err := createTestNamespace(t, adminClient, "test-"+randSeq(5)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("could not create test namespace: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer cleanup() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| testSecretName := "test-secret-" + randSeq(5) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = createAnnotatedCABundleInjectionSecret(adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error creating annotated secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = pollForCABundleInjectionSecret(adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error fetching ca bundle injection secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = checkSecretCABundleInjectionData(adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error when checking ca bundle injection secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func testCABundleInjectionSecretUpdate(t testing.TB) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adminClient, err := getKubeClient() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error getting kube client: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ns, cleanup, err := createTestNamespace(t, adminClient, "test-"+randSeq(5)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("could not create test namespace: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer cleanup() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| testSecretName := "test-secret-" + randSeq(5) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = createAnnotatedCABundleInjectionSecret(adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error creating annotated secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = pollForCABundleInjectionSecret(adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error fetching ca bundle injection secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = checkSecretCABundleInjectionData(adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error when checking ca bundle injection secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = editSecretCABundleInjectionData(t, adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error editing ca bundle injection secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = checkSecretCABundleInjectionData(adminClient, testSecretName, ns.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("error when checking ca bundle injection secret: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func createAnnotatedCABundleInjectionSecret(client *kubernetes.Clientset, secretName, namespace string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| obj := &v1.Secret{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TypeMeta: metav1.TypeMeta{}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ObjectMeta: metav1.ObjectMeta{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Name: secretName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setInjectionAnnotation(&obj.ObjectMeta) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _, err := client.CoreV1().Secrets(namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func pollForCABundleInjectionSecret(client *kubernetes.Clientset, secretName, namespace string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return wait.PollImmediate(time.Second, 10*time.Second, func() (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil && errors.IsNotFound(err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func checkSecretCABundleInjectionData(client *kubernetes.Clientset, secretName, namespace string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| secret, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(secret.Data) != 1 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("unexpected ca bundle injection secret data map length: %v", len(secret.Data)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if _, ok := secret.Data[api.InjectionDataKey]; !ok { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("unexpected ca bundle injection secret data: %v", secret.Data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func editSecretCABundleInjectionData(t testing.TB, client *kubernetes.Clientset, secretName, namespace string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| secret, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| secretCopy := secret.DeepCopy() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(secretCopy.Data) != 1 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("ca bundle injection secret missing data") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| secretCopy.Data["foo"] = []byte("blah") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _, err = client.CoreV1().Secrets(namespace).Update(context.TODO(), secretCopy, metav1.UpdateOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return pollForSecretDataChange(t, client, secretCopy, "foo") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+880
to
+896
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overwrite the injected entry in the update test. This helper mutates a new Suggested fix func editSecretCABundleInjectionData(t testing.TB, client *kubernetes.Clientset, secretName, namespace string) error {
secret, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
if err != nil {
return err
}
secretCopy := secret.DeepCopy()
if len(secretCopy.Data) != 1 {
return fmt.Errorf("ca bundle injection secret missing data")
}
- secretCopy.Data["foo"] = []byte("blah")
+ secretCopy.Data[api.InjectionDataKey] = []byte("blah")
_, err = client.CoreV1().Secrets(namespace).Update(context.TODO(), secretCopy, metav1.UpdateOptions{})
if err != nil {
return err
}
- return pollForSecretDataChange(t, client, secretCopy, "foo")
+ return pollForSecretDataChange(t, client, secretCopy, api.InjectionDataKey)
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same symmetry argument as the other threads: this is a verbatim mirror of Strengthening coverage to also verify that a corrupted There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Would you like me to open a follow-up GitHub issue to track strengthening both
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes please, a follow-up issue to track strengthening both helpers symmetrically would be useful. Thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: Length of output: 76 🏁 Script executed: Length of output: 59
Note: I wasn't able to assign it automatically due to integration permission limits — you may want to assign it manually.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: Length of output: 60 🏁 Script executed: Length of output: 60
Could you please close |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func pollForSecretDataChange(t testing.TB, client *kubernetes.Clientset, compareSecret *v1.Secret, keysToChange ...string) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return wait.PollImmediate(pollInterval, rotationPollTimeout, func() (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| s, err := client.CoreV1().Secrets(compareSecret.Namespace).Get(context.TODO(), compareSecret.Name, metav1.GetOptions{}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Logf("%s: failed to get secret: %v", time.Now().Format(time.RFC1123Z), err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, key := range keysToChange { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if bytes.Equal(s.Data[key], compareSecret.Data[key]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // pollForConfigMapCAInjection polls until the configmap has CA bundle injected. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is different from pollForCABundleInjectionConfigMap which only checks if | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the configmap exists. This function validates the injection data is present. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix nil
Annotationspanic and reconsider overwriting all Secret dataTwo things to flag here:
secretCopy.Annotationsmay benilfor a freshly-created Secret. Writing to a nil map will panic:Initialize the map before writing:
Datais fully overwritten (design/semantics):secretCopy.Data = map[string][]byte{...}drops all existing keys and, together with thelen(secret.Data) == 1skip condition, forces injected Secrets to contain onlyservice-ca.crt. That’s consistent with the new e2e tests (which assertlen(Data)==1and that extra keys get “stomped”), but it is stricter than the README wording (“adds or updates a data item”) and prevents co-locating other material in the same Secret.If you want parity with a more typical “only manage
InjectionDataKey” pattern (and to better match the README text), consider instead:and relaxing the
len(secret.Data) == 1checks inSync/tests to only care thatInjectionDataKeymatches the operator’s bundle.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intentionally mirroring
configMapCABundleInjectorhere; changing either behaviour would diverge the two injectors.Annotations: the object is only handed toSyncafter passingannotationsChecker(InjectCABundleAnnotationName), which requires the annotation to be present, soAnnotationsis always non-nil in this path.configmap.gorelies on the same invariant.Data: the injector's contract (documented in the README and asserted by the existing configmap "stomp on update" e2e test) is that the target object is exclusively managed by the injector. The matching behaviour for Secrets is by design, and the newca-bundle-injection-secret-updatecase verifies it.If we want to relax this to "only manage
service-ca.crtand leave other keys alone", it should be applied symmetrically to both injectors in a separate PR.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mnencia, thank you for the detailed explanation — both points are well-reasoned.Annotations: You're right thatannotationsCheckeracts as a precondition guaranteeing the annotation (and thus the map) is already present beforeSyncis ever called. I missed that invariant.Data: The exclusive-ownership contract makes sense, and the symmetry argument withconfigMapCABundleInjectoris a strong one. Theca-bundle-injection-secret-updatee2e case clearly documents the intended behaviour.I've updated my understanding accordingly.