Skip to content
Open
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
2 changes: 2 additions & 0 deletions internal/controller/appconfigurationprovider_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type ReconciliationState struct {
SentinelETags map[acpv1.Sentinel]*azcore.ETag
KeyValueETags map[acpv1.ComparableSelector][]*azcore.ETag
FeatureFlagETags map[acpv1.ComparableSelector][]*azcore.ETag
EnhancedFeatureFlagETags map[acpv1.ComparableSelector][]*azcore.ETag
ExistingK8sSecrets map[string]*loader.TargetK8sSecretMetadata
NextKeyValueRefreshReconcileTime metav1.Time
NextSecretReferenceRefreshReconcileTime metav1.Time
Expand Down Expand Up @@ -149,6 +150,7 @@ func (reconciler *AzureAppConfigurationProviderReconciler) Reconcile(ctx context
SentinelETags: make(map[acpv1.Sentinel]*azcore.ETag),
KeyValueETags: make(map[acpv1.ComparableSelector][]*azcore.ETag),
FeatureFlagETags: make(map[acpv1.ComparableSelector][]*azcore.ETag),
EnhancedFeatureFlagETags: make(map[acpv1.ComparableSelector][]*azcore.ETag),
ExistingK8sSecrets: make(map[string]*loader.TargetK8sSecretMetadata),
ClientManager: nil,
}
Expand Down
61 changes: 37 additions & 24 deletions internal/controller/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,20 @@ type AppConfigurationProviderProcessor struct {
}

type RefreshOptions struct {
keyValueRefreshEnabled bool
secretReferenceRefreshEnabled bool
secretReferenceRefreshNeeded bool
featureFlagRefreshEnabled bool
featureFlagRefreshNeeded bool
ConfigMapSettingPopulated bool
SecretSettingPopulated bool
sentinelChanged bool
keyValuePageETagsChanged bool
updatedSentinelETags map[acpv1.Sentinel]*azcore.ETag
updatedKeyValueETags map[acpv1.ComparableSelector][]*azcore.ETag
updatedFeatureFlagETags map[acpv1.ComparableSelector][]*azcore.ETag
keyValueRefreshEnabled bool
secretReferenceRefreshEnabled bool
secretReferenceRefreshNeeded bool
featureFlagRefreshEnabled bool
featureFlagRefreshNeeded bool
enhancedFeatureFlagRefreshNeeded bool
ConfigMapSettingPopulated bool
SecretSettingPopulated bool
sentinelChanged bool
keyValuePageETagsChanged bool
updatedSentinelETags map[acpv1.Sentinel]*azcore.ETag
updatedKeyValueETags map[acpv1.ComparableSelector][]*azcore.ETag
updatedFeatureFlagETags map[acpv1.ComparableSelector][]*azcore.ETag
updatedEnhancedFeatureFlagETags map[acpv1.ComparableSelector][]*azcore.ETag
}

func (processor *AppConfigurationProviderProcessor) PopulateSettings(existingConfigMap *corev1.ConfigMap, existingSecrets map[string]corev1.Secret) error {
Expand Down Expand Up @@ -75,6 +77,7 @@ func (processor *AppConfigurationProviderProcessor) processFullReconciliation()
processor.RefreshOptions.ConfigMapSettingPopulated = true
processor.RefreshOptions.updatedKeyValueETags = updatedSettings.KeyValueETags
processor.RefreshOptions.updatedFeatureFlagETags = updatedSettings.FeatureFlagETags
processor.RefreshOptions.updatedEnhancedFeatureFlagETags = updatedSettings.EnhancedFeatureFlagETags
processor.RefreshOptions.updatedSentinelETags = updatedSettings.SentinelETags
if processor.Provider.Spec.Secret != nil {
processor.RefreshOptions.SecretSettingPopulated = true
Expand Down Expand Up @@ -112,7 +115,11 @@ func (processor *AppConfigurationProviderProcessor) processFeatureFlagRefresh(ex
return err
}

if !processor.RefreshOptions.featureFlagRefreshNeeded {
if processor.RefreshOptions.enhancedFeatureFlagRefreshNeeded, err = (processor.Retriever).CheckIfEnhancedFeatureFlagsChanged(processor.Context, reconcileState.EnhancedFeatureFlagETags); err != nil {
return err
}

Comment on lines +118 to +121
if !(processor.RefreshOptions.featureFlagRefreshNeeded || processor.RefreshOptions.enhancedFeatureFlagRefreshNeeded) {
reconcileState.NextFeatureFlagRefreshReconcileTime = nextFeatureFlagRefreshReconcileTime
return nil
}
Expand All @@ -123,6 +130,7 @@ func (processor *AppConfigurationProviderProcessor) processFeatureFlagRefresh(ex
}

processor.RefreshOptions.updatedFeatureFlagETags = featureFlagRefreshedSettings.FeatureFlagETags
processor.RefreshOptions.updatedEnhancedFeatureFlagETags = featureFlagRefreshedSettings.EnhancedFeatureFlagETags
processor.Settings = featureFlagRefreshedSettings
processor.RefreshOptions.ConfigMapSettingPopulated = true
// Update next refresh time only if settings updated successfully
Expand Down Expand Up @@ -323,6 +331,10 @@ func (processor *AppConfigurationProviderProcessor) Finish() (ctrl.Result, error
processor.ReconciliationState.FeatureFlagETags = processor.RefreshOptions.updatedFeatureFlagETags
}

if processor.RefreshOptions.updatedEnhancedFeatureFlagETags != nil {
processor.ReconciliationState.EnhancedFeatureFlagETags = processor.RefreshOptions.updatedEnhancedFeatureFlagETags
}

if processor.ShouldReconcile {
processor.ReconciliationState.SentinelETags = processor.RefreshOptions.updatedSentinelETags
}
Expand All @@ -348,7 +360,7 @@ func (processor *AppConfigurationProviderProcessor) Finish() (ctrl.Result, error
processor.Provider.Status.RefreshStatus.LastKeyVaultReferenceRefreshTime = processor.CurrentTime
}
// Update provider last feature flag refresh time
if processor.RefreshOptions.featureFlagRefreshNeeded {
if processor.RefreshOptions.featureFlagRefreshNeeded || processor.RefreshOptions.enhancedFeatureFlagRefreshNeeded {
processor.Provider.Status.RefreshStatus.LastFeatureFlagRefreshTime = processor.CurrentTime
}
// At least one dynamic feature is enabled, requeueAfterInterval need be recalculated
Expand All @@ -361,16 +373,17 @@ func (processor *AppConfigurationProviderProcessor) Finish() (ctrl.Result, error

func NewRefreshOptions() *RefreshOptions {
return &RefreshOptions{
keyValueRefreshEnabled: false,
secretReferenceRefreshEnabled: false,
secretReferenceRefreshNeeded: false,
featureFlagRefreshEnabled: false,
featureFlagRefreshNeeded: false,
ConfigMapSettingPopulated: false,
SecretSettingPopulated: false,
sentinelChanged: false,
keyValuePageETagsChanged: false,
updatedSentinelETags: make(map[acpv1.Sentinel]*azcore.ETag),
keyValueRefreshEnabled: false,
secretReferenceRefreshEnabled: false,
secretReferenceRefreshNeeded: false,
featureFlagRefreshEnabled: false,
featureFlagRefreshNeeded: false,
enhancedFeatureFlagRefreshNeeded: false,
ConfigMapSettingPopulated: false,
SecretSettingPopulated: false,
sentinelChanged: false,
keyValuePageETagsChanged: false,
updatedSentinelETags: make(map[acpv1.Sentinel]*azcore.ETag),
}
}

Expand Down
3 changes: 3 additions & 0 deletions internal/controller/processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ var _ = Describe("AppConfiguationProvider processor", func() {
BeforeEach(func() {
mockCtrl = gomock.NewController(GinkgoT())
mockConfigurationSettings = mocks.NewMockConfigurationSettingsRetriever(mockCtrl)
// The dedicated feature flag endpoint is checked whenever the classic feature flag page ETags
// are unchanged; default to reporting no change so existing scenarios are unaffected.
mockConfigurationSettings.EXPECT().CheckIfEnhancedFeatureFlagsChanged(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes()
})

AfterEach(func() {
Expand Down
3 changes: 3 additions & 0 deletions internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ var _ = BeforeSuite(func() {

mockCtrl = gomock.NewController(GinkgoT())
mockConfigurationSettings = mocks.NewMockConfigurationSettingsRetriever(mockCtrl)
// The dedicated feature flag endpoint is checked whenever the classic feature flag page ETags
// are unchanged; default to reporting no change so existing scenarios are unaffected.
mockConfigurationSettings.EXPECT().CheckIfEnhancedFeatureFlagsChanged(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes()

err = (&AzureAppConfigurationProviderReconciler{
Client: k8sManager.GetClient(),
Expand Down
92 changes: 92 additions & 0 deletions internal/loader/app_configuration_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package loader

import (
"context"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
azappconfig "github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig/v2"
)

// AppConfigurationClient abstracts the Azure App Configuration operations used by the provider.
type AppConfigurationClient interface {
// Key-value configuration operations.
NewListSettingsPager(selector azappconfig.SettingSelector, options *azappconfig.ListSettingsOptions) *runtime.Pager[azappconfig.ListSettingsPageResponse]
GetSetting(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error)
GetSnapshot(ctx context.Context, snapshotName string, options *azappconfig.GetSnapshotOptions) (azappconfig.GetSnapshotResponse, error)
NewListSettingsForSnapshotPager(snapshotName string, options *azappconfig.ListSettingsForSnapshotOptions) *runtime.Pager[azappconfig.ListSettingsForSnapshotResponse]

// Feature flag operations served by the dedicated feature flag endpoint.
NewListFeatureFlagsPager(selector azappconfig.FeatureFlagSelector, options *azappconfig.ListFeatureFlagsOptions) *runtime.Pager[azappconfig.ListFeatureFlagsPageResponse]

Check failure on line 23 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.ListFeatureFlagsPageResponse

Check failure on line 23 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.ListFeatureFlagsOptions

Check failure on line 23 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.FeatureFlagSelector

Check failure on line 23 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.ListFeatureFlagsPageResponse

Check failure on line 23 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.ListFeatureFlagsOptions

Check failure on line 23 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.FeatureFlagSelector
}

type appConfigurationClient struct {
configurationClient *azappconfig.Client
featureFlagClient *azappconfig.FeatureFlagClient

Check failure on line 28 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.FeatureFlagClient

Check failure on line 28 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.FeatureFlagClient
}

func NewAppConfigurationClient(endpoint string, credential azcore.TokenCredential, options *azappconfig.ClientOptions) (AppConfigurationClient, error) {
configurationClient, err := azappconfig.NewClient(endpoint, credential, options)
if err != nil {
return nil, err
}

featureFlagClient, err := azappconfig.NewFeatureFlagClient(endpoint, credential, featureFlagClientOptions(options))
if err != nil {
return nil, err
}

return &appConfigurationClient{
configurationClient: configurationClient,
featureFlagClient: featureFlagClient,
}, nil
}

func NewAppConfigurationClientFromConnectionString(connectionString string, options *azappconfig.ClientOptions) (AppConfigurationClient, error) {
configurationClient, err := azappconfig.NewClientFromConnectionString(connectionString, options)
if err != nil {
return nil, err
}

featureFlagClient, err := azappconfig.NewFeatureFlagClientFromConnectionString(connectionString, featureFlagClientOptions(options))
if err != nil {
return nil, err
}

return &appConfigurationClient{
configurationClient: configurationClient,
featureFlagClient: featureFlagClient,
}, nil
}

// featureFlagClientOptions mirrors the configuration client options onto feature flag client options
func featureFlagClientOptions(options *azappconfig.ClientOptions) *azappconfig.FeatureFlagClientOptions {

Check failure on line 66 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.FeatureFlagClientOptions

Check failure on line 66 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.FeatureFlagClientOptions
if options == nil {
return nil
}

return &azappconfig.FeatureFlagClientOptions{ClientOptions: options.ClientOptions}
}

func (c *appConfigurationClient) NewListSettingsPager(selector azappconfig.SettingSelector, options *azappconfig.ListSettingsOptions) *runtime.Pager[azappconfig.ListSettingsPageResponse] {
return c.configurationClient.NewListSettingsPager(selector, options)
}

func (c *appConfigurationClient) GetSetting(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) {
return c.configurationClient.GetSetting(ctx, key, options)
}

func (c *appConfigurationClient) GetSnapshot(ctx context.Context, snapshotName string, options *azappconfig.GetSnapshotOptions) (azappconfig.GetSnapshotResponse, error) {
return c.configurationClient.GetSnapshot(ctx, snapshotName, options)
}

func (c *appConfigurationClient) NewListSettingsForSnapshotPager(snapshotName string, options *azappconfig.ListSettingsForSnapshotOptions) *runtime.Pager[azappconfig.ListSettingsForSnapshotResponse] {
return c.configurationClient.NewListSettingsForSnapshotPager(snapshotName, options)
}

func (c *appConfigurationClient) NewListFeatureFlagsPager(selector azappconfig.FeatureFlagSelector, options *azappconfig.ListFeatureFlagsOptions) *runtime.Pager[azappconfig.ListFeatureFlagsPageResponse] {

Check failure on line 90 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.ListFeatureFlagsPageResponse

Check failure on line 90 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.ListFeatureFlagsOptions

Check failure on line 90 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / lint

undefined: azappconfig.FeatureFlagSelector

Check failure on line 90 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.ListFeatureFlagsPageResponse

Check failure on line 90 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.ListFeatureFlagsOptions

Check failure on line 90 in internal/loader/app_configuration_client.go

View workflow job for this annotation

GitHub Actions / build

undefined: azappconfig.FeatureFlagSelector
return c.featureFlagClient.NewListFeatureFlagsPager(selector, options)
}
Loading
Loading