Skip to content

Commit

Permalink
Merge pull request #42 from digitalocean/CON-9202-update-do-operator
Browse files Browse the repository at this point in the history
CON-9202 update Go to 1.20 and dependencies
  • Loading branch information
llDrLove authored May 26, 2023
2 parents 28a2bc0 + 2ec206f commit 434c508
Show file tree
Hide file tree
Showing 820 changed files with 49,745 additions and 27,192 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Install go
uses: actions/setup-go@v3
with:
go-version: ^1.19
go-version: ^1.20
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Validate that the release manifest exists
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Install go
uses: actions/setup-go@v3
with:
go-version: ^1.19
go-version: ^1.20
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Run make test
Expand Down
61 changes: 31 additions & 30 deletions api/v1alpha1/databasecluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"net/http"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"strconv"

"github.com/digitalocean/godo"
Expand All @@ -47,14 +48,14 @@ func (r *DatabaseCluster) SetupWebhookWithManager(mgr ctrl.Manager, godoClient *
var _ webhook.Validator = &DatabaseCluster{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseCluster) ValidateCreate() error {
func (r *DatabaseCluster) ValidateCreate() (warnings admission.Warnings, err error) {
databaseclusterlog.Info("validate create", "name", r.Name)
ctx := context.TODO()

godoReq := r.Spec.ToGodoValidateCreateRequest()
req, err := godoClient.NewRequest(ctx, http.MethodPost, "/v2/databases", godoReq)
if err != nil {
return fmt.Errorf("creating http request: %v", err)
return warnings, fmt.Errorf("creating http request: %v", err)
}
_, err = godoClient.Do(ctx, req, nil)
if err != nil {
Expand All @@ -72,95 +73,95 @@ func (r *DatabaseCluster) ValidateCreate() error {
opts, _, err := godoClient.Databases.ListOptions(ctx)
if err != nil {
databaseclusterlog.Error(err, "getting database options from the DigitalOcean api")
return invalidSpecErr
return warnings, invalidSpecErr
}
engineOpts, haveOpts := engineOptsFromOptions(opts, r.Spec.Engine)

switch godoErr.Message {
case "invalid cluster name":
return field.Invalid(specField.Child("name"), r.Spec.Name, godoErr.Message)
return warnings, field.Invalid(specField.Child("name"), r.Spec.Name, godoErr.Message)
case "invalid node count":
var validNumNodes []string
if !haveOpts {
return field.Invalid(specField.Child("numNodes"), r.Spec.NumNodes, godoErr.Message)
return warnings, field.Invalid(specField.Child("numNodes"), r.Spec.NumNodes, godoErr.Message)
}
for _, layout := range engineOpts.Layouts {
validNumNodes = append(validNumNodes, strconv.Itoa(layout.NodeNum))
}
return field.NotSupported(specField.Child("numNodes"), r.Spec.NumNodes, validNumNodes)
return warnings, field.NotSupported(specField.Child("numNodes"), r.Spec.NumNodes, validNumNodes)
case "invalid engine":
// The options API doesn't return us the list of engines in a
// friendly format, so we just hardcode them.
return field.NotSupported(specField.Child("engine"), r.Spec.Engine, []string{"mysql", "pg", "redis", "mongodb"})
return warnings, field.NotSupported(specField.Child("engine"), r.Spec.Engine, []string{"mysql", "pg", "redis", "mongodb"})
case "invalid size":
if !haveOpts {
return field.Invalid(specField.Child("size"), r.Spec.Size, godoErr.Message)
return warnings, field.Invalid(specField.Child("size"), r.Spec.Size, godoErr.Message)
}
for _, layout := range engineOpts.Layouts {
if layout.NodeNum == int(r.Spec.NumNodes) {
return field.NotSupported(specField.Child("size"), r.Spec.Size, layout.Sizes)
return warnings, field.NotSupported(specField.Child("size"), r.Spec.Size, layout.Sizes)
}
}
return field.Invalid(specField.Child("size"), r.Spec.Size, godoErr.Message)
return warnings, field.Invalid(specField.Child("size"), r.Spec.Size, godoErr.Message)
case "invalid region":
if !haveOpts {
return field.Invalid(specField.Child("region"), r.Spec.Region, godoErr.Message)
return warnings, field.Invalid(specField.Child("region"), r.Spec.Region, godoErr.Message)
}
return field.NotSupported(specField.Child("region"), r.Spec.Region, engineOpts.Regions)
return warnings, field.NotSupported(specField.Child("region"), r.Spec.Region, engineOpts.Regions)
case "invalid cluster engine version":
if !haveOpts {
return field.Invalid(specField.Child("version"), r.Spec.Version, godoErr.Message)
return warnings, field.Invalid(specField.Child("version"), r.Spec.Version, godoErr.Message)
}
return field.NotSupported(specField.Child("version"), r.Spec.Version, engineOpts.Versions)
return warnings, field.NotSupported(specField.Child("version"), r.Spec.Version, engineOpts.Versions)
}

return invalidSpecErr
return warnings, invalidSpecErr
}

return field.Invalid(specField, r.Spec, err.Error())
return warnings, field.Invalid(specField, r.Spec, err.Error())
}

return nil
return warnings, nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseCluster) ValidateUpdate(old runtime.Object) error {
func (r *DatabaseCluster) ValidateUpdate(old runtime.Object) (warnings admission.Warnings, err error) {
databaseclusterlog.Info("validate update", "name", r.Name)
ctx := context.TODO()

oldCluster, ok := old.(*DatabaseCluster)
if !ok {
return fmt.Errorf("old is unexpected type %T", old)
return warnings, fmt.Errorf("old is unexpected type %T", old)
}

enginePath := field.NewPath("spec").Child("engine")
if oldCluster.Spec.Engine != r.Spec.Engine {
return field.Forbidden(enginePath, "engine is immutable")
return warnings, field.Forbidden(enginePath, "engine is immutable")
}
namePath := field.NewPath("spec").Child("name")
if oldCluster.Spec.Name != r.Spec.Name {
return field.Forbidden(namePath, "name is immutable")
return warnings, field.Forbidden(namePath, "name is immutable")
}
// TODO(awg) Remove once we support upgrades in the controller.
versionPath := field.NewPath("spec").Child("version")
if oldCluster.Spec.Version != r.Spec.Version {
return field.Forbidden(versionPath, "database upgrades are not yet supported in the do-operator")
return warnings, field.Forbidden(versionPath, "database upgrades are not yet supported in the do-operator")
}
// TODO(awg) Remove once we support migrations in the controller.
regionPath := field.NewPath("spec").Child("region")
if oldCluster.Spec.Region != r.Spec.Region {
return field.Forbidden(regionPath, "database region migrations are not yet supported in the do-operator")
return warnings, field.Forbidden(regionPath, "database region migrations are not yet supported in the do-operator")
}

opts, _, err := godoClient.Databases.ListOptions(ctx)
if err != nil {
return fmt.Errorf("getting database options from the DigitalOcean api: %v", err)
return warnings, fmt.Errorf("getting database options from the DigitalOcean api: %v", err)
}
engineOpts, ok := engineOptsFromOptions(opts, r.Spec.Engine)
if !ok {
// We *should* get options back for all supported engines, but if the
// API is missing one we shouldn't block the user from updating.
return nil
return warnings, nil
}

var (
Expand All @@ -179,7 +180,7 @@ func (r *DatabaseCluster) ValidateUpdate(old runtime.Object) error {
}
if !numNodesValid {
numNodesPath := field.NewPath("spec").Child("numNodes")
return field.NotSupported(numNodesPath, r.Spec.NumNodes, validNumNodes)
return warnings, field.NotSupported(numNodesPath, r.Spec.NumNodes, validNumNodes)
}
for _, size := range selectedLayout.Sizes {
if size == r.Spec.Size {
Expand All @@ -189,14 +190,14 @@ func (r *DatabaseCluster) ValidateUpdate(old runtime.Object) error {
}
if !sizeValid {
sizePath := field.NewPath("spec").Child("size")
return field.NotSupported(sizePath, r.Spec.Size, selectedLayout.Sizes)
return warnings, field.NotSupported(sizePath, r.Spec.Size, selectedLayout.Sizes)
}

return nil
return warnings, nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseCluster) ValidateDelete() error {
func (r *DatabaseCluster) ValidateDelete() (warnings admission.Warnings, err error) {
databaseclusterlog.Info("validate delete", "name", r.Name)
return nil
return warnings, nil
}
21 changes: 11 additions & 10 deletions api/v1alpha1/databaseclusterreference_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"net/http"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"github.com/digitalocean/godo"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -45,40 +46,40 @@ func (r *DatabaseClusterReference) SetupWebhookWithManager(mgr ctrl.Manager, god
var _ webhook.Validator = &DatabaseClusterReference{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseClusterReference) ValidateCreate() error {
func (r *DatabaseClusterReference) ValidateCreate() (warnings admission.Warnings, err error) {
databaseclusterreferencelog.Info("validate create", "name", r.Name)

dbUUID := r.Spec.UUID
uuidPath := field.NewPath("spec").Child("uuid")
_, resp, err := godoClient.Databases.Get(context.TODO(), dbUUID)
if err != nil {
if resp.StatusCode == http.StatusNotFound {
return field.Invalid(uuidPath, dbUUID, "database does not exist; you must create it before referencing it")
return warnings, field.Invalid(uuidPath, dbUUID, "database does not exist; you must create it before referencing it")
}
return fmt.Errorf("failed to look up database: %v", err)
return warnings, fmt.Errorf("failed to look up database: %v", err)
}

return nil
return warnings, nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseClusterReference) ValidateUpdate(old runtime.Object) error {
func (r *DatabaseClusterReference) ValidateUpdate(old runtime.Object) (warnings admission.Warnings, err error) {
databaseclusterreferencelog.Info("validate update", "name", r.Name)

oldDBRef, ok := old.(*DatabaseClusterReference)
if !ok {
return fmt.Errorf("old is unexpected type %T", old)
return warnings, fmt.Errorf("old is unexpected type %T", old)
}
uuidPath := field.NewPath("spec").Child("uuid")
if r.Spec.UUID != oldDBRef.Spec.UUID {
return field.Forbidden(uuidPath, "database UUID is immutable")
return warnings, field.Forbidden(uuidPath, "database UUID is immutable")
}

return nil
return warnings, nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseClusterReference) ValidateDelete() error {
func (r *DatabaseClusterReference) ValidateDelete() (warnings admission.Warnings, err error) {
databaseclusterreferencelog.Info("validate delete", "name", r.Name)
return nil
return warnings, nil
}
35 changes: 18 additions & 17 deletions api/v1alpha1/databaseuser_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"net/http"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"strings"

"github.com/digitalocean/godo"
Expand Down Expand Up @@ -51,15 +52,15 @@ func (r *DatabaseUser) SetupWebhookWithManager(mgr ctrl.Manager, godoClient *god
var _ webhook.Validator = &DatabaseUser{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseUser) ValidateCreate() error {
func (r *DatabaseUser) ValidateCreate() (warnings admission.Warnings, err error) {
databaseuserlog.Info("validate create", "name", r.Name)
ctx := context.TODO()

clusterPath := field.NewPath("spec").Child("cluster")

clusterAPIGroup := pointer.StringDeref(r.Spec.Cluster.APIGroup, "")
if clusterAPIGroup != GroupVersion.Group {
return field.Invalid(clusterPath.Child("apiGroup"), clusterAPIGroup, "apiGroup must be "+GroupVersion.Group)
return warnings, field.Invalid(clusterPath.Child("apiGroup"), clusterAPIGroup, "apiGroup must be "+GroupVersion.Group)
}

var (
Expand All @@ -76,22 +77,22 @@ func (r *DatabaseUser) ValidateCreate() error {
var cluster DatabaseCluster
if err := webhookClient.Get(ctx, clusterNN, &cluster); err != nil {
if kerrors.IsNotFound(err) {
return field.NotFound(clusterPath, clusterNN)
return warnings, field.NotFound(clusterPath, clusterNN)
}
return fmt.Errorf("failed to fetch DatabaseCluster %s: %s", clusterNN, err)
return warnings, fmt.Errorf("failed to fetch DatabaseCluster %s: %s", clusterNN, err)
}
clusterUUID = cluster.Status.UUID
case strings.ToLower(DatabaseClusterReferenceKind):
var clusterRef DatabaseClusterReference
if err := webhookClient.Get(ctx, clusterNN, &clusterRef); err != nil {
if kerrors.IsNotFound(err) {
return field.NotFound(clusterPath, clusterNN)
return warnings, field.NotFound(clusterPath, clusterNN)
}
return fmt.Errorf("failed to fetch DatabaseClusterReference %s: %s", clusterNN, err)
return warnings, fmt.Errorf("failed to fetch DatabaseClusterReference %s: %s", clusterNN, err)
}
clusterUUID = clusterRef.Spec.UUID
default:
return field.TypeInvalid(
return warnings, field.TypeInvalid(
clusterPath.Child("kind"),
clusterKind,
"kind must be DatabaseCluster or DatabaseClusterReference",
Expand All @@ -100,37 +101,37 @@ func (r *DatabaseUser) ValidateCreate() error {

_, resp, err := godoClient.Databases.GetUser(ctx, clusterUUID, r.Spec.Username)
if err != nil && resp.StatusCode != http.StatusNotFound {
return fmt.Errorf("failed to look up database: %v", err)
return warnings, fmt.Errorf("failed to look up database: %v", err)
}
if err == nil {
return field.Duplicate(field.NewPath("spec").Child("username"), r.Spec.Username)
return warnings, field.Duplicate(field.NewPath("spec").Child("username"), r.Spec.Username)
}

return nil
return warnings, nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseUser) ValidateUpdate(old runtime.Object) error {
func (r *DatabaseUser) ValidateUpdate(old runtime.Object) (warnings admission.Warnings, err error) {
databaseuserlog.Info("validate update", "name", r.Name)

oldUser, ok := old.(*DatabaseUser)
if !ok {
return fmt.Errorf("old is unexpected type %T", old)
return warnings, fmt.Errorf("old is unexpected type %T", old)
}
usernamePath := field.NewPath("spec").Child("username")
if r.Spec.Username != oldUser.Spec.Username {
return field.Forbidden(usernamePath, "username is immutable")
return warnings, field.Forbidden(usernamePath, "username is immutable")
}
clusterPath := field.NewPath("spec").Child("cluster")
if !cmp.Equal(r.Spec.Cluster, oldUser.Spec.Cluster) {
return field.Forbidden(clusterPath, "cluster is immutable")
return warnings, field.Forbidden(clusterPath, "cluster is immutable")
}

return nil
return warnings, nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *DatabaseUser) ValidateDelete() error {
func (r *DatabaseUser) ValidateDelete() (warnings admission.Warnings, err error) {
databaseuserlog.Info("validate delete", "name", r.Name)
return nil
return warnings, nil
}
Loading

0 comments on commit 434c508

Please sign in to comment.