Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpk profile changes #17038

Merged
merged 16 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
3 changes: 2 additions & 1 deletion src/go/rpk/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ require (
github.com/lestrrat-go/jwx v1.2.28
github.com/linkedin/goavro/v2 v2.12.0
github.com/lorenzosaino/go-sysctl v0.3.1
github.com/mattn/go-isatty v0.0.20
github.com/moby/term v0.5.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pkg/errors v0.9.1
github.com/prometheus/client_model v0.6.0
github.com/prometheus/common v0.49.0
github.com/rs/xid v1.5.0
github.com/safchain/ethtool v0.3.0
github.com/schollz/progressbar/v3 v3.14.2
github.com/sethgrid/pester v1.2.0
Expand Down Expand Up @@ -98,7 +100,6 @@ require (
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions src/go/rpk/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/pkg/cli/acl/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Allow write permissions to user buzz to transactional id "txn":
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, _ []string) {
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

adm, err := kafka.NewAdmin(fs, p)
out.MaybeDie(err, "unable to initialize kafka client: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/pkg/cli/acl/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ resource names:
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, _ []string) {
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

adm, err := kafka.NewAdmin(fs, p)
out.MaybeDie(err, "unable to initialize kafka client: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/pkg/cli/acl/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ resource names:
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, _ []string) {
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

adm, err := kafka.NewAdmin(fs, p)
out.MaybeDie(err, "unable to initialize kafka client: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/pkg/cli/acl/user/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ acl help text for more info.
out.Exit(h)
}
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

cl, err := adminapi.NewClient(fs, p)
out.MaybeDie(err, "unable to initialize admin client: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/pkg/cli/acl/user/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ delete any ACLs that may exist for this user.
out.Exit(h)
}
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

cl, err := adminapi.NewClient(fs, p)
out.MaybeDie(err, "unable to initialize admin client: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/pkg/cli/acl/user/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func newListUsersCommand(fs afero.Fs, p *config.Params) *cobra.Command {
out.Exit(h)
}
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

cl, err := adminapi.NewClient(fs, p)
out.MaybeDie(err, "unable to initialize admin client: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion src/go/rpk/pkg/cli/acl/user/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func newUpdateCommand(fs afero.Fs, p *config.Params) *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
f := p.Formatter
p, err := p.LoadVirtualProfile(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

cl, err := adminapi.NewClient(fs, p)
out.MaybeDie(err, "unable to initialize admin client: %v", err)
Expand Down
10 changes: 10 additions & 0 deletions src/go/rpk/pkg/cli/cloud/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,13 @@ func validAuths(fs afero.Fs, p *config.Params) func(*cobra.Command, []string, st
return names, cobra.ShellCompDirectiveDefault
}
}

func findName(y *config.RpkYaml, name string) map[int]struct{} {
nameMatches := make(map[int]struct{})
for i, a := range y.CloudAuths {
if a.Name == name {
nameMatches[i] = struct{}{}
}
}
return nameMatches
}
87 changes: 8 additions & 79 deletions src/go/rpk/pkg/cli/cloud/auth/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,94 +11,23 @@ package auth

import (
"fmt"
"reflect"
"strings"

"github.com/redpanda-data/redpanda/src/go/rpk/pkg/config"
"github.com/redpanda-data/redpanda/src/go/rpk/pkg/out"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)

func newCreateCommand(fs afero.Fs, p *config.Params) *cobra.Command {
var (
set []string
description string
)
cmd := &cobra.Command{
Use: "create [NAME]",
Short: "Create an rpk cloud auth",
Long: `Create an rpk cloud auth.

This command creates a new rpk cloud auth. The default SSO login does not need
any additional auth values (the token is populated on login) so you can just
create an empty auth. You can also use --set to set key=value pairs for client
credentials.

The --set flag supports autocompletion, suggesting the -X key format. If you
begin writing a YAML path, the flag will suggest the rest of the path.

rpk always switches the current cloud auth to the newly created auth.
`,
Args: cobra.ExactArgs(1),
Run: func(_ *cobra.Command, args []string) {
cfg, err := p.Load(fs)
out.MaybeDie(err, "unable to load config: %v", err)

yAct, err := cfg.ActualRpkYamlOrEmpty()
out.MaybeDie(err, "unable to load rpk.yaml: %v", err)

name := args[0]
if a := yAct.Auth(name); a != nil {
out.Die("cloud auth %q already exists", name)
}

var a config.RpkCloudAuth
for _, kv := range set {
split := strings.SplitN(kv, "=", 2)
if len(split) != 2 {
out.Die("invalid key=value pair %q", kv)
}
err := config.Set(&a, split[0], split[1])
out.MaybeDieErr(err)
}
a.Name = name
a.Description = description

if a.ClientID != "" || a.ClientSecret != "" {
if a.ClientID == "" || a.ClientSecret == "" {
out.Die("client-id and client-secret must both be set or both be empty")
}
}

yAct.CurrentCloudAuth = yAct.PushAuth(a)
err = yAct.Write(fs)
out.MaybeDie(err, "unable to write rpk.yaml: %v", err)
fmt.Printf("Created and switched to new cloud auth %q.\n", name)
Use: "create [NAME]",
Short: "Create an rpk cloud auth",
Hidden: true,
Run: func(*cobra.Command, []string) {
fmt.Println("'rpk cloud auth create' is deprecated as a no-op; use 'rpk cloud login' instead.")
},
}
cmd.Flags().StringSliceVarP(&set, "set", "s", nil, "A key=value pair to set in the cloud auth")
cmd.Flags().StringVar(&description, "description", "", "Optional description of the auth")
cmd.RegisterFlagCompletionFunc("set", createSetCompletion)
var slice []string
cmd.Flags().StringSliceVarP(&slice, "set", "s", nil, "A key=value pair to set in the cloud auth")
cmd.Flags().StringVar(new(string), "description", "", "Optional description of the auth")
return cmd
}

func createSetCompletion(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var possibilities []string
t := reflect.TypeOf(config.RpkCloudAuth{})
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
tag := sf.Tag.Get("yaml")
if comma := strings.IndexByte(tag, ','); comma != -1 {
tag = tag[:comma]
}
if tag == "name" || tag == "description" {
// name is an arg, description is a flag: do not autocomplete these
continue
}
if strings.HasPrefix(tag, toComplete) {
possibilities = append(possibilities, tag+"=")
}
}
return possibilities, cobra.ShellCompDirectiveNoSpace
}
111 changes: 79 additions & 32 deletions src/go/rpk/pkg/cli/cloud/auth/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package auth

import (
"fmt"
"sort"

"github.com/redpanda-data/redpanda/src/go/rpk/pkg/config"
"github.com/redpanda-data/redpanda/src/go/rpk/pkg/out"
Expand All @@ -19,59 +20,105 @@ import (
)

func newDeleteCommand(fs afero.Fs, p *config.Params) *cobra.Command {
return &cobra.Command{
cmd := &cobra.Command{
Use: "delete [NAME]",
Short: "Delete an rpk cloud auth",
Long: `Delete an rpk cloud auth.

Deleting a cloud auth removes it from the rpk.yaml file. If the deleted
auth was the current auth, rpk will use a default SSO auth the next time
you try to login and, if the login is successful, will safe that auth.
you try to login and save that auth.

If you delete an auth that is used by profiles, affected profiles have
their auth cleared and you will only be able to access the profile's
cluster via SASL credentials.
`,
Args: cobra.ExactArgs(1),
ValidArgsFunction: validAuths(fs, p),
Run: func(_ *cobra.Command, args []string) {
cfg, err := p.Load(fs)
out.MaybeDie(err, "unable to load config: %v", err)
out.MaybeDie(err, "rpk unable to load config: %v", err)

y, ok := cfg.ActualRpkYaml()
if !ok {
out.Die("rpk.yaml file does not exist")
}

name := args[0]
wasUsing, err := deleteAuth(fs, y, name)
out.MaybeDieErr(err)

nameMatches := findName(y, name)
if len(nameMatches) == 0 {
out.Die("cloud auth %q does not exist", name)
}

// We could have deleted multiple if there is a bug in
// the logic or if the file is corrupted somehow; we do
// exact name match and should prevent creation of
// duplicates. But, if we delete multiple, that's ok;
// we just print information about the first.
keep := y.CloudAuths[:0]
var deleted config.RpkCloudAuth
for i := range y.CloudAuths {
a := y.CloudAuths[i]
if _, ok := nameMatches[i]; ok {
deleted = a
} else {
keep = append(keep, a)
}
}
y.CloudAuths = keep

// Gather all profiles containing this auth and
// prompt confirm if the user is ok with deleting
// auth attached to profiles.
attachedProfiles := make(map[int]struct{})
for i, p := range y.Profiles {
if p.CloudCluster.HasAuth(deleted) {
attachedProfiles[i] = struct{}{}
}
}

if len(attachedProfiles) > 0 {
names := make([]string, 0, len(attachedProfiles))
for i := range attachedProfiles {
names = append(names, y.Profiles[i].Name)
}
sort.Strings(names)
fmt.Println("The following profiles are currently using this cloud auth:")
for _, name := range names {
fmt.Printf(" %s\n", name)
}
fmt.Println("Deleting this auth will mean the profiles no longer can talk to the cloud.")
fmt.Println("The profiles will only be able to talk to the cluster via SASL credentials,")
fmt.Println("which you must set in the profile manually via 'rpk profile edit'.")
c, err := out.Confirm("Do you still want to delete this auth and remove it from the affected profiles?")
out.MaybeDie(err, "unable to confirm deletion: %v", err)
if !c {
fmt.Println("Deletion canceled.")
return
}
for i := range attachedProfiles {
p := &y.Profiles[i]
p.FromCloud = false
p.CloudCluster.AuthOrgID = ""
p.CloudCluster.AuthKind = ""
}
}

err = y.Write(fs)
out.MaybeDie(err, "unable to write rpk.yaml: %v", err)

wasUsing := y.CurrentCloudAuthOrgID == deleted.OrgID && y.CurrentCloudAuthKind == deleted.Kind

fmt.Printf("Deleted cloud auth %q.\n", name)
if wasUsing {
fmt.Println("The current profile was using this cloud auth.\nYou may need to reauthenticate before the current profile can be used again.")
fmt.Print(`This was the current auth selected.

You may need to reauthenticate with 'rpk cloud login' or swap to a different
profile that uses different auth using cloud API commands again.
`)
}
},
}
}

func deleteAuth(
fs afero.Fs,
y *config.RpkYaml,
name string,
) (wasUsing bool, err error) {
idx := -1
for i, a := range y.CloudAuths {
if a.Name == name {
idx = i
break
}
}
if idx == -1 {
return false, fmt.Errorf("cloud auth %q does not exist", name)
}
y.CloudAuths = append(y.CloudAuths[:idx], y.CloudAuths[idx+1:]...)
ca := y.Auth(y.CurrentCloudAuth)
if wasUsing = ca != nil && ca.Name == name; wasUsing {
y.CurrentCloudAuth = ""
}
if err := y.Write(fs); err != nil {
return false, fmt.Errorf("unable to write rpk file: %v", err)
}
return wasUsing, nil
return cmd
}
Loading
Loading