diff --git a/helm-charts b/helm-charts index 721c2e665..16935da80 160000 --- a/helm-charts +++ b/helm-charts @@ -1 +1 @@ -Subproject commit 721c2e665bc29fb54f2728d2a8c5db0c636f0900 +Subproject commit 16935da8070907f984a5667b328755ef86934aa5 diff --git a/src/operator/api/v1alpha2/clientintents_webhook.go b/src/operator/api/v1alpha2/clientintents_webhook.go index 1c9da59f8..a6a3fd7ba 100644 --- a/src/operator/api/v1alpha2/clientintents_webhook.go +++ b/src/operator/api/v1alpha2/clientintents_webhook.go @@ -80,7 +80,7 @@ func convertTopicsV1alpha2toV1alpha3(srcTopics []KafkaTopic) []v1alpha3.KafkaTop return dstTopics } -// ConvertFrom converts the Hub version (v2alpha1) to this ClientIntents. +// ConvertFrom converts the Hub version (v2beta1) to this ClientIntents. func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { src := &v1alpha3.ClientIntents{} if err := src.ConvertFrom(srcRaw); err != nil { diff --git a/src/operator/api/v1alpha2/kafkaserverconfig_types.go b/src/operator/api/v1alpha2/kafkaserverconfig_types.go index d89b7c456..2d176e90a 100644 --- a/src/operator/api/v1alpha2/kafkaserverconfig_types.go +++ b/src/operator/api/v1alpha2/kafkaserverconfig_types.go @@ -17,7 +17,7 @@ limitations under the License. package v1alpha2 import ( - "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + "github.com/otterize/intents-operator/src/operator/api/v2beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/conversion" ) @@ -96,21 +96,21 @@ func init() { // ConvertTo converts this ProtectedService to the Hub version (v1alpha3). func (ksc *KafkaServerConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.KafkaServerConfig) + dst := dstRaw.(*v2beta1.KafkaServerConfig) dst.ObjectMeta = ksc.ObjectMeta - dst.Spec = v2alpha1.KafkaServerConfigSpec{} + dst.Spec = v2beta1.KafkaServerConfigSpec{} dst.Spec.Addr = ksc.Spec.Addr - dst.Spec.Workload = v2alpha1.Workload{Name: ksc.Spec.Service.Name} + dst.Spec.Workload = v2beta1.Workload{Name: ksc.Spec.Service.Name} dst.Spec.NoAutoCreateIntentsForOperator = ksc.Spec.NoAutoCreateIntentsForOperator - dst.Spec.TLS = v2alpha1.TLSSource{ + dst.Spec.TLS = v2beta1.TLSSource{ CertFile: ksc.Spec.TLS.CertFile, KeyFile: ksc.Spec.TLS.KeyFile, RootCAFile: ksc.Spec.TLS.RootCAFile, } for _, topic := range ksc.Spec.Topics { - dst.Spec.Topics = append(dst.Spec.Topics, v2alpha1.TopicConfig{ + dst.Spec.Topics = append(dst.Spec.Topics, v2beta1.TopicConfig{ Topic: topic.Topic, - Pattern: v2alpha1.ResourcePatternType(topic.Pattern), // this casting is fine as v1alpha2 == v1alpha3 + Pattern: v2beta1.ResourcePatternType(topic.Pattern), // this casting is fine as v1alpha2 == v1alpha3 ClientIdentityRequired: topic.ClientIdentityRequired, IntentsRequired: topic.IntentsRequired, }) @@ -120,7 +120,7 @@ func (ksc *KafkaServerConfig) ConvertTo(dstRaw conversion.Hub) error { // ConvertFrom converts the Hub version (v1alpha3) to this KafkaServerConfig. func (ksc *KafkaServerConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.KafkaServerConfig) + src := srcRaw.(*v2beta1.KafkaServerConfig) ksc.ObjectMeta = src.ObjectMeta ksc.Spec = KafkaServerConfigSpec{} ksc.Spec.Addr = src.Spec.Addr diff --git a/src/operator/api/v1alpha3/webhooks.go b/src/operator/api/v1alpha3/webhooks.go index d6e47d76f..b2ccb7828 100644 --- a/src/operator/api/v1alpha3/webhooks.go +++ b/src/operator/api/v1alpha3/webhooks.go @@ -1,7 +1,7 @@ package v1alpha3 import ( - "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/shared/serviceidresolver/serviceidentity" "github.com/samber/lo" "golang.org/x/exp/slices" @@ -19,13 +19,13 @@ func (in *MySQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, validator } func (in *MySQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.MySQLServerConfig) + dst := dstRaw.(*v2beta1.MySQLServerConfig) dst.ObjectMeta = in.ObjectMeta dst.Spec.Address = in.Spec.Address dst.Spec.Credentials.Username = in.Spec.Credentials.Username dst.Spec.Credentials.Password = in.Spec.Credentials.Password if in.Spec.Credentials.SecretRef != nil { - dst.Spec.Credentials.SecretRef = &v2alpha1.DatabaseCredentialsSecretRef{ + dst.Spec.Credentials.SecretRef = &v2beta1.DatabaseCredentialsSecretRef{ Name: in.Spec.Credentials.SecretRef.Name, Namespace: in.Spec.Credentials.SecretRef.Namespace, UsernameKey: in.Spec.Credentials.SecretRef.UsernameKey, @@ -36,7 +36,7 @@ func (in *MySQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { } func (in *MySQLServerConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.MySQLServerConfig) + src := srcRaw.(*v2beta1.MySQLServerConfig) in.ObjectMeta = src.ObjectMeta in.Spec.Address = src.Spec.Address in.Spec.Credentials.Username = src.Spec.Credentials.Username @@ -61,13 +61,13 @@ func (in *PostgreSQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, vali } func (in *PostgreSQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.PostgreSQLServerConfig) + dst := dstRaw.(*v2beta1.PostgreSQLServerConfig) dst.ObjectMeta = in.ObjectMeta dst.Spec.Address = in.Spec.Address dst.Spec.Credentials.Username = in.Spec.Credentials.Username dst.Spec.Credentials.Password = in.Spec.Credentials.Password if in.Spec.Credentials.SecretRef != nil { - dst.Spec.Credentials.SecretRef = &v2alpha1.DatabaseCredentialsSecretRef{ + dst.Spec.Credentials.SecretRef = &v2beta1.DatabaseCredentialsSecretRef{ Name: in.Spec.Credentials.SecretRef.Name, Namespace: in.Spec.Credentials.SecretRef.Namespace, UsernameKey: in.Spec.Credentials.SecretRef.UsernameKey, @@ -79,7 +79,7 @@ func (in *PostgreSQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { } func (in *PostgreSQLServerConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.PostgreSQLServerConfig) + src := srcRaw.(*v2beta1.PostgreSQLServerConfig) in.ObjectMeta = src.ObjectMeta in.Spec.Address = src.Spec.Address in.Spec.Credentials.Username = src.Spec.Credentials.Username @@ -104,22 +104,22 @@ func (ksc *KafkaServerConfig) SetupWebhookWithManager(mgr ctrl.Manager) error { } func (ksc *KafkaServerConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.KafkaServerConfig) + dst := dstRaw.(*v2beta1.KafkaServerConfig) dst.ObjectMeta = ksc.ObjectMeta // convert each spec attribute dst.Spec.Workload.Name = ksc.Spec.Service.Name dst.Spec.Workload.Kind = ksc.Spec.Service.Kind dst.Spec.NoAutoCreateIntentsForOperator = ksc.Spec.NoAutoCreateIntentsForOperator dst.Spec.Addr = ksc.Spec.Addr - dst.Spec.TLS = v2alpha1.TLSSource{ + dst.Spec.TLS = v2beta1.TLSSource{ CertFile: ksc.Spec.TLS.CertFile, KeyFile: ksc.Spec.TLS.KeyFile, RootCAFile: ksc.Spec.TLS.RootCAFile, } - dst.Spec.Topics = make([]v2alpha1.TopicConfig, len(ksc.Spec.Topics)) + dst.Spec.Topics = make([]v2beta1.TopicConfig, len(ksc.Spec.Topics)) for i, topic := range ksc.Spec.Topics { dst.Spec.Topics[i].Topic = topic.Topic - dst.Spec.Topics[i].Pattern = v2alpha1.ResourcePatternType(topic.Pattern) + dst.Spec.Topics[i].Pattern = v2beta1.ResourcePatternType(topic.Pattern) dst.Spec.Topics[i].ClientIdentityRequired = topic.ClientIdentityRequired dst.Spec.Topics[i].IntentsRequired = topic.IntentsRequired } @@ -127,7 +127,7 @@ func (ksc *KafkaServerConfig) ConvertTo(dstRaw conversion.Hub) error { } func (ksc *KafkaServerConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.KafkaServerConfig) + src := srcRaw.(*v2beta1.KafkaServerConfig) ksc.ObjectMeta = src.ObjectMeta // convert each spec attribute ksc.Spec.Service.Name = src.Spec.Workload.Name @@ -159,7 +159,7 @@ func (in *ProtectedService) SetupWebhookWithManager(mgr ctrl.Manager, validator } func (in *ProtectedService) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.ProtectedService) + dst := dstRaw.(*v2beta1.ProtectedService) dst.ObjectMeta = in.ObjectMeta dst.Spec.Name = in.Spec.Name dst.Spec.Kind = in.Spec.Kind @@ -167,7 +167,7 @@ func (in *ProtectedService) ConvertTo(dstRaw conversion.Hub) error { } func (in *ProtectedService) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.ProtectedService) + src := srcRaw.(*v2beta1.ProtectedService) in.ObjectMeta = src.ObjectMeta in.Spec.Name = src.Spec.Name in.Spec.Kind = src.Spec.Kind @@ -183,19 +183,19 @@ func (in *ClientIntents) SetupWebhookWithManager(mgr ctrl.Manager, validator web } func (in *ClientIntents) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.ClientIntents) + dst := dstRaw.(*v2beta1.ClientIntents) dst.ObjectMeta = in.ObjectMeta dst.Status.UpToDate = in.Status.UpToDate dst.Status.ObservedGeneration = in.Status.ObservedGeneration - dst.Status.ResolvedIPs = lo.Map(in.Status.ResolvedIPs, func(resolvedIPs ResolvedIPs, _ int) v2alpha1.ResolvedIPs { - return v2alpha1.ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} + dst.Status.ResolvedIPs = lo.Map(in.Status.ResolvedIPs, func(resolvedIPs ResolvedIPs, _ int) v2beta1.ResolvedIPs { + return v2beta1.ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} }) if dst.Spec == nil { - dst.Spec = &v2alpha1.IntentsSpec{} + dst.Spec = &v2beta1.IntentsSpec{} } dst.Spec.Workload.Name = in.Spec.Service.Name dst.Spec.Workload.Kind = in.Spec.Service.Kind - dst.Spec.Targets = make([]v2alpha1.Target, len(in.Spec.Calls)) + dst.Spec.Targets = make([]v2beta1.Target, len(in.Spec.Calls)) for i, call := range in.Spec.Calls { if call.IsTargetInCluster() && call.Type != IntentTypeKafka { var name string @@ -204,79 +204,79 @@ func (in *ClientIntents) ConvertTo(dstRaw conversion.Hub) error { } else { name = call.GetServerFullyQualifiedName(in.Namespace) } - kubernetesTarget := v2alpha1.KubernetesTarget{Name: name, Kind: call.GetTargetServerKind()} + kubernetesTarget := v2beta1.KubernetesTarget{Name: name, Kind: call.GetTargetServerKind()} if kubernetesTarget.Kind == serviceidentity.KindOtterizeLegacy { // This is an internal kind the user doesn't need to see it kubernetesTarget.Kind = "" } if call.Type == IntentTypeHTTP && len(call.HTTPResources) > 0 { - httpTargets := make([]v2alpha1.HTTPTarget, len(call.HTTPResources)) + httpTargets := make([]v2beta1.HTTPTarget, len(call.HTTPResources)) for j, http := range call.HTTPResources { - methods := lo.Map(http.Methods, func(method HTTPMethod, _ int) v2alpha1.HTTPMethod { return v2alpha1.HTTPMethod(method) }) - httpTargets[j] = v2alpha1.HTTPTarget{Path: http.Path, Methods: methods} + methods := lo.Map(http.Methods, func(method HTTPMethod, _ int) v2beta1.HTTPMethod { return v2beta1.HTTPMethod(method) }) + httpTargets[j] = v2beta1.HTTPTarget{Path: http.Path, Methods: methods} } kubernetesTarget.HTTP = httpTargets } - dst.Spec.Targets[i] = v2alpha1.Target{Kubernetes: &kubernetesTarget} + dst.Spec.Targets[i] = v2beta1.Target{Kubernetes: &kubernetesTarget} continue } if call.Type == IntentTypeKafka && len(call.Topics) > 0 { - topics := lo.Map(call.Topics, func(topic KafkaTopic, _ int) v2alpha1.KafkaTopic { - return v2alpha1.KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation KafkaOperation, _ int) v2alpha1.KafkaOperation { - return v2alpha1.KafkaOperation(operation) + topics := lo.Map(call.Topics, func(topic KafkaTopic, _ int) v2beta1.KafkaTopic { + return v2beta1.KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation KafkaOperation, _ int) v2beta1.KafkaOperation { + return v2beta1.KafkaOperation(operation) })} }) - dst.Spec.Targets[i] = v2alpha1.Target{Kafka: lo.ToPtr(v2alpha1.KafkaTarget{Name: call.Name, Topics: topics})} + dst.Spec.Targets[i] = v2beta1.Target{Kafka: lo.ToPtr(v2beta1.KafkaTarget{Name: call.Name, Topics: topics})} } if call.Type == IntentTypeDatabase && len(call.DatabaseResources) > 0 { - tables := lo.Map(call.DatabaseResources, func(resource DatabaseResource, _ int) v2alpha1.SQLPrivileges { - return v2alpha1.SQLPrivileges{Table: resource.Table, DatabaseName: resource.DatabaseName, Operations: lo.Map(resource.Operations, func(operation DatabaseOperation, _ int) v2alpha1.DatabaseOperation { - return v2alpha1.DatabaseOperation(operation) + tables := lo.Map(call.DatabaseResources, func(resource DatabaseResource, _ int) v2beta1.SQLPrivileges { + return v2beta1.SQLPrivileges{Table: resource.Table, DatabaseName: resource.DatabaseName, Operations: lo.Map(resource.Operations, func(operation DatabaseOperation, _ int) v2beta1.DatabaseOperation { + return v2beta1.DatabaseOperation(operation) })} }) - dst.Spec.Targets[i] = v2alpha1.Target{SQL: lo.ToPtr(v2alpha1.SQLTarget{Name: call.Name, Privileges: tables})} + dst.Spec.Targets[i] = v2beta1.Target{SQL: lo.ToPtr(v2beta1.SQLTarget{Name: call.Name, Privileges: tables})} continue } if call.Type == IntentTypeAWS { - dst.Spec.Targets[i] = v2alpha1.Target{AWS: lo.ToPtr(v2alpha1.AWSTarget{ARN: call.Name, Actions: call.AWSActions})} + dst.Spec.Targets[i] = v2beta1.Target{AWS: lo.ToPtr(v2beta1.AWSTarget{ARN: call.Name, Actions: call.AWSActions})} continue } if call.Type == IntentTypeGCP { - dst.Spec.Targets[i] = v2alpha1.Target{GCP: lo.ToPtr(v2alpha1.GCPTarget{Resource: call.Name, Permissions: call.GCPPermissions})} + dst.Spec.Targets[i] = v2beta1.Target{GCP: lo.ToPtr(v2beta1.GCPTarget{Resource: call.Name, Permissions: call.GCPPermissions})} continue } if call.Type == IntentTypeAzure { - dst.Spec.Targets[i] = v2alpha1.Target{Azure: lo.ToPtr(v2alpha1.AzureTarget{Scope: call.Name, Roles: call.AzureRoles})} + dst.Spec.Targets[i] = v2beta1.Target{Azure: lo.ToPtr(v2beta1.AzureTarget{Scope: call.Name, Roles: call.AzureRoles})} if call.AzureKeyVaultPolicy == nil { continue } - 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 = &v2beta1.AzureKeyVaultPolicy{} + dst.Spec.Targets[i].Azure.KeyVaultPolicy.KeyPermissions = lo.Map(call.AzureKeyVaultPolicy.KeyPermissions, func(permission AzureKeyVaultKeyPermission, _ int) v2beta1.AzureKeyVaultKeyPermission { + return v2beta1.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.SecretPermissions = lo.Map(call.AzureKeyVaultPolicy.SecretPermissions, func(permission AzureKeyVaultSecretPermission, _ int) v2beta1.AzureKeyVaultSecretPermission { + return v2beta1.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.CertificatePermissions = lo.Map(call.AzureKeyVaultPolicy.CertificatePermissions, func(permission AzureKeyVaultCertificatePermission, _ int) v2beta1.AzureKeyVaultCertificatePermission { + return v2beta1.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.StoragePermissions = lo.Map(call.AzureKeyVaultPolicy.StoragePermissions, func(permission AzureKeyVaultStoragePermission, _ int) v2beta1.AzureKeyVaultStoragePermission { + return v2beta1.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})} + dst.Spec.Targets[i] = v2beta1.Target{Internet: lo.ToPtr(v2beta1.Internet{Domains: call.Internet.Domains, Ports: call.Internet.Ports, Ips: call.Internet.Ips})} } } return nil } func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.ClientIntents) + src := srcRaw.(*v2beta1.ClientIntents) in.ObjectMeta = src.ObjectMeta in.Status.UpToDate = src.Status.UpToDate in.Status.ObservedGeneration = src.Status.ObservedGeneration - in.Status.ResolvedIPs = lo.Map(src.Status.ResolvedIPs, func(resolvedIPs v2alpha1.ResolvedIPs, _ int) ResolvedIPs { + in.Status.ResolvedIPs = lo.Map(src.Status.ResolvedIPs, func(resolvedIPs v2beta1.ResolvedIPs, _ int) ResolvedIPs { return ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} }) in.Spec = &IntentsSpec{} @@ -293,8 +293,8 @@ func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { if target.Kubernetes != nil { in.Spec.Calls[i] = Intent{Name: target.Kubernetes.Name, Kind: target.Kubernetes.Kind} if len(target.Kubernetes.HTTP) > 0 { - in.Spec.Calls[i].HTTPResources = lo.Map(target.Kubernetes.HTTP, func(http v2alpha1.HTTPTarget, _ int) HTTPResource { - return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2alpha1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} + in.Spec.Calls[i].HTTPResources = lo.Map(target.Kubernetes.HTTP, func(http v2beta1.HTTPTarget, _ int) HTTPResource { + return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2beta1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} }) in.Spec.Calls[i].Type = IntentTypeHTTP } @@ -303,22 +303,22 @@ func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { if target.Service != nil { in.Spec.Calls[i] = Intent{Name: target.Service.Name, Kind: serviceidentity.KindService} if len(target.Service.HTTP) > 0 { - in.Spec.Calls[i].HTTPResources = lo.Map(target.Service.HTTP, func(http v2alpha1.HTTPTarget, _ int) HTTPResource { - return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2alpha1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} + in.Spec.Calls[i].HTTPResources = lo.Map(target.Service.HTTP, func(http v2beta1.HTTPTarget, _ int) HTTPResource { + return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2beta1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} }) in.Spec.Calls[i].Type = IntentTypeHTTP } continue } if target.Kafka != nil { - in.Spec.Calls[i] = Intent{Type: IntentTypeKafka, Name: target.Kafka.Name, Topics: lo.Map(target.Kafka.Topics, func(topic v2alpha1.KafkaTopic, _ int) KafkaTopic { - return KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation v2alpha1.KafkaOperation, _ int) KafkaOperation { return KafkaOperation(operation) })} + in.Spec.Calls[i] = Intent{Type: IntentTypeKafka, Name: target.Kafka.Name, Topics: lo.Map(target.Kafka.Topics, func(topic v2beta1.KafkaTopic, _ int) KafkaTopic { + return KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation v2beta1.KafkaOperation, _ int) KafkaOperation { return KafkaOperation(operation) })} })} continue } if target.SQL != nil { - in.Spec.Calls[i] = Intent{Type: IntentTypeDatabase, Name: target.SQL.Name, DatabaseResources: lo.Map(target.SQL.Privileges, func(permission v2alpha1.SQLPrivileges, _ int) DatabaseResource { - return DatabaseResource{Table: permission.Table, DatabaseName: permission.DatabaseName, Operations: lo.Map(permission.Operations, func(operation v2alpha1.DatabaseOperation, _ int) DatabaseOperation { + in.Spec.Calls[i] = Intent{Type: IntentTypeDatabase, Name: target.SQL.Name, DatabaseResources: lo.Map(target.SQL.Privileges, func(permission v2beta1.SQLPrivileges, _ int) DatabaseResource { + return DatabaseResource{Table: permission.Table, DatabaseName: permission.DatabaseName, Operations: lo.Map(permission.Operations, func(operation v2beta1.DatabaseOperation, _ int) DatabaseOperation { return DatabaseOperation(operation) })} })} @@ -338,16 +338,16 @@ func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { continue } in.Spec.Calls[i].AzureKeyVaultPolicy = &AzureKeyVaultPolicy{} - in.Spec.Calls[i].AzureKeyVaultPolicy.KeyPermissions = lo.Map(target.Azure.KeyVaultPolicy.KeyPermissions, func(permission v2alpha1.AzureKeyVaultKeyPermission, _ int) AzureKeyVaultKeyPermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.KeyPermissions = lo.Map(target.Azure.KeyVaultPolicy.KeyPermissions, func(permission v2beta1.AzureKeyVaultKeyPermission, _ int) AzureKeyVaultKeyPermission { return AzureKeyVaultKeyPermission(permission) }) - in.Spec.Calls[i].AzureKeyVaultPolicy.SecretPermissions = lo.Map(target.Azure.KeyVaultPolicy.SecretPermissions, func(permission v2alpha1.AzureKeyVaultSecretPermission, _ int) AzureKeyVaultSecretPermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.SecretPermissions = lo.Map(target.Azure.KeyVaultPolicy.SecretPermissions, func(permission v2beta1.AzureKeyVaultSecretPermission, _ int) AzureKeyVaultSecretPermission { return AzureKeyVaultSecretPermission(permission) }) - in.Spec.Calls[i].AzureKeyVaultPolicy.CertificatePermissions = lo.Map(target.Azure.KeyVaultPolicy.CertificatePermissions, func(permission v2alpha1.AzureKeyVaultCertificatePermission, _ int) AzureKeyVaultCertificatePermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.CertificatePermissions = lo.Map(target.Azure.KeyVaultPolicy.CertificatePermissions, func(permission v2beta1.AzureKeyVaultCertificatePermission, _ int) AzureKeyVaultCertificatePermission { return AzureKeyVaultCertificatePermission(permission) }) - in.Spec.Calls[i].AzureKeyVaultPolicy.StoragePermissions = lo.Map(target.Azure.KeyVaultPolicy.StoragePermissions, func(permission v2alpha1.AzureKeyVaultStoragePermission, _ int) AzureKeyVaultStoragePermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.StoragePermissions = lo.Map(target.Azure.KeyVaultPolicy.StoragePermissions, func(permission v2beta1.AzureKeyVaultStoragePermission, _ int) AzureKeyVaultStoragePermission { return AzureKeyVaultStoragePermission(permission) }) } diff --git a/src/operator/api/v1alpha3/webhooks_test.go b/src/operator/api/v1alpha3/webhooks_test.go index 48c625adb..2af7c4ce9 100644 --- a/src/operator/api/v1alpha3/webhooks_test.go +++ b/src/operator/api/v1alpha3/webhooks_test.go @@ -1,7 +1,7 @@ package v1alpha3 import ( - "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/stretchr/testify/suite" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "testing" @@ -34,7 +34,7 @@ func (t *WebhooksTestSuite) TestMySQLServerConfigConversion() { } // ConvertTo - dstRaw := &v2alpha1.MySQLServerConfig{} + dstRaw := &v2beta1.MySQLServerConfig{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -71,7 +71,7 @@ func (t *WebhooksTestSuite) TestPostgreSQLServerConfigConversion() { } // ConvertTo - dstRaw := &v2alpha1.PostgreSQLServerConfig{} + dstRaw := &v2beta1.PostgreSQLServerConfig{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -107,7 +107,7 @@ func (t *WebhooksTestSuite) TestKafkaServerConfigConversion() { } // ConvertTo - dstRaw := &v2alpha1.KafkaServerConfig{} + dstRaw := &v2beta1.KafkaServerConfig{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -134,7 +134,7 @@ func (t *WebhooksTestSuite) TestProtectedServiceConversion() { } // ConvertTo - dstRaw := &v2alpha1.ProtectedService{} + dstRaw := &v2beta1.ProtectedService{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -176,7 +176,7 @@ func (t *WebhooksTestSuite) TestClientIntentsKubernetes() { }} // ConvertTo - dstRaw := &v2alpha1.ClientIntents{} + dstRaw := &v2beta1.ClientIntents{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -190,12 +190,12 @@ func (t *WebhooksTestSuite) TestClientIntentsKubernetes() { } func (t *WebhooksTestSuite) TestClientIntentsFromV2_serviceKubernetesDefault() { - // Create a v2alpha1.ClientIntents with random data - original := &v2alpha1.ClientIntents{ - Spec: &v2alpha1.IntentsSpec{ - Targets: []v2alpha1.Target{ + // Create a v2beta1.ClientIntents with random data + original := &v2beta1.ClientIntents{ + Spec: &v2beta1.IntentsSpec{ + Targets: []v2beta1.Target{ { - Service: &v2alpha1.ServiceTarget{ + Service: &v2beta1.ServiceTarget{ Name: "kubernetes.default", }, }, @@ -211,20 +211,20 @@ func (t *WebhooksTestSuite) TestClientIntentsFromV2_serviceKubernetesDefault() { } func (t *WebhooksTestSuite) TestClientIntentsFromV2_EmptySliceHTTPShouldNotBeTypeHTTP() { - // Create a v2alpha1.ClientIntents with random data - original := &v2alpha1.ClientIntents{ - Spec: &v2alpha1.IntentsSpec{ - Targets: []v2alpha1.Target{ + // Create a v2beta1.ClientIntents with random data + original := &v2beta1.ClientIntents{ + Spec: &v2beta1.IntentsSpec{ + Targets: []v2beta1.Target{ { - Service: &v2alpha1.ServiceTarget{ + Service: &v2beta1.ServiceTarget{ Name: "test", - HTTP: []v2alpha1.HTTPTarget{}, + HTTP: []v2beta1.HTTPTarget{}, }, }, { - Kubernetes: &v2alpha1.KubernetesTarget{ + Kubernetes: &v2beta1.KubernetesTarget{ Name: "test2", - HTTP: []v2alpha1.HTTPTarget{}, + HTTP: []v2beta1.HTTPTarget{}, }, }, }, diff --git a/src/operator/api/v1beta1/webhooks.go b/src/operator/api/v1beta1/webhooks.go index f3bfccc93..58f9de944 100644 --- a/src/operator/api/v1beta1/webhooks.go +++ b/src/operator/api/v1beta1/webhooks.go @@ -1,7 +1,7 @@ package v1beta1 import ( - "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/shared/serviceidresolver/serviceidentity" "github.com/samber/lo" "golang.org/x/exp/slices" @@ -19,13 +19,13 @@ func (in *MySQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, validator } func (in *MySQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.MySQLServerConfig) + dst := dstRaw.(*v2beta1.MySQLServerConfig) dst.ObjectMeta = in.ObjectMeta dst.Spec.Address = in.Spec.Address dst.Spec.Credentials.Username = in.Spec.Credentials.Username dst.Spec.Credentials.Password = in.Spec.Credentials.Password if in.Spec.Credentials.SecretRef != nil { - dst.Spec.Credentials.SecretRef = &v2alpha1.DatabaseCredentialsSecretRef{ + dst.Spec.Credentials.SecretRef = &v2beta1.DatabaseCredentialsSecretRef{ Name: in.Spec.Credentials.SecretRef.Name, Namespace: in.Spec.Credentials.SecretRef.Namespace, UsernameKey: in.Spec.Credentials.SecretRef.UsernameKey, @@ -36,7 +36,7 @@ func (in *MySQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { } func (in *MySQLServerConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.MySQLServerConfig) + src := srcRaw.(*v2beta1.MySQLServerConfig) in.ObjectMeta = src.ObjectMeta in.Spec.Address = src.Spec.Address in.Spec.Credentials.Username = src.Spec.Credentials.Username @@ -61,13 +61,13 @@ func (in *PostgreSQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, vali } func (in *PostgreSQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.PostgreSQLServerConfig) + dst := dstRaw.(*v2beta1.PostgreSQLServerConfig) dst.ObjectMeta = in.ObjectMeta dst.Spec.Address = in.Spec.Address dst.Spec.Credentials.Username = in.Spec.Credentials.Username dst.Spec.Credentials.Password = in.Spec.Credentials.Password if in.Spec.Credentials.SecretRef != nil { - dst.Spec.Credentials.SecretRef = &v2alpha1.DatabaseCredentialsSecretRef{ + dst.Spec.Credentials.SecretRef = &v2beta1.DatabaseCredentialsSecretRef{ Name: in.Spec.Credentials.SecretRef.Name, Namespace: in.Spec.Credentials.SecretRef.Namespace, UsernameKey: in.Spec.Credentials.SecretRef.UsernameKey, @@ -78,7 +78,7 @@ func (in *PostgreSQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { } func (in *PostgreSQLServerConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.PostgreSQLServerConfig) + src := srcRaw.(*v2beta1.PostgreSQLServerConfig) in.ObjectMeta = src.ObjectMeta in.Spec.Address = src.Spec.Address in.Spec.Credentials.Username = src.Spec.Credentials.Username @@ -104,22 +104,22 @@ func (ksc *KafkaServerConfig) SetupWebhookWithManager(mgr ctrl.Manager) error { } func (ksc *KafkaServerConfig) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.KafkaServerConfig) + dst := dstRaw.(*v2beta1.KafkaServerConfig) dst.ObjectMeta = ksc.ObjectMeta // convert each spec attribute dst.Spec.Workload.Name = ksc.Spec.Service.Name dst.Spec.Workload.Kind = ksc.Spec.Service.Kind dst.Spec.NoAutoCreateIntentsForOperator = ksc.Spec.NoAutoCreateIntentsForOperator dst.Spec.Addr = ksc.Spec.Addr - dst.Spec.TLS = v2alpha1.TLSSource{ + dst.Spec.TLS = v2beta1.TLSSource{ CertFile: ksc.Spec.TLS.CertFile, KeyFile: ksc.Spec.TLS.KeyFile, RootCAFile: ksc.Spec.TLS.RootCAFile, } - dst.Spec.Topics = make([]v2alpha1.TopicConfig, len(ksc.Spec.Topics)) + dst.Spec.Topics = make([]v2beta1.TopicConfig, len(ksc.Spec.Topics)) for i, topic := range ksc.Spec.Topics { dst.Spec.Topics[i].Topic = topic.Topic - dst.Spec.Topics[i].Pattern = v2alpha1.ResourcePatternType(topic.Pattern) + dst.Spec.Topics[i].Pattern = v2beta1.ResourcePatternType(topic.Pattern) dst.Spec.Topics[i].ClientIdentityRequired = topic.ClientIdentityRequired dst.Spec.Topics[i].IntentsRequired = topic.IntentsRequired } @@ -127,7 +127,7 @@ func (ksc *KafkaServerConfig) ConvertTo(dstRaw conversion.Hub) error { } func (ksc *KafkaServerConfig) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.KafkaServerConfig) + src := srcRaw.(*v2beta1.KafkaServerConfig) ksc.ObjectMeta = src.ObjectMeta // convert each spec attribute ksc.Spec.Service.Name = src.Spec.Workload.Name @@ -159,7 +159,7 @@ func (in *ProtectedService) SetupWebhookWithManager(mgr ctrl.Manager, validator } func (in *ProtectedService) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.ProtectedService) + dst := dstRaw.(*v2beta1.ProtectedService) dst.ObjectMeta = in.ObjectMeta dst.Spec.Name = in.Spec.Name dst.Spec.Kind = in.Spec.Kind @@ -167,7 +167,7 @@ func (in *ProtectedService) ConvertTo(dstRaw conversion.Hub) error { } func (in *ProtectedService) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.ProtectedService) + src := srcRaw.(*v2beta1.ProtectedService) in.ObjectMeta = src.ObjectMeta in.Spec.Name = src.Spec.Name in.Spec.Kind = src.Spec.Kind @@ -183,20 +183,20 @@ func (in *ClientIntents) SetupWebhookWithManager(mgr ctrl.Manager, validator web } func (in *ClientIntents) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v2alpha1.ClientIntents) + dst := dstRaw.(*v2beta1.ClientIntents) dst.ObjectMeta = in.ObjectMeta if dst.Spec == nil { - dst.Spec = &v2alpha1.IntentsSpec{} + dst.Spec = &v2beta1.IntentsSpec{} } dst.Status.UpToDate = in.Status.UpToDate dst.Status.ObservedGeneration = in.Status.ObservedGeneration - dst.Status.ResolvedIPs = lo.Map(in.Status.ResolvedIPs, func(resolvedIPs ResolvedIPs, _ int) v2alpha1.ResolvedIPs { - return v2alpha1.ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} + dst.Status.ResolvedIPs = lo.Map(in.Status.ResolvedIPs, func(resolvedIPs ResolvedIPs, _ int) v2beta1.ResolvedIPs { + return v2beta1.ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} }) dst.Spec.Workload.Name = in.Spec.Service.Name dst.Spec.Workload.Kind = in.Spec.Service.Kind - dst.Spec.Targets = make([]v2alpha1.Target, len(in.Spec.Calls)) + dst.Spec.Targets = make([]v2beta1.Target, len(in.Spec.Calls)) for i, call := range in.Spec.Calls { if call.IsTargetInCluster() && call.Type != IntentTypeKafka { var name string @@ -205,85 +205,85 @@ func (in *ClientIntents) ConvertTo(dstRaw conversion.Hub) error { } else { name = call.GetServerFullyQualifiedName(in.Namespace) } - kubernetesTarget := v2alpha1.KubernetesTarget{Name: name, Kind: call.GetTargetServerKind()} + kubernetesTarget := v2beta1.KubernetesTarget{Name: name, Kind: call.GetTargetServerKind()} if kubernetesTarget.Kind == serviceidentity.KindOtterizeLegacy { // This is an internal kind the user doesn't need to see it kubernetesTarget.Kind = "" } if call.Type == IntentTypeHTTP && len(call.HTTPResources) > 0 { - httpTargets := make([]v2alpha1.HTTPTarget, len(call.HTTPResources)) + httpTargets := make([]v2beta1.HTTPTarget, len(call.HTTPResources)) for j, http := range call.HTTPResources { - methods := lo.Map(http.Methods, func(method HTTPMethod, _ int) v2alpha1.HTTPMethod { return v2alpha1.HTTPMethod(method) }) - httpTargets[j] = v2alpha1.HTTPTarget{Path: http.Path, Methods: methods} + methods := lo.Map(http.Methods, func(method HTTPMethod, _ int) v2beta1.HTTPMethod { return v2beta1.HTTPMethod(method) }) + httpTargets[j] = v2beta1.HTTPTarget{Path: http.Path, Methods: methods} } kubernetesTarget.HTTP = httpTargets } - dst.Spec.Targets[i] = v2alpha1.Target{Kubernetes: &kubernetesTarget} + dst.Spec.Targets[i] = v2beta1.Target{Kubernetes: &kubernetesTarget} continue } if call.Type == IntentTypeKafka && len(call.Topics) > 0 { - topics := lo.Map(call.Topics, func(topic KafkaTopic, _ int) v2alpha1.KafkaTopic { - return v2alpha1.KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation KafkaOperation, _ int) v2alpha1.KafkaOperation { - return v2alpha1.KafkaOperation(operation) + topics := lo.Map(call.Topics, func(topic KafkaTopic, _ int) v2beta1.KafkaTopic { + return v2beta1.KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation KafkaOperation, _ int) v2beta1.KafkaOperation { + return v2beta1.KafkaOperation(operation) })} }) - dst.Spec.Targets[i] = v2alpha1.Target{Kafka: lo.ToPtr(v2alpha1.KafkaTarget{Name: call.Name, Topics: topics})} + dst.Spec.Targets[i] = v2beta1.Target{Kafka: lo.ToPtr(v2beta1.KafkaTarget{Name: call.Name, Topics: topics})} } if call.Type == IntentTypeDatabase && len(call.DatabaseResources) > 0 { - tables := lo.Map(call.DatabaseResources, func(resource DatabaseResource, _ int) v2alpha1.SQLPrivileges { - return v2alpha1.SQLPrivileges{Table: resource.Table, DatabaseName: resource.DatabaseName, Operations: lo.Map(resource.Operations, func(operation DatabaseOperation, _ int) v2alpha1.DatabaseOperation { - return v2alpha1.DatabaseOperation(operation) + tables := lo.Map(call.DatabaseResources, func(resource DatabaseResource, _ int) v2beta1.SQLPrivileges { + return v2beta1.SQLPrivileges{Table: resource.Table, DatabaseName: resource.DatabaseName, Operations: lo.Map(resource.Operations, func(operation DatabaseOperation, _ int) v2beta1.DatabaseOperation { + return v2beta1.DatabaseOperation(operation) })} }) - dst.Spec.Targets[i] = v2alpha1.Target{SQL: lo.ToPtr(v2alpha1.SQLTarget{Name: call.Name, Privileges: tables})} + dst.Spec.Targets[i] = v2beta1.Target{SQL: lo.ToPtr(v2beta1.SQLTarget{Name: call.Name, Privileges: tables})} continue } if call.Type == IntentTypeAWS { - dst.Spec.Targets[i] = v2alpha1.Target{AWS: lo.ToPtr(v2alpha1.AWSTarget{ARN: call.Name, Actions: call.AWSActions})} + dst.Spec.Targets[i] = v2beta1.Target{AWS: lo.ToPtr(v2beta1.AWSTarget{ARN: call.Name, Actions: call.AWSActions})} continue } if call.Type == IntentTypeGCP { - dst.Spec.Targets[i] = v2alpha1.Target{GCP: lo.ToPtr(v2alpha1.GCPTarget{Resource: call.Name, Permissions: call.GCPPermissions})} + dst.Spec.Targets[i] = v2beta1.Target{GCP: lo.ToPtr(v2beta1.GCPTarget{Resource: call.Name, Permissions: call.GCPPermissions})} continue } if call.Type == IntentTypeAzure { - dst.Spec.Targets[i] = v2alpha1.Target{Azure: lo.ToPtr(v2alpha1.AzureTarget{Scope: call.Name, Roles: call.AzureRoles})} + dst.Spec.Targets[i] = v2beta1.Target{Azure: lo.ToPtr(v2beta1.AzureTarget{Scope: call.Name, Roles: call.AzureRoles})} 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) }) + dst.Spec.Targets[i].Azure.Actions = lo.Map(call.AzureActions, func(action AzureAction, _ int) v2beta1.AzureAction { return v2beta1.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) }) + dst.Spec.Targets[i].Azure.DataActions = lo.Map(call.AzureDataActions, func(action AzureDataAction, _ int) v2beta1.AzureDataAction { return v2beta1.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 = &v2beta1.AzureKeyVaultPolicy{} + dst.Spec.Targets[i].Azure.KeyVaultPolicy.KeyPermissions = lo.Map(call.AzureKeyVaultPolicy.KeyPermissions, func(permission AzureKeyVaultKeyPermission, _ int) v2beta1.AzureKeyVaultKeyPermission { + return v2beta1.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.SecretPermissions = lo.Map(call.AzureKeyVaultPolicy.SecretPermissions, func(permission AzureKeyVaultSecretPermission, _ int) v2beta1.AzureKeyVaultSecretPermission { + return v2beta1.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.CertificatePermissions = lo.Map(call.AzureKeyVaultPolicy.CertificatePermissions, func(permission AzureKeyVaultCertificatePermission, _ int) v2beta1.AzureKeyVaultCertificatePermission { + return v2beta1.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.StoragePermissions = lo.Map(call.AzureKeyVaultPolicy.StoragePermissions, func(permission AzureKeyVaultStoragePermission, _ int) v2beta1.AzureKeyVaultStoragePermission { + return v2beta1.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})} + dst.Spec.Targets[i] = v2beta1.Target{Internet: lo.ToPtr(v2beta1.Internet{Domains: call.Internet.Domains, Ports: call.Internet.Ports, Ips: call.Internet.Ips})} } } return nil } func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v2alpha1.ClientIntents) + src := srcRaw.(*v2beta1.ClientIntents) in.ObjectMeta = src.ObjectMeta in.Status.UpToDate = src.Status.UpToDate in.Status.ObservedGeneration = src.Status.ObservedGeneration - in.Status.ResolvedIPs = lo.Map(src.Status.ResolvedIPs, func(resolvedIPs v2alpha1.ResolvedIPs, _ int) ResolvedIPs { + in.Status.ResolvedIPs = lo.Map(src.Status.ResolvedIPs, func(resolvedIPs v2beta1.ResolvedIPs, _ int) ResolvedIPs { return ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} }) in.Spec = &IntentsSpec{} @@ -300,8 +300,8 @@ func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { if target.Kubernetes != nil { in.Spec.Calls[i] = Intent{Name: target.Kubernetes.Name, Kind: target.Kubernetes.Kind} if len(target.Kubernetes.HTTP) > 0 { - in.Spec.Calls[i].HTTPResources = lo.Map(target.Kubernetes.HTTP, func(http v2alpha1.HTTPTarget, _ int) HTTPResource { - return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2alpha1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} + in.Spec.Calls[i].HTTPResources = lo.Map(target.Kubernetes.HTTP, func(http v2beta1.HTTPTarget, _ int) HTTPResource { + return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2beta1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} }) in.Spec.Calls[i].Type = IntentTypeHTTP } @@ -310,22 +310,22 @@ func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { if target.Service != nil { in.Spec.Calls[i] = Intent{Name: target.Service.Name, Kind: serviceidentity.KindService} if len(target.Service.HTTP) > 0 { - in.Spec.Calls[i].HTTPResources = lo.Map(target.Service.HTTP, func(http v2alpha1.HTTPTarget, _ int) HTTPResource { - return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2alpha1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} + in.Spec.Calls[i].HTTPResources = lo.Map(target.Service.HTTP, func(http v2beta1.HTTPTarget, _ int) HTTPResource { + return HTTPResource{Path: http.Path, Methods: lo.Map(http.Methods, func(method v2beta1.HTTPMethod, _ int) HTTPMethod { return HTTPMethod(method) })} }) in.Spec.Calls[i].Type = IntentTypeHTTP } continue } if target.Kafka != nil { - in.Spec.Calls[i] = Intent{Type: IntentTypeKafka, Name: target.Kafka.Name, Topics: lo.Map(target.Kafka.Topics, func(topic v2alpha1.KafkaTopic, _ int) KafkaTopic { - return KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation v2alpha1.KafkaOperation, _ int) KafkaOperation { return KafkaOperation(operation) })} + in.Spec.Calls[i] = Intent{Type: IntentTypeKafka, Name: target.Kafka.Name, Topics: lo.Map(target.Kafka.Topics, func(topic v2beta1.KafkaTopic, _ int) KafkaTopic { + return KafkaTopic{Name: topic.Name, Operations: lo.Map(topic.Operations, func(operation v2beta1.KafkaOperation, _ int) KafkaOperation { return KafkaOperation(operation) })} })} continue } if target.SQL != nil { - in.Spec.Calls[i] = Intent{Type: IntentTypeDatabase, Name: target.SQL.Name, DatabaseResources: lo.Map(target.SQL.Privileges, func(permission v2alpha1.SQLPrivileges, _ int) DatabaseResource { - return DatabaseResource{Table: permission.Table, DatabaseName: permission.DatabaseName, Operations: lo.Map(permission.Operations, func(operation v2alpha1.DatabaseOperation, _ int) DatabaseOperation { + in.Spec.Calls[i] = Intent{Type: IntentTypeDatabase, Name: target.SQL.Name, DatabaseResources: lo.Map(target.SQL.Privileges, func(permission v2beta1.SQLPrivileges, _ int) DatabaseResource { + return DatabaseResource{Table: permission.Table, DatabaseName: permission.DatabaseName, Operations: lo.Map(permission.Operations, func(operation v2beta1.DatabaseOperation, _ int) DatabaseOperation { return DatabaseOperation(operation) })} })} @@ -342,25 +342,25 @@ 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) }) + in.Spec.Calls[i].AzureActions = lo.Map(target.Azure.Actions, func(action v2beta1.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) }) + in.Spec.Calls[i].AzureDataActions = lo.Map(target.Azure.DataActions, func(action v2beta1.AzureDataAction, _ int) AzureDataAction { return AzureDataAction(action) }) } if target.Azure.KeyVaultPolicy == nil { continue } in.Spec.Calls[i].AzureKeyVaultPolicy = &AzureKeyVaultPolicy{} - in.Spec.Calls[i].AzureKeyVaultPolicy.KeyPermissions = lo.Map(target.Azure.KeyVaultPolicy.KeyPermissions, func(permission v2alpha1.AzureKeyVaultKeyPermission, _ int) AzureKeyVaultKeyPermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.KeyPermissions = lo.Map(target.Azure.KeyVaultPolicy.KeyPermissions, func(permission v2beta1.AzureKeyVaultKeyPermission, _ int) AzureKeyVaultKeyPermission { return AzureKeyVaultKeyPermission(permission) }) - in.Spec.Calls[i].AzureKeyVaultPolicy.SecretPermissions = lo.Map(target.Azure.KeyVaultPolicy.SecretPermissions, func(permission v2alpha1.AzureKeyVaultSecretPermission, _ int) AzureKeyVaultSecretPermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.SecretPermissions = lo.Map(target.Azure.KeyVaultPolicy.SecretPermissions, func(permission v2beta1.AzureKeyVaultSecretPermission, _ int) AzureKeyVaultSecretPermission { return AzureKeyVaultSecretPermission(permission) }) - in.Spec.Calls[i].AzureKeyVaultPolicy.CertificatePermissions = lo.Map(target.Azure.KeyVaultPolicy.CertificatePermissions, func(permission v2alpha1.AzureKeyVaultCertificatePermission, _ int) AzureKeyVaultCertificatePermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.CertificatePermissions = lo.Map(target.Azure.KeyVaultPolicy.CertificatePermissions, func(permission v2beta1.AzureKeyVaultCertificatePermission, _ int) AzureKeyVaultCertificatePermission { return AzureKeyVaultCertificatePermission(permission) }) - in.Spec.Calls[i].AzureKeyVaultPolicy.StoragePermissions = lo.Map(target.Azure.KeyVaultPolicy.StoragePermissions, func(permission v2alpha1.AzureKeyVaultStoragePermission, _ int) AzureKeyVaultStoragePermission { + in.Spec.Calls[i].AzureKeyVaultPolicy.StoragePermissions = lo.Map(target.Azure.KeyVaultPolicy.StoragePermissions, func(permission v2beta1.AzureKeyVaultStoragePermission, _ int) AzureKeyVaultStoragePermission { return AzureKeyVaultStoragePermission(permission) }) } diff --git a/src/operator/api/v1beta1/webhooks_test.go b/src/operator/api/v1beta1/webhooks_test.go index 3fb6809b0..4e7041ede 100644 --- a/src/operator/api/v1beta1/webhooks_test.go +++ b/src/operator/api/v1beta1/webhooks_test.go @@ -1,7 +1,7 @@ package v1beta1 import ( - "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/stretchr/testify/suite" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "testing" @@ -34,7 +34,7 @@ func (t *WebhooksTestSuite) TestMySQLServerConfigConversion() { } // ConvertTo - dstRaw := &v2alpha1.MySQLServerConfig{} + dstRaw := &v2beta1.MySQLServerConfig{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -71,7 +71,7 @@ func (t *WebhooksTestSuite) TestPostgreSQLServerConfigConversion() { } // ConvertTo - dstRaw := &v2alpha1.PostgreSQLServerConfig{} + dstRaw := &v2beta1.PostgreSQLServerConfig{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -107,7 +107,7 @@ func (t *WebhooksTestSuite) TestKafkaServerConfigConversion() { } // ConvertTo - dstRaw := &v2alpha1.KafkaServerConfig{} + dstRaw := &v2beta1.KafkaServerConfig{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -134,7 +134,7 @@ func (t *WebhooksTestSuite) TestProtectedServiceConversion() { } // ConvertTo - dstRaw := &v2alpha1.ProtectedService{} + dstRaw := &v2beta1.ProtectedService{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -176,7 +176,7 @@ func (t *WebhooksTestSuite) TestClientIntentsKubernetes() { }} // ConvertTo - dstRaw := &v2alpha1.ClientIntents{} + dstRaw := &v2beta1.ClientIntents{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) @@ -190,12 +190,12 @@ func (t *WebhooksTestSuite) TestClientIntentsKubernetes() { } func (t *WebhooksTestSuite) TestClientIntentsFromV2_serviceKubernetesDefault() { - // Create a v2alpha1.ClientIntents with random data - original := &v2alpha1.ClientIntents{ - Spec: &v2alpha1.IntentsSpec{ - Targets: []v2alpha1.Target{ + // Create a v2beta1.ClientIntents with random data + original := &v2beta1.ClientIntents{ + Spec: &v2beta1.IntentsSpec{ + Targets: []v2beta1.Target{ { - Service: &v2alpha1.ServiceTarget{ + Service: &v2beta1.ServiceTarget{ Name: "kubernetes.default", }, }, @@ -211,20 +211,20 @@ func (t *WebhooksTestSuite) TestClientIntentsFromV2_serviceKubernetesDefault() { } func (t *WebhooksTestSuite) TestClientIntentsFromV2_EmptySliceHTTPShouldNotBeTypeHTTP() { - // Create a v2alpha1.ClientIntents with random data - original := &v2alpha1.ClientIntents{ - Spec: &v2alpha1.IntentsSpec{ - Targets: []v2alpha1.Target{ + // Create a v2beta1.ClientIntents with random data + original := &v2beta1.ClientIntents{ + Spec: &v2beta1.IntentsSpec{ + Targets: []v2beta1.Target{ { - Service: &v2alpha1.ServiceTarget{ + Service: &v2beta1.ServiceTarget{ Name: "test", - HTTP: []v2alpha1.HTTPTarget{}, + HTTP: []v2beta1.HTTPTarget{}, }, }, { - Kubernetes: &v2alpha1.KubernetesTarget{ + Kubernetes: &v2beta1.KubernetesTarget{ Name: "test2", - HTTP: []v2alpha1.HTTPTarget{}, + HTTP: []v2beta1.HTTPTarget{}, }, }, }, @@ -274,7 +274,7 @@ func (t *WebhooksTestSuite) TestClientIntentsAzureActionsDataActions() { } // ConvertTo - dstRaw := &v2alpha1.ClientIntents{} + dstRaw := &v2beta1.ClientIntents{} err := original.ConvertTo(dstRaw) t.Require().NoError(err) diff --git a/src/operator/api/v2alpha1/webhooks.go b/src/operator/api/v2alpha1/webhooks.go index e5bd2d70c..4af28888d 100644 --- a/src/operator/api/v2alpha1/webhooks.go +++ b/src/operator/api/v2alpha1/webhooks.go @@ -1,7 +1,11 @@ package v2alpha1 import ( + "github.com/otterize/intents-operator/src/operator/api/v2beta1" + "github.com/samber/lo" + "golang.org/x/exp/slices" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/webhook" ) @@ -13,7 +17,40 @@ func (in *MySQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, validator Complete() } -func (in *MySQLServerConfig) Hub() {} +// convert to and convert from functions for MySQLServerConfig (hub version is v2beta1) +func (in *MySQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v2beta1.MySQLServerConfig) + dst.ObjectMeta = in.ObjectMeta + dst.Spec.Address = in.Spec.Address + dst.Spec.Credentials.Username = in.Spec.Credentials.Username + dst.Spec.Credentials.Password = in.Spec.Credentials.Password + if in.Spec.Credentials.SecretRef != nil { + dst.Spec.Credentials.SecretRef = &v2beta1.DatabaseCredentialsSecretRef{ + Name: in.Spec.Credentials.SecretRef.Name, + Namespace: in.Spec.Credentials.SecretRef.Namespace, + UsernameKey: in.Spec.Credentials.SecretRef.UsernameKey, + PasswordKey: in.Spec.Credentials.SecretRef.PasswordKey, + } + } + return nil +} + +func (in *MySQLServerConfig) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v2beta1.MySQLServerConfig) + in.ObjectMeta = src.ObjectMeta + in.Spec.Address = src.Spec.Address + in.Spec.Credentials.Username = src.Spec.Credentials.Username + in.Spec.Credentials.Password = src.Spec.Credentials.Password + if src.Spec.Credentials.SecretRef != nil { + in.Spec.Credentials.SecretRef = &DatabaseCredentialsSecretRef{ + Name: src.Spec.Credentials.SecretRef.Name, + Namespace: src.Spec.Credentials.SecretRef.Namespace, + UsernameKey: src.Spec.Credentials.SecretRef.UsernameKey, + PasswordKey: src.Spec.Credentials.SecretRef.PasswordKey, + } + } + return nil +} // PostgreSQLServerConfig // @@ -23,7 +60,39 @@ func (in *PostgreSQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, vali Complete() } -func (in *PostgreSQLServerConfig) Hub() {} +func (in *PostgreSQLServerConfig) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v2beta1.PostgreSQLServerConfig) + dst.ObjectMeta = in.ObjectMeta + dst.Spec.Address = in.Spec.Address + dst.Spec.Credentials.Username = in.Spec.Credentials.Username + dst.Spec.Credentials.Password = in.Spec.Credentials.Password + if in.Spec.Credentials.SecretRef != nil { + dst.Spec.Credentials.SecretRef = &v2beta1.DatabaseCredentialsSecretRef{ + Name: in.Spec.Credentials.SecretRef.Name, + Namespace: in.Spec.Credentials.SecretRef.Namespace, + UsernameKey: in.Spec.Credentials.SecretRef.UsernameKey, + PasswordKey: in.Spec.Credentials.SecretRef.PasswordKey, + } + } + return nil +} + +func (in *PostgreSQLServerConfig) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v2beta1.PostgreSQLServerConfig) + in.ObjectMeta = src.ObjectMeta + in.Spec.Address = src.Spec.Address + in.Spec.Credentials.Username = src.Spec.Credentials.Username + in.Spec.Credentials.Password = src.Spec.Credentials.Password + if src.Spec.Credentials.SecretRef != nil { + in.Spec.Credentials.SecretRef = &DatabaseCredentialsSecretRef{ + Name: src.Spec.Credentials.SecretRef.Name, + Namespace: src.Spec.Credentials.SecretRef.Namespace, + UsernameKey: src.Spec.Credentials.SecretRef.UsernameKey, + PasswordKey: src.Spec.Credentials.SecretRef.PasswordKey, + } + } + return nil +} // KafkaServerConfig // @@ -33,7 +102,45 @@ func (ksc *KafkaServerConfig) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -func (ksc *KafkaServerConfig) Hub() {} +func (ksc *KafkaServerConfig) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v2beta1.KafkaServerConfig) + dst.ObjectMeta = ksc.ObjectMeta + dst.Spec.Addr = ksc.Spec.Addr + dst.Spec.TLS.CertFile = ksc.Spec.TLS.CertFile + dst.Spec.TLS.KeyFile = ksc.Spec.TLS.KeyFile + dst.Spec.TLS.RootCAFile = ksc.Spec.TLS.RootCAFile + dst.Spec.Workload.Name = ksc.Spec.Workload.Name + dst.Spec.Workload.Kind = ksc.Spec.Workload.Kind + dst.Spec.Topics = make([]v2beta1.TopicConfig, len(ksc.Spec.Topics)) + for i, topic := range ksc.Spec.Topics { + dst.Spec.Topics[i].Topic = topic.Topic + dst.Spec.Topics[i].Pattern = v2beta1.ResourcePatternType(topic.Pattern) + dst.Spec.Topics[i].ClientIdentityRequired = topic.ClientIdentityRequired + dst.Spec.Topics[i].IntentsRequired = topic.IntentsRequired + } + dst.Spec.NoAutoCreateIntentsForOperator = ksc.Spec.NoAutoCreateIntentsForOperator + return nil +} + +func (ksc *KafkaServerConfig) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v2beta1.KafkaServerConfig) + ksc.ObjectMeta = src.ObjectMeta + ksc.Spec.Addr = src.Spec.Addr + ksc.Spec.TLS.CertFile = src.Spec.TLS.CertFile + ksc.Spec.TLS.KeyFile = src.Spec.TLS.KeyFile + ksc.Spec.TLS.RootCAFile = src.Spec.TLS.RootCAFile + ksc.Spec.Workload.Name = src.Spec.Workload.Name + ksc.Spec.Workload.Kind = src.Spec.Workload.Kind + ksc.Spec.Topics = make([]TopicConfig, len(src.Spec.Topics)) + for i, topic := range src.Spec.Topics { + ksc.Spec.Topics[i].Topic = topic.Topic + ksc.Spec.Topics[i].Pattern = ResourcePatternType(topic.Pattern) + ksc.Spec.Topics[i].ClientIdentityRequired = topic.ClientIdentityRequired + ksc.Spec.Topics[i].IntentsRequired = topic.IntentsRequired + } + ksc.Spec.NoAutoCreateIntentsForOperator = src.Spec.NoAutoCreateIntentsForOperator + return nil +} // ProtectedService // @@ -43,7 +150,21 @@ func (in *ProtectedService) SetupWebhookWithManager(mgr ctrl.Manager, validator Complete() } -func (in *ProtectedService) Hub() {} +func (in *ProtectedService) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v2beta1.ProtectedService) + dst.ObjectMeta = in.ObjectMeta + dst.Spec.Name = in.Spec.Name + dst.Spec.Kind = in.Spec.Kind + return nil +} + +func (in *ProtectedService) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v2beta1.ProtectedService) + in.ObjectMeta = src.ObjectMeta + in.Spec.Name = src.Spec.Name + in.Spec.Kind = src.Spec.Kind + return nil +} // ClientIntents // @@ -53,4 +174,345 @@ func (in *ClientIntents) SetupWebhookWithManager(mgr ctrl.Manager, validator web Complete() } -func (in *ClientIntents) Hub() {} +func (in *ClientIntents) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v2beta1.ClientIntents) + dst.ObjectMeta = in.ObjectMeta + + // convert status + dst.Status.UpToDate = in.Status.UpToDate + dst.Status.ObservedGeneration = in.Status.ObservedGeneration + dst.Status.ResolvedIPs = lo.Map(in.Status.ResolvedIPs, func(resolvedIPs ResolvedIPs, _ int) v2beta1.ResolvedIPs { + return v2beta1.ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} + }) + + if dst.Spec == nil { + dst.Spec = &v2beta1.IntentsSpec{} + } + + // convert workload + dst.Spec.Workload.Name = in.Spec.Workload.Name + dst.Spec.Workload.Kind = in.Spec.Workload.Kind + + // copy targets + dst.Spec.Targets = make([]v2beta1.Target, len(in.Spec.Targets)) + for i, target := range in.Spec.Targets { + dst.Spec.Targets[i] = v2beta1.Target{} + if in.Spec.Targets[i].Service != nil { + dst.Spec.Targets[i].Service = &v2beta1.ServiceTarget{Name: target.Service.Name} + if target.Service.HTTP != nil { + dst.Spec.Targets[i].Service.HTTP = lo.Map( + target.Service.HTTP, + func(http HTTPTarget, _ int) v2beta1.HTTPTarget { + return v2beta1.HTTPTarget{ + Path: http.Path, + Methods: lo.Map( + http.Methods, + func(method HTTPMethod, _ int) v2beta1.HTTPMethod { + return v2beta1.HTTPMethod(method) + }), + } + }) + } + } + if in.Spec.Targets[i].Kubernetes != nil { + dst.Spec.Targets[i].Kubernetes = &v2beta1.KubernetesTarget{ + Kind: target.Kubernetes.Kind, + Name: target.Kubernetes.Name, + } + if target.Kubernetes.HTTP != nil { + dst.Spec.Targets[i].Kubernetes.HTTP = lo.Map( + target.Kubernetes.HTTP, + func(http HTTPTarget, _ int) v2beta1.HTTPTarget { + return v2beta1.HTTPTarget{ + Path: http.Path, + Methods: lo.Map( + http.Methods, + func(method HTTPMethod, _ int) v2beta1.HTTPMethod { + return v2beta1.HTTPMethod(method) + }), + } + }) + } + } + if in.Spec.Targets[i].Kafka != nil { + dst.Spec.Targets[i].Kafka = &v2beta1.KafkaTarget{ + Name: target.Kafka.Name, + Topics: lo.Map( + target.Kafka.Topics, + func(topic KafkaTopic, _ int) v2beta1.KafkaTopic { + return v2beta1.KafkaTopic{ + Name: topic.Name, + Operations: lo.Map( + topic.Operations, + func(operation KafkaOperation, _ int) v2beta1.KafkaOperation { + return v2beta1.KafkaOperation(operation) + }), + } + }, + ), + } + } + if in.Spec.Targets[i].SQL != nil { + dst.Spec.Targets[i].SQL = &v2beta1.SQLTarget{ + Name: target.SQL.Name, + Privileges: lo.Map( + target.SQL.Privileges, + func(privilege SQLPrivileges, _ int) v2beta1.SQLPrivileges { + return v2beta1.SQLPrivileges{ + DatabaseName: privilege.DatabaseName, + Table: privilege.Table, + Operations: lo.Map( + privilege.Operations, + func(operation DatabaseOperation, _ int) v2beta1.DatabaseOperation { + return v2beta1.DatabaseOperation(operation) + }, + ), + } + }, + ), + } + } + if in.Spec.Targets[i].GCP != nil { + dst.Spec.Targets[i].GCP = &v2beta1.GCPTarget{ + Resource: target.GCP.Resource, + Permissions: slices.Clone(target.GCP.Permissions), + } + } + if in.Spec.Targets[i].AWS != nil { + dst.Spec.Targets[i].AWS = &v2beta1.AWSTarget{ + ARN: target.AWS.ARN, + Actions: slices.Clone(target.AWS.Actions), + } + } + if in.Spec.Targets[i].Azure != nil { + dst.Spec.Targets[i].Azure = &v2beta1.AzureTarget{Scope: target.Azure.Scope} + if target.Azure.Actions != nil { + dst.Spec.Targets[i].Azure.Actions = lo.Map( + target.Azure.Actions, + func(action AzureAction, _ int) v2beta1.AzureAction { + return v2beta1.AzureAction(action) + }, + ) + } + if target.Azure.DataActions != nil { + dst.Spec.Targets[i].Azure.DataActions = lo.Map( + target.Azure.DataActions, + func(dataAction AzureDataAction, _ int) v2beta1.AzureDataAction { + return v2beta1.AzureDataAction(dataAction) + }, + ) + } + dst.Spec.Targets[i].Azure.Roles = slices.Clone(target.Azure.Roles) + if target.Azure.KeyVaultPolicy != nil { + dst.Spec.Targets[i].Azure.KeyVaultPolicy = &v2beta1.AzureKeyVaultPolicy{ + CertificatePermissions: lo.Map( + target.Azure.KeyVaultPolicy.CertificatePermissions, + func(permission AzureKeyVaultCertificatePermission, _ int) v2beta1.AzureKeyVaultCertificatePermission { + return v2beta1.AzureKeyVaultCertificatePermission(permission) + }, + ), + KeyPermissions: lo.Map( + target.Azure.KeyVaultPolicy.KeyPermissions, + func(permission AzureKeyVaultKeyPermission, _ int) v2beta1.AzureKeyVaultKeyPermission { + return v2beta1.AzureKeyVaultKeyPermission(permission) + }, + ), + SecretPermissions: lo.Map( + target.Azure.KeyVaultPolicy.SecretPermissions, + func(permission AzureKeyVaultSecretPermission, _ int) v2beta1.AzureKeyVaultSecretPermission { + return v2beta1.AzureKeyVaultSecretPermission(permission) + }, + ), + StoragePermissions: lo.Map( + target.Azure.KeyVaultPolicy.StoragePermissions, + func(permission AzureKeyVaultStoragePermission, _ int) v2beta1.AzureKeyVaultStoragePermission { + return v2beta1.AzureKeyVaultStoragePermission(permission) + }, + ), + } + } + } + if target.Internet != nil { + dst.Spec.Targets[i].Internet = &v2beta1.Internet{ + Domains: slices.Clone(target.Internet.Domains), + Ips: slices.Clone(target.Internet.Ips), + Ports: slices.Clone(target.Internet.Ports), + } + } + } + return nil +} + +func (in *ClientIntents) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v2beta1.ClientIntents) + in.ObjectMeta = src.ObjectMeta + + // convert status + in.Status.UpToDate = src.Status.UpToDate + in.Status.ObservedGeneration = src.Status.ObservedGeneration + in.Status.ResolvedIPs = lo.Map(src.Status.ResolvedIPs, func(resolvedIPs v2beta1.ResolvedIPs, _ int) ResolvedIPs { + return ResolvedIPs{DNS: resolvedIPs.DNS, IPs: slices.Clone(resolvedIPs.IPs)} + }) + + if in.Spec == nil { + in.Spec = &IntentsSpec{} + } + + // convert workload + in.Spec.Workload.Name = src.Spec.Workload.Name + in.Spec.Workload.Kind = src.Spec.Workload.Kind + + // copy targets + in.Spec.Targets = make([]Target, len(src.Spec.Targets)) + for i, target := range src.Spec.Targets { + in.Spec.Targets[i] = Target{} + if src.Spec.Targets[i].Service != nil { + in.Spec.Targets[i].Service = &ServiceTarget{Name: target.Service.Name} + if target.Service.HTTP != nil { + in.Spec.Targets[i].Service.HTTP = lo.Map( + target.Service.HTTP, + func(http v2beta1.HTTPTarget, _ int) HTTPTarget { + return HTTPTarget{ + Path: http.Path, + Methods: lo.Map( + http.Methods, + func(method v2beta1.HTTPMethod, _ int) HTTPMethod { + return HTTPMethod(method) + }, + ), + } + }, + ) + } + } + if src.Spec.Targets[i].Kubernetes != nil { + in.Spec.Targets[i].Kubernetes = &KubernetesTarget{ + Kind: target.Kubernetes.Kind, + Name: target.Kubernetes.Name, + } + if target.Kubernetes.HTTP != nil { + in.Spec.Targets[i].Kubernetes.HTTP = lo.Map( + target.Kubernetes.HTTP, + func(http v2beta1.HTTPTarget, _ int) HTTPTarget { + return HTTPTarget{ + Path: http.Path, + Methods: lo.Map( + http.Methods, + func(method v2beta1.HTTPMethod, _ int) HTTPMethod { + return HTTPMethod(method) + }, + ), + } + }, + ) + } + } + if src.Spec.Targets[i].Kafka != nil { + in.Spec.Targets[i].Kafka = &KafkaTarget{ + Name: target.Kafka.Name, + Topics: lo.Map( + target.Kafka.Topics, + func(topic v2beta1.KafkaTopic, _ int) KafkaTopic { + return KafkaTopic{ + Name: topic.Name, + Operations: lo.Map( + topic.Operations, + func(operation v2beta1.KafkaOperation, _ int) KafkaOperation { + return KafkaOperation(operation) + }, + ), + } + }, + ), + } + } + if src.Spec.Targets[i].SQL != nil { + in.Spec.Targets[i].SQL = &SQLTarget{ + Name: target.SQL.Name, + Privileges: lo.Map( + target.SQL.Privileges, + func(privilege v2beta1.SQLPrivileges, _ int) SQLPrivileges { + return SQLPrivileges{ + DatabaseName: privilege.DatabaseName, + Table: privilege.Table, + Operations: lo.Map( + privilege.Operations, + func(operation v2beta1.DatabaseOperation, _ int) DatabaseOperation { + return DatabaseOperation(operation) + }, + ), + } + }, + ), + } + } + if src.Spec.Targets[i].GCP != nil { + in.Spec.Targets[i].GCP = &GCPTarget{ + Resource: target.GCP.Resource, + Permissions: slices.Clone(target.GCP.Permissions), + } + } + if src.Spec.Targets[i].AWS != nil { + in.Spec.Targets[i].AWS = &AWSTarget{ + ARN: target.AWS.ARN, + Actions: slices.Clone(target.AWS.Actions), + } + } + if src.Spec.Targets[i].Azure != nil { + in.Spec.Targets[i].Azure = &AzureTarget{Scope: target.Azure.Scope} + if target.Azure.Actions != nil { + in.Spec.Targets[i].Azure.Actions = lo.Map( + target.Azure.Actions, + func(action v2beta1.AzureAction, _ int) AzureAction { + return AzureAction(action) + }, + ) + } + if target.Azure.DataActions != nil { + in.Spec.Targets[i].Azure.DataActions = lo.Map( + target.Azure.DataActions, + func(dataAction v2beta1.AzureDataAction, _ int) AzureDataAction { + return AzureDataAction(dataAction) + }, + ) + } + in.Spec.Targets[i].Azure.Roles = slices.Clone(target.Azure.Roles) + if target.Azure.KeyVaultPolicy != nil { + in.Spec.Targets[i].Azure.KeyVaultPolicy = &AzureKeyVaultPolicy{ + CertificatePermissions: lo.Map( + target.Azure.KeyVaultPolicy.CertificatePermissions, + func(permission v2beta1.AzureKeyVaultCertificatePermission, _ int) AzureKeyVaultCertificatePermission { + return AzureKeyVaultCertificatePermission(permission) + }, + ), + KeyPermissions: lo.Map( + target.Azure.KeyVaultPolicy.KeyPermissions, + func(permission v2beta1.AzureKeyVaultKeyPermission, _ int) AzureKeyVaultKeyPermission { + return AzureKeyVaultKeyPermission(permission) + }, + ), + SecretPermissions: lo.Map( + target.Azure.KeyVaultPolicy.SecretPermissions, + func(permission v2beta1.AzureKeyVaultSecretPermission, _ int) AzureKeyVaultSecretPermission { + return AzureKeyVaultSecretPermission(permission) + }, + ), + StoragePermissions: lo.Map( + target.Azure.KeyVaultPolicy.StoragePermissions, + func(permission v2beta1.AzureKeyVaultStoragePermission, _ int) AzureKeyVaultStoragePermission { + return AzureKeyVaultStoragePermission(permission) + }, + ), + } + } + } + if target.Internet != nil { + in.Spec.Targets[i].Internet = &Internet{ + Domains: slices.Clone(target.Internet.Domains), + Ips: slices.Clone(target.Internet.Ips), + Ports: slices.Clone(target.Internet.Ports), + } + } + } + return nil +} diff --git a/src/operator/api/v2alpha1/webhooks_test.go b/src/operator/api/v2alpha1/webhooks_test.go new file mode 100644 index 000000000..85be47dea --- /dev/null +++ b/src/operator/api/v2alpha1/webhooks_test.go @@ -0,0 +1,251 @@ +package v2alpha1 + +import ( + "github.com/otterize/intents-operator/src/operator/api/v2beta1" + "github.com/stretchr/testify/suite" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +type WebhooksTestSuite struct { + suite.Suite +} + +func (t *WebhooksTestSuite) TestMySQLServerConfigConversion() { + // Create a MySQLServerConfig with random data + original := &MySQLServerConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: MySQLServerConfigSpec{ + Address: "testAddress", + Credentials: DatabaseCredentials{ + Username: "testUsername", + Password: "testPassword", + SecretRef: &DatabaseCredentialsSecretRef{ + Name: "testName", + Namespace: "testNamespace", + UsernameKey: "testUsernameKey", + PasswordKey: "testPasswordKey", + }, + }, + }, + } + + // ConvertTo + dstRaw := &v2beta1.MySQLServerConfig{} + err := original.ConvertTo(dstRaw) + t.Require().NoError(err) + + // ConvertFrom + converted := &MySQLServerConfig{} + err = converted.ConvertFrom(dstRaw) + t.Require().NoError(err) + + t.Require().Equal(original.Spec, converted.Spec) + +} + +// PostgreSQLServerConfig test +func (t *WebhooksTestSuite) TestPostgreSQLServerConfigConversion() { + // Create a PostgreSQLServerConfig with random data + original := &PostgreSQLServerConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: PostgreSQLServerConfigSpec{ + Address: "testAddress", + Credentials: DatabaseCredentials{ + Username: "testUsername", + Password: "testPassword", + SecretRef: &DatabaseCredentialsSecretRef{ + Name: "testName", + Namespace: "testNamespace", + UsernameKey: "testUsernameKey", + PasswordKey: "testPasswordKey", + }, + }, + }, + } + + // ConvertTo + dstRaw := &v2beta1.PostgreSQLServerConfig{} + err := original.ConvertTo(dstRaw) + t.Require().NoError(err) + + // ConvertFrom + converted := &PostgreSQLServerConfig{} + err = converted.ConvertFrom(dstRaw) + t.Require().NoError(err) + + t.Require().Equal(original.Spec, converted.Spec) +} + +// KafkaServerConfig test +func (t *WebhooksTestSuite) TestKafkaServerConfigConversion() { + // Create a KafkaServerConfig with random data + original := &KafkaServerConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: KafkaServerConfigSpec{ + // random data for tests + Workload: Workload{ + Name: "test", + Kind: "test", + }, + NoAutoCreateIntentsForOperator: true, + Addr: "test", + TLS: TLSSource{CertFile: "sa", KeyFile: "aa", RootCAFile: "aa"}, + Topics: []TopicConfig{{ + Topic: "test", + }, + }}, + } + + // ConvertTo + dstRaw := &v2beta1.KafkaServerConfig{} + err := original.ConvertTo(dstRaw) + t.Require().NoError(err) + + // ConvertFrom + converted := &KafkaServerConfig{} + err = converted.ConvertFrom(dstRaw) + t.Require().NoError(err) + + t.Require().Equal(original.Spec, converted.Spec) +} + +// ProtectedService test +func (t *WebhooksTestSuite) TestProtectedServiceConversion() { + // Create a ProtectedService with random data + original := &ProtectedService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: ProtectedServiceSpec{ + Name: "test", + Kind: "Deployment", + }, + } + + // ConvertTo + dstRaw := &v2beta1.ProtectedService{} + err := original.ConvertTo(dstRaw) + t.Require().NoError(err) + + // ConvertFrom + converted := &ProtectedService{} + err = converted.ConvertFrom(dstRaw) + t.Require().NoError(err) + + t.Require().Equal(original.Spec, converted.Spec) +} + +func (t *WebhooksTestSuite) TestClientIntentsKubernetes() { + // Create a ClientIntents with random data + original := &ClientIntents{ + Status: IntentsStatus{ObservedGeneration: 1, UpToDate: true, ResolvedIPs: []ResolvedIPs{{DNS: "a.test", IPs: []string{"1.3.3.7"}}}}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: &IntentsSpec{ + Workload: Workload{ + Name: "test", + Kind: "test", + }, + Targets: []Target{ + { + Kubernetes: &KubernetesTarget{ + Name: "test.test", + }, + }, + { + Kubernetes: &KubernetesTarget{ + Name: "test2", + Kind: "Deployment", + }, + }, + { + Kubernetes: &KubernetesTarget{ + Name: "test3.other-namespace", + Kind: "Deployment", + }, + }, + { + Service: &ServiceTarget{ + Name: "kubernetes.default", + }, + }, + }, + }} + + // ConvertTo + dstRaw := &v2beta1.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) + t.Require().Equal(original.Status, converted.Status) +} + +func (t *WebhooksTestSuite) TestClientIntentsAzureActionsDataActions() { + // Create a ClientIntents with random data + original := &ClientIntents{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: &IntentsSpec{ + Workload: Workload{ + Name: "test", + }, + Targets: []Target{ + { + Azure: &AzureTarget{ + Scope: "testscope1", + DataActions: []AzureDataAction{ + "testDataAction1", + "testDataAction2", + }, + }, + }, + { + Azure: &AzureTarget{ + Scope: "testscope2", + Actions: []AzureAction{ + "testAction1", + "testAction2", + }, + }, + }, + }, + }, + } + + // ConvertTo + dstRaw := &v2beta1.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/v2beta1/clientintents_types.go b/src/operator/api/v2beta1/clientintents_types.go new file mode 100644 index 000000000..a305865a6 --- /dev/null +++ b/src/operator/api/v2beta1/clientintents_types.go @@ -0,0 +1,941 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + "context" + "encoding/json" + "fmt" + "github.com/otterize/intents-operator/src/shared/errors" + "github.com/otterize/intents-operator/src/shared/serviceidresolver/podownerresolver" + "github.com/otterize/intents-operator/src/shared/serviceidresolver/serviceidentity" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "strconv" + "strings" + + "github.com/otterize/intents-operator/src/shared/otterizecloud/graphqlclient" + "github.com/samber/lo" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +const ( + OtterizeAccessLabelPrefix = "intents.otterize.com/access" + OtterizeServiceAccessLabelPrefix = "intents.otterize.com/svc-access" + OtterizeAccessLabelKey = "intents.otterize.com/access-%s" + OtterizeSvcAccessLabelKey = "intents.otterize.com/svc-access-%s" + OtterizeClientLabelKey = "intents.otterize.com/client" + OtterizeServiceLabelKey = "intents.otterize.com/service" + OtterizeOwnerKindLabelKey = "intents.otterize.com/owner-kind" + OtterizeServerLabelKeyDeprecated = "intents.otterize.com/server" + OtterizeKubernetesServiceLabelKeyPrefix = "intents.otterize.com/k8s-svc" + OtterizeKubernetesServiceLabelKey = "intents.otterize.com/k8s-svc-%s" + KubernetesStandardNamespaceNameLabelKey = "kubernetes.io/metadata.name" + AllIntentsRemovedAnnotation = "intents.otterize.com/all-intents-removed" + OtterizeCreatedForServiceAnnotation = "intents.otterize.com/created-for-service" + OtterizeCreatedForIngressAnnotation = "intents.otterize.com/created-for-ingress" + OtterizeSingleNetworkPolicyNameTemplate = "%s-access" + OtterizeLinkerdMeshTLSAnnotationKey = "intents.otterize.com/linkerd-authenticates" + OtterizeLinkerdServerAnnotationKey = "intents.otterize.com/linkerd-server" + OtterizeNetworkPolicyIngressNameTemplate = "%s-ingress" + OtterizeNetworkPolicyEgressNameTemplate = "%s-egress" + OtterizeNetworkPolicy = "intents.otterize.com/network-policy" + OtterizeSvcNetworkPolicy = "intents.otterize.com/svc-network-policy" + OtterizeNetworkPolicyServiceDefaultDeny = "intents.otterize.com/network-policy-service-default-deny" + OtterizeNetworkPolicyExternalTraffic = "intents.otterize.com/network-policy-external-traffic" + ClientIntentsFinalizerName = "intents.otterize.com/client-intents-finalizer" + ProtectedServicesFinalizerName = "intents.otterize.com/protected-services-finalizer" + OtterizeIstioClientAnnotationKeyDeprecated = "intents.otterize.com/istio-client" + OtterizeIstioClientWithKindLabelKey = "intents.otterize.com/istio-client-with-kind" + OtterizeClientServiceAccountAnnotation = "intents.otterize.com/client-intents-service-account" + OtterizeSharedServiceAccountAnnotation = "intents.otterize.com/shared-service-account" + OtterizeMissingSidecarAnnotation = "intents.otterize.com/service-missing-sidecar" + OtterizeServersWithoutSidecarAnnotation = "intents.otterize.com/servers-without-sidecar" + OtterizeTargetServerIndexField = "spec.service.calls.server" + OtterizeKafkaServerConfigServiceNameField = "spec.service.name" + OtterizeProtectedServiceNameIndexField = "spec.name" + OtterizeFormattedTargetServerIndexField = "formattedTargetServer" + OtterizePodByOwnerKindAndNameIndexField = "podByOwnerKindAndName" + EndpointsPodNamesIndexField = "endpointsPodNames" + IngressServiceNamesIndexField = "ingressServiceNames" + MaxOtterizeNameLength = 20 + MaxNamespaceLength = 20 + OtterizeSvcEgressNetworkPolicy = "intents.otterize.com/svc-egress-network-policy" + OtterizeEgressNetworkPolicy = "intents.otterize.com/egress-network-policy" + OtterizeInternetNetworkPolicy = "intents.otterize.com/egress-internet-network-policy" + OtterizeInternetTargetName = "internet" + KubernetesAPIServerName = "kubernetes" + KubernetesAPIServerNamespace = "default" +) + +// +kubebuilder:validation:Enum=http;kafka;database;aws;gcp;azure;internet +type IntentType string + +const ( + IntentTypeHTTP IntentType = "http" + IntentTypeKafka IntentType = "kafka" + IntentTypeDatabase IntentType = "database" + IntentTypeAWS IntentType = "aws" + IntentTypeGCP IntentType = "gcp" + IntentTypeAzure IntentType = "azure" + IntentTypeInternet IntentType = "internet" +) + +// +kubebuilder:validation:Enum=all;consume;produce;create;alter;delete;describe;ClusterAction;DescribeConfigs;AlterConfigs;IdempotentWrite +type KafkaOperation string + +const ( + KafkaOperationAll KafkaOperation = "all" + KafkaOperationConsume KafkaOperation = "consume" + KafkaOperationProduce KafkaOperation = "produce" + KafkaOperationCreate KafkaOperation = "create" + KafkaOperationAlter KafkaOperation = "alter" + KafkaOperationDelete KafkaOperation = "delete" + KafkaOperationDescribe KafkaOperation = "describe" + KafkaOperationClusterAction KafkaOperation = "ClusterAction" + KafkaOperationDescribeConfigs KafkaOperation = "DescribeConfigs" + KafkaOperationAlterConfigs KafkaOperation = "AlterConfigs" + KafkaOperationIdempotentWrite KafkaOperation = "IdempotentWrite" +) + +// +kubebuilder:validation:Enum=GET;POST;PUT;DELETE;OPTIONS;TRACE;PATCH;CONNECT +type HTTPMethod string + +const ( + HTTPMethodGet HTTPMethod = "GET" + HTTPMethodPost HTTPMethod = "POST" + HTTPMethodPut HTTPMethod = "PUT" + HTTPMethodDelete HTTPMethod = "DELETE" + HTTPMethodOptions HTTPMethod = "OPTIONS" + HTTPMethodTrace HTTPMethod = "TRACE" + HTTPMethodPatch HTTPMethod = "PATCH" + HTTPMethodConnect HTTPMethod = "CONNECT" +) + +// +kubebuilder:validation:Enum=ALL;SELECT;INSERT;UPDATE;DELETE +type DatabaseOperation string + +const ( + DatabaseOperationAll DatabaseOperation = "ALL" + DatabaseOperationSelect DatabaseOperation = "SELECT" + DatabaseOperationInsert DatabaseOperation = "INSERT" + DatabaseOperationUpdate DatabaseOperation = "UPDATE" + DatabaseOperationDelete DatabaseOperation = "DELETE" +) + +// +kubebuilder:validation:Enum=all;backup;create;delete;deleteissuers;get;getissuers;import;list;listissuers;managecontacts;manageissuers;purge;recover;restore;setissuers;update +type AzureKeyVaultCertificatePermission string + +const ( + AzureKeyVaultCertificatePermissionAll AzureKeyVaultCertificatePermission = "all" + AzureKeyVaultCertificatePermissionBackup AzureKeyVaultCertificatePermission = "backup" + AzureKeyVaultCertificatePermissionCreate AzureKeyVaultCertificatePermission = "create" + AzureKeyVaultCertificatePermissionDelete AzureKeyVaultCertificatePermission = "delete" + AzureKeyVaultCertificatePermissionDeleteIssuers AzureKeyVaultCertificatePermission = "deleteissuers" + AzureKeyVaultCertificatePermissionGet AzureKeyVaultCertificatePermission = "get" + AzureKeyVaultCertificatePermissionGetIssuers AzureKeyVaultCertificatePermission = "getissuers" + AzureKeyVaultCertificatePermissionImport AzureKeyVaultCertificatePermission = "import" + AzureKeyVaultCertificatePermissionList AzureKeyVaultCertificatePermission = "list" + AzureKeyVaultCertificatePermissionListIssuers AzureKeyVaultCertificatePermission = "listissuers" + AzureKeyVaultCertificatePermissionManageContacts AzureKeyVaultCertificatePermission = "managecontacts" + AzureKeyVaultCertificatePermissionManageIssuers AzureKeyVaultCertificatePermission = "manageissuers" + AzureKeyVaultCertificatePermissionPurge AzureKeyVaultCertificatePermission = "purge" + AzureKeyVaultCertificatePermissionRecover AzureKeyVaultCertificatePermission = "recover" + AzureKeyVaultCertificatePermissionRestore AzureKeyVaultCertificatePermission = "restore" + AzureKeyVaultCertificatePermissionSetIssuers AzureKeyVaultCertificatePermission = "setissuers" + AzureKeyVaultCertificatePermissionUpdate AzureKeyVaultCertificatePermission = "update" +) + +// +kubebuilder:validation:Enum=all;backup;create;decrypt;delete;encrypt;get;getrotationpolicy;import;list;purge;recover;release;restore;rotate;setrotationpolicy;sign;unwrapkey;update;verify;wrapkey +type AzureKeyVaultKeyPermission string + +const ( + AzureKeyVaultKeyPermissionAll AzureKeyVaultKeyPermission = "all" + AzureKeyVaultKeyPermissionBackup AzureKeyVaultKeyPermission = "backup" + AzureKeyVaultKeyPermissionCreate AzureKeyVaultKeyPermission = "create" + AzureKeyVaultKeyPermissionDecrypt AzureKeyVaultKeyPermission = "decrypt" + AzureKeyVaultKeyPermissionDelete AzureKeyVaultKeyPermission = "delete" + AzureKeyVaultKeyPermissionEncrypt AzureKeyVaultKeyPermission = "encrypt" + AzureKeyVaultKeyPermissionGet AzureKeyVaultKeyPermission = "get" + AzureKeyVaultKeyPermissionGetRotationPolicy AzureKeyVaultKeyPermission = "getrotationpolicy" + AzureKeyVaultKeyPermissionImport AzureKeyVaultKeyPermission = "import" + AzureKeyVaultKeyPermissionList AzureKeyVaultKeyPermission = "list" + AzureKeyVaultKeyPermissionPurge AzureKeyVaultKeyPermission = "purge" + AzureKeyVaultKeyPermissionRecover AzureKeyVaultKeyPermission = "recover" + AzureKeyVaultKeyPermissionRelease AzureKeyVaultKeyPermission = "release" + AzureKeyVaultKeyPermissionRestore AzureKeyVaultKeyPermission = "restore" + AzureKeyVaultKeyPermissionRotate AzureKeyVaultKeyPermission = "rotate" + AzureKeyVaultKeyPermissionSetRotationPolicy AzureKeyVaultKeyPermission = "setrotationpolicy" + AzureKeyVaultKeyPermissionSign AzureKeyVaultKeyPermission = "sign" + AzureKeyVaultKeyPermissionUnwrapKey AzureKeyVaultKeyPermission = "unwrapkey" + AzureKeyVaultKeyPermissionUpdate AzureKeyVaultKeyPermission = "update" + AzureKeyVaultKeyPermissionVerify AzureKeyVaultKeyPermission = "verify" + AzureKeyVaultKeyPermissionWrapKey AzureKeyVaultKeyPermission = "wrapkey" +) + +// +kubebuilder:validation:Enum=all;backup;delete;get;list;purge;recover;restore;set +type AzureKeyVaultSecretPermission string + +const ( + AzureKeyVaultSecretPermissionAll AzureKeyVaultSecretPermission = "all" + AzureKeyVaultSecretPermissionBackup AzureKeyVaultSecretPermission = "backup" + AzureKeyVaultSecretPermissionDelete AzureKeyVaultSecretPermission = "delete" + AzureKeyVaultSecretPermissionGet AzureKeyVaultSecretPermission = "get" + AzureKeyVaultSecretPermissionList AzureKeyVaultSecretPermission = "list" + AzureKeyVaultSecretPermissionPurge AzureKeyVaultSecretPermission = "purge" + AzureKeyVaultSecretPermissionRecover AzureKeyVaultSecretPermission = "recover" + AzureKeyVaultSecretPermissionRestore AzureKeyVaultSecretPermission = "restore" + AzureKeyVaultSecretPermissionSet AzureKeyVaultSecretPermission = "set" +) + +// +kubebuilder:validation:Enum=all;backup;delete;deletesas;get;getsas;list;listsas;purge;recover;regeneratekey;restore;set;setsas;update +type AzureKeyVaultStoragePermission string + +const ( + AzureKeyVaultStoragePermissionAll AzureKeyVaultStoragePermission = "all" + AzureKeyVaultStoragePermissionBackup AzureKeyVaultStoragePermission = "backup" + AzureKeyVaultStoragePermissionDelete AzureKeyVaultStoragePermission = "delete" + AzureKeyVaultStoragePermissionDeleteSas AzureKeyVaultStoragePermission = "deletesas" + AzureKeyVaultStoragePermissionGet AzureKeyVaultStoragePermission = "get" + AzureKeyVaultStoragePermissionGetSas AzureKeyVaultStoragePermission = "getsas" + AzureKeyVaultStoragePermissionList AzureKeyVaultStoragePermission = "list" + AzureKeyVaultStoragePermissionListSas AzureKeyVaultStoragePermission = "listsas" + AzureKeyVaultStoragePermissionPurge AzureKeyVaultStoragePermission = "purge" + AzureKeyVaultStoragePermissionRecover AzureKeyVaultStoragePermission = "recover" + AzureKeyVaultStoragePermissionRegenerateKey AzureKeyVaultStoragePermission = "regeneratekey" + AzureKeyVaultStoragePermissionRestore AzureKeyVaultStoragePermission = "restore" + AzureKeyVaultStoragePermissionSet AzureKeyVaultStoragePermission = "set" + AzureKeyVaultStoragePermissionSetSas AzureKeyVaultStoragePermission = "setsas" + AzureKeyVaultStoragePermissionUpdate AzureKeyVaultStoragePermission = "update" +) + +// IntentsSpec defines the desired state of ClientIntents +type IntentsSpec struct { + Workload Workload `json:"workload" yaml:"workload"` + Targets []Target `json:"targets" yaml:"targets"` +} + +type Workload struct { + Name string `json:"name" yaml:"name"` + //+optional + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` +} + +type Target struct { + //+optional + Kubernetes *KubernetesTarget `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty"` + + //+optional + Service *ServiceTarget `json:"service,omitempty" yaml:"service,omitempty"` + + //+optional + Kafka *KafkaTarget `json:"kafka,omitempty" yaml:"kafka,omitempty"` + + //+optional + SQL *SQLTarget `json:"sql,omitempty" yaml:"sql,omitempty"` + + //+optional + AWS *AWSTarget `json:"aws,omitempty" yaml:"aws,omitempty"` + + //+optional + GCP *GCPTarget `json:"gcp,omitempty" yaml:"gcp,omitempty"` + + //+optional + Azure *AzureTarget `json:"azure,omitempty" yaml:"azure,omitempty"` + + //+optional + Internet *Internet `json:"internet,omitempty" yaml:"internet,omitempty"` +} + +type AWSTarget struct { + ARN string `json:"arn,omitempty" yaml:"arn,omitempty"` + //+optional + Actions []string `json:"actions,omitempty" yaml:"actions,omitempty"` +} + +type GCPTarget struct { + Resource string `json:"resource,omitempty" yaml:"resource,omitempty"` + Permissions []string `json:"permissions,omitempty" yaml:"permissions,omitempty"` +} + +type AzureTarget struct { + 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"` + //+optional + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + //+optional + HTTP []HTTPTarget `json:"http,omitempty" yaml:"http,omitempty"` +} + +type ServiceTarget struct { + Name string `json:"name" yaml:"name"` + HTTP []HTTPTarget `json:"http,omitempty" yaml:"http,omitempty"` +} + +type Internet struct { + //+optional + Domains []string `json:"domains,omitempty" yaml:"domains,omitempty"` + //+optional + Ips []string `json:"ips,omitempty" yaml:"ips,omitempty"` + //+optional + Ports []int `json:"ports,omitempty" yaml:"ports,omitempty"` +} + +type SQLTarget struct { + Name string `json:"name" yaml:"name"` + //+optional + Privileges []SQLPrivileges `json:"privileges,omitempty" yaml:"privileges,omitempty"` +} + +type SQLPrivileges struct { + DatabaseName string `json:"databaseName" yaml:"databaseName"` + //+optional + Table string `json:"table" yaml:"table"` + //+optional + Operations []DatabaseOperation `json:"operations" yaml:"operations"` +} + +type HTTPTarget struct { + Path string `json:"path"` + Methods []HTTPMethod `json:"methods" yaml:"methods"` +} + +type KafkaTarget struct { + Name string `json:"name" yaml:"name"` + //+optional + Topics []KafkaTopic `json:"topics,omitempty" yaml:"topics,omitempty"` +} + +type KafkaTopic struct { + Name string `json:"name" yaml:"name"` + Operations []KafkaOperation `json:"operations" yaml:"operations"` +} + +type ResolvedIPs struct { + DNS string `json:"dns,omitempty" yaml:"dns,omitempty"` + IPs []string `json:"ips,omitempty" yaml:"ips,omitempty"` +} + +type AzureKeyVaultPolicy struct { + //+optional + CertificatePermissions []AzureKeyVaultCertificatePermission `json:"certificatePermissions,omitempty" yaml:"certificatePermissions,omitempty"` + //+optional + KeyPermissions []AzureKeyVaultKeyPermission `json:"keyPermissions,omitempty" yaml:"keyPermissions,omitempty"` + //+optional + SecretPermissions []AzureKeyVaultSecretPermission `json:"secretPermissions,omitempty" yaml:"secretPermissions,omitempty"` + //+optional + StoragePermissions []AzureKeyVaultStoragePermission `json:"storagePermissions,omitempty" yaml:"storagePermissions,omitempty"` +} + +// IntentsStatus defines the observed state of ClientIntents +type IntentsStatus struct { + // upToDate field reflects whether the client intents have successfully been applied + // to the cluster to the state specified + // +optional + UpToDate bool `json:"upToDate"` + // The last generation of the intents that was successfully reconciled. + // +optional + ObservedGeneration int64 `json:"observedGeneration"` + // ResolvedIPs stores resolved IPs for a domain name - the network mapper populates it when DNS internetTarget is used + // +optional + ResolvedIPs []ResolvedIPs `json:"resolvedIPs,omitempty" yaml:"resolvedIPs,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// ClientIntents is the Schema for the intents API +type ClientIntents struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + + Spec *IntentsSpec `json:"spec,omitempty" yaml:"spec,omitempty"` + Status IntentsStatus `json:"status,omitempty" yaml:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ClientIntentsList contains a list of ClientIntents +type ClientIntentsList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + Items []ClientIntents `json:"items" yaml:"items"` +} + +func init() { + SchemeBuilder.Register(&ClientIntents{}, &ClientIntentsList{}) +} + +func (in *ClientIntents) GetWorkloadName() string { + return in.Spec.Workload.Name +} + +func (in *ClientIntents) GetTargetList() []Target { + return in.Spec.Targets +} + +func (in *ClientIntents) GetFilteredTargetList(intentTypes ...IntentType) []Target { + return lo.Filter(in.GetTargetList(), func(item Target, index int) bool { + for _, intentType := range intentTypes { + if intentType == IntentTypeHTTP { + if item.Kubernetes != nil && len(item.Kubernetes.HTTP) > 0 { + return true + } + if item.Service != nil && len(item.Service.HTTP) > 0 { + return true + } + } + if intentType == IntentTypeKafka && item.Kafka != nil { + return true + } + if intentType == IntentTypeDatabase && item.SQL != nil { + return true + } + if intentType == IntentTypeAWS && item.AWS != nil { + return true + } + if intentType == IntentTypeGCP && item.GCP != nil { + return true + } + if intentType == IntentTypeAzure && item.Azure != nil { + return true + } + if intentType == IntentTypeInternet && item.Internet != nil { + return true + } + } + return false + }) +} + +func (in *ClientIntents) GetClientKind() string { + if in.Spec.Workload.Kind == "" { + return serviceidentity.KindOtterizeLegacy + } + return in.Spec.Workload.Kind +} + +func (in *ClientIntents) GetIntentsLabelMapping(requestNamespace string) map[string]string { + otterizeAccessLabels := make(map[string]string) + + for _, intent := range in.GetTargetList() { + if intent.IsTargetOutOfCluster() { + continue + } + targetServiceIdentity := intent.ToServiceIdentity(requestNamespace) + labelKey := fmt.Sprintf(OtterizeAccessLabelKey, targetServiceIdentity.GetFormattedOtterizeIdentityWithKind()) + if intent.IsTargetServerKubernetesService() { + labelKey = fmt.Sprintf(OtterizeSvcAccessLabelKey, targetServiceIdentity.GetFormattedOtterizeIdentityWithKind()) + } + otterizeAccessLabels[labelKey] = "true" + } + + return otterizeAccessLabels +} + +func (in *ClientIntents) GetDatabaseIntents() []Target { + return in.GetFilteredTargetList(IntentTypeDatabase) +} + +// GetTargetServerNamespace returns target namespace for intent if exists +// or the entire resource's namespace if the specific intent has no target namespace, as it's optional +func (in *Target) GetTargetServerNamespace(intentsObjNamespace string) string { + nameWithNamespace := strings.Split(in.GetTargetServerNameAsWritten(), ".") + if len(nameWithNamespace) == 1 { + return intentsObjNamespace + } + // serverName.namespace --> "namespace" + return nameWithNamespace[1] +} + +func (in *Target) IsTargetServerKubernetesService() bool { + return in.Service != nil || (in.Kubernetes != nil && in.Kubernetes.Kind == serviceidentity.KindService) +} + +func (in *Target) GetIntentType() IntentType { + if in.Kubernetes != nil && len(in.Kubernetes.HTTP) > 0 { + return IntentTypeHTTP + } + if in.Service != nil && len(in.Service.HTTP) > 0 { + return IntentTypeHTTP + } + if in.Kafka != nil { + return IntentTypeKafka + } + if in.SQL != nil { + return IntentTypeDatabase + } + if in.AWS != nil { + return IntentTypeAWS + } + if in.GCP != nil { + return IntentTypeGCP + } + if in.Azure != nil { + return IntentTypeAzure + } + if in.Internet != nil { + return IntentTypeInternet + } + return "" +} + +func (in *Target) IsTargetTheKubernetesAPIServer(objectNamespace string) bool { + return in.GetTargetServerName() == KubernetesAPIServerName && + in.GetTargetServerNamespace(objectNamespace) == KubernetesAPIServerNamespace +} + +func (in *Target) IsTargetInCluster() bool { + if in.Service != nil || in.Kubernetes != nil || in.Kafka != nil { + return true + } + return false +} + +func (in *Target) IsTargetOutOfCluster() bool { + return !in.IsTargetInCluster() +} + +// GetTargetServerName returns server's service name, without namespace, or the Kubernetes service without the `svc:` prefix +func (in *Target) GetTargetServerName() string { + name := in.GetTargetServerNameAsWritten() + if in.IsTargetOutOfCluster() { + return name + } + nameWithNamespace := strings.Split(name, ".") + return nameWithNamespace[0] +} + +func (in *Target) GetTargetServerNameAsWritten() string { + if in.Internet != nil { + return OtterizeInternetTargetName + } + + if in.Azure != nil { + return in.Azure.Scope + } + + if in.AWS != nil { + return in.AWS.ARN + } + + if in.GCP != nil { + return in.GCP.Resource + } + + if in.SQL != nil { + return in.SQL.Name + } + + if in.Kafka != nil { + return in.Kafka.Name + } + + if in.Service != nil { + return in.Service.Name + } + + if in.Kubernetes != nil { + return in.Kubernetes.Name + } + + panic("target server name not found") +} + +func (in *Target) GetTargetServerKind() string { + if in.Kubernetes != nil && in.Kubernetes.Kind != "" { + return in.Kubernetes.Kind + } + if in.Service != nil { + return serviceidentity.KindService + } + return serviceidentity.KindOtterizeLegacy +} + +func (in *Target) GetServerFullyQualifiedName(intentsObjNamespace string) string { + fullyQualifiedName := fmt.Sprintf("%s.%s", in.GetTargetServerName(), in.GetTargetServerNamespace(intentsObjNamespace)) + return fullyQualifiedName +} + +func (in *Target) GetK8sServiceFullyQualifiedName(intentsObjNamespace string) (string, bool) { + fullyQualifiedName := fmt.Sprintf("%s.%s", in.GetTargetServerName(), in.GetTargetServerNamespace(intentsObjNamespace)) + if in.IsTargetServerKubernetesService() { + fullyQualifiedName = fmt.Sprintf("svc:%s", fullyQualifiedName) + return fullyQualifiedName, true + } + return "", false +} + +func (in *Target) typeAsGQLType() graphqlclient.IntentType { + switch in.GetIntentType() { + case IntentTypeHTTP: + return graphqlclient.IntentTypeHttp + case IntentTypeKafka: + return graphqlclient.IntentTypeKafka + case IntentTypeDatabase: + return graphqlclient.IntentTypeDatabase + case IntentTypeAWS: + return graphqlclient.IntentTypeAws + case IntentTypeGCP: + return graphqlclient.IntentTypeGcp + case IntentTypeAzure: + return graphqlclient.IntentTypeAzure + case IntentTypeInternet: + return graphqlclient.IntentTypeInternet + default: + return "" + } +} + +func (in *ClientIntents) GetServersWithoutSidecar() (sets.Set[string], error) { + if in.Annotations == nil { + return sets.New[string](), nil + } + + servers, ok := in.Annotations[OtterizeServersWithoutSidecarAnnotation] + if !ok { + return sets.New[string](), nil + } + + serversList := make([]string, 0) + err := json.Unmarshal([]byte(servers), &serversList) + if err != nil { + return nil, errors.Wrap(err) + } + + return sets.New[string](serversList...), nil +} + +func (in *ClientIntents) IsServerMissingSidecar(intent Target) (bool, error) { + serversSet, err := in.GetServersWithoutSidecar() + if err != nil { + return false, errors.Wrap(err) + } + identity := intent.ToServiceIdentity(in.Namespace) + serverIdentity := identity.GetFormattedOtterizeIdentityWithoutKind() + return serversSet.Has(serverIdentity), nil +} + +func (in *ClientIntentsList) FormatAsOtterizeIntents(ctx context.Context, k8sClient client.Client) ([]*graphqlclient.IntentInput, error) { + otterizeIntents := make([]*graphqlclient.IntentInput, 0) + for _, clientIntents := range in.Items { + for _, intent := range clientIntents.GetTargetList() { + clientServiceIdentity := clientIntents.ToServiceIdentity() + input, err := intent.ConvertToCloudFormat(ctx, k8sClient, clientServiceIdentity) + if err != nil { + return nil, errors.Wrap(err) + } + statusInput, ok, err := clientIntentsStatusToCloudFormat(clientIntents, intent) + if err != nil { + return nil, errors.Wrap(err) + } + + input.Status = nil + if ok { + input.Status = statusInput + } + otterizeIntents = append(otterizeIntents, lo.ToPtr(input)) + } + } + + return otterizeIntents, nil +} + +func clientIntentsStatusToCloudFormat(clientIntents ClientIntents, intent Target) (*graphqlclient.IntentStatusInput, bool, error) { + status := graphqlclient.IntentStatusInput{ + IstioStatus: &graphqlclient.IstioStatusInput{}, + } + + serviceAccountName, ok := clientIntents.Annotations[OtterizeClientServiceAccountAnnotation] + if !ok { + // Status is not set, nothing to do + return nil, false, nil + } + + status.IstioStatus.ServiceAccountName = toPtrOrNil(serviceAccountName) + isSharedValue, ok := clientIntents.Annotations[OtterizeSharedServiceAccountAnnotation] + if !ok { + return nil, false, errors.Errorf("missing annotation shared service account for client intents %s", clientIntents.Name) + } + + isShared, err := strconv.ParseBool(isSharedValue) + if err != nil { + return nil, false, errors.Errorf("failed to parse shared service account annotation for client intents %s", clientIntents.Name) + } + status.IstioStatus.IsServiceAccountShared = lo.ToPtr(isShared) + + clientMissingSidecarValue, ok := clientIntents.Annotations[OtterizeMissingSidecarAnnotation] + if !ok { + return nil, false, errors.Errorf("missing annotation missing sidecar for client intents %s", clientIntents.Name) + } + + clientMissingSidecar, err := strconv.ParseBool(clientMissingSidecarValue) + if err != nil { + return nil, false, errors.Errorf("failed to parse missing sidecar annotation for client intents %s", clientIntents.Name) + } + status.IstioStatus.IsClientMissingSidecar = lo.ToPtr(clientMissingSidecar) + isServerMissingSidecar, err := clientIntents.IsServerMissingSidecar(intent) + if err != nil { + return nil, false, errors.Wrap(err) + } + status.IstioStatus.IsServerMissingSidecar = lo.ToPtr(isServerMissingSidecar) + return &status, true, nil +} + +func toPtrOrNil(s string) *string { + if s == "" { + return nil + } + return lo.ToPtr(s) +} + +func kafkaOperationK8sToCloud(op KafkaOperation) graphqlclient.KafkaOperation { + switch op { + case KafkaOperationAll: + return graphqlclient.KafkaOperationAll + case KafkaOperationConsume: + return graphqlclient.KafkaOperationConsume + case KafkaOperationProduce: + return graphqlclient.KafkaOperationProduce + case KafkaOperationCreate: + return graphqlclient.KafkaOperationCreate + case KafkaOperationAlter: + return graphqlclient.KafkaOperationAlter + case KafkaOperationDelete: + return graphqlclient.KafkaOperationDelete + case KafkaOperationDescribe: + return graphqlclient.KafkaOperationDescribe + case KafkaOperationClusterAction: + return graphqlclient.KafkaOperationClusterAction + case KafkaOperationDescribeConfigs: + return graphqlclient.KafkaOperationDescribeConfigs + case KafkaOperationAlterConfigs: + return graphqlclient.KafkaOperationAlterConfigs + case KafkaOperationIdempotentWrite: + return graphqlclient.KafkaOperationIdempotentWrite + default: + logrus.Panic(fmt.Sprintf("Unknown KafkaOperation: %s", op)) + return "" // We won't reach here + } +} + +func databaseOperationToCloud(op DatabaseOperation) graphqlclient.DatabaseOperation { + switch op { + case DatabaseOperationAll: + return graphqlclient.DatabaseOperationAll + case DatabaseOperationDelete: + return graphqlclient.DatabaseOperationDelete + case DatabaseOperationInsert: + return graphqlclient.DatabaseOperationInsert + case DatabaseOperationSelect: + return graphqlclient.DatabaseOperationSelect + case DatabaseOperationUpdate: + return graphqlclient.DatabaseOperationUpdate + default: + logrus.Panic(fmt.Sprintf("Unknown DatabaseOperation: %s", op)) + return "" + } +} + +func enumSliceToStrPtrSlice[T ~string](enumSlice []T) []*string { + return lo.Map(enumSlice, func(s T, i int) *string { + return lo.ToPtr(string(s)) + }) +} + +func (in *Target) GetHTTPResources() []HTTPTarget { + if in.Kubernetes != nil && len(in.Kubernetes.HTTP) > 0 { + return in.Kubernetes.HTTP + } + if in.Service != nil && len(in.Service.HTTP) > 0 { + return in.Service.HTTP + } + return make([]HTTPTarget, 0) +} + +func (in *Target) ConvertToCloudFormat(ctx context.Context, k8sClient client.Client, clientServiceIdentity serviceidentity.ServiceIdentity) (graphqlclient.IntentInput, error) { + serverServiceIdentity := in.ToServiceIdentity(clientServiceIdentity.Namespace) + var alias *graphqlclient.ServerAliasInput + if in.IsTargetTheKubernetesAPIServer(clientServiceIdentity.Namespace) { + alias = &graphqlclient.ServerAliasInput{ + Name: lo.ToPtr(serverServiceIdentity.GetNameAsServer()), + Kind: lo.ToPtr(serviceidentity.KindService), + } + } else if in.IsTargetServerKubernetesService() { + // alias should be the kubernetes service + alias = &graphqlclient.ServerAliasInput{ + Name: lo.ToPtr(serverServiceIdentity.GetNameAsServer()), + Kind: lo.ToPtr(serverServiceIdentity.Kind), + } + labelSelector, ok, err := ServiceIdentityToLabelsForWorkloadSelection(ctx, k8sClient, serverServiceIdentity) + if err != nil { + return graphqlclient.IntentInput{}, errors.Wrap(err) + } + if ok { + podList := &corev1.PodList{} + err := k8sClient.List(ctx, podList, client.InNamespace(serverServiceIdentity.Namespace), client.MatchingLabels(labelSelector)) + if err != nil { + return graphqlclient.IntentInput{}, errors.Wrap(err) + } + if len(podList.Items) > 0 { + si, err := podownerresolver.ResolvePodToServiceIdentity(ctx, k8sClient, &podList.Items[0]) + if err != nil { + return graphqlclient.IntentInput{}, errors.Wrap(err) + } + // The server service Identity should be the workload (Deployment, statefulset, etc) + serverServiceIdentity = si + } + } + } + + intentInput := graphqlclient.IntentInput{ + ClientName: lo.ToPtr(clientServiceIdentity.Name), + ClientWorkloadKind: lo.Ternary(clientServiceIdentity.Kind != serviceidentity.KindOtterizeLegacy, lo.ToPtr(clientServiceIdentity.Kind), nil), + ServerName: lo.ToPtr(serverServiceIdentity.Name), + ServerWorkloadKind: lo.Ternary(serverServiceIdentity.Kind != serviceidentity.KindOtterizeLegacy, lo.ToPtr(serverServiceIdentity.Kind), nil), + Namespace: lo.ToPtr(clientServiceIdentity.Namespace), + ServerNamespace: toPtrOrNil(in.GetTargetServerNamespace(clientServiceIdentity.Namespace)), + ServerAlias: alias, + } + if gqlType := in.typeAsGQLType(); gqlType != "" { + intentInput.Type = lo.ToPtr(gqlType) + } + + if in.Kafka != nil { + otterizeTopics := lo.Map(in.Kafka.Topics, func(topic KafkaTopic, i int) *graphqlclient.KafkaConfigInput { + return lo.ToPtr(graphqlclient.KafkaConfigInput{ + Name: lo.ToPtr(topic.Name), + Operations: lo.Map(topic.Operations, func(op KafkaOperation, i int) *graphqlclient.KafkaOperation { + operation := kafkaOperationK8sToCloud(op) + return &operation + }), + }) + }) + intentInput.Topics = otterizeTopics + } + + if in.typeAsGQLType() == graphqlclient.IntentTypeHttp { + intentInput.Resources = lo.Map(in.GetHTTPResources(), intentsHTTPResourceToCloud) + } + + if in.SQL != nil { + intentInput.DatabaseResources = lo.Map(in.SQL.Privileges, func(resource SQLPrivileges, _ int) *graphqlclient.DatabaseConfigInput { + databaseConfigInput := graphqlclient.DatabaseConfigInput{ + Table: lo.ToPtr(resource.Table), + Dbname: lo.ToPtr(resource.DatabaseName), + Operations: lo.Map(resource.Operations, func(operation DatabaseOperation, _ int) *graphqlclient.DatabaseOperation { + cloudOperation := databaseOperationToCloud(operation) + return &cloudOperation + }), + } + return &databaseConfigInput + }) + } + + if in.Internet != nil { + intentInput.Internet = &graphqlclient.InternetConfigInput{} + if len(in.Internet.Domains) != 0 { + intentInput.Internet.Domains = lo.ToSlicePtr(in.Internet.Domains) + } + if len(in.Internet.Ips) != 0 { + intentInput.Internet.Ips = lo.ToSlicePtr(in.Internet.Ips) + } + if in.Internet.Ports != nil && len(in.Internet.Ports) != 0 { + intentInput.Internet.Ports = lo.ToSlicePtr(in.Internet.Ports) + } + } + + if in.AWS != nil { + intentInput.AwsActions = lo.ToSlicePtr(in.AWS.Actions) + } + + if in.Azure != nil { + intentInput.AzureRoles = lo.ToSlicePtr(in.Azure.Roles) + intentInput.AzureActions = lo.Map(in.Azure.Actions, func(action AzureAction, _ int) *string { + return lo.ToPtr(string(action)) + }) + intentInput.AzureDataActions = lo.Map(in.Azure.DataActions, func(action AzureDataAction, _ int) *string { + return lo.ToPtr(string(action)) + }) + + if in.Azure.KeyVaultPolicy != nil { + intentInput.AzureKeyVaultPolicy = &graphqlclient.AzureKeyVaultPolicyInput{ + CertificatePermissions: enumSliceToStrPtrSlice(in.Azure.KeyVaultPolicy.CertificatePermissions), + KeyPermissions: enumSliceToStrPtrSlice(in.Azure.KeyVaultPolicy.KeyPermissions), + SecretPermissions: enumSliceToStrPtrSlice(in.Azure.KeyVaultPolicy.SecretPermissions), + StoragePermissions: enumSliceToStrPtrSlice(in.Azure.KeyVaultPolicy.StoragePermissions), + } + } + } + + if in.GCP != nil { + intentInput.GcpPermissions = lo.ToSlicePtr(in.GCP.Permissions) + } + + return intentInput, nil +} + +func intentsHTTPResourceToCloud(resource HTTPTarget, _ int) *graphqlclient.HTTPConfigInput { + methods := lo.Map(resource.Methods, func(method HTTPMethod, _ int) *graphqlclient.HTTPMethod { + return lo.ToPtr(graphqlclient.HTTPMethod(method)) + }) + + httpConfig := graphqlclient.HTTPConfigInput{ + Path: lo.ToPtr(resource.Path), + Methods: methods, + } + + return &httpConfig +} + +func (in *ClientIntents) HasKafkaTypeInCallList() bool { + for _, intent := range in.GetTargetList() { + if intent.Kafka != nil { + return true + } + } + return false +} + +func (in *ClientIntents) HasDatabaseTypeInCallList() bool { + for _, intent := range in.GetTargetList() { + if intent.SQL != nil { + return true + } + } + return false +} diff --git a/src/operator/api/v2beta1/groupversion_info.go b/src/operator/api/v2beta1/groupversion_info.go new file mode 100644 index 000000000..e91cb2020 --- /dev/null +++ b/src/operator/api/v2beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v2beta1 contains API Schema definitions for the otterize v2beta1 API group +// +kubebuilder:object:generate=true +// +groupName=k8s.otterize.com +package v2beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "k8s.otterize.com", Version: "v2beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/src/operator/api/v2beta1/kafkaserverconfig_types.go b/src/operator/api/v2beta1/kafkaserverconfig_types.go new file mode 100644 index 000000000..f082113e0 --- /dev/null +++ b/src/operator/api/v2beta1/kafkaserverconfig_types.go @@ -0,0 +1,93 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +type TLSSource struct { + // +kubebuilder:validation:Required + CertFile string `json:"certFile" yaml:"certFile"` + // +kubebuilder:validation:Required + KeyFile string `json:"keyFile" yaml:"keyFile"` + // +kubebuilder:validation:Required + RootCAFile string `json:"rootCAFile" yaml:"rootCAFile"` +} + +// +kubebuilder:validation:Enum=literal;prefix +type ResourcePatternType string + +const ( + ResourcePatternTypeLiteral = "literal" + ResourcePatternTypePrefix = "prefix" +) + +type TopicConfig struct { + Topic string `json:"topic" yaml:"topic"` + Pattern ResourcePatternType `json:"pattern" yaml:"pattern"` + ClientIdentityRequired bool `json:"clientIdentityRequired" yaml:"clientIdentityRequired"` + IntentsRequired bool `json:"intentsRequired" yaml:"intentsRequired"` +} + +// KafkaServerConfigSpec defines the desired state of KafkaServerConfig +type KafkaServerConfigSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + Workload Workload `json:"workload,omitempty" yaml:"workload,omitempty"` + // If Intents for network policies are enabled, and there are other Intents to this Kafka server, + // will automatically create an Target so that the Intents Operator can connect. Set to true to disable. + NoAutoCreateIntentsForOperator bool `json:"noAutoCreateIntentsForOperator,omitempty" yaml:"noAutoCreateIntentsForOperator,omitempty"` + Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` + // +kubebuilder:validation:Optional + TLS TLSSource `json:"tls,omitempty" yaml:"tls,omitempty"` + Topics []TopicConfig `json:"topics,omitempty" yaml:"topics,omitempty"` +} + +// KafkaServerConfigStatus defines the observed state of KafkaServerConfig +type KafkaServerConfigStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// KafkaServerConfig is the Schema for the kafkaserverconfigs API +type KafkaServerConfig struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + + Spec KafkaServerConfigSpec `json:"spec,omitempty" yaml:"spec,omitempty"` + Status KafkaServerConfigStatus `json:"status,omitempty" yaml:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// KafkaServerConfigList contains a list of KafkaServerConfig +type KafkaServerConfigList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + Items []KafkaServerConfig `json:"items" yaml:"items"` +} + +func init() { + SchemeBuilder.Register(&KafkaServerConfig{}, &KafkaServerConfigList{}) +} diff --git a/src/operator/api/v2beta1/mysqlserverconfig_types.go b/src/operator/api/v2beta1/mysqlserverconfig_types.go new file mode 100644 index 000000000..9f1047ef1 --- /dev/null +++ b/src/operator/api/v2beta1/mysqlserverconfig_types.go @@ -0,0 +1,42 @@ +package v2beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// MySQLServerConfigSpec defines the desired state of MySQLServerConfig +type MySQLServerConfigSpec struct { + Address string `json:"address"` + Credentials DatabaseCredentials `json:"credentials"` +} + +// MySQLServerConfigStatus defines the observed state of MySQLServerConfig +type MySQLServerConfigStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// MySQLServerConfig is the Schema for the mysqlserverconfig API +type MySQLServerConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MySQLServerConfigSpec `json:"spec,omitempty"` + Status MySQLServerConfigStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// MySQLServerConfigList contains a list of MySQLServerConfig +type MySQLServerConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MySQLServerConfig `json:"items"` +} + +func init() { + SchemeBuilder.Register(&MySQLServerConfig{}, &MySQLServerConfigList{}) +} diff --git a/src/operator/api/v2beta1/otterize_labels.go b/src/operator/api/v2beta1/otterize_labels.go new file mode 100644 index 000000000..70760e071 --- /dev/null +++ b/src/operator/api/v2beta1/otterize_labels.go @@ -0,0 +1,127 @@ +package v2beta1 + +import ( + "context" + "fmt" + "github.com/otterize/intents-operator/src/shared/errors" + "github.com/otterize/intents-operator/src/shared/serviceidresolver/serviceidentity" + "golang.org/x/exp/maps" + v1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "strings" +) + +// IsMissingOtterizeAccessLabels checks if a pod's labels need updating +func IsMissingOtterizeAccessLabels(pod *v1.Pod, otterizeAccessLabels map[string]string) bool { + podOtterizeAccessLabels := GetOtterizeLabelsFromPod(pod) + if len(podOtterizeAccessLabels) != len(otterizeAccessLabels) { + return true + } + + // Length is equal, check for diff in keys + for k := range podOtterizeAccessLabels { + if _, ok := otterizeAccessLabels[k]; !ok { + return true + } + } + return false +} + +// UpdateOtterizeAccessLabels updates a pod's labels with Otterize labels representing their intents +// The pod is also labeled with "otterize-client=" to mark it as having intents or being the client-side of an egress netpol +func UpdateOtterizeAccessLabels(pod *v1.Pod, serviceIdentity serviceidentity.ServiceIdentity, otterizeAccessLabels map[string]string) *v1.Pod { + if pod.Labels == nil { + pod.Labels = make(map[string]string) + } + // TODO: We should understand what kind + pod = cleanupOtterizeLabelsAndAnnotations(pod) + for k, v := range otterizeAccessLabels { + pod.Labels[k] = v + } + pod.Labels[OtterizeClientLabelKey] = serviceIdentity.GetFormattedOtterizeIdentityWithoutKind() + return pod +} + +func HasOtterizeServiceLabel(pod *v1.Pod, labelValue string) bool { + value, exists := pod.Labels[OtterizeServiceLabelKey] + return exists && value == labelValue +} + +func HasOtterizeOwnerKindLabel(pod *v1.Pod, labelValue string) bool { + value, exists := pod.Labels[OtterizeOwnerKindLabelKey] + return exists && value == labelValue +} + +func HasOtterizeDeprecatedServerLabel(pod *v1.Pod) bool { + _, exists := pod.Labels[OtterizeServerLabelKeyDeprecated] + return exists +} + +func cleanupOtterizeLabelsAndAnnotations(pod *v1.Pod) *v1.Pod { + for k := range pod.Labels { + if isOtterizeAccessLabel(k) { + delete(pod.Labels, k) + } + } + + delete(pod.Annotations, AllIntentsRemovedAnnotation) + + return pod +} + +func isOtterizeAccessLabel(s string) bool { + return strings.HasPrefix(s, OtterizeAccessLabelPrefix) || strings.HasPrefix(s, OtterizeServiceAccessLabelPrefix) +} + +func CleanupOtterizeKubernetesServiceLabels(pod *v1.Pod) *v1.Pod { + for k := range pod.Labels { + if isOtterizeKubernetesServiceLabel(k) { + delete(pod.Labels, k) + } + } + + return pod +} + +func isOtterizeKubernetesServiceLabel(s string) bool { + return strings.HasPrefix(s, OtterizeKubernetesServiceLabelKeyPrefix) +} + +func GetOtterizeLabelsFromPod(pod *v1.Pod) map[string]string { + otterizeLabels := make(map[string]string) + for k, v := range pod.Labels { + if isOtterizeAccessLabel(k) { + otterizeLabels[k] = v + } + } + + return otterizeLabels +} + +func ServiceIdentityToLabelsForWorkloadSelection(ctx context.Context, k8sClient client.Client, identity serviceidentity.ServiceIdentity) (map[string]string, bool, error) { + // This is here for backwards compatibility + if identity.Kind == "" || identity.Kind == serviceidentity.KindOtterizeLegacy { + return map[string]string{OtterizeServiceLabelKey: identity.GetFormattedOtterizeIdentityWithoutKind()}, true, nil + } + + if identity.Kind == serviceidentity.KindService { + svc := v1.Service{} + err := k8sClient.Get(ctx, types.NamespacedName{Name: identity.Name, Namespace: identity.Namespace}, &svc) + if err != nil { + if k8serrors.IsNotFound(err) { + return nil, false, nil + } + return nil, false, errors.Wrap(err) + } + if svc.Spec.Selector == nil { + return nil, false, fmt.Errorf("service %s/%s has no selector", svc.Namespace, svc.Name) + } + return maps.Clone(svc.Spec.Selector), true, nil + } + + // This should be replaced with a logic that gets the pod owners and uses its labelsSelector (for known kinds) + return map[string]string{OtterizeOwnerKindLabelKey: identity.Kind, + OtterizeServiceLabelKey: identity.GetFormattedOtterizeIdentityWithoutKind()}, true, nil +} diff --git a/src/operator/api/v2beta1/postgresqlserverconfig_types.go b/src/operator/api/v2beta1/postgresqlserverconfig_types.go new file mode 100644 index 000000000..cbc7cb070 --- /dev/null +++ b/src/operator/api/v2beta1/postgresqlserverconfig_types.go @@ -0,0 +1,95 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type DBPermissionChange string + +const ( + DBPermissionChangeApply DBPermissionChange = "APPLY" + DBPermissionChangeDelete DBPermissionChange = "DELETE" +) + +// DatabaseCredentials defines the credentials to access the database +type DatabaseCredentials struct { + // Username is the plaintext username to access the database + //+optional + Username string `json:"username,omitempty"` + // Password is the plaintext password to access the database + //+optional + Password string `json:"password,omitempty"` + // SecretRef is a reference to a k8s secret storing the credentials + //+optional + SecretRef *DatabaseCredentialsSecretRef `json:"secretRef,omitempty"` +} + +type DatabaseCredentialsSecretRef struct { + // Name is the name of he k8s secret storing the credentials + Name string `json:"name,omitempty"` + // Namespace is the namespace in which the secret is stored. + // If not provided, the operator will look for the secret in the same namespace as the database ServerConfig. + //+optional + Namespace string `json:"namespace,omitempty"` + // UsernameKey is the key in the secret that stores the username + //+optional + //+kubebuilder:default="username" + UsernameKey string `json:"usernameKey,omitempty"` + // PasswordKey is the key in the secret that stores the password + //+optional + //+kubebuilder:default="password" + PasswordKey string `json:"passwordKey,omitempty"` +} + +// PostgreSQLServerConfigSpec defines the desired state of PostgreSQLServerConfig +type PostgreSQLServerConfigSpec struct { + Address string `json:"address"` + Credentials DatabaseCredentials `json:"credentials"` +} + +// PostgreSQLServerConfigStatus defines the observed state of PostgreSQLServerConfig +type PostgreSQLServerConfigStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// PostgreSQLServerConfig is the Schema for the databaseserverconfig API +type PostgreSQLServerConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PostgreSQLServerConfigSpec `json:"spec,omitempty"` + Status PostgreSQLServerConfigStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// PostgreSQLServerConfigList contains a list of PostgreSQLServerConfig +type PostgreSQLServerConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PostgreSQLServerConfig `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PostgreSQLServerConfig{}, &PostgreSQLServerConfigList{}) +} diff --git a/src/operator/api/v2beta1/protectedservice_types.go b/src/operator/api/v2beta1/protectedservice_types.go new file mode 100644 index 000000000..0ba66b0b1 --- /dev/null +++ b/src/operator/api/v2beta1/protectedservice_types.go @@ -0,0 +1,68 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + "github.com/otterize/intents-operator/src/shared/serviceidresolver/serviceidentity" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ProtectedServiceSpec defines the desired state of ProtectedService +type ProtectedServiceSpec struct { + Name string `json:"name,omitempty"` + + //+optional + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` +} + +// ProtectedServiceStatus defines the observed state of ProtectedService +type ProtectedServiceStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// ProtectedService is the Schema for the protectedservice API +type ProtectedService struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ProtectedServiceSpec `json:"spec,omitempty"` + Status ProtectedServiceStatus `json:"status,omitempty"` +} + +func (in *ProtectedService) GetKind() string { + if in.Spec.Kind == "" { + return serviceidentity.KindOtterizeLegacy + } + return in.Spec.Kind +} + +//+kubebuilder:object:root=true + +// ProtectedServiceList contains a list of ProtectedService +type ProtectedServiceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ProtectedService `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ProtectedService{}, &ProtectedServiceList{}) +} diff --git a/src/operator/api/v2beta1/serviceidentity.go b/src/operator/api/v2beta1/serviceidentity.go new file mode 100644 index 000000000..0e2fd6cdf --- /dev/null +++ b/src/operator/api/v2beta1/serviceidentity.go @@ -0,0 +1,27 @@ +package v2beta1 + +import "github.com/otterize/intents-operator/src/shared/serviceidresolver/serviceidentity" + +func (in *ClientIntents) ToServiceIdentity() serviceidentity.ServiceIdentity { + return serviceidentity.ServiceIdentity{ + Name: in.Spec.Workload.Name, + Namespace: in.Namespace, + Kind: in.GetClientKind(), + } +} + +func (in *Target) ToServiceIdentity(intentsObjNamespace string) serviceidentity.ServiceIdentity { + return serviceidentity.ServiceIdentity{ + Name: in.GetTargetServerName(), + Namespace: in.GetTargetServerNamespace(intentsObjNamespace), + Kind: in.GetTargetServerKind(), + } +} + +func (in *ProtectedService) ToServiceIdentity() serviceidentity.ServiceIdentity { + return serviceidentity.ServiceIdentity{ + Name: in.Spec.Name, + Namespace: in.Namespace, + Kind: in.GetKind(), + } +} diff --git a/src/operator/api/v2beta1/webhooks.go b/src/operator/api/v2beta1/webhooks.go new file mode 100644 index 000000000..c8a2e3330 --- /dev/null +++ b/src/operator/api/v2beta1/webhooks.go @@ -0,0 +1,56 @@ +package v2beta1 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// MySQLServerConfig // + +func (in *MySQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, validator webhook.CustomValidator) error { + return ctrl.NewWebhookManagedBy(mgr). + For(in).WithValidator(validator). + Complete() +} + +func (in *MySQLServerConfig) Hub() {} + +// PostgreSQLServerConfig // + +func (in *PostgreSQLServerConfig) SetupWebhookWithManager(mgr ctrl.Manager, validator webhook.CustomValidator) error { + return ctrl.NewWebhookManagedBy(mgr). + For(in).WithValidator(validator). + Complete() +} + +func (in *PostgreSQLServerConfig) Hub() {} + +// KafkaServerConfig // + +func (ksc *KafkaServerConfig) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(ksc). + Complete() +} + +func (ksc *KafkaServerConfig) Hub() {} + +// ProtectedService // + +func (in *ProtectedService) SetupWebhookWithManager(mgr ctrl.Manager, validator webhook.CustomValidator) error { + return ctrl.NewWebhookManagedBy(mgr). + For(in).WithValidator(validator). + Complete() +} + +func (in *ProtectedService) Hub() {} + +// ClientIntents // + +func (in *ClientIntents) SetupWebhookWithManager(mgr ctrl.Manager, validator webhook.CustomValidator) error { + return ctrl.NewWebhookManagedBy(mgr). + For(in).WithValidator(validator). + Complete() +} + +func (in *ClientIntents) Hub() {} diff --git a/src/operator/api/v2beta1/zz_generated.deepcopy.go b/src/operator/api/v2beta1/zz_generated.deepcopy.go new file mode 100644 index 000000000..9a63f110e --- /dev/null +++ b/src/operator/api/v2beta1/zz_generated.deepcopy.go @@ -0,0 +1,941 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v2beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSTarget) DeepCopyInto(out *AWSTarget) { + *out = *in + if in.Actions != nil { + in, out := &in.Actions, &out.Actions + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSTarget. +func (in *AWSTarget) DeepCopy() *AWSTarget { + if in == nil { + return nil + } + out := new(AWSTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AzureKeyVaultPolicy) DeepCopyInto(out *AzureKeyVaultPolicy) { + *out = *in + if in.CertificatePermissions != nil { + in, out := &in.CertificatePermissions, &out.CertificatePermissions + *out = make([]AzureKeyVaultCertificatePermission, len(*in)) + copy(*out, *in) + } + if in.KeyPermissions != nil { + in, out := &in.KeyPermissions, &out.KeyPermissions + *out = make([]AzureKeyVaultKeyPermission, len(*in)) + copy(*out, *in) + } + if in.SecretPermissions != nil { + in, out := &in.SecretPermissions, &out.SecretPermissions + *out = make([]AzureKeyVaultSecretPermission, len(*in)) + copy(*out, *in) + } + if in.StoragePermissions != nil { + in, out := &in.StoragePermissions, &out.StoragePermissions + *out = make([]AzureKeyVaultStoragePermission, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKeyVaultPolicy. +func (in *AzureKeyVaultPolicy) DeepCopy() *AzureKeyVaultPolicy { + if in == nil { + return nil + } + out := new(AzureKeyVaultPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AzureTarget) DeepCopyInto(out *AzureTarget) { + *out = *in + if in.Roles != nil { + in, out := &in.Roles, &out.Roles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.KeyVaultPolicy != nil { + in, out := &in.KeyVaultPolicy, &out.KeyVaultPolicy + *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. +func (in *AzureTarget) DeepCopy() *AzureTarget { + if in == nil { + return nil + } + out := new(AzureTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientIntents) DeepCopyInto(out *ClientIntents) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(IntentsSpec) + (*in).DeepCopyInto(*out) + } + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientIntents. +func (in *ClientIntents) DeepCopy() *ClientIntents { + if in == nil { + return nil + } + out := new(ClientIntents) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClientIntents) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientIntentsList) DeepCopyInto(out *ClientIntentsList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClientIntents, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientIntentsList. +func (in *ClientIntentsList) DeepCopy() *ClientIntentsList { + if in == nil { + return nil + } + out := new(ClientIntentsList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClientIntentsList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseCredentials) DeepCopyInto(out *DatabaseCredentials) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(DatabaseCredentialsSecretRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseCredentials. +func (in *DatabaseCredentials) DeepCopy() *DatabaseCredentials { + if in == nil { + return nil + } + out := new(DatabaseCredentials) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseCredentialsSecretRef) DeepCopyInto(out *DatabaseCredentialsSecretRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseCredentialsSecretRef. +func (in *DatabaseCredentialsSecretRef) DeepCopy() *DatabaseCredentialsSecretRef { + if in == nil { + return nil + } + out := new(DatabaseCredentialsSecretRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GCPTarget) DeepCopyInto(out *GCPTarget) { + *out = *in + if in.Permissions != nil { + in, out := &in.Permissions, &out.Permissions + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GCPTarget. +func (in *GCPTarget) DeepCopy() *GCPTarget { + if in == nil { + return nil + } + out := new(GCPTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPTarget) DeepCopyInto(out *HTTPTarget) { + *out = *in + if in.Methods != nil { + in, out := &in.Methods, &out.Methods + *out = make([]HTTPMethod, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPTarget. +func (in *HTTPTarget) DeepCopy() *HTTPTarget { + if in == nil { + return nil + } + out := new(HTTPTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IntentsSpec) DeepCopyInto(out *IntentsSpec) { + *out = *in + out.Workload = in.Workload + if in.Targets != nil { + in, out := &in.Targets, &out.Targets + *out = make([]Target, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentsSpec. +func (in *IntentsSpec) DeepCopy() *IntentsSpec { + if in == nil { + return nil + } + out := new(IntentsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IntentsStatus) DeepCopyInto(out *IntentsStatus) { + *out = *in + if in.ResolvedIPs != nil { + in, out := &in.ResolvedIPs, &out.ResolvedIPs + *out = make([]ResolvedIPs, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntentsStatus. +func (in *IntentsStatus) DeepCopy() *IntentsStatus { + if in == nil { + return nil + } + out := new(IntentsStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Internet) DeepCopyInto(out *Internet) { + *out = *in + if in.Domains != nil { + in, out := &in.Domains, &out.Domains + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Ips != nil { + in, out := &in.Ips, &out.Ips + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]int, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Internet. +func (in *Internet) DeepCopy() *Internet { + if in == nil { + return nil + } + out := new(Internet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KafkaServerConfig) DeepCopyInto(out *KafkaServerConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KafkaServerConfig. +func (in *KafkaServerConfig) DeepCopy() *KafkaServerConfig { + if in == nil { + return nil + } + out := new(KafkaServerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KafkaServerConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KafkaServerConfigList) DeepCopyInto(out *KafkaServerConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KafkaServerConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KafkaServerConfigList. +func (in *KafkaServerConfigList) DeepCopy() *KafkaServerConfigList { + if in == nil { + return nil + } + out := new(KafkaServerConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KafkaServerConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KafkaServerConfigSpec) DeepCopyInto(out *KafkaServerConfigSpec) { + *out = *in + out.Workload = in.Workload + out.TLS = in.TLS + if in.Topics != nil { + in, out := &in.Topics, &out.Topics + *out = make([]TopicConfig, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KafkaServerConfigSpec. +func (in *KafkaServerConfigSpec) DeepCopy() *KafkaServerConfigSpec { + if in == nil { + return nil + } + out := new(KafkaServerConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KafkaServerConfigStatus) DeepCopyInto(out *KafkaServerConfigStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KafkaServerConfigStatus. +func (in *KafkaServerConfigStatus) DeepCopy() *KafkaServerConfigStatus { + if in == nil { + return nil + } + out := new(KafkaServerConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KafkaTarget) DeepCopyInto(out *KafkaTarget) { + *out = *in + if in.Topics != nil { + in, out := &in.Topics, &out.Topics + *out = make([]KafkaTopic, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KafkaTarget. +func (in *KafkaTarget) DeepCopy() *KafkaTarget { + if in == nil { + return nil + } + out := new(KafkaTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KafkaTopic) DeepCopyInto(out *KafkaTopic) { + *out = *in + if in.Operations != nil { + in, out := &in.Operations, &out.Operations + *out = make([]KafkaOperation, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KafkaTopic. +func (in *KafkaTopic) DeepCopy() *KafkaTopic { + if in == nil { + return nil + } + out := new(KafkaTopic) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesTarget) DeepCopyInto(out *KubernetesTarget) { + *out = *in + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = make([]HTTPTarget, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesTarget. +func (in *KubernetesTarget) DeepCopy() *KubernetesTarget { + if in == nil { + return nil + } + out := new(KubernetesTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MySQLServerConfig) DeepCopyInto(out *MySQLServerConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MySQLServerConfig. +func (in *MySQLServerConfig) DeepCopy() *MySQLServerConfig { + if in == nil { + return nil + } + out := new(MySQLServerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MySQLServerConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MySQLServerConfigList) DeepCopyInto(out *MySQLServerConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MySQLServerConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MySQLServerConfigList. +func (in *MySQLServerConfigList) DeepCopy() *MySQLServerConfigList { + if in == nil { + return nil + } + out := new(MySQLServerConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MySQLServerConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MySQLServerConfigSpec) DeepCopyInto(out *MySQLServerConfigSpec) { + *out = *in + in.Credentials.DeepCopyInto(&out.Credentials) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MySQLServerConfigSpec. +func (in *MySQLServerConfigSpec) DeepCopy() *MySQLServerConfigSpec { + if in == nil { + return nil + } + out := new(MySQLServerConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MySQLServerConfigStatus) DeepCopyInto(out *MySQLServerConfigStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MySQLServerConfigStatus. +func (in *MySQLServerConfigStatus) DeepCopy() *MySQLServerConfigStatus { + if in == nil { + return nil + } + out := new(MySQLServerConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLServerConfig) DeepCopyInto(out *PostgreSQLServerConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLServerConfig. +func (in *PostgreSQLServerConfig) DeepCopy() *PostgreSQLServerConfig { + if in == nil { + return nil + } + out := new(PostgreSQLServerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgreSQLServerConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLServerConfigList) DeepCopyInto(out *PostgreSQLServerConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PostgreSQLServerConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLServerConfigList. +func (in *PostgreSQLServerConfigList) DeepCopy() *PostgreSQLServerConfigList { + if in == nil { + return nil + } + out := new(PostgreSQLServerConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgreSQLServerConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLServerConfigSpec) DeepCopyInto(out *PostgreSQLServerConfigSpec) { + *out = *in + in.Credentials.DeepCopyInto(&out.Credentials) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLServerConfigSpec. +func (in *PostgreSQLServerConfigSpec) DeepCopy() *PostgreSQLServerConfigSpec { + if in == nil { + return nil + } + out := new(PostgreSQLServerConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgreSQLServerConfigStatus) DeepCopyInto(out *PostgreSQLServerConfigStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLServerConfigStatus. +func (in *PostgreSQLServerConfigStatus) DeepCopy() *PostgreSQLServerConfigStatus { + if in == nil { + return nil + } + out := new(PostgreSQLServerConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProtectedService) DeepCopyInto(out *ProtectedService) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProtectedService. +func (in *ProtectedService) DeepCopy() *ProtectedService { + if in == nil { + return nil + } + out := new(ProtectedService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProtectedService) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProtectedServiceList) DeepCopyInto(out *ProtectedServiceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProtectedService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProtectedServiceList. +func (in *ProtectedServiceList) DeepCopy() *ProtectedServiceList { + if in == nil { + return nil + } + out := new(ProtectedServiceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProtectedServiceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProtectedServiceSpec) DeepCopyInto(out *ProtectedServiceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProtectedServiceSpec. +func (in *ProtectedServiceSpec) DeepCopy() *ProtectedServiceSpec { + if in == nil { + return nil + } + out := new(ProtectedServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProtectedServiceStatus) DeepCopyInto(out *ProtectedServiceStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProtectedServiceStatus. +func (in *ProtectedServiceStatus) DeepCopy() *ProtectedServiceStatus { + if in == nil { + return nil + } + out := new(ProtectedServiceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedIPs) DeepCopyInto(out *ResolvedIPs) { + *out = *in + if in.IPs != nil { + in, out := &in.IPs, &out.IPs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedIPs. +func (in *ResolvedIPs) DeepCopy() *ResolvedIPs { + if in == nil { + return nil + } + out := new(ResolvedIPs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SQLPrivileges) DeepCopyInto(out *SQLPrivileges) { + *out = *in + if in.Operations != nil { + in, out := &in.Operations, &out.Operations + *out = make([]DatabaseOperation, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SQLPrivileges. +func (in *SQLPrivileges) DeepCopy() *SQLPrivileges { + if in == nil { + return nil + } + out := new(SQLPrivileges) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SQLTarget) DeepCopyInto(out *SQLTarget) { + *out = *in + if in.Privileges != nil { + in, out := &in.Privileges, &out.Privileges + *out = make([]SQLPrivileges, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SQLTarget. +func (in *SQLTarget) DeepCopy() *SQLTarget { + if in == nil { + return nil + } + out := new(SQLTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceTarget) DeepCopyInto(out *ServiceTarget) { + *out = *in + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = make([]HTTPTarget, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceTarget. +func (in *ServiceTarget) DeepCopy() *ServiceTarget { + if in == nil { + return nil + } + out := new(ServiceTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSSource) DeepCopyInto(out *TLSSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSSource. +func (in *TLSSource) DeepCopy() *TLSSource { + if in == nil { + return nil + } + out := new(TLSSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Target) DeepCopyInto(out *Target) { + *out = *in + if in.Kubernetes != nil { + in, out := &in.Kubernetes, &out.Kubernetes + *out = new(KubernetesTarget) + (*in).DeepCopyInto(*out) + } + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(ServiceTarget) + (*in).DeepCopyInto(*out) + } + if in.Kafka != nil { + in, out := &in.Kafka, &out.Kafka + *out = new(KafkaTarget) + (*in).DeepCopyInto(*out) + } + if in.SQL != nil { + in, out := &in.SQL, &out.SQL + *out = new(SQLTarget) + (*in).DeepCopyInto(*out) + } + if in.AWS != nil { + in, out := &in.AWS, &out.AWS + *out = new(AWSTarget) + (*in).DeepCopyInto(*out) + } + if in.GCP != nil { + in, out := &in.GCP, &out.GCP + *out = new(GCPTarget) + (*in).DeepCopyInto(*out) + } + if in.Azure != nil { + in, out := &in.Azure, &out.Azure + *out = new(AzureTarget) + (*in).DeepCopyInto(*out) + } + if in.Internet != nil { + in, out := &in.Internet, &out.Internet + *out = new(Internet) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Target. +func (in *Target) DeepCopy() *Target { + if in == nil { + return nil + } + out := new(Target) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TopicConfig) DeepCopyInto(out *TopicConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopicConfig. +func (in *TopicConfig) DeepCopy() *TopicConfig { + if in == nil { + return nil + } + out := new(TopicConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Workload) DeepCopyInto(out *Workload) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workload. +func (in *Workload) DeepCopy() *Workload { + if in == nil { + return nil + } + out := new(Workload) + in.DeepCopyInto(out) + return out +} diff --git a/src/operator/config/crd/k8s.otterize.com_clientintents.patched b/src/operator/config/crd/k8s.otterize.com_clientintents.patched index 583e87e9e..886449786 100644 --- a/src/operator/config/crd/k8s.otterize.com_clientintents.patched +++ b/src/operator/config/crd/k8s.otterize.com_clientintents.patched @@ -1037,3 +1037,334 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: ClientIntents is the Schema for the intents API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IntentsSpec defines the desired state of ClientIntents + properties: + targets: + items: + properties: + aws: + properties: + actions: + items: + type: string + type: array + arn: + type: string + type: object + azure: + properties: + actions: + items: + type: string + type: array + dataActions: + items: + type: string + type: array + keyVaultPolicy: + properties: + certificatePermissions: + items: + enum: + - all + - backup + - create + - delete + - deleteissuers + - get + - getissuers + - import + - list + - listissuers + - managecontacts + - manageissuers + - purge + - recover + - restore + - setissuers + - update + type: string + type: array + keyPermissions: + items: + enum: + - all + - backup + - create + - decrypt + - delete + - encrypt + - get + - getrotationpolicy + - import + - list + - purge + - recover + - release + - restore + - rotate + - setrotationpolicy + - sign + - unwrapkey + - update + - verify + - wrapkey + type: string + type: array + secretPermissions: + items: + enum: + - all + - backup + - delete + - get + - list + - purge + - recover + - restore + - set + type: string + type: array + storagePermissions: + items: + enum: + - all + - backup + - delete + - deletesas + - get + - getsas + - list + - listsas + - purge + - recover + - regeneratekey + - restore + - set + - setsas + - update + type: string + type: array + type: object + roles: + items: + type: string + type: array + scope: + type: string + type: object + gcp: + properties: + permissions: + items: + type: string + type: array + resource: + type: string + type: object + internet: + properties: + domains: + items: + type: string + type: array + ips: + items: + type: string + type: array + ports: + items: + type: integer + type: array + type: object + kafka: + properties: + name: + type: string + topics: + items: + properties: + name: + type: string + operations: + items: + enum: + - all + - consume + - produce + - create + - alter + - delete + - describe + - ClusterAction + - DescribeConfigs + - AlterConfigs + - IdempotentWrite + type: string + type: array + required: + - name + - operations + type: object + type: array + required: + - name + type: object + kubernetes: + properties: + http: + items: + properties: + methods: + items: + enum: + - GET + - POST + - PUT + - DELETE + - OPTIONS + - TRACE + - PATCH + - CONNECT + type: string + type: array + path: + type: string + required: + - methods + - path + type: object + type: array + kind: + type: string + name: + type: string + required: + - name + type: object + service: + properties: + http: + items: + properties: + methods: + items: + enum: + - GET + - POST + - PUT + - DELETE + - OPTIONS + - TRACE + - PATCH + - CONNECT + type: string + type: array + path: + type: string + required: + - methods + - path + type: object + type: array + name: + type: string + required: + - name + type: object + sql: + properties: + name: + type: string + privileges: + items: + properties: + databaseName: + type: string + operations: + items: + enum: + - ALL + - SELECT + - INSERT + - UPDATE + - DELETE + type: string + type: array + table: + type: string + required: + - databaseName + type: object + type: array + required: + - name + type: object + type: object + type: array + workload: + properties: + kind: + type: string + name: + type: string + required: + - name + type: object + required: + - targets + - workload + type: object + status: + description: IntentsStatus defines the observed state of ClientIntents + properties: + observedGeneration: + description: The last generation of the intents that was successfully reconciled. + format: int64 + type: integer + resolvedIPs: + description: ResolvedIPs stores resolved IPs for a domain name - the network mapper populates it when DNS internetTarget is used + items: + properties: + dns: + type: string + ips: + items: + type: string + type: array + type: object + type: array + upToDate: + description: |- + upToDate field reflects whether the client intents have successfully been applied + to the cluster to the state specified + type: boolean + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_clientintents.yaml b/src/operator/config/crd/k8s.otterize.com_clientintents.yaml index 965ee1e50..5ab73bca0 100644 --- a/src/operator/config/crd/k8s.otterize.com_clientintents.yaml +++ b/src/operator/config/crd/k8s.otterize.com_clientintents.yaml @@ -1028,3 +1028,336 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: ClientIntents is the Schema for the intents API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IntentsSpec defines the desired state of ClientIntents + properties: + targets: + items: + properties: + aws: + properties: + actions: + items: + type: string + type: array + arn: + type: string + type: object + azure: + properties: + actions: + items: + type: string + type: array + dataActions: + items: + type: string + type: array + keyVaultPolicy: + properties: + certificatePermissions: + items: + enum: + - all + - backup + - create + - delete + - deleteissuers + - get + - getissuers + - import + - list + - listissuers + - managecontacts + - manageissuers + - purge + - recover + - restore + - setissuers + - update + type: string + type: array + keyPermissions: + items: + enum: + - all + - backup + - create + - decrypt + - delete + - encrypt + - get + - getrotationpolicy + - import + - list + - purge + - recover + - release + - restore + - rotate + - setrotationpolicy + - sign + - unwrapkey + - update + - verify + - wrapkey + type: string + type: array + secretPermissions: + items: + enum: + - all + - backup + - delete + - get + - list + - purge + - recover + - restore + - set + type: string + type: array + storagePermissions: + items: + enum: + - all + - backup + - delete + - deletesas + - get + - getsas + - list + - listsas + - purge + - recover + - regeneratekey + - restore + - set + - setsas + - update + type: string + type: array + type: object + roles: + items: + type: string + type: array + scope: + type: string + type: object + gcp: + properties: + permissions: + items: + type: string + type: array + resource: + type: string + type: object + internet: + properties: + domains: + items: + type: string + type: array + ips: + items: + type: string + type: array + ports: + items: + type: integer + type: array + type: object + kafka: + properties: + name: + type: string + topics: + items: + properties: + name: + type: string + operations: + items: + enum: + - all + - consume + - produce + - create + - alter + - delete + - describe + - ClusterAction + - DescribeConfigs + - AlterConfigs + - IdempotentWrite + type: string + type: array + required: + - name + - operations + type: object + type: array + required: + - name + type: object + kubernetes: + properties: + http: + items: + properties: + methods: + items: + enum: + - GET + - POST + - PUT + - DELETE + - OPTIONS + - TRACE + - PATCH + - CONNECT + type: string + type: array + path: + type: string + required: + - methods + - path + type: object + type: array + kind: + type: string + name: + type: string + required: + - name + type: object + service: + properties: + http: + items: + properties: + methods: + items: + enum: + - GET + - POST + - PUT + - DELETE + - OPTIONS + - TRACE + - PATCH + - CONNECT + type: string + type: array + path: + type: string + required: + - methods + - path + type: object + type: array + name: + type: string + required: + - name + type: object + sql: + properties: + name: + type: string + privileges: + items: + properties: + databaseName: + type: string + operations: + items: + enum: + - ALL + - SELECT + - INSERT + - UPDATE + - DELETE + type: string + type: array + table: + type: string + required: + - databaseName + type: object + type: array + required: + - name + type: object + type: object + type: array + workload: + properties: + kind: + type: string + name: + type: string + required: + - name + type: object + required: + - targets + - workload + type: object + status: + description: IntentsStatus defines the observed state of ClientIntents + properties: + observedGeneration: + description: The last generation of the intents that was successfully + reconciled. + format: int64 + type: integer + resolvedIPs: + description: ResolvedIPs stores resolved IPs for a domain name - the + network mapper populates it when DNS internetTarget is used + items: + properties: + dns: + type: string + ips: + items: + type: string + type: array + type: object + type: array + upToDate: + description: |- + upToDate field reflects whether the client intents have successfully been applied + to the cluster to the state specified + type: boolean + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.patched b/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.patched index c6a099b1a..0f0398eb1 100644 --- a/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.patched +++ b/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.patched @@ -362,3 +362,87 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: KafkaServerConfig is the Schema for the kafkaserverconfigs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: KafkaServerConfigSpec defines the desired state of KafkaServerConfig + properties: + addr: + type: string + noAutoCreateIntentsForOperator: + description: |- + If Intents for network policies are enabled, and there are other Intents to this Kafka server, + will automatically create an Target so that the Intents Operator can connect. Set to true to disable. + type: boolean + tls: + properties: + certFile: + type: string + keyFile: + type: string + rootCAFile: + type: string + required: + - certFile + - keyFile + - rootCAFile + type: object + topics: + items: + properties: + clientIdentityRequired: + type: boolean + intentsRequired: + type: boolean + pattern: + enum: + - literal + - prefix + type: string + topic: + type: string + required: + - clientIdentityRequired + - intentsRequired + - pattern + - topic + type: object + type: array + workload: + properties: + kind: + type: string + name: + type: string + required: + - name + type: object + type: object + status: + description: KafkaServerConfigStatus defines the observed state of KafkaServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.yaml b/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.yaml index 80e8aa5ad..baf6eaf27 100644 --- a/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.yaml +++ b/src/operator/config/crd/k8s.otterize.com_kafkaserverconfigs.yaml @@ -348,3 +348,87 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: KafkaServerConfig is the Schema for the kafkaserverconfigs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: KafkaServerConfigSpec defines the desired state of KafkaServerConfig + properties: + addr: + type: string + noAutoCreateIntentsForOperator: + description: |- + If Intents for network policies are enabled, and there are other Intents to this Kafka server, + will automatically create an Target so that the Intents Operator can connect. Set to true to disable. + type: boolean + tls: + properties: + certFile: + type: string + keyFile: + type: string + rootCAFile: + type: string + required: + - certFile + - keyFile + - rootCAFile + type: object + topics: + items: + properties: + clientIdentityRequired: + type: boolean + intentsRequired: + type: boolean + pattern: + enum: + - literal + - prefix + type: string + topic: + type: string + required: + - clientIdentityRequired + - intentsRequired + - pattern + - topic + type: object + type: array + workload: + properties: + kind: + type: string + name: + type: string + required: + - name + type: object + type: object + status: + description: KafkaServerConfigStatus defines the observed state of KafkaServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.patched b/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.patched index 3d2128024..7c6d8067d 100644 --- a/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.patched +++ b/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.patched @@ -235,3 +235,72 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: MySQLServerConfig is the Schema for the mysqlserverconfig API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: MySQLServerConfigSpec defines the desired state of MySQLServerConfig + properties: + address: + type: string + credentials: + description: DatabaseCredentials defines the credentials to access the database + properties: + password: + description: Password is the plaintext password to access the database + type: string + secretRef: + description: SecretRef is a reference to a k8s secret storing the credentials + properties: + name: + description: Name is the name of he k8s secret storing the credentials + type: string + namespace: + description: |- + Namespace is the namespace in which the secret is stored. + If not provided, the operator will look for the secret in the same namespace as the database ServerConfig. + type: string + passwordKey: + default: password + description: PasswordKey is the key in the secret that stores the password + type: string + usernameKey: + default: username + description: UsernameKey is the key in the secret that stores the username + type: string + type: object + username: + description: Username is the plaintext username to access the database + type: string + type: object + required: + - address + - credentials + type: object + status: + description: MySQLServerConfigStatus defines the observed state of MySQLServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.yaml b/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.yaml index 13369251c..8a47c9819 100644 --- a/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.yaml +++ b/src/operator/config/crd/k8s.otterize.com_mysqlserverconfigs.yaml @@ -242,3 +242,79 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: MySQLServerConfig is the Schema for the mysqlserverconfig API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: MySQLServerConfigSpec defines the desired state of MySQLServerConfig + properties: + address: + type: string + credentials: + description: DatabaseCredentials defines the credentials to access + the database + properties: + password: + description: Password is the plaintext password to access the + database + type: string + secretRef: + description: SecretRef is a reference to a k8s secret storing + the credentials + properties: + name: + description: Name is the name of he k8s secret storing the + credentials + type: string + namespace: + description: |- + Namespace is the namespace in which the secret is stored. + If not provided, the operator will look for the secret in the same namespace as the database ServerConfig. + type: string + passwordKey: + default: password + description: PasswordKey is the key in the secret that stores + the password + type: string + usernameKey: + default: username + description: UsernameKey is the key in the secret that stores + the username + type: string + type: object + username: + description: Username is the plaintext username to access the + database + type: string + type: object + required: + - address + - credentials + type: object + status: + description: MySQLServerConfigStatus defines the observed state of MySQLServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.patched b/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.patched index b3b101982..50581d30c 100644 --- a/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.patched +++ b/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.patched @@ -235,3 +235,72 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: PostgreSQLServerConfig is the Schema for the databaseserverconfig API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PostgreSQLServerConfigSpec defines the desired state of PostgreSQLServerConfig + properties: + address: + type: string + credentials: + description: DatabaseCredentials defines the credentials to access the database + properties: + password: + description: Password is the plaintext password to access the database + type: string + secretRef: + description: SecretRef is a reference to a k8s secret storing the credentials + properties: + name: + description: Name is the name of he k8s secret storing the credentials + type: string + namespace: + description: |- + Namespace is the namespace in which the secret is stored. + If not provided, the operator will look for the secret in the same namespace as the database ServerConfig. + type: string + passwordKey: + default: password + description: PasswordKey is the key in the secret that stores the password + type: string + usernameKey: + default: username + description: UsernameKey is the key in the secret that stores the username + type: string + type: object + username: + description: Username is the plaintext username to access the database + type: string + type: object + required: + - address + - credentials + type: object + status: + description: PostgreSQLServerConfigStatus defines the observed state of PostgreSQLServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.yaml b/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.yaml index 1bf43a88c..6ebc4ba59 100644 --- a/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.yaml +++ b/src/operator/config/crd/k8s.otterize.com_postgresqlserverconfigs.yaml @@ -248,3 +248,81 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: PostgreSQLServerConfig is the Schema for the databaseserverconfig + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PostgreSQLServerConfigSpec defines the desired state of PostgreSQLServerConfig + properties: + address: + type: string + credentials: + description: DatabaseCredentials defines the credentials to access + the database + properties: + password: + description: Password is the plaintext password to access the + database + type: string + secretRef: + description: SecretRef is a reference to a k8s secret storing + the credentials + properties: + name: + description: Name is the name of he k8s secret storing the + credentials + type: string + namespace: + description: |- + Namespace is the namespace in which the secret is stored. + If not provided, the operator will look for the secret in the same namespace as the database ServerConfig. + type: string + passwordKey: + default: password + description: PasswordKey is the key in the secret that stores + the password + type: string + usernameKey: + default: username + description: UsernameKey is the key in the secret that stores + the username + type: string + type: object + username: + description: Username is the plaintext username to access the + database + type: string + type: object + required: + - address + - credentials + type: object + status: + description: PostgreSQLServerConfigStatus defines the observed state of + PostgreSQLServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_protectedservices.patched b/src/operator/config/crd/k8s.otterize.com_protectedservices.patched index ebd3793f0..72b8b48f3 100644 --- a/src/operator/config/crd/k8s.otterize.com_protectedservices.patched +++ b/src/operator/config/crd/k8s.otterize.com_protectedservices.patched @@ -178,3 +178,41 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: ProtectedService is the Schema for the protectedservice API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ProtectedServiceSpec defines the desired state of ProtectedService + properties: + kind: + type: string + name: + type: string + type: object + status: + description: ProtectedServiceStatus defines the observed state of ProtectedService + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/crd/k8s.otterize.com_protectedservices.yaml b/src/operator/config/crd/k8s.otterize.com_protectedservices.yaml index fd1000203..e9d352725 100644 --- a/src/operator/config/crd/k8s.otterize.com_protectedservices.yaml +++ b/src/operator/config/crd/k8s.otterize.com_protectedservices.yaml @@ -164,3 +164,41 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: ProtectedService is the Schema for the protectedservice API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ProtectedServiceSpec defines the desired state of ProtectedService + properties: + kind: + type: string + name: + type: string + type: object + status: + description: ProtectedServiceStatus defines the observed state of ProtectedService + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/config/webhook/manifests-patched b/src/operator/config/webhook/manifests-patched index ea75032fc..7959eabd0 100644 --- a/src/operator/config/webhook/manifests-patched +++ b/src/operator/config/webhook/manifests-patched @@ -101,6 +101,27 @@ webhooks: resources: - clientintents sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: intents-operator-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-k8s-otterize-com-v2beta1-clientintents + failurePolicy: Fail + matchPolicy: Exact + name: clientintentsv2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - clientintents + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -164,6 +185,27 @@ webhooks: resources: - mysqlserverconfigs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: intents-operator-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-k8s-otterize-com-v2beta1-mysqlserverconfig + failurePolicy: Fail + matchPolicy: Exact + name: mysqlserverconfigv2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - mysqlserverconfigs + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -227,6 +269,27 @@ webhooks: resources: - postgresqlserverconfigs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: intents-operator-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-k8s-otterize-com-v2beta1-postgresqlserverconfig + failurePolicy: Fail + matchPolicy: Exact + name: postgresqlserverconfigv2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - postgresqlserverconfigs + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -311,3 +374,24 @@ webhooks: resources: - protectedservice sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: intents-operator-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-k8s-otterize-com-v2beta1-protectedservice + failurePolicy: Fail + matchPolicy: Exact + name: protectedservicev2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - protectedservice + sideEffects: None diff --git a/src/operator/config/webhook/manifests.yaml b/src/operator/config/webhook/manifests.yaml index bdaa0f2de..2d047f925 100644 --- a/src/operator/config/webhook/manifests.yaml +++ b/src/operator/config/webhook/manifests.yaml @@ -88,6 +88,27 @@ webhooks: resources: - clientintents sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-k8s-otterize-com-v2beta1-clientintents + failurePolicy: Fail + matchPolicy: Exact + name: clientintentsv2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - clientintents + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -151,6 +172,27 @@ webhooks: resources: - mysqlserverconfigs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-k8s-otterize-com-v2beta1-mysqlserverconfig + failurePolicy: Fail + matchPolicy: Exact + name: mysqlserverconfigv2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - mysqlserverconfigs + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -214,6 +256,27 @@ webhooks: resources: - postgresqlserverconfigs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-k8s-otterize-com-v2beta1-postgresqlserverconfig + failurePolicy: Fail + matchPolicy: Exact + name: postgresqlserverconfigv2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - postgresqlserverconfigs + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -298,3 +361,24 @@ webhooks: resources: - protectedservice sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-k8s-otterize-com-v2beta1-protectedservice + failurePolicy: Fail + matchPolicy: Exact + name: protectedservicev2beta1.kb.io + rules: + - apiGroups: + - k8s.otterize.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - protectedservice + sideEffects: None diff --git a/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_test.go b/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_test.go index 39ae991b8..15b5b4a6b 100644 --- a/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_test.go +++ b/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_test.go @@ -9,6 +9,7 @@ import ( otterizev1alpha3 "github.com/otterize/intents-operator/src/operator/api/v1alpha3" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/operator/controllers" "github.com/otterize/intents-operator/src/operator/controllers/external_traffic" "github.com/otterize/intents-operator/src/operator/controllers/intents_reconcilers" @@ -69,6 +70,7 @@ func (s *ExternalNetworkPolicyReconcilerTestSuite) SetupSuite() { utilruntime.Must(otterizev1alpha3.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev1beta1.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev2alpha1.AddToScheme(s.TestEnv.Scheme)) + utilruntime.Must(otterizev2beta1.AddToScheme(s.TestEnv.Scheme)) s.RestConfig, err = s.TestEnv.Start() s.Require().NoError(err) @@ -87,6 +89,8 @@ func (s *ExternalNetworkPolicyReconcilerTestSuite) SetupTest() { s.Require().NoError((&otterizev1alpha3.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator13)) intentsValidator2 := webhooks.NewIntentsValidatorV2alpha1(s.Mgr.GetClient()) s.Require().NoError((&otterizev2alpha1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2)) + intentsValidator2beta1 := webhooks.NewIntentsValidatorV2beta1(s.Mgr.GetClient()) + s.Require().NoError((&otterizev2beta1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2beta1)) recorder := s.Mgr.GetEventRecorderFor("intents-operator") testName := s.T().Name() diff --git a/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_ingress_controllers_configured_test.go b/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_ingress_controllers_configured_test.go index c991c7406..fb5088d83 100644 --- a/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_ingress_controllers_configured_test.go +++ b/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_ingress_controllers_configured_test.go @@ -9,6 +9,7 @@ import ( otterizev1alpha3 "github.com/otterize/intents-operator/src/operator/api/v1alpha3" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/operator/controllers" "github.com/otterize/intents-operator/src/operator/controllers/external_traffic" "github.com/otterize/intents-operator/src/operator/controllers/intents_reconcilers" @@ -77,6 +78,7 @@ func (s *ExternalNetworkPolicyReconcilerWithIngressControllersConfiguredTestSuit utilruntime.Must(otterizev1alpha3.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev1beta1.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev2alpha1.AddToScheme(s.TestEnv.Scheme)) + utilruntime.Must(otterizev2beta1.AddToScheme(s.TestEnv.Scheme)) s.RestConfig, err = s.TestEnv.Start() s.Require().NoError(err) @@ -95,6 +97,8 @@ func (s *ExternalNetworkPolicyReconcilerWithIngressControllersConfiguredTestSuit s.Require().NoError((&otterizev1alpha3.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator13)) intentsValidator2 := webhooks.NewIntentsValidatorV2alpha1(s.Mgr.GetClient()) s.Require().NoError((&otterizev2alpha1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2)) + intentsValidator2beta1 := webhooks.NewIntentsValidatorV2beta1(s.Mgr.GetClient()) + s.Require().NoError((&otterizev2beta1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2beta1)) recorder := s.Mgr.GetEventRecorderFor("intents-operator") testName := s.T().Name() diff --git a/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_no_intents_test.go b/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_no_intents_test.go index 1fefb4af7..d3a693455 100644 --- a/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_no_intents_test.go +++ b/src/operator/controllers/intents_reconcilers/external_traffic_network_policy/external_traffic_network_policy_with_no_intents_test.go @@ -8,6 +8,7 @@ import ( otterizev1alpha3 "github.com/otterize/intents-operator/src/operator/api/v1alpha3" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/operator/controllers" "github.com/otterize/intents-operator/src/operator/controllers/external_traffic" "github.com/otterize/intents-operator/src/operator/controllers/intents_reconcilers" @@ -66,6 +67,7 @@ func (s *ExternalNetworkPolicyReconcilerWithNoIntentsTestSuite) SetupSuite() { utilruntime.Must(otterizev1alpha3.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev1beta1.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev2alpha1.AddToScheme(s.TestEnv.Scheme)) + utilruntime.Must(otterizev2beta1.AddToScheme(s.TestEnv.Scheme)) s.RestConfig, err = s.TestEnv.Start() s.Require().NoError(err) @@ -84,6 +86,8 @@ func (s *ExternalNetworkPolicyReconcilerWithNoIntentsTestSuite) SetupTest() { s.Require().NoError((&otterizev1alpha3.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator13)) intentsValidator2 := webhooks.NewIntentsValidatorV2alpha1(s.Mgr.GetClient()) s.Require().NoError((&otterizev2alpha1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2)) + intentsValidator2Beta1 := webhooks.NewIntentsValidatorV2beta1(s.Mgr.GetClient()) + s.Require().NoError((&otterizev2beta1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2Beta1)) recorder := s.Mgr.GetEventRecorderFor("intents-operator") netpolHandler := external_traffic.NewNetworkPolicyHandler(s.Mgr.GetClient(), s.TestEnv.Scheme, allowexternaltraffic.Always, make([]serviceidentity.ServiceIdentity, 0), false) diff --git a/src/operator/controllers/intents_reconcilers/kafka_acls_test.go b/src/operator/controllers/intents_reconcilers/kafka_acls_test.go index 320facd31..628c3c4d8 100644 --- a/src/operator/controllers/intents_reconcilers/kafka_acls_test.go +++ b/src/operator/controllers/intents_reconcilers/kafka_acls_test.go @@ -9,6 +9,7 @@ import ( otterizev1alpha3 "github.com/otterize/intents-operator/src/operator/api/v1alpha3" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/operator/controllers/intents_reconcilers/consts" intentsreconcilersmocks "github.com/otterize/intents-operator/src/operator/controllers/intents_reconcilers/mocks" "github.com/otterize/intents-operator/src/operator/controllers/kafkaacls" @@ -74,6 +75,7 @@ func (s *KafkaACLReconcilerTestSuite) SetupSuite() { utilruntime.Must(otterizev1alpha3.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev1beta1.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev2alpha1.AddToScheme(s.TestEnv.Scheme)) + utilruntime.Must(otterizev2beta1.AddToScheme(s.TestEnv.Scheme)) s.RestConfig, err = s.TestEnv.Start() s.Require().NoError(err) @@ -217,6 +219,8 @@ func (s *KafkaACLReconcilerTestSuite) SetupTest() { s.Require().NoError((&otterizev1alpha3.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator13)) intentsValidator2 := webhooks.NewIntentsValidatorV2alpha1(s.Mgr.GetClient()) s.Require().NoError((&otterizev2alpha1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2)) + intentsValidator2beta := webhooks.NewIntentsValidatorV2beta1(s.Mgr.GetClient()) + s.Require().NoError((&otterizev2beta1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2beta)) } func (s *KafkaACLReconcilerTestSuite) TestKafkaACLGetCreatedAndUpdatedBasedOnIntents() { diff --git a/src/operator/controllers/pod_reconcilers/pods_test.go b/src/operator/controllers/pod_reconcilers/pods_test.go index 8451dbc8c..26b4f88fd 100644 --- a/src/operator/controllers/pod_reconcilers/pods_test.go +++ b/src/operator/controllers/pod_reconcilers/pods_test.go @@ -7,6 +7,7 @@ import ( otterizev1alpha3 "github.com/otterize/intents-operator/src/operator/api/v1alpha3" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" mocks "github.com/otterize/intents-operator/src/operator/controllers/intents_reconcilers/mocks" podreconcilersmocks "github.com/otterize/intents-operator/src/operator/controllers/pod_reconcilers/mocks" "github.com/otterize/intents-operator/src/operator/webhooks" @@ -51,6 +52,7 @@ func (s *WatcherPodLabelReconcilerTestSuite) SetupSuite() { utilruntime.Must(otterizev1alpha3.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev1beta1.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev2alpha1.AddToScheme(s.TestEnv.Scheme)) + utilruntime.Must(otterizev2beta1.AddToScheme(s.TestEnv.Scheme)) s.RestConfig, err = s.TestEnv.Start() s.Require().NoError(err) @@ -69,6 +71,8 @@ func (s *WatcherPodLabelReconcilerTestSuite) SetupTest() { s.Require().NoError((&otterizev1alpha3.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator13)) intentsValidator2 := webhooks.NewIntentsValidatorV2alpha1(s.Mgr.GetClient()) s.Require().NoError((&otterizev2alpha1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2)) + intentsValidator2beta1 := webhooks.NewIntentsValidatorV2beta1(s.Mgr.GetClient()) + s.Require().NoError((&otterizev2beta1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2beta1)) recorder := s.Mgr.GetEventRecorderFor("intents-operator") controller := gomock.NewController(s.T()) diff --git a/src/operator/main.go b/src/operator/main.go index 6ef769508..ce41fa01b 100644 --- a/src/operator/main.go +++ b/src/operator/main.go @@ -27,6 +27,7 @@ import ( otterizev1alpha2 "github.com/otterize/intents-operator/src/operator/api/v1alpha2" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/operator/controllers" "github.com/otterize/intents-operator/src/operator/controllers/external_traffic" "github.com/otterize/intents-operator/src/operator/controllers/iam_pod_reconciler" @@ -103,6 +104,7 @@ func init() { utilruntime.Must(otterizev1alpha3.AddToScheme(scheme)) utilruntime.Must(otterizev1beta1.AddToScheme(scheme)) utilruntime.Must(otterizev2alpha1.AddToScheme(scheme)) + utilruntime.Must(otterizev2beta1.AddToScheme(scheme)) utilruntime.Must(linkerdauthscheme.AddToScheme(scheme)) utilruntime.Must(linkerdserverscheme.AddToScheme(scheme)) @@ -578,6 +580,11 @@ func initWebhookValidators(mgr manager.Manager) { logrus.WithError(err).Panic(err, "unable to create webhook v2alpha1", "webhook", "ClientIntents") } + intentsValidatorV2beta1 := webhooks.NewIntentsValidatorV2beta1(mgr.GetClient()) + if err := (&otterizev2beta1.ClientIntents{}).SetupWebhookWithManager(mgr, intentsValidatorV2beta1); err != nil { + logrus.WithError(err).Panic(err, "unable to create webhook v2beta1", "webhook", "ClientIntents") + } + protectedServiceValidator := webhooks.NewProtectedServiceValidatorV1alpha2(mgr.GetClient()) if err := (&otterizev1alpha2.ProtectedService{}).SetupWebhookWithManager(mgr, protectedServiceValidator); err != nil { logrus.WithError(err).Panic("unable to create webhook v1alpha2", "webhook", "ProtectedService") @@ -598,6 +605,11 @@ func initWebhookValidators(mgr manager.Manager) { logrus.WithError(err).Panic("unable to create webhook v2alpha1", "webhook", "ProtectedService") } + protectedServiceValidatorV2beta1 := webhooks.NewProtectedServiceValidatorV2beta1(mgr.GetClient()) + if err := (&otterizev2beta1.ProtectedService{}).SetupWebhookWithManager(mgr, protectedServiceValidatorV2beta1); err != nil { + logrus.WithError(err).Panic("unable to create webhook v2beta1", "webhook", "ProtectedService") + } + if err := (&otterizev1alpha2.KafkaServerConfig{}).SetupWebhookWithManager(mgr); err != nil { logrus.WithError(err).Panic("unable to create webhook v1alpha2", "webhook", "KafkaServerConfig") } @@ -614,6 +626,10 @@ func initWebhookValidators(mgr manager.Manager) { logrus.WithError(err).Panic("unable to create webhook v2alpha1", "webhook", "KafkaServerConfig") } + if err := (&otterizev2beta1.KafkaServerConfig{}).SetupWebhookWithManager(mgr); err != nil { + logrus.WithError(err).Panic("unable to create webhook v2beta1", "webhook", "KafkaServerConfig") + } + pgServerConfValidator := webhooks.NewPostgresConfValidator(mgr.GetClient()) if err := (&otterizev1alpha3.PostgreSQLServerConfig{}).SetupWebhookWithManager(mgr, pgServerConfValidator); err != nil { logrus.WithError(err).Panic("unable to create webhook v1alpha3", "webhook", "PostgreSQLServerConfig") @@ -629,6 +645,11 @@ func initWebhookValidators(mgr manager.Manager) { logrus.WithError(err).Panic("unable to create webhook v2alpha1", "webhook", "PostgreSQLServerConfig") } + pgServerConfValidatorV2beta1 := webhooks.NewPostgresConfValidatorV2beta1(mgr.GetClient()) + if err := (&otterizev2beta1.PostgreSQLServerConfig{}).SetupWebhookWithManager(mgr, pgServerConfValidatorV2beta1); err != nil { + logrus.WithError(err).Panic("unable to create webhook v2beta1", "webhook", "PostgreSQLServerConfig") + } + mysqlServerConfValidator := webhooks.NewMySQLConfValidator(mgr.GetClient()) if err := (&otterizev1alpha3.MySQLServerConfig{}).SetupWebhookWithManager(mgr, mysqlServerConfValidator); err != nil { logrus.WithError(err).Panic("unable to create webhook v1alpha3", "webhook", "MySQLServerConfig") @@ -643,4 +664,9 @@ func initWebhookValidators(mgr manager.Manager) { if err := (&otterizev2alpha1.MySQLServerConfig{}).SetupWebhookWithManager(mgr, mysqlServerConfValidatorV2alpha1); err != nil { logrus.WithError(err).Panic("unable to create webhook v2alpha1", "webhook", "MySQLServerConfig") } + + mysqlServerConfValidatorV2beta1 := webhooks.NewMySQLConfValidatorV2beta1(mgr.GetClient()) + if err := (&otterizev2beta1.MySQLServerConfig{}).SetupWebhookWithManager(mgr, mysqlServerConfValidatorV2beta1); err != nil { + logrus.WithError(err).Panic("unable to create webhook v2beta1", "webhook", "MySQLServerConfig") + } } diff --git a/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml b/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml index 583e87e9e..886449786 100644 --- a/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml +++ b/src/operator/otterizecrds/clientintents-customresourcedefinition.yaml @@ -1037,3 +1037,334 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: ClientIntents is the Schema for the intents API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IntentsSpec defines the desired state of ClientIntents + properties: + targets: + items: + properties: + aws: + properties: + actions: + items: + type: string + type: array + arn: + type: string + type: object + azure: + properties: + actions: + items: + type: string + type: array + dataActions: + items: + type: string + type: array + keyVaultPolicy: + properties: + certificatePermissions: + items: + enum: + - all + - backup + - create + - delete + - deleteissuers + - get + - getissuers + - import + - list + - listissuers + - managecontacts + - manageissuers + - purge + - recover + - restore + - setissuers + - update + type: string + type: array + keyPermissions: + items: + enum: + - all + - backup + - create + - decrypt + - delete + - encrypt + - get + - getrotationpolicy + - import + - list + - purge + - recover + - release + - restore + - rotate + - setrotationpolicy + - sign + - unwrapkey + - update + - verify + - wrapkey + type: string + type: array + secretPermissions: + items: + enum: + - all + - backup + - delete + - get + - list + - purge + - recover + - restore + - set + type: string + type: array + storagePermissions: + items: + enum: + - all + - backup + - delete + - deletesas + - get + - getsas + - list + - listsas + - purge + - recover + - regeneratekey + - restore + - set + - setsas + - update + type: string + type: array + type: object + roles: + items: + type: string + type: array + scope: + type: string + type: object + gcp: + properties: + permissions: + items: + type: string + type: array + resource: + type: string + type: object + internet: + properties: + domains: + items: + type: string + type: array + ips: + items: + type: string + type: array + ports: + items: + type: integer + type: array + type: object + kafka: + properties: + name: + type: string + topics: + items: + properties: + name: + type: string + operations: + items: + enum: + - all + - consume + - produce + - create + - alter + - delete + - describe + - ClusterAction + - DescribeConfigs + - AlterConfigs + - IdempotentWrite + type: string + type: array + required: + - name + - operations + type: object + type: array + required: + - name + type: object + kubernetes: + properties: + http: + items: + properties: + methods: + items: + enum: + - GET + - POST + - PUT + - DELETE + - OPTIONS + - TRACE + - PATCH + - CONNECT + type: string + type: array + path: + type: string + required: + - methods + - path + type: object + type: array + kind: + type: string + name: + type: string + required: + - name + type: object + service: + properties: + http: + items: + properties: + methods: + items: + enum: + - GET + - POST + - PUT + - DELETE + - OPTIONS + - TRACE + - PATCH + - CONNECT + type: string + type: array + path: + type: string + required: + - methods + - path + type: object + type: array + name: + type: string + required: + - name + type: object + sql: + properties: + name: + type: string + privileges: + items: + properties: + databaseName: + type: string + operations: + items: + enum: + - ALL + - SELECT + - INSERT + - UPDATE + - DELETE + type: string + type: array + table: + type: string + required: + - databaseName + type: object + type: array + required: + - name + type: object + type: object + type: array + workload: + properties: + kind: + type: string + name: + type: string + required: + - name + type: object + required: + - targets + - workload + type: object + status: + description: IntentsStatus defines the observed state of ClientIntents + properties: + observedGeneration: + description: The last generation of the intents that was successfully reconciled. + format: int64 + type: integer + resolvedIPs: + description: ResolvedIPs stores resolved IPs for a domain name - the network mapper populates it when DNS internetTarget is used + items: + properties: + dns: + type: string + ips: + items: + type: string + type: array + type: object + type: array + upToDate: + description: |- + upToDate field reflects whether the client intents have successfully been applied + to the cluster to the state specified + type: boolean + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/otterizecrds/kafkaserverconfigs-customresourcedefinition.yaml b/src/operator/otterizecrds/kafkaserverconfigs-customresourcedefinition.yaml index c6a099b1a..0f0398eb1 100644 --- a/src/operator/otterizecrds/kafkaserverconfigs-customresourcedefinition.yaml +++ b/src/operator/otterizecrds/kafkaserverconfigs-customresourcedefinition.yaml @@ -362,3 +362,87 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: KafkaServerConfig is the Schema for the kafkaserverconfigs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: KafkaServerConfigSpec defines the desired state of KafkaServerConfig + properties: + addr: + type: string + noAutoCreateIntentsForOperator: + description: |- + If Intents for network policies are enabled, and there are other Intents to this Kafka server, + will automatically create an Target so that the Intents Operator can connect. Set to true to disable. + type: boolean + tls: + properties: + certFile: + type: string + keyFile: + type: string + rootCAFile: + type: string + required: + - certFile + - keyFile + - rootCAFile + type: object + topics: + items: + properties: + clientIdentityRequired: + type: boolean + intentsRequired: + type: boolean + pattern: + enum: + - literal + - prefix + type: string + topic: + type: string + required: + - clientIdentityRequired + - intentsRequired + - pattern + - topic + type: object + type: array + workload: + properties: + kind: + type: string + name: + type: string + required: + - name + type: object + type: object + status: + description: KafkaServerConfigStatus defines the observed state of KafkaServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/otterizecrds/mysqlserverconfigs-customresourcedefinition.yaml b/src/operator/otterizecrds/mysqlserverconfigs-customresourcedefinition.yaml index 3d2128024..7c6d8067d 100644 --- a/src/operator/otterizecrds/mysqlserverconfigs-customresourcedefinition.yaml +++ b/src/operator/otterizecrds/mysqlserverconfigs-customresourcedefinition.yaml @@ -235,3 +235,72 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: MySQLServerConfig is the Schema for the mysqlserverconfig API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: MySQLServerConfigSpec defines the desired state of MySQLServerConfig + properties: + address: + type: string + credentials: + description: DatabaseCredentials defines the credentials to access the database + properties: + password: + description: Password is the plaintext password to access the database + type: string + secretRef: + description: SecretRef is a reference to a k8s secret storing the credentials + properties: + name: + description: Name is the name of he k8s secret storing the credentials + type: string + namespace: + description: |- + Namespace is the namespace in which the secret is stored. + If not provided, the operator will look for the secret in the same namespace as the database ServerConfig. + type: string + passwordKey: + default: password + description: PasswordKey is the key in the secret that stores the password + type: string + usernameKey: + default: username + description: UsernameKey is the key in the secret that stores the username + type: string + type: object + username: + description: Username is the plaintext username to access the database + type: string + type: object + required: + - address + - credentials + type: object + status: + description: MySQLServerConfigStatus defines the observed state of MySQLServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/otterizecrds/postgresqlserverconfigs-customresourcedefinition.yaml b/src/operator/otterizecrds/postgresqlserverconfigs-customresourcedefinition.yaml index b3b101982..50581d30c 100644 --- a/src/operator/otterizecrds/postgresqlserverconfigs-customresourcedefinition.yaml +++ b/src/operator/otterizecrds/postgresqlserverconfigs-customresourcedefinition.yaml @@ -235,3 +235,72 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: PostgreSQLServerConfig is the Schema for the databaseserverconfig API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PostgreSQLServerConfigSpec defines the desired state of PostgreSQLServerConfig + properties: + address: + type: string + credentials: + description: DatabaseCredentials defines the credentials to access the database + properties: + password: + description: Password is the plaintext password to access the database + type: string + secretRef: + description: SecretRef is a reference to a k8s secret storing the credentials + properties: + name: + description: Name is the name of he k8s secret storing the credentials + type: string + namespace: + description: |- + Namespace is the namespace in which the secret is stored. + If not provided, the operator will look for the secret in the same namespace as the database ServerConfig. + type: string + passwordKey: + default: password + description: PasswordKey is the key in the secret that stores the password + type: string + usernameKey: + default: username + description: UsernameKey is the key in the secret that stores the username + type: string + type: object + username: + description: Username is the plaintext username to access the database + type: string + type: object + required: + - address + - credentials + type: object + status: + description: PostgreSQLServerConfigStatus defines the observed state of PostgreSQLServerConfig + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/otterizecrds/protectedservices-customresourcedefinition.yaml b/src/operator/otterizecrds/protectedservices-customresourcedefinition.yaml index ebd3793f0..72b8b48f3 100644 --- a/src/operator/otterizecrds/protectedservices-customresourcedefinition.yaml +++ b/src/operator/otterizecrds/protectedservices-customresourcedefinition.yaml @@ -178,3 +178,41 @@ spec: storage: false subresources: status: {} + - name: v2beta1 + schema: + openAPIV3Schema: + description: ProtectedService is the Schema for the protectedservice API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ProtectedServiceSpec defines the desired state of ProtectedService + properties: + kind: + type: string + name: + type: string + type: object + status: + description: ProtectedServiceStatus defines the observed state of ProtectedService + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/src/operator/webhooks/clientintents_webhook_v2beta1.go b/src/operator/webhooks/clientintents_webhook_v2beta1.go new file mode 100644 index 000000000..8dee46a7f --- /dev/null +++ b/src/operator/webhooks/clientintents_webhook_v2beta1.go @@ -0,0 +1,466 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package webhooks + +import ( + "context" + "encoding/json" + "fmt" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" + "github.com/otterize/intents-operator/src/shared/errors" + "golang.org/x/net/idna" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + "net/netip" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "strings" +) + +type IntentsValidatorV2beta1 struct { + client.Client +} + +func (v *IntentsValidatorV2beta1) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&otterizev2beta1.ClientIntents{}). + WithValidator(v). + Complete() +} + +func NewIntentsValidatorV2beta1(c client.Client) *IntentsValidatorV2beta1 { + return &IntentsValidatorV2beta1{ + Client: c, + } +} + +//+kubebuilder:webhook:matchPolicy=Exact,path=/validate-k8s-otterize-com-v2beta1-clientintents,mutating=false,failurePolicy=fail,sideEffects=None,groups=k8s.otterize.com,resources=clientintents,verbs=create;update,versions=v2beta1,name=clientintentsv2beta1.kb.io,admissionReviewVersions=v1 + +var _ webhook.CustomValidator = &IntentsValidatorV2beta1{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (v *IntentsValidatorV2beta1) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + intentsObj := obj.(*otterizev2beta1.ClientIntents) + intentsList := &otterizev2beta1.ClientIntentsList{} + if err := v.List(ctx, intentsList, &client.ListOptions{Namespace: intentsObj.Namespace}); err != nil { + return nil, errors.Wrap(err) + } + if err := v.validateNoDuplicateClients(intentsObj, intentsList); err != nil { + allErrs = append(allErrs, err) + } + + if err := v.validateSpec(intentsObj); err != nil { + allErrs = append(allErrs, err) + } + + if len(allErrs) == 0 { + return nil, nil + } + + gvk := intentsObj.GroupVersionKind() + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + intentsObj.Name, allErrs) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (v *IntentsValidatorV2beta1) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + intentsObj := newObj.(*otterizev2beta1.ClientIntents) + intentsList := &otterizev2beta1.ClientIntentsList{} + if err := v.List(ctx, intentsList, &client.ListOptions{Namespace: intentsObj.Namespace}); err != nil { + return nil, errors.Wrap(err) + } + if err := v.validateNoDuplicateClients(intentsObj, intentsList); err != nil { + allErrs = append(allErrs, err) + } + + if err := v.validateSpec(intentsObj); err != nil { + allErrs = append(allErrs, err) + } + + if len(allErrs) == 0 { + return nil, nil + } + + gvk := intentsObj.GroupVersionKind() + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + intentsObj.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (v *IntentsValidatorV2beta1) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil +} + +func (v *IntentsValidatorV2beta1) validateNoDuplicateClients( + intentsObj *otterizev2beta1.ClientIntents, + intentsList *otterizev2beta1.ClientIntentsList) *field.Error { + + desiredClientName := intentsObj.GetWorkloadName() + for _, existingIntent := range intentsList.Items { + // Deny admission if intents already exist for this client, and it's not the same object being updated + if existingIntent.GetWorkloadName() == desiredClientName && existingIntent.Name != intentsObj.Name && existingIntent.GetClientKind() == intentsObj.GetClientKind() { + return &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: "name", + BadValue: desiredClientName, + Detail: fmt.Sprintf( + "Intents for client %s already exist in resource %s", desiredClientName, existingIntent.Name), + } + } + } + return nil +} + +// validate kubernetes kind +func (v *IntentsValidatorV2beta1) validateKubernetesKind(kind string) *field.Error { + if kind != "" && strings.ToUpper(string(kind[0])) != string(kind[0]) { + return &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "kind", + Detail: "Kubernetes Kinds must start with an uppercase letter", + } + } + return nil +} + +// validateSpec +func (v *IntentsValidatorV2beta1) validateSpec(intents *otterizev2beta1.ClientIntents) *field.Error { + // validate that if kind is specified, it starts with an uppercase letter + if err := v.validateKubernetesKind(intents.Spec.Workload.Kind); err != nil { + return err + } + if strings.Contains(intents.Spec.Workload.Name, ".") { + return &field.Error{ + Type: field.ErrorTypeForbidden, + Field: "workload.name", + Detail: "Workload name should not contain '.' character", + } + } + + for _, target := range intents.GetTargetList() { + err := v.validateIntentTarget(target) + if err != nil { + return err + } + } + return nil +} + +// Validate intent target +func (v *IntentsValidatorV2beta1) validateIntentTarget(intentTarget otterizev2beta1.Target) *field.Error { + err := v.validateOnlyOneTargetFieldSet(intentTarget) + if err != nil { + return err + } + if err := v.validateKubernetesTarget(intentTarget.Kubernetes); err != nil { + return err + } + + if err := v.validateServiceTarget(intentTarget.Service); err != nil { + return err + } + + if err := v.validateKafkaTarget(intentTarget.Kafka); err != nil { + return err + } + if err := v.validateSQLTarget(intentTarget.SQL); err != nil { + return err + } + + if err := v.validateAWSTarget(intentTarget.AWS); err != nil { + return err + } + + if err := v.validateGCPTarget(intentTarget.GCP); err != nil { + return err + } + + if err := v.validateAzureTarget(intentTarget.Azure); err != nil { + return err + } + + if err := v.validateInternetTarget(intentTarget.Internet); err != nil { + return err + } + + return nil +} + +// Validate that one and only one of the target's fields is set. We do it by marshal the target to json and check there's only one key +func (v *IntentsValidatorV2beta1) validateOnlyOneTargetFieldSet(target otterizev2beta1.Target) *field.Error { + // Marshal the target to json: + jsonString, err := json.Marshal(target) + if err != nil { + return &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "target", + Detail: "could not marshal target to json", + } + } + // Unmarshal the json to a map + var targetMap map[string]interface{} + err = json.Unmarshal(jsonString, &targetMap) + if err != nil { + return &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "target", + Detail: "could not unmarshal target json", + } + } + // Check that the map has only one key + if len(targetMap) != 1 { + return &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "target", + Detail: "each target must have exactly one field set", + } + } + return nil +} + +// validate KubernetesTarget +func (v *IntentsValidatorV2beta1) validateKubernetesTarget(kubernetesTarget *otterizev2beta1.KubernetesTarget) *field.Error { + if kubernetesTarget == nil { + return nil + } + if err := v.validateKubernetesKind(kubernetesTarget.Kind); err != nil { + return err + } + if kubernetesTarget.Name == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "name", + Detail: "invalid intent format, field name is required", + } + } + if strings.Count(kubernetesTarget.Name, ".") > 1 { + return &field.Error{ + Type: field.ErrorTypeForbidden, + Field: "Table", + Detail: "Target server name should not contain more than one '.' character", + } + } + + for _, httpTarget := range kubernetesTarget.HTTP { + if err := v.validateHTTP(httpTarget); err != nil { + return err + } + } + return nil +} + +// validate ServiceTarget +func (v *IntentsValidatorV2beta1) validateServiceTarget(serviceTarget *otterizev2beta1.ServiceTarget) *field.Error { + if serviceTarget == nil { + return nil + } + if serviceTarget.Name == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "name", + Detail: "invalid intent format, field name is required", + } + } + if strings.Count(serviceTarget.Name, ".") > 1 { + return &field.Error{ + Type: field.ErrorTypeForbidden, + Field: "Table", + Detail: "Target server name should not contain more than one '.' character", + } + } + for _, httpTarget := range serviceTarget.HTTP { + if err := v.validateHTTP(httpTarget); err != nil { + return err + } + } + return nil +} + +// validate HTTPTarget +func (v *IntentsValidatorV2beta1) validateHTTP(httpTarget otterizev2beta1.HTTPTarget) *field.Error { + if httpTarget.Path == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "path", + Detail: "path is required", + } + } + return nil +} + +// validate KafkaTarget +func (v *IntentsValidatorV2beta1) validateKafkaTarget(kafkaTarget *otterizev2beta1.KafkaTarget) *field.Error { + if kafkaTarget == nil { + return nil + } + if kafkaTarget.Name == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "name", + Detail: "invalid intent format, field name is required", + } + } + return nil +} + +// validate SQLTarget +func (v *IntentsValidatorV2beta1) validateSQLTarget(sqlTarget *otterizev2beta1.SQLTarget) *field.Error { + if sqlTarget == nil { + return nil + } + if sqlTarget.Name == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "name", + Detail: "invalid intent format, field name is required", + } + } + return nil + +} + +// validate AWSTarget +func (v *IntentsValidatorV2beta1) validateAWSTarget(awsTarget *otterizev2beta1.AWSTarget) *field.Error { + if awsTarget == nil { + return nil + } + if awsTarget.ARN == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "ARN", + Detail: "invalid intent format, field ARN is required", + } + } + return nil + +} + +// validate GCPTarget +func (v *IntentsValidatorV2beta1) validateGCPTarget(gcpTarget *otterizev2beta1.GCPTarget) *field.Error { + if gcpTarget == nil { + return nil + } + if gcpTarget.Resource == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "resource", + Detail: "invalid intent format, field resource is required", + } + } + return nil + +} + +// validate AzureTarget +func (v *IntentsValidatorV2beta1) validateAzureTarget(azureTarget *otterizev2beta1.AzureTarget) *field.Error { + if azureTarget == nil { + return nil + } + if azureTarget.Scope == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "scope", + 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 + +} + +// validate internet target +func (v *IntentsValidatorV2beta1) validateInternetTarget(internetTarget *otterizev2beta1.Internet) *field.Error { + if internetTarget == nil { + return nil + } + hasIPs := len(internetTarget.Ips) > 0 + hasDNS := len(internetTarget.Domains) > 0 + if !hasIPs && !hasDNS { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "ips", + Detail: fmt.Sprintf("invalid target format. type %s must contain ips or domanin names", otterizev2beta1.IntentTypeInternet), + } + } + if len(internetTarget.Ips) == 0 && len(internetTarget.Domains) == 0 { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "ips", + Detail: "ips or domains are required", + } + } + for _, dns := range internetTarget.Domains { + _, err := idna.Lookup.ToASCII(dns) + if err != nil && !strings.HasPrefix(dns, "*") { + return &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "domains", + Detail: "should be valid DNS name", + BadValue: dns, + } + } + } + for _, ip := range internetTarget.Ips { + if ip == "" { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "ips", + Detail: fmt.Sprintf("invalid target format. type %s must contain ips", otterizev2beta1.IntentTypeInternet), + } + } + var err error + if strings.Contains(ip, "/") { + _, err = netip.ParsePrefix(ip) + } else { + _, err = netip.ParseAddr(ip) + } + if err != nil { + return &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "ips", + Detail: "should be value IP address or CIDR", + BadValue: ip, + } + } + } + return nil +} diff --git a/src/operator/webhooks/database_server_conf_utils.go b/src/operator/webhooks/database_server_conf_utils.go index 1c0668735..cef786bb9 100644 --- a/src/operator/webhooks/database_server_conf_utils.go +++ b/src/operator/webhooks/database_server_conf_utils.go @@ -6,6 +6,7 @@ import ( otterizev1alpha3 "github.com/otterize/intents-operator/src/operator/api/v1alpha3" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/shared/errors" "github.com/samber/lo" "k8s.io/apimachinery/pkg/util/validation/field" @@ -123,6 +124,19 @@ func validateCredentialsNotEmpty(credentials otterizev2alpha1.DatabaseCredential return nil } +func validateCredentialsNotEmptyV2beta1(credentials otterizev2beta1.DatabaseCredentials) *field.Error { + if (credentials.Username == "" || credentials.Password == "") && credentials.SecretRef == nil { + return &field.Error{ + Type: field.ErrorTypeRequired, + Field: "credentials", + BadValue: credentials, + Detail: "Either username and password must be provided or a secretRef must be provided", + } + } + + return nil +} + func validateCredentialsNotEmptyV1alpha3(credentials otterizev1alpha3.DatabaseCredentials) *field.Error { if (credentials.Username == "" || credentials.Password == "") && credentials.SecretRef == nil { return &field.Error{ diff --git a/src/operator/webhooks/mysqlserverconfigs_webhook_v2beta1.go b/src/operator/webhooks/mysqlserverconfigs_webhook_v2beta1.go new file mode 100644 index 000000000..05974274f --- /dev/null +++ b/src/operator/webhooks/mysqlserverconfigs_webhook_v2beta1.go @@ -0,0 +1,114 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package webhooks + +import ( + "context" + goerrors "errors" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" + "github.com/otterize/intents-operator/src/shared/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +type MySQLConfValidatorV2beta1 struct { + client.Client +} + +func (v *MySQLConfValidatorV2beta1) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&otterizev2beta1.MySQLServerConfig{}). + WithValidator(v). + Complete() +} + +func NewMySQLConfValidatorV2beta1(c client.Client) *MySQLConfValidatorV2beta1 { + return &MySQLConfValidatorV2beta1{ + Client: c, + } +} + +//+kubebuilder:webhook:matchPolicy=Exact,path=/validate-k8s-otterize-com-v2beta1-mysqlserverconfig,mutating=false,failurePolicy=fail,sideEffects=None,groups=k8s.otterize.com,resources=mysqlserverconfigs,verbs=create;update,versions=v2beta1,name=mysqlserverconfigv2beta1.kb.io,admissionReviewVersions=v1 + +var _ webhook.CustomValidator = &MySQLConfValidatorV2beta1{} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (v *MySQLConfValidatorV2beta1) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (v *MySQLConfValidatorV2beta1) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + allErrs := field.ErrorList{} + mysqlServerConf := obj.(*otterizev2beta1.MySQLServerConfig) + gvk := mysqlServerConf.GroupVersionKind() + + if err := validateCredentialsNotEmptyV2beta1(mysqlServerConf.Spec.Credentials); err != nil { + allErrs = append(allErrs, err) + } + + err := validateNoDuplicateForCreate(ctx, v.Client, mysqlServerConf.Name) + if fieldErr := (&field.Error{}); goerrors.As(err, &fieldErr) { + allErrs = append(allErrs, fieldErr) + } else if err != nil { + return nil, errors.Wrap(err) + } + + if len(allErrs) > 0 { + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + mysqlServerConf.Name, allErrs) + } + + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (v *MySQLConfValidatorV2beta1) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + allErrs := field.ErrorList{} + mysqlServerConf := newObj.(*otterizev2beta1.MySQLServerConfig) + gvk := mysqlServerConf.GroupVersionKind() + + if err := validateCredentialsNotEmptyV2beta1(mysqlServerConf.Spec.Credentials); err != nil { + allErrs = append(allErrs, err) + } + + err := validateNoDuplicateForUpdate(ctx, v.Client, DatabaseServerConfig{ + Name: mysqlServerConf.Name, + Namespace: mysqlServerConf.Namespace, + Type: MySQL, + }) + if fieldErr := (&field.Error{}); goerrors.As(err, &fieldErr) { + allErrs = append(allErrs, fieldErr) + } else if err != nil { + return nil, errors.Wrap(err) + } + + if len(allErrs) > 0 { + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + mysqlServerConf.Name, allErrs) + } + + return nil, nil +} diff --git a/src/operator/webhooks/postgresqlserverconfigs_webhook_v2beta1.go b/src/operator/webhooks/postgresqlserverconfigs_webhook_v2beta1.go new file mode 100644 index 000000000..62d28d7af --- /dev/null +++ b/src/operator/webhooks/postgresqlserverconfigs_webhook_v2beta1.go @@ -0,0 +1,115 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package webhooks + +import ( + "context" + goerrors "errors" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" + "github.com/otterize/intents-operator/src/shared/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +type PostgresConfValidatorV2beta1 struct { + client.Client +} + +func (v *PostgresConfValidatorV2beta1) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&otterizev2beta1.PostgreSQLServerConfig{}). + WithValidator(v). + Complete() +} + +func NewPostgresConfValidatorV2beta1(c client.Client) *PostgresConfValidatorV2beta1 { + return &PostgresConfValidatorV2beta1{ + Client: c, + } +} + +//+kubebuilder:webhook:matchPolicy=Exact,path=/validate-k8s-otterize-com-v2beta1-postgresqlserverconfig,mutating=false,failurePolicy=fail,sideEffects=None,groups=k8s.otterize.com,resources=postgresqlserverconfigs,verbs=create;update,versions=v2beta1,name=postgresqlserverconfigv2beta1.kb.io,admissionReviewVersions=v1 + +var _ webhook.CustomValidator = &PostgresConfValidatorV2beta1{} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (v *PostgresConfValidatorV2beta1) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (v *PostgresConfValidatorV2beta1) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + allErrs := field.ErrorList{} + pgServerConf := obj.(*otterizev2beta1.PostgreSQLServerConfig) + gvk := pgServerConf.GroupVersionKind() + + if err := validateCredentialsNotEmptyV2beta1(pgServerConf.Spec.Credentials); err != nil { + allErrs = append(allErrs, err) + } + + err := validateNoDuplicateForCreate(ctx, v.Client, pgServerConf.Name) + if fieldErr := (&field.Error{}); goerrors.As(err, &fieldErr) { + allErrs = append(allErrs, fieldErr) + } else if err != nil { + return nil, errors.Wrap(err) + } + + if len(allErrs) > 0 { + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + pgServerConf.Name, allErrs) + } + + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (v *PostgresConfValidatorV2beta1) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + allErrs := field.ErrorList{} + pgServerConf := newObj.(*otterizev2beta1.PostgreSQLServerConfig) + gvk := pgServerConf.GroupVersionKind() + + if err := validateCredentialsNotEmptyV2beta1(pgServerConf.Spec.Credentials); err != nil { + allErrs = append(allErrs, err) + } + + err := validateNoDuplicateForUpdate(ctx, v.Client, DatabaseServerConfig{ + Name: pgServerConf.Name, + Namespace: pgServerConf.Namespace, + Type: Postgres, + }) + + if fieldErr := (&field.Error{}); goerrors.As(err, &fieldErr) { + allErrs = append(allErrs, fieldErr) + } else if err != nil { + return nil, errors.Wrap(err) + } + + if len(allErrs) > 0 { + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + pgServerConf.Name, allErrs) + } + + return nil, nil +} diff --git a/src/operator/webhooks/protectedservices_webhook_v2beta1.go b/src/operator/webhooks/protectedservices_webhook_v2beta1.go new file mode 100644 index 000000000..71edc26f9 --- /dev/null +++ b/src/operator/webhooks/protectedservices_webhook_v2beta1.go @@ -0,0 +1,154 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package webhooks + +import ( + "context" + "fmt" + "github.com/asaskevich/govalidator" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" + "github.com/otterize/intents-operator/src/shared/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "strings" +) + +type ProtectedServiceValidatorV2beta1 struct { + client.Client +} + +func (v *ProtectedServiceValidatorV2beta1) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&otterizev2beta1.ProtectedService{}). + WithValidator(v). + Complete() +} + +func NewProtectedServiceValidatorV2beta1(c client.Client) *ProtectedServiceValidatorV2beta1 { + return &ProtectedServiceValidatorV2beta1{ + Client: c, + } +} + +//+kubebuilder:webhook:matchPolicy=Exact,path=/validate-k8s-otterize-com-v2beta1-protectedservice,mutating=false,failurePolicy=fail,sideEffects=None,groups=k8s.otterize.com,resources=protectedservice,verbs=create;update,versions=v2beta1,name=protectedservicev2beta1.kb.io,admissionReviewVersions=v1 + +var _ webhook.CustomValidator = &ProtectedServiceValidatorV2beta1{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (v *ProtectedServiceValidatorV2beta1) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + protectedService := obj.(*otterizev2beta1.ProtectedService) + + protectedServicesList := &otterizev2beta1.ProtectedServiceList{} + if err := v.List(ctx, protectedServicesList, &client.ListOptions{Namespace: protectedService.Namespace}); err != nil { + return nil, errors.Wrap(err) + } + + if err := v.validateNoDuplicateClients(protectedService, protectedServicesList); err != nil { + allErrs = append(allErrs, err) + } + + if err := v.validateSpec(protectedService); err != nil { + allErrs = append(allErrs, err) + } + + if len(allErrs) == 0 { + return nil, nil + } + + gvk := protectedService.GroupVersionKind() + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + protectedService.Name, allErrs) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (v *ProtectedServiceValidatorV2beta1) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + var allErrs field.ErrorList + protectedService := newObj.(*otterizev2beta1.ProtectedService) + + protectedServicesList := &otterizev2beta1.ProtectedServiceList{} + if err := v.List(ctx, protectedServicesList, &client.ListOptions{Namespace: protectedService.Namespace}); err != nil { + return nil, errors.Wrap(err) + } + + if err := v.validateNoDuplicateClients(protectedService, protectedServicesList); err != nil { + allErrs = append(allErrs, err) + } + + if err := v.validateSpec(protectedService); err != nil { + allErrs = append(allErrs, err) + } + + if len(allErrs) == 0 { + return nil, nil + } + + gvk := protectedService.GroupVersionKind() + return nil, k8serrors.NewInvalid( + schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, + protectedService.Name, allErrs) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (v *ProtectedServiceValidatorV2beta1) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil +} + +func (v *ProtectedServiceValidatorV2beta1) validateNoDuplicateClients( + protectedService *otterizev2beta1.ProtectedService, protectedServicesList *otterizev2beta1.ProtectedServiceList) *field.Error { + + protectedServiceName := protectedService.Spec.Name + for _, protectedServiceFromList := range protectedServicesList.Items { + // Deny admission if intents already exist for this client, and it's not the same object being updated + if protectedServiceFromList.Spec.Name == protectedServiceName && protectedServiceFromList.Name != protectedService.Spec.Name { + return &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: "name", + BadValue: protectedServiceName, + Detail: fmt.Sprintf( + "Protected service for service %s already exist in resource %s", protectedServiceName, protectedServiceFromList.Name), + } + } + } + return nil +} + +// validateSpec +func (v *ProtectedServiceValidatorV2beta1) validateSpec(protectedService *otterizev2beta1.ProtectedService) *field.Error { + serviceName := strings.ReplaceAll(protectedService.Spec.Name, "-", "") + serviceName = strings.ReplaceAll(serviceName, "_", "") + // Validate Workload Name contains only lowercase betanumeric characters + // Workload name should be a valid RFC 1123 subdomain name + // It's a namespaced resource, we do not expect resources in other namespaces + if !govalidator.IsAlphanumeric(serviceName) || !govalidator.IsLowerCase(serviceName) { + message := fmt.Sprintf("Invalid Name: %s. Workload name must contain only lowercase betanumeric characters, '-' or '_'", protectedService.Spec.Name) + return &field.Error{ + Type: field.ErrorTypeForbidden, + Field: "Name", + Detail: message, + } + } + + return nil +} diff --git a/src/operator/webhooks/webhook_suite_test.go b/src/operator/webhooks/webhook_suite_test.go index 5640ff700..a94ea7b80 100644 --- a/src/operator/webhooks/webhook_suite_test.go +++ b/src/operator/webhooks/webhook_suite_test.go @@ -23,6 +23,7 @@ import ( otterizev1alpha3 "github.com/otterize/intents-operator/src/operator/api/v1alpha3" otterizev1beta1 "github.com/otterize/intents-operator/src/operator/api/v1beta1" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/shared/testbase" "github.com/sirupsen/logrus" "github.com/stretchr/testify/suite" @@ -51,6 +52,7 @@ type ValidationWebhookTestSuite struct { func (s *ValidationWebhookTestSuite) SetupSuite() { logrus.Info("Setting up test suite") + logrus.SetLevel(logrus.DebugLevel) s.TestEnv = &envtest.Environment{Scheme: scheme.Scheme} var err error s.TestEnv.CRDDirectoryPaths = []string{filepath.Join("..", "config", "crd")} @@ -65,6 +67,7 @@ func (s *ValidationWebhookTestSuite) SetupSuite() { utilruntime.Must(otterizev1alpha3.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev1beta1.AddToScheme(s.TestEnv.Scheme)) utilruntime.Must(otterizev2alpha1.AddToScheme(s.TestEnv.Scheme)) + utilruntime.Must(otterizev2beta1.AddToScheme(s.TestEnv.Scheme)) s.RestConfig, err = s.TestEnv.Start() s.Require().NoError(err) @@ -84,6 +87,8 @@ func (s *ValidationWebhookTestSuite) SetupTest() { s.Require().NoError((&otterizev1alpha3.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator13)) intentsValidator2 := NewIntentsValidatorV2alpha1(s.Mgr.GetClient()) s.Require().NoError((&otterizev2alpha1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2)) + intentsValidator2beta1 := NewIntentsValidatorV2beta1(s.Mgr.GetClient()) + s.Require().NoError((&otterizev2beta1.ClientIntents{}).SetupWebhookWithManager(s.Mgr, intentsValidator2beta1)) } func (s *ValidationWebhookTestSuite) BeforeTest(suiteName, testName string) { @@ -102,15 +107,15 @@ func (s *ValidationWebhookTestSuite) BeforeTest(suiteName, testName string) { // Test Only one Target Type Can be used in a target func (s *ValidationWebhookTestSuite) TestOnlyOneTargetTypeAllowed() { - _, err := s.AddIntentsv2alpha1("intents", "someclient", []otterizev2alpha1.Target{ + _, err := s.AddIntentsv2beta1("intents", "someclient", []otterizev2beta1.Target{ { - Kafka: &otterizev2alpha1.KafkaTarget{ - Topics: []otterizev2alpha1.KafkaTopic{{ + Kafka: &otterizev2beta1.KafkaTarget{ + Topics: []otterizev2beta1.KafkaTopic{{ Name: "sometopic", - Operations: []otterizev2alpha1.KafkaOperation{otterizev2alpha1.KafkaOperationConsume}, + Operations: []otterizev2beta1.KafkaOperation{otterizev2beta1.KafkaOperationConsume}, }}, }, - Internet: &otterizev2alpha1.Internet{ + Internet: &otterizev2beta1.Internet{ Ips: []string{"8.8.8.8"}, Ports: []int{80}, }, @@ -118,28 +123,28 @@ func (s *ValidationWebhookTestSuite) TestOnlyOneTargetTypeAllowed() { }) s.Require().ErrorContains(err, "each target must have exactly one field set") - _, err = s.AddIntentsv2alpha1("intents", "someclient", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("intents", "someclient", []otterizev2beta1.Target{ { - AWS: &otterizev2alpha1.AWSTarget{ + AWS: &otterizev2beta1.AWSTarget{ Actions: []string{"s3:GetObject"}, ARN: "arn:aws:s3:::mybucket/*", }, - Kubernetes: &otterizev2alpha1.KubernetesTarget{Name: "omriservice", Kind: "Deployment"}, + Kubernetes: &otterizev2beta1.KubernetesTarget{Name: "omriservice", Kind: "Deployment"}, }, }) s.Require().ErrorContains(err, "each target must have exactly one field set") - _, err = s.AddIntentsv2alpha1("intents", "someclient", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("intents", "someclient", []otterizev2beta1.Target{ { - Azure: &otterizev2alpha1.AzureTarget{ + Azure: &otterizev2beta1.AzureTarget{ Roles: []string{"Contributor"}, Scope: "subscriptions/1234-5678-9012-3456", }, - SQL: &otterizev2alpha1.SQLTarget{ - Privileges: []otterizev2alpha1.SQLPrivileges{{ + SQL: &otterizev2beta1.SQLTarget{ + Privileges: []otterizev2beta1.SQLPrivileges{{ DatabaseName: "sadfsdf", Table: "sometable", - Operations: []otterizev2alpha1.DatabaseOperation{otterizev2alpha1.DatabaseOperationSelect}, + Operations: []otterizev2beta1.DatabaseOperation{otterizev2beta1.DatabaseOperationSelect}, }}, }, }, @@ -148,10 +153,10 @@ func (s *ValidationWebhookTestSuite) TestOnlyOneTargetTypeAllowed() { } func (s *ValidationWebhookTestSuite) TestNoDuplicateClientsAllowed() { - _, err := s.AddIntentsv2alpha1("intents", "someclient", []otterizev2alpha1.Target{}) + _, err := s.AddIntentsv2beta1("intents", "someclient", []otterizev2beta1.Target{}) s.Require().NoError(err) - _, err = s.AddIntentsv2alpha1("intents2", "someclient", []otterizev2alpha1.Target{}) + _, err = s.AddIntentsv2beta1("intents2", "someclient", []otterizev2beta1.Target{}) s.Require().ErrorContains(err, "Intents for client someclient already exist in resource") } @@ -199,12 +204,12 @@ func (s *ValidationWebhookTestSuite) TestNoTopicsForHTTPIntentsAfterUpdate() { func (s *ValidationWebhookTestSuite) TestNameRequiredForEveryTypeExceptInternet() { missingNameFieldErr := "invalid intent format, field name is required" - _, err := s.AddIntentsv2alpha1("kafka-intents", "kafka-client", []otterizev2alpha1.Target{ + _, err := s.AddIntentsv2beta1("kafka-intents", "kafka-client", []otterizev2beta1.Target{ { - Kafka: &otterizev2alpha1.KafkaTarget{ - Topics: []otterizev2alpha1.KafkaTopic{{ + Kafka: &otterizev2beta1.KafkaTarget{ + Topics: []otterizev2beta1.KafkaTopic{{ Name: "sometopic", - Operations: []otterizev2alpha1.KafkaOperation{otterizev2alpha1.KafkaOperationConsume}, + Operations: []otterizev2beta1.KafkaOperation{otterizev2beta1.KafkaOperationConsume}, }}, }, }, @@ -212,12 +217,12 @@ func (s *ValidationWebhookTestSuite) TestNameRequiredForEveryTypeExceptInternet( logrus.Infof("Error: %v", err) s.Require().ErrorContains(err, missingNameFieldErr) - _, err = s.AddIntentsv2alpha1("http-intents", "http-client", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("http-intents", "http-client", []otterizev2beta1.Target{ { - Kubernetes: &otterizev2alpha1.KubernetesTarget{ - HTTP: []otterizev2alpha1.HTTPTarget{{ + Kubernetes: &otterizev2beta1.KubernetesTarget{ + HTTP: []otterizev2beta1.HTTPTarget{{ Path: "/somepath", - Methods: []otterizev2alpha1.HTTPMethod{otterizev2alpha1.HTTPMethodGet}, + Methods: []otterizev2beta1.HTTPMethod{otterizev2beta1.HTTPMethodGet}, }}, }, }, @@ -225,13 +230,13 @@ func (s *ValidationWebhookTestSuite) TestNameRequiredForEveryTypeExceptInternet( logrus.Infof("Error: %v", err) s.Require().ErrorContains(err, missingNameFieldErr) - _, err = s.AddIntentsv2alpha1("database-intents", "database-client", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("database-intents", "database-client", []otterizev2beta1.Target{ { - SQL: &otterizev2alpha1.SQLTarget{ - Privileges: []otterizev2alpha1.SQLPrivileges{{ + SQL: &otterizev2beta1.SQLTarget{ + Privileges: []otterizev2beta1.SQLPrivileges{{ DatabaseName: "sadfsdf", Table: "sometable", - Operations: []otterizev2alpha1.DatabaseOperation{otterizev2alpha1.DatabaseOperationSelect}, + Operations: []otterizev2beta1.DatabaseOperation{otterizev2beta1.DatabaseOperationSelect}, }}, }, }, @@ -239,9 +244,9 @@ func (s *ValidationWebhookTestSuite) TestNameRequiredForEveryTypeExceptInternet( logrus.Infof("Error: %v", err) s.Require().ErrorContains(err, missingNameFieldErr) - _, err = s.AddIntentsv2alpha1("aws-intents", "aws-client", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("aws-intents", "aws-client", []otterizev2beta1.Target{ { - AWS: &otterizev2alpha1.AWSTarget{ + AWS: &otterizev2beta1.AWSTarget{ Actions: []string{"s3:GetObject"}, }, }, @@ -249,9 +254,9 @@ func (s *ValidationWebhookTestSuite) TestNameRequiredForEveryTypeExceptInternet( logrus.Infof("Error: %v", err) s.Require().ErrorContains(err, strings.Replace(missingNameFieldErr, "name", "ARN", 1)) - _, err = s.AddIntentsv2alpha1("azure-intents", "aws-client", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("azure-intents", "aws-client", []otterizev2beta1.Target{ { - Azure: &otterizev2alpha1.AzureTarget{ + Azure: &otterizev2beta1.AzureTarget{ Roles: []string{"Contributor"}, }, }, @@ -259,9 +264,9 @@ func (s *ValidationWebhookTestSuite) TestNameRequiredForEveryTypeExceptInternet( logrus.Infof("Error: %v", err) s.Require().ErrorContains(err, strings.Replace(missingNameFieldErr, "name", "scope", 1)) - _, err = s.AddIntentsv2alpha1("gcp-intents", "aws-client", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("gcp-intents", "aws-client", []otterizev2beta1.Target{ { - GCP: &otterizev2alpha1.GCPTarget{ + GCP: &otterizev2beta1.GCPTarget{ Permissions: []string{"storage.objects.get"}, }, }, @@ -269,9 +274,9 @@ func (s *ValidationWebhookTestSuite) TestNameRequiredForEveryTypeExceptInternet( logrus.Infof("Error: %v", err) s.Require().ErrorContains(err, strings.Replace(missingNameFieldErr, "name", "resource", 1)) - _, err = s.AddIntentsv2alpha1("internet-intents", "internet-client", []otterizev2alpha1.Target{ + _, err = s.AddIntentsv2beta1("internet-intents", "internet-client", []otterizev2beta1.Target{ { - Internet: &otterizev2alpha1.Internet{ + Internet: &otterizev2beta1.Internet{ Ips: []string{"1.1.1.1"}, Ports: []int{80}, }, @@ -379,7 +384,7 @@ type ConversionWebhookTestSuite struct { suite.Suite } -// Validate the conversion between v1alpha3 and v2alpha1 works properly +// Validate the conversion between v1alpha3 and v2beta1 works properly func (s *ConversionWebhookTestSuite) TestConversionWebhookRegularIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -397,14 +402,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookRegularIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal @@ -417,7 +422,7 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookRegularIntents() { } } -// Validate the conversion between v1alpha3 and v2alpha1 works properly - kafka intents +// Validate the conversion between v1alpha3 and v2beta1 works properly - kafka intents func (s *ConversionWebhookTestSuite) TestConversionWebhookKafkaIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -446,14 +451,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookKafkaIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal @@ -471,7 +476,7 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookKafkaIntents() { } } -// Validate the conversion between v1alpha3 and v2alpha1 works properly - http intents +// Validate the conversion between v1alpha3 and v2beta1 works properly - http intents func (s *ConversionWebhookTestSuite) TestConversionWebhookHTTPIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -500,14 +505,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookHTTPIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal @@ -526,7 +531,7 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookHTTPIntents() { } } -// Validate the conversion between v1alpha3 and v2alpha1 works properly - database intents +// Validate the conversion between v1alpha3 and v2beta1 works properly - database intents func (s *ConversionWebhookTestSuite) TestConversionWebhookDatabaseIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -557,14 +562,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookDatabaseIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal @@ -584,7 +589,7 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookDatabaseIntents() { } } -// Validate the conversion between v1alpha3 and v2alpha1 works properly - aws intents +// Validate the conversion between v1alpha3 and v2beta1 works properly - aws intents func (s *ConversionWebhookTestSuite) TestConversionWebhookAWSIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -607,14 +612,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookAWSIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal @@ -628,7 +633,7 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookAWSIntents() { } } -// Validate the conversion between v1alpha3 and v2alpha1 works properly - azure intents +// Validate the conversion between v1alpha3 and v2beta1 works properly - azure intents func (s *ConversionWebhookTestSuite) TestConversionWebhookAzureIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -651,14 +656,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookAzureIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal @@ -672,7 +677,7 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookAzureIntents() { } } -// Validate the conversion between v1alpha3 and v2alpha1 works properly - gcp intents +// Validate the conversion between v1alpha3 and v2beta1 works properly - gcp intents func (s *ConversionWebhookTestSuite) TestConversionWebhookGCPIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -695,14 +700,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookGCPIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal @@ -716,7 +721,7 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookGCPIntents() { } } -// Validate the conversion between v1alpha3 and v2alpha1 works properly - internet intents +// Validate the conversion between v1alpha3 and v2beta1 works properly - internet intents func (s *ConversionWebhookTestSuite) TestConversionWebhookInternetIntents() { // Create a v1alpha3 object v1alpha3Intents := &otterizev1alpha3.ClientIntents{ @@ -744,14 +749,14 @@ func (s *ConversionWebhookTestSuite) TestConversionWebhookInternetIntents() { }, } - // Convert the v1alpha3 object to a v2alpha1 object - v2alpha1Obj := &otterizev2alpha1.ClientIntents{} - err := v1alpha3Intents.ConvertTo(v2alpha1Obj) + // Convert the v1alpha3 object to a v2beta1 object + v2beta1Obj := &otterizev2beta1.ClientIntents{} + err := v1alpha3Intents.ConvertTo(v2beta1Obj) s.Require().NoError(err) - // Convert the v2alpha1 object back to a v1alpha3 object + // Convert the v2beta1 object back to a v1alpha3 object anotherV1alpha3Intents := &otterizev1alpha3.ClientIntents{} - err = anotherV1alpha3Intents.ConvertFrom(v2alpha1Obj) + err = anotherV1alpha3Intents.ConvertFrom(v2beta1Obj) s.Require().NoError(err) // Check that the two v1alpha3 objects are equal diff --git a/src/shared/testbase/testsuitebase.go b/src/shared/testbase/testsuitebase.go index fa7bd994f..00e2c6295 100644 --- a/src/shared/testbase/testsuitebase.go +++ b/src/shared/testbase/testsuitebase.go @@ -6,6 +6,7 @@ import ( "github.com/google/uuid" otterizev1alpha2 "github.com/otterize/intents-operator/src/operator/api/v1alpha2" otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1" + otterizev2beta1 "github.com/otterize/intents-operator/src/operator/api/v2beta1" "github.com/otterize/intents-operator/src/operator/controllers/intents_reconcilers/protected_services" "github.com/otterize/intents-operator/src/shared/errors" "github.com/samber/lo" @@ -506,6 +507,13 @@ func (s *ControllerManagerTestSuiteBase) AddIntentsv2alpha1( return s.AddIntentsInNamespacev2alpha1(objName, clientName, s.TestNamespace, callList) } +func (s *ControllerManagerTestSuiteBase) AddIntentsv2beta1( + objName, + clientName string, + callList []otterizev2beta1.Target) (*otterizev2beta1.ClientIntents, error) { + return s.AddIntentsInNamespaceV2beta1(objName, clientName, s.TestNamespace, callList) +} + func (s *ControllerManagerTestSuiteBase) AddIntentsInNamespaceV1alpha2( objName, clientName string, @@ -563,6 +571,35 @@ func (s *ControllerManagerTestSuiteBase) AddIntentsInNamespacev2alpha1( return intents, nil } +func (s *ControllerManagerTestSuiteBase) AddIntentsInNamespaceV2beta1( + objName, + clientName string, + namespace string, + callList []otterizev2beta1.Target) (*otterizev2beta1.ClientIntents, error) { + + intents := &otterizev2beta1.ClientIntents{ + ObjectMeta: metav1.ObjectMeta{ + Name: objName, + Namespace: namespace, + Finalizers: []string{ + // Dummy finalizer so the object won't actually be deleted just marked as deleted + "dummy-finalizer", + }, + }, + Spec: &otterizev2beta1.IntentsSpec{ + Workload: otterizev2beta1.Workload{Name: clientName}, + Targets: callList, + }, + } + err := s.Mgr.GetClient().Create(context.Background(), intents) + if err != nil { + return nil, errors.Wrap(err) + } + s.WaitForObjectToBeCreated(intents) + + return intents, nil +} + func (s *ControllerManagerTestSuiteBase) UpdateIntents( objName string, callList []otterizev2alpha1.Target) error {