Skip to content

Commit

Permalink
Merge pull request #4 from burmanm/add_user_operations
Browse files Browse the repository at this point in the history
Add the ability to add new users to the Cassandra datacenter
  • Loading branch information
burmanm authored Jan 30, 2023
2 parents f2439ef + 95ef9f0 commit 9fc6608
Show file tree
Hide file tree
Showing 13 changed files with 642 additions and 31 deletions.
26 changes: 11 additions & 15 deletions .github/workflows/build-client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ on:
branches: [ main ]
jobs:
build_and_test:
name: Unit testing and linting
runs-on: ubuntu-latest
env:
CGO_ENABLED: 0
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
if: github.event_name == 'pull_request'
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/checkout@v2
- uses: actions/checkout@v3
if: github.event_name != 'pull_request'
- name: Set up Go 1.19
uses: actions/setup-go@v1
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
go-version-file: 'go.mod'
cache: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
Expand All @@ -39,18 +42,11 @@ jobs:
if: github.ref == 'refs/heads/master'
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
Expand All @@ -61,7 +57,7 @@ jobs:
echo ::set-output name=tag_name::${GITHUB_REF#refs/tags/}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
file: cmd/kubectl-k8ssandra/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kubectl-k8ssandra/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ COPY cmd/ cmd/
COPY pkg/ pkg/

# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o kubectl-k8ssandra cmd/kubectl-k8ssandra/main.go
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o kubectl-k8ssandra cmd/kubectl-k8ssandra/main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
4 changes: 2 additions & 2 deletions cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
// "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/migrate"
// "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/nodetool"
"github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/operate"
// "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/users"
"github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/users"

"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
Expand Down Expand Up @@ -47,7 +47,7 @@ func NewCmd(streams genericclioptions.IOStreams) *cobra.Command {
cmd.AddCommand(operate.NewStopCmd(streams))
// cmd.AddCommand(list.NewCmd(streams))
// cmd.AddCommand(migrate.NewCmd(streams))
// cmd.AddCommand(users.NewCmd(streams))
cmd.AddCommand(users.NewCmd(streams))
// cmd.AddCommand(migrate.NewInstallCmd(streams))

// cmd.Flags().BoolVar(&o.listNamespaces, "list", o.listNamespaces, "if true, print the list of all namespaces in the current KUBECONFIG")
Expand Down
159 changes: 159 additions & 0 deletions cmd/kubectl-k8ssandra/users/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package users

import (
"context"
"fmt"

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

var (
userAddExample = `
# Add new users to CassandraDatacenter
%[1]s add [<args>]
# 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")
)

type addOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
namespace string
datacenter string
superuser bool

// For manual entering from CLI
username string
password string

// When reading from files
secretPath string
}

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

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

cmd := &cobra.Command{
Use: "add [flags]",
Short: "Add new users to CassandraDatacenter installation",
Example: fmt.Sprintf(userAddExample, "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.secretPath, "path", "", "path to users data")
fl.StringVar(&o.datacenter, "dc", "", "target datacenter")
fl.BoolVar(&o.superuser, "superuser", true, "create users as superusers")
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)
return cmd
}

// Complete parses the arguments and necessary flags to options
func (c *addOptions) 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 *addOptions) Validate() error {
if c.datacenter == "" {
return errNoDcDc
}

if c.secretPath != "" && c.username != "" {
return errDoubleDefinition
}

if c.password != "" && 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 *addOptions) 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()

if c.secretPath != "" {
return users.AddNewUsersFromSecret(ctx, kubeClient, c.datacenter, c.secretPath, c.superuser)
}

// Interactive prompt

prompts := make([]*ui.Prompt, 0, 1)

userPrompt := ui.NewPrompt("Username")
passPrompt := ui.NewPrompt("Password").Mask()

if c.username == "" {
prompts = append(prompts, userPrompt)
}

if c.password == "" {
prompts = append(prompts, passPrompt)
}

if len(prompts) > 0 {
prompter := ui.NewPrompter(prompts)
if _, err := tea.NewProgram(prompter).Run(); err != nil {
return err
}

// Parse values
c.password = passPrompt.Value()
if c.username == "" {
c.username = userPrompt.Value()
}
}

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

import (
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

type ClientOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
}

// NewClientOptions provides an instance of ClientOptions with default values
func NewClientOptions(streams genericclioptions.IOStreams) *ClientOptions {
return &ClientOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}

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

cmd := &cobra.Command{
Use: "users [subcommand] [flags]",
}

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

return cmd
}
23 changes: 18 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ module github.com/k8ssandra/k8ssandra-client
go 1.19

require (
github.com/charmbracelet/bubbles v0.14.0
github.com/charmbracelet/bubbletea v0.23.1
github.com/charmbracelet/lipgloss v0.5.0
github.com/google/uuid v1.2.0
github.com/k8ssandra/cass-operator v1.13.1
github.com/spf13/cobra v1.4.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.2
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.9.4
k8s.io/api v0.24.2
Expand Down Expand Up @@ -35,9 +40,12 @@ require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/containerd v1.6.6 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down Expand Up @@ -71,7 +79,6 @@ require (
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
Expand All @@ -86,10 +93,12 @@ require (
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.6 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
Expand All @@ -101,6 +110,10 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.13.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
Expand All @@ -111,12 +124,12 @@ require (
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rubenv/sql-migrate v1.1.1 // indirect
github.com/russross/blackfriday v1.5.2 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/stretchr/testify v1.7.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
Expand All @@ -126,7 +139,7 @@ require (
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
Expand Down
Loading

0 comments on commit 9fc6608

Please sign in to comment.