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

Feature flag support #7

Merged
merged 7 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
26 changes: 17 additions & 9 deletions api/v1/azureappconfigurationprovider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ import (
type AzureAppConfigurationProviderSpec struct {
// The endpoint url of AppConfiguration which should sync the configuration key-values from.
// +kubebuilder:validation:Format=uri
Endpoint *string `json:"endpoint,omitempty"`
ConnectionStringReference *string `json:"connectionStringReference,omitempty"`
Target ConfigurationGenerationParameters `json:"target"`
Auth *AzureAppConfigurationProviderAuth `json:"auth,omitempty"`
Configuration AzureAppConfigurationKeyValueOptions `json:"configuration,omitempty"`
Secret *AzureKeyVaultReference `json:"secret,omitempty"`
Endpoint *string `json:"endpoint,omitempty"`
ConnectionStringReference *string `json:"connectionStringReference,omitempty"`
Target ConfigurationGenerationParameters `json:"target"`
Auth *AzureAppConfigurationProviderAuth `json:"auth,omitempty"`
Configuration AzureAppConfigurationKeyValueOptions `json:"configuration,omitempty"`
Secret *AzureKeyVaultReference `json:"secret,omitempty"`
FeatureFlag *AzureAppConfigurationFeatureFlagOptions `json:"featureFlag,omitempty"`
}

// AzureAppConfigurationProviderStatus defines the observed state of AzureAppConfigurationProvider
Expand All @@ -51,6 +52,7 @@ type AzureAppConfigurationProviderStatus struct {
type RefreshStatus struct {
LastKeyVaultReferenceRefreshTime metav1.Time `json:"lastKeyVaultReferenceRefreshTime,omitempty"`
LastSentinelBasedRefreshTime metav1.Time `json:"lastSentinelBasedRefreshTime,omitempty"`
LastFeatureFlagRefreshTime metav1.Time `json:"lastFeatureFlagRefreshTime,omitempty"`
}

// ConfigurationGenerationParameters defines the name of target ConfigMap
Expand All @@ -64,13 +66,19 @@ type ConfigurationGenerationParameters struct {

// AzureAppConfigurationKeyValueOptions defines the options of fetching key-values from AppConfiguration.
type AzureAppConfigurationKeyValueOptions struct {
Selectors []KeyValueSelector `json:"selectors,omitempty"`
Selectors []KeyLabelSelector `json:"selectors,omitempty"`
TrimKeyPrefixes []string `json:"trimKeyPrefixes,omitempty"`
Refresh *DynamicConfigurationRefreshParameters `json:"refresh,omitempty"`
}

// KeyValueSelector defines the filters when fetching the data from Azure AppConfiguration
type KeyValueSelector struct {
// AzureAppConfigurationFeatureFlagOptions defines the options of fetching feature flags from AppConfiguration.
type AzureAppConfigurationFeatureFlagOptions struct {
Selectors []KeyLabelSelector `json:"selectors,omitempty"`
Refresh *RefreshSettings `json:"refresh,omitempty"`
}

// KeyLabelSelector defines the filters when fetching the data from Azure AppConfiguration
type KeyLabelSelector struct {
KeyFilter string `json:"keyFilter"`
LabelFilter *string `json:"labelFilter,omitempty"`
}
Expand Down
43 changes: 38 additions & 5 deletions api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 34 additions & 1 deletion config/crd/bases/azconfig.io_azureappconfigurationproviders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ spec:
type: object
selectors:
items:
description: KeyValueSelector defines the filters when fetching
description: KeyLabelSelector defines the filters when fetching
the data from Azure AppConfiguration
properties:
keyFilter:
Expand All @@ -131,6 +131,36 @@ spec:
the configuration key-values from.
format: uri
type: string
featureFlag:
description: AzureAppConfigurationFeatureFlagOptions defines the options
of fetching feature flags from AppConfiguration.
properties:
refresh:
description: Defines the settings for refresh.
properties:
enabled:
default: false
type: boolean
interval:
format: duration
type: string
required:
- interval
type: object
selectors:
items:
description: KeyLabelSelector defines the filters when fetching
the data from Azure AppConfiguration
properties:
keyFilter:
type: string
labelFilter:
type: string
required:
- keyFilter
type: object
type: array
type: object
secret:
description: AzureKeyVaultReference defines the authentication type
used to Azure KeyVault resolve KeyVaultReference
Expand Down Expand Up @@ -289,6 +319,9 @@ spec:
description: RefreshStatus defines last refresh time of configmap
and secret when dynamic feature is enabled
properties:
lastFeatureFlagRefreshTime:
format: date-time
type: string
lastKeyVaultReferenceRefreshTime:
format: date-time
type: string
Expand Down
3 changes: 2 additions & 1 deletion 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
NextSentinelBasedRefreshReconcileTime metav1.Time
NextKeyVaultReferenceRefreshReconcileTime metav1.Time
NextFeatureFlagRefreshReconcileTime metav1.Time
CachedSecretReferences map[string]loader.KeyVaultSecretUriSegment
}

Expand Down Expand Up @@ -192,7 +193,7 @@ func (reconciler *AzureAppConfigurationProviderReconciler) Reconcile(ctx context
ResolveSecretReference: nil,
}

if err := processor.PopulateSettings(&existingSecret); err != nil {
if err := processor.PopulateSettings(&existingConfigMap, &existingSecret); err != nil {
return reconciler.requeueWhenGetSettingsFailed(ctx, provider, err)
}

Expand Down
69 changes: 64 additions & 5 deletions internal/controller/appconfigurationprovider_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ var _ = Describe("AppConfiguationProvider controller", func() {
ConfigMapSettings: mapResult,
}

mockConfigurationSettings.EXPECT().CreateKeyValueSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)
mockConfigurationSettings.EXPECT().CreateTargetSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)

ctx := context.Background()
providerName := "test-appconfigurationprovider"
Expand Down Expand Up @@ -104,7 +104,7 @@ var _ = Describe("AppConfiguationProvider controller", func() {
ConfigMapSettings: mapResult,
}

mockConfigurationSettings.EXPECT().CreateKeyValueSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)
mockConfigurationSettings.EXPECT().CreateTargetSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)

ctx := context.Background()
providerName := "test-appconfigurationprovider-2"
Expand Down Expand Up @@ -155,7 +155,7 @@ var _ = Describe("AppConfiguationProvider controller", func() {
SecretSettings: mapResult,
}

mockConfigurationSettings.EXPECT().CreateKeyValueSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)
mockConfigurationSettings.EXPECT().CreateTargetSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)

ctx := context.Background()
providerName := "test-appconfigurationprovider-3"
Expand Down Expand Up @@ -218,7 +218,7 @@ var _ = Describe("AppConfiguationProvider controller", func() {
ConfigMapSettings: configMapResult,
}

mockConfigurationSettings.EXPECT().CreateKeyValueSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)
mockConfigurationSettings.EXPECT().CreateTargetSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)

ctx := context.Background()
providerName := "test-appconfigurationprovider-5"
Expand Down Expand Up @@ -287,7 +287,7 @@ var _ = Describe("AppConfiguationProvider controller", func() {
ConfigMapSettings: mapResult,
}

mockConfigurationSettings.EXPECT().CreateKeyValueSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)
mockConfigurationSettings.EXPECT().CreateTargetSettings(gomock.Any(), gomock.Any()).Return(allSettings, nil)

ctx := context.Background()
providerName := "test-appconfigurationprovider-7"
Expand Down Expand Up @@ -443,6 +443,65 @@ var _ = Describe("AppConfiguationProvider controller", func() {
Expect(verifyObject(configProviderSpec).Error()).Should(Equal("spec.target.configMapData.separator: separator field is not allowed when type is properties"))
})

It("Should return error if feature flag is set when data type is default", func() {
configMapName := "test-configmap"
configProviderSpec := acpv1.AzureAppConfigurationProviderSpec{
Endpoint: &EndpointName,
Target: acpv1.ConfigurationGenerationParameters{
ConfigMapName: configMapName,
},
FeatureFlag: &acpv1.AzureAppConfigurationFeatureFlagOptions{
Selectors: []acpv1.KeyLabelSelector{
{
KeyFilter: "testKey",
},
},
},
}

Expect(verifyObject(configProviderSpec).Error()).Should(Equal("spec.target.configMapData: target.configMapData.type must be json or yaml when FeatureFlag is set"))
})

It("Should return error if feature flag is set when data type is properties", func() {
configMapName := "test-configmap"
configProviderSpec := acpv1.AzureAppConfigurationProviderSpec{
Endpoint: &EndpointName,
Target: acpv1.ConfigurationGenerationParameters{
ConfigMapName: configMapName,
ConfigMapData: &acpv1.ConfigMapDataOptions{
Type: acpv1.Properties,
Key: "testKey",
},
},
FeatureFlag: &acpv1.AzureAppConfigurationFeatureFlagOptions{
Selectors: []acpv1.KeyLabelSelector{
{
KeyFilter: "testKeyFilter",
},
},
},
}

Expect(verifyObject(configProviderSpec).Error()).Should(Equal("spec.target.configMapData: target.configMapData.type must be json or yaml when FeatureFlag is set"))
})

It("Should return error if feature flag selector is not set", func() {
configMapName := "test-configmap"
configProviderSpec := acpv1.AzureAppConfigurationProviderSpec{
Endpoint: &EndpointName,
Target: acpv1.ConfigurationGenerationParameters{
ConfigMapName: configMapName,
ConfigMapData: &acpv1.ConfigMapDataOptions{
Type: acpv1.Json,
Key: "testKey",
},
},
FeatureFlag: &acpv1.AzureAppConfigurationFeatureFlagOptions{},
}

Expect(verifyObject(configProviderSpec).Error()).Should(Equal("spec.featureFlag.selectors: featureFlag.selectors must be specified when FeatureFlag is set"))
})

It("Should return error if both endpoint and connectionStringReference are not set", func() {
configMapName := "test-configmap"
configProviderSpec := acpv1.AzureAppConfigurationProviderSpec{
Expand Down
Loading