Skip to content

Commit

Permalink
Add major upgrade support for postgres
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Saratura committed Jan 9, 2025
1 parent e60b671 commit d8f6850
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 2 deletions.
2 changes: 1 addition & 1 deletion apis/helm/release/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type NamespacedName struct {

// DataKeySelector defines required spec to access a key of a configmap or secret
type DataKeySelector struct {
NamespacedName `json:",inline,omitempty"`
NamespacedName `json:",inline"`
Key string `json:"key,omitempty"`
Optional bool `json:"optional,omitempty"`
}
Expand Down
4 changes: 4 additions & 0 deletions apis/vshn/v1/dbaas_vshn_postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ type VSHNPostgreSQLTLS struct {
type VSHNPostgreSQLStatus struct {
// InstanceNamespace contains the name of the namespace where the instance resides
InstanceNamespace string `json:"instanceNamespace,omitempty"`

// MajorVersion contains the current version of PostgreSQL.
MajorVersion string `json:"majorVersion,omitempty"`

// PostgreSQLConditions contains the status conditions of the backing object.
PostgreSQLConditions []Condition `json:"postgresqlConditions,omitempty"`
NamespaceConditions []Condition `json:"namespaceConditions,omitempty"`
Expand Down
3 changes: 3 additions & 0 deletions crds/vshn.appcat.vshn.io_vshnpostgresqls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5359,6 +5359,9 @@ spec:
type: string
type: object
type: array
majorVersion:
description: MajorVersion contains the current version of PostgreSQL.
type: string
namespaceConditions:
items:
properties:
Expand Down
3 changes: 3 additions & 0 deletions crds/vshn.appcat.vshn.io_xvshnpostgresqls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6101,6 +6101,9 @@ spec:
type: string
type: object
type: array
majorVersion:
description: MajorVersion contains the current version of PostgreSQL.
type: string
namespaceConditions:
items:
properties:
Expand Down
122 changes: 122 additions & 0 deletions pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package vshnpostgres

import (
"context"
"errors"
"fmt"
xfnproto "github.com/crossplane/function-sdk-go/proto/v1beta1"
stackgresv1 "github.com/vshn/appcat/v4/apis/stackgres/v1"
vshnv1 "github.com/vshn/appcat/v4/apis/vshn/v1"
"github.com/vshn/appcat/v4/pkg/comp-functions/runtime"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pointer "k8s.io/utils/ptr"
)

const (
majorUpgradeSuffix = "-major-upgrade-dbops"
)

func MajorVersionUpgrade(ctx context.Context, comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime) *xfnproto.Result {
comp, err := getVSHNPostgreSQL(ctx, svc)

if err != nil {
return runtime.NewFatalResult(fmt.Errorf("cannot get composite from function io: %w", err))
}

expectedV := comp.Spec.Parameters.Service.MajorVersion
currentV := comp.Status.MajorVersion

majorUpgradeDbOps := &stackgresv1.SGDbOps{}
err = svc.GetObservedKubeObject(majorUpgradeDbOps, comp.GetName()+majorUpgradeSuffix)
if err != nil && !errors.Is(err, runtime.ErrNotFound) {
return runtime.NewFatalResult(fmt.Errorf("cannot get observed kube object major upgrade sgdbops: %w", err))
}

// If current and expected versions do not match then issue a major version upgrade via SGDBOps resource
if currentV != expectedV {
// If SGDBOps resource does not exist create it otherwise cleanup if successful or keep the resource on fail
if errors.Is(err, runtime.ErrNotFound) {
return createMajorUpgradeSgDbOps(svc, comp, expectedV)
} else if isSuccessful(majorUpgradeDbOps.Status.Conditions) {
return cleanUp(svc, comp, expectedV)
} else {
return keepSgDbOpsResource(svc, comp, majorUpgradeDbOps)
}
}

return runtime.NewNormalResult("No major upgrade issued")
}

func keepSgDbOpsResource(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL, majorUpgradeDbOps *stackgresv1.SGDbOps) *xfnproto.Result {
err := svc.SetDesiredKubeObject(majorUpgradeDbOps, comp.GetName()+majorUpgradeSuffix)
if err != nil {
return runtime.NewWarningResult(fmt.Sprintf("cannot keep major upgrade kube object %s", comp.GetName()))
}
return runtime.NewWarningResult("Major upgrade is not completed or it failed")
}

func cleanUp(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL, expectedV string) *xfnproto.Result {
comp.Status.MajorVersion = expectedV
err := svc.SetDesiredCompositeStatus(comp)
if err != nil {
return runtime.NewFatalResult(fmt.Errorf("cannot update status field with the newest major postgres version: %w", err))
}
return runtime.NewNormalResult("Major upgrade successfully finished, SGDBOps cleaned up")
}

func isSuccessful(conditions *[]stackgresv1.SGDbOpsStatusConditionsItem) bool {
var successful, completed bool
if conditions != nil {
for _, c := range *conditions {
if !(*c.Reason == "OperationFailed" && *c.Status == "True") {
successful = true
}
if *c.Reason == "OperationCompleted" && *c.Status == "True" {
completed = true
}
}
}
return successful && completed
}

func createMajorUpgradeSgDbOps(svc *runtime.ServiceRuntime, comp *vshnv1.VSHNPostgreSQL, expectedV string) *xfnproto.Result {
cluster := &stackgresv1.SGCluster{}
err := svc.GetObservedKubeObject(cluster, "cluster")
if err != nil {
return runtime.NewFatalResult(fmt.Errorf("cannot get observed kube object cluster: %w", err))
}

conf := &stackgresv1.SGPostgresConfig{}
err = svc.GetObservedKubeObject(conf, comp.GetName()+"-"+configResourceName)
if err != nil {
return runtime.NewFatalResult(fmt.Errorf("cannot get observed kube object postgres config: %w", err))
}

sgdbops := &stackgresv1.SGDbOps{
ObjectMeta: v1.ObjectMeta{
Name: comp.GetName() + majorUpgradeSuffix,
Namespace: comp.GetInstanceNamespace(),
},
Spec: stackgresv1.SGDbOpsSpec{
MajorVersionUpgrade: &stackgresv1.SGDbOpsSpecMajorVersionUpgrade{
//TODO do we need to configure the backup path?
BackupPath: nil,

Check: pointer.To(true),
Clone: nil,
Link: pointer.To(true),
PostgresVersion: &expectedV,

//TODO do we reuse the old version config?
SgPostgresConfig: pointer.To(conf.GetName()),
},
Op: "majorVersionUpgrade",
SgCluster: cluster.GetName(),
},
}
err = svc.SetDesiredKubeObject(sgdbops, comp.GetName()+majorUpgradeSuffix)
if err != nil {
return runtime.NewWarningResult(fmt.Sprintf("cannot create major upgrade kube object %s", comp.GetName()))
}
return runtime.NewNormalResult("SGDBOps for major upgrade created")
}
4 changes: 4 additions & 0 deletions pkg/comp-functions/functions/vshnpostgres/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func init() {
Name: "custom-exporter-configs",
Execute: PgExporterConfig,
},
{
Name: "major-version-upgrade",
Execute: MajorVersionUpgrade,
},
},
})
}
2 changes: 1 addition & 1 deletion pkg/controller/webhooks/postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ func validateMajorVersionUpgrade(newPg *vshnv1.VSHNPostgreSQL, oldPg *vshnv1.VSH
fmt.Sprintf("invalid major version: %s", err.Error()),
)
}
oldVersion, err := strconv.Atoi(oldPg.Spec.Parameters.Service.MajorVersion)
oldVersion, err := strconv.Atoi(oldPg.Status.MajorVersion)
if err != nil {
return field.Invalid(
field.NewPath("spec.parameters.service.majorVersion"),
Expand Down
24 changes: 24 additions & 0 deletions pkg/controller/webhooks/postgresql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -520,6 +523,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
expectErr: nil,
},
Expand All @@ -533,6 +539,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -542,6 +551,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
expectErr: nil,
},
Expand All @@ -555,6 +567,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -564,6 +579,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
expectErr: field.Forbidden(
field.NewPath("spec.parameters.service.majorVersion"),
Expand All @@ -580,6 +598,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -589,6 +610,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
expectErr: field.Forbidden(
field.NewPath("spec.parameters.service.majorVersion"),
Expand Down

0 comments on commit d8f6850

Please sign in to comment.