diff --git a/.gitignore b/.gitignore index af52fae..cdba104 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ # dep/glide vendor/ bin/ +rebuild.sh +go.mod +go.sum diff --git a/Makefile b/Makefile index d6f06fc..4f97514 100644 --- a/Makefile +++ b/Makefile @@ -11,14 +11,14 @@ TRAVIS := $(shell printenv TRAVIS) all: bootstrap build docker push fmt: - go fmt ./pkg/... ./cmd/... + go fmt -mod=mod ./pkg/... ./cmd/... vet: - go vet ./pkg/... ./cmd/... + go vet -mod=mod ./pkg/... ./cmd/... # Build cain binary build: fmt vet - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(LDFLAGS) -o bin/cain cmd/cain.go + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod=mod -ldflags $(LDFLAGS) -o bin/cain cmd/cain.go # Build cain docker image docker: fmt vet diff --git a/README.md b/README.md index 6b380af..cc663dc 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,18 @@ Usage: cain backup [flags] Flags: - -b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75) - --cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data") - -c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra") - --dst string destination to backup to. Example: s3://bucket/cassandra. Overrides $CAIN_DST - -k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE - -n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default") - -p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1) - -l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra") + -a, --authentication use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION + -b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75) + --cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data") + -u, --cassandra-username string cassandra username. Overrides $CAIN_CASSANDRA_USERNAME (default "cain") + -c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra") + --dst string destination to backup to. Example: s3://bucket/cassandra. Overrides $CAIN_DST + -h, --help help for backup + -k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE + -n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default") + --nodetool-credentials-file string path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE (default "/home/cassandra/.nodetool/credentials") + -p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1) + -l, --selector string ``` #### Examples @@ -77,6 +81,19 @@ cain backup \ --dst s3://db-backup/cassandra ``` +Backup to AWS S3 with Cassandra authentication enabled + +``` +cain backup \ + -n default \ + -l release=cassandra \ + -k keyspace \ + --dst s3://db-backup/cassandra + -a + -u cain + --nodetool-credentials-file /home/cassandra/.nodetool/credentials +``` + Backup to Azure Blob Storage ``` @@ -105,17 +122,21 @@ Usage: cain restore [flags] Flags: - -b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75) - --cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data") - -c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra") - -k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE - -n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default") - -p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1) - -s, --schema string schema version to restore (optional). Overrides $CAIN_SCHEMA - -l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra") - --src string source to restore from. Example: s3://bucket/cassandra/namespace/cluster-name. Overrides $CAIN_SRC - -t, --tag string tag to restore. Overrides $CAIN_TAG - --user-group string user and group who should own restored files. Overrides $CAIN_USER_GROUP (default "cassandra:cassandra") + -a, --authentication use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION + -b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75) + --cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data") + -u, --cassandra-username string cassandra username. Overrides $CAIN_CASSANDRA_USERNAME (default "cain") + -c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra") + -h, --help help for restore + -k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE + -n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default") + -f, --nodetool-credentials-file string path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE (default "/home/cassandra/.nodetool/credentials") + -p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1) + -s, --schema string schema version to restore (optional). Overrides $CAIN_SCHEMA + -l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra") + --src string source to restore from. Example: s3://bucket/cassandra/namespace/cluster-name. Overrides $CAIN_SRC + -t, --tag string tag to restore. Overrides $CAIN_TAG + --user-group string user and group who should own restored files. Overrides $CAIN_USER_GROUP (default "cassandra:cassandra") ``` #### Examples @@ -211,6 +232,7 @@ Since Cain uses [Skbn](https://github.com/nuvo/skbn), adding support for additio | Cain version | Skbn version | |--------------|--------------| +| 0.5.2 | 0.4.2 | | 0.5.1 | 0.4.2 | | 0.5.0 | 0.4.1 | | 0.4.2 | 0.4.1 | @@ -239,6 +261,18 @@ Skbn uses the default AWS [credentials chain](https://docs.aws.amazon.com/sdk-fo Skbn uses `AZURE_STORAGE_ACCOUNT` and `AZURE_STORAGE_ACCESS_KEY` environment variables for authentication. +### Cassandra Credentials +When Authentication is enabled Cain will look for default credentials +for `cqlsh` in `/home/cassandra/.cassandra/credentials` +if you use authentication please make sure the cassandra +container has this file and the username and password are correct. + +For `nodetool` authentications default credentials are in: +`/home/cassandra/.nodetool/credentials` can be overridden by +setting the `--nodetool-credentials-file` flag. +When this flag is used, the username for the nodetool +authentication must be provided as well . + ## Examples 1. [Helm example](/examples/helm) diff --git a/cmd/cain.go b/cmd/cain.go index 6fb56e0..631f052 100644 --- a/cmd/cain.go +++ b/cmd/cain.go @@ -39,14 +39,17 @@ func NewRootCmd(args []string) *cobra.Command { } type backupCmd struct { - namespace string - selector string - container string - keyspace string - dst string - parallel int - bufferSize float64 - cassandraDataDir string + namespace string + selector string + container string + keyspace string + dst string + parallel int + bufferSize float64 + cassandraDataDir string + authentication bool + cassandraUsername string + nodetoolCredentialsFile string out io.Writer } @@ -73,14 +76,17 @@ func NewBackupCmd(out io.Writer) *cobra.Command { }, Run: func(cmd *cobra.Command, args []string) { options := cain.BackupOptions{ - Namespace: b.namespace, - Selector: b.selector, - Container: b.container, - Keyspace: b.keyspace, - Dst: b.dst, - Parallel: b.parallel, - BufferSize: b.bufferSize, - CassandraDataDir: b.cassandraDataDir, + Namespace: b.namespace, + Selector: b.selector, + Container: b.container, + Keyspace: b.keyspace, + Dst: b.dst, + Parallel: b.parallel, + BufferSize: b.bufferSize, + CassandraDataDir: b.cassandraDataDir, + Authentication: b.authentication, + CassandraUsername: b.cassandraUsername, + NodetoolCredentialsFile: b.nodetoolCredentialsFile, } if _, err := cain.Backup(options); err != nil { log.Fatal(err) @@ -97,22 +103,27 @@ func NewBackupCmd(out io.Writer) *cobra.Command { f.IntVarP(&b.parallel, "parallel", "p", utils.GetIntEnvVar("CAIN_PARALLEL", 1), "number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL") f.Float64VarP(&b.bufferSize, "buffer-size", "b", utils.GetFloat64EnvVar("CAIN_BUFFER_SIZE", 6.75), "in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE") f.StringVar(&b.cassandraDataDir, "cassandra-data-dir", utils.GetStringEnvVar("CAIN_CASSANDRA_DATA_DIR", "/var/lib/cassandra/data"), "cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR") - + f.BoolVarP(&b.authentication, "authentication", "a", utils.GetBoolEnvVar("CAIN_AUTHENTICATION", false), "use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION") + f.StringVarP(&b.cassandraUsername, "cassandra-username", "u", utils.GetStringEnvVar("CAIN_CASSANDRA_USERNAME", "cain"), "cassandra username. Overrides $CAIN_CASSANDRA_USERNAME") + f.StringVar(&b.nodetoolCredentialsFile, "nodetool-credentials-file", utils.GetStringEnvVar("CAIN_NODETOOL_CREDENTIALS_FILE", "/home/cassandra/.nodetool/credentials"), "path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE") return cmd } type restoreCmd struct { - src string - keyspace string - tag string - schema string - namespace string - selector string - container string - parallel int - bufferSize float64 - userGroup string - cassandraDataDir string + src string + keyspace string + tag string + schema string + namespace string + selector string + container string + parallel int + bufferSize float64 + userGroup string + cassandraDataDir string + authentication bool + cassandraUsername string + nodetoolCredentialsFile string out io.Writer } @@ -142,17 +153,20 @@ func NewRestoreCmd(out io.Writer) *cobra.Command { }, Run: func(cmd *cobra.Command, args []string) { options := cain.RestoreOptions{ - Src: r.src, - Keyspace: r.keyspace, - Tag: r.tag, - Schema: r.schema, - Namespace: r.namespace, - Selector: r.selector, - Container: r.container, - Parallel: r.parallel, - BufferSize: r.bufferSize, - UserGroup: r.userGroup, - CassandraDataDir: r.cassandraDataDir, + Src: r.src, + Keyspace: r.keyspace, + Tag: r.tag, + Schema: r.schema, + Namespace: r.namespace, + Selector: r.selector, + Container: r.container, + Parallel: r.parallel, + BufferSize: r.bufferSize, + UserGroup: r.userGroup, + CassandraDataDir: r.cassandraDataDir, + Authentication: r.authentication, + CassandraUsername: r.cassandraUsername, + NodetoolCredentialsFile: r.nodetoolCredentialsFile, } if err := cain.Restore(options); err != nil { log.Fatal(err) @@ -172,7 +186,9 @@ func NewRestoreCmd(out io.Writer) *cobra.Command { f.Float64VarP(&r.bufferSize, "buffer-size", "b", utils.GetFloat64EnvVar("CAIN_BUFFER_SIZE", 6.75), "in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE") f.StringVar(&r.userGroup, "user-group", utils.GetStringEnvVar("CAIN_USER_GROUP", "cassandra:cassandra"), "user and group who should own restored files. Overrides $CAIN_USER_GROUP") f.StringVar(&r.cassandraDataDir, "cassandra-data-dir", utils.GetStringEnvVar("CAIN_CASSANDRA_DATA_DIR", "/var/lib/cassandra/data"), "cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR") - + f.BoolVarP(&r.authentication, "authentication", "a", utils.GetBoolEnvVar("CAIN_AUTHENTICATION", false), "use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION") + f.StringVarP(&r.cassandraUsername, "cassandra-username", "u", utils.GetStringEnvVar("CAIN_CASSANDRA_USERNAME", "cain"), "cassandra username. Overrides $CAIN_CASSANDRA_USERNAME") + f.StringVarP(&r.nodetoolCredentialsFile, "nodetool-credentials-file", "f", utils.GetStringEnvVar("CAIN_NODETOOL_CREDENTIALS_FILE", "/home/cassandra/.nodetool/credentials"), "path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE") return cmd } diff --git a/pkg/cain/cain.go b/pkg/cain/cain.go index 21a8f73..4632293 100644 --- a/pkg/cain/cain.go +++ b/pkg/cain/cain.go @@ -9,16 +9,26 @@ import ( "github.com/nuvo/skbn/pkg/skbn" ) +// Authentication options if cassandra cluster uses authentication +type Credentials struct { + enabled bool + username string + nodetoolCredentialsFile string +} + // BackupOptions are the options to pass to Backup type BackupOptions struct { - Namespace string - Selector string - Container string - Keyspace string - Dst string - Parallel int - BufferSize float64 - CassandraDataDir string + Namespace string + Selector string + Container string + Keyspace string + Dst string + Parallel int + BufferSize float64 + CassandraDataDir string + Authentication bool + CassandraUsername string + NodetoolCredentialsFile string } // Backup performs backup @@ -46,15 +56,26 @@ func Backup(o BackupOptions) (string, error) { if err := utils.TestK8sDirectory(k8sClient, pods, o.Namespace, o.Container, o.CassandraDataDir); err != nil { return "", err } + creds := Credentials{ + enabled: o.Authentication, + username: o.CassandraUsername, + nodetoolCredentialsFile: o.NodetoolCredentialsFile, + } + if o.Authentication { + log.Println("Testing existence of nodetool credentials file") + if err := utils.TestK8sDirectory(k8sClient, pods, o.Namespace, o.Container, o.NodetoolCredentialsFile); err != nil { + return "", err + } + } log.Println("Backing up schema") - dstBasePath, err := BackupKeyspaceSchema(k8sClient, dstClient, o.Namespace, pods[0], o.Container, o.Keyspace, dstPrefix, dstPath) + dstBasePath, err := BackupKeyspaceSchema(k8sClient, dstClient, o.Namespace, pods[0], o.Container, o.Keyspace, dstPrefix, dstPath, creds) if err != nil { return "", err } log.Println("Taking snapshots") - tag := TakeSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace) + tag := TakeSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, creds) log.Println("Calculating paths. This may take a while...") fromToPathsAllPods, err := utils.GetFromAndToPathsFromK8s(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, tag, dstBasePath, o.CassandraDataDir) @@ -68,7 +89,7 @@ func Backup(o BackupOptions) (string, error) { } log.Println("Clearing snapshots") - ClearSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, tag) + ClearSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, tag, creds) log.Println("All done!") return tag, nil @@ -76,17 +97,20 @@ func Backup(o BackupOptions) (string, error) { // RestoreOptions are the options to pass to Restore type RestoreOptions struct { - Src string - Keyspace string - Tag string - Schema string - Namespace string - Selector string - Container string - Parallel int - BufferSize float64 - UserGroup string - CassandraDataDir string + Src string + Keyspace string + Tag string + Schema string + Namespace string + Selector string + Container string + Parallel int + BufferSize float64 + UserGroup string + CassandraDataDir string + Authentication bool + CassandraUsername string + NodetoolCredentialsFile string } // Restore performs restore @@ -110,7 +134,17 @@ func Restore(o RestoreOptions) error { if err := utils.TestK8sDirectory(k8sClient, existingPods, o.Namespace, o.Container, o.CassandraDataDir); err != nil { return err } - + creds := Credentials{ + enabled: o.Authentication, + username: o.CassandraUsername, + nodetoolCredentialsFile: o.NodetoolCredentialsFile, + } + if o.Authentication { + log.Println("Testing existence of nodetool credentials file") + if err := utils.TestK8sDirectory(k8sClient, existingPods, o.Namespace, o.Container, o.NodetoolCredentialsFile); err != nil { + return err + } + } log.Println("Getting current schema") _, sum, err := DescribeKeyspaceSchema(k8sClient, o.Namespace, existingPods[0], o.Container, o.Keyspace) if err != nil { @@ -163,7 +197,7 @@ func Restore(o RestoreOptions) error { } log.Println("Refreshing tables") - RefreshTables(k8sClient, o.Namespace, o.Container, o.Keyspace, podsToBeRestored, tablesToRefresh) + RefreshTables(k8sClient, o.Namespace, o.Container, o.Keyspace, podsToBeRestored, tablesToRefresh, creds) log.Println("All done!") return nil diff --git a/pkg/cain/cqlsh.go b/pkg/cain/cqlsh.go index df29b36..76d9552 100644 --- a/pkg/cain/cqlsh.go +++ b/pkg/cain/cqlsh.go @@ -13,8 +13,8 @@ import ( ) // BackupKeyspaceSchema gets the schema of the keyspace and backs it up -func BackupKeyspaceSchema(iK8sClient, iDstClient interface{}, namespace, pod, container, keyspace, dstPrefix, dstPath string) (string, error) { - clusterName, err := GetClusterName(iK8sClient, namespace, pod, container) +func BackupKeyspaceSchema(iK8sClient, iDstClient interface{}, namespace, pod, container, keyspace, dstPrefix, dstPath string, creds Credentials) (string, error) { + clusterName, err := GetClusterName(iK8sClient, namespace, pod, container, creds) if err != nil { return "", err } @@ -148,7 +148,6 @@ func CqlshF(iK8sClient interface{}, namespace, pod, container string, file strin command := []string{"cqlsh", "-f", file} stdout := new(bytes.Buffer) stderr, err := skbn.Exec(*k8sClient, namespace, pod, container, command, nil, stdout) - if len(stderr) != 0 { return nil, fmt.Errorf("STDERR: " + (string)(stderr)) } diff --git a/pkg/cain/nodetool.go b/pkg/cain/nodetool.go index 8c1e2b5..7e988b6 100644 --- a/pkg/cain/nodetool.go +++ b/pkg/cain/nodetool.go @@ -11,7 +11,7 @@ import ( ) // TakeSnapshots takes a snapshot using nodetool in all pods in parallel -func TakeSnapshots(iClient interface{}, pods []string, namespace, container, keyspace string) string { +func TakeSnapshots(iClient interface{}, pods []string, namespace, container, keyspace string, creds Credentials) string { k8sClient := iClient.(*skbn.K8sClient) tag := utils.GetTimeStamp() bwgSize := len(pods) @@ -20,7 +20,7 @@ func TakeSnapshots(iClient interface{}, pods []string, namespace, container, key bwg.Add(1) go func(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, tag string) { - if err := takeSnapshot(k8sClient, namespace, pod, container, keyspace, tag); err != nil { + if err := takeSnapshot(k8sClient, namespace, pod, container, keyspace, tag, creds); err != nil { log.Fatal(err) } bwg.Done() @@ -32,7 +32,7 @@ func TakeSnapshots(iClient interface{}, pods []string, namespace, container, key } // ClearSnapshots clears a snapshot using nodetool in all pods in parallel -func ClearSnapshots(iClient interface{}, pods []string, namespace, container, keyspace, tag string) { +func ClearSnapshots(iClient interface{}, pods []string, namespace, container, keyspace, tag string, creds Credentials) { k8sClient := iClient.(*skbn.K8sClient) bwgSize := len(pods) bwg := utils.NewBoundedWaitGroup(bwgSize) @@ -40,7 +40,7 @@ func ClearSnapshots(iClient interface{}, pods []string, namespace, container, ke bwg.Add(1) go func(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, tag string) { - if err := clearSnapshot(k8sClient, namespace, pod, container, keyspace, tag); err != nil { + if err := clearSnapshot(k8sClient, namespace, pod, container, keyspace, tag, creds); err != nil { log.Fatal(err) } bwg.Done() @@ -50,30 +50,30 @@ func ClearSnapshots(iClient interface{}, pods []string, namespace, container, ke } // RefreshTables refreshes tables in all pods in parallel -func RefreshTables(iClient interface{}, namespace, container, keyspace string, pods, tables []string) { +func RefreshTables(iClient interface{}, namespace, container, keyspace string, pods, tables []string, creds Credentials) { k8sClient := iClient.(*skbn.K8sClient) bwgSize := len(pods) bwg := utils.NewBoundedWaitGroup(bwgSize) for _, pod := range pods { bwg.Add(1) - go func(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace string, table []string) { + go func(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace string, tables []string, creds Credentials) { for _, table := range tables { - if err := refreshTable(k8sClient, namespace, pod, container, keyspace, table); err != nil { + if err := refreshTable(k8sClient, namespace, pod, container, keyspace, table, creds); err != nil { log.Fatal(err) } } bwg.Done() - }(k8sClient, namespace, pod, container, keyspace, tables) + }(k8sClient, namespace, pod, container, keyspace, tables, creds) } bwg.Wait() } // GetClusterName gets the name of the cassandra cluster -func GetClusterName(iClient interface{}, namespace, pod, container string) (string, error) { +func GetClusterName(iClient interface{}, namespace, pod, container string, creds Credentials) (string, error) { k8sClient := iClient.(*skbn.K8sClient) command := []string{"describecluster"} - output, err := nodetool(k8sClient, namespace, pod, container, command) + output, err := nodetool(k8sClient, namespace, pod, container, command, creds) if err != nil { return "", err } @@ -89,10 +89,10 @@ func GetClusterName(iClient interface{}, namespace, pod, container string) (stri return output, nil } -func takeSnapshot(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, tag string) error { +func takeSnapshot(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, tag string, creds Credentials) error { log.Println(pod, "Taking snapshot of keyspace", keyspace) command := []string{"snapshot", "-t", tag, keyspace} - output, err := nodetool(k8sClient, namespace, pod, container, command) + output, err := nodetool(k8sClient, namespace, pod, container, command, creds) if err != nil { return err } @@ -100,10 +100,10 @@ func takeSnapshot(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace return nil } -func clearSnapshot(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, tag string) error { +func clearSnapshot(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, tag string, creds Credentials) error { log.Println(pod, "Clearing snapshot of keyspace", keyspace) command := []string{"clearsnapshot", "-t", tag, keyspace} - output, err := nodetool(k8sClient, namespace, pod, container, command) + output, err := nodetool(k8sClient, namespace, pod, container, command, creds) if err != nil { return err } @@ -111,10 +111,10 @@ func clearSnapshot(k8sClient *skbn.K8sClient, namespace, pod, container, keyspac return nil } -func refreshTable(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, table string) error { +func refreshTable(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, table string, creds Credentials) error { log.Println(pod, "Refreshing table", table, "in keyspace", keyspace) command := []string{"refresh", keyspace, table} - output, err := nodetool(k8sClient, namespace, pod, container, command) + output, err := nodetool(k8sClient, namespace, pod, container, command, creds) if err != nil { return err } @@ -122,8 +122,13 @@ func refreshTable(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace return nil } -func nodetool(k8sClient *skbn.K8sClient, namespace, pod, container string, command []string) (string, error) { - command = append([]string{"nodetool"}, command...) +func nodetool(k8sClient *skbn.K8sClient, namespace, pod, container string, args []string, creds Credentials) (string, error) { + var command []string + if creds.enabled { + command = append([]string{"nodetool", "-u", creds.username, "-pwf", creds.nodetoolCredentialsFile}, args...) + } else { + command = append([]string{"nodetool"}, args...) + } stdout := new(bytes.Buffer) stderr, err := skbn.Exec(*k8sClient, namespace, pod, container, command, nil, stdout) if len(stderr) != 0 {