From 186d617f5aa04c7280ac3727cd11cfd3ec4441e2 Mon Sep 17 00:00:00 2001 From: Frode Sundby Date: Thu, 22 Oct 2026 13:37:06 +0200 Subject: [PATCH 1/2] feat(postgres)!: promote Nais Postgres to the root command BREAKING CHANGE: nais alpha postgres has moved to nais postgres. The postgres and pg aliases for Cloud SQL have been removed; use nais cloudsql instead. --- docs/index.md | 10 +++ internal/alpha/command/alpha.go | 2 - internal/application/application.go | 2 + .../application/completion_validation_test.go | 4 +- .../application/postgres_migration_test.go | 82 +++++++++++++++++++ internal/cloudsql/command/cloudsql.go | 23 +----- internal/cloudsql/command/cloudsql_test.go | 30 ------- internal/{alpha => }/postgres/access.go | 0 internal/{alpha => }/postgres/access_test.go | 0 internal/{alpha => }/postgres/branch.go | 0 .../{alpha => }/postgres/command/access.go | 12 +-- .../postgres/command/access_reason_test.go | 0 .../postgres/command/access_test.go | 0 .../{alpha => }/postgres/command/branch.go | 8 +- .../command/branch_create_interactive_test.go | 2 +- .../postgres/command/branch_create_test.go | 2 +- .../postgres/command/branch_delete_test.go | 0 .../postgres/command/branch_test.go | 0 .../postgres/command/choices_test.go | 0 .../postgres/command/environment.go | 4 +- .../postgres/command/environment_test.go | 2 +- .../{alpha => }/postgres/command/flag/flag.go | 2 +- internal/{alpha => }/postgres/command/get.go | 4 +- .../postgres/command/get_readiness_test.go | 2 +- .../{alpha => }/postgres/command/get_test.go | 2 +- internal/{alpha => }/postgres/command/hint.go | 4 +- .../command/inspection_commands_test.go | 6 +- .../{alpha => }/postgres/command/instance.go | 4 +- .../instance_create_environment_test.go | 2 +- .../postgres/command/instance_test.go | 2 +- .../postgres/command/instance_update_test.go | 0 .../{alpha => }/postgres/command/postgres.go | 8 +- .../{alpha => }/postgres/command/progress.go | 0 .../postgres/command/progress_test.go | 0 .../{alpha => }/postgres/command/readiness.go | 2 +- .../postgres/command/recovery_time_test.go | 0 .../postgres/command/validation_test.go | 2 +- internal/{alpha => }/postgres/get.go | 0 internal/{alpha => }/postgres/instance.go | 0 .../{alpha => }/postgres/instance_test.go | 0 internal/{alpha => }/postgres/list.go | 0 internal/{alpha => }/postgres/list_test.go | 0 internal/{alpha => }/postgres/relay/relay.go | 0 .../{alpha => }/postgres/relay/relay_test.go | 0 44 files changed, 132 insertions(+), 91 deletions(-) create mode 100644 internal/application/postgres_migration_test.go delete mode 100644 internal/cloudsql/command/cloudsql_test.go rename internal/{alpha => }/postgres/access.go (100%) rename internal/{alpha => }/postgres/access_test.go (100%) rename internal/{alpha => }/postgres/branch.go (100%) rename internal/{alpha => }/postgres/command/access.go (96%) rename internal/{alpha => }/postgres/command/access_reason_test.go (100%) rename internal/{alpha => }/postgres/command/access_test.go (100%) rename internal/{alpha => }/postgres/command/branch.go (97%) rename internal/{alpha => }/postgres/command/branch_create_interactive_test.go (99%) rename internal/{alpha => }/postgres/command/branch_create_test.go (99%) rename internal/{alpha => }/postgres/command/branch_delete_test.go (100%) rename internal/{alpha => }/postgres/command/branch_test.go (100%) rename internal/{alpha => }/postgres/command/choices_test.go (100%) rename internal/{alpha => }/postgres/command/environment.go (97%) rename internal/{alpha => }/postgres/command/environment_test.go (98%) rename internal/{alpha => }/postgres/command/flag/flag.go (99%) rename internal/{alpha => }/postgres/command/get.go (98%) rename internal/{alpha => }/postgres/command/get_readiness_test.go (98%) rename internal/{alpha => }/postgres/command/get_test.go (99%) rename internal/{alpha => }/postgres/command/hint.go (80%) rename internal/{alpha => }/postgres/command/inspection_commands_test.go (88%) rename internal/{alpha => }/postgres/command/instance.go (98%) rename internal/{alpha => }/postgres/command/instance_create_environment_test.go (99%) rename internal/{alpha => }/postgres/command/instance_test.go (98%) rename internal/{alpha => }/postgres/command/instance_update_test.go (100%) rename internal/{alpha => }/postgres/command/postgres.go (85%) rename internal/{alpha => }/postgres/command/progress.go (100%) rename internal/{alpha => }/postgres/command/progress_test.go (100%) rename internal/{alpha => }/postgres/command/readiness.go (95%) rename internal/{alpha => }/postgres/command/recovery_time_test.go (100%) rename internal/{alpha => }/postgres/command/validation_test.go (96%) rename internal/{alpha => }/postgres/get.go (100%) rename internal/{alpha => }/postgres/instance.go (100%) rename internal/{alpha => }/postgres/instance_test.go (100%) rename internal/{alpha => }/postgres/list.go (100%) rename internal/{alpha => }/postgres/list_test.go (100%) rename internal/{alpha => }/postgres/relay/relay.go (100%) rename internal/{alpha => }/postgres/relay/relay_test.go (100%) diff --git a/docs/index.md b/docs/index.md index 5336cf62..229aa1a8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -52,6 +52,16 @@ Then explore available commands: nais --help ``` +## Postgres command migration (breaking change) + +`nais postgres` now manages Nais Postgres. The commands previously available under +`nais alpha postgres` have moved directly to `nais postgres`, without an alias at +the old location. + +For Google Cloud SQL, use `nais cloudsql` instead of `nais postgres` or `nais pg`. +The old Cloud SQL aliases have been removed. Update scripts and shell aliases +before upgrading to this major release. + ## Shell completion Enable shell completion for faster navigation: diff --git a/internal/alpha/command/alpha.go b/internal/alpha/command/alpha.go index 0c617226..016137af 100644 --- a/internal/alpha/command/alpha.go +++ b/internal/alpha/command/alpha.go @@ -2,7 +2,6 @@ package command import ( "github.com/nais/cli/internal/alpha/command/flag" - postgrescmd "github.com/nais/cli/internal/alpha/postgres/command" "github.com/nais/cli/internal/flags" krakend "github.com/nais/cli/internal/krakend/command" mcpcmd "github.com/nais/cli/internal/mcp/command" @@ -19,7 +18,6 @@ func Alpha(parentFlags *flags.GlobalFlags) *naistrix.Command { SubCommands: []*naistrix.Command{ krakend.Krakend(flags), mcpcmd.MCP(flags), - postgrescmd.Postgres(parentFlags), }, } } diff --git a/internal/application/application.go b/internal/application/application.go index 90d19f0a..f88aa193 100644 --- a/internal/application/application.go +++ b/internal/application/application.go @@ -28,6 +28,7 @@ import ( naisapiCommand "github.com/nais/cli/internal/naisapi/command" naisdeviceCommand "github.com/nais/cli/internal/naisdevice/command" opensearchCommand "github.com/nais/cli/internal/opensearch/command" + postgresCommand "github.com/nais/cli/internal/postgres/command" secretCommand "github.com/nais/cli/internal/secret/command" statusCommand "github.com/nais/cli/internal/status/command" validateCommand "github.com/nais/cli/internal/validate/command" @@ -85,6 +86,7 @@ func New(w io.Writer) (*Application, *flags.GlobalFlags, error) { naisapiCommand.Api(globalFlags), naisdeviceCommand.Naisdevice(globalFlags), opensearchCommand.OpenSearch(globalFlags), + postgresCommand.Postgres(globalFlags), cloudsqlCommand.CloudSQL(globalFlags), secretCommand.Secrets(globalFlags), statusCommand.Status(globalFlags), diff --git a/internal/application/completion_validation_test.go b/internal/application/completion_validation_test.go index 0c69c754..6afef69c 100644 --- a/internal/application/completion_validation_test.go +++ b/internal/application/completion_validation_test.go @@ -35,8 +35,8 @@ func TestPositionalCompletionValidatesFlagsBeforeAPI(t *testing.T) { {name: "OpenSearch environment", args: []string{"opensearch", "get", "-t", "my-team"}, wantError: "missing required environment"}, {name: "Valkey team", args: []string{"valkey", "get", "-e", "dev"}, wantError: "missing required team"}, {name: "Valkey environment", args: []string{"valkey", "get", "-t", "my-team"}, wantError: "missing required environment"}, - {name: "Postgres team", args: []string{"alpha", "postgres", "update"}, wantError: "missing required team"}, - {name: "Postgres branch team", args: []string{"alpha", "postgres", "branch", "activate", "orders"}, wantError: "missing required team"}, + {name: "Postgres team", args: []string{"postgres", "update"}, wantError: "missing required team"}, + {name: "Postgres branch team", args: []string{"postgres", "branch", "activate", "orders"}, wantError: "missing required team"}, } { t.Run(tt.name, func(t *testing.T) { var calls atomic.Int32 diff --git a/internal/application/postgres_migration_test.go b/internal/application/postgres_migration_test.go new file mode 100644 index 00000000..b0ced89b --- /dev/null +++ b/internal/application/postgres_migration_test.go @@ -0,0 +1,82 @@ +package application + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/nais/naistrix" +) + +func TestPostgresCommandMigration(t *testing.T) { + for _, tt := range []struct { + name string + args []string + want []string + rejected bool + }{ + { + name: "postgres manages Nais Postgres", + args: []string{"postgres", "--help"}, + want: []string{"Manage Nais Postgres instances.", "branch", "create"}, + }, + { + name: "postgres completes Nais Postgres commands", + args: []string{"__complete", "postgres", ""}, + want: []string{"branch\t", "create\t", "get\t"}, + }, + { + name: "Cloud SQL remains available", + args: []string{"cloudsql", "--help"}, + want: []string{"Manage Google Cloud SQL instances.", "password", "users"}, + }, + { + name: "alpha postgres is removed", + args: []string{"alpha", "postgres", "list"}, + rejected: true, + }, + { + name: "pg Cloud SQL alias is removed", + args: []string{"pg", "list"}, + rejected: true, + }, + { + name: "Cloud SQL commands are not exposed through postgres", + args: []string{"postgres", "password", "rotate"}, + rejected: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + config := filepath.Join(t.TempDir(), "config.yaml") + if err := os.WriteFile(config, []byte("{}\n"), 0o600); err != nil { + t.Fatal(err) + } + var output bytes.Buffer + app, _, err := New(&output) + if err != nil { + t.Fatal(err) + } + args := append([]string{"--config", config, "--no-colors"}, tt.args...) + err = app.Run(naistrix.RunWithArgs(args)) + if tt.rejected { + if err == nil { + t.Fatal("removed command was accepted") + } + return + } + if err != nil { + t.Fatal(err) + } + for _, want := range tt.want { + if !strings.Contains(output.String(), want) { + t.Errorf("output missing %q: %s", want, output.String()) + } + } + if strings.Contains(strings.ToLower(output.String()), "experimental") { + t.Errorf("stable command still marked experimental: %s", output.String()) + } + }) + } +} diff --git a/internal/cloudsql/command/cloudsql.go b/internal/cloudsql/command/cloudsql.go index b29cc7c2..caeb750c 100644 --- a/internal/cloudsql/command/cloudsql.go +++ b/internal/cloudsql/command/cloudsql.go @@ -2,10 +2,6 @@ package command import ( "context" - "fmt" - "io" - "os" - "strings" "github.com/nais/cli/internal/cloudsql/command/flag" "github.com/nais/cli/internal/flags" @@ -19,9 +15,7 @@ func CloudSQL(parentFlags *flags.GlobalFlags) *naistrix.Command { } return &naistrix.Command{ - Name: "cloudsql", - // TODO: Remove the aliases once users have moved from the old `nais postgres` (Cloud SQL) commands. - Aliases: []string{"postgres", "pg"}, + Name: "cloudsql", Title: "Manage Google Cloud SQL instances.", Description: "Manage Google Cloud SQL instances, including listing, user management, password rotation, and direct database access.", StickyFlags: flags, @@ -38,23 +32,8 @@ func CloudSQL(parentFlags *flags.GlobalFlags) *naistrix.Command { revokeCommand(flags), }, ValidateArgsFunc: func(ctx context.Context, _ *naistrix.Arguments) error { - warnIfLegacyAlias(os.Args[1:], os.Stderr) _, err := gcloud.ValidateAndGetUserLogin(ctx, false) return err }, } } - -// warnIfLegacyAlias tells users who typed `nais postgres` or `nais pg` that these now mean Cloud SQL. -// naistrix does not expose which alias was used, so the first non-flag argument is inspected. -func warnIfLegacyAlias(args []string, w io.Writer) { - for _, arg := range args { - if strings.HasPrefix(arg, "-") { - continue - } - if arg == "postgres" || arg == "pg" { - _, _ = fmt.Fprintf(w, "Warning: nais %s has moved to nais cloudsql. Use nais cloudsql instead, as nais %[1]s will stop working for Cloud SQL in the future.\n", arg) - } - return - } -} diff --git a/internal/cloudsql/command/cloudsql_test.go b/internal/cloudsql/command/cloudsql_test.go deleted file mode 100644 index 71ce8b7e..00000000 --- a/internal/cloudsql/command/cloudsql_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package command - -import ( - "bytes" - "strings" - "testing" -) - -func TestWarnIfLegacyAlias(t *testing.T) { - for name, tt := range map[string]struct { - args []string - warn bool - }{ - "postgres": {[]string{"postgres", "list"}, true}, - "pg": {[]string{"pg", "psql", "app"}, true}, - "flag before": {[]string{"--team", "x", "postgres"}, false}, - "cloudsql": {[]string{"cloudsql", "list"}, false}, - "alpha postgres": {[]string{"alpha", "postgres", "list"}, false}, - "only flags": {[]string{"--help"}, false}, - "no args": {nil, false}, - } { - t.Run(name, func(t *testing.T) { - var buf bytes.Buffer - warnIfLegacyAlias(tt.args, &buf) - if got := strings.Contains(buf.String(), "has moved to nais cloudsql"); got != tt.warn { - t.Fatalf("warned=%v, want %v (%q)", got, tt.warn, buf.String()) - } - }) - } -} diff --git a/internal/alpha/postgres/access.go b/internal/postgres/access.go similarity index 100% rename from internal/alpha/postgres/access.go rename to internal/postgres/access.go diff --git a/internal/alpha/postgres/access_test.go b/internal/postgres/access_test.go similarity index 100% rename from internal/alpha/postgres/access_test.go rename to internal/postgres/access_test.go diff --git a/internal/alpha/postgres/branch.go b/internal/postgres/branch.go similarity index 100% rename from internal/alpha/postgres/branch.go rename to internal/postgres/branch.go diff --git a/internal/alpha/postgres/command/access.go b/internal/postgres/command/access.go similarity index 96% rename from internal/alpha/postgres/command/access.go rename to internal/postgres/command/access.go index 84b3e8f8..facceb31 100644 --- a/internal/alpha/postgres/command/access.go +++ b/internal/postgres/command/access.go @@ -12,10 +12,10 @@ import ( "syscall" "time" - "github.com/nais/cli/internal/alpha/postgres" - "github.com/nais/cli/internal/alpha/postgres/command/flag" - "github.com/nais/cli/internal/alpha/postgres/relay" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres" + "github.com/nais/cli/internal/postgres/command/flag" + "github.com/nais/cli/internal/postgres/relay" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" "github.com/nais/naistrix/input" @@ -29,7 +29,7 @@ func accessFlags(parent *flag.Postgres) *flag.Access { func psqlCommand(parent *flag.Postgres) *naistrix.Command { f := accessFlags(parent) return &naistrix.Command{ - Name: "psql", Title: "Connect to Nais Postgres via psql (experimental).", + Name: "psql", Title: "Connect to Nais Postgres via psql.", Description: "Request personal access, open a local relay tunnel and start psql with end-to-end TLS verification. If --reason is omitted, prompt for an audit reason interactively.", Args: []naistrix.Argument{{Name: "postgres", Prompt: "Name of the Postgres instance to connect to", ChoicesFunc: postgresNameChoices(parent)}}, Flags: f, ValidateFlagsFunc: validation.RequireTeamFlag(f), @@ -97,7 +97,7 @@ func psqlCommand(parent *flag.Postgres) *naistrix.Command { func proxyCommand(parent *flag.Postgres) *naistrix.Command { f := &flag.Proxy{Postgres: parent, AccessLevel: "read", TTL: 30 * time.Minute, Host: "127.0.0.1"} return &naistrix.Command{ - Name: "proxy", Title: "Expose a Nais Postgres relay tunnel locally (experimental).", + Name: "proxy", Title: "Expose a Nais Postgres relay tunnel locally.", Description: "Request personal access and listen on loopback. If --reason is omitted, prompt for an audit reason interactively. PostgreSQL clients must verify the server certificate; use psql for automatic TLS setup. Credentials are not printed unless --print-password is set.", Args: []naistrix.Argument{{Name: "postgres", Prompt: "Name of the Postgres instance to proxy", ChoicesFunc: postgresNameChoices(parent)}}, Flags: f, ValidateFlagsFunc: validation.RequireTeamFlag(f), @@ -140,7 +140,7 @@ func proxyCommand(parent *flag.Postgres) *naistrix.Command { return err } out.Printf("Postgres relay listening at %s; user: %s; TLS server name: %s; CA: %s\n", listener.Addr(), connection.Username, connection.ServerName, ca.Name()) - out.Println("Use host= hostaddr= sslmode=verify-full sslrootcert=. For automatic setup use 'nais alpha postgres psql'.") + out.Println("Use host= hostaddr= sslmode=verify-full sslrootcert=. For automatic setup use 'nais postgres psql'.") out.Println("Database password is only available here with --print-password (sensitive output).") if f.PrintPassword { out.Errorf("Warning: printing a database password; avoid terminal capture and shell history.\n") diff --git a/internal/alpha/postgres/command/access_reason_test.go b/internal/postgres/command/access_reason_test.go similarity index 100% rename from internal/alpha/postgres/command/access_reason_test.go rename to internal/postgres/command/access_reason_test.go diff --git a/internal/alpha/postgres/command/access_test.go b/internal/postgres/command/access_test.go similarity index 100% rename from internal/alpha/postgres/command/access_test.go rename to internal/postgres/command/access_test.go diff --git a/internal/alpha/postgres/command/branch.go b/internal/postgres/command/branch.go similarity index 97% rename from internal/alpha/postgres/command/branch.go rename to internal/postgres/command/branch.go index 6e5d4ae3..521e6725 100644 --- a/internal/alpha/postgres/command/branch.go +++ b/internal/postgres/command/branch.go @@ -7,9 +7,9 @@ import ( "strings" "time" - "github.com/nais/cli/internal/alpha/postgres" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" "github.com/nais/naistrix/input" @@ -18,7 +18,7 @@ import ( func branchCommand(parent *flag.Postgres) *naistrix.Command { return &naistrix.Command{ - Name: "branch", Title: "Manage branches of a Nais Postgres (experimental).", + Name: "branch", Title: "Manage branches of a Nais Postgres.", Description: "Create, list, inspect, activate and delete branches of a Postgres. A branch is a copy of the database restored from a point in time. Only one branch is active at a time; workloads can also select a branch explicitly with uses.postgres[].branch.", SubCommands: []*naistrix.Command{ branchListCommand(parent), branchCreateCommand(parent), @@ -115,7 +115,7 @@ func branchCreateCommand(parent *flag.Postgres) *naistrix.Command { } else { entered, err := input.Input("Name of the new branch") if errors.Is(err, input.ErrNotInteractive) { - return fmt.Errorf("specify the new branch name as the second positional argument, e.g. nais alpha postgres branch create mypg 5m-restore --ago 5m") + return fmt.Errorf("specify the new branch name as the second positional argument, e.g. nais postgres branch create mypg 5m-restore --ago 5m") } if err != nil { return fmt.Errorf("prompting for new branch name: %w", err) diff --git a/internal/alpha/postgres/command/branch_create_interactive_test.go b/internal/postgres/command/branch_create_interactive_test.go similarity index 99% rename from internal/alpha/postgres/command/branch_create_interactive_test.go rename to internal/postgres/command/branch_create_interactive_test.go index 96e5f0f0..8bdcf3ac 100644 --- a/internal/alpha/postgres/command/branch_create_interactive_test.go +++ b/internal/postgres/command/branch_create_interactive_test.go @@ -43,7 +43,7 @@ func TestBranchCreateNameAndProgress(t *testing.T) { }, { name: "missing branch requires positional argument noninteractively", positionals: []string{"orders"}, resolveEnvironment: true, - wantError: "specify the new branch name as the second positional argument, e.g. nais alpha postgres branch create mypg 5m-restore --ago 5m", + wantError: "specify the new branch name as the second positional argument, e.g. nais postgres branch create mypg 5m-restore --ago 5m", }, {name: "no Postgres", wantError: "Expected at least 1 argument"}, {name: "empty Postgres", positionals: []string{"", "5m-restore"}, wantError: "Postgres name must not be empty"}, diff --git a/internal/alpha/postgres/command/branch_create_test.go b/internal/postgres/command/branch_create_test.go similarity index 99% rename from internal/alpha/postgres/command/branch_create_test.go rename to internal/postgres/command/branch_create_test.go index 71ea578f..3cbfbcfd 100644 --- a/internal/alpha/postgres/command/branch_create_test.go +++ b/internal/postgres/command/branch_create_test.go @@ -12,9 +12,9 @@ import ( "testing" "time" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/flags" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/naistrix" ) diff --git a/internal/alpha/postgres/command/branch_delete_test.go b/internal/postgres/command/branch_delete_test.go similarity index 100% rename from internal/alpha/postgres/command/branch_delete_test.go rename to internal/postgres/command/branch_delete_test.go diff --git a/internal/alpha/postgres/command/branch_test.go b/internal/postgres/command/branch_test.go similarity index 100% rename from internal/alpha/postgres/command/branch_test.go rename to internal/postgres/command/branch_test.go diff --git a/internal/alpha/postgres/command/choices_test.go b/internal/postgres/command/choices_test.go similarity index 100% rename from internal/alpha/postgres/command/choices_test.go rename to internal/postgres/command/choices_test.go diff --git a/internal/alpha/postgres/command/environment.go b/internal/postgres/command/environment.go similarity index 97% rename from internal/alpha/postgres/command/environment.go rename to internal/postgres/command/environment.go index 2720a661..23e184ad 100644 --- a/internal/alpha/postgres/command/environment.go +++ b/internal/postgres/command/environment.go @@ -7,9 +7,9 @@ import ( "sort" "strings" - "github.com/nais/cli/internal/alpha/postgres" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/naisapi" + "github.com/nais/cli/internal/postgres" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" "github.com/nais/naistrix/input" diff --git a/internal/alpha/postgres/command/environment_test.go b/internal/postgres/command/environment_test.go similarity index 98% rename from internal/alpha/postgres/command/environment_test.go rename to internal/postgres/command/environment_test.go index 91c14752..dfc6f0a4 100644 --- a/internal/alpha/postgres/command/environment_test.go +++ b/internal/postgres/command/environment_test.go @@ -4,7 +4,7 @@ import ( "slices" "testing" - "github.com/nais/cli/internal/alpha/postgres" + "github.com/nais/cli/internal/postgres" "github.com/nais/naistrix/output" ) diff --git a/internal/alpha/postgres/command/flag/flag.go b/internal/postgres/command/flag/flag.go similarity index 99% rename from internal/alpha/postgres/command/flag/flag.go rename to internal/postgres/command/flag/flag.go index 75cd378f..aeda5ff9 100644 --- a/internal/alpha/postgres/command/flag/flag.go +++ b/internal/postgres/command/flag/flag.go @@ -6,9 +6,9 @@ import ( "sort" "time" - "github.com/nais/cli/internal/alpha/postgres" "github.com/nais/cli/internal/flags" "github.com/nais/cli/internal/labels" + "github.com/nais/cli/internal/postgres" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" ) diff --git a/internal/alpha/postgres/command/get.go b/internal/postgres/command/get.go similarity index 98% rename from internal/alpha/postgres/command/get.go rename to internal/postgres/command/get.go index 53f842d2..dc142317 100644 --- a/internal/alpha/postgres/command/get.go +++ b/internal/postgres/command/get.go @@ -4,8 +4,8 @@ import ( "context" "strings" - "github.com/nais/cli/internal/alpha/postgres" - "github.com/nais/cli/internal/alpha/postgres/command/flag" + "github.com/nais/cli/internal/postgres" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" "github.com/nais/naistrix/output" diff --git a/internal/alpha/postgres/command/get_readiness_test.go b/internal/postgres/command/get_readiness_test.go similarity index 98% rename from internal/alpha/postgres/command/get_readiness_test.go rename to internal/postgres/command/get_readiness_test.go index 9e7f10b3..f768850e 100644 --- a/internal/alpha/postgres/command/get_readiness_test.go +++ b/internal/postgres/command/get_readiness_test.go @@ -3,8 +3,8 @@ package command import ( "testing" - "github.com/nais/cli/internal/alpha/postgres" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres" ) func TestPostgresReadiness(t *testing.T) { diff --git a/internal/alpha/postgres/command/get_test.go b/internal/postgres/command/get_test.go similarity index 99% rename from internal/alpha/postgres/command/get_test.go rename to internal/postgres/command/get_test.go index e67153e8..2399b3a5 100644 --- a/internal/alpha/postgres/command/get_test.go +++ b/internal/postgres/command/get_test.go @@ -13,8 +13,8 @@ import ( "strings" "testing" - "github.com/nais/cli/internal/alpha/postgres" "github.com/nais/cli/internal/flags" + "github.com/nais/cli/internal/postgres" "github.com/nais/naistrix" ) diff --git a/internal/alpha/postgres/command/hint.go b/internal/postgres/command/hint.go similarity index 80% rename from internal/alpha/postgres/command/hint.go rename to internal/postgres/command/hint.go index 2f257b3e..68671732 100644 --- a/internal/alpha/postgres/command/hint.go +++ b/internal/postgres/command/hint.go @@ -14,11 +14,11 @@ func quoteShellArgument(value string) string { } func postgresGetCommandLine(name, team, environment, configFile string) string { - return postgresCommandLine("nais alpha postgres get "+quoteShellArgument(name), team, environment, configFile) + return postgresCommandLine("nais postgres get "+quoteShellArgument(name), team, environment, configFile) } func branchListCommandLine(name, team, environment, configFile string) string { - return postgresCommandLine("nais alpha postgres branch list "+quoteShellArgument(name), team, environment, configFile) + return postgresCommandLine("nais postgres branch list "+quoteShellArgument(name), team, environment, configFile) } func postgresCommandLine(command, team, environment, configFile string) string { diff --git a/internal/alpha/postgres/command/inspection_commands_test.go b/internal/postgres/command/inspection_commands_test.go similarity index 88% rename from internal/alpha/postgres/command/inspection_commands_test.go rename to internal/postgres/command/inspection_commands_test.go index 3e0d35d3..aebc2eee 100644 --- a/internal/alpha/postgres/command/inspection_commands_test.go +++ b/internal/postgres/command/inspection_commands_test.go @@ -17,9 +17,9 @@ func TestPostgresInspectionHelpAndCompletion(t *testing.T) { args []string rejected bool }{ - {name: "help lists get instead of top-level status", args: []string{"alpha", "postgres", "--help"}}, - {name: "completion lists get instead of top-level status", args: []string{"__complete", "alpha", "postgres", ""}}, - {name: "removed command is not aliased", args: []string{"alpha", "postgres", "status", "orders"}, rejected: true}, + {name: "help lists get instead of top-level status", args: []string{"postgres", "--help"}}, + {name: "completion lists get instead of top-level status", args: []string{"__complete", "postgres", ""}}, + {name: "removed command is not aliased", args: []string{"postgres", "status", "orders"}, rejected: true}, } { t.Run(tt.name, func(t *testing.T) { t.Setenv("HOME", t.TempDir()) diff --git a/internal/alpha/postgres/command/instance.go b/internal/postgres/command/instance.go similarity index 98% rename from internal/alpha/postgres/command/instance.go rename to internal/postgres/command/instance.go index eaae8b87..cc01b362 100644 --- a/internal/alpha/postgres/command/instance.go +++ b/internal/postgres/command/instance.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/nais/cli/internal/alpha/postgres" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" "github.com/nais/naistrix/input" diff --git a/internal/alpha/postgres/command/instance_create_environment_test.go b/internal/postgres/command/instance_create_environment_test.go similarity index 99% rename from internal/alpha/postgres/command/instance_create_environment_test.go rename to internal/postgres/command/instance_create_environment_test.go index 5dc40b6c..222238a1 100644 --- a/internal/alpha/postgres/command/instance_create_environment_test.go +++ b/internal/postgres/command/instance_create_environment_test.go @@ -11,9 +11,9 @@ import ( "strings" "testing" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/flags" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" ) diff --git a/internal/alpha/postgres/command/instance_test.go b/internal/postgres/command/instance_test.go similarity index 98% rename from internal/alpha/postgres/command/instance_test.go rename to internal/postgres/command/instance_test.go index 129aab31..77999210 100644 --- a/internal/alpha/postgres/command/instance_test.go +++ b/internal/postgres/command/instance_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/flags" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/naistrix" ) diff --git a/internal/alpha/postgres/command/instance_update_test.go b/internal/postgres/command/instance_update_test.go similarity index 100% rename from internal/alpha/postgres/command/instance_update_test.go rename to internal/postgres/command/instance_update_test.go diff --git a/internal/alpha/postgres/command/postgres.go b/internal/postgres/command/postgres.go similarity index 85% rename from internal/alpha/postgres/command/postgres.go rename to internal/postgres/command/postgres.go index 8fc7d788..f64f132e 100644 --- a/internal/alpha/postgres/command/postgres.go +++ b/internal/postgres/command/postgres.go @@ -3,10 +3,10 @@ package command import ( "context" - "github.com/nais/cli/internal/alpha/postgres" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/flags" "github.com/nais/cli/internal/labels" + "github.com/nais/cli/internal/postgres" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/naistrix" "github.com/nais/naistrix/output" ) @@ -14,8 +14,8 @@ import ( func Postgres(parentFlags *flags.GlobalFlags) *naistrix.Command { flags := &flag.Postgres{GlobalFlags: parentFlags} return &naistrix.Command{ - Name: "postgres", Title: "Manage Nais Postgres instances (experimental).", - Description: "Experimental commands for Nais Postgres branches and brokered personal access.", StickyFlags: flags, + Name: "postgres", Title: "Manage Nais Postgres instances.", + Description: "Manage Nais Postgres instances, branches, and brokered personal access.", StickyFlags: flags, SubCommands: []*naistrix.Command{ listCommand(flags), instanceGetCommand(flags), diff --git a/internal/alpha/postgres/command/progress.go b/internal/postgres/command/progress.go similarity index 100% rename from internal/alpha/postgres/command/progress.go rename to internal/postgres/command/progress.go diff --git a/internal/alpha/postgres/command/progress_test.go b/internal/postgres/command/progress_test.go similarity index 100% rename from internal/alpha/postgres/command/progress_test.go rename to internal/postgres/command/progress_test.go diff --git a/internal/alpha/postgres/command/readiness.go b/internal/postgres/command/readiness.go similarity index 95% rename from internal/alpha/postgres/command/readiness.go rename to internal/postgres/command/readiness.go index 10bc0c7d..e5c2919e 100644 --- a/internal/alpha/postgres/command/readiness.go +++ b/internal/postgres/command/readiness.go @@ -1,8 +1,8 @@ package command import ( - "github.com/nais/cli/internal/alpha/postgres" "github.com/nais/cli/internal/naisapi/gql" + "github.com/nais/cli/internal/postgres" ) func postgresReadiness(status postgres.BranchStatus) string { diff --git a/internal/alpha/postgres/command/recovery_time_test.go b/internal/postgres/command/recovery_time_test.go similarity index 100% rename from internal/alpha/postgres/command/recovery_time_test.go rename to internal/postgres/command/recovery_time_test.go diff --git a/internal/alpha/postgres/command/validation_test.go b/internal/postgres/command/validation_test.go similarity index 96% rename from internal/alpha/postgres/command/validation_test.go rename to internal/postgres/command/validation_test.go index d4208ab1..b3713e65 100644 --- a/internal/alpha/postgres/command/validation_test.go +++ b/internal/postgres/command/validation_test.go @@ -4,8 +4,8 @@ import ( "errors" "testing" - "github.com/nais/cli/internal/alpha/postgres/command/flag" "github.com/nais/cli/internal/flags" + "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/cli/internal/validation" "github.com/nais/naistrix" ) diff --git a/internal/alpha/postgres/get.go b/internal/postgres/get.go similarity index 100% rename from internal/alpha/postgres/get.go rename to internal/postgres/get.go diff --git a/internal/alpha/postgres/instance.go b/internal/postgres/instance.go similarity index 100% rename from internal/alpha/postgres/instance.go rename to internal/postgres/instance.go diff --git a/internal/alpha/postgres/instance_test.go b/internal/postgres/instance_test.go similarity index 100% rename from internal/alpha/postgres/instance_test.go rename to internal/postgres/instance_test.go diff --git a/internal/alpha/postgres/list.go b/internal/postgres/list.go similarity index 100% rename from internal/alpha/postgres/list.go rename to internal/postgres/list.go diff --git a/internal/alpha/postgres/list_test.go b/internal/postgres/list_test.go similarity index 100% rename from internal/alpha/postgres/list_test.go rename to internal/postgres/list_test.go diff --git a/internal/alpha/postgres/relay/relay.go b/internal/postgres/relay/relay.go similarity index 100% rename from internal/alpha/postgres/relay/relay.go rename to internal/postgres/relay/relay.go diff --git a/internal/alpha/postgres/relay/relay_test.go b/internal/postgres/relay/relay_test.go similarity index 100% rename from internal/alpha/postgres/relay/relay_test.go rename to internal/postgres/relay/relay_test.go From 0647e60d8dac570383829c54bfefe40b8b55271d Mon Sep 17 00:00:00 2001 From: Frode Sundby Date: Thu, 22 Oct 2026 13:51:15 +0200 Subject: [PATCH 2/2] fix(postgres): use authenticated tenant for console links --- internal/postgres/list.go | 12 +++++++----- internal/postgres/list_test.go | 36 ++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/internal/postgres/list.go b/internal/postgres/list.go index 434c7110..430d9978 100644 --- a/internal/postgres/list.go +++ b/internal/postgres/list.go @@ -9,8 +9,6 @@ import ( "github.com/nais/naistrix/output" ) -const consoleBaseURL = "https://console.nav.cloud.nais.io" - type PostgresInstance struct { Name output.Link `json:"name"` Environment string `json:"environment"` @@ -48,6 +46,10 @@ func GetTeamPostgreses(ctx context.Context, team string, environments []string, if err != nil { return nil, err } + user, err := naisapi.GetAuthenticatedUser(ctx) + if err != nil { + return nil, err + } filter := &gql.TeamPostgresFilter{Environments: environments, Labels: labelFilters} var ret []PostgresInstance var after *string @@ -56,7 +58,7 @@ func GetTeamPostgreses(ctx context.Context, team string, environments []string, if err != nil { return nil, err } - ret = append(ret, postgresesFromTeam(resp.Team, team)...) + ret = append(ret, postgresesFromTeam(resp.Team, team, user.ConsoleHost())...) if !resp.Team.Postgreses.PageInfo.HasNextPage { break } @@ -69,12 +71,12 @@ func GetTeamPostgreses(ctx context.Context, team string, environments []string, return ret, nil } -func postgresesFromTeam(data gql.GetTeamPostgresesTeam, team string) []PostgresInstance { +func postgresesFromTeam(data gql.GetTeamPostgresesTeam, team, consoleHost string) []PostgresInstance { ret := make([]PostgresInstance, 0, len(data.Postgreses.Nodes)) for _, pg := range data.Postgreses.Nodes { env := pg.TeamEnvironment.Environment.Name instance := PostgresInstance{ - Name: output.Link{Name: pg.Name, URL: fmt.Sprintf("%s/team/%s/%s/postgres/%s", consoleBaseURL, team, env, pg.Name)}, + Name: output.Link{Name: pg.Name, URL: fmt.Sprintf("https://%s/team/%s/%s/postgres/%s", consoleHost, team, env, pg.Name)}, Environment: env, Version: pg.MajorVersion, HighAvailability: pg.HighAvailability, } diff --git a/internal/postgres/list_test.go b/internal/postgres/list_test.go index 94da3b1c..ae7486b2 100644 --- a/internal/postgres/list_test.go +++ b/internal/postgres/list_test.go @@ -2,7 +2,10 @@ package postgres import ( "encoding/json" + "net/http" + "net/http/httptest" "reflect" + "strings" "testing" "github.com/nais/cli/internal/naisapi/gql" @@ -18,11 +21,36 @@ func TestPostgresesFromTeam(t *testing.T) { if err := json.Unmarshal([]byte(teamData), &data); err != nil { t.Fatal(err) } + for _, host := range []string{"console.nav.cloud.nais.io", "console.dev-nais.cloud.nais.io"} { + t.Run(host, func(t *testing.T) { + want := []PostgresInstance{ + {Name: output.Link{Name: "orders", URL: "https://" + host + "/team/my-team/dev/postgres/orders"}, Environment: "dev", Version: "17", HighAvailability: true, ActiveBranch: "main"}, + {Name: output.Link{Name: "reports", URL: "https://" + host + "/team/my-team/prod/postgres/reports"}, Environment: "prod", Version: "16"}, + } + if got := postgresesFromTeam(data, "my-team", host); !reflect.DeepEqual(got, want) { + t.Errorf("postgresesFromTeam() = %#v, want %#v", got, want) + } + }) + } +} + +func TestGetTeamPostgresesUsesAuthenticatedConsoleHost(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"data":{"team":{"postgreses":{"nodes":[{"name":"orders","teamEnvironment":{"environment":{"name":"dev"}},"majorVersion":"18","highAvailability":true}],"pageInfo":{"hasNextPage":false}}}}}`)) + })) + defer server.Close() + host := strings.TrimPrefix(server.URL, "http://") + t.Setenv("NAIS_API_LOCAL_HOST", host) + + got, err := GetTeamPostgreses(t.Context(), "my-team", nil, nil) + if err != nil { + t.Fatal(err) + } want := []PostgresInstance{ - {Name: output.Link{Name: "orders", URL: consoleBaseURL + "/team/my-team/dev/postgres/orders"}, Environment: "dev", Version: "17", HighAvailability: true, ActiveBranch: "main"}, - {Name: output.Link{Name: "reports", URL: consoleBaseURL + "/team/my-team/prod/postgres/reports"}, Environment: "prod", Version: "16"}, + {Name: output.Link{Name: "orders", URL: "https://" + host + "/team/my-team/dev/postgres/orders"}, Environment: "dev", Version: "18", HighAvailability: true}, } - if got := postgresesFromTeam(data, "my-team"); !reflect.DeepEqual(got, want) { - t.Errorf("postgresesFromTeam() = %#v, want %#v", got, want) + if !reflect.DeepEqual(got, want) { + t.Errorf("GetTeamPostgreses() = %#v, want %#v", got, want) } }