diff --git a/apis/vshn/v1/dbaas_vshn_keycloak.go b/apis/vshn/v1/dbaas_vshn_keycloak.go index 7bf6a772a..f75d5d2d6 100644 --- a/apis/vshn/v1/dbaas_vshn_keycloak.go +++ b/apis/vshn/v1/dbaas_vshn_keycloak.go @@ -2,7 +2,6 @@ package v1 import ( "fmt" - xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -334,3 +333,11 @@ func (v *VSHNKeycloak) GetPDBLabels() map[string]string { func (v *VSHNKeycloak) GetSecurity() *Security { return &v.Spec.Parameters.Security } + +func (v *VSHNKeycloak) GetWorkloadPodTemplateLabelsManager() PodTemplateLabelsManager { + return &StatefulSetManager{} +} + +func (v *VSHNKeycloak) GetWorkloadName() string { + return v.GetName() + "-keycloakx" +} diff --git a/apis/vshn/v1/dbaas_vshn_mariadb.go b/apis/vshn/v1/dbaas_vshn_mariadb.go index 48bfff6fb..dfe3d6a84 100644 --- a/apis/vshn/v1/dbaas_vshn_mariadb.go +++ b/apis/vshn/v1/dbaas_vshn_mariadb.go @@ -2,7 +2,6 @@ package v1 import ( "fmt" - xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -99,8 +98,8 @@ type VSHNMariaDBServiceSpec struct { // ServiceLevel defines the service level of this service. Either Best Effort or Guaranteed Availability is allowed. ServiceLevel VSHNDBaaSServiceLevel `json:"serviceLevel,omitempty"` - // +kubebuilder:default="standalone" - // +kubebuilder:validation:Enum=replication;standalone + // Access defines additional users and databases for this instance. + Access []VSHNAccess `json:"access,omitempty"` } // VSHNMariaDBTLSSpec contains settings to control tls traffic of a service. @@ -273,3 +272,11 @@ func (v *VSHNMariaDB) GetPDBLabels() map[string]string { func (v *VSHNMariaDB) GetSecurity() *Security { return &v.Spec.Parameters.Security } + +func (v *VSHNMariaDB) GetWorkloadPodTemplateLabelsManager() PodTemplateLabelsManager { + return &StatefulSetManager{} +} + +func (v *VSHNMariaDB) GetWorkloadName() string { + return v.GetName() +} diff --git a/apis/vshn/v1/dbaas_vshn_postgresql.go b/apis/vshn/v1/dbaas_vshn_postgresql.go index 307af4869..285e997c3 100644 --- a/apis/vshn/v1/dbaas_vshn_postgresql.go +++ b/apis/vshn/v1/dbaas_vshn_postgresql.go @@ -2,7 +2,6 @@ package v1 import ( "fmt" - xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" sgv1 "github.com/vshn/appcat/v4/apis/stackgres/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -126,7 +125,7 @@ type VSHNPostgreSQLUpdateStrategy struct { // VSHNPostgreSQLServiceSpec contains PostgreSQL DBaaS specific properties type VSHNPostgreSQLServiceSpec struct { - // +kubebuilder:validation:Enum="12";"13";"14";"15" + // +kubebuilder:validation:Enum="12";"13";"14";"15";"16" // +kubebuilder:default="15" // MajorVersion contains supported version of PostgreSQL. @@ -154,6 +153,7 @@ type VSHNPostgreSQLServiceSpec struct { // +kubebuilder:default=false VacuumEnabled bool `json:"vacuumEnabled,omitempty"` + // Access defines additional users and databases for this instance. Access []VSHNAccess `json:"access,omitempty"` } @@ -397,3 +397,11 @@ func (v *VSHNPostgreSQL) GetPDBLabels() map[string]string { func (v *VSHNPostgreSQL) GetSecurity() *Security { return &v.Spec.Parameters.Security } + +func (v *VSHNPostgreSQL) GetWorkloadPodTemplateLabelsManager() PodTemplateLabelsManager { + return &StatefulSetManager{} +} + +func (v *VSHNPostgreSQL) GetWorkloadName() string { + return v.GetName() +} diff --git a/apis/vshn/v1/dbaas_vshn_redis.go b/apis/vshn/v1/dbaas_vshn_redis.go index 43594b7e0..3703fb2cb 100644 --- a/apis/vshn/v1/dbaas_vshn_redis.go +++ b/apis/vshn/v1/dbaas_vshn_redis.go @@ -2,7 +2,6 @@ package v1 import ( "fmt" - xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -298,3 +297,11 @@ func (v *VSHNRedis) GetPDBLabels() map[string]string { func (v *VSHNRedis) GetSecurity() *Security { return &v.Spec.Parameters.Security } + +func (v *VSHNRedis) GetWorkloadPodTemplateLabelsManager() PodTemplateLabelsManager { + return &StatefulSetManager{} +} + +func (v *VSHNRedis) GetWorkloadName() string { + return "redis-master" +} diff --git a/apis/vshn/v1/non_gen_types.go b/apis/vshn/v1/non_gen_types.go new file mode 100644 index 000000000..0e8c00993 --- /dev/null +++ b/apis/vshn/v1/non_gen_types.go @@ -0,0 +1,57 @@ +package v1 + +import ( + v1 "k8s.io/api/apps/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// +kubebuilder:skip +// +kubebuilder:skipclient +// +kubebuilder:skipdeepcopy +// +kubebuilder:object:generate=false +type PodTemplateLabelsManager interface { + SetPodTemplateLabels(map[string]string) + GetPodTemplateLabels() map[string]string + GetObject() client.Object + client.Object +} + +// +kubebuilder:skip +// +kubebuilder:skipclient +// +kubebuilder:skipdeepcopy +// +kubebuilder:object:generate=false +type DeploymentManager struct { + v1.Deployment +} + +// +kubebuilder:skip +// +kubebuilder:skipclient +// +kubebuilder:skipdeepcopy +// +kubebuilder:object:generate=false +type StatefulSetManager struct { + v1.StatefulSet +} + +func (d *DeploymentManager) SetPodTemplateLabels(labels map[string]string) { + d.Spec.Template.SetLabels(labels) +} + +func (s *StatefulSetManager) SetPodTemplateLabels(labels map[string]string) { + s.Spec.Template.SetLabels(labels) +} + +func (d *DeploymentManager) GetPodTemplateLabels() map[string]string { + return d.Spec.Template.GetLabels() +} + +func (s *StatefulSetManager) GetPodTemplateLabels() map[string]string { + return s.Spec.Template.GetLabels() +} + +func (d *DeploymentManager) GetObject() client.Object { + return &d.Deployment +} + +func (d *StatefulSetManager) GetObject() client.Object { + return &d.StatefulSet +} diff --git a/apis/vshn/v1/vshn_minio.go b/apis/vshn/v1/vshn_minio.go index d8978921d..e18726728 100644 --- a/apis/vshn/v1/vshn_minio.go +++ b/apis/vshn/v1/vshn_minio.go @@ -2,7 +2,6 @@ package v1 import ( "fmt" - xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -247,3 +246,14 @@ func (v *VSHNMinio) GetPDBLabels() map[string]string { func (v *VSHNMinio) GetSecurity() *Security { return &v.Spec.Parameters.Security } + +func (v *VSHNMinio) GetWorkloadPodTemplateLabelsManager() PodTemplateLabelsManager { + if v.GetInstances() == 1 { + return &DeploymentManager{} + } + return &StatefulSetManager{} +} + +func (v *VSHNMinio) GetWorkloadName() string { + return v.GetName() +} diff --git a/apis/vshn/v1/vshn_nextcloud.go b/apis/vshn/v1/vshn_nextcloud.go index 3c56f129a..293df45f2 100644 --- a/apis/vshn/v1/vshn_nextcloud.go +++ b/apis/vshn/v1/vshn_nextcloud.go @@ -2,7 +2,6 @@ package v1 import ( "fmt" - xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -310,3 +309,11 @@ func (v *VSHNNextcloud) GetPDBLabels() map[string]string { func (v *VSHNNextcloud) GetSecurity() *Security { return &v.Spec.Parameters.Security } + +func (v *VSHNNextcloud) GetWorkloadPodTemplateLabelsManager() PodTemplateLabelsManager { + return &DeploymentManager{} +} + +func (v *VSHNNextcloud) GetWorkloadName() string { + return v.GetName() +} diff --git a/apis/vshn/v1/zz_generated.deepcopy.go b/apis/vshn/v1/zz_generated.deepcopy.go index 7d71f6886..c7cb758b0 100644 --- a/apis/vshn/v1/zz_generated.deepcopy.go +++ b/apis/vshn/v1/zz_generated.deepcopy.go @@ -526,7 +526,7 @@ func (in *VSHNMariaDBList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VSHNMariaDBParameters) DeepCopyInto(out *VSHNMariaDBParameters) { *out = *in - out.Service = in.Service + in.Service.DeepCopyInto(&out.Service) out.Size = in.Size in.Scheduling.DeepCopyInto(&out.Scheduling) out.TLS = in.TLS @@ -550,6 +550,13 @@ func (in *VSHNMariaDBParameters) DeepCopy() *VSHNMariaDBParameters { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VSHNMariaDBServiceSpec) DeepCopyInto(out *VSHNMariaDBServiceSpec) { *out = *in + if in.Access != nil { + in, out := &in.Access, &out.Access + *out = make([]VSHNAccess, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNMariaDBServiceSpec. diff --git a/crds/vshn.appcat.vshn.io_vshnkeycloaks.yaml b/crds/vshn.appcat.vshn.io_vshnkeycloaks.yaml index 0ca7822fd..22345d0c6 100644 --- a/crds/vshn.appcat.vshn.io_vshnkeycloaks.yaml +++ b/crds/vshn.appcat.vshn.io_vshnkeycloaks.yaml @@ -9776,6 +9776,7 @@ spec: description: Service contains PostgreSQL DBaaS specific properties properties: access: + description: Access defines additional users and databases for this instance. items: properties: database: @@ -9837,6 +9838,7 @@ spec: - "13" - "14" - "15" + - "16" type: string pgBouncerSettings: description: PgBouncerSettings passes additional configuration to the pgBouncer instance. diff --git a/crds/vshn.appcat.vshn.io_vshnmariadbs.yaml b/crds/vshn.appcat.vshn.io_vshnmariadbs.yaml index 30b942c8a..82caf646b 100644 --- a/crds/vshn.appcat.vshn.io_vshnmariadbs.yaml +++ b/crds/vshn.appcat.vshn.io_vshnmariadbs.yaml @@ -4843,6 +4843,47 @@ spec: service: description: Service contains MariaDB DBaaS specific properties properties: + access: + description: Access defines additional users and databases for this instance. + items: + properties: + database: + description: Database is the name of the database to create, defaults to user. + type: string + privileges: + description: |- + Privileges specifies the privileges to grant the user. Please check + the database's docs for available privileges. + items: + type: string + type: array + user: + description: |- + User specifies the username. If all other fields are left empty + then a new database with the same name and all permissions will be created. + type: string + writeConnectionSecretToRef: + description: |- + WriteConnectionSecretToReference specifies the namespace and name of a + Secret to which any connection details for this user should + be written. + If not specified, a secret with the name $claimname-$username will be + created in the namespace where the claim is located. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - user + type: object + type: array mariadbSettings: description: MariadbSettings contains additional MariaDB settings. type: string diff --git a/crds/vshn.appcat.vshn.io_vshnnextclouds.yaml b/crds/vshn.appcat.vshn.io_vshnnextclouds.yaml index 1b0738467..8b8d87ba3 100644 --- a/crds/vshn.appcat.vshn.io_vshnnextclouds.yaml +++ b/crds/vshn.appcat.vshn.io_vshnnextclouds.yaml @@ -9773,6 +9773,7 @@ spec: description: Service contains PostgreSQL DBaaS specific properties properties: access: + description: Access defines additional users and databases for this instance. items: properties: database: @@ -9834,6 +9835,7 @@ spec: - "13" - "14" - "15" + - "16" type: string pgBouncerSettings: description: PgBouncerSettings passes additional configuration to the pgBouncer instance. diff --git a/crds/vshn.appcat.vshn.io_vshnpostgresqls.yaml b/crds/vshn.appcat.vshn.io_vshnpostgresqls.yaml index b71ffb667..271178a44 100644 --- a/crds/vshn.appcat.vshn.io_vshnpostgresqls.yaml +++ b/crds/vshn.appcat.vshn.io_vshnpostgresqls.yaml @@ -4966,6 +4966,7 @@ spec: description: Service contains PostgreSQL DBaaS specific properties properties: access: + description: Access defines additional users and databases for this instance. items: properties: database: @@ -5027,6 +5028,7 @@ spec: - "13" - "14" - "15" + - "16" type: string pgBouncerSettings: description: PgBouncerSettings passes additional configuration to the pgBouncer instance. diff --git a/crds/vshn.appcat.vshn.io_xvshnkeycloaks.yaml b/crds/vshn.appcat.vshn.io_xvshnkeycloaks.yaml index 101bf5fc7..f784d43ba 100644 --- a/crds/vshn.appcat.vshn.io_xvshnkeycloaks.yaml +++ b/crds/vshn.appcat.vshn.io_xvshnkeycloaks.yaml @@ -11556,6 +11556,8 @@ spec: properties properties: access: + description: Access defines additional users and databases + for this instance. items: properties: database: @@ -11620,6 +11622,7 @@ spec: - "13" - "14" - "15" + - "16" type: string pgBouncerSettings: description: PgBouncerSettings passes additional configuration diff --git a/crds/vshn.appcat.vshn.io_xvshnmariadbs.yaml b/crds/vshn.appcat.vshn.io_xvshnmariadbs.yaml index 7706817bd..6bf286063 100644 --- a/crds/vshn.appcat.vshn.io_xvshnmariadbs.yaml +++ b/crds/vshn.appcat.vshn.io_xvshnmariadbs.yaml @@ -5567,6 +5567,49 @@ spec: service: description: Service contains MariaDB DBaaS specific properties properties: + access: + description: Access defines additional users and databases + for this instance. + items: + properties: + database: + description: Database is the name of the database to + create, defaults to user. + type: string + privileges: + description: |- + Privileges specifies the privileges to grant the user. Please check + the database's docs for available privileges. + items: + type: string + type: array + user: + description: |- + User specifies the username. If all other fields are left empty + then a new database with the same name and all permissions will be created. + type: string + writeConnectionSecretToRef: + description: |- + WriteConnectionSecretToReference specifies the namespace and name of a + Secret to which any connection details for this user should + be written. + If not specified, a secret with the name $claimname-$username will be + created in the namespace where the claim is located. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - user + type: object + type: array mariadbSettings: description: MariadbSettings contains additional MariaDB settings. type: string diff --git a/crds/vshn.appcat.vshn.io_xvshnnextclouds.yaml b/crds/vshn.appcat.vshn.io_xvshnnextclouds.yaml index 8e1fca442..a9ac8efbb 100644 --- a/crds/vshn.appcat.vshn.io_xvshnnextclouds.yaml +++ b/crds/vshn.appcat.vshn.io_xvshnnextclouds.yaml @@ -11559,6 +11559,8 @@ spec: properties properties: access: + description: Access defines additional users and databases + for this instance. items: properties: database: @@ -11623,6 +11625,7 @@ spec: - "13" - "14" - "15" + - "16" type: string pgBouncerSettings: description: PgBouncerSettings passes additional configuration diff --git a/crds/vshn.appcat.vshn.io_xvshnpostgresqls.yaml b/crds/vshn.appcat.vshn.io_xvshnpostgresqls.yaml index a253bf804..9569b3d23 100644 --- a/crds/vshn.appcat.vshn.io_xvshnpostgresqls.yaml +++ b/crds/vshn.appcat.vshn.io_xvshnpostgresqls.yaml @@ -5648,6 +5648,8 @@ spec: description: Service contains PostgreSQL DBaaS specific properties properties: access: + description: Access defines additional users and databases + for this instance. items: properties: database: @@ -5712,6 +5714,7 @@ spec: - "13" - "14" - "15" + - "16" type: string pgBouncerSettings: description: PgBouncerSettings passes additional configuration diff --git a/pkg/comp-functions/functions/common/billing.go b/pkg/comp-functions/functions/common/billing.go new file mode 100644 index 000000000..d4f5d74a7 --- /dev/null +++ b/pkg/comp-functions/functions/common/billing.go @@ -0,0 +1,53 @@ +package common + +import ( + "context" + "fmt" + v12 "github.com/crossplane/crossplane-runtime/apis/common/v1" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + xkube "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" + "reflect" + controllerruntime "sigs.k8s.io/controller-runtime" + "strings" +) + +const billingLabel = "appcat.io/billing" + +// InjectBillingLabelToService adds billing label to a service (StatefulSet or Deployment). +// It uses a kube Object to achieve post provisioning labelling +func InjectBillingLabelToService(ctx context.Context, svc *runtime.ServiceRuntime, comp InfoGetter) *xfnproto.Result { + log := controllerruntime.LoggerFrom(ctx) + log.Info("Enabling billing for service", "service", comp.GetName()) + + s := comp.GetWorkloadPodTemplateLabelsManager() + s.SetName(comp.GetWorkloadName()) + s.SetNamespace(comp.GetInstanceNamespace()) + kubeName := comp.GetName() + "-" + getType(s) + + _ = svc.GetObservedKubeObject(s, kubeName) + mp := v12.ManagementPolicies{v12.ManagementActionObserve} + labels := s.GetPodTemplateLabels() + _, exists := labels[billingLabel] + if !s.GetCreationTimestamp().Time.IsZero() { + if !exists { + labels[billingLabel] = "true" + s.SetPodTemplateLabels(labels) + mp = append(mp, v12.ManagementActionCreate, v12.ManagementActionUpdate) + } + } + + err := svc.SetDesiredKubeObject(s.GetObject(), kubeName, func(obj *xkube.Object) { + obj.Spec.ManagementPolicies = mp + }) + + if err != nil && !exists { + runtime.NewWarningResult(fmt.Sprintf("cannot add billing to service object %s", s.GetName())) + } + + return runtime.NewNormalResult("billing enabled") +} + +func getType(myvar interface{}) (res string) { + return strings.ToLower(reflect.TypeOf(myvar).Elem().Field(0).Name) +} diff --git a/pkg/comp-functions/functions/common/interfaces.go b/pkg/comp-functions/functions/common/interfaces.go index d2e193e3f..31ba70c5f 100644 --- a/pkg/comp-functions/functions/common/interfaces.go +++ b/pkg/comp-functions/functions/common/interfaces.go @@ -18,6 +18,8 @@ type InfoGetter interface { GetSecurity() *vshnv1.Security InstanceNamespaceInfo GetPDBLabels() map[string]string + GetWorkloadPodTemplateLabelsManager() vshnv1.PodTemplateLabelsManager + GetWorkloadName() string } // InstanceNamespaceInfo provides all the necessary information to create diff --git a/pkg/comp-functions/functions/vshnkeycloak/billing.go b/pkg/comp-functions/functions/vshnkeycloak/billing.go new file mode 100644 index 000000000..0d81ee5d7 --- /dev/null +++ b/pkg/comp-functions/functions/vshnkeycloak/billing.go @@ -0,0 +1,20 @@ +package vshnkeycloak + +import ( + "context" + "fmt" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + v1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" +) + +// AddServiceBillingLabel adds billingLabel to all the objects of a services that must be billed +func AddServiceBillingLabel(ctx context.Context, comp *v1.VSHNKeycloak, svc *runtime.ServiceRuntime) *xfnproto.Result { + err := svc.GetObservedComposite(comp) + if err != nil { + return runtime.NewFatalResult(fmt.Errorf("can't get composite: %w", err)) + } + + return common.InjectBillingLabelToService(ctx, svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnkeycloak/register.go b/pkg/comp-functions/functions/vshnkeycloak/register.go index 33027c2ad..695fa6cd1 100644 --- a/pkg/comp-functions/functions/vshnkeycloak/register.go +++ b/pkg/comp-functions/functions/vshnkeycloak/register.go @@ -39,6 +39,10 @@ func init() { Name: "pdb", Execute: common.AddPDBSettings[*vshnv1.VSHNKeycloak], }, + { + Name: "billing", + Execute: AddServiceBillingLabel, + }, }, }) } diff --git a/pkg/comp-functions/functions/vshnmariadb/billing.go b/pkg/comp-functions/functions/vshnmariadb/billing.go new file mode 100644 index 000000000..ed50df386 --- /dev/null +++ b/pkg/comp-functions/functions/vshnmariadb/billing.go @@ -0,0 +1,20 @@ +package vshnmariadb + +import ( + "context" + "fmt" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + v1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" +) + +// AddServiceBillingLabel adds billingLabel to all the objects of a services that must be billed +func AddServiceBillingLabel(ctx context.Context, comp *v1.VSHNMariaDB, svc *runtime.ServiceRuntime) *xfnproto.Result { + err := svc.GetObservedComposite(comp) + if err != nil { + return runtime.NewFatalResult(fmt.Errorf("can't get composite: %w", err)) + } + + return common.InjectBillingLabelToService(ctx, svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnmariadb/register.go b/pkg/comp-functions/functions/vshnmariadb/register.go index ff8dca2cf..4fe1f57ec 100644 --- a/pkg/comp-functions/functions/vshnmariadb/register.go +++ b/pkg/comp-functions/functions/vshnmariadb/register.go @@ -35,6 +35,14 @@ func init() { Name: "non-sla-prometheus-rules", Execute: nonsla.GenerateNonSLAPromRules[*vshnv1.VSHNMariaDB](nonsla.NewAlertSetBuilder("mariadb", "mariadb").AddAll().GetAlerts()), }, + { + Name: "billing", + Execute: AddServiceBillingLabel, + }, + { + Name: "user-management", + Execute: UserManagement, + }, }, }) } diff --git a/pkg/comp-functions/functions/vshnmariadb/user_management.go b/pkg/comp-functions/functions/vshnmariadb/user_management.go new file mode 100644 index 000000000..d4dc46ada --- /dev/null +++ b/pkg/comp-functions/functions/vshnmariadb/user_management.go @@ -0,0 +1,265 @@ +package vshnmariadb + +import ( + "context" + "fmt" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + my1alpha1 "github.com/vshn/appcat/v4/apis/sql/mysql/v1alpha1" + vshnv1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func UserManagement(ctx context.Context, comp *vshnv1.VSHNMariaDB, svc *runtime.ServiceRuntime) *xfnproto.Result { + + err := svc.GetDesiredComposite(comp) + if err != nil { + return runtime.NewFatalResult(fmt.Errorf("Cannot get composite from function io: %w", err)) + } + + // Nothing defined, let's return early + if len(comp.Spec.Parameters.Service.Access) == 0 { + return nil + } + + addProviderConfig(comp, svc) + + for _, access := range comp.Spec.Parameters.Service.Access { + + userPasswordRef := addUser(comp, svc, *access.User) + + dbname := *access.User + if access.Database != nil { + dbname = *access.Database + } + + addDatabase(comp, svc, dbname) + + addGrants(comp, svc, *access.User, dbname, access.Privileges) + + addConnectionDetail(comp, svc, userPasswordRef, *access.User, dbname, access.WriteConnectionSecretToReference) + } + + return nil +} + +func addUser(comp common.Composite, svc *runtime.ServiceRuntime, username string) string { + secretName, err := common.AddGenericSecret(comp, svc, "userpass-"+username, []string{"userpass"}) + if err != nil { + svc.Log.Error(err, "cannot deploy user password secret") + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot deploy user password secret: %s", err))) + } + + role := &my1alpha1.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s-role", comp.GetName(), username), + Annotations: map[string]string{ + "crossplane.io/external-name": username, + }, + Labels: map[string]string{ + runtime.ProviderConfigIgnoreLabel: "true", + }, + }, + Spec: my1alpha1.UserSpec{ + ForProvider: my1alpha1.UserParameters{}, + ResourceSpec: xpv1.ResourceSpec{ + ProviderConfigReference: &xpv1.Reference{ + Name: comp.GetName(), + }, + }, + }, + } + + err = svc.SetDesiredComposedResource(role, runtime.ComposedOptionProtects(comp.GetName()+"-provider-conf-credentials"), runtime.ComposedOptionProtects(secretName)) + if err != nil { + svc.Log.Error(err, "cannot apply user") + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot apply user: %s", err))) + } + + return secretName +} + +func addConnectionDetail(comp common.Composite, svc *runtime.ServiceRuntime, secretName, username, dbname string, connectionDetailRef *xpv1.SecretReference) { + userpassCD, err := svc.GetObservedComposedResourceConnectionDetails(secretName) + if err != nil { + svc.Log.Error(err, "cannot get userpassword from secret") + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot get userpassword from secret: %s", err))) + } + + compositeCD := svc.GetConnectionDetails() + + url := fmt.Sprintf("mysql://%s:%s@%s:%s", username, userpassCD["userpass"], compositeCD["MARIADB_HOST"], compositeCD["MARIADB_PORT"]) + + om := metav1.ObjectMeta{ + Name: comp.GetLabels()["crossplane.io/claim-name"] + "-" + username, + Namespace: comp.GetClaimNamespace(), + } + if connectionDetailRef != nil { + om.Name = connectionDetailRef.Name + om.Namespace = connectionDetailRef.Namespace + } + + userpassSecret := &corev1.Secret{ + ObjectMeta: om, + Type: corev1.SecretType("connection.crossplane.io/v1alpha1"), + Data: map[string][]byte{ + "MARIADB_USERNAME": []byte(username), + "MARIADB_PASSWORD": userpassCD["userpass"], + "MARIADB_DB": []byte(dbname), + "MARIADB_HOST": compositeCD["MARIADB_HOST"], + "MARIADB_PORT": compositeCD["MARIADB_PORT"], + "MARIADB_URL": []byte(url), + "ca.crt": compositeCD["ca.crt"], + }, + } + + err = svc.SetDesiredKubeObject(userpassSecret, fmt.Sprintf("%s-user-%s", comp.GetName(), username)) + if err != nil { + svc.Log.Error(err, "cannot get userpassword from secret") + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot get userpassword from secret: %s", err))) + } +} + +func addProviderConfig(comp *vshnv1.VSHNMariaDB, svc *runtime.ServiceRuntime) { + cd := svc.GetConnectionDetails() + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "provider-conf-credentials", + Namespace: comp.GetInstanceNamespace(), + }, + Data: map[string][]byte{ + "username": cd["MARIADB_USERNAME"], + "password": cd["MARIADB_PASSWORD"], + "endpoint": cd["MARIADB_HOST"], + "port": cd["MARIADB_PORT"], + }, + } + + err := svc.SetDesiredKubeObject(secret, comp.GetName()+"-provider-conf-credentials", + runtime.KubeOptionProtects("namespace-conditions"), + runtime.KubeOptionProtects("cluster"), + runtime.KubeOptionProtects(comp.GetName()+"-netpol")) + if err != nil { + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot set credential secret for provider-sql: %s", err))) + svc.Log.Error(err, "cannot set credential secret for provider-sql") + } + + tls := "preferred" + if comp.Spec.Parameters.TLS.TLSEnabled { + // We need to skip-verify here as with the postgresql. + // provider-sql does not support custom CA certs. + tls = "skip-verify" + } + + config := &my1alpha1.ProviderConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: comp.GetName(), + }, + Spec: my1alpha1.ProviderConfigSpec{ + TLS: &tls, + Credentials: my1alpha1.ProviderCredentials{ + Source: "MySQLConnectionSecret", + ConnectionSecretRef: &xpv1.SecretReference{ + Name: "provider-conf-credentials", + Namespace: comp.GetInstanceNamespace(), + }, + }, + }, + } + + err = svc.SetDesiredKubeObject(config, comp.GetName()+"-providerconfig") + if err != nil { + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot apply the provider config for provider sql: %s", err))) + svc.Log.Error(err, "cannot apply the provider config for provider sql") + } +} + +// We check if the database is already specified. +// If not it will be added. +// This should handle cases where there are mutliple users pointing to the same +// database, and one is deleted, that the database is not dropped. +func addDatabase(comp common.Composite, svc *runtime.ServiceRuntime, name string) { + resname := fmt.Sprintf("%s-%s-database", comp.GetName(), name) + + xdb := &my1alpha1.Database{} + + // If there's a database with the same name we will just return + err := svc.GetDesiredComposedResourceByName(xdb, resname) + if err == nil { + return + } + if err != runtime.ErrNotFound { + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot check if database exists: %s", err))) + svc.Log.Error(err, "cannot check if database exists") + } + + xdb = &my1alpha1.Database{ + ObjectMeta: metav1.ObjectMeta{ + Name: resname, + Annotations: map[string]string{ + "crossplane.io/external-name": name, + }, + Labels: map[string]string{ + runtime.ProviderConfigIgnoreLabel: "true", + }, + }, + Spec: my1alpha1.DatabaseSpec{ + ForProvider: my1alpha1.DatabaseParameters{}, + ResourceSpec: xpv1.ResourceSpec{ + ProviderConfigReference: &xpv1.Reference{ + Name: comp.GetName(), + }, + }, + }, + } + + err = svc.SetDesiredComposedResource(xdb, runtime.ComposedOptionProtects(comp.GetName()+"-provider-conf-credentials")) + if err != nil { + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot apply database: %s", err))) + svc.Log.Error(err, "cannot apply database") + } +} + +func addGrants(comp common.Composite, svc *runtime.ServiceRuntime, username, dbname string, privileges []string) { + privs := []my1alpha1.GrantPrivilege{} + + if len(privileges) == 0 { + privs = append(privs, "ALL") + } + + for _, priv := range privileges { + privs = append(privs, my1alpha1.GrantPrivilege(priv)) + } + + grant := &my1alpha1.Grant{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s-%s-grants", comp.GetName(), username, dbname), + Labels: map[string]string{ + runtime.ProviderConfigIgnoreLabel: "true", + }, + }, + Spec: my1alpha1.GrantSpec{ + ForProvider: my1alpha1.GrantParameters{ + Privileges: privs, + User: &username, + Database: &dbname, + }, + ResourceSpec: xpv1.ResourceSpec{ + ProviderConfigReference: &xpv1.Reference{ + Name: comp.GetName(), + }, + }, + }, + } + + err := svc.SetDesiredComposedResource(grant, runtime.ComposedOptionProtects(comp.GetName()+"-provider-conf-credentials")) + if err != nil { + svc.AddResult(runtime.NewWarningResult(fmt.Sprintf("cannot apply database: %s", err))) + svc.Log.Error(err, "cannot apply database") + } +} diff --git a/pkg/comp-functions/functions/vshnmariadb/user_management_test.go b/pkg/comp-functions/functions/vshnmariadb/user_management_test.go new file mode 100644 index 000000000..127189149 --- /dev/null +++ b/pkg/comp-functions/functions/vshnmariadb/user_management_test.go @@ -0,0 +1,136 @@ +package vshnmariadb + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + my1alpha1 "github.com/vshn/appcat/v4/apis/sql/mysql/v1alpha1" + pgv1alpha1 "github.com/vshn/appcat/v4/apis/sql/postgresql/v1alpha1" + vshnv1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/commontest" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" +) + +func Test_addProviderConfig(t *testing.T) { + // given + svc := commontest.LoadRuntimeFromFile(t, "vshnmariadb/usermanagement/01-emptyaccess.yaml") + + // when + comp := &vshnv1.VSHNMariaDB{} + assert.NoError(t, svc.GetObservedComposite(comp)) + addProviderConfig(comp, svc) + + // then + secret := &corev1.Secret{} + assert.NoError(t, svc.GetDesiredKubeObject(secret, comp.GetName()+"-provider-conf-credentials")) + + config := &pgv1alpha1.ProviderConfig{} + assert.NoError(t, svc.GetDesiredKubeObject(config, comp.GetName()+"-providerconfig")) + assert.Equal(t, comp.GetInstanceNamespace(), secret.GetNamespace()) + +} + +func Test_addUser(t *testing.T) { + // given + svc := commontest.LoadRuntimeFromFile(t, "vshnmariadb/usermanagement/01-emptyaccess.yaml") + + // when + comp := &vshnv1.VSHNMariaDB{} + assert.NoError(t, svc.GetObservedComposite(comp)) + + addUser(comp, svc, "unit") + + // then + role := &pgv1alpha1.Role{} + assert.NoError(t, svc.GetDesiredComposedResourceByName(role, fmt.Sprintf("%s-%s-role", comp.GetName(), "unit"))) + +} + +func Test_addDatabase(t *testing.T) { + // given + svc := commontest.LoadRuntimeFromFile(t, "vshnmariadb/usermanagement/01-emptyaccess.yaml") + + // when + comp := &vshnv1.VSHNMariaDB{} + assert.NoError(t, svc.GetObservedComposite(comp)) + + addDatabase(comp, svc, "unit") + + // then + db := &my1alpha1.Database{} + assert.NoError(t, svc.GetDesiredComposedResourceByName(db, fmt.Sprintf("%s-%s-database", comp.GetName(), "unit"))) + +} + +func Test_addGrants(t *testing.T) { + // given + svc := commontest.LoadRuntimeFromFile(t, "vshnmariadb/usermanagement/01-emptyaccess.yaml") + + // when + comp := &vshnv1.VSHNMariaDB{} + assert.NoError(t, svc.GetObservedComposite(comp)) + + addGrants(comp, svc, "unit", "unit", []string{"ALL"}) + + // then + grant := &my1alpha1.Grant{} + assert.NoError(t, svc.GetDesiredComposedResourceByName(grant, fmt.Sprintf("%s-%s-%s-grants", comp.GetName(), "unit", "unit"))) + +} + +func TestUserManagement(t *testing.T) { + // given with empty accesss object + svc := commontest.LoadRuntimeFromFile(t, "vshnmariadb/usermanagement/01-emptyaccess.yaml") + + // when applied + assert.Nil(t, UserManagement(context.TODO(), &vshnv1.VSHNMariaDB{}, svc)) + + // then expect no database + comp := &vshnv1.VSHNMariaDB{} + assert.NoError(t, svc.GetObservedComposite(comp)) + + db := &my1alpha1.Database{} + assert.Error(t, svc.GetDesiredComposedResourceByName(db, fmt.Sprintf("%s-%s-database", comp.GetName(), "prod"))) + + // when adding an user + comp.Spec.Parameters.Service.Access = []vshnv1.VSHNAccess{ + { + User: ptr.To("prod"), + }, + } + + assert.NoError(t, svc.SetDesiredCompositeStatus(comp)) + assert.Nil(t, UserManagement(context.TODO(), &vshnv1.VSHNMariaDB{}, svc)) + + // then expect database + db = &my1alpha1.Database{} + assert.NoError(t, svc.GetDesiredComposedResourceByName(db, fmt.Sprintf("%s-%s-database", comp.GetName(), "prod"))) + + // when adding user pointing to same db + comp.Spec.Parameters.Service.Access = append(comp.Spec.Parameters.Service.Access, vshnv1.VSHNAccess{ + User: ptr.To("another"), + Database: ptr.To("prod"), + }) + + assert.NoError(t, svc.SetDesiredCompositeStatus(comp)) + assert.Nil(t, UserManagement(context.TODO(), &vshnv1.VSHNMariaDB{}, svc)) + + // then expect database + db = &my1alpha1.Database{} + assert.NoError(t, svc.GetDesiredComposedResourceByName(db, fmt.Sprintf("%s-%s-database", comp.GetName(), "prod"))) + + // When deleting user pointing to same db + comp.Spec.Parameters.Service.Access = []vshnv1.VSHNAccess{ + { + User: ptr.To("another"), + Database: ptr.To("prod"), + }, + } + + // then expect database + db = &my1alpha1.Database{} + assert.NoError(t, svc.GetDesiredComposedResourceByName(db, fmt.Sprintf("%s-%s-database", comp.GetName(), "prod"))) +} diff --git a/pkg/comp-functions/functions/vshnminio/billing.go b/pkg/comp-functions/functions/vshnminio/billing.go new file mode 100644 index 000000000..feefc28e6 --- /dev/null +++ b/pkg/comp-functions/functions/vshnminio/billing.go @@ -0,0 +1,20 @@ +package vshnminio + +import ( + "context" + "fmt" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + v1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" +) + +// AddServiceBillingLabel adds billingLabel to all the objects of a services that must be billed +func AddServiceBillingLabel(ctx context.Context, comp *v1.VSHNMinio, svc *runtime.ServiceRuntime) *xfnproto.Result { + err := svc.GetObservedComposite(comp) + if err != nil { + return runtime.NewFatalResult(fmt.Errorf("can't get composite: %w", err)) + } + + return common.InjectBillingLabelToService(ctx, svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnminio/register.go b/pkg/comp-functions/functions/vshnminio/register.go index e5bbf3b90..4ebf7ddd5 100644 --- a/pkg/comp-functions/functions/vshnminio/register.go +++ b/pkg/comp-functions/functions/vshnminio/register.go @@ -43,6 +43,10 @@ func init() { Name: "pdb", Execute: common.AddPDBSettings[*vshnv1.VSHNMinio], }, + { + Name: "billing", + Execute: AddServiceBillingLabel, + }, }, }) } diff --git a/pkg/comp-functions/functions/vshnnextcloud/billing.go b/pkg/comp-functions/functions/vshnnextcloud/billing.go new file mode 100644 index 000000000..0fe1d7249 --- /dev/null +++ b/pkg/comp-functions/functions/vshnnextcloud/billing.go @@ -0,0 +1,20 @@ +package vshnnextcloud + +import ( + "context" + "fmt" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + v1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" +) + +// AddServiceBillingLabel adds billingLabel to all the objects of a services that must be billed +func AddServiceBillingLabel(ctx context.Context, comp *v1.VSHNNextcloud, svc *runtime.ServiceRuntime) *xfnproto.Result { + err := svc.GetObservedComposite(comp) + if err != nil { + return runtime.NewFatalResult(fmt.Errorf("can't get composite: %w", err)) + } + + return common.InjectBillingLabelToService(ctx, svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnnextcloud/register.go b/pkg/comp-functions/functions/vshnnextcloud/register.go index fdd71ad05..a25f275cb 100644 --- a/pkg/comp-functions/functions/vshnnextcloud/register.go +++ b/pkg/comp-functions/functions/vshnnextcloud/register.go @@ -29,6 +29,10 @@ func init() { Name: "collabora", Execute: DeployCollabora, }, + { + Name: "billing", + Execute: AddServiceBillingLabel, + }, }, }) } diff --git a/pkg/comp-functions/functions/vshnpostgres/billing.go b/pkg/comp-functions/functions/vshnpostgres/billing.go new file mode 100644 index 000000000..40efc4708 --- /dev/null +++ b/pkg/comp-functions/functions/vshnpostgres/billing.go @@ -0,0 +1,20 @@ +package vshnpostgres + +import ( + "context" + "fmt" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + v1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" +) + +// AddServiceBillingLabel adds billingLabel to all the objects of a services that must be billed +func AddServiceBillingLabel(ctx context.Context, comp *v1.VSHNPostgreSQL, svc *runtime.ServiceRuntime) *xfnproto.Result { + err := svc.GetObservedComposite(comp) + if err != nil { + return runtime.NewFatalResult(fmt.Errorf("can't get composite: %w", err)) + } + + return common.InjectBillingLabelToService(ctx, svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnpostgres/register.go b/pkg/comp-functions/functions/vshnpostgres/register.go index 1fd323ffc..d0d9ce6bf 100644 --- a/pkg/comp-functions/functions/vshnpostgres/register.go +++ b/pkg/comp-functions/functions/vshnpostgres/register.go @@ -81,6 +81,10 @@ func init() { Name: "pdb", Execute: common.AddPDBSettings[*vshnv1.VSHNPostgreSQL], }, + { + Name: "billing", + Execute: AddServiceBillingLabel, + }, }, }) } diff --git a/pkg/comp-functions/functions/vshnpostgres/user_management.go b/pkg/comp-functions/functions/vshnpostgres/user_management.go index 532bb85a4..3510cb5da 100644 --- a/pkg/comp-functions/functions/vshnpostgres/user_management.go +++ b/pkg/comp-functions/functions/vshnpostgres/user_management.go @@ -22,7 +22,7 @@ func UserManagement(ctx context.Context, comp *vshnv1.VSHNPostgreSQL, svc *runti } // Nothing defined, let's return early - if comp.Spec.Parameters.Service.Access == nil || len(comp.Spec.Parameters.Service.Access) == 0 { + if len(comp.Spec.Parameters.Service.Access) == 0 { return nil } @@ -47,7 +47,7 @@ func UserManagement(ctx context.Context, comp *vshnv1.VSHNPostgreSQL, svc *runti return nil } -func addUser(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime, username string) string { +func addUser(comp common.Composite, svc *runtime.ServiceRuntime, username string) string { secretName, err := common.AddGenericSecret(comp, svc, "userpass-"+username, []string{"userpass"}) if err != nil { svc.Log.Error(err, "cannot deploy user password secret") @@ -72,7 +72,7 @@ func addUser(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime, username PasswordSecretRef: &xpv1.SecretKeySelector{ SecretReference: xpv1.SecretReference{ Name: secretName, - Namespace: comp.Status.InstanceNamespace, + Namespace: comp.GetInstanceNamespace(), }, Key: "userpass", }, @@ -94,7 +94,7 @@ func addUser(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime, username return secretName } -func addConnectionDetail(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime, secretName, username, dbname string, connectionDetailRef *xpv1.SecretReference) { +func addConnectionDetail(comp common.Composite, svc *runtime.ServiceRuntime, secretName, username, dbname string, connectionDetailRef *xpv1.SecretReference) { userpassCD, err := svc.GetObservedComposedResourceConnectionDetails(secretName) if err != nil { svc.Log.Error(err, "cannot get userpassword from secret") @@ -137,7 +137,7 @@ func addConnectionDetail(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntim } } -func addProviderConfig(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime) { +func addProviderConfig(comp common.Composite, svc *runtime.ServiceRuntime) { cd := svc.GetConnectionDetails() secret := &corev1.Secret{ @@ -191,7 +191,7 @@ func addProviderConfig(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime) // If not it will be added. // This should handle cases where there are mutliple users pointing to the same // database, and one is deleted, that the database is not dropped. -func addDatabase(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime, name string) { +func addDatabase(comp common.Composite, svc *runtime.ServiceRuntime, name string) { resname := fmt.Sprintf("%s-%s-database", comp.GetName(), name) xdb := &pgv1alpha1.Database{} @@ -233,7 +233,7 @@ func addDatabase(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime, name } } -func addGrants(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime, username, dbname string, privileges []string) { +func addGrants(comp common.Composite, svc *runtime.ServiceRuntime, username, dbname string, privileges []string) { privs := []pgv1alpha1.GrantPrivilege{} if len(privileges) == 0 { diff --git a/pkg/comp-functions/functions/vshnredis/backup.go b/pkg/comp-functions/functions/vshnredis/backup.go index 9a578a1d1..393b61957 100644 --- a/pkg/comp-functions/functions/vshnredis/backup.go +++ b/pkg/comp-functions/functions/vshnredis/backup.go @@ -45,7 +45,7 @@ func AddBackup(ctx context.Context, comp *vshnv1.VSHNRedis, svc *runtime.Service } l.Info("Creating backup config map") - err = createScriptCM(ctx, comp, svc) + err = createScriptCM(comp, svc) if err != nil { return runtime.NewFatalResult(fmt.Errorf("cannot create backup config map: %w", err)) } @@ -53,7 +53,7 @@ func AddBackup(ctx context.Context, comp *vshnv1.VSHNRedis, svc *runtime.Service return nil } -func createScriptCM(ctx context.Context, comp *vshnv1.VSHNRedis, svc *runtime.ServiceRuntime) error { +func createScriptCM(comp *vshnv1.VSHNRedis, svc *runtime.ServiceRuntime) error { cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/comp-functions/functions/vshnredis/billing.go b/pkg/comp-functions/functions/vshnredis/billing.go new file mode 100644 index 000000000..af57a4681 --- /dev/null +++ b/pkg/comp-functions/functions/vshnredis/billing.go @@ -0,0 +1,20 @@ +package vshnredis + +import ( + "context" + "fmt" + xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1" + v1 "github.com/vshn/appcat/v4/apis/vshn/v1" + "github.com/vshn/appcat/v4/pkg/comp-functions/functions/common" + "github.com/vshn/appcat/v4/pkg/comp-functions/runtime" +) + +// AddServiceBillingLabel adds billingLabel to all the objects of a services that must be billed +func AddServiceBillingLabel(ctx context.Context, comp *v1.VSHNRedis, svc *runtime.ServiceRuntime) *xfnproto.Result { + err := svc.GetObservedComposite(comp) + if err != nil { + return runtime.NewFatalResult(fmt.Errorf("can't get composite: %w", err)) + } + + return common.InjectBillingLabelToService(ctx, svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnredis/register.go b/pkg/comp-functions/functions/vshnredis/register.go index 7fa695302..79aa603dd 100644 --- a/pkg/comp-functions/functions/vshnredis/register.go +++ b/pkg/comp-functions/functions/vshnredis/register.go @@ -50,6 +50,10 @@ func init() { Name: "non-sla-prometheus-rules", Execute: nonsla.GenerateNonSLAPromRules[*vshnv1.VSHNRedis](nonsla.NewAlertSetBuilder("redis", "redis").AddAll().GetAlerts()), }, + { + Name: "billing", + Execute: AddServiceBillingLabel, + }, }, }) } diff --git a/pkg/comp-functions/functions/vshnredis/script/backup.sh b/pkg/comp-functions/functions/vshnredis/script/backup.sh index d7470bd1c..2ee5181ec 100644 --- a/pkg/comp-functions/functions/vshnredis/script/backup.sh +++ b/pkg/comp-functions/functions/vshnredis/script/backup.sh @@ -5,7 +5,11 @@ # it will also backup that one. redis_cmd() { - redis-cli -a "${REDIS_PASSWORD}" --cacert "${REDIS_TLS_CA_FILE}" --cert "${REDIS_TLS_CERT_FILE}" --key "${REDIS_TLS_KEY_FILE}" --tls "$@" + if [ -z "$REDIS_TLS_CERT_FILE" ]; then + redis-cli -a "${REDIS_PASSWORD}" "$@" + else + redis-cli -a "${REDIS_PASSWORD}" --cacert "${REDIS_TLS_CA_FILE}" --cert "${REDIS_TLS_CERT_FILE}" --key "${REDIS_TLS_KEY_FILE}" --tls "$@" + fi } rewrite_percentage=$(redis_cmd --raw CONFIG GET auto-aof-rewrite-percentage | tail -n 1) 1>&2 diff --git a/pkg/scheme.go b/pkg/scheme.go index 8fc92c000..7499ac06b 100644 --- a/pkg/scheme.go +++ b/pkg/scheme.go @@ -11,6 +11,7 @@ import ( promv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" alertmanagerv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" xkube "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" + my1alpha1 "github.com/vshn/appcat/v4/apis/sql/mysql/v1alpha1" pgv1alpha1 "github.com/vshn/appcat/v4/apis/sql/postgresql/v1alpha1" stackgresv1 "github.com/vshn/appcat/v4/apis/stackgres/v1" stackgresv1beta1 "github.com/vshn/appcat/v4/apis/stackgres/v1beta1" @@ -62,4 +63,5 @@ func AddToScheme(s *runtime.Scheme) { _ = cloudscalev1.SchemeBuilder.AddToScheme(s) _ = exoscalev1.SchemeBuilder.AddToScheme(s) _ = spksv1alpha1.SchemeBuilder.AddToScheme(s) + _ = my1alpha1.SchemeBuilder.AddToScheme(s) } diff --git a/test/functions/vshnmariadb/usermanagement/01-emptyaccess.yaml b/test/functions/vshnmariadb/usermanagement/01-emptyaccess.yaml new file mode 100644 index 000000000..fbce0fe64 --- /dev/null +++ b/test/functions/vshnmariadb/usermanagement/01-emptyaccess.yaml @@ -0,0 +1,32 @@ +desired: + composite: + connection_details: + MARIADB_USER: cm9vdA== #root + MARIADB_PASSWORD: cm9vdA== #root + MARIADB_HOST: bG9jYWxob3N0IC1uCg== #localhost + MARIADB_PORT: NTQzMgo= + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNMariaDB + metadata: + name: my-gc9x4 + spec: + parameters: null + writeConnectionSecretToRef: {} + status: {} +observed: + composite: + connection_details: + MARIADB_USER: cm9vdA== #root + MARIADB_PASSWORD: cm9vdA== #root + MARIADB_HOST: bG9jYWxob3N0IC1uCg== #localhost + MARIADB_PORT: NTQzMgo= + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNMariaDB + metadata: + name: my-gc9x4 + spec: + parameters: null + writeConnectionSecretToRef: {} + status: {}