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

Refresh secret when data change #53

Merged
merged 9 commits into from
Jul 22, 2024

Conversation

linglingye001
Copy link
Contributor

No description provided.

@linglingye001 linglingye001 force-pushed the user/linglingye/refreshSecret branch from 637f5ae to c77306b Compare July 5, 2024 08:07
@linglingye001 linglingye001 marked this pull request as ready for review July 9, 2024 07:39
reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences[secretObj.Name].SecretResourceVersion = secretObj.ResourceVersion
reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences[secretObj.Name].UpdateNeeded = false
klog.V(5).Infof("Secret %q in %q namespace is %s", secretObj.Name, secretObj.Namespace, string(operationResult))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a v(5) log here if the secret is not updated.

@@ -28,6 +28,7 @@ type KeyVaultSecretUriSegment struct {
HostName string
Copy link
Contributor

@RichardChen820 RichardChen820 Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer just indicates the UriSegment of the Secret, how about rename this type to KeyVaultSecretMetadata ?

@@ -201,6 +210,7 @@ func (processor *AppConfigurationProviderProcessor) processSecretReferenceRefres

// Only resolve the secret references that not specified the secret version
secretReferencesToSolve := make(map[string]*loader.TargetSecretReference)
cachedSecretReferences := make(map[string]*loader.TargetSecretReference)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable seems useless, do we really need it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently ResolveSecretReferences doesn't pass/return new parameters for secretID of latest secret, it just update the secretID in kvReferencesToResolve . So need to cache secretRef before resolving.

@@ -332,3 +354,28 @@ func (processor *AppConfigurationProviderProcessor) calculateRequeueAfterInterva

return requeueAfterInterval
}

func checkSecretVersion(existingSecretReferences map[string]*loader.TargetSecretReference, latestSecretReferences map[string]*loader.TargetSecretReference) {
Copy link
Contributor

@RichardChen820 RichardChen820 Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not well self-explained, check secret version for what? I can learn what the method do after reading the implementation, but this is not a good design to a method. A good method is that you can know what the method is doing from the method name and input/output params.

@@ -70,6 +70,9 @@ func (processor *AppConfigurationProviderProcessor) processFullReconciliation()
return err
}
processor.Settings = updatedSettings
for _, reference := range updatedSettings.SecretReferences {
reference.UpdateNeeded = true
}
processor.ReconciliationState.ExistingSecretReferences = updatedSettings.SecretReferences
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the SecretReferences now involves the information of secret now, we should set it to ExistingSecretReferences in the Finish() step now, otherwise it might mismatch with the actual version in the K8s secret.

if _, ok := processor.ReconciliationState.ExistingSecretReferences[secretName]; ok {
reference.SecretResourceVersion = processor.ReconciliationState.ExistingSecretReferences[secretName].SecretResourceVersion
}
}
reconcileState.ExistingSecretReferences = keyValueRefreshedSettings.SecretReferences
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with what I comment on the line 73

@@ -70,6 +70,9 @@ func (processor *AppConfigurationProviderProcessor) processFullReconciliation()
return err
}
processor.Settings = updatedSettings
for _, reference := range updatedSettings.SecretReferences {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current all the version check are in the processor, result in we need to set the updateNeeded everywhere for the secret references, could we put the version check in the controller right before trying to update the Secret? I think that would be more clear.

@@ -201,17 +199,26 @@ func (processor *AppConfigurationProviderProcessor) processSecretReferenceRefres

// Only resolve the secret references that not specified the secret version
secretReferencesToSolve := make(map[string]*loader.TargetSecretReference)
cachedSecretReferences := make(map[string]*loader.TargetSecretReference)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is really confusion-leading, what you do here is to deep copy the reconcileState.ExistingSecretReferences, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, how about copiedSecretReferences?

@@ -50,7 +50,7 @@ type TargetKeyValueSettings struct {

type TargetSecretReference struct {
Type corev1.SecretType
UriSegments map[string]KeyVaultSecretUriSegment
UriSegments map[string]KeyVaultSecretMetadata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name UriSegments does not make sense now, how about updated to SecretMetadatas

@@ -302,3 +302,41 @@ func verifySelectorObject(selector acpv1.Selector) error {

return nil
}

func checkAndUpdateSecretRef(existingSecretReferences map[string]*loader.TargetSecretReference, latestSecretReferences map[string]*loader.TargetSecretReference, shouldReconcile bool) map[string]bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we let the method check secretUpdateNeeded one by one?

Type: secret.Type,
}
secretToUpdate := checkAndUpdateSecretRef(reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences, processor.Settings.SecretReferences, processor.ShouldReconcile)
for secretName, secret := range processor.Settings.SecretSettings {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about doing this:

for secretName, secret := range processor.Settings.SecretSettings {
   if (!shouldCreateOrUpdate()){
     continue;
   }

    // do create or update
     .....
}

if _, ok := reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences[secretName]; ok {
processor.Settings.SecretReferences[secretName].SecretResourceVersion = reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences[secretName].SecretResourceVersion
}
klog.V(5).Infof("Skip updating the secret %q in %q namespace since its data is up-to-date", secretName, provider.Namespace)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip updating the secret %q in %q namespace since data is not changed.

Type: secret.Type,
}
for secretName, secret := range processor.Settings.SecretSettings {
if processor.ShouldReconcile || shouldCreateOrUpdate(reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences, secretName, processor.Settings.SecretReferences[secretName]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is too long, break it into two lines.

Type: secret.Type,
}
for secretName, secret := range processor.Settings.SecretSettings {
if processor.ShouldReconcile || shouldCreateOrUpdate(reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences, secretName, processor.Settings.SecretReferences[secretName]) {
Copy link
Contributor

@RichardChen820 RichardChen820 Jul 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I know there's much difference in logic, but I think doing this is better for readability.

  1. check shouldReconcile in the shouldCreateOrUpdate method

  2. Change the logic to

if !shouldCreateOrUpdate(xxx) {
    // do something
  continue
}

// do something

@@ -728,6 +763,7 @@ var _ = Describe("AppConfiguationProvider controller", func() {
Expect(string(secret.Data["testSecretKey3"])).Should(Equal("newTestSecretValue3"))
Expect(secret.Type).Should(Equal(corev1.SecretType("Opaque")))
})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: extra line

SecretReferences: mockedSecretReference,
}

mockConfigurationSettings.EXPECT().ResolveSecretReferences(gomock.Any(), gomock.Any(), gomock.Any()).Return(newTargetSettings, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't quite sure why check the refresh two times. And seems you didn't add the mocks for the secretId, how it can be refreshed without checking secretID?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test covers 2 refresh scenarios, the first time refresh when secretMetaData change, the second time not refresh when data not change. The first time refresh because mocked secretReferences length changed. Will add secretID.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got you, and please add the scenario for the secretId check

@@ -302,3 +302,25 @@ func verifySelectorObject(selector acpv1.Selector) error {

return nil
}

func shouldCreateOrUpdate(existingSecretReferences map[string]*loader.TargetSecretReference, secretName string, secretReference *loader.TargetSecretReference) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not pass the existing and target secretMetadata as input?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Target secretMetadata may not exist in existingSecretReferences map

@@ -50,7 +50,7 @@ type TargetKeyValueSettings struct {

type TargetSecretReference struct {
Type corev1.SecretType
UriSegments map[string]KeyVaultSecretUriSegment
SecretMetadata map[string]KeyVaultSecretMetadata
Copy link
Contributor

@RichardChen820 RichardChen820 Jul 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecretMetadatas, should use the plural

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MetaData is plural noun, no need s

@@ -425,7 +425,15 @@ func (reconciler *AzureAppConfigurationProviderReconciler) createOrUpdateSecrets
Namespace: provider.Namespace,
}

for secretName, secret := range settings.SecretSettings {
for secretName, secret := range processor.Settings.SecretSettings {
if !shouldCreateOrUpdate(reconciler.ProvidersReconcileState[namespacedName].ExistingSecretReferences, secretName, processor.Settings.SecretReferences[secretName], processor.ShouldReconcile) {
Copy link
Contributor

@RichardChen820 RichardChen820 Jul 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just pass the processor?

}, timeout, interval).Should(BeTrue())

Expect(secret.Namespace).Should(Equal(ProviderNamespace))
Expect(string(secret.Data["testSecretKey"])).Should(Equal("newTestSecretValue"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also verify the secret with unchanged ID would not be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line746-763 include scenario when secretID not changed.

@@ -245,7 +245,7 @@ func (csl *ConfigurationSettingLoader) CreateKeyValueSettings(ctx context.Contex
}

currentUrl := *setting.Value
secretUriSegment, err := parse(currentUrl)
secretMetadata, err := parse(currentUrl)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line and line200 are all using the secretMatadata word, that indeed causes confusion. I would suggest SecretMatadatas(regardless the plural and singular same form) or SecretsMetadata in TargetSecretReference

for secretName, reference := range reconcileState.ExistingSecretReferences {
for key, uriSegment := range reference.UriSegments {
for key, uriSegment := range reference.SecretsMetadata {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uriSegment -> secretMetadata

if _, ok := existingSecretReferences[secretName].SecretsMetadata[key]; !ok {
return true
}
if existingSecretReferences[secretName].SecretsMetadata[key].SecretId != nil && secretMetadata.SecretId != nil &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Break this long check into multiple lines

@@ -386,7 +387,10 @@ func (csl *ConfigurationSettingLoader) ResolveSecretReferences(
}
lock.Lock()
defer lock.Unlock()
resolvedSecretReferences[name].Data[currentKey] = []byte(*resolvedSecret.Value)
resolvedSecrets[name].Data[currentKey] = []byte(*resolvedSecret.Value)
currentUriSegment := targetSecretReference.SecretMetadata[currentKey]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is not accurate now, currentSecretMetadata

@linglingye001 linglingye001 changed the base branch from release/v2/preview to release/v2/stable July 22, 2024 08:09
@linglingye001 linglingye001 merged commit 0d28b27 into release/v2/stable Jul 22, 2024
3 checks passed
@linglingye001 linglingye001 deleted the user/linglingye/refreshSecret branch July 22, 2024 08:20
linglingye001 added a commit that referenced this pull request Nov 8, 2024
* resolve confict

* update

* resolve comments

* add test

* update

* update

* update

* rename secretMetadata to secretsMetadata

* update
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants