diff --git a/src/operator/api/v1beta1/clientintents_types.go b/src/operator/api/v1beta1/clientintents_types.go index 34f8cde0e..25f44f732 100644 --- a/src/operator/api/v1beta1/clientintents_types.go +++ b/src/operator/api/v1beta1/clientintents_types.go @@ -258,6 +258,12 @@ type Intent struct { //+optional AzureRoles []string `json:"azureRoles,omitempty" yaml:"azureRoles,omitempty"` + //+optional + AzureDataActions []AzureDataAction `json:"azureDataActions,omitempty" yaml:"azureDataActions,omitempty"` + + //+optional + AzureActions []AzureAction `json:"azureActions,omitempty" yaml:"azureActions,omitempty"` + //+optional AzureKeyVaultPolicy *AzureKeyVaultPolicy `json:"azureKeyVaultPolicy,omitempty" yaml:"azureKeyVaultPolicy,omitempty"` @@ -265,6 +271,9 @@ type Intent struct { Internet *Internet `json:"internet,omitempty" yaml:"internet,omitempty"` } +type AzureDataAction string +type AzureAction string + type Internet struct { //+optional Domains []string `json:"domains,omitempty" yaml:"domains,omitempty"` diff --git a/src/operator/api/v1beta1/webhooks.go b/src/operator/api/v1beta1/webhooks.go index 7850da4bc..73d6bf6b1 100644 --- a/src/operator/api/v1beta1/webhooks.go +++ b/src/operator/api/v1beta1/webhooks.go @@ -248,22 +248,28 @@ func (in *ClientIntents) ConvertTo(dstRaw conversion.Hub) error { } if call.Type == IntentTypeAzure { dst.Spec.Targets[i] = v2alpha1.Target{Azure: lo.ToPtr(v2alpha1.AzureTarget{Scope: call.Name, Roles: call.AzureRoles})} - if call.AzureKeyVaultPolicy == nil { - continue + if len(call.AzureActions) > 0 { + dst.Spec.Targets[i].Azure.Actions = lo.Map(call.AzureActions, func(action AzureAction, _ int) v2alpha1.AzureAction { return v2alpha1.AzureAction(action) }) + } + if len(call.AzureDataActions) > 0 { + dst.Spec.Targets[i].Azure.DataActions = lo.Map(call.AzureDataActions, func(action AzureDataAction, _ int) v2alpha1.AzureDataAction { return v2alpha1.AzureDataAction(action) }) + } + + if call.AzureKeyVaultPolicy != nil { + dst.Spec.Targets[i].Azure.KeyVaultPolicy = &v2alpha1.AzureKeyVaultPolicy{} + dst.Spec.Targets[i].Azure.KeyVaultPolicy.KeyPermissions = lo.Map(call.AzureKeyVaultPolicy.KeyPermissions, func(permission AzureKeyVaultKeyPermission, _ int) v2alpha1.AzureKeyVaultKeyPermission { + return v2alpha1.AzureKeyVaultKeyPermission(permission) + }) + dst.Spec.Targets[i].Azure.KeyVaultPolicy.SecretPermissions = lo.Map(call.AzureKeyVaultPolicy.SecretPermissions, func(permission AzureKeyVaultSecretPermission, _ int) v2alpha1.AzureKeyVaultSecretPermission { + return v2alpha1.AzureKeyVaultSecretPermission(permission) + }) + dst.Spec.Targets[i].Azure.KeyVaultPolicy.CertificatePermissions = lo.Map(call.AzureKeyVaultPolicy.CertificatePermissions, func(permission AzureKeyVaultCertificatePermission, _ int) v2alpha1.AzureKeyVaultCertificatePermission { + return v2alpha1.AzureKeyVaultCertificatePermission(permission) + }) + dst.Spec.Targets[i].Azure.KeyVaultPolicy.StoragePermissions = lo.Map(call.AzureKeyVaultPolicy.StoragePermissions, func(permission AzureKeyVaultStoragePermission, _ int) v2alpha1.AzureKeyVaultStoragePermission { + return v2alpha1.AzureKeyVaultStoragePermission(permission) + }) } - dst.Spec.Targets[i].Azure.KeyVaultPolicy = &v2alpha1.AzureKeyVaultPolicy{} - dst.Spec.Targets[i].Azure.KeyVaultPolicy.KeyPermissions = lo.Map(call.AzureKeyVaultPolicy.KeyPermissions, func(permission AzureKeyVaultKeyPermission, _ int) v2alpha1.AzureKeyVaultKeyPermission { - return v2alpha1.AzureKeyVaultKeyPermission(permission) - }) - dst.Spec.Targets[i].Azure.KeyVaultPolicy.SecretPermissions = lo.Map(call.AzureKeyVaultPolicy.SecretPermissions, func(permission AzureKeyVaultSecretPermission, _ int) v2alpha1.AzureKeyVaultSecretPermission { - return v2alpha1.AzureKeyVaultSecretPermission(permission) - }) - dst.Spec.Targets[i].Azure.KeyVaultPolicy.CertificatePermissions = lo.Map(call.AzureKeyVaultPolicy.CertificatePermissions, func(permission AzureKeyVaultCertificatePermission, _ int) v2alpha1.AzureKeyVaultCertificatePermission { - return v2alpha1.AzureKeyVaultCertificatePermission(permission) - }) - dst.Spec.Targets[i].Azure.KeyVaultPolicy.StoragePermissions = lo.Map(call.AzureKeyVaultPolicy.StoragePermissions, func(permission AzureKeyVaultStoragePermission, _ int) v2alpha1.AzureKeyVaultStoragePermission { - return v2alpha1.AzureKeyVaultStoragePermission(permission) - }) } if call.Type == IntentTypeInternet && call.Internet != nil { dst.Spec.Targets[i] = v2alpha1.Target{Internet: lo.ToPtr(v2alpha1.Internet{Domains: call.Internet.Domains, Ports: call.Internet.Ports, Ips: call.Internet.Ips})} @@ -335,6 +341,12 @@ func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { } if target.Azure != nil { in.Spec.Calls[i] = Intent{Type: IntentTypeAzure, Name: target.Azure.Scope, AzureRoles: target.Azure.Roles} + if len(target.Azure.Actions) > 0 { + in.Spec.Calls[i].AzureActions = lo.Map(target.Azure.Actions, func(action v2alpha1.AzureAction, _ int) AzureAction { return AzureAction(action) }) + } + if len(target.Azure.DataActions) > 0 { + in.Spec.Calls[i].AzureDataActions = lo.Map(target.Azure.DataActions, func(action v2alpha1.AzureDataAction, _ int) AzureDataAction { return AzureDataAction(action) }) + } if target.Azure.KeyVaultPolicy == nil { continue } diff --git a/src/operator/api/v1beta1/webhooks_test.go b/src/operator/api/v1beta1/webhooks_test.go index f7b06c243..3fb6809b0 100644 --- a/src/operator/api/v1beta1/webhooks_test.go +++ b/src/operator/api/v1beta1/webhooks_test.go @@ -241,6 +241,51 @@ func (t *WebhooksTestSuite) TestClientIntentsFromV2_EmptySliceHTTPShouldNotBeTyp t.Require().Equal("", string(converted.Spec.Calls[1].Type)) } +func (t *WebhooksTestSuite) TestClientIntentsAzureActionsDataActions() { + // Create a ClientIntents with random data + original := &ClientIntents{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: &IntentsSpec{ + Service: Service{ + Name: "test", + }, + Calls: []Intent{ + { + Name: "testscope1", + Type: IntentTypeAzure, + AzureDataActions: []AzureDataAction{ + "testDataAction1", + "testDataAction2", + }, + }, + { + Name: "testscope2", + Type: IntentTypeAzure, + AzureActions: []AzureAction{ + "testAction1", + "testAction2", + }, + }, + }, + }, + } + + // ConvertTo + dstRaw := &v2alpha1.ClientIntents{} + err := original.ConvertTo(dstRaw) + t.Require().NoError(err) + + // ConvertFrom + converted := &ClientIntents{} + err = converted.ConvertFrom(dstRaw) + t.Require().NoError(err) + + t.Require().Equal(original.Spec, converted.Spec) +} + func TestWebhooksTestSuite(t *testing.T) { suite.Run(t, new(WebhooksTestSuite)) } diff --git a/src/operator/api/v1beta1/zz_generated.deepcopy.go b/src/operator/api/v1beta1/zz_generated.deepcopy.go index daeb78966..91946d021 100644 --- a/src/operator/api/v1beta1/zz_generated.deepcopy.go +++ b/src/operator/api/v1beta1/zz_generated.deepcopy.go @@ -236,6 +236,16 @@ func (in *Intent) DeepCopyInto(out *Intent) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.AzureDataActions != nil { + in, out := &in.AzureDataActions, &out.AzureDataActions + *out = make([]AzureDataAction, len(*in)) + copy(*out, *in) + } + if in.AzureActions != nil { + in, out := &in.AzureActions, &out.AzureActions + *out = make([]AzureAction, len(*in)) + copy(*out, *in) + } if in.AzureKeyVaultPolicy != nil { in, out := &in.AzureKeyVaultPolicy, &out.AzureKeyVaultPolicy *out = new(AzureKeyVaultPolicy) diff --git a/src/operator/api/v2alpha1/clientintents_types.go b/src/operator/api/v2alpha1/clientintents_types.go index 3bfe221f4..cac1675a7 100644 --- a/src/operator/api/v2alpha1/clientintents_types.go +++ b/src/operator/api/v2alpha1/clientintents_types.go @@ -276,12 +276,21 @@ type GCPTarget struct { } type AzureTarget struct { - Scope string `json:"scope,omitempty" yaml:"scope,omitempty"` + Scope string `json:"scope,omitempty" yaml:"scope,omitempty"` + //+optional Roles []string `json:"roles,omitempty" yaml:"roles,omitempty"` //+optional KeyVaultPolicy *AzureKeyVaultPolicy `json:"keyVaultPolicy,omitempty" yaml:"keyVaultPolicy,omitempty"` + //+optional + Actions []AzureAction `json:"actions,omitempty" yaml:"actions,omitempty"` + //+optional + DataActions []AzureDataAction `json:"dataActions,omitempty" yaml:"dataActions,omitempty"` } +type AzureAction string + +type AzureDataAction string + type KubernetesTarget struct { Name string `json:"name" yaml:"name"` Kind string `json:"kind" yaml:"kind"` diff --git a/src/operator/api/v2alpha1/zz_generated.deepcopy.go b/src/operator/api/v2alpha1/zz_generated.deepcopy.go index 8b7226a53..5d463e279 100644 --- a/src/operator/api/v2alpha1/zz_generated.deepcopy.go +++ b/src/operator/api/v2alpha1/zz_generated.deepcopy.go @@ -92,6 +92,16 @@ func (in *AzureTarget) DeepCopyInto(out *AzureTarget) { *out = new(AzureKeyVaultPolicy) (*in).DeepCopyInto(*out) } + if in.Actions != nil { + in, out := &in.Actions, &out.Actions + *out = make([]AzureAction, len(*in)) + copy(*out, *in) + } + if in.DataActions != nil { + in, out := &in.DataActions, &out.DataActions + *out = make([]AzureDataAction, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureTarget. diff --git a/src/operator/config/crd/k8s.otterize.com_clientintents.patched b/src/operator/config/crd/k8s.otterize.com_clientintents.patched index fbf5c934f..d9839b08c 100644 --- a/src/operator/config/crd/k8s.otterize.com_clientintents.patched +++ b/src/operator/config/crd/k8s.otterize.com_clientintents.patched @@ -485,6 +485,14 @@ spec: items: type: string type: array + azureActions: + items: + type: string + type: array + azureDataActions: + items: + type: string + type: array azureKeyVaultPolicy: properties: certificatePermissions: @@ -737,6 +745,14 @@ spec: type: object azure: properties: + actions: + items: + type: string + type: array + dataActions: + items: + type: string + type: array keyVaultPolicy: properties: certificatePermissions: diff --git a/src/operator/config/crd/k8s.otterize.com_clientintents.yaml b/src/operator/config/crd/k8s.otterize.com_clientintents.yaml index c4c800c31..2f4aa253b 100644 --- a/src/operator/config/crd/k8s.otterize.com_clientintents.yaml +++ b/src/operator/config/crd/k8s.otterize.com_clientintents.yaml @@ -473,6 +473,14 @@ spec: items: type: string type: array + azureActions: + items: + type: string + type: array + azureDataActions: + items: + type: string + type: array azureKeyVaultPolicy: properties: certificatePermissions: @@ -726,6 +734,14 @@ spec: type: object azure: properties: + actions: + items: + type: string + type: array + dataActions: + items: + type: string + type: array keyVaultPolicy: properties: certificatePermissions: diff --git a/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml b/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml index fbf5c934f..d9839b08c 100644 --- a/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml +++ b/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml @@ -485,6 +485,14 @@ spec: items: type: string type: array + azureActions: + items: + type: string + type: array + azureDataActions: + items: + type: string + type: array azureKeyVaultPolicy: properties: certificatePermissions: @@ -737,6 +745,14 @@ spec: type: object azure: properties: + actions: + items: + type: string + type: array + dataActions: + items: + type: string + type: array keyVaultPolicy: properties: certificatePermissions: diff --git a/src/operator/webhooks/clientintents_webhook_v2alpha1.go b/src/operator/webhooks/clientintents_webhook_v2alpha1.go index eeb7a3d22..dd673a117 100644 --- a/src/operator/webhooks/clientintents_webhook_v2alpha1.go +++ b/src/operator/webhooks/clientintents_webhook_v2alpha1.go @@ -385,6 +385,24 @@ func (v *IntentsValidatorV2alpha1) validateAzureTarget(azureTarget *otterizev2al Detail: "invalid intent format, field scope is required", } } + // check that at least one of the optional fields is set + if len(azureTarget.Actions) == 0 && len(azureTarget.DataActions) == 0 && len(azureTarget.Roles) == 0 && azureTarget.KeyVaultPolicy == nil { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "actions", + Detail: "invalid intent format, at least one of [actions, dataActions, roles, keyVaultPolicy] must be set", + } + } + + // check that that if intents uses actions/dataActions then roles must be empty (and vice versa) + if (len(azureTarget.Actions) > 0 || len(azureTarget.DataActions) > 0) && len(azureTarget.Roles) > 0 { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "roles", + Detail: "invalid intent format, if actions or dataActions are set, roles must be empty", + } + } + return nil }