diff --git a/apis/vshn/v1/dbaas_vshn_keycloak.go b/apis/vshn/v1/dbaas_vshn_keycloak.go index 7bf6a772a..ececbc9b2 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..643af44ed 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" ) @@ -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..c3480ade3 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" @@ -397,3 +396,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 dc87debc0..4b846a49a 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" ) @@ -304,3 +303,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/pkg/comp-functions/functions/common/billing.go b/pkg/comp-functions/functions/common/billing.go new file mode 100644 index 000000000..3819f3b1b --- /dev/null +++ b/pkg/comp-functions/functions/common/billing.go @@ -0,0 +1,49 @@ +package common + +import ( + "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" + "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 +// name is the name of the Deployment or StatefulSet that the release is using +func InjectBillingLabelToService(svc *runtime.ServiceRuntime, comp InfoGetter) *xfnproto.Result { + s := comp.GetWorkloadPodTemplateLabelsManager() + s.SetName(comp.GetWorkloadName()) + s.SetNamespace(comp.GetInstanceNamespace()) + kubeName := s.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..5f7394b0a --- /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(_ 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(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..817b601ea --- /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(_ 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(svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnmariadb/register.go b/pkg/comp-functions/functions/vshnmariadb/register.go index ff8dca2cf..a1deaeeec 100644 --- a/pkg/comp-functions/functions/vshnmariadb/register.go +++ b/pkg/comp-functions/functions/vshnmariadb/register.go @@ -35,6 +35,10 @@ func init() { Name: "non-sla-prometheus-rules", Execute: nonsla.GenerateNonSLAPromRules[*vshnv1.VSHNMariaDB](nonsla.NewAlertSetBuilder("mariadb", "mariadb").AddAll().GetAlerts()), }, + { + Name: "billing", + Execute: AddServiceBillingLabel, + }, }, }) } diff --git a/pkg/comp-functions/functions/vshnminio/billing.go b/pkg/comp-functions/functions/vshnminio/billing.go new file mode 100644 index 000000000..2d4988da1 --- /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(_ 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(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..b2b70e485 --- /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(_ 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(svc, comp) +} diff --git a/pkg/comp-functions/functions/vshnnextcloud/register.go b/pkg/comp-functions/functions/vshnnextcloud/register.go index f784bfce2..180d4758f 100644 --- a/pkg/comp-functions/functions/vshnnextcloud/register.go +++ b/pkg/comp-functions/functions/vshnnextcloud/register.go @@ -25,6 +25,10 @@ func init() { Name: "backup", Execute: AddBackup, }, + { + 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..6726e7d20 --- /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(_ 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(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/vshnredis/billing.go b/pkg/comp-functions/functions/vshnredis/billing.go new file mode 100644 index 000000000..2fe94058b --- /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(_ 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(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, + }, }, }) }