From 662ecf16f83a865e54960803e25bb80ad96ce8e0 Mon Sep 17 00:00:00 2001 From: Murad Biashimov Date: Thu, 2 May 2024 09:23:12 +0200 Subject: [PATCH] feat(serviceintegrationendpoint): add kind (#721) --- CHANGELOG.md | 1 + PROJECT | 12 + api/v1alpha1/serviceintegration_types.go | 12 +- .../serviceintegrationendpoint_types.go | 167 +++++ .../serviceintegrationendpoint_webhook.go | 57 ++ api/v1alpha1/setup_webhooks.go | 3 + api/v1alpha1/zz_generated.deepcopy.go | 175 +++++ .../aiven.io_serviceintegrationendpoints.yaml | 654 ++++++++++++++++++ .../templates/cluster_role.yaml | 28 + .../mutating_webhook_configuration.yaml | 20 + .../validating_webhook_configuration.yaml | 20 + .../aiven.io_serviceintegrationendpoints.yaml | 654 ++++++++++++++++++ config/crd/kustomization.yaml | 3 + ...ection_in_serviceintegrationendpoints.yaml | 7 + ...ebhook_in_serviceintegrationendpoints.yaml | 16 + config/rbac/role.yaml | 28 + ...erviceintegrationendpoint_editor_role.yaml | 31 + ...erviceintegrationendpoint_viewer_role.yaml | 27 + .../_v1alpha1_serviceintegrationendpoint.yaml | 12 + config/samples/kustomization.yaml | 1 + config/webhook/manifests.yaml | 40 ++ controllers/serviceintegration_controller.go | 5 +- .../serviceintegrationendpoint_controller.go | 178 +++++ controllers/setup.go | 1 + .../serviceintegrationendpoint.md | 275 ++++++++ go.mod | 1 + go.sum | 8 +- tests/serviceintegrationendpoint_test.go | 112 +++ 28 files changed, 2540 insertions(+), 8 deletions(-) create mode 100644 api/v1alpha1/serviceintegrationendpoint_types.go create mode 100644 api/v1alpha1/serviceintegrationendpoint_webhook.go create mode 100644 charts/aiven-operator-crds/templates/aiven.io_serviceintegrationendpoints.yaml create mode 100644 config/crd/bases/aiven.io_serviceintegrationendpoints.yaml create mode 100644 config/crd/patches/cainjection_in_serviceintegrationendpoints.yaml create mode 100644 config/crd/patches/webhook_in_serviceintegrationendpoints.yaml create mode 100644 config/rbac/serviceintegrationendpoint_editor_role.yaml create mode 100644 config/rbac/serviceintegrationendpoint_viewer_role.yaml create mode 100644 config/samples/_v1alpha1_serviceintegrationendpoint.yaml create mode 100644 controllers/serviceintegrationendpoint_controller.go create mode 100644 docs/docs/api-reference/serviceintegrationendpoint.md create mode 100644 tests/serviceintegrationendpoint_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index b21730c3..55498e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [MAJOR.MINOR.PATCH] - YYYY-MM-DD +- Add kind: `ServiceIntegrationEndpoint` - Change `Kafka` field `userConfig.kafka_version`: enum ~~`[3.4, 3.5, 3.6]`~~ → `[3.4, 3.5, 3.6, 3.7]` ## v0.19.0 - 2024-04-18 diff --git a/PROJECT b/PROJECT index 99df9613..5e3df5d6 100644 --- a/PROJECT +++ b/PROJECT @@ -275,4 +275,16 @@ resources: kind: ClickhouseRole path: github.com/aiven/aiven-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: aiven.io + kind: ServiceIntegrationEndpoint + path: github.com/aiven/aiven-operator/api/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 version: "3" diff --git a/api/v1alpha1/serviceintegration_types.go b/api/v1alpha1/serviceintegration_types.go index fa8ec3a0..9a26dd5d 100644 --- a/api/v1alpha1/serviceintegration_types.go +++ b/api/v1alpha1/serviceintegration_types.go @@ -127,8 +127,8 @@ func (in *ServiceIntegration) Conditions() *[]metav1.Condition { return &in.Status.Conditions } -func (in *ServiceIntegration) GetUserConfig() (any, error) { - configs := map[string]any{ +func (in *ServiceIntegration) getUserConfigFields() map[string]any { + return map[string]any{ "clickhouse_kafka": in.Spec.ClickhouseKafkaUserConfig, "clickhouse_postgresql": in.Spec.ClickhousePostgreSQLUserConfig, "datadog": in.Spec.DatadogUserConfig, @@ -139,7 +139,15 @@ func (in *ServiceIntegration) GetUserConfig() (any, error) { "logs": in.Spec.LogsUserConfig, "metrics": in.Spec.MetricsUserConfig, } +} +func (in *ServiceIntegration) HasUserConfig() bool { + _, ok := in.getUserConfigFields()[in.Spec.IntegrationType] + return ok +} + +func (in *ServiceIntegration) GetUserConfig() (any, error) { + configs := in.getUserConfigFields() thisType := in.Spec.IntegrationType // Checks if it is the only configuration set diff --git a/api/v1alpha1/serviceintegrationendpoint_types.go b/api/v1alpha1/serviceintegrationendpoint_types.go new file mode 100644 index 00000000..531e662b --- /dev/null +++ b/api/v1alpha1/serviceintegrationendpoint_types.go @@ -0,0 +1,167 @@ +// Copyright (c) 2024 Aiven, Helsinki, Finland. https://aiven.io/ + +package v1alpha1 + +import ( + "fmt" + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + datadoguserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/datadog" + externalawscloudwatchlogsuserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_aws_cloudwatch_logs" + externalawscloudwatchmetricsuserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_aws_cloudwatch_metrics" + externalelasticsearchlogsuserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_elasticsearch_logs" + externalgooglecloudbigqueryuserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_google_cloud_bigquery" + externalgooglecloudlogginguserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_google_cloud_logging" + externalkafkauserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_kafka" + externalopensearchlogsuserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_opensearch_logs" + externalpostgresqluserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_postgresql" + externalschemaregistryuserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_schema_registry" + jolokiauserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/jolokia" + prometheususerconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/prometheus" + rsysloguserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/rsyslog" +) + +// ServiceIntegrationEndpointSpec defines the desired state of ServiceIntegrationEndpoint +type ServiceIntegrationEndpointSpec struct { + ProjectDependant `json:",inline"` + + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:Enum=autoscaler;datadog;external_aws_cloudwatch_logs;external_aws_cloudwatch_metrics;external_aws_s3;external_clickhouse;external_elasticsearch_logs;external_google_cloud_bigquery;external_google_cloud_logging;external_kafka;external_mysql;external_opensearch_logs;external_postgresql;external_redis;external_schema_registry;jolokia;prometheus;rsyslog + // Type of the service integration endpoint + EndpointType string `json:"endpointType"` + + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:MaxLength=36 + // Source endpoint for the integration (if any) + EndpointName string `json:"endpointName,omitempty"` + + // Datadog configuration values + Datadog *datadoguserconfig.DatadogUserConfig `json:"datadog,omitempty"` + + // ExternalAwsCloudwatchLogs configuration values + ExternalAwsCloudwatchLogs *externalawscloudwatchlogsuserconfig.ExternalAwsCloudwatchLogsUserConfig `json:"externalAWSCloudwatchLogs,omitempty"` + + // ExternalAwsCloudwatchMetrics configuration values + ExternalAwsCloudwatchMetrics *externalawscloudwatchmetricsuserconfig.ExternalAwsCloudwatchMetricsUserConfig `json:"externalAWSCloudwatchMetrics,omitempty"` + + // ExternalElasticsearchLogs configuration values + ExternalElasticsearchLogs *externalelasticsearchlogsuserconfig.ExternalElasticsearchLogsUserConfig `json:"externalElasticsearchLogs,omitempty"` + + // ExternalGoogleCloudBigquery configuration values + ExternalGoogleCloudBigquery *externalgooglecloudbigqueryuserconfig.ExternalGoogleCloudBigqueryUserConfig `json:"externalGoogleCloudBigquery,omitempty"` + + // ExternalGoogleCloudLogging configuration values + ExternalGoogleCloudLogging *externalgooglecloudlogginguserconfig.ExternalGoogleCloudLoggingUserConfig `json:"externalGoogleCloudLogging,omitempty"` + + // ExternalKafka configuration values + ExternalKafka *externalkafkauserconfig.ExternalKafkaUserConfig `json:"externalKafka,omitempty"` + + // ExternalOpensearchLogs configuration values + ExternalOpensearchLogs *externalopensearchlogsuserconfig.ExternalOpensearchLogsUserConfig `json:"externalOpensearchLogs,omitempty"` + + // ExternalPostgresql configuration values + ExternalPostgresql *externalpostgresqluserconfig.ExternalPostgresqlUserConfig `json:"externalPostgresql,omitempty"` + + // ExternalSchemaRegistry configuration values + ExternalSchemaRegistry *externalschemaregistryuserconfig.ExternalSchemaRegistryUserConfig `json:"externalSchemaRegistry,omitempty"` + + // Jolokia configuration values + Jolokia *jolokiauserconfig.JolokiaUserConfig `json:"jolokia,omitempty"` + + // Prometheus configuration values + Prometheus *prometheususerconfig.PrometheusUserConfig `json:"prometheus,omitempty"` + + // Rsyslog configuration values + Rsyslog *rsysloguserconfig.RsyslogUserConfig `json:"rsyslog,omitempty"` +} + +// ServiceIntegrationEndpointStatus defines the observed state of ServiceIntegrationEndpoint +type ServiceIntegrationEndpointStatus struct { + // Conditions represent the latest available observations of an ServiceIntegrationEndpoint state + Conditions []metav1.Condition `json:"conditions"` + + // Service integration ID + ID string `json:"id"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// ServiceIntegrationEndpoint is the Schema for the serviceintegrationendpoints API +// +kubebuilder:printcolumn:name="Project",type="string",JSONPath=".spec.project" +// +kubebuilder:printcolumn:name="Endpoint Name",type="string",JSONPath=".spec.endpointName" +// +kubebuilder:printcolumn:name="Endpoint Type",type="string",JSONPath=".spec.endpointType" +// +kubebuilder:printcolumn:name="ID",type="string",JSONPath=".status.id" +type ServiceIntegrationEndpoint struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServiceIntegrationEndpointSpec `json:"spec,omitempty"` + Status ServiceIntegrationEndpointStatus `json:"status,omitempty"` +} + +var _ AivenManagedObject = &ServiceIntegrationEndpoint{} + +func (*ServiceIntegrationEndpoint) NoSecret() bool { + return true +} + +func (in *ServiceIntegrationEndpoint) AuthSecretRef() *AuthSecretReference { + return in.Spec.AuthSecretRef +} + +func (in *ServiceIntegrationEndpoint) Conditions() *[]metav1.Condition { + return &in.Status.Conditions +} + +func (in *ServiceIntegrationEndpoint) getUserConfigFields() map[string]any { + return map[string]any{ + "datadog": in.Spec.Datadog, + "external_aws_cloudwatch_logs": in.Spec.ExternalAwsCloudwatchLogs, + "external_aws_cloudwatch_metrics": in.Spec.ExternalAwsCloudwatchMetrics, + "external_elasticsearch_logs": in.Spec.ExternalElasticsearchLogs, + "external_google_cloud_bigquery": in.Spec.ExternalGoogleCloudBigquery, + "external_google_cloud_logging": in.Spec.ExternalGoogleCloudLogging, + "external_kafka": in.Spec.ExternalKafka, + "external_opensearch_logs": in.Spec.ExternalOpensearchLogs, + "external_postgresql": in.Spec.ExternalPostgresql, + "external_schema_registry": in.Spec.ExternalSchemaRegistry, + "jolokia": in.Spec.Jolokia, + "prometheus": in.Spec.Prometheus, + "rsyslog": in.Spec.Rsyslog, + } +} + +func (in *ServiceIntegrationEndpoint) HasUserConfig() bool { + _, ok := in.getUserConfigFields()[in.Spec.EndpointType] + return ok +} + +func (in *ServiceIntegrationEndpoint) GetUserConfig() (any, error) { + configs := in.getUserConfigFields() + thisType := in.Spec.EndpointType + + // Checks if it is the only configuration set + for k, v := range configs { + if k != thisType && !reflect.ValueOf(v).IsNil() { + return nil, fmt.Errorf("got additional configuration for integration endpoint type %q", k) + } + } + + return configs[thisType], nil +} + +// +kubebuilder:object:root=true + +// ServiceIntegrationEndpointList contains a list of ServiceIntegrationEndpoint +type ServiceIntegrationEndpointList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceIntegrationEndpoint `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ServiceIntegrationEndpoint{}, &ServiceIntegrationEndpointList{}) +} diff --git a/api/v1alpha1/serviceintegrationendpoint_webhook.go b/api/v1alpha1/serviceintegrationendpoint_webhook.go new file mode 100644 index 00000000..84fc4a86 --- /dev/null +++ b/api/v1alpha1/serviceintegrationendpoint_webhook.go @@ -0,0 +1,57 @@ +// Copyright (c) 2024 Aiven, Helsinki, Finland. https://aiven.io/ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// log is for logging in this package. +var serviceintegrationendpointlog = logf.Log.WithName("serviceintegrationendpoint-resource") + +func (in *ServiceIntegrationEndpoint) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(in). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-aiven-io-v1alpha1-serviceintegrationendpoint,mutating=true,failurePolicy=fail,groups=aiven.io,resources=serviceintegrationendpoints,verbs=create;update,versions=v1alpha1,name=mserviceintegrationendpoint.kb.io,sideEffects=none,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &ServiceIntegrationEndpoint{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (in *ServiceIntegrationEndpoint) Default() { + serviceintegrationendpointlog.Info("default", "name", in.Name) +} + +//+kubebuilder:webhook:verbs=create;update,path=/validate-aiven-io-v1alpha1-serviceintegrationendpoint,mutating=false,failurePolicy=fail,groups=aiven.io,resources=serviceintegrationendpoints,versions=v1alpha1,name=vserviceintegrationendpoint.kb.io,sideEffects=none,admissionReviewVersions=v1 + +var _ webhook.Validator = &ServiceIntegrationEndpoint{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (in *ServiceIntegrationEndpoint) ValidateCreate() error { + serviceintegrationendpointlog.Info("validate create", "name", in.Name) + + // We need the validation here only + _, err := in.GetUserConfig() + return err +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (in *ServiceIntegrationEndpoint) ValidateUpdate(old runtime.Object) error { + serviceintegrationendpointlog.Info("validate update", "name", in.Name) + + // We need the validation here only + _, err := in.GetUserConfig() + return err +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (in *ServiceIntegrationEndpoint) ValidateDelete() error { + serviceintegrationendpointlog.Info("validate delete", "name", in.Name) + + return nil +} diff --git a/api/v1alpha1/setup_webhooks.go b/api/v1alpha1/setup_webhooks.go index 9bdaf547..2e6d7bf5 100644 --- a/api/v1alpha1/setup_webhooks.go +++ b/api/v1alpha1/setup_webhooks.go @@ -40,6 +40,9 @@ func SetupWebhooks(mgr ctrl.Manager) error { if err := (&ServiceIntegration{}).SetupWebhookWithManager(mgr); err != nil { return fmt.Errorf("webhook ServiceIntegration: %w", err) } + if err := (&ServiceIntegrationEndpoint{}).SetupWebhookWithManager(mgr); err != nil { + return fmt.Errorf("webhook ServiceIntegrationEndpoint: %w", err) + } if err := (&KafkaConnector{}).SetupWebhookWithManager(mgr); err != nil { return fmt.Errorf("webhook KafkaConnector: %w", err) } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1dfb2f86..9a486f67 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -20,6 +20,19 @@ import ( kafka_mirrormaker "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integration/kafka_mirrormaker" logs "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integration/logs" metrics "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integration/metrics" + integrationendpointsdatadog "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/datadog" + external_aws_cloudwatch_logs "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_aws_cloudwatch_logs" + integrationendpointsexternal_aws_cloudwatch_metrics "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_aws_cloudwatch_metrics" + external_elasticsearch_logs "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_elasticsearch_logs" + external_google_cloud_bigquery "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_google_cloud_bigquery" + external_google_cloud_logging "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_google_cloud_logging" + external_kafka "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_kafka" + external_opensearch_logs "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_opensearch_logs" + external_postgresql "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_postgresql" + external_schema_registry "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/external_schema_registry" + jolokia "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/jolokia" + prometheus "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/prometheus" + rsyslog "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/integrationendpoints/rsyslog" cassandra "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/service/cassandra" clickhouse "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/service/clickhouse" grafana "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/service/grafana" @@ -2409,6 +2422,168 @@ func (in *ServiceIntegration) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceIntegrationEndpoint) DeepCopyInto(out *ServiceIntegrationEndpoint) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceIntegrationEndpoint. +func (in *ServiceIntegrationEndpoint) DeepCopy() *ServiceIntegrationEndpoint { + if in == nil { + return nil + } + out := new(ServiceIntegrationEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceIntegrationEndpoint) 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 *ServiceIntegrationEndpointList) DeepCopyInto(out *ServiceIntegrationEndpointList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceIntegrationEndpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceIntegrationEndpointList. +func (in *ServiceIntegrationEndpointList) DeepCopy() *ServiceIntegrationEndpointList { + if in == nil { + return nil + } + out := new(ServiceIntegrationEndpointList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceIntegrationEndpointList) 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 *ServiceIntegrationEndpointSpec) DeepCopyInto(out *ServiceIntegrationEndpointSpec) { + *out = *in + in.ProjectDependant.DeepCopyInto(&out.ProjectDependant) + if in.Datadog != nil { + in, out := &in.Datadog, &out.Datadog + *out = new(integrationendpointsdatadog.DatadogUserConfig) + (*in).DeepCopyInto(*out) + } + if in.ExternalAwsCloudwatchLogs != nil { + in, out := &in.ExternalAwsCloudwatchLogs, &out.ExternalAwsCloudwatchLogs + *out = new(external_aws_cloudwatch_logs.ExternalAwsCloudwatchLogsUserConfig) + (*in).DeepCopyInto(*out) + } + if in.ExternalAwsCloudwatchMetrics != nil { + in, out := &in.ExternalAwsCloudwatchMetrics, &out.ExternalAwsCloudwatchMetrics + *out = new(integrationendpointsexternal_aws_cloudwatch_metrics.ExternalAwsCloudwatchMetricsUserConfig) + **out = **in + } + if in.ExternalElasticsearchLogs != nil { + in, out := &in.ExternalElasticsearchLogs, &out.ExternalElasticsearchLogs + *out = new(external_elasticsearch_logs.ExternalElasticsearchLogsUserConfig) + (*in).DeepCopyInto(*out) + } + if in.ExternalGoogleCloudBigquery != nil { + in, out := &in.ExternalGoogleCloudBigquery, &out.ExternalGoogleCloudBigquery + *out = new(external_google_cloud_bigquery.ExternalGoogleCloudBigqueryUserConfig) + **out = **in + } + if in.ExternalGoogleCloudLogging != nil { + in, out := &in.ExternalGoogleCloudLogging, &out.ExternalGoogleCloudLogging + *out = new(external_google_cloud_logging.ExternalGoogleCloudLoggingUserConfig) + **out = **in + } + if in.ExternalKafka != nil { + in, out := &in.ExternalKafka, &out.ExternalKafka + *out = new(external_kafka.ExternalKafkaUserConfig) + (*in).DeepCopyInto(*out) + } + if in.ExternalOpensearchLogs != nil { + in, out := &in.ExternalOpensearchLogs, &out.ExternalOpensearchLogs + *out = new(external_opensearch_logs.ExternalOpensearchLogsUserConfig) + (*in).DeepCopyInto(*out) + } + if in.ExternalPostgresql != nil { + in, out := &in.ExternalPostgresql, &out.ExternalPostgresql + *out = new(external_postgresql.ExternalPostgresqlUserConfig) + (*in).DeepCopyInto(*out) + } + if in.ExternalSchemaRegistry != nil { + in, out := &in.ExternalSchemaRegistry, &out.ExternalSchemaRegistry + *out = new(external_schema_registry.ExternalSchemaRegistryUserConfig) + (*in).DeepCopyInto(*out) + } + if in.Jolokia != nil { + in, out := &in.Jolokia, &out.Jolokia + *out = new(jolokia.JolokiaUserConfig) + (*in).DeepCopyInto(*out) + } + if in.Prometheus != nil { + in, out := &in.Prometheus, &out.Prometheus + *out = new(prometheus.PrometheusUserConfig) + (*in).DeepCopyInto(*out) + } + if in.Rsyslog != nil { + in, out := &in.Rsyslog, &out.Rsyslog + *out = new(rsyslog.RsyslogUserConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceIntegrationEndpointSpec. +func (in *ServiceIntegrationEndpointSpec) DeepCopy() *ServiceIntegrationEndpointSpec { + if in == nil { + return nil + } + out := new(ServiceIntegrationEndpointSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceIntegrationEndpointStatus) DeepCopyInto(out *ServiceIntegrationEndpointStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceIntegrationEndpointStatus. +func (in *ServiceIntegrationEndpointStatus) DeepCopy() *ServiceIntegrationEndpointStatus { + if in == nil { + return nil + } + out := new(ServiceIntegrationEndpointStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceIntegrationItem) DeepCopyInto(out *ServiceIntegrationItem) { *out = *in diff --git a/charts/aiven-operator-crds/templates/aiven.io_serviceintegrationendpoints.yaml b/charts/aiven-operator-crds/templates/aiven.io_serviceintegrationendpoints.yaml new file mode 100644 index 00000000..9e33b7e9 --- /dev/null +++ b/charts/aiven-operator-crds/templates/aiven.io_serviceintegrationendpoints.yaml @@ -0,0 +1,654 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: serviceintegrationendpoints.aiven.io +spec: + group: aiven.io + names: + kind: ServiceIntegrationEndpoint + listKind: ServiceIntegrationEndpointList + plural: serviceintegrationendpoints + singular: serviceintegrationendpoint + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.project + name: Project + type: string + - jsonPath: .spec.endpointName + name: Endpoint Name + type: string + - jsonPath: .spec.endpointType + name: Endpoint Type + type: string + - jsonPath: .status.id + name: ID + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: + ServiceIntegrationEndpoint is the Schema for the serviceintegrationendpoints + 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: + ServiceIntegrationEndpointSpec defines the desired state + of ServiceIntegrationEndpoint + properties: + authSecretRef: + description: Authentication reference to Aiven token in a secret + properties: + key: + minLength: 1 + type: string + name: + minLength: 1 + type: string + required: + - key + - name + type: object + datadog: + description: Datadog configuration values + properties: + datadog_api_key: + description: Datadog API key + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9]{1,256}$ + type: string + datadog_tags: + description: Custom tags provided by user + items: + description: Datadog tag defined by user + properties: + comment: + description: Optional tag explanation + maxLength: 1024 + type: string + tag: + description: + "Tag format and usage are described here: https://docs.datadoghq.com/getting_started/tagging. + Tags with prefix 'aiven-' are reserved for Aiven." + maxLength: 200 + minLength: 1 + type: string + required: + - tag + type: object + maxItems: 32 + type: array + disable_consumer_stats: + description: Disable consumer group metrics + type: boolean + kafka_consumer_check_instances: + description: + Number of separate instances to fetch kafka consumer + statistics with + maximum: 100 + minimum: 1 + type: integer + kafka_consumer_stats_timeout: + description: + Number of seconds that datadog will wait to get consumer + statistics from brokers + maximum: 300 + minimum: 2 + type: integer + max_partition_contexts: + description: Maximum number of partition contexts to send + maximum: 200000 + minimum: 200 + type: integer + site: + description: Datadog intake site. Defaults to datadoghq.com + enum: + - datadoghq.com + - datadoghq.eu + - us3.datadoghq.com + - us5.datadoghq.com + - ddog-gov.com + type: string + required: + - datadog_api_key + type: object + endpointName: + description: Source endpoint for the integration (if any) + maxLength: 36 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + endpointType: + description: Type of the service integration endpoint + enum: + - autoscaler + - datadog + - external_aws_cloudwatch_logs + - external_aws_cloudwatch_metrics + - external_aws_s3 + - external_clickhouse + - external_elasticsearch_logs + - external_google_cloud_bigquery + - external_google_cloud_logging + - external_kafka + - external_mysql + - external_opensearch_logs + - external_postgresql + - external_redis + - external_schema_registry + - jolokia + - prometheus + - rsyslog + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + externalAWSCloudwatchLogs: + description: ExternalAwsCloudwatchLogs configuration values + properties: + access_key: + description: + AWS access key. Required permissions are logs:CreateLogGroup, + logs:CreateLogStream, logs:PutLogEvents and logs:DescribeLogStreams + maxLength: 4096 + type: string + log_group_name: + description: AWS CloudWatch log group name + maxLength: 512 + minLength: 1 + pattern: ^[\.\-_/#A-Za-z0-9]+$ + type: string + region: + description: AWS region + maxLength: 32 + type: string + secret_key: + description: AWS secret key + maxLength: 4096 + type: string + required: + - access_key + - region + - secret_key + type: object + externalAWSCloudwatchMetrics: + description: ExternalAwsCloudwatchMetrics configuration values + properties: + access_key: + description: AWS access key. Required permissions are cloudwatch:PutMetricData + maxLength: 4096 + type: string + namespace: + description: AWS CloudWatch Metrics Namespace + maxLength: 255 + minLength: 1 + type: string + region: + description: AWS region + maxLength: 32 + type: string + secret_key: + description: AWS secret key + maxLength: 4096 + type: string + required: + - access_key + - namespace + - region + - secret_key + type: object + externalElasticsearchLogs: + description: ExternalElasticsearchLogs configuration values + properties: + ca: + description: PEM encoded CA certificate + maxLength: 16384 + type: string + index_days_max: + description: Maximum number of days of logs to keep + maximum: 10000 + minimum: 1 + type: integer + index_prefix: + description: Elasticsearch index prefix + maxLength: 1000 + minLength: 1 + pattern: ^[a-z0-9][a-z0-9-_.]+$ + type: string + timeout: + description: Elasticsearch request timeout limit + maximum: 120 + minimum: 10 + type: number + url: + description: Elasticsearch connection URL + maxLength: 2048 + minLength: 12 + type: string + required: + - index_prefix + - url + type: object + externalGoogleCloudBigquery: + description: ExternalGoogleCloudBigquery configuration values + properties: + project_id: + description: GCP project id. + maxLength: 30 + minLength: 6 + type: string + service_account_credentials: + description: + This is a JSON object with the fields documented + in https://cloud.google.com/iam/docs/creating-managing-service-account-keys + . + maxLength: 4096 + type: string + required: + - project_id + - service_account_credentials + type: object + externalGoogleCloudLogging: + description: ExternalGoogleCloudLogging configuration values + properties: + log_id: + description: Google Cloud Logging log id + maxLength: 512 + type: string + project_id: + description: GCP project id. + maxLength: 30 + minLength: 6 + type: string + service_account_credentials: + description: + This is a JSON object with the fields documented + in https://cloud.google.com/iam/docs/creating-managing-service-account-keys + . + maxLength: 4096 + type: string + required: + - log_id + - project_id + - service_account_credentials + type: object + externalKafka: + description: ExternalKafka configuration values + properties: + bootstrap_servers: + description: Bootstrap servers + maxLength: 256 + minLength: 3 + type: string + sasl_mechanism: + description: + SASL mechanism used for connections to the Kafka + server. + enum: + - PLAIN + - SCRAM-SHA-256 + - SCRAM-SHA-512 + type: string + sasl_plain_password: + description: Password for SASL PLAIN mechanism in the Kafka server. + maxLength: 256 + minLength: 1 + type: string + sasl_plain_username: + description: Username for SASL PLAIN mechanism in the Kafka server. + maxLength: 256 + minLength: 1 + type: string + security_protocol: + description: Security protocol + enum: + - PLAINTEXT + - SSL + - SASL_PLAINTEXT + - SASL_SSL + type: string + ssl_ca_cert: + description: PEM-encoded CA certificate + maxLength: 16384 + type: string + ssl_client_cert: + description: PEM-encoded client certificate + maxLength: 16384 + type: string + ssl_client_key: + description: PEM-encoded client key + maxLength: 16384 + type: string + ssl_endpoint_identification_algorithm: + description: + The endpoint identification algorithm to validate + server hostname using server certificate. + enum: + - https + - "" + type: string + required: + - bootstrap_servers + - security_protocol + type: object + externalOpensearchLogs: + description: ExternalOpensearchLogs configuration values + properties: + ca: + description: PEM encoded CA certificate + maxLength: 16384 + type: string + index_days_max: + description: Maximum number of days of logs to keep + maximum: 10000 + minimum: 1 + type: integer + index_prefix: + description: OpenSearch index prefix + maxLength: 1000 + minLength: 1 + pattern: ^[a-z0-9][a-z0-9-_.]+$ + type: string + timeout: + description: OpenSearch request timeout limit + maximum: 120 + minimum: 10 + type: number + url: + description: OpenSearch connection URL + maxLength: 2048 + minLength: 12 + type: string + required: + - index_prefix + - url + type: object + externalPostgresql: + description: ExternalPostgresql configuration values + properties: + default_database: + description: Default database + maxLength: 63 + pattern: ^[_A-Za-z0-9][-_A-Za-z0-9]{0,62}$ + type: string + host: + description: Hostname or IP address of the server + maxLength: 255 + type: string + password: + description: Password + maxLength: 256 + type: string + port: + description: Port number of the server + maximum: 65535 + minimum: 1 + type: integer + ssl_client_certificate: + description: Client certificate + maxLength: 16384 + type: string + ssl_client_key: + description: Client key + maxLength: 16384 + type: string + ssl_mode: + description: SSL Mode + enum: + - disable + - allow + - prefer + - require + - verify-ca + - verify-full + type: string + ssl_root_cert: + description: SSL Root Cert + maxLength: 16384 + type: string + username: + description: User name + maxLength: 256 + type: string + required: + - host + - port + - username + type: object + externalSchemaRegistry: + description: ExternalSchemaRegistry configuration values + properties: + authentication: + description: Authentication method + enum: + - none + - basic + type: string + basic_auth_password: + description: Basic authentication password + maxLength: 256 + type: string + basic_auth_username: + description: Basic authentication user name + maxLength: 256 + type: string + url: + description: Schema Registry URL + maxLength: 2048 + type: string + required: + - authentication + - url + type: object + jolokia: + description: Jolokia configuration values + properties: + basic_auth_password: + description: Jolokia basic authentication password + maxLength: 64 + minLength: 8 + type: string + basic_auth_username: + description: Jolokia basic authentication username + maxLength: 32 + minLength: 5 + pattern: ^[a-z0-9\-@_]{5,32}$ + type: string + type: object + project: + description: Identifies the project this resource belongs to + format: ^[a-zA-Z0-9_-]+$ + maxLength: 63 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + prometheus: + description: Prometheus configuration values + properties: + basic_auth_password: + description: Prometheus basic authentication password + maxLength: 64 + minLength: 8 + type: string + basic_auth_username: + description: Prometheus basic authentication username + maxLength: 32 + minLength: 5 + pattern: ^[a-z0-9\-@_]{5,32}$ + type: string + type: object + rsyslog: + description: Rsyslog configuration values + properties: + ca: + description: PEM encoded CA certificate + maxLength: 16384 + type: string + cert: + description: PEM encoded client certificate + maxLength: 16384 + type: string + format: + description: Message format + enum: + - rfc5424 + - rfc3164 + - custom + type: string + key: + description: PEM encoded client key + maxLength: 16384 + type: string + logline: + description: Custom syslog message format + maxLength: 512 + minLength: 1 + pattern: ^[ -~\t]+$ + type: string + max_message_size: + description: Rsyslog max message size + maximum: 2147483647 + minimum: 2048 + type: integer + port: + description: Rsyslog server port + maximum: 65535 + minimum: 1 + type: integer + sd: + description: Structured data block for log message + maxLength: 1024 + type: string + server: + description: Rsyslog server IP address or hostname + maxLength: 255 + minLength: 4 + type: string + tls: + description: Require TLS + type: boolean + required: + - format + - port + - server + - tls + type: object + required: + - endpointType + - project + type: object + status: + description: + ServiceIntegrationEndpointStatus defines the observed state + of ServiceIntegrationEndpoint + properties: + conditions: + description: + Conditions represent the latest available observations + of an ServiceIntegrationEndpoint state + items: + description: + "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: + lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: + message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: + observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: + reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: + type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + id: + description: Service integration ID + type: string + required: + - conditions + - id + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/aiven-operator/templates/cluster_role.yaml b/charts/aiven-operator/templates/cluster_role.yaml index 088c8811..78a8559d 100644 --- a/charts/aiven-operator/templates/cluster_role.yaml +++ b/charts/aiven-operator/templates/cluster_role.yaml @@ -614,6 +614,34 @@ rules: - get - patch - update + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints/finalizers + verbs: + - create + - get + - update + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints/status + verbs: + - get + - patch + - update - apiGroups: - aiven.io resources: diff --git a/charts/aiven-operator/templates/mutating_webhook_configuration.yaml b/charts/aiven-operator/templates/mutating_webhook_configuration.yaml index bd459fd5..e3b9fca2 100644 --- a/charts/aiven-operator/templates/mutating_webhook_configuration.yaml +++ b/charts/aiven-operator/templates/mutating_webhook_configuration.yaml @@ -369,6 +369,26 @@ webhooks: resources: - serviceintegrations sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: {{ include "aiven-operator.fullname" . }}-webhook-service + namespace: {{ include "aiven-operator.namespace" . }} + path: /mutate-aiven-io-v1alpha1-serviceintegrationendpoint + failurePolicy: Fail + name: mserviceintegrationendpoint.kb.io + rules: + - apiGroups: + - aiven.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceintegrationendpoints + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/charts/aiven-operator/templates/validating_webhook_configuration.yaml b/charts/aiven-operator/templates/validating_webhook_configuration.yaml index cffbafc1..1296ce37 100644 --- a/charts/aiven-operator/templates/validating_webhook_configuration.yaml +++ b/charts/aiven-operator/templates/validating_webhook_configuration.yaml @@ -383,6 +383,26 @@ webhooks: resources: - serviceintegrations sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: {{ include "aiven-operator.fullname" . }}-webhook-service + namespace: {{ include "aiven-operator.namespace" . }} + path: /validate-aiven-io-v1alpha1-serviceintegrationendpoint + failurePolicy: Fail + name: vserviceintegrationendpoint.kb.io + rules: + - apiGroups: + - aiven.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceintegrationendpoints + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/config/crd/bases/aiven.io_serviceintegrationendpoints.yaml b/config/crd/bases/aiven.io_serviceintegrationendpoints.yaml new file mode 100644 index 00000000..9e33b7e9 --- /dev/null +++ b/config/crd/bases/aiven.io_serviceintegrationendpoints.yaml @@ -0,0 +1,654 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: serviceintegrationendpoints.aiven.io +spec: + group: aiven.io + names: + kind: ServiceIntegrationEndpoint + listKind: ServiceIntegrationEndpointList + plural: serviceintegrationendpoints + singular: serviceintegrationendpoint + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.project + name: Project + type: string + - jsonPath: .spec.endpointName + name: Endpoint Name + type: string + - jsonPath: .spec.endpointType + name: Endpoint Type + type: string + - jsonPath: .status.id + name: ID + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: + ServiceIntegrationEndpoint is the Schema for the serviceintegrationendpoints + 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: + ServiceIntegrationEndpointSpec defines the desired state + of ServiceIntegrationEndpoint + properties: + authSecretRef: + description: Authentication reference to Aiven token in a secret + properties: + key: + minLength: 1 + type: string + name: + minLength: 1 + type: string + required: + - key + - name + type: object + datadog: + description: Datadog configuration values + properties: + datadog_api_key: + description: Datadog API key + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9]{1,256}$ + type: string + datadog_tags: + description: Custom tags provided by user + items: + description: Datadog tag defined by user + properties: + comment: + description: Optional tag explanation + maxLength: 1024 + type: string + tag: + description: + "Tag format and usage are described here: https://docs.datadoghq.com/getting_started/tagging. + Tags with prefix 'aiven-' are reserved for Aiven." + maxLength: 200 + minLength: 1 + type: string + required: + - tag + type: object + maxItems: 32 + type: array + disable_consumer_stats: + description: Disable consumer group metrics + type: boolean + kafka_consumer_check_instances: + description: + Number of separate instances to fetch kafka consumer + statistics with + maximum: 100 + minimum: 1 + type: integer + kafka_consumer_stats_timeout: + description: + Number of seconds that datadog will wait to get consumer + statistics from brokers + maximum: 300 + minimum: 2 + type: integer + max_partition_contexts: + description: Maximum number of partition contexts to send + maximum: 200000 + minimum: 200 + type: integer + site: + description: Datadog intake site. Defaults to datadoghq.com + enum: + - datadoghq.com + - datadoghq.eu + - us3.datadoghq.com + - us5.datadoghq.com + - ddog-gov.com + type: string + required: + - datadog_api_key + type: object + endpointName: + description: Source endpoint for the integration (if any) + maxLength: 36 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + endpointType: + description: Type of the service integration endpoint + enum: + - autoscaler + - datadog + - external_aws_cloudwatch_logs + - external_aws_cloudwatch_metrics + - external_aws_s3 + - external_clickhouse + - external_elasticsearch_logs + - external_google_cloud_bigquery + - external_google_cloud_logging + - external_kafka + - external_mysql + - external_opensearch_logs + - external_postgresql + - external_redis + - external_schema_registry + - jolokia + - prometheus + - rsyslog + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + externalAWSCloudwatchLogs: + description: ExternalAwsCloudwatchLogs configuration values + properties: + access_key: + description: + AWS access key. Required permissions are logs:CreateLogGroup, + logs:CreateLogStream, logs:PutLogEvents and logs:DescribeLogStreams + maxLength: 4096 + type: string + log_group_name: + description: AWS CloudWatch log group name + maxLength: 512 + minLength: 1 + pattern: ^[\.\-_/#A-Za-z0-9]+$ + type: string + region: + description: AWS region + maxLength: 32 + type: string + secret_key: + description: AWS secret key + maxLength: 4096 + type: string + required: + - access_key + - region + - secret_key + type: object + externalAWSCloudwatchMetrics: + description: ExternalAwsCloudwatchMetrics configuration values + properties: + access_key: + description: AWS access key. Required permissions are cloudwatch:PutMetricData + maxLength: 4096 + type: string + namespace: + description: AWS CloudWatch Metrics Namespace + maxLength: 255 + minLength: 1 + type: string + region: + description: AWS region + maxLength: 32 + type: string + secret_key: + description: AWS secret key + maxLength: 4096 + type: string + required: + - access_key + - namespace + - region + - secret_key + type: object + externalElasticsearchLogs: + description: ExternalElasticsearchLogs configuration values + properties: + ca: + description: PEM encoded CA certificate + maxLength: 16384 + type: string + index_days_max: + description: Maximum number of days of logs to keep + maximum: 10000 + minimum: 1 + type: integer + index_prefix: + description: Elasticsearch index prefix + maxLength: 1000 + minLength: 1 + pattern: ^[a-z0-9][a-z0-9-_.]+$ + type: string + timeout: + description: Elasticsearch request timeout limit + maximum: 120 + minimum: 10 + type: number + url: + description: Elasticsearch connection URL + maxLength: 2048 + minLength: 12 + type: string + required: + - index_prefix + - url + type: object + externalGoogleCloudBigquery: + description: ExternalGoogleCloudBigquery configuration values + properties: + project_id: + description: GCP project id. + maxLength: 30 + minLength: 6 + type: string + service_account_credentials: + description: + This is a JSON object with the fields documented + in https://cloud.google.com/iam/docs/creating-managing-service-account-keys + . + maxLength: 4096 + type: string + required: + - project_id + - service_account_credentials + type: object + externalGoogleCloudLogging: + description: ExternalGoogleCloudLogging configuration values + properties: + log_id: + description: Google Cloud Logging log id + maxLength: 512 + type: string + project_id: + description: GCP project id. + maxLength: 30 + minLength: 6 + type: string + service_account_credentials: + description: + This is a JSON object with the fields documented + in https://cloud.google.com/iam/docs/creating-managing-service-account-keys + . + maxLength: 4096 + type: string + required: + - log_id + - project_id + - service_account_credentials + type: object + externalKafka: + description: ExternalKafka configuration values + properties: + bootstrap_servers: + description: Bootstrap servers + maxLength: 256 + minLength: 3 + type: string + sasl_mechanism: + description: + SASL mechanism used for connections to the Kafka + server. + enum: + - PLAIN + - SCRAM-SHA-256 + - SCRAM-SHA-512 + type: string + sasl_plain_password: + description: Password for SASL PLAIN mechanism in the Kafka server. + maxLength: 256 + minLength: 1 + type: string + sasl_plain_username: + description: Username for SASL PLAIN mechanism in the Kafka server. + maxLength: 256 + minLength: 1 + type: string + security_protocol: + description: Security protocol + enum: + - PLAINTEXT + - SSL + - SASL_PLAINTEXT + - SASL_SSL + type: string + ssl_ca_cert: + description: PEM-encoded CA certificate + maxLength: 16384 + type: string + ssl_client_cert: + description: PEM-encoded client certificate + maxLength: 16384 + type: string + ssl_client_key: + description: PEM-encoded client key + maxLength: 16384 + type: string + ssl_endpoint_identification_algorithm: + description: + The endpoint identification algorithm to validate + server hostname using server certificate. + enum: + - https + - "" + type: string + required: + - bootstrap_servers + - security_protocol + type: object + externalOpensearchLogs: + description: ExternalOpensearchLogs configuration values + properties: + ca: + description: PEM encoded CA certificate + maxLength: 16384 + type: string + index_days_max: + description: Maximum number of days of logs to keep + maximum: 10000 + minimum: 1 + type: integer + index_prefix: + description: OpenSearch index prefix + maxLength: 1000 + minLength: 1 + pattern: ^[a-z0-9][a-z0-9-_.]+$ + type: string + timeout: + description: OpenSearch request timeout limit + maximum: 120 + minimum: 10 + type: number + url: + description: OpenSearch connection URL + maxLength: 2048 + minLength: 12 + type: string + required: + - index_prefix + - url + type: object + externalPostgresql: + description: ExternalPostgresql configuration values + properties: + default_database: + description: Default database + maxLength: 63 + pattern: ^[_A-Za-z0-9][-_A-Za-z0-9]{0,62}$ + type: string + host: + description: Hostname or IP address of the server + maxLength: 255 + type: string + password: + description: Password + maxLength: 256 + type: string + port: + description: Port number of the server + maximum: 65535 + minimum: 1 + type: integer + ssl_client_certificate: + description: Client certificate + maxLength: 16384 + type: string + ssl_client_key: + description: Client key + maxLength: 16384 + type: string + ssl_mode: + description: SSL Mode + enum: + - disable + - allow + - prefer + - require + - verify-ca + - verify-full + type: string + ssl_root_cert: + description: SSL Root Cert + maxLength: 16384 + type: string + username: + description: User name + maxLength: 256 + type: string + required: + - host + - port + - username + type: object + externalSchemaRegistry: + description: ExternalSchemaRegistry configuration values + properties: + authentication: + description: Authentication method + enum: + - none + - basic + type: string + basic_auth_password: + description: Basic authentication password + maxLength: 256 + type: string + basic_auth_username: + description: Basic authentication user name + maxLength: 256 + type: string + url: + description: Schema Registry URL + maxLength: 2048 + type: string + required: + - authentication + - url + type: object + jolokia: + description: Jolokia configuration values + properties: + basic_auth_password: + description: Jolokia basic authentication password + maxLength: 64 + minLength: 8 + type: string + basic_auth_username: + description: Jolokia basic authentication username + maxLength: 32 + minLength: 5 + pattern: ^[a-z0-9\-@_]{5,32}$ + type: string + type: object + project: + description: Identifies the project this resource belongs to + format: ^[a-zA-Z0-9_-]+$ + maxLength: 63 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + prometheus: + description: Prometheus configuration values + properties: + basic_auth_password: + description: Prometheus basic authentication password + maxLength: 64 + minLength: 8 + type: string + basic_auth_username: + description: Prometheus basic authentication username + maxLength: 32 + minLength: 5 + pattern: ^[a-z0-9\-@_]{5,32}$ + type: string + type: object + rsyslog: + description: Rsyslog configuration values + properties: + ca: + description: PEM encoded CA certificate + maxLength: 16384 + type: string + cert: + description: PEM encoded client certificate + maxLength: 16384 + type: string + format: + description: Message format + enum: + - rfc5424 + - rfc3164 + - custom + type: string + key: + description: PEM encoded client key + maxLength: 16384 + type: string + logline: + description: Custom syslog message format + maxLength: 512 + minLength: 1 + pattern: ^[ -~\t]+$ + type: string + max_message_size: + description: Rsyslog max message size + maximum: 2147483647 + minimum: 2048 + type: integer + port: + description: Rsyslog server port + maximum: 65535 + minimum: 1 + type: integer + sd: + description: Structured data block for log message + maxLength: 1024 + type: string + server: + description: Rsyslog server IP address or hostname + maxLength: 255 + minLength: 4 + type: string + tls: + description: Require TLS + type: boolean + required: + - format + - port + - server + - tls + type: object + required: + - endpointType + - project + type: object + status: + description: + ServiceIntegrationEndpointStatus defines the observed state + of ServiceIntegrationEndpoint + properties: + conditions: + description: + Conditions represent the latest available observations + of an ServiceIntegrationEndpoint state + items: + description: + "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: + lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: + message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: + observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: + reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: + type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + id: + description: Service integration ID + type: string + required: + - conditions + - id + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 4680357b..59f1b24c 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -25,6 +25,7 @@ resources: - bases/aiven.io_clickhousedatabases.yaml - bases/aiven.io_kafkaschemaregistryacls.yaml - bases/aiven.io_clickhouseroles.yaml + - bases/aiven.io_serviceintegrationendpoints.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -49,6 +50,7 @@ patchesStrategicMerge: - patches/webhook_in_mysqls.yaml - patches/webhook_in_cassandras.yaml - patches/webhook_in_grafanas.yaml + - patches/webhook_in_serviceintegrationendpoints.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. @@ -72,6 +74,7 @@ patchesStrategicMerge: - patches/cainjection_in_mysqls.yaml - patches/cainjection_in_cassandras.yaml - patches/cainjection_in_grafanas.yaml + - patches/cainjection_in_serviceintegrationendpoints.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_serviceintegrationendpoints.yaml b/config/crd/patches/cainjection_in_serviceintegrationendpoints.yaml new file mode 100644 index 00000000..fb6d8068 --- /dev/null +++ b/config/crd/patches/cainjection_in_serviceintegrationendpoints.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: serviceintegrationendpoints.aiven.io diff --git a/config/crd/patches/webhook_in_serviceintegrationendpoints.yaml b/config/crd/patches/webhook_in_serviceintegrationendpoints.yaml new file mode 100644 index 00000000..fa9f3690 --- /dev/null +++ b/config/crd/patches/webhook_in_serviceintegrationendpoints.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serviceintegrationendpoints.aiven.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 2fabd059..8cc77e23 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -612,6 +612,34 @@ rules: - get - patch - update + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints/finalizers + verbs: + - create + - get + - update + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints/status + verbs: + - get + - patch + - update - apiGroups: - aiven.io resources: diff --git a/config/rbac/serviceintegrationendpoint_editor_role.yaml b/config/rbac/serviceintegrationendpoint_editor_role.yaml new file mode 100644 index 00000000..18f74d70 --- /dev/null +++ b/config/rbac/serviceintegrationendpoint_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit serviceintegrationendpoints. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: serviceintegrationendpoint-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: aiven-operator + app.kubernetes.io/part-of: aiven-operator + app.kubernetes.io/managed-by: kustomize + name: serviceintegrationendpoint-editor-role +rules: + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints/status + verbs: + - get diff --git a/config/rbac/serviceintegrationendpoint_viewer_role.yaml b/config/rbac/serviceintegrationendpoint_viewer_role.yaml new file mode 100644 index 00000000..d289bc65 --- /dev/null +++ b/config/rbac/serviceintegrationendpoint_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view serviceintegrationendpoints. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: serviceintegrationendpoint-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: aiven-operator + app.kubernetes.io/part-of: aiven-operator + app.kubernetes.io/managed-by: kustomize + name: serviceintegrationendpoint-viewer-role +rules: + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints + verbs: + - get + - list + - watch + - apiGroups: + - aiven.io + resources: + - serviceintegrationendpoints/status + verbs: + - get diff --git a/config/samples/_v1alpha1_serviceintegrationendpoint.yaml b/config/samples/_v1alpha1_serviceintegrationendpoint.yaml new file mode 100644 index 00000000..f0843db7 --- /dev/null +++ b/config/samples/_v1alpha1_serviceintegrationendpoint.yaml @@ -0,0 +1,12 @@ +apiVersion: aiven.io/v1alpha1 +kind: ServiceIntegrationEndpoint +metadata: + labels: + app.kubernetes.io/name: serviceintegrationendpoint + app.kubernetes.io/instance: serviceintegrationendpoint-sample + app.kubernetes.io/part-of: aiven-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: aiven-operator + name: serviceintegrationendpoint-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 3c71ce98..8dc58df7 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -23,4 +23,5 @@ resources: - _v1alpha1_clickhousedatabase.yaml - _v1alpha1_kafkaschemaregistryacl.yaml - _v1alpha1_clickhouserole.yaml + - _v1alpha1_serviceintegrationendpoint.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index f1395456..ec5fe76a 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -365,6 +365,26 @@ webhooks: resources: - serviceintegrations sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-aiven-io-v1alpha1-serviceintegrationendpoint + failurePolicy: Fail + name: mserviceintegrationendpoint.kb.io + rules: + - apiGroups: + - aiven.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceintegrationendpoints + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -766,6 +786,26 @@ webhooks: resources: - serviceintegrations sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-aiven-io-v1alpha1-serviceintegrationendpoint + failurePolicy: Fail + name: vserviceintegrationendpoint.kb.io + rules: + - apiGroups: + - aiven.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceintegrationendpoints + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/controllers/serviceintegration_controller.go b/controllers/serviceintegration_controller.go index ab7a807e..c91e9b42 100644 --- a/controllers/serviceintegration_controller.go +++ b/controllers/serviceintegration_controller.go @@ -5,7 +5,6 @@ package controllers import ( "context" "fmt" - "slices" "strconv" "strings" @@ -85,9 +84,7 @@ func (h ServiceIntegrationHandler) createOrUpdate(ctx context.Context, avn *aive reason = "Created" si.Status.ID = integration.ServiceIntegrationId } else { - // Not all service integrations have user_config available; skip the update if user_config is unavailable. - withUserConfig := []string{"clickhouse_kafka", "clickhouse_postgresql", "datadog", "kafka_connect", "kafka_logs", "kafka_mirrormaker", "logs", "metrics", "external_aws_cloudwatch_metrics"} - if !slices.Contains(withUserConfig, si.Spec.IntegrationType) { + if !si.HasUserConfig() { return nil } diff --git a/controllers/serviceintegrationendpoint_controller.go b/controllers/serviceintegrationendpoint_controller.go new file mode 100644 index 00000000..4d69ec9e --- /dev/null +++ b/controllers/serviceintegrationendpoint_controller.go @@ -0,0 +1,178 @@ +// Copyright (c) 2024 Aiven, Helsinki, Finland. https://aiven.io/ + +package controllers + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/aiven/aiven-go-client/v2" + avngen "github.com/aiven/go-client-codegen" + "github.com/aiven/go-client-codegen/handler/serviceintegration" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/aiven/aiven-operator/api/v1alpha1" +) + +// ServiceIntegrationEndpointReconciler reconciles a ServiceIntegrationEndpoint object +type ServiceIntegrationEndpointReconciler struct { + Controller +} + +func newServiceIntegrationEndpointReconciler(c Controller) reconcilerType { + return &ServiceIntegrationEndpointReconciler{Controller: c} +} + +//+kubebuilder:rbac:groups=aiven.io,resources=serviceintegrationendpoints,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=aiven.io,resources=serviceintegrationendpoints/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=aiven.io,resources=serviceintegrationendpoints/finalizers,verbs=get;create;update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +func (r *ServiceIntegrationEndpointReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.reconcileInstance(ctx, req, ServiceIntegrationEndpointHandler{}, &v1alpha1.ServiceIntegrationEndpoint{}) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ServiceIntegrationEndpointReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.ServiceIntegrationEndpoint{}). + Complete(r) +} + +type ServiceIntegrationEndpointHandler struct{} + +func (h ServiceIntegrationEndpointHandler) createOrUpdate(ctx context.Context, avn *aiven.Client, avnGen avngen.Client, obj client.Object, refs []client.Object) error { + si, err := h.convert(obj) + if err != nil { + return err + } + + userConfig, err := si.GetUserConfig() + if err != nil { + return err + } + + var reason string + if si.Status.ID == "" { + userConfigMap, err := CreateUserConfiguration(userConfig) + if err != nil { + return err + } + + endpoint, err := avnGen.ServiceIntegrationEndpointCreate( + ctx, + si.Spec.Project, + &serviceintegration.ServiceIntegrationEndpointCreateIn{ + EndpointName: si.Spec.EndpointName, + EndpointType: serviceintegration.EndpointType(si.Spec.EndpointType), + UserConfig: userConfigMap, + }, + ) + if err != nil { + return fmt.Errorf("cannot createOrUpdate service integration: %w", err) + } + + reason = "Created" + si.Status.ID = endpoint.EndpointId + } else { + if !si.HasUserConfig() { + return nil + } + + userConfigMap, err := UpdateUserConfiguration(userConfig) + if err != nil { + return err + } + + updatedIntegration, err := avnGen.ServiceIntegrationEndpointUpdate( + ctx, + si.Spec.Project, + si.Status.ID, + &serviceintegration.ServiceIntegrationEndpointUpdateIn{ + UserConfig: userConfigMap, + }, + ) + reason = "Updated" + if err != nil { + if strings.Contains(strings.ToLower(err.Error()), "user config not changed") { + return nil + } + return err + } + si.Status.ID = updatedIntegration.EndpointId + } + + meta.SetStatusCondition(&si.Status.Conditions, + getInitializedCondition(reason, + "Instance was created or update on Aiven side")) + + meta.SetStatusCondition(&si.Status.Conditions, + getRunningCondition(metav1.ConditionUnknown, reason, + "Instance was created or update on Aiven side, status remains unknown")) + + metav1.SetMetaDataAnnotation(&si.ObjectMeta, + processedGenerationAnnotation, strconv.FormatInt(si.GetGeneration(), formatIntBaseDecimal)) + + return nil +} + +func (h ServiceIntegrationEndpointHandler) delete(ctx context.Context, avn *aiven.Client, avnGen avngen.Client, obj client.Object) (bool, error) { + si, err := h.convert(obj) + if err != nil { + return false, err + } + + if si.Status.ID == "" { + return false, nil + } + + err = avnGen.ServiceIntegrationEndpointDelete(ctx, si.Spec.Project, si.Status.ID) + if err != nil && avngen.IsNotFound(err) { + return false, fmt.Errorf("aiven client delete service integration endpoint error: %w", err) + } + + return true, nil +} + +func (h ServiceIntegrationEndpointHandler) get(ctx context.Context, avn *aiven.Client, avnGen avngen.Client, obj client.Object) (*corev1.Secret, error) { + si, err := h.convert(obj) + if err != nil { + return nil, err + } + + meta.SetStatusCondition(&si.Status.Conditions, + getRunningCondition(metav1.ConditionTrue, "CheckRunning", + "Instance is running on Aiven side")) + + metav1.SetMetaDataAnnotation(&si.ObjectMeta, instanceIsRunningAnnotation, "true") + + return nil, nil +} + +func (h ServiceIntegrationEndpointHandler) checkPreconditions(ctx context.Context, avn *aiven.Client, avnGen avngen.Client, obj client.Object) (bool, error) { + si, err := h.convert(obj) + if err != nil { + return false, err + } + + meta.SetStatusCondition(&si.Status.Conditions, + getInitializedCondition("Preconditions", "Checking preconditions")) + + return true, nil +} + +func (h ServiceIntegrationEndpointHandler) convert(i client.Object) (*v1alpha1.ServiceIntegrationEndpoint, error) { + si, ok := i.(*v1alpha1.ServiceIntegrationEndpoint) + if !ok { + return nil, fmt.Errorf("cannot convert object to ServiceIntegrationEndpoint") + } + + return si, nil +} diff --git a/controllers/setup.go b/controllers/setup.go index 6e999b5b..27d3613a 100644 --- a/controllers/setup.go +++ b/controllers/setup.go @@ -46,6 +46,7 @@ func SetupControllers(mgr ctrl.Manager, defaultToken, kubeVersion, operatorVersi "ProjectVPC": newProjectVPCReconciler, "Redis": newRedisReconciler, "ServiceIntegration": newServiceIntegrationReconciler, + "ServiceIntegrationEndpoint": newServiceIntegrationEndpointReconciler, "ServiceUser": newServiceUserReconciler, } diff --git a/docs/docs/api-reference/serviceintegrationendpoint.md b/docs/docs/api-reference/serviceintegrationendpoint.md new file mode 100644 index 00000000..6b47a742 --- /dev/null +++ b/docs/docs/api-reference/serviceintegrationendpoint.md @@ -0,0 +1,275 @@ +--- +title: "ServiceIntegrationEndpoint" +--- + +## ServiceIntegrationEndpoint {: #ServiceIntegrationEndpoint } + +ServiceIntegrationEndpoint is the Schema for the serviceintegrationendpoints API. + +**Required** + +- [`apiVersion`](#apiVersion-property){: name='apiVersion-property'} (string). Value `aiven.io/v1alpha1`. +- [`kind`](#kind-property){: name='kind-property'} (string). Value `ServiceIntegrationEndpoint`. +- [`metadata`](#metadata-property){: name='metadata-property'} (object). Data that identifies the object, including a `name` string and optional `namespace`. +- [`spec`](#spec-property){: name='spec-property'} (object). ServiceIntegrationEndpointSpec defines the desired state of ServiceIntegrationEndpoint. See below for [nested schema](#spec). + +## spec {: #spec } + +_Appears on [`ServiceIntegrationEndpoint`](#ServiceIntegrationEndpoint)._ + +ServiceIntegrationEndpointSpec defines the desired state of ServiceIntegrationEndpoint. + +**Required** + +- [`endpointType`](#spec.endpointType-property){: name='spec.endpointType-property'} (string, Enum: `autoscaler`, `datadog`, `external_aws_cloudwatch_logs`, `external_aws_cloudwatch_metrics`, `external_aws_s3`, `external_clickhouse`, `external_elasticsearch_logs`, `external_google_cloud_bigquery`, `external_google_cloud_logging`, `external_kafka`, `external_mysql`, `external_opensearch_logs`, `external_postgresql`, `external_redis`, `external_schema_registry`, `jolokia`, `prometheus`, `rsyslog`, Immutable). Type of the service integration endpoint. +- [`project`](#spec.project-property){: name='spec.project-property'} (string, Immutable, MaxLength: 63, Format: `^[a-zA-Z0-9_-]+$`). Identifies the project this resource belongs to. + +**Optional** + +- [`authSecretRef`](#spec.authSecretRef-property){: name='spec.authSecretRef-property'} (object). Authentication reference to Aiven token in a secret. See below for [nested schema](#spec.authSecretRef). +- [`datadog`](#spec.datadog-property){: name='spec.datadog-property'} (object). Datadog configuration values. See below for [nested schema](#spec.datadog). +- [`endpointName`](#spec.endpointName-property){: name='spec.endpointName-property'} (string, Immutable, MaxLength: 36). Source endpoint for the integration (if any). +- [`externalAWSCloudwatchLogs`](#spec.externalAWSCloudwatchLogs-property){: name='spec.externalAWSCloudwatchLogs-property'} (object). ExternalAwsCloudwatchLogs configuration values. See below for [nested schema](#spec.externalAWSCloudwatchLogs). +- [`externalAWSCloudwatchMetrics`](#spec.externalAWSCloudwatchMetrics-property){: name='spec.externalAWSCloudwatchMetrics-property'} (object). ExternalAwsCloudwatchMetrics configuration values. See below for [nested schema](#spec.externalAWSCloudwatchMetrics). +- [`externalElasticsearchLogs`](#spec.externalElasticsearchLogs-property){: name='spec.externalElasticsearchLogs-property'} (object). ExternalElasticsearchLogs configuration values. See below for [nested schema](#spec.externalElasticsearchLogs). +- [`externalGoogleCloudBigquery`](#spec.externalGoogleCloudBigquery-property){: name='spec.externalGoogleCloudBigquery-property'} (object). ExternalGoogleCloudBigquery configuration values. See below for [nested schema](#spec.externalGoogleCloudBigquery). +- [`externalGoogleCloudLogging`](#spec.externalGoogleCloudLogging-property){: name='spec.externalGoogleCloudLogging-property'} (object). ExternalGoogleCloudLogging configuration values. See below for [nested schema](#spec.externalGoogleCloudLogging). +- [`externalKafka`](#spec.externalKafka-property){: name='spec.externalKafka-property'} (object). ExternalKafka configuration values. See below for [nested schema](#spec.externalKafka). +- [`externalOpensearchLogs`](#spec.externalOpensearchLogs-property){: name='spec.externalOpensearchLogs-property'} (object). ExternalOpensearchLogs configuration values. See below for [nested schema](#spec.externalOpensearchLogs). +- [`externalPostgresql`](#spec.externalPostgresql-property){: name='spec.externalPostgresql-property'} (object). ExternalPostgresql configuration values. See below for [nested schema](#spec.externalPostgresql). +- [`externalSchemaRegistry`](#spec.externalSchemaRegistry-property){: name='spec.externalSchemaRegistry-property'} (object). ExternalSchemaRegistry configuration values. See below for [nested schema](#spec.externalSchemaRegistry). +- [`jolokia`](#spec.jolokia-property){: name='spec.jolokia-property'} (object). Jolokia configuration values. See below for [nested schema](#spec.jolokia). +- [`prometheus`](#spec.prometheus-property){: name='spec.prometheus-property'} (object). Prometheus configuration values. See below for [nested schema](#spec.prometheus). +- [`rsyslog`](#spec.rsyslog-property){: name='spec.rsyslog-property'} (object). Rsyslog configuration values. See below for [nested schema](#spec.rsyslog). + +## authSecretRef {: #spec.authSecretRef } + +_Appears on [`spec`](#spec)._ + +Authentication reference to Aiven token in a secret. + +**Required** + +- [`key`](#spec.authSecretRef.key-property){: name='spec.authSecretRef.key-property'} (string, MinLength: 1). +- [`name`](#spec.authSecretRef.name-property){: name='spec.authSecretRef.name-property'} (string, MinLength: 1). + +## datadog {: #spec.datadog } + +_Appears on [`spec`](#spec)._ + +Datadog configuration values. + +**Required** + +- [`datadog_api_key`](#spec.datadog.datadog_api_key-property){: name='spec.datadog.datadog_api_key-property'} (string, Pattern: `^[A-Za-z0-9]{1,256}$`, MinLength: 1, MaxLength: 256). Datadog API key. + +**Optional** + +- [`datadog_tags`](#spec.datadog.datadog_tags-property){: name='spec.datadog.datadog_tags-property'} (array of objects, MaxItems: 32). Custom tags provided by user. See below for [nested schema](#spec.datadog.datadog_tags). +- [`disable_consumer_stats`](#spec.datadog.disable_consumer_stats-property){: name='spec.datadog.disable_consumer_stats-property'} (boolean). Disable consumer group metrics. +- [`kafka_consumer_check_instances`](#spec.datadog.kafka_consumer_check_instances-property){: name='spec.datadog.kafka_consumer_check_instances-property'} (integer, Minimum: 1, Maximum: 100). Number of separate instances to fetch kafka consumer statistics with. +- [`kafka_consumer_stats_timeout`](#spec.datadog.kafka_consumer_stats_timeout-property){: name='spec.datadog.kafka_consumer_stats_timeout-property'} (integer, Minimum: 2, Maximum: 300). Number of seconds that datadog will wait to get consumer statistics from brokers. +- [`max_partition_contexts`](#spec.datadog.max_partition_contexts-property){: name='spec.datadog.max_partition_contexts-property'} (integer, Minimum: 200, Maximum: 200000). Maximum number of partition contexts to send. +- [`site`](#spec.datadog.site-property){: name='spec.datadog.site-property'} (string, Enum: `datadoghq.com`, `datadoghq.eu`, `us3.datadoghq.com`, `us5.datadoghq.com`, `ddog-gov.com`). Datadog intake site. Defaults to datadoghq.com. + +### datadog_tags {: #spec.datadog.datadog_tags } + +_Appears on [`spec.datadog`](#spec.datadog)._ + +Custom tags provided by user. + +**Required** + +- [`tag`](#spec.datadog.datadog_tags.tag-property){: name='spec.datadog.datadog_tags.tag-property'} (string, MinLength: 1, MaxLength: 200). Tag format and usage are described here: https://docs.datadoghq.com/getting_started/tagging. Tags with prefix `aiven-` are reserved for Aiven. + +**Optional** + +- [`comment`](#spec.datadog.datadog_tags.comment-property){: name='spec.datadog.datadog_tags.comment-property'} (string, MaxLength: 1024). Optional tag explanation. + +## externalAWSCloudwatchLogs {: #spec.externalAWSCloudwatchLogs } + +_Appears on [`spec`](#spec)._ + +ExternalAwsCloudwatchLogs configuration values. + +**Required** + +- [`access_key`](#spec.externalAWSCloudwatchLogs.access_key-property){: name='spec.externalAWSCloudwatchLogs.access_key-property'} (string, MaxLength: 4096). AWS access key. Required permissions are logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents and logs:DescribeLogStreams. +- [`region`](#spec.externalAWSCloudwatchLogs.region-property){: name='spec.externalAWSCloudwatchLogs.region-property'} (string, MaxLength: 32). AWS region. +- [`secret_key`](#spec.externalAWSCloudwatchLogs.secret_key-property){: name='spec.externalAWSCloudwatchLogs.secret_key-property'} (string, MaxLength: 4096). AWS secret key. + +**Optional** + +- [`log_group_name`](#spec.externalAWSCloudwatchLogs.log_group_name-property){: name='spec.externalAWSCloudwatchLogs.log_group_name-property'} (string, Pattern: `^[\.\-_/#A-Za-z0-9]+$`, MinLength: 1, MaxLength: 512). AWS CloudWatch log group name. + +## externalAWSCloudwatchMetrics {: #spec.externalAWSCloudwatchMetrics } + +_Appears on [`spec`](#spec)._ + +ExternalAwsCloudwatchMetrics configuration values. + +**Required** + +- [`access_key`](#spec.externalAWSCloudwatchMetrics.access_key-property){: name='spec.externalAWSCloudwatchMetrics.access_key-property'} (string, MaxLength: 4096). AWS access key. Required permissions are cloudwatch:PutMetricData. +- [`namespace`](#spec.externalAWSCloudwatchMetrics.namespace-property){: name='spec.externalAWSCloudwatchMetrics.namespace-property'} (string, MinLength: 1, MaxLength: 255). AWS CloudWatch Metrics Namespace. +- [`region`](#spec.externalAWSCloudwatchMetrics.region-property){: name='spec.externalAWSCloudwatchMetrics.region-property'} (string, MaxLength: 32). AWS region. +- [`secret_key`](#spec.externalAWSCloudwatchMetrics.secret_key-property){: name='spec.externalAWSCloudwatchMetrics.secret_key-property'} (string, MaxLength: 4096). AWS secret key. + +## externalElasticsearchLogs {: #spec.externalElasticsearchLogs } + +_Appears on [`spec`](#spec)._ + +ExternalElasticsearchLogs configuration values. + +**Required** + +- [`index_prefix`](#spec.externalElasticsearchLogs.index_prefix-property){: name='spec.externalElasticsearchLogs.index_prefix-property'} (string, Pattern: `^[a-z0-9][a-z0-9-_.]+$`, MinLength: 1, MaxLength: 1000). Elasticsearch index prefix. +- [`url`](#spec.externalElasticsearchLogs.url-property){: name='spec.externalElasticsearchLogs.url-property'} (string, MinLength: 12, MaxLength: 2048). Elasticsearch connection URL. + +**Optional** + +- [`ca`](#spec.externalElasticsearchLogs.ca-property){: name='spec.externalElasticsearchLogs.ca-property'} (string, MaxLength: 16384). PEM encoded CA certificate. +- [`index_days_max`](#spec.externalElasticsearchLogs.index_days_max-property){: name='spec.externalElasticsearchLogs.index_days_max-property'} (integer, Minimum: 1, Maximum: 10000). Maximum number of days of logs to keep. +- [`timeout`](#spec.externalElasticsearchLogs.timeout-property){: name='spec.externalElasticsearchLogs.timeout-property'} (number, Minimum: 10, Maximum: 120). Elasticsearch request timeout limit. + +## externalGoogleCloudBigquery {: #spec.externalGoogleCloudBigquery } + +_Appears on [`spec`](#spec)._ + +ExternalGoogleCloudBigquery configuration values. + +**Required** + +- [`project_id`](#spec.externalGoogleCloudBigquery.project_id-property){: name='spec.externalGoogleCloudBigquery.project_id-property'} (string, MinLength: 6, MaxLength: 30). GCP project id. +- [`service_account_credentials`](#spec.externalGoogleCloudBigquery.service_account_credentials-property){: name='spec.externalGoogleCloudBigquery.service_account_credentials-property'} (string, MaxLength: 4096). This is a JSON object with the fields documented in https://cloud.google.com/iam/docs/creating-managing-service-account-keys . + +## externalGoogleCloudLogging {: #spec.externalGoogleCloudLogging } + +_Appears on [`spec`](#spec)._ + +ExternalGoogleCloudLogging configuration values. + +**Required** + +- [`log_id`](#spec.externalGoogleCloudLogging.log_id-property){: name='spec.externalGoogleCloudLogging.log_id-property'} (string, MaxLength: 512). Google Cloud Logging log id. +- [`project_id`](#spec.externalGoogleCloudLogging.project_id-property){: name='spec.externalGoogleCloudLogging.project_id-property'} (string, MinLength: 6, MaxLength: 30). GCP project id. +- [`service_account_credentials`](#spec.externalGoogleCloudLogging.service_account_credentials-property){: name='spec.externalGoogleCloudLogging.service_account_credentials-property'} (string, MaxLength: 4096). This is a JSON object with the fields documented in https://cloud.google.com/iam/docs/creating-managing-service-account-keys . + +## externalKafka {: #spec.externalKafka } + +_Appears on [`spec`](#spec)._ + +ExternalKafka configuration values. + +**Required** + +- [`bootstrap_servers`](#spec.externalKafka.bootstrap_servers-property){: name='spec.externalKafka.bootstrap_servers-property'} (string, MinLength: 3, MaxLength: 256). Bootstrap servers. +- [`security_protocol`](#spec.externalKafka.security_protocol-property){: name='spec.externalKafka.security_protocol-property'} (string, Enum: `PLAINTEXT`, `SSL`, `SASL_PLAINTEXT`, `SASL_SSL`). Security protocol. + +**Optional** + +- [`sasl_mechanism`](#spec.externalKafka.sasl_mechanism-property){: name='spec.externalKafka.sasl_mechanism-property'} (string, Enum: `PLAIN`, `SCRAM-SHA-256`, `SCRAM-SHA-512`). SASL mechanism used for connections to the Kafka server. +- [`sasl_plain_password`](#spec.externalKafka.sasl_plain_password-property){: name='spec.externalKafka.sasl_plain_password-property'} (string, MinLength: 1, MaxLength: 256). Password for SASL PLAIN mechanism in the Kafka server. +- [`sasl_plain_username`](#spec.externalKafka.sasl_plain_username-property){: name='spec.externalKafka.sasl_plain_username-property'} (string, MinLength: 1, MaxLength: 256). Username for SASL PLAIN mechanism in the Kafka server. +- [`ssl_ca_cert`](#spec.externalKafka.ssl_ca_cert-property){: name='spec.externalKafka.ssl_ca_cert-property'} (string, MaxLength: 16384). PEM-encoded CA certificate. +- [`ssl_client_cert`](#spec.externalKafka.ssl_client_cert-property){: name='spec.externalKafka.ssl_client_cert-property'} (string, MaxLength: 16384). PEM-encoded client certificate. +- [`ssl_client_key`](#spec.externalKafka.ssl_client_key-property){: name='spec.externalKafka.ssl_client_key-property'} (string, MaxLength: 16384). PEM-encoded client key. +- [`ssl_endpoint_identification_algorithm`](#spec.externalKafka.ssl_endpoint_identification_algorithm-property){: name='spec.externalKafka.ssl_endpoint_identification_algorithm-property'} (string, Enum: `https`, ``). The endpoint identification algorithm to validate server hostname using server certificate. + +## externalOpensearchLogs {: #spec.externalOpensearchLogs } + +_Appears on [`spec`](#spec)._ + +ExternalOpensearchLogs configuration values. + +**Required** + +- [`index_prefix`](#spec.externalOpensearchLogs.index_prefix-property){: name='spec.externalOpensearchLogs.index_prefix-property'} (string, Pattern: `^[a-z0-9][a-z0-9-_.]+$`, MinLength: 1, MaxLength: 1000). OpenSearch index prefix. +- [`url`](#spec.externalOpensearchLogs.url-property){: name='spec.externalOpensearchLogs.url-property'} (string, MinLength: 12, MaxLength: 2048). OpenSearch connection URL. + +**Optional** + +- [`ca`](#spec.externalOpensearchLogs.ca-property){: name='spec.externalOpensearchLogs.ca-property'} (string, MaxLength: 16384). PEM encoded CA certificate. +- [`index_days_max`](#spec.externalOpensearchLogs.index_days_max-property){: name='spec.externalOpensearchLogs.index_days_max-property'} (integer, Minimum: 1, Maximum: 10000). Maximum number of days of logs to keep. +- [`timeout`](#spec.externalOpensearchLogs.timeout-property){: name='spec.externalOpensearchLogs.timeout-property'} (number, Minimum: 10, Maximum: 120). OpenSearch request timeout limit. + +## externalPostgresql {: #spec.externalPostgresql } + +_Appears on [`spec`](#spec)._ + +ExternalPostgresql configuration values. + +**Required** + +- [`host`](#spec.externalPostgresql.host-property){: name='spec.externalPostgresql.host-property'} (string, MaxLength: 255). Hostname or IP address of the server. +- [`port`](#spec.externalPostgresql.port-property){: name='spec.externalPostgresql.port-property'} (integer, Minimum: 1, Maximum: 65535). Port number of the server. +- [`username`](#spec.externalPostgresql.username-property){: name='spec.externalPostgresql.username-property'} (string, MaxLength: 256). User name. + +**Optional** + +- [`default_database`](#spec.externalPostgresql.default_database-property){: name='spec.externalPostgresql.default_database-property'} (string, Pattern: `^[_A-Za-z0-9][-_A-Za-z0-9]{0,62}$`, MaxLength: 63). Default database. +- [`password`](#spec.externalPostgresql.password-property){: name='spec.externalPostgresql.password-property'} (string, MaxLength: 256). Password. +- [`ssl_client_certificate`](#spec.externalPostgresql.ssl_client_certificate-property){: name='spec.externalPostgresql.ssl_client_certificate-property'} (string, MaxLength: 16384). Client certificate. +- [`ssl_client_key`](#spec.externalPostgresql.ssl_client_key-property){: name='spec.externalPostgresql.ssl_client_key-property'} (string, MaxLength: 16384). Client key. +- [`ssl_mode`](#spec.externalPostgresql.ssl_mode-property){: name='spec.externalPostgresql.ssl_mode-property'} (string, Enum: `disable`, `allow`, `prefer`, `require`, `verify-ca`, `verify-full`). SSL Mode. +- [`ssl_root_cert`](#spec.externalPostgresql.ssl_root_cert-property){: name='spec.externalPostgresql.ssl_root_cert-property'} (string, MaxLength: 16384). SSL Root Cert. + +## externalSchemaRegistry {: #spec.externalSchemaRegistry } + +_Appears on [`spec`](#spec)._ + +ExternalSchemaRegistry configuration values. + +**Required** + +- [`authentication`](#spec.externalSchemaRegistry.authentication-property){: name='spec.externalSchemaRegistry.authentication-property'} (string, Enum: `none`, `basic`). Authentication method. +- [`url`](#spec.externalSchemaRegistry.url-property){: name='spec.externalSchemaRegistry.url-property'} (string, MaxLength: 2048). Schema Registry URL. + +**Optional** + +- [`basic_auth_password`](#spec.externalSchemaRegistry.basic_auth_password-property){: name='spec.externalSchemaRegistry.basic_auth_password-property'} (string, MaxLength: 256). Basic authentication password. +- [`basic_auth_username`](#spec.externalSchemaRegistry.basic_auth_username-property){: name='spec.externalSchemaRegistry.basic_auth_username-property'} (string, MaxLength: 256). Basic authentication user name. + +## jolokia {: #spec.jolokia } + +_Appears on [`spec`](#spec)._ + +Jolokia configuration values. + +**Optional** + +- [`basic_auth_password`](#spec.jolokia.basic_auth_password-property){: name='spec.jolokia.basic_auth_password-property'} (string, MinLength: 8, MaxLength: 64). Jolokia basic authentication password. +- [`basic_auth_username`](#spec.jolokia.basic_auth_username-property){: name='spec.jolokia.basic_auth_username-property'} (string, Pattern: `^[a-z0-9\-@_]{5,32}$`, MinLength: 5, MaxLength: 32). Jolokia basic authentication username. + +## prometheus {: #spec.prometheus } + +_Appears on [`spec`](#spec)._ + +Prometheus configuration values. + +**Optional** + +- [`basic_auth_password`](#spec.prometheus.basic_auth_password-property){: name='spec.prometheus.basic_auth_password-property'} (string, MinLength: 8, MaxLength: 64). Prometheus basic authentication password. +- [`basic_auth_username`](#spec.prometheus.basic_auth_username-property){: name='spec.prometheus.basic_auth_username-property'} (string, Pattern: `^[a-z0-9\-@_]{5,32}$`, MinLength: 5, MaxLength: 32). Prometheus basic authentication username. + +## rsyslog {: #spec.rsyslog } + +_Appears on [`spec`](#spec)._ + +Rsyslog configuration values. + +**Required** + +- [`format`](#spec.rsyslog.format-property){: name='spec.rsyslog.format-property'} (string, Enum: `rfc5424`, `rfc3164`, `custom`). Message format. +- [`port`](#spec.rsyslog.port-property){: name='spec.rsyslog.port-property'} (integer, Minimum: 1, Maximum: 65535). Rsyslog server port. +- [`server`](#spec.rsyslog.server-property){: name='spec.rsyslog.server-property'} (string, MinLength: 4, MaxLength: 255). Rsyslog server IP address or hostname. +- [`tls`](#spec.rsyslog.tls-property){: name='spec.rsyslog.tls-property'} (boolean). Require TLS. + +**Optional** + +- [`ca`](#spec.rsyslog.ca-property){: name='spec.rsyslog.ca-property'} (string, MaxLength: 16384). PEM encoded CA certificate. +- [`cert`](#spec.rsyslog.cert-property){: name='spec.rsyslog.cert-property'} (string, MaxLength: 16384). PEM encoded client certificate. +- [`key`](#spec.rsyslog.key-property){: name='spec.rsyslog.key-property'} (string, MaxLength: 16384). PEM encoded client key. +- [`logline`](#spec.rsyslog.logline-property){: name='spec.rsyslog.logline-property'} (string, Pattern: `^[ -~\t]+$`, MinLength: 1, MaxLength: 512). Custom syslog message format. +- [`max_message_size`](#spec.rsyslog.max_message_size-property){: name='spec.rsyslog.max_message_size-property'} (integer, Minimum: 2048, Maximum: 2147483647). Rsyslog max message size. +- [`sd`](#spec.rsyslog.sd-property){: name='spec.rsyslog.sd-property'} (string, MaxLength: 1024). Structured data block for log message. diff --git a/go.mod b/go.mod index 60a0993d..54a701bc 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.15.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect diff --git a/go.sum b/go.sum index 341ab9fc..27aaeda1 100644 --- a/go.sum +++ b/go.sum @@ -110,6 +110,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -172,6 +174,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -253,8 +257,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= -github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= diff --git a/tests/serviceintegrationendpoint_test.go b/tests/serviceintegrationendpoint_test.go new file mode 100644 index 00000000..d3b51f0f --- /dev/null +++ b/tests/serviceintegrationendpoint_test.go @@ -0,0 +1,112 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aiven/aiven-operator/api/v1alpha1" +) + +func getServiceIntegrationEndpointYaml(project, endpointPgName, endpointRegistryName string) string { + return fmt.Sprintf(` +apiVersion: aiven.io/v1alpha1 +kind: ServiceIntegrationEndpoint +metadata: + name: %[2]s +spec: + authSecretRef: + name: aiven-token + key: token + + project: %[1]s + endpointName: %[2]s + endpointType: external_postgresql + + externalPostgresql: + username: admin_username + password: admin_password + host: example.example + port: 5432 + ssl_mode: disable + +--- + +apiVersion: aiven.io/v1alpha1 +kind: ServiceIntegrationEndpoint +metadata: + name: %[3]s +spec: + authSecretRef: + name: aiven-token + key: token + + project: %[1]s + endpointName: %[3]s + endpointType: external_schema_registry + + externalSchemaRegistry: + url: https://schema-registry.example.com:8081 + authentication: basic + basic_auth_username: username + basic_auth_password: password +`, project, endpointPgName, endpointRegistryName) +} + +func TestServiceIntegrationEndpoint(t *testing.T) { + t.Parallel() + defer recoverPanic(t) + + // GIVEN + ctx, cancel := testCtx() + defer cancel() + + endpointPgName := randName("postgresql") + endpointRegistryName := randName("schema-registry") + + yml := getServiceIntegrationEndpointYaml(cfg.Project, endpointPgName, endpointRegistryName) + s := NewSession(ctx, k8sClient, cfg.Project) + + // Cleans test afterward + defer s.Destroy() + + // WHEN + // Applies given manifest + require.NoError(t, s.Apply(yml)) + + // THEN + + // Validates ServiceIntegrationEndpoint externalPostgresql + endpointPg := new(v1alpha1.ServiceIntegrationEndpoint) + require.NoError(t, s.GetRunning(endpointPg, endpointPgName)) + endpointPgAvn, err := avnGen.ServiceIntegrationEndpointGet(ctx, cfg.Project, endpointPg.Status.ID) + require.NoError(t, err) + assert.Equal(t, "external_postgresql", string(endpointPgAvn.EndpointType)) + assert.Equal(t, "admin_username", endpointPg.Spec.ExternalPostgresql.Username) + assert.Equal(t, "admin_password", *endpointPg.Spec.ExternalPostgresql.Password) + assert.Equal(t, "example.example", endpointPg.Spec.ExternalPostgresql.Host) + assert.Equal(t, 5432, endpointPg.Spec.ExternalPostgresql.Port) + assert.EqualValues(t, endpointPgAvn.EndpointType, endpointPg.Spec.EndpointType) + assert.EqualValues(t, endpointPgAvn.UserConfig["username"], endpointPg.Spec.ExternalPostgresql.Username) + assert.EqualValues(t, endpointPgAvn.UserConfig["password"], *endpointPg.Spec.ExternalPostgresql.Password) + assert.EqualValues(t, endpointPgAvn.UserConfig["host"], endpointPg.Spec.ExternalPostgresql.Host) + assert.EqualValues(t, endpointPgAvn.UserConfig["port"], endpointPg.Spec.ExternalPostgresql.Port) + + // Validates ServiceIntegrationEndpoint externalSchemaRegistry + endpointRegistry := new(v1alpha1.ServiceIntegrationEndpoint) + require.NoError(t, s.GetRunning(endpointRegistry, endpointRegistryName)) + endpointRegistryAvn, err := avnGen.ServiceIntegrationEndpointGet(ctx, cfg.Project, endpointRegistry.Status.ID) + require.NoError(t, err) + assert.Equal(t, "external_schema_registry", string(endpointRegistryAvn.EndpointType)) + assert.Equal(t, "https://schema-registry.example.com:8081", endpointRegistry.Spec.ExternalSchemaRegistry.Url) + assert.Equal(t, "basic", endpointRegistry.Spec.ExternalSchemaRegistry.Authentication) + assert.EqualValues(t, "username", *endpointRegistry.Spec.ExternalSchemaRegistry.BasicAuthUsername) + assert.EqualValues(t, "password", *endpointRegistry.Spec.ExternalSchemaRegistry.BasicAuthPassword) + assert.EqualValues(t, endpointRegistryAvn.EndpointType, endpointRegistry.Spec.EndpointType) + assert.EqualValues(t, endpointRegistryAvn.UserConfig["url"], endpointRegistry.Spec.ExternalSchemaRegistry.Url) + assert.EqualValues(t, endpointRegistryAvn.UserConfig["authentication"], endpointRegistry.Spec.ExternalSchemaRegistry.Authentication) + assert.EqualValues(t, endpointRegistryAvn.UserConfig["basic_auth_username"], *endpointRegistry.Spec.ExternalSchemaRegistry.BasicAuthUsername) + assert.EqualValues(t, endpointRegistryAvn.UserConfig["basic_auth_password"], *endpointRegistry.Spec.ExternalSchemaRegistry.BasicAuthPassword) +}