Skip to content

Commit

Permalink
Add new command, users list and delete. Also, create automatic port f…
Browse files Browse the repository at this point in the history
…orwarder behind the scenes to allow connecting to the Pod directly without manual port-forwarding or ingress to allow mgmt-api access to Pods
  • Loading branch information
burmanm committed Nov 14, 2024
1 parent a1e1580 commit ce0cb19
Show file tree
Hide file tree
Showing 12 changed files with 506 additions and 44 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION ?= 0.3.0
VERSION ?= 0.7.0

COMMIT := $(shell git rev-parse --short HEAD)
DATE := $(shell date +%Y%m%d)
Expand Down Expand Up @@ -83,7 +83,7 @@ $(LOCALBIN):
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
ENVTEST ?= $(LOCALBIN)/setup-envtest

GOLINT_VERSION ?= 1.56.2
GOLINT_VERSION ?= 1.61.0

.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
Expand Down
8 changes: 8 additions & 0 deletions cmd/kubectl-k8ssandra/tasks/tasks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package tasks

/*
Task operations.
tasks list [--cluster || --datacenter]
tasks create <task> [--datacenter || --cluster] <args>
*/
9 changes: 6 additions & 3 deletions cmd/kubectl-k8ssandra/users/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ var (
# Add new users to CassandraDatacenter
%[1]s add [<args>]
# Add new user example to CassandraDatacenter dc2 with password prompting
%[1]s add --dc dc1 --username example --superuser
# Add new superusers to CassandraDatacenter dc1 from a path /tmp/users.txt
%[1]s add --dc dc1 --path /tmp/users.txt --superuser
`
errNoDcDc = fmt.Errorf("target CassandraDatacenter is required")
errDoubleDefinition = fmt.Errorf("either --path or --username is allowed, not both")
errMissingUsername = fmt.Errorf("if --password is set, --username is required")
errMissingUsername = fmt.Errorf("--username is required")
)

type addOptions struct {
Expand Down Expand Up @@ -73,7 +76,7 @@ func NewAddCmd(streams genericclioptions.IOStreams) *cobra.Command {
fl := cmd.Flags()
fl.StringVar(&o.secretPath, "path", "", "path to users data")
fl.StringVar(&o.datacenter, "dc", "", "target datacenter")
fl.BoolVar(&o.superuser, "superuser", true, "create users as superusers")
fl.BoolVar(&o.superuser, "superuser", true, "create users as superusers") // TODO Set default to false
fl.StringVarP(&o.username, "username", "u", "", "username to add")
fl.StringVarP(&o.password, "password", "p", "", "password to set for the user")
o.configFlags.AddFlags(fl)
Expand Down Expand Up @@ -155,5 +158,5 @@ func (c *addOptions) Run() error {
}
}

return users.AddNewUser(ctx, kubeClient, c.datacenter, c.username, c.password, c.superuser)
return users.Add(ctx, kubeClient, c.datacenter, c.username, c.password, c.superuser)
}
114 changes: 114 additions & 0 deletions cmd/kubectl-k8ssandra/users/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package users

import (
"context"
"fmt"

"github.com/k8ssandra/k8ssandra-client/pkg/kubernetes"
"github.com/k8ssandra/k8ssandra-client/pkg/users"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

var (
userDeleteExample = `
# Delete users from CassandraDatacenter
%[1]s delete [<args>]
# Delete user tryme from CassandraDatacenter dc1
%[1]s delete --dc dc1 --username tryme
`
)

type deleteOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
namespace string
datacenter string

// For manual entering from CLI
username string
}

func newDeleteOptions(streams genericclioptions.IOStreams) *deleteOptions {
return &deleteOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}

// NewCmd provides a cobra command wrapping newAddOptions
func NewDeleteCmd(streams genericclioptions.IOStreams) *cobra.Command {
o := newDeleteOptions(streams)

cmd := &cobra.Command{
Use: "delete [flags]",
Short: "Delete user from CassandraDatacenter installation",
Example: fmt.Sprintf(userDeleteExample, "kubectl k8ssandra users"),
RunE: func(c *cobra.Command, args []string) error {
if err := o.Complete(c, args); err != nil {
return err
}
if err := o.Validate(); err != nil {
return err
}
if err := o.Run(); err != nil {
return err
}

return nil
},
}

fl := cmd.Flags()
fl.StringVar(&o.datacenter, "dc", "", "target datacenter")
fl.StringVarP(&o.username, "username", "u", "", "username to add")
o.configFlags.AddFlags(fl)
return cmd
}

// Complete parses the arguments and necessary flags to options
func (c *deleteOptions) Complete(cmd *cobra.Command, args []string) error {
var err error

c.namespace, _, err = c.configFlags.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}

if c.username == "" && len(args) > 0 {
c.username = args[0]
}

return nil
}

// Validate ensures that all required arguments and flag values are provided
func (c *deleteOptions) Validate() error {
if c.datacenter == "" {
return errNoDcDc
}

if c.username == "" {
return errMissingUsername
}

return nil
}

// Run processes the input, creates a connection to Kubernetes and processes a secret to add the users
func (c *deleteOptions) Run() error {
restConfig, err := c.configFlags.ToRESTConfig()
if err != nil {
return err
}

kubeClient, err := kubernetes.GetClientInNamespace(restConfig, c.namespace)
if err != nil {
return err
}

ctx := context.Background()

return users.Delete(ctx, kubeClient, c.datacenter, c.username)
}
151 changes: 151 additions & 0 deletions cmd/kubectl-k8ssandra/users/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package users

import (
"context"
"errors"
"fmt"

"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/lipgloss"
"github.com/k8ssandra/k8ssandra-client/pkg/users"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

var (
userListExample = `
# List users of CassandraDatacenter or K8ssandraCluster
%[1]s list [<args>]
# List users of CassandraDatacenter dc1
%[1]s list --dc dc1
# List users of K8ssandraCluster cluster1
%[1]s list --cluster cluster1
`

errNoDcOrCluster = errors.New("either cluster or datacenter target is required")
)

type listOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
namespace string
datacenter string
cluster string
}

func newListOptions(streams genericclioptions.IOStreams) *listOptions {
return &listOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}

// NewCmd provides a cobra command wrapping newAddOptions
func NewListCmd(streams genericclioptions.IOStreams) *cobra.Command {
o := newListOptions(streams)

cmd := &cobra.Command{
Use: "list [flags]",
Short: "List users of CassandraDatacenter or K8ssandraCluster installation",
Example: fmt.Sprintf(userListExample, "kubectl k8ssandra users"),
RunE: func(c *cobra.Command, args []string) error {
if err := o.Complete(c, args); err != nil {
return err
}
if err := o.Validate(); err != nil {
return err
}
if err := o.Run(); err != nil {
return err
}

return nil
},
}

fl := cmd.Flags()
fl.StringVar(&o.datacenter, "dc", "", "target datacenter")
// fl.StringVar(&o.cluster, "cluster", "", "target cluster")
o.configFlags.AddFlags(fl)
return cmd
}

// Complete parses the arguments and necessary flags to options
func (c *listOptions) Complete(cmd *cobra.Command, args []string) error {
var err error

c.namespace, _, err = c.configFlags.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}

return nil
}

// Validate ensures that all required arguments and flag values are provided
func (c *listOptions) Validate() error {
if c.datacenter == "" && c.cluster == "" {
return errNoDcOrCluster
}

return nil
}

// Run processes the input, creates a connection to Kubernetes and processes a secret to add the users
func (c *listOptions) Run() error {
restConfig, err := c.configFlags.ToRESTConfig()
if err != nil {
return err
}

ctx := context.Background()

users, err := users.List(ctx, restConfig, c.namespace, c.datacenter)
if err != nil {
return err
}

columns := []table.Column{
{Title: "Name", Width: 30},
{Title: "Superuser", Width: 10},
{Title: "Login", Width: 7},
{Title: "Options", Width: 10},
{Title: "Datacenters", Width: 10},
}

rows := make([]table.Row, 0, len(users))

for _, user := range users {
row := table.Row{
user.Name,
user.Super,
user.Login,
user.Options,
user.Datacenters,
}
rows = append(rows, row)
}

t := table.New(
table.WithColumns(columns),
table.WithRows(rows),
)

s := table.DefaultStyles()
s.Header = s.Header.
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240")).
BorderBottom(true).
Bold(false)
s.Selected = s.Selected.
Foreground(lipgloss.Color("229")).
Background(lipgloss.Color("57")).
Bold(false)
t.SetStyles(s)

fmt.Print(t.View())

return nil
}
7 changes: 7 additions & 0 deletions cmd/kubectl-k8ssandra/users/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import (
"k8s.io/cli-runtime/pkg/genericclioptions"
)

/*
users list
users delete (does mgmt-api have this ability?)
*/

type ClientOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
Expand All @@ -28,6 +33,8 @@ func NewCmd(streams genericclioptions.IOStreams) *cobra.Command {

// Add subcommands
cmd.AddCommand(NewAddCmd(streams))
cmd.AddCommand(NewDeleteCmd(streams))
cmd.AddCommand(NewListCmd(streams))
o.configFlags.AddFlags(cmd.Flags())

return cmd
Expand Down
Loading

0 comments on commit ce0cb19

Please sign in to comment.