Skip to content

Commit

Permalink
feat: add new kind AlloyDB Omni
Browse files Browse the repository at this point in the history
  • Loading branch information
rriski committed Nov 15, 2024
1 parent 474a2c6 commit 44b3205
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
minimum ~~`1`~~`0`
- Change `OpenSearch` field `userConfig.opensearch.auth_failure_listeners.ip_rate_limiting.time_window_seconds`:
minimum ~~`1`~~`0`
- Add kind: `AlloyDBOmni`

## v0.25.0 - 2024-09-19

Expand Down
8 changes: 8 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ plugins:
projectName: aiven-operator
repo: github.com/aiven/aiven-operator
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: aiven.io
kind: AlloyDBOmni
path: github.com/aiven/aiven-operator/api/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
Expand Down
70 changes: 70 additions & 0 deletions api/v1alpha1/alloydbomni_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) 2024 Aiven, Helsinki, Finland. https://aiven.io/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

alloydbomni "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/service/alloydbomni"
)

// AlloyDBOmniSpec defines the desired state of AlloyDB Omni instance
type AlloyDBOmniSpec struct {
ServiceCommonSpec `json:",inline"`

// AlloyDBOmni specific user configuration options
UserConfig *alloydbomni.AlloydbomniUserConfig `json:"userConfig,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// AlloyDBOmni is the Schema for the alloydbomni API.
// Info "Exposes secret keys": `ALLOYDBOMNI_HOST`, `ALLOYDBOMNI_PORT`, `ALLOYDBOMNI_DATABASE`, `ALLOYDBOMNI_USER`, `ALLOYDBOMNI_PASSWORD`, `ALLOYDBOMNI_SSLMODE`, `ALLOYDBOMNI_DATABASE_URI`
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Project",type="string",JSONPath=".spec.project"
// +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.cloudName"
// +kubebuilder:printcolumn:name="Plan",type="string",JSONPath=".spec.plan"
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state"
type AlloyDBOmni struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec AlloyDBOmniSpec `json:"spec,omitempty"`
Status ServiceStatus `json:"status,omitempty"`
}

var _ AivenManagedObject = &AlloyDBOmni{}

func (in *AlloyDBOmni) NoSecret() bool {
return in.Spec.ConnInfoSecretTargetDisabled != nil && *in.Spec.ConnInfoSecretTargetDisabled
}

func (in *AlloyDBOmni) AuthSecretRef() *AuthSecretReference {
return in.Spec.AuthSecretRef
}

func (in *AlloyDBOmni) Conditions() *[]metav1.Condition {
return &in.Status.Conditions
}

func (in *AlloyDBOmni) GetRefs() []*ResourceReferenceObject {
return in.Spec.GetRefs(in.GetNamespace())
}

func (in *AlloyDBOmni) GetConnInfoSecretTarget() ConnInfoSecretTarget {
return in.Spec.ConnInfoSecretTarget
}

// +kubebuilder:object:root=true

// AlloyDBOmniList contains a list of AlloyDBOmni instances
type AlloyDBOmniList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []AlloyDBOmni `json:"items"`
}

func init() {
SchemeBuilder.Register(&AlloyDBOmni{}, &AlloyDBOmniList{})
}
62 changes: 62 additions & 0 deletions api/v1alpha1/alloydbomni_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) 2024 Aiven, Helsinki, Finland. https://aiven.io/

package v1alpha1

import (
"errors"

"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 alloydbomnilog = logf.Log.WithName("alloydbomni-resource")

func (in *AlloyDBOmni) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(in).
Complete()
}

//+kubebuilder:webhook:path=/mutate-aiven-io-v1alpha1-alloydbomni,mutating=true,failurePolicy=fail,groups=aiven.io,resources=alloydbomnis,verbs=create;update,versions=v1alpha1,name=mpg.kb.io,sideEffects=none,admissionReviewVersions=v1

var _ webhook.Defaulter = &AlloyDBOmni{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (in *AlloyDBOmni) Default() {
alloydbomnilog.Info("default", "name", in.Name)
}

//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-aiven-io-v1alpha1-alloydbomni,mutating=false,failurePolicy=fail,groups=aiven.io,resources=alloydbomnis,versions=v1alpha1,name=vpg.kb.io,sideEffects=none,admissionReviewVersions=v1

var _ webhook.Validator = &AlloyDBOmni{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (in *AlloyDBOmni) ValidateCreate() error {
alloydbomnilog.Info("validate create", "name", in.Name)

return in.Spec.Validate()
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (in *AlloyDBOmni) ValidateUpdate(old runtime.Object) error {
alloydbomnilog.Info("validate update", "name", in.Name)
return in.Spec.Validate()
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (in *AlloyDBOmni) ValidateDelete() error {
alloydbomnilog.Info("validate delete", "name", in.Name)

if in.Spec.TerminationProtection != nil && *in.Spec.TerminationProtection {
return errors.New("cannot delete AlloyDBOmni service, termination protection is on")
}

if in.Spec.ProjectVPCID != "" && in.Spec.ProjectVPCRef != nil {
return errors.New("cannot use both projectVpcId and projectVPCRef")
}

return nil
}
136 changes: 136 additions & 0 deletions controllers/alloydbomni_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright (c) 2024 Aiven, Helsinki, Finland. https://aiven.io/

package controllers

import (
"context"
"fmt"

"github.com/aiven/aiven-go-client/v2"
avngen "github.com/aiven/go-client-codegen"
"github.com/aiven/go-client-codegen/handler/service"
corev1 "k8s.io/api/core/v1"
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"
alloydbomniuserconfig "github.com/aiven/aiven-operator/api/v1alpha1/userconfig/service/alloydbomni"
)

// AlloyDBOmniReconciler reconciles a AlloyDBOmni object
type AlloyDBOmniReconciler struct {
Controller
}

func newAlloyDBOmniReconciler(c Controller) reconcilerType {
return &AlloyDBOmniReconciler{Controller: c}
}

//+kubebuilder:rbac:groups=aiven.io,resources=alloydbomnis,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=aiven.io,resources=alloydbomnis/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=aiven.io,resources=alloydbomnis/finalizers,verbs=get;create;update

func (r *AlloyDBOmniReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
return r.reconcileInstance(ctx, req, newGenericServiceHandler(newAlloyDBOmniAdapter), &v1alpha1.AlloyDBOmni{})
}

func (r *AlloyDBOmniReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.AlloyDBOmni{}).
Owns(&corev1.Secret{}).
Complete(r)
}

func newAlloyDBOmniAdapter(_ *aiven.Client, object client.Object) (serviceAdapter, error) {
adbo, ok := object.(*v1alpha1.AlloyDBOmni)
if !ok {
return nil, fmt.Errorf("object is not of type v1alpha1.AlloyDBOmni")
}
return &alloyDBOmniAdapter{adbo}, nil
}

// alloyDBOmniAdapter handles an Aiven AlloyDBOmni service
type alloyDBOmniAdapter struct {
*v1alpha1.AlloyDBOmni
}

func (a *alloyDBOmniAdapter) getObjectMeta() *metav1.ObjectMeta {
return &a.ObjectMeta
}

func (a *alloyDBOmniAdapter) getServiceStatus() *v1alpha1.ServiceStatus {
return &a.Status
}

func (a *alloyDBOmniAdapter) getServiceCommonSpec() *v1alpha1.ServiceCommonSpec {
return &a.Spec.ServiceCommonSpec
}

func (a *alloyDBOmniAdapter) getUserConfig() any {
return a.Spec.UserConfig
}

func (a *alloyDBOmniAdapter) newSecret(ctx context.Context, s *service.ServiceGetOut) (*corev1.Secret, error) {
prefix := getSecretPrefix(a)
stringData := map[string]string{
prefix + "HOST": s.ServiceUriParams["host"],
prefix + "PORT": s.ServiceUriParams["port"],
prefix + "DATABASE": s.ServiceUriParams["dbname"],
prefix + "USER": s.ServiceUriParams["user"],
prefix + "PASSWORD": s.ServiceUriParams["password"],
prefix + "SSLMODE": s.ServiceUriParams["sslmode"],
prefix + "DATABASE_URI": s.ServiceUri,
}

return newSecret(a, stringData, false), nil
}

func (a *alloyDBOmniAdapter) getServiceType() string {
return "alloydbomni"
}

func (a *alloyDBOmniAdapter) getDiskSpace() string {
return a.Spec.DiskSpace
}

func (a *alloyDBOmniAdapter) performUpgradeTaskIfNeeded(ctx context.Context, avn avngen.Client, old *service.ServiceGetOut) error {
var currentVersion string = old.UserConfig["alloydbomni_version"].(string)
targetUserConfig := a.getUserConfig().(*alloydbomniuserconfig.AlloydbomniUserConfig)
if targetUserConfig == nil || targetUserConfig.PgVersion == nil {
return nil
}
var targetVersion string = *targetUserConfig.PgVersion

// No need to upgrade if alloydbomni_version hasn't changed
if targetVersion == currentVersion {
return nil
}

task, err := avn.ServiceTaskCreate(ctx, a.getServiceCommonSpec().Project, a.getObjectMeta().Name, &service.ServiceTaskCreateIn{
TargetVersion: service.TargetVersionType(targetVersion),
TaskType: service.TaskTypeUpgradeCheck,
})
if err != nil {
return fmt.Errorf("cannot create AlloyDB Omni upgrade check task: %q", err)
}

finalTaskResult, err := waitForTaskToComplete(ctx, func() (bool, *service.ServiceTaskGetOut, error) {
t, getErr := avn.ServiceTaskGet(ctx, a.getServiceCommonSpec().Project, a.getObjectMeta().Name, task.TaskId)
if getErr != nil {
return true, nil, fmt.Errorf("error fetching service task %s: %q", t.TaskId, getErr)
}

if !t.Success {
return false, nil, nil
}
return true, t, nil
})
if err != nil {
return err
}
if !finalTaskResult.Success {
return fmt.Errorf("AlloyDB Omni service upgrade check error, version upgrade from %s to %s, result: %s", currentVersion, targetVersion, finalTaskResult.Result)
}
return nil
}
1 change: 1 addition & 0 deletions controllers/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func SetupControllers(mgr ctrl.Manager, defaultToken, kubeVersion, operatorVersi
}

builders := map[string]reconcilerBuilder{
"AlloyDBOmni": newAlloyDBOmniReconciler,
"Cassandra": newCassandraReconciler,
"Clickhouse": newClickhouseReconciler,
"ClickhouseDatabase": newClickhouseDatabaseReconciler,
Expand Down
Loading

0 comments on commit 44b3205

Please sign in to comment.