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

Add the ability to add new users to the Cassandra datacenter #4

Merged
merged 2 commits into from
Jan 30, 2023
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
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