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 10, 2025
1 parent e60b671 commit 7e91fc8
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 20 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.NewWarningResult(fmt.Sprintf("cannot get composite from function io: %w", err))

Check failure on line 23 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / test

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 23 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-lint

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 23 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-build

fmt.Sprintf does not support error-wrapping directive %w
}

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.NewWarningResult(fmt.Sprintf("cannot get observed kube object major upgrade sgdbops: %w", err))

Check failure on line 32 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / test

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 32 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-lint

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 32 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-build

fmt.Sprintf does not support error-wrapping directive %w
}

// If current and expected versions do not match then issue a major version upgrade via SGDBOps resource
if currentV != "" && 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.NewWarningResult(fmt.Sprintf("cannot update status field with the newest major postgres version: %w", err))

Check failure on line 62 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / test

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 62 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-lint

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 62 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-build

fmt.Sprintf does not support error-wrapping directive %w
}
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.NewWarningResult(fmt.Sprintf("cannot get observed kube object cluster: %w", err))

Check failure on line 86 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / test

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 86 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-lint

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 86 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-build

fmt.Sprintf does not support error-wrapping directive %w
}

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

Check failure on line 92 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / test

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 92 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-lint

fmt.Sprintf does not support error-wrapping directive %w

Check failure on line 92 in pkg/comp-functions/functions/vshnpostgres/major_version_upgrade.go

View workflow job for this annotation

GitHub Actions / go-build

fmt.Sprintf does not support error-wrapping directive %w
}
conf.Spec.PostgresVersion = expectedV
err = svc.SetDesiredKubeObject(conf, comp.GetName()+"-"+configResourceName)
if err != nil {
return runtime.NewWarningResult(fmt.Sprintf("cannot set observed kube object postgres config %s", comp.GetName()))
}

sgdbops := &stackgresv1.SGDbOps{
ObjectMeta: v1.ObjectMeta{
Name: comp.GetName() + majorUpgradeSuffix,
Namespace: comp.GetInstanceNamespace(),
},
Spec: stackgresv1.SGDbOpsSpec{
MajorVersionUpgrade: &stackgresv1.SGDbOpsSpecMajorVersionUpgrade{
Check: pointer.To(true),
Clone: nil,
Link: pointer.To(true),
PostgresVersion: &expectedV,
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")
}
10 changes: 10 additions & 0 deletions pkg/comp-functions/functions/vshnpostgres/postgresql_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ func DeployPostgreSQL(ctx context.Context, comp *vshnv1.VSHNPostgreSQL, svc *run
if err != nil {
return runtime.NewWarningResult(fmt.Errorf("cannot bootstrap instance namespace: %w", err).Error())
}
l.Info("Set major version in status")
err = setMajorVersionStatus(comp, svc)
if err != nil {
return runtime.NewWarningResult(fmt.Errorf("cannot create tls certificate: %w", err).Error())
}

l.Info("Create tls certificate")
err = createCerts(comp, svc)
Expand Down Expand Up @@ -91,6 +96,11 @@ func DeployPostgreSQL(ctx context.Context, comp *vshnv1.VSHNPostgreSQL, svc *run
return nil
}

func setMajorVersionStatus(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime) error {
comp.Status.MajorVersion = comp.Spec.Parameters.Service.MajorVersion
return svc.SetDesiredCompositeStatus(comp)
}

func createCerts(comp *vshnv1.VSHNPostgreSQL, svc *runtime.ServiceRuntime) error {
selfSignedIssuer := &cmv1.Issuer{
ObjectMeta: metav1.ObjectMeta{
Expand Down
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,
},
},
})
}
46 changes: 27 additions & 19 deletions pkg/controller/webhooks/postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,10 @@ func (p *PostgreSQLWebhookHandler) ValidateUpdate(ctx context.Context, oldObj, n
func (p *PostgreSQLWebhookHandler) validatePostgreSQL(ctx context.Context, newObj, oldObj runtime.Object, isCreate bool) (admission.Warnings, error) {
allErrs := field.ErrorList{}
newPg, ok := newObj.(*vshnv1.VSHNPostgreSQL)
oldPg, ok := oldObj.(*vshnv1.VSHNPostgreSQL)
if !ok {
return nil, fmt.Errorf("provided manifest is not a valid VSHNPostgreSQL object")
}

if !isCreate {
if newPg.DeletionTimestamp != nil {
return nil, nil
}

// Validate major upgrades
if err := validateMajorVersionUpgrade(newPg, oldPg); err != nil {
allErrs = append(allErrs, err)
}
}

// Validate Vacuum and Repack settings
if err := validateVacuumRepack(newPg.Spec.Parameters.Service.VacuumEnabled, newPg.Spec.Parameters.Service.RepackEnabled); err != nil {
allErrs = append(allErrs, err)
Expand All @@ -131,6 +119,21 @@ func (p *PostgreSQLWebhookHandler) validatePostgreSQL(ctx context.Context, newOb
// Validate PostgreSQL configuration
allErrs = append(allErrs, validatePgConf(newPg)...)

if !isCreate {
oldPg, ok := oldObj.(*vshnv1.VSHNPostgreSQL)
if !ok {
return nil, fmt.Errorf("provided manifest is not a valid VSHNPostgreSQL object")
}
if newPg.DeletionTimestamp != nil {
return nil, nil
}

// Validate major upgrades
if err := validateMajorVersionUpgrade(newPg, oldPg); err != nil {
allErrs = append(allErrs, err)
}
}

if len(allErrs) > 0 {
return nil, apierrors.NewInvalid(pgGK, newPg.GetName(), allErrs)
}
Expand Down Expand Up @@ -282,13 +285,18 @@ 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)
if err != nil {
return field.Invalid(
field.NewPath("spec.parameters.service.majorVersion"),
oldPg.Spec.Parameters.Service.MajorVersion,
fmt.Sprintf("invalid major version: %s", err.Error()),
)
var oldVersion int
if oldPg.Status.MajorVersion == "" {
oldVersion = newVersion
} else {
oldVersion, err = strconv.Atoi(oldPg.Status.MajorVersion)
if err != nil {
return field.Invalid(
field.NewPath("status.majorVersion"),
oldPg.Status.MajorVersion,
fmt.Sprintf("invalid major version: %s", err.Error()),
)
}
}

// Check if the upgrade is allowed
Expand Down
28 changes: 28 additions & 0 deletions pkg/controller/webhooks/postgresql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,13 @@ func TestPostgreSQLWebhookHandler_ValidateUpdate(t *testing.T) {
Instances: 1,
Service: vshnv1.VSHNPostgreSQLServiceSpec{
RepackEnabled: true,
MajorVersion: "15",
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
}

// check pgSettings with single good setting
Expand Down Expand Up @@ -511,6 +515,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -520,6 +527,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
expectErr: nil,
},
Expand All @@ -533,6 +543,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -542,6 +555,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
expectErr: nil,
},
Expand All @@ -555,6 +571,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -564,6 +583,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 +602,9 @@ func TestPostgreSQLWebhookHandler_ValidateMajorVersionUpgrade(t *testing.T) {
},
},
},
Status: vshnv1.VSHNPostgreSQLStatus{
MajorVersion: "15",
},
},
old: &vshnv1.VSHNPostgreSQL{
Spec: vshnv1.VSHNPostgreSQLSpec{
Expand All @@ -589,6 +614,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 7e91fc8

Please sign in to comment.