Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 0 additions & 2 deletions internal/alpha/command/alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -19,7 +18,6 @@ func Alpha(parentFlags *flags.GlobalFlags) *naistrix.Command {
SubCommands: []*naistrix.Command{
krakend.Krakend(flags),
mcpcmd.MCP(flags),
postgrescmd.Postgres(parentFlags),
},
}
}
28 changes: 0 additions & 28 deletions internal/alpha/postgres/list_test.go

This file was deleted.

2 changes: 2 additions & 0 deletions internal/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions internal/application/completion_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
82 changes: 82 additions & 0 deletions internal/application/postgres_migration_test.go
Original file line number Diff line number Diff line change
@@ -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())
}
})
}
}
23 changes: 1 addition & 22 deletions internal/cloudsql/command/cloudsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
Expand All @@ -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
}
}
30 changes: 0 additions & 30 deletions internal/cloudsql/command/cloudsql_test.go

This file was deleted.

File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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=<server name> hostaddr=<loopback IP> sslmode=verify-full sslrootcert=<CA path>. For automatic setup use 'nais alpha postgres psql'.")
out.Println("Use host=<server name> hostaddr=<loopback IP> sslmode=verify-full sslrootcert=<CA path>. 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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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),
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading