From 3491f55d407484751a9a5977e66a28b8d42c9f87 Mon Sep 17 00:00:00 2001 From: Mykhailo Zahlada Date: Thu, 11 Apr 2024 19:51:49 -0700 Subject: [PATCH 1/5] adds operatorSpec the example --- .../v1api20230131_userassignedidentity.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/v2/samples/managedidentity/v1api20230131/v1api20230131_userassignedidentity.yaml b/v2/samples/managedidentity/v1api20230131/v1api20230131_userassignedidentity.yaml index 9a19dfce8b6..1d6406f52c1 100644 --- a/v2/samples/managedidentity/v1api20230131/v1api20230131_userassignedidentity.yaml +++ b/v2/samples/managedidentity/v1api20230131/v1api20230131_userassignedidentity.yaml @@ -7,3 +7,24 @@ spec: location: westcentralus owner: name: aso-sample-rg + operatorSpec: + configMaps: + clientId: + name: umi-cm + key: clientId + principalId: + name: umi-cm + key: principalId + tenantId: + name: umi-cm + key: tenantId + secrets: + clientId: + name: umi-secret + key: clientId + principalId: + name: umi-secret + key: principalId + tenantId: + name: umi-secret + key: tenantId From 207eafc396b0e5c76eb2dd52888809f305cdf922 Mon Sep 17 00:00:00 2001 From: Mykhailo Zahlada Date: Thu, 11 Apr 2024 19:54:14 -0700 Subject: [PATCH 2/5] extends the UserAssignedIdentity operatorSpec with secrets section --- ...signed_identity_extention_authorization.go | 86 ++++++++++++ .../user_assigned_identity_types_gen.go | 20 +++ .../v1api20230131/storage/structure.txt | 9 +- .../user_assigned_identity_types_gen.go | 9 ++ .../user_assigned_identity_types_gen_test.go | 56 ++++++++ .../storage/zz_generated.deepcopy.go | 42 ++++++ .../v1api20230131/structure.txt | 14 +- .../user_assigned_identity_types_gen.go | 131 +++++++++++++++++- .../user_assigned_identity_types_gen_test.go | 98 +++++++++++++ .../v1api20230131/zz_generated.deepcopy.go | 35 +++++ v2/azure-arm.yaml | 4 + 11 files changed, 496 insertions(+), 8 deletions(-) create mode 100644 v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go diff --git a/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go new file mode 100644 index 00000000000..c6f78e2f748 --- /dev/null +++ b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go @@ -0,0 +1,86 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + */ + +package customizations + +import ( + "context" + "fmt" + + v20230131s "github.com/Azure/azure-service-operator/v2/api/managedidentity/v1api20230131/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/pkg/genruntime" + "github.com/Azure/azure-service-operator/v2/pkg/genruntime/secrets" + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +var _ genruntime.KubernetesExporter = &UserAssignedIdentityExtension{} + +// ExportKubernetesResources defines a resource which can create other resources in Kubernetes. +func (identity *UserAssignedIdentityExtension) ExportKubernetesResources( + ctx context.Context, + obj genruntime.MetaObject, + armClient *genericarmclient.GenericClient, + log logr.Logger, +) ([]client.Object, error) { + typedObj, ok := obj.(*v20230131s.UserAssignedIdentity) + if !ok { + return nil, fmt.Errorf( + "cannot run on unknown resource type %T, expected *v20230131s.UserAssignedIdentity", obj) + } + + // Type assert that we are the hub type. This will fail to compile if + // the hub type has been changed but this extension has not + var _ conversion.Hub = typedObj + + hasSecrets := secretsSpecified(typedObj) + if !hasSecrets { + log.V(Debug).Info("No secrets retrieval to perform as operatorSpec.Secrets is empty") + return nil, nil + } + + // obj.Status.ClientId + + collector := secrets.NewCollector(typedObj.Namespace) + if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { + if typedObj.Status.ClientId != nil { + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.ClientId, *typedObj.Status.ClientId) + } + } + if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { + if typedObj.Status.PrincipalId != nil { + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.PrincipalId, *typedObj.Status.PrincipalId) + } + } + if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { + if typedObj.Status.TenantId != nil { + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.TenantId, *typedObj.Status.TenantId) + } + } + result, err := collector.Values() + if err != nil { + return nil, err + } + return secrets.SliceToClientObjectSlice(result), nil +} + +func secretsSpecified(obj *v20230131s.UserAssignedIdentity) bool { + if obj.Spec.OperatorSpec == nil || obj.Spec.OperatorSpec.Secrets == nil { + return false + } + + specSecrets := obj.Spec.OperatorSpec.Secrets + hasSecrets := false + if specSecrets.ClientId != nil || + specSecrets.PrincipalId != nil || + specSecrets.TenantId != nil { + hasSecrets = true + } + + return hasSecrets +} diff --git a/v2/api/managedidentity/v1api20181130/storage/user_assigned_identity_types_gen.go b/v2/api/managedidentity/v1api20181130/storage/user_assigned_identity_types_gen.go index ba85fd1099b..0e1a287571b 100644 --- a/v2/api/managedidentity/v1api20181130/storage/user_assigned_identity_types_gen.go +++ b/v2/api/managedidentity/v1api20181130/storage/user_assigned_identity_types_gen.go @@ -673,6 +673,13 @@ func (operator *UserAssignedIdentityOperatorSpec) AssignProperties_From_UserAssi operator.ConfigMaps = nil } + // Secrets + if source.Secrets != nil { + propertyBag.Add("Secrets", *source.Secrets) + } else { + propertyBag.Remove("Secrets") + } + // Update the property bag if len(propertyBag) > 0 { operator.PropertyBag = propertyBag @@ -710,6 +717,19 @@ func (operator *UserAssignedIdentityOperatorSpec) AssignProperties_To_UserAssign destination.ConfigMaps = nil } + // Secrets + if propertyBag.Contains("Secrets") { + var secret v20230131s.UserAssignedIdentityOperatorSecrets + err := propertyBag.Pull("Secrets", &secret) + if err != nil { + return errors.Wrap(err, "pulling 'Secrets' from propertyBag") + } + + destination.Secrets = &secret + } else { + destination.Secrets = nil + } + // Update the property bag if len(propertyBag) > 0 { destination.PropertyBag = propertyBag diff --git a/v2/api/managedidentity/v1api20230131/storage/structure.txt b/v2/api/managedidentity/v1api20230131/storage/structure.txt index 2f9036afdd5..a336d0a7c36 100644 --- a/v2/api/managedidentity/v1api20230131/storage/structure.txt +++ b/v2/api/managedidentity/v1api20230131/storage/structure.txt @@ -36,13 +36,18 @@ github.com/Azure/azure-service-operator/v2/api/managedidentity/v1api20230131/sto ├── Spec: Object (7 properties) │ ├── AzureName: string │ ├── Location: *string - │ ├── OperatorSpec: *Object (2 properties) + │ ├── OperatorSpec: *Object (3 properties) │ │ ├── ConfigMaps: *Object (4 properties) │ │ │ ├── ClientId: *genruntime.ConfigMapDestination │ │ │ ├── PrincipalId: *genruntime.ConfigMapDestination │ │ │ ├── PropertyBag: genruntime.PropertyBag │ │ │ └── TenantId: *genruntime.ConfigMapDestination - │ │ └── PropertyBag: genruntime.PropertyBag + │ │ ├── PropertyBag: genruntime.PropertyBag + │ │ └── Secrets: *Object (4 properties) + │ │ ├── ClientId: *genruntime.SecretDestination + │ │ ├── PrincipalId: *genruntime.SecretDestination + │ │ ├── PropertyBag: genruntime.PropertyBag + │ │ └── TenantId: *genruntime.SecretDestination │ ├── OriginalVersion: string │ ├── Owner: *genruntime.KnownResourceReference │ ├── PropertyBag: genruntime.PropertyBag diff --git a/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen.go b/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen.go index daca1ff3602..91aed76d311 100644 --- a/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen.go +++ b/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen.go @@ -248,6 +248,7 @@ func (identity *UserAssignedIdentity_STATUS) ConvertStatusTo(destination genrunt type UserAssignedIdentityOperatorSpec struct { ConfigMaps *UserAssignedIdentityOperatorConfigMaps `json:"configMaps,omitempty"` PropertyBag genruntime.PropertyBag `json:"$propertyBag,omitempty"` + Secrets *UserAssignedIdentityOperatorSecrets `json:"secrets,omitempty"` } // Storage version of v1api20230131.UserAssignedIdentityOperatorConfigMaps @@ -258,6 +259,14 @@ type UserAssignedIdentityOperatorConfigMaps struct { TenantId *genruntime.ConfigMapDestination `json:"tenantId,omitempty"` } +// Storage version of v1api20230131.UserAssignedIdentityOperatorSecrets +type UserAssignedIdentityOperatorSecrets struct { + ClientId *genruntime.SecretDestination `json:"clientId,omitempty"` + PrincipalId *genruntime.SecretDestination `json:"principalId,omitempty"` + PropertyBag genruntime.PropertyBag `json:"$propertyBag,omitempty"` + TenantId *genruntime.SecretDestination `json:"tenantId,omitempty"` +} + func init() { SchemeBuilder.Register(&UserAssignedIdentity{}, &UserAssignedIdentityList{}) } diff --git a/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen_test.go b/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen_test.go index 63d26f447bc..f1331a66cd3 100644 --- a/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen_test.go +++ b/v2/api/managedidentity/v1api20230131/storage/user_assigned_identity_types_gen_test.go @@ -298,6 +298,7 @@ func UserAssignedIdentityOperatorSpecGenerator() gopter.Gen { // AddRelatedPropertyGeneratorsForUserAssignedIdentityOperatorSpec is a factory method for creating gopter generators func AddRelatedPropertyGeneratorsForUserAssignedIdentityOperatorSpec(gens map[string]gopter.Gen) { gens["ConfigMaps"] = gen.PtrOf(UserAssignedIdentityOperatorConfigMapsGenerator()) + gens["Secrets"] = gen.PtrOf(UserAssignedIdentityOperatorSecretsGenerator()) } func Test_UserAssignedIdentityOperatorConfigMaps_WhenSerializedToJson_DeserializesAsEqual(t *testing.T) { @@ -354,3 +355,58 @@ func UserAssignedIdentityOperatorConfigMapsGenerator() gopter.Gen { return userAssignedIdentityOperatorConfigMapsGenerator } + +func Test_UserAssignedIdentityOperatorSecrets_WhenSerializedToJson_DeserializesAsEqual(t *testing.T) { + t.Parallel() + parameters := gopter.DefaultTestParameters() + parameters.MinSuccessfulTests = 100 + parameters.MaxSize = 3 + properties := gopter.NewProperties(parameters) + properties.Property( + "Round trip of UserAssignedIdentityOperatorSecrets via JSON returns original", + prop.ForAll(RunJSONSerializationTestForUserAssignedIdentityOperatorSecrets, UserAssignedIdentityOperatorSecretsGenerator())) + properties.TestingRun(t, gopter.NewFormatedReporter(true, 240, os.Stdout)) +} + +// RunJSONSerializationTestForUserAssignedIdentityOperatorSecrets runs a test to see if a specific instance of UserAssignedIdentityOperatorSecrets round trips to JSON and back losslessly +func RunJSONSerializationTestForUserAssignedIdentityOperatorSecrets(subject UserAssignedIdentityOperatorSecrets) string { + // Serialize to JSON + bin, err := json.Marshal(subject) + if err != nil { + return err.Error() + } + + // Deserialize back into memory + var actual UserAssignedIdentityOperatorSecrets + err = json.Unmarshal(bin, &actual) + if err != nil { + return err.Error() + } + + // Check for outcome + match := cmp.Equal(subject, actual, cmpopts.EquateEmpty()) + if !match { + actualFmt := pretty.Sprint(actual) + subjectFmt := pretty.Sprint(subject) + result := diff.Diff(subjectFmt, actualFmt) + return result + } + + return "" +} + +// Generator of UserAssignedIdentityOperatorSecrets instances for property testing - lazily instantiated by +// UserAssignedIdentityOperatorSecretsGenerator() +var userAssignedIdentityOperatorSecretsGenerator gopter.Gen + +// UserAssignedIdentityOperatorSecretsGenerator returns a generator of UserAssignedIdentityOperatorSecrets instances for property testing. +func UserAssignedIdentityOperatorSecretsGenerator() gopter.Gen { + if userAssignedIdentityOperatorSecretsGenerator != nil { + return userAssignedIdentityOperatorSecretsGenerator + } + + generators := make(map[string]gopter.Gen) + userAssignedIdentityOperatorSecretsGenerator = gen.Struct(reflect.TypeOf(UserAssignedIdentityOperatorSecrets{}), generators) + + return userAssignedIdentityOperatorSecretsGenerator +} diff --git a/v2/api/managedidentity/v1api20230131/storage/zz_generated.deepcopy.go b/v2/api/managedidentity/v1api20230131/storage/zz_generated.deepcopy.go index ac4a6d0f9c4..c30b4eb23ed 100644 --- a/v2/api/managedidentity/v1api20230131/storage/zz_generated.deepcopy.go +++ b/v2/api/managedidentity/v1api20230131/storage/zz_generated.deepcopy.go @@ -338,6 +338,43 @@ func (in *UserAssignedIdentityOperatorConfigMaps) DeepCopy() *UserAssignedIdenti return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserAssignedIdentityOperatorSecrets) DeepCopyInto(out *UserAssignedIdentityOperatorSecrets) { + *out = *in + if in.ClientId != nil { + in, out := &in.ClientId, &out.ClientId + *out = new(genruntime.SecretDestination) + **out = **in + } + if in.PrincipalId != nil { + in, out := &in.PrincipalId, &out.PrincipalId + *out = new(genruntime.SecretDestination) + **out = **in + } + if in.PropertyBag != nil { + in, out := &in.PropertyBag, &out.PropertyBag + *out = make(genruntime.PropertyBag, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.TenantId != nil { + in, out := &in.TenantId, &out.TenantId + *out = new(genruntime.SecretDestination) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserAssignedIdentityOperatorSecrets. +func (in *UserAssignedIdentityOperatorSecrets) DeepCopy() *UserAssignedIdentityOperatorSecrets { + if in == nil { + return nil + } + out := new(UserAssignedIdentityOperatorSecrets) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserAssignedIdentityOperatorSpec) DeepCopyInto(out *UserAssignedIdentityOperatorSpec) { *out = *in @@ -353,6 +390,11 @@ func (in *UserAssignedIdentityOperatorSpec) DeepCopyInto(out *UserAssignedIdenti (*out)[key] = val } } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = new(UserAssignedIdentityOperatorSecrets) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserAssignedIdentityOperatorSpec. diff --git a/v2/api/managedidentity/v1api20230131/structure.txt b/v2/api/managedidentity/v1api20230131/structure.txt index d59aaa50639..e44ab33640f 100644 --- a/v2/api/managedidentity/v1api20230131/structure.txt +++ b/v2/api/managedidentity/v1api20230131/structure.txt @@ -70,11 +70,15 @@ github.com/Azure/azure-service-operator/v2/api/managedidentity/v1api20230131 │ ├── Spec: Object (5 properties) │ │ ├── AzureName: string │ │ ├── Location: *string -│ │ ├── OperatorSpec: *Object (1 property) -│ │ │ └── ConfigMaps: *Object (3 properties) -│ │ │ ├── ClientId: *genruntime.ConfigMapDestination -│ │ │ ├── PrincipalId: *genruntime.ConfigMapDestination -│ │ │ └── TenantId: *genruntime.ConfigMapDestination +│ │ ├── OperatorSpec: *Object (2 properties) +│ │ │ ├── ConfigMaps: *Object (3 properties) +│ │ │ │ ├── ClientId: *genruntime.ConfigMapDestination +│ │ │ │ ├── PrincipalId: *genruntime.ConfigMapDestination +│ │ │ │ └── TenantId: *genruntime.ConfigMapDestination +│ │ │ └── Secrets: *Object (3 properties) +│ │ │ ├── ClientId: *genruntime.SecretDestination +│ │ │ ├── PrincipalId: *genruntime.SecretDestination +│ │ │ └── TenantId: *genruntime.SecretDestination │ │ ├── Owner: *genruntime.KnownResourceReference │ │ └── Tags: map[string]string │ └── Status: Object (10 properties) diff --git a/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen.go b/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen.go index c7d6f469b0d..9274926492d 100644 --- a/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen.go +++ b/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen.go @@ -240,7 +240,7 @@ func (identity *UserAssignedIdentity) ValidateUpdate(old runtime.Object) (admiss // createValidations validates the creation of the resource func (identity *UserAssignedIdentity) createValidations() []func() (admission.Warnings, error) { - return []func() (admission.Warnings, error){identity.validateResourceReferences, identity.validateOwnerReference, identity.validateConfigMapDestinations} + return []func() (admission.Warnings, error){identity.validateResourceReferences, identity.validateOwnerReference, identity.validateSecretDestinations, identity.validateConfigMapDestinations} } // deleteValidations validates the deletion of the resource @@ -258,6 +258,9 @@ func (identity *UserAssignedIdentity) updateValidations() []func(old runtime.Obj func(old runtime.Object) (admission.Warnings, error) { return identity.validateOwnerReference() }, + func(old runtime.Object) (admission.Warnings, error) { + return identity.validateSecretDestinations() + }, func(old runtime.Object) (admission.Warnings, error) { return identity.validateConfigMapDestinations() }, @@ -294,6 +297,22 @@ func (identity *UserAssignedIdentity) validateResourceReferences() (admission.Wa return genruntime.ValidateResourceReferences(refs) } +// validateSecretDestinations validates there are no colliding genruntime.SecretDestination's +func (identity *UserAssignedIdentity) validateSecretDestinations() (admission.Warnings, error) { + if identity.Spec.OperatorSpec == nil { + return nil, nil + } + if identity.Spec.OperatorSpec.Secrets == nil { + return nil, nil + } + toValidate := []*genruntime.SecretDestination{ + identity.Spec.OperatorSpec.Secrets.ClientId, + identity.Spec.OperatorSpec.Secrets.PrincipalId, + identity.Spec.OperatorSpec.Secrets.TenantId, + } + return genruntime.ValidateSecretDestinations(toValidate) +} + // validateWriteOnceProperties validates all WriteOnce properties func (identity *UserAssignedIdentity) validateWriteOnceProperties(old runtime.Object) (admission.Warnings, error) { oldObj, ok := old.(*UserAssignedIdentity) @@ -902,6 +921,9 @@ func (identity *UserAssignedIdentity_STATUS) AssignProperties_To_UserAssignedIde type UserAssignedIdentityOperatorSpec struct { // ConfigMaps: configures where to place operator written ConfigMaps. ConfigMaps *UserAssignedIdentityOperatorConfigMaps `json:"configMaps,omitempty"` + + // Secrets: configures where to place Azure generated secrets. + Secrets *UserAssignedIdentityOperatorSecrets `json:"secrets,omitempty"` } // AssignProperties_From_UserAssignedIdentityOperatorSpec populates our UserAssignedIdentityOperatorSpec from the provided source UserAssignedIdentityOperatorSpec @@ -919,6 +941,18 @@ func (operator *UserAssignedIdentityOperatorSpec) AssignProperties_From_UserAssi operator.ConfigMaps = nil } + // Secrets + if source.Secrets != nil { + var secret UserAssignedIdentityOperatorSecrets + err := secret.AssignProperties_From_UserAssignedIdentityOperatorSecrets(source.Secrets) + if err != nil { + return errors.Wrap(err, "calling AssignProperties_From_UserAssignedIdentityOperatorSecrets() to populate field Secrets") + } + operator.Secrets = &secret + } else { + operator.Secrets = nil + } + // No error return nil } @@ -940,6 +974,18 @@ func (operator *UserAssignedIdentityOperatorSpec) AssignProperties_To_UserAssign destination.ConfigMaps = nil } + // Secrets + if operator.Secrets != nil { + var secret v20230131s.UserAssignedIdentityOperatorSecrets + err := operator.Secrets.AssignProperties_To_UserAssignedIdentityOperatorSecrets(&secret) + if err != nil { + return errors.Wrap(err, "calling AssignProperties_To_UserAssignedIdentityOperatorSecrets() to populate field Secrets") + } + destination.Secrets = &secret + } else { + destination.Secrets = nil + } + // Update the property bag if len(propertyBag) > 0 { destination.PropertyBag = propertyBag @@ -1033,6 +1079,89 @@ func (maps *UserAssignedIdentityOperatorConfigMaps) AssignProperties_To_UserAssi return nil } +type UserAssignedIdentityOperatorSecrets struct { + // ClientId: indicates where the ClientId secret should be placed. If omitted, the secret will not be retrieved from Azure. + ClientId *genruntime.SecretDestination `json:"clientId,omitempty"` + + // PrincipalId: indicates where the PrincipalId secret should be placed. If omitted, the secret will not be retrieved from + // Azure. + PrincipalId *genruntime.SecretDestination `json:"principalId,omitempty"` + + // TenantId: indicates where the TenantId secret should be placed. If omitted, the secret will not be retrieved from Azure. + TenantId *genruntime.SecretDestination `json:"tenantId,omitempty"` +} + +// AssignProperties_From_UserAssignedIdentityOperatorSecrets populates our UserAssignedIdentityOperatorSecrets from the provided source UserAssignedIdentityOperatorSecrets +func (secrets *UserAssignedIdentityOperatorSecrets) AssignProperties_From_UserAssignedIdentityOperatorSecrets(source *v20230131s.UserAssignedIdentityOperatorSecrets) error { + + // ClientId + if source.ClientId != nil { + clientId := source.ClientId.Copy() + secrets.ClientId = &clientId + } else { + secrets.ClientId = nil + } + + // PrincipalId + if source.PrincipalId != nil { + principalId := source.PrincipalId.Copy() + secrets.PrincipalId = &principalId + } else { + secrets.PrincipalId = nil + } + + // TenantId + if source.TenantId != nil { + tenantId := source.TenantId.Copy() + secrets.TenantId = &tenantId + } else { + secrets.TenantId = nil + } + + // No error + return nil +} + +// AssignProperties_To_UserAssignedIdentityOperatorSecrets populates the provided destination UserAssignedIdentityOperatorSecrets from our UserAssignedIdentityOperatorSecrets +func (secrets *UserAssignedIdentityOperatorSecrets) AssignProperties_To_UserAssignedIdentityOperatorSecrets(destination *v20230131s.UserAssignedIdentityOperatorSecrets) error { + // Create a new property bag + propertyBag := genruntime.NewPropertyBag() + + // ClientId + if secrets.ClientId != nil { + clientId := secrets.ClientId.Copy() + destination.ClientId = &clientId + } else { + destination.ClientId = nil + } + + // PrincipalId + if secrets.PrincipalId != nil { + principalId := secrets.PrincipalId.Copy() + destination.PrincipalId = &principalId + } else { + destination.PrincipalId = nil + } + + // TenantId + if secrets.TenantId != nil { + tenantId := secrets.TenantId.Copy() + destination.TenantId = &tenantId + } else { + destination.TenantId = nil + } + + // Update the property bag + if len(propertyBag) > 0 { + destination.PropertyBag = propertyBag + } else { + destination.PropertyBag = nil + } + + // No error + return nil +} + func init() { SchemeBuilder.Register(&UserAssignedIdentity{}, &UserAssignedIdentityList{}) } diff --git a/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen_test.go b/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen_test.go index 337d266b259..46b437acd04 100644 --- a/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen_test.go +++ b/v2/api/managedidentity/v1api20230131/user_assigned_identity_types_gen_test.go @@ -509,6 +509,7 @@ func UserAssignedIdentityOperatorSpecGenerator() gopter.Gen { // AddRelatedPropertyGeneratorsForUserAssignedIdentityOperatorSpec is a factory method for creating gopter generators func AddRelatedPropertyGeneratorsForUserAssignedIdentityOperatorSpec(gens map[string]gopter.Gen) { gens["ConfigMaps"] = gen.PtrOf(UserAssignedIdentityOperatorConfigMapsGenerator()) + gens["Secrets"] = gen.PtrOf(UserAssignedIdentityOperatorSecretsGenerator()) } func Test_UserAssignedIdentityOperatorConfigMaps_WhenPropertiesConverted_RoundTripsWithoutLoss(t *testing.T) { @@ -607,3 +608,100 @@ func UserAssignedIdentityOperatorConfigMapsGenerator() gopter.Gen { return userAssignedIdentityOperatorConfigMapsGenerator } + +func Test_UserAssignedIdentityOperatorSecrets_WhenPropertiesConverted_RoundTripsWithoutLoss(t *testing.T) { + t.Parallel() + parameters := gopter.DefaultTestParameters() + parameters.MaxSize = 10 + properties := gopter.NewProperties(parameters) + properties.Property( + "Round trip from UserAssignedIdentityOperatorSecrets to UserAssignedIdentityOperatorSecrets via AssignProperties_To_UserAssignedIdentityOperatorSecrets & AssignProperties_From_UserAssignedIdentityOperatorSecrets returns original", + prop.ForAll(RunPropertyAssignmentTestForUserAssignedIdentityOperatorSecrets, UserAssignedIdentityOperatorSecretsGenerator())) + properties.TestingRun(t, gopter.NewFormatedReporter(false, 240, os.Stdout)) +} + +// RunPropertyAssignmentTestForUserAssignedIdentityOperatorSecrets tests if a specific instance of UserAssignedIdentityOperatorSecrets can be assigned to storage and back losslessly +func RunPropertyAssignmentTestForUserAssignedIdentityOperatorSecrets(subject UserAssignedIdentityOperatorSecrets) string { + // Copy subject to make sure assignment doesn't modify it + copied := subject.DeepCopy() + + // Use AssignPropertiesTo() for the first stage of conversion + var other v20230131s.UserAssignedIdentityOperatorSecrets + err := copied.AssignProperties_To_UserAssignedIdentityOperatorSecrets(&other) + if err != nil { + return err.Error() + } + + // Use AssignPropertiesFrom() to convert back to our original type + var actual UserAssignedIdentityOperatorSecrets + err = actual.AssignProperties_From_UserAssignedIdentityOperatorSecrets(&other) + if err != nil { + return err.Error() + } + + // Check for a match + match := cmp.Equal(subject, actual, cmpopts.EquateEmpty()) + if !match { + actualFmt := pretty.Sprint(actual) + subjectFmt := pretty.Sprint(subject) + result := diff.Diff(subjectFmt, actualFmt) + return result + } + + return "" +} + +func Test_UserAssignedIdentityOperatorSecrets_WhenSerializedToJson_DeserializesAsEqual(t *testing.T) { + t.Parallel() + parameters := gopter.DefaultTestParameters() + parameters.MinSuccessfulTests = 100 + parameters.MaxSize = 3 + properties := gopter.NewProperties(parameters) + properties.Property( + "Round trip of UserAssignedIdentityOperatorSecrets via JSON returns original", + prop.ForAll(RunJSONSerializationTestForUserAssignedIdentityOperatorSecrets, UserAssignedIdentityOperatorSecretsGenerator())) + properties.TestingRun(t, gopter.NewFormatedReporter(true, 240, os.Stdout)) +} + +// RunJSONSerializationTestForUserAssignedIdentityOperatorSecrets runs a test to see if a specific instance of UserAssignedIdentityOperatorSecrets round trips to JSON and back losslessly +func RunJSONSerializationTestForUserAssignedIdentityOperatorSecrets(subject UserAssignedIdentityOperatorSecrets) string { + // Serialize to JSON + bin, err := json.Marshal(subject) + if err != nil { + return err.Error() + } + + // Deserialize back into memory + var actual UserAssignedIdentityOperatorSecrets + err = json.Unmarshal(bin, &actual) + if err != nil { + return err.Error() + } + + // Check for outcome + match := cmp.Equal(subject, actual, cmpopts.EquateEmpty()) + if !match { + actualFmt := pretty.Sprint(actual) + subjectFmt := pretty.Sprint(subject) + result := diff.Diff(subjectFmt, actualFmt) + return result + } + + return "" +} + +// Generator of UserAssignedIdentityOperatorSecrets instances for property testing - lazily instantiated by +// UserAssignedIdentityOperatorSecretsGenerator() +var userAssignedIdentityOperatorSecretsGenerator gopter.Gen + +// UserAssignedIdentityOperatorSecretsGenerator returns a generator of UserAssignedIdentityOperatorSecrets instances for property testing. +func UserAssignedIdentityOperatorSecretsGenerator() gopter.Gen { + if userAssignedIdentityOperatorSecretsGenerator != nil { + return userAssignedIdentityOperatorSecretsGenerator + } + + generators := make(map[string]gopter.Gen) + userAssignedIdentityOperatorSecretsGenerator = gen.Struct(reflect.TypeOf(UserAssignedIdentityOperatorSecrets{}), generators) + + return userAssignedIdentityOperatorSecretsGenerator +} diff --git a/v2/api/managedidentity/v1api20230131/zz_generated.deepcopy.go b/v2/api/managedidentity/v1api20230131/zz_generated.deepcopy.go index 40529372c92..c21c835a1bd 100644 --- a/v2/api/managedidentity/v1api20230131/zz_generated.deepcopy.go +++ b/v2/api/managedidentity/v1api20230131/zz_generated.deepcopy.go @@ -475,6 +475,36 @@ func (in *UserAssignedIdentityOperatorConfigMaps) DeepCopy() *UserAssignedIdenti return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserAssignedIdentityOperatorSecrets) DeepCopyInto(out *UserAssignedIdentityOperatorSecrets) { + *out = *in + if in.ClientId != nil { + in, out := &in.ClientId, &out.ClientId + *out = new(genruntime.SecretDestination) + **out = **in + } + if in.PrincipalId != nil { + in, out := &in.PrincipalId, &out.PrincipalId + *out = new(genruntime.SecretDestination) + **out = **in + } + if in.TenantId != nil { + in, out := &in.TenantId, &out.TenantId + *out = new(genruntime.SecretDestination) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserAssignedIdentityOperatorSecrets. +func (in *UserAssignedIdentityOperatorSecrets) DeepCopy() *UserAssignedIdentityOperatorSecrets { + if in == nil { + return nil + } + out := new(UserAssignedIdentityOperatorSecrets) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserAssignedIdentityOperatorSpec) DeepCopyInto(out *UserAssignedIdentityOperatorSpec) { *out = *in @@ -483,6 +513,11 @@ func (in *UserAssignedIdentityOperatorSpec) DeepCopyInto(out *UserAssignedIdenti *out = new(UserAssignedIdentityOperatorConfigMaps) (*in).DeepCopyInto(*out) } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = new(UserAssignedIdentityOperatorSecrets) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserAssignedIdentityOperatorSpec. diff --git a/v2/azure-arm.yaml b/v2/azure-arm.yaml index 1f762d7b1ff..73e4fbf3dc2 100644 --- a/v2/azure-arm.yaml +++ b/v2/azure-arm.yaml @@ -2208,6 +2208,10 @@ objectModelConfiguration: UserAssignedIdentity: $export: true $supportedFrom: v2.5.0 + $azureGeneratedSecrets: + - ClientId + - PrincipalId + - TenantId $generatedConfigs: ClientId: $.Status.ClientId PrincipalId: $.Status.PrincipalId From 6a418d94f8963efd89f55332e1c4beb857d16b18 Mon Sep 17 00:00:00 2001 From: Mykhailo Zahlada Date: Thu, 18 Apr 2024 09:44:38 -0700 Subject: [PATCH 3/5] removes debug comment --- .../user_assigned_identity_extention_authorization.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go index c6f78e2f748..e4039626617 100644 --- a/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go +++ b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go @@ -44,8 +44,6 @@ func (identity *UserAssignedIdentityExtension) ExportKubernetesResources( return nil, nil } - // obj.Status.ClientId - collector := secrets.NewCollector(typedObj.Namespace) if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { if typedObj.Status.ClientId != nil { From 0e300aff7e41bfb2027833bff61a47c8abed15a2 Mon Sep 17 00:00:00 2001 From: Mykhailo Zahlada Date: Thu, 18 Apr 2024 09:53:41 -0700 Subject: [PATCH 4/5] refactor: AddValue handles the required nil-checking for the left and right parameters --- ...assigned_identity_extention_authorization.go | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go index e4039626617..271ec834390 100644 --- a/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go +++ b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go @@ -46,20 +46,11 @@ func (identity *UserAssignedIdentityExtension) ExportKubernetesResources( collector := secrets.NewCollector(typedObj.Namespace) if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { - if typedObj.Status.ClientId != nil { - collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.ClientId, *typedObj.Status.ClientId) - } - } - if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { - if typedObj.Status.PrincipalId != nil { - collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.PrincipalId, *typedObj.Status.PrincipalId) - } - } - if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { - if typedObj.Status.TenantId != nil { - collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.TenantId, *typedObj.Status.TenantId) - } + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.ClientId, *typedObj.Status.ClientId) + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.PrincipalId, *typedObj.Status.PrincipalId) + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.TenantId, *typedObj.Status.TenantId) } + result, err := collector.Values() if err != nil { return nil, err From 9a7af943539721ceb149354c43a53e1bb4568da1 Mon Sep 17 00:00:00 2001 From: Mykhailo Zahlada Date: Thu, 18 Apr 2024 10:18:12 -0700 Subject: [PATCH 5/5] fix: extract a value from a pointer and return default value if nill is returned --- .../user_assigned_identity_extention_authorization.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go index 271ec834390..15c951ffbed 100644 --- a/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go +++ b/v2/api/managedidentity/customizations/user_assigned_identity_extention_authorization.go @@ -12,6 +12,7 @@ import ( v20230131s "github.com/Azure/azure-service-operator/v2/api/managedidentity/v1api20230131/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/util/to" "github.com/Azure/azure-service-operator/v2/pkg/genruntime" "github.com/Azure/azure-service-operator/v2/pkg/genruntime/secrets" "github.com/go-logr/logr" @@ -46,9 +47,9 @@ func (identity *UserAssignedIdentityExtension) ExportKubernetesResources( collector := secrets.NewCollector(typedObj.Namespace) if typedObj.Spec.OperatorSpec != nil && typedObj.Spec.OperatorSpec.Secrets != nil { - collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.ClientId, *typedObj.Status.ClientId) - collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.PrincipalId, *typedObj.Status.PrincipalId) - collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.TenantId, *typedObj.Status.TenantId) + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.ClientId, to.Value(typedObj.Status.ClientId)) + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.PrincipalId, to.Value(typedObj.Status.PrincipalId)) + collector.AddValue(typedObj.Spec.OperatorSpec.Secrets.TenantId, to.Value(typedObj.Status.TenantId)) } result, err := collector.Values()