diff --git a/update/postgres.go b/update/postgres.go new file mode 100644 index 0000000..74815d1 --- /dev/null +++ b/update/postgres.go @@ -0,0 +1,64 @@ +package update + +import ( + "context" + "fmt" + + "github.com/crossplane/crossplane-runtime/pkg/resource" + infra "github.com/ninech/apis/infrastructure/v1alpha1" + storage "github.com/ninech/apis/storage/v1alpha1" + "github.com/ninech/nctl/api" + "github.com/ninech/nctl/internal/file" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type postgresCmd struct { + Name string `arg:"" default:"" help:"Name of the PostgreSQL instance to update."` + MachineType *infra.MachineType `placeholder:"${postgres_machine_default}" help:"Defines the sizing for a particular PostgreSQL instance. Available types: ${postgres_machine_types}"` + AllowedCidrs *[]storage.IPv4CIDR `placeholder:"0.0.0.0/0" help:"Specifies the IP addresses allowed to connect to the instance." ` + SSHKeys []storage.SSHKey `help:"Contains a list of SSH public keys, allowed to connect to the db server, in order to up-/download and directly restore database backups."` + SSHKeysFile string `help:"Path to a file containing a list of SSH public keys (see above), separated by newlines."` + KeepDailyBackups *int `placeholder:"${postgres_backup_retention_days}" help:"Number of daily database backups to keep. Note that setting this to 0, backup will be disabled and existing dumps deleted immediately."` +} + +func (cmd *postgresCmd) Run(ctx context.Context, client *api.Client) error { + postgres := &storage.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: cmd.Name, + Namespace: client.Project, + }, + } + + upd := newUpdater(client, postgres, storage.PostgresKind, func(current resource.Managed) error { + postgres, ok := current.(*storage.Postgres) + if !ok { + return fmt.Errorf("resource is of type %T, expected %T", current, storage.Postgres{}) + } + + sshkeys, err := file.ReadSSHKeys(cmd.SSHKeysFile) + if err != nil { + return fmt.Errorf("error when reading SSH keys file: %w", err) + } + cmd.SSHKeys = append(cmd.SSHKeys, sshkeys...) + + cmd.applyUpdates(postgres) + return nil + }) + + return upd.Update(ctx) +} + +func (cmd *postgresCmd) applyUpdates(postgres *storage.Postgres) { + if cmd.MachineType != nil { + postgres.Spec.ForProvider.MachineType = *cmd.MachineType + } + if cmd.AllowedCidrs != nil { + postgres.Spec.ForProvider.AllowedCIDRs = *cmd.AllowedCidrs + } + if cmd.SSHKeys != nil { + postgres.Spec.ForProvider.SSHKeys = cmd.SSHKeys + } + if cmd.KeepDailyBackups != nil { + postgres.Spec.ForProvider.KeepDailyBackups = cmd.KeepDailyBackups + } +} diff --git a/update/postgres_test.go b/update/postgres_test.go new file mode 100644 index 0000000..1a24e91 --- /dev/null +++ b/update/postgres_test.go @@ -0,0 +1,99 @@ +package update + +import ( + "context" + "reflect" + "testing" + + infra "github.com/ninech/apis/infrastructure/v1alpha1" + storage "github.com/ninech/apis/storage/v1alpha1" + "github.com/ninech/nctl/api" + "github.com/ninech/nctl/internal/test" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestPostgres(t *testing.T) { + tests := []struct { + name string + create storage.PostgresParameters + update postgresCmd + want storage.PostgresParameters + wantErr bool + }{ + { + name: "simple", + }, + { + name: "increase-machineType", + update: postgresCmd{MachineType: ptr.To(infra.MachineType("nine-standard-1"))}, + want: storage.PostgresParameters{MachineType: infra.MachineType("nine-standard-1")}, + }, + { + name: "decrease-machineType", + create: storage.PostgresParameters{MachineType: infra.MachineType("nine-standard-2")}, + update: postgresCmd{MachineType: ptr.To(infra.MachineType("nine-standard-1"))}, + want: storage.PostgresParameters{MachineType: infra.MachineType("nine-standard-1")}, + }, + { + name: "sshKeys", + update: postgresCmd{SSHKeys: []storage.SSHKey{"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJGG5/nnivrW4zLD4ANLclVT3y68GAg6NOA3HpzFLo5e test@test"}}, + want: storage.PostgresParameters{SSHKeys: []storage.SSHKey{"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJGG5/nnivrW4zLD4ANLclVT3y68GAg6NOA3HpzFLo5e test@test"}}, + }, + { + name: "keepDailyBackups", + update: postgresCmd{KeepDailyBackups: ptr.To(5)}, + want: storage.PostgresParameters{KeepDailyBackups: ptr.To(5)}, + }, + { + name: "allowedCIDRs-nothing-set-initially", + update: postgresCmd{AllowedCidrs: &[]storage.IPv4CIDR{storage.IPv4CIDR("0.0.0.0/0")}}, + want: storage.PostgresParameters{AllowedCIDRs: []storage.IPv4CIDR{storage.IPv4CIDR("0.0.0.0/0")}}, + }, + { + name: "allowedCIDRs-set-initially", + create: storage.PostgresParameters{AllowedCIDRs: []storage.IPv4CIDR{"192.168.0.1/24"}}, + update: postgresCmd{AllowedCidrs: &[]storage.IPv4CIDR{storage.IPv4CIDR("0.0.0.0/0")}}, + want: storage.PostgresParameters{AllowedCIDRs: []storage.IPv4CIDR{storage.IPv4CIDR("0.0.0.0/0")}}, + }, + { + name: "multi-update", + create: storage.PostgresParameters{AllowedCIDRs: []storage.IPv4CIDR{"0.0.0.0/0"}}, + update: postgresCmd{MachineType: ptr.To(infra.MachineType("nine-standard-1"))}, + want: storage.PostgresParameters{MachineType: infra.MachineType("nine-standard-1"), AllowedCIDRs: []storage.IPv4CIDR{storage.IPv4CIDR("0.0.0.0/0")}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.update.Name = "test-" + t.Name() + + scheme, err := api.NewScheme() + if err != nil { + t.Fatal(err) + } + apiClient := &api.Client{WithWatch: fake.NewClientBuilder().WithScheme(scheme).Build(), Project: "default"} + ctx := context.Background() + + created := test.Postgres(tt.update.Name, apiClient.Project, "nine-es34") + created.Spec.ForProvider = tt.create + if err := apiClient.Create(ctx, created); err != nil { + t.Fatalf("postgres create error, got: %s", err) + } + if err := apiClient.Get(ctx, api.ObjectName(created), created); err != nil { + t.Fatalf("expected postgres to exist, got: %s", err) + } + + updated := &storage.Postgres{} + if err := tt.update.Run(ctx, apiClient); (err != nil) != tt.wantErr { + t.Errorf("postgresCmd.Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err := apiClient.Get(ctx, api.ObjectName(created), updated); err != nil { + t.Fatalf("expected postgres to exist, got: %s", err) + } + + if !reflect.DeepEqual(updated.Spec.ForProvider, tt.want) { + t.Fatalf("expected postgres.Spec.ForProvider = %v, got: %v", updated.Spec.ForProvider, tt.want) + } + }) + } +} diff --git a/update/update.go b/update/update.go index 05288a0..cc203f0 100644 --- a/update/update.go +++ b/update/update.go @@ -13,9 +13,9 @@ type Cmd struct { Config configCmd `cmd:"" group:"deplo.io" name:"config" help:"Update an existing deplo.io Project Configuration. (Beta - requires access)"` Project projectCmd `cmd:"" group:"management.nine.ch" name:"project" help:"Update an existing Project"` MySQL mySQLCmd `cmd:"" group:"storage.nine.ch" name:"mysql" help:"Update an existing MySQL instance."` + Postgres postgresCmd `cmd:"" group:"storage.nine.ch" name:"postgres" help:"Update an existing PostgreSQL instance."` KeyValueStore keyValueStoreCmd `cmd:"" group:"storage.nine.ch" name:"keyvaluestore" aliases:"kvs" help:"Update an existing KeyValueStore instance"` CloudVirtualMachine cloudVMCmd `cmd:"" group:"infrastructure.nine.ch" name:"cloudvirtualmachine" aliases:"cloudvm" help:"Update a CloudVM."` - //Postgres postgresCmd `cmd:"" group:"storage.nine.ch" name:"postgres" help:"Update an existing PostgreSQL instance."` } type updater struct {