From df156a17e195c21f0afe899124ac7ad3d3111d2f Mon Sep 17 00:00:00 2001 From: Vegar Sechmann Molvig Date: Fri, 6 Feb 2026 15:40:16 +0100 Subject: [PATCH 1/2] feat: let user specify env/team with fallback to ctx/ns --- internal/postgres/access.go | 20 +++--- internal/postgres/audit.go | 20 +++--- internal/postgres/command/enable_audit.go | 2 +- internal/postgres/command/flag/environment.go | 24 +++++++ internal/postgres/command/flag/flag.go | 7 +- internal/postgres/command/password.go | 2 +- internal/postgres/command/postgres.go | 1 + internal/postgres/command/prepare.go | 15 ++-- internal/postgres/command/proxy.go | 2 +- internal/postgres/command/psql.go | 2 +- internal/postgres/command/revoke.go | 12 ++-- internal/postgres/command/users.go | 6 +- internal/postgres/command/verify_audit.go | 2 +- internal/postgres/iam.go | 24 +++---- internal/postgres/password.go | 6 +- internal/postgres/proxy.go | 8 +-- internal/postgres/psql.go | 6 +- internal/postgres/secret.go | 71 +++++++------------ 18 files changed, 112 insertions(+), 118 deletions(-) create mode 100644 internal/postgres/command/flag/environment.go diff --git a/internal/postgres/access.go b/internal/postgres/access.go index b9ffe55f..da6a9141 100644 --- a/internal/postgres/access.go +++ b/internal/postgres/access.go @@ -34,37 +34,37 @@ var ( revokeUsage = `REVOKE USAGE ON SCHEMA $schema FROM cloudsqliamuser;` ) -func PrepareAccess(ctx context.Context, appName string, namespace flag.Namespace, cluster flag.Context, schema string, allPrivs bool, out *naistrix.OutputWriter) error { +func PrepareAccess(ctx context.Context, appName string, fl *flag.Prepare, out *naistrix.OutputWriter) error { // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonPrepareAccess, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonPrepareAccess, out); err != nil { return err } prependUsageIfNotPublic := func(statement string) string { - if schema != "public" { + if fl.Schema != "public" { return grantUsage + "\n" + statement } return statement } - if allPrivs { - return sqlExecAsAppUser(ctx, appName, namespace, cluster, schema, prependUsageIfNotPublic(grantAllPrivs)) + if fl.AllPrivileges { + return sqlExecAsAppUser(ctx, appName, fl.Namespace, fl.Context, fl.Schema, prependUsageIfNotPublic(grantAllPrivs)) } else { - return sqlExecAsAppUser(ctx, appName, namespace, cluster, schema, prependUsageIfNotPublic(grantSelectPrivs)) + return sqlExecAsAppUser(ctx, appName, fl.Namespace, fl.Context, fl.Schema, prependUsageIfNotPublic(grantSelectPrivs)) } } -func RevokeAccess(ctx context.Context, appName string, namespace flag.Namespace, cluster flag.Context, schema string, out *naistrix.OutputWriter) error { +func RevokeAccess(ctx context.Context, appName string, fl *flag.Revoke, out *naistrix.OutputWriter) error { // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonRevokeAccess, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonRevokeAccess, out); err != nil { return err } q := revokeAllPrivs - if schema != "public" { + if fl.Schema != "public" { q += "\n" + revokeUsage } - return sqlExecAsAppUser(ctx, appName, namespace, cluster, schema, q) + return sqlExecAsAppUser(ctx, appName, fl.Namespace, fl.Context, fl.Schema, q) } func sqlExecAsAppUser(ctx context.Context, appName string, namespace flag.Namespace, cluster flag.Context, schema, statement string) error { diff --git a/internal/postgres/audit.go b/internal/postgres/audit.go index 0d0922f5..83ead386 100644 --- a/internal/postgres/audit.go +++ b/internal/postgres/audit.go @@ -12,20 +12,20 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -func EnableAuditLogging(ctx context.Context, appName string, cluster flag.Context, namespace flag.Namespace, out *naistrix.OutputWriter) error { +func EnableAuditLogging(ctx context.Context, appName string, fl *flag.EnableAudit, out *naistrix.OutputWriter) error { // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonEnableAudit, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonEnableAudit, out); err != nil { return err } - return enableAuditAsAppUser(ctx, appName, namespace, cluster, out) + return enableAuditAsAppUser(ctx, appName, fl.Namespace, fl.Context, out) } -func VerifyAuditLogging(ctx context.Context, appName string, cluster flag.Context, namespace flag.Namespace, out *naistrix.OutputWriter) error { +func VerifyAuditLogging(ctx context.Context, appName string, fl *flag.VerifyAudit, out *naistrix.OutputWriter) error { // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonVerifyAudit, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonVerifyAudit, out); err != nil { return err } - _, err := verifyAuditAsAppUser(ctx, appName, namespace, cluster, out) + _, err := verifyAuditAsAppUser(ctx, appName, fl.Namespace, fl.Context, out) return err } @@ -166,23 +166,23 @@ func getDBFlags(ctx context.Context, info *CloudSQLDBInfo) (map[string]string, e return dbFlags, fmt.Errorf("GetDBInstance: expected one sqlinstance for app %q in %q, got %d", info.appName, info.namespace, len(sqlInstances.Items)) } - spec, ok := sqlInstances.Items[0].Object["spec"].(map[string]interface{}) + spec, ok := sqlInstances.Items[0].Object["spec"].(map[string]any) if !ok { return dbFlags, fmt.Errorf("GetDBInstance: error accessing spec for app %q in %q", info.appName, info.namespace) } - settings, ok := spec["settings"].(map[string]interface{}) + settings, ok := spec["settings"].(map[string]any) if !ok { return dbFlags, fmt.Errorf("GetDBInstance: error accessing settings for app %q in %q", info.appName, info.namespace) } - databaseFlags, ok := settings["databaseFlags"].([]interface{}) + databaseFlags, ok := settings["databaseFlags"].([]any) if !ok { return dbFlags, fmt.Errorf("GetDBInstance: error accessing databaseFlags for app %q in %q", info.appName, info.namespace) } for _, flag := range databaseFlags { - f, ok := flag.(map[string]interface{}) + f, ok := flag.(map[string]any) if !ok { return dbFlags, fmt.Errorf("GetDBInstance: error accessing databaseFlags for app %q in %q", info.appName, info.namespace) } diff --git a/internal/postgres/command/enable_audit.go b/internal/postgres/command/enable_audit.go index 458685b2..7086d8d5 100644 --- a/internal/postgres/command/enable_audit.go +++ b/internal/postgres/command/enable_audit.go @@ -20,7 +20,7 @@ func enableAuditCommand(parentFlags *flag.Postgres) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - err := postgres.EnableAuditLogging(ctx, args.Get("app_name"), flags.Context, flags.Namespace, out) + err := postgres.EnableAuditLogging(ctx, args.Get("app_name"), flags, out) if err != nil { metric.CreateAndIncreaseCounter(ctx, "enable_audit_logging_error") } diff --git a/internal/postgres/command/flag/environment.go b/internal/postgres/command/flag/environment.go new file mode 100644 index 00000000..fb1ec43d --- /dev/null +++ b/internal/postgres/command/flag/environment.go @@ -0,0 +1,24 @@ +package flag + +import ( + "context" + "fmt" + + "github.com/nais/cli/internal/naisapi" + "github.com/nais/naistrix" +) + +type Environment string + +func (e *Environment) AutoComplete(ctx context.Context, args *naistrix.Arguments, str string, flags any) ([]string, string) { + return autoCompleteEnvironments(ctx) +} + +func autoCompleteEnvironments(ctx context.Context) ([]string, string) { + envs, err := naisapi.GetAllEnvironments(ctx) + if err != nil { + return nil, fmt.Sprintf("Failed to fetch environments for auto-completion: %v", err) + } + + return envs, "Available environments" +} diff --git a/internal/postgres/command/flag/flag.go b/internal/postgres/command/flag/flag.go index 6edc14f7..3e3cf27e 100644 --- a/internal/postgres/command/flag/flag.go +++ b/internal/postgres/command/flag/flag.go @@ -4,9 +4,10 @@ import "github.com/nais/cli/internal/flags" type Postgres struct { *flags.GlobalFlags - Namespace Namespace `name:"namespace" short:"n" usage:"The kubernetes |NAMESPACE| to use. Defaults to current namespace."` - Context Context `name:"context" short:"c" usage:"The kubeconfig |CONTEXT| to use. Defaults to current context."` - Reason string `name:"reason" short:"r" usage:"Justification for accessing the database. Required for audit logging."` + Namespace Namespace `name:"namespace" short:"n" usage:"The kubernetes |NAMESPACE| to use. Defaults to current namespace."` + Context Context `name:"context" short:"c" usage:"The kubeconfig |CONTEXT| to use. Defaults to current context."` + Environment Environment `name:"environment" short:"e" usage:"The |ENVIRONMENT| to use. Defaults to same as context."` + Reason string `name:"reason" short:"r" usage:"Justification for accessing the database. Required for audit logging."` } type Migrate struct { diff --git a/internal/postgres/command/password.go b/internal/postgres/command/password.go index e3eea808..fbe54b77 100644 --- a/internal/postgres/command/password.go +++ b/internal/postgres/command/password.go @@ -23,7 +23,7 @@ func passwordCommand(parentFlags *flag.Postgres) *naistrix.Command { {Name: "app_name"}, }, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - return postgres.RotatePassword(ctx, args.Get("app_name"), flags.Context, flags.Namespace, out) + return postgres.RotatePassword(ctx, args.Get("app_name"), flags, out) }, }, }, diff --git a/internal/postgres/command/postgres.go b/internal/postgres/command/postgres.go index 2a403dc0..f86bba51 100644 --- a/internal/postgres/command/postgres.go +++ b/internal/postgres/command/postgres.go @@ -16,6 +16,7 @@ func Postgres(parentFlags *flags.GlobalFlags) *naistrix.Command { GlobalFlags: parentFlags, Namespace: flag.Namespace(defaultNamespace), Context: flag.Context(defaultContext), + Environment: flag.Environment(defaultContext), } return &naistrix.Command{ diff --git a/internal/postgres/command/prepare.go b/internal/postgres/command/prepare.go index 19e56522..b44a5226 100644 --- a/internal/postgres/command/prepare.go +++ b/internal/postgres/command/prepare.go @@ -1,17 +1,15 @@ package command import ( - "bufio" "context" "fmt" - "os" - "strings" _ "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/postgres" "github.com/MakeNowJust/heredoc/v2" "github.com/nais/cli/internal/postgres" "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/naistrix" + "github.com/pterm/pterm" ) func prepareCommand(parentFlags *flag.Postgres) *naistrix.Command { @@ -35,17 +33,12 @@ func prepareCommand(parentFlags *flag.Postgres) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - out.Println("", "Are you sure you want to continue (y/N): ") - i, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return fmt.Errorf("failed to read input: %w", err) - } - - if !strings.EqualFold(strings.TrimSpace(i), "y") { + result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure you want to continue?") + if !result { return fmt.Errorf("cancelled by user") } - return postgres.PrepareAccess(ctx, args.Get("app_name"), flags.Namespace, flags.Context, flags.Schema, flags.AllPrivileges, out) + return postgres.PrepareAccess(ctx, args.Get("app_name"), flags, out) }, } } diff --git a/internal/postgres/command/proxy.go b/internal/postgres/command/proxy.go index ca3ab51a..679b6325 100644 --- a/internal/postgres/command/proxy.go +++ b/internal/postgres/command/proxy.go @@ -24,7 +24,7 @@ func proxyCommand(parentFlags *flag.Postgres) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - return postgres.RunProxy(ctx, args.Get("app_name"), flags.Context, flags.Namespace, flags.Host, flags.Port, flags.IsVerbose(), flags.Reason, out) + return postgres.RunProxy(ctx, args.Get("app_name"), flags, out) }, } } diff --git a/internal/postgres/command/psql.go b/internal/postgres/command/psql.go index bf8fb271..5ed31aa5 100644 --- a/internal/postgres/command/psql.go +++ b/internal/postgres/command/psql.go @@ -19,7 +19,7 @@ func psqlCommand(parentFlags *flag.Postgres) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - return postgres.RunPSQL(ctx, args.Get("app_name"), flags.Context, flags.Namespace, flags.Reason, out) + return postgres.RunPSQL(ctx, args.Get("app_name"), flags, out) }, } } diff --git a/internal/postgres/command/revoke.go b/internal/postgres/command/revoke.go index d367a579..1044d009 100644 --- a/internal/postgres/command/revoke.go +++ b/internal/postgres/command/revoke.go @@ -1,17 +1,15 @@ package command import ( - "bufio" "context" "fmt" - "os" - "strings" _ "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/postgres" "github.com/MakeNowJust/heredoc/v2" "github.com/nais/cli/internal/postgres" "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/naistrix" + "github.com/pterm/pterm" ) func revokeCommand(parentFlags *flag.Postgres) *naistrix.Command { @@ -35,14 +33,12 @@ func revokeCommand(parentFlags *flag.Postgres) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - out.Println("", "Are you sure you want to continue (y/N): ") - input := bufio.NewScanner(os.Stdin) - input.Scan() - if !strings.EqualFold(strings.TrimSpace(input.Text()), "y") { + result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure you want to continue?") + if !result { return fmt.Errorf("cancelled by user") } - return postgres.RevokeAccess(ctx, args.Get("app_name"), flags.Namespace, flags.Context, flags.Schema, out) + return postgres.RevokeAccess(ctx, args.Get("app_name"), flags, out) }, } } diff --git a/internal/postgres/command/users.go b/internal/postgres/command/users.go index 73a40507..056b1ca1 100644 --- a/internal/postgres/command/users.go +++ b/internal/postgres/command/users.go @@ -38,7 +38,7 @@ func addCommand(parentFlags *flag.User) *naistrix.Command { }, Flags: userAddFlags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - return postgres.AddUser(ctx, args.Get("app_name"), args.Get("username"), args.Get("password"), userAddFlags.Context, userAddFlags.Namespace, userAddFlags.Privilege, out) + return postgres.AddUser(ctx, args.Get("app_name"), args.Get("username"), args.Get("password"), userAddFlags, out) }, } } @@ -53,7 +53,7 @@ func listCommand(parentFlags *flag.User) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - return postgres.ListUsers(ctx, args.Get("app_name"), flags.Context, flags.Namespace, out) + return postgres.ListUsers(ctx, args.Get("app_name"), flags, out) }, } } @@ -70,7 +70,7 @@ func dropCommand(parentFlags *flag.User) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - return postgres.DropUser(ctx, args.Get("app_name"), args.Get("username"), flags.Context, flags.Namespace, out) + return postgres.DropUser(ctx, args.Get("app_name"), args.Get("username"), flags, out) }, } } diff --git a/internal/postgres/command/verify_audit.go b/internal/postgres/command/verify_audit.go index 0a303e70..2896f7c4 100644 --- a/internal/postgres/command/verify_audit.go +++ b/internal/postgres/command/verify_audit.go @@ -20,7 +20,7 @@ func verifyAuditCommand(parentFlags *flag.Postgres) *naistrix.Command { }, Flags: flags, RunFunc: func(ctx context.Context, args *naistrix.Arguments, out *naistrix.OutputWriter) error { - err := postgres.VerifyAuditLogging(ctx, args.Get("app_name"), flags.Context, flags.Namespace, out) + err := postgres.VerifyAuditLogging(ctx, args.Get("app_name"), flags, out) if err != nil { metric.CreateAndIncreaseCounter(ctx, "verify_audit_logging_error") } diff --git a/internal/postgres/iam.go b/internal/postgres/iam.go index ba988814..7d8be1f6 100644 --- a/internal/postgres/iam.go +++ b/internal/postgres/iam.go @@ -211,13 +211,13 @@ func formatCondition(expr, title string) string { return fmt.Sprintf("expression=%v,title=%v", expr, title) } -func ListUsers(ctx context.Context, appName string, cluster flag.Context, namespace flag.Namespace, out *naistrix.OutputWriter) error { +func ListUsers(ctx context.Context, appName string, fl *flag.UserList, out *naistrix.OutputWriter) error { // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonListUsers, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonListUsers, out); err != nil { return err } - dbInfo, err := NewDBInfo(ctx, appName, namespace, cluster) + dbInfo, err := NewDBInfo(ctx, appName, fl.Namespace, fl.Context) if err != nil { return err } @@ -255,18 +255,18 @@ func ListUsers(ctx context.Context, appName string, cluster flag.Context, namesp return err } -func AddUser(ctx context.Context, appName, username, password string, cluster flag.Context, namespace flag.Namespace, privilege string, out *naistrix.OutputWriter) error { - err := validateSQLVariables(username, password, privilege) +func AddUser(ctx context.Context, appName, username, password string, fl *flag.UserAdd, out *naistrix.OutputWriter) error { + err := validateSQLVariables(username, password, fl.Privilege) if err != nil { return err } // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonAddUser, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonAddUser, out); err != nil { return err } - dbInfo, err := NewDBInfo(ctx, appName, namespace, cluster) + dbInfo, err := NewDBInfo(ctx, appName, fl.Namespace, fl.Context) if err != nil { return err } @@ -287,12 +287,12 @@ func AddUser(ctx context.Context, appName, username, password string, cluster fl } out.Printf("Created user: %v", username) - _, err = db.ExecContext(ctx, fmt.Sprintf(`alter default privileges in schema public grant %v on tables to %q;`, privilege, username)) + _, err = db.ExecContext(ctx, fmt.Sprintf(`alter default privileges in schema public grant %v on tables to %q;`, fl.Privilege, username)) if err != nil { return formatInvalidGrantError(err) } - _, err = db.ExecContext(ctx, fmt.Sprintf(`grant %v on all tables in schema public to %q;`, privilege, username)) + _, err = db.ExecContext(ctx, fmt.Sprintf(`grant %v on all tables in schema public to %q;`, fl.Privilege, username)) if err != nil { return formatInvalidGrantError(err) } @@ -300,13 +300,13 @@ func AddUser(ctx context.Context, appName, username, password string, cluster fl return nil } -func DropUser(ctx context.Context, appName string, username string, cluster flag.Context, namespace flag.Namespace, out *naistrix.OutputWriter) error { +func DropUser(ctx context.Context, appName string, username string, fl *flag.UserDrop, out *naistrix.OutputWriter) error { // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonDropUser, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonDropUser, out); err != nil { return err } - dbInfo, err := NewDBInfo(ctx, appName, namespace, cluster) + dbInfo, err := NewDBInfo(ctx, appName, fl.Namespace, fl.Context) if err != nil { return err } diff --git a/internal/postgres/password.go b/internal/postgres/password.go index 417cc79d..9d7537dd 100644 --- a/internal/postgres/password.go +++ b/internal/postgres/password.go @@ -17,13 +17,13 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func RotatePassword(ctx context.Context, appName string, cluster flag.Context, namespace flag.Namespace, out *naistrix.OutputWriter) error { +func RotatePassword(ctx context.Context, appName string, fl *flag.Password, out *naistrix.OutputWriter) error { // Get secret values (access is logged for audit purposes) - if _, err := GetSecretValues(ctx, appName, namespace, cluster, ReasonPasswordRotate, out); err != nil { + if _, err := GetSecretValues(ctx, appName, fl.Postgres, ReasonPasswordRotate, out); err != nil { return err } - dbInfo, err := NewDBInfo(ctx, appName, namespace, cluster) + dbInfo, err := NewDBInfo(ctx, appName, fl.Namespace, fl.Context) if err != nil { return err } diff --git a/internal/postgres/proxy.go b/internal/postgres/proxy.go index 4a31ea1b..a1fb4565 100644 --- a/internal/postgres/proxy.go +++ b/internal/postgres/proxy.go @@ -11,18 +11,18 @@ import ( "github.com/nais/naistrix" ) -func RunProxy(ctx context.Context, appName string, cluster flag.Context, namespace flag.Namespace, host string, port uint, verbose bool, reason string, out *naistrix.OutputWriter) error { +func RunProxy(ctx context.Context, appName string, fl *flag.Proxy, out *naistrix.OutputWriter) error { // Get secret values with user-provided reason (access is logged for audit purposes) - if _, err := GetSecretValuesWithUserReason(ctx, appName, namespace, cluster, reason, out); err != nil { + if _, err := GetSecretValuesWithUserReason(ctx, appName, fl.Postgres, fl.Reason, out); err != nil { return err } - dbInfo, err := NewDBInfo(ctx, appName, namespace, cluster) + dbInfo, err := NewDBInfo(ctx, appName, fl.Namespace, fl.Context) if err != nil { return err } - return dbInfo.RunProxy(ctx, host, &port, make(chan<- int, 1), out, true) + return dbInfo.RunProxy(ctx, fl.Host, &fl.Port, make(chan<- int, 1), out, true) } func copy(closer chan struct{}, dst io.Writer, src io.Reader) { diff --git a/internal/postgres/psql.go b/internal/postgres/psql.go index f0d9271d..cccae57f 100644 --- a/internal/postgres/psql.go +++ b/internal/postgres/psql.go @@ -11,9 +11,9 @@ import ( "github.com/nais/naistrix" ) -func RunPSQL(ctx context.Context, appName string, cluster flag.Context, namespace flag.Namespace, reason string, out *naistrix.OutputWriter) error { +func RunPSQL(ctx context.Context, appName string, fl *flag.Psql, out *naistrix.OutputWriter) error { // Get secret values with user-provided reason (access is logged for audit purposes) - if _, err := GetSecretValuesWithUserReason(ctx, appName, namespace, cluster, reason, out); err != nil { + if _, err := GetSecretValuesWithUserReason(ctx, appName, fl.Postgres, fl.Reason, out); err != nil { return err } @@ -22,7 +22,7 @@ func RunPSQL(ctx context.Context, appName string, cluster flag.Context, namespac return err } - dbInfo, err := NewDBInfo(ctx, appName, namespace, cluster) + dbInfo, err := NewDBInfo(ctx, appName, fl.Namespace, fl.Context) if err != nil { return err } diff --git a/internal/postgres/secret.go b/internal/postgres/secret.go index 20004587..0ee5aff8 100644 --- a/internal/postgres/secret.go +++ b/internal/postgres/secret.go @@ -8,7 +8,6 @@ import ( "github.com/nais/cli/internal/naisapi" "github.com/nais/cli/internal/postgres/command/flag" "github.com/nais/naistrix" - "k8s.io/client-go/tools/clientcmd" ) // Hardcoded reasons for administrative operations @@ -49,55 +48,32 @@ func (s *SecretValues) GetBySuffix(suffix string) string { return "" } -// resolveTeamAndEnvironment extracts team and environment from namespace and cluster flags -func resolveTeamAndEnvironment(namespace flag.Namespace, cluster flag.Context) (team, environment string, err error) { - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{ - CurrentContext: string(cluster), +// GetSecretValues retrieves the values of a database secret via the API. +// This is the preferred method for accessing secret values as it combines +// authorization, logging, and value retrieval in a single operation. +// The access is logged for audit purposes. +func GetSecretValues(ctx context.Context, appName string, fl *flag.Postgres, reason string, out *naistrix.OutputWriter) (*SecretValues, error) { + if reason == "" { + reason = fl.Reason + if reason == "" { + return nil, fmt.Errorf("reason is required for accessing database secrets") + } } - kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - // Determine team slug from namespace - team = string(namespace) + team := fl.Team if team == "" { - ns, _, err := kubeConfig.Namespace() - if err != nil { - return "", "", fmt.Errorf("unable to get namespace from kubeconfig: %w", err) + team = string(fl.Namespace) + if team == "" { + return nil, fmt.Errorf("team is required") } - team = ns - } - if team == "" { - return "", "", fmt.Errorf("namespace is required to determine team (use --namespace flag or set in kubeconfig)") } - // Determine environment from kubeconfig context - environment = string(cluster) + environment := string(fl.Environment) if environment == "" { - rawConfig, err := kubeConfig.RawConfig() - if err != nil { - return "", "", fmt.Errorf("unable to get kubeconfig: %w", err) + environment = string(fl.Context) + if environment == "" { + return nil, fmt.Errorf("environment is required") } - environment = rawConfig.CurrentContext - } - if environment == "" { - return "", "", fmt.Errorf("kubeconfig context is required to determine environment (use --context flag or set current-context in kubeconfig)") - } - - return team, environment, nil -} - -// GetSecretValues retrieves the values of a database secret via the API. -// This is the preferred method for accessing secret values as it combines -// authorization, logging, and value retrieval in a single operation. -// The access is logged for audit purposes. -func GetSecretValues(ctx context.Context, appName string, namespace flag.Namespace, cluster flag.Context, reason string, out *naistrix.OutputWriter) (*SecretValues, error) { - if reason == "" { - return nil, fmt.Errorf("reason is required for accessing database secrets") - } - - team, environmentName, err := resolveTeamAndEnvironment(namespace, cluster) - if err != nil { - return nil, err } // The secret name follows the pattern "google-sql-" @@ -105,7 +81,7 @@ func GetSecretValues(ctx context.Context, appName string, namespace flag.Namespa out.Debugf("Requesting access to secret %q for database connection...\n", secretName) - values, err := naisapi.ViewSecretValues(ctx, team, environmentName, secretName, reason) + values, err := naisapi.ViewSecretValues(ctx, team, environment, secretName, reason) if err != nil { // Check if the error indicates the user is not authorized if strings.Contains(err.Error(), "not authorized") || strings.Contains(err.Error(), "Not authorized") { @@ -130,14 +106,17 @@ func GetSecretValues(ctx context.Context, appName string, namespace flag.Namespa // GetSecretValuesWithUserReason retrieves secret values with a user-provided reason. // This should be used for interactive operations like proxy and psql where the user // should provide justification for accessing the database. -func GetSecretValuesWithUserReason(ctx context.Context, appName string, namespace flag.Namespace, cluster flag.Context, reason string, out *naistrix.OutputWriter) (*SecretValues, error) { +func GetSecretValuesWithUserReason(ctx context.Context, appName string, fl *flag.Postgres, reason string, out *naistrix.OutputWriter) (*SecretValues, error) { if reason == "" { - return nil, fmt.Errorf("reason is required for accessing database secrets (use --reason flag)") + reason = fl.Reason + if reason == "" { + return nil, fmt.Errorf("reason is required for accessing database secrets (use --reason flag)") + } } if len(reason) < 10 { return nil, fmt.Errorf("reason must be at least 10 characters") } - return GetSecretValues(ctx, appName, namespace, cluster, reason, out) + return GetSecretValues(ctx, appName, fl, reason, out) } From eda4a3a08e547bd38af645509a1d832b01ab4d13 Mon Sep 17 00:00:00 2001 From: Vegar Sechmann Molvig Date: Mon, 9 Feb 2026 11:18:21 +0100 Subject: [PATCH 2/2] fix: bump go and remove dead code --- internal/postgres/secret.go | 21 --------------------- mise/config.toml | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/internal/postgres/secret.go b/internal/postgres/secret.go index 0ee5aff8..e89680a9 100644 --- a/internal/postgres/secret.go +++ b/internal/postgres/secret.go @@ -27,27 +27,6 @@ type SecretValues struct { values map[string]string } -// Get returns the value for a given key, or empty string if not found -func (s *SecretValues) Get(key string) string { - if s == nil || s.values == nil { - return "" - } - return s.values[key] -} - -// GetBySuffix returns the value for a key that ends with the given suffix -func (s *SecretValues) GetBySuffix(suffix string) string { - if s == nil || s.values == nil { - return "" - } - for k, v := range s.values { - if strings.HasSuffix(k, suffix) { - return v - } - } - return "" -} - // GetSecretValues retrieves the values of a database secret via the API. // This is the preferred method for accessing secret values as it combines // authorization, logging, and value retrieval in a single operation. diff --git a/mise/config.toml b/mise/config.toml index 0cced1fb..653b1a7b 100644 --- a/mise/config.toml +++ b/mise/config.toml @@ -1,6 +1,6 @@ [tools] git-cliff = "2.10.1" -go = "1.25.6" +go = "1.25.7" yamlfmt = "0.17.2" [settings]