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

Update secrets interface to work for CEL export #4383

Merged
merged 1 commit into from
Oct 30, 2024
Merged
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
50 changes: 35 additions & 15 deletions v2/api/apimanagement/customizations/subscription_extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,31 @@ import (
"github.com/go-logr/logr"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/conversion"

apimanagement "github.com/Azure/azure-service-operator/v2/api/apimanagement/v1api20220801/storage"
"github.com/Azure/azure-service-operator/v2/internal/genericarmclient"
. "github.com/Azure/azure-service-operator/v2/internal/logging"
"github.com/Azure/azure-service-operator/v2/internal/set"
"github.com/Azure/azure-service-operator/v2/internal/util/to"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime/secrets"
)

var _ genruntime.KubernetesExporter = &SubscriptionExtension{}
const (
primaryKey = "primaryKey"
secondaryKey = "secondaryKey"
)

var _ genruntime.KubernetesSecretExporter = &SubscriptionExtension{}

func (ext *SubscriptionExtension) ExportKubernetesResources(
func (ext *SubscriptionExtension) ExportKubernetesSecrets(
ctx context.Context,
obj genruntime.MetaObject,
additionalSecrets set.Set[string],
armClient *genericarmclient.GenericClient,
log logr.Logger,
) ([]client.Object, error) {
) (*genruntime.KubernetesSecretExportResult, error) {
// This has to be the current hub storage version. It will need to be updated
// if the hub storage version changes.
typedObj, ok := obj.(*apimanagement.Subscription)
Expand All @@ -42,8 +48,9 @@ func (ext *SubscriptionExtension) ExportKubernetesResources(
// the hub type has been changed but this extension has not
var _ conversion.Hub = typedObj

hasSecrets := secretsSpecified(typedObj)
if !hasSecrets {
primarySecrets := secretsSpecified(typedObj)
requestedSecrets := set.Union(primarySecrets, additionalSecrets)
if len(requestedSecrets) == 0 {
log.V(Debug).Info("No secrets retrieval to perform as operatorSpec is empty")
return nil, nil
}
Expand All @@ -60,7 +67,7 @@ func (ext *SubscriptionExtension) ExportKubernetesResources(

// Only bother calling ListSecrets if there are secrets to retrieve
var s armapimanagement.SubscriptionKeysContract
if hasSecrets {
if len(requestedSecrets) > 0 {
subscription := id.SubscriptionID
// Using armClient.ClientOptions() here ensures we share the same HTTP connection, so this is not opening a new
// connection each time through
Expand All @@ -79,32 +86,45 @@ func (ext *SubscriptionExtension) ExportKubernetesResources(
s = resp.SubscriptionKeysContract
}

resolvedSecrets := map[string]string{}
if to.Value(s.PrimaryKey) != "" {
resolvedSecrets[primaryKey] = to.Value(s.PrimaryKey)
}
if to.Value(s.SecondaryKey) != "" {
resolvedSecrets[secondaryKey] = to.Value(s.SecondaryKey)
}
secretSlice, err := secretsToWrite(typedObj, s)
if err != nil {
return nil, err
}

return secrets.SliceToClientObjectSlice(secretSlice), nil
return &genruntime.KubernetesSecretExportResult{
Objs: secrets.SliceToClientObjectSlice(secretSlice),
RawSecrets: secrets.SelectSecrets(additionalSecrets, resolvedSecrets),
}, nil
}

func secretsSpecified(obj *apimanagement.Subscription) bool {
func secretsSpecified(obj *apimanagement.Subscription) set.Set[string] {
if obj.Spec.OperatorSpec == nil || obj.Spec.OperatorSpec.Secrets == nil {
return false
return nil
}

hasSecrets := false
secrets := obj.Spec.OperatorSpec.Secrets
if secrets.PrimaryKey != nil || secrets.SecondaryKey != nil {
hasSecrets = true
result := make(set.Set[string])
if secrets.PrimaryKey != nil {
result.Add(primaryKey)
}
if secrets.SecondaryKey != nil {
result.Add(secondaryKey)
}

return hasSecrets
return result
}

func secretsToWrite(obj *apimanagement.Subscription, s armapimanagement.SubscriptionKeysContract) ([]*v1.Secret, error) {
operatorSpecSecrets := obj.Spec.OperatorSpec.Secrets
if operatorSpecSecrets == nil {
return nil, errors.Errorf("unexpected nil operatorspec")
return nil, nil
}

collector := secrets.NewCollector(obj.Namespace)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*/

package customizations

import (
"reflect"
"testing"

. "github.com/onsi/gomega"

apimanagement "github.com/Azure/azure-service-operator/v2/api/apimanagement/v1api20220801/storage"
"github.com/Azure/azure-service-operator/v2/internal/reflecthelpers"
testreflect "github.com/Azure/azure-service-operator/v2/internal/testcommon/reflect"
)

func Test_SecretsSpecified_AllSecretsSpecifiedAllSecretsReturned(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

secrets := &apimanagement.SubscriptionOperatorSecrets{}
testreflect.PopulateStruct(secrets)

obj := &apimanagement.Subscription{
Spec: apimanagement.Subscription_Spec{
OperatorSpec: &apimanagement.SubscriptionOperatorSpec{
Secrets: secrets,
},
},
}
secretNames := secretsSpecified(obj)
expectedTags := reflecthelpers.GetJSONTags(reflect.TypeOf(apimanagement.SubscriptionOperatorSecrets{}))
expectedTags.Remove("$propertyBag")

g.Expect(expectedTags).To(Equal(secretNames))
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,41 @@ import (
"github.com/go-logr/logr"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/conversion"

storage "github.com/Azure/azure-service-operator/v2/api/appconfiguration/v1api20220501/storage"
"github.com/Azure/azure-service-operator/v2/internal/genericarmclient"
. "github.com/Azure/azure-service-operator/v2/internal/logging"
"github.com/Azure/azure-service-operator/v2/internal/set"
"github.com/Azure/azure-service-operator/v2/internal/util/to"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime/secrets"
)

var _ genruntime.KubernetesExporter = &ConfigurationStoreExtension{}
const (
primaryKeyID = "primaryKeyID"
secondaryKeyID = "secondaryKeyID"
primaryReadOnlyKeyID = "primaryReadOnlyKeyID"
secondaryReadOnlyKeyID = "secondaryReadOnlyKeyID"
primaryKey = "primaryKey"
secondaryKey = "secondaryKey"
primaryReadOnlyKey = "primaryReadOnlyKey"
secondaryReadOnlyKey = "secondaryReadOnlyKey"
primaryConnectionString = "primaryConnectionString"
secondaryConnectionString = "secondaryConnectionString"
primaryReadOnlyConnectionString = "primaryReadOnlyConnectionString"
secondaryReadOnlyConnectionString = "secondaryReadOnlyConnectionString"
)

var _ genruntime.KubernetesSecretExporter = &ConfigurationStoreExtension{}

func (ext *ConfigurationStoreExtension) ExportKubernetesResources(
func (ext *ConfigurationStoreExtension) ExportKubernetesSecrets(
ctx context.Context,
obj genruntime.MetaObject,
additionalSecrets set.Set[string],
armClient *genericarmclient.GenericClient,
log logr.Logger,
) ([]client.Object, error) {
) (*genruntime.KubernetesSecretExportResult, error) {
// This has to be the current hub storage version. It will need to be updated
// if the hub storage version changes.
typedObj, ok := obj.(*storage.ConfigurationStore)
Expand All @@ -42,8 +59,9 @@ func (ext *ConfigurationStoreExtension) ExportKubernetesResources(
// the hub type has been changed but this extension has not
var _ conversion.Hub = typedObj

hasSecrets := secretsSpecified(typedObj)
if !hasSecrets {
primarySecrets := secretsSpecified(typedObj)
requestedSecrets := set.Union(primarySecrets, additionalSecrets)
if len(requestedSecrets) == 0 {
log.V(Debug).Info("No secrets retrieval to perform as operatorSpec is empty")
return nil, nil
}
Expand All @@ -55,7 +73,7 @@ func (ext *ConfigurationStoreExtension) ExportKubernetesResources(

keys := make(map[string]armappconfiguration.APIKey)
// Only bother calling ListKeys if there are secrets to retrieve
if hasSecrets {
if len(requestedSecrets) > 0 {
subscription := id.SubscriptionID
// Using armClient.ClientOptions() here ensures we share the same HTTP connection, so this is not opening a new
// connection each time through
Expand All @@ -70,49 +88,71 @@ func (ext *ConfigurationStoreExtension) ExportKubernetesResources(
pager = confClient.NewListKeysPager(id.ResourceGroupName, typedObj.AzureName(), nil)
for pager.More() {
resp, err = pager.NextPage(ctx)
if err != nil {
return nil, errors.Wrapf(err, "failed to retreive response")
}
addSecretsToMap(resp.Value, keys)

}
if err != nil {
return nil, errors.Wrapf(err, "failed to retreive response")
}
if err != nil {
return nil, errors.Wrapf(err, "failed listing keys")
}

}

secretSlice, err := secretsToWrite(typedObj, keys)
if err != nil {
return nil, err
}

return secrets.SliceToClientObjectSlice(secretSlice), nil
resolvedSecrets := makeResolvedSecretsMap(keys)

return &genruntime.KubernetesSecretExportResult{
Objs: secrets.SliceToClientObjectSlice(secretSlice),
RawSecrets: secrets.SelectSecrets(additionalSecrets, resolvedSecrets),
}, nil
}

func secretsSpecified(obj *storage.ConfigurationStore) bool {
func secretsSpecified(obj *storage.ConfigurationStore) set.Set[string] {
if obj.Spec.OperatorSpec == nil || obj.Spec.OperatorSpec.Secrets == nil {
return false
return nil
}

secrets := obj.Spec.OperatorSpec.Secrets

if secrets.PrimaryKeyID != nil ||
secrets.SecondaryKeyID != nil ||
secrets.PrimaryReadOnlyKeyID != nil ||
secrets.SecondaryReadOnlyKeyID != nil ||
secrets.PrimaryKey != nil ||
secrets.SecondaryKey != nil ||
secrets.PrimaryReadOnlyKey != nil ||
secrets.SecondaryReadOnlyKey != nil ||
secrets.PrimaryConnectionString != nil ||
secrets.SecondaryConnectionString != nil ||
secrets.PrimaryReadOnlyConnectionString != nil ||
secrets.SecondaryReadOnlyConnectionString != nil {
return true
}

return false
result := make(set.Set[string])
if secrets.PrimaryKeyID != nil {
result.Add(primaryKeyID)
}
if secrets.SecondaryKeyID != nil {
result.Add(secondaryKeyID)
}
if secrets.PrimaryReadOnlyKeyID != nil {
result.Add(primaryReadOnlyKeyID)
}
if secrets.SecondaryReadOnlyKeyID != nil {
result.Add(secondaryReadOnlyKeyID)
}
if secrets.PrimaryKey != nil {
result.Add(primaryKey)
}
if secrets.SecondaryKey != nil {
result.Add(secondaryKey)
}
if secrets.PrimaryReadOnlyKey != nil {
result.Add(primaryReadOnlyKey)
}
if secrets.SecondaryReadOnlyKey != nil {
result.Add(secondaryReadOnlyKey)
}
if secrets.PrimaryConnectionString != nil {
result.Add(primaryConnectionString)
}
if secrets.SecondaryConnectionString != nil {
result.Add(secondaryConnectionString)
}
if secrets.PrimaryReadOnlyConnectionString != nil {
result.Add(primaryReadOnlyConnectionString)
}
if secrets.SecondaryReadOnlyConnectionString != nil {
result.Add(secondaryReadOnlyConnectionString)
}
return result
}

func addSecretsToMap(keys []*armappconfiguration.APIKey, result map[string]armappconfiguration.APIKey) {
Expand All @@ -127,37 +167,70 @@ func addSecretsToMap(keys []*armappconfiguration.APIKey, result map[string]armap
func secretsToWrite(obj *storage.ConfigurationStore, keys map[string]armappconfiguration.APIKey) ([]*v1.Secret, error) {
operatorSpecSecrets := obj.Spec.OperatorSpec.Secrets
if operatorSpecSecrets == nil {
return nil, errors.Errorf("unexpected nil operatorspec")
return nil, nil
}

collector := secrets.NewCollector(obj.Namespace)
primary, ok := keys["Primary"]
if ok {
collector.AddValue(operatorSpecSecrets.PrimaryConnectionString, *primary.ConnectionString)
collector.AddValue(operatorSpecSecrets.PrimaryKeyID, *primary.ID)
collector.AddValue(operatorSpecSecrets.PrimaryKey, *primary.Value)
collector.AddValue(operatorSpecSecrets.PrimaryConnectionString, to.Value(primary.ConnectionString))
collector.AddValue(operatorSpecSecrets.PrimaryKeyID, to.Value(primary.ID))
collector.AddValue(operatorSpecSecrets.PrimaryKey, to.Value(primary.Value))
}

primaryReadOnly, ok := keys["Primary Read Only"]
if ok {
collector.AddValue(operatorSpecSecrets.PrimaryReadOnlyConnectionString, *primaryReadOnly.ConnectionString)
collector.AddValue(operatorSpecSecrets.PrimaryReadOnlyKeyID, *primaryReadOnly.ID)
collector.AddValue(operatorSpecSecrets.PrimaryReadOnlyKey, *primaryReadOnly.Value)
collector.AddValue(operatorSpecSecrets.PrimaryReadOnlyConnectionString, to.Value(primaryReadOnly.ConnectionString))
collector.AddValue(operatorSpecSecrets.PrimaryReadOnlyKeyID, to.Value(primaryReadOnly.ID))
collector.AddValue(operatorSpecSecrets.PrimaryReadOnlyKey, to.Value(primaryReadOnly.Value))
}

secondary, ok := keys["Secondary"]
if ok {
collector.AddValue(operatorSpecSecrets.SecondaryConnectionString, *secondary.ConnectionString)
collector.AddValue(operatorSpecSecrets.SecondaryKeyID, *secondary.ID)
collector.AddValue(operatorSpecSecrets.SecondaryKey, *secondary.Value)
collector.AddValue(operatorSpecSecrets.SecondaryConnectionString, to.Value(secondary.ConnectionString))
collector.AddValue(operatorSpecSecrets.SecondaryKeyID, to.Value(secondary.ID))
collector.AddValue(operatorSpecSecrets.SecondaryKey, to.Value(secondary.Value))
}

secondaryReadOnly, ok := keys["Secondary Read Only"]
if ok {
collector.AddValue(operatorSpecSecrets.SecondaryReadOnlyConnectionString, *secondaryReadOnly.ConnectionString)
collector.AddValue(operatorSpecSecrets.SecondaryReadOnlyKeyID, *secondaryReadOnly.ID)
collector.AddValue(operatorSpecSecrets.SecondaryReadOnlyKey, *secondaryReadOnly.Value)
collector.AddValue(operatorSpecSecrets.SecondaryReadOnlyConnectionString, to.Value(secondaryReadOnly.ConnectionString))
collector.AddValue(operatorSpecSecrets.SecondaryReadOnlyKeyID, to.Value(secondaryReadOnly.ID))
collector.AddValue(operatorSpecSecrets.SecondaryReadOnlyKey, to.Value(secondaryReadOnly.Value))
}

return collector.Values()
}

func makeResolvedSecretsMap(keys map[string]armappconfiguration.APIKey) map[string]string {
result := make(map[string]string)
primary, ok := keys["Primary"]
if ok {
result[primaryConnectionString] = to.Value(primary.ConnectionString)
result[primaryKeyID] = to.Value(primary.ID)
result[primaryKey] = to.Value(primary.Value)
}

primaryReadOnly, ok := keys["Primary Read Only"]
if ok {
result[primaryReadOnlyConnectionString] = to.Value(primaryReadOnly.ConnectionString)
result[primaryReadOnlyKeyID] = to.Value(primaryReadOnly.ID)
result[primaryReadOnlyKey] = to.Value(primaryReadOnly.Value)
}

secondary, ok := keys["Secondary"]
if ok {
result[secondaryConnectionString] = to.Value(secondary.ConnectionString)
result[secondaryKeyID] = to.Value(secondary.ID)
result[secondaryKey] = to.Value(secondary.Value)
}

secondaryReadOnly, ok := keys["Secondary Read Only"]
if ok {
result[secondaryReadOnlyConnectionString] = to.Value(secondaryReadOnly.ConnectionString)
result[secondaryReadOnlyKeyID] = to.Value(secondaryReadOnly.ID)
result[secondaryReadOnlyKey] = to.Value(secondaryReadOnly.Value)
}

return result
}
Loading
Loading