feat(cmd/influx): add secret cli
parent
e3acd7f86b
commit
670d2b7113
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
1. [16765](https://github.com/influxdata/influxdb/pull/16765): Extend influx cli pkg command with ability to take multiple files and directories
|
1. [16765](https://github.com/influxdata/influxdb/pull/16765): Extend influx cli pkg command with ability to take multiple files and directories
|
||||||
1. [16767](https://github.com/influxdata/influxdb/pull/16767): Extend influx cli pkg command with ability to take multiple urls, files, directories, and stdin at the same time
|
1. [16767](https://github.com/influxdata/influxdb/pull/16767): Extend influx cli pkg command with ability to take multiple urls, files, directories, and stdin at the same time
|
||||||
|
1. [16786](https://github.com/influxdata/influxdb/pull/16786): influx cli can manage secrets.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/influxdb"
|
"github.com/influxdata/influxdb"
|
||||||
"github.com/influxdata/influxdb/cmd/influx/internal"
|
|
||||||
"github.com/influxdata/influxdb/http"
|
"github.com/influxdata/influxdb/http"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
@ -108,7 +107,7 @@ func (b *cmdBucketBuilder) cmdCreateRunEFn(*cobra.Command, []string) error {
|
||||||
return fmt.Errorf("failed to create bucket: %v", err)
|
return fmt.Errorf("failed to create bucket: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID")
|
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID")
|
||||||
w.Write(map[string]interface{}{
|
w.Write(map[string]interface{}{
|
||||||
"ID": bkt.ID.String(),
|
"ID": bkt.ID.String(),
|
||||||
|
@ -152,7 +151,7 @@ func (b *cmdBucketBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) er
|
||||||
return fmt.Errorf("failed to delete bucket with id %q: %v", id, err)
|
return fmt.Errorf("failed to delete bucket with id %q: %v", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID", "Deleted")
|
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID", "Deleted")
|
||||||
w.Write(map[string]interface{}{
|
w.Write(map[string]interface{}{
|
||||||
"ID": bkt.ID.String(),
|
"ID": bkt.ID.String(),
|
||||||
|
@ -225,7 +224,7 @@ func (b *cmdBucketBuilder) cmdFindRunEFn(cmd *cobra.Command, args []string) erro
|
||||||
return fmt.Errorf("failed to retrieve buckets: %s", err)
|
return fmt.Errorf("failed to retrieve buckets: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.HideHeaders(!b.headers)
|
w.HideHeaders(!b.headers)
|
||||||
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID")
|
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID")
|
||||||
for _, b := range buckets {
|
for _, b := range buckets {
|
||||||
|
@ -291,7 +290,7 @@ func (b *cmdBucketBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) er
|
||||||
return fmt.Errorf("failed to update bucket: %v", err)
|
return fmt.Errorf("failed to update bucket: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID")
|
w.WriteHeaders("ID", "Name", "Retention", "OrganizationID")
|
||||||
w.Write(map[string]interface{}{
|
w.Write(map[string]interface{}{
|
||||||
"ID": bkt.ID.String(),
|
"ID": bkt.ID.String(),
|
||||||
|
|
|
@ -9,30 +9,35 @@ import (
|
||||||
platform "github.com/influxdata/influxdb"
|
platform "github.com/influxdata/influxdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tabWriter struct {
|
// TabWriter wraps tab writer headers logic.
|
||||||
|
type TabWriter struct {
|
||||||
writer *tabwriter.Writer
|
writer *tabwriter.Writer
|
||||||
headers []string
|
headers []string
|
||||||
hideHeaders bool
|
hideHeaders bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTabWriter(w io.Writer) *tabWriter {
|
// NewTabWriter creates a new tab writer.
|
||||||
return &tabWriter{
|
func NewTabWriter(w io.Writer) *TabWriter {
|
||||||
|
return &TabWriter{
|
||||||
writer: tabwriter.NewWriter(w, 0, 8, 1, '\t', 0),
|
writer: tabwriter.NewWriter(w, 0, 8, 1, '\t', 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *tabWriter) HideHeaders(b bool) {
|
// HideHeaders will set the hideHeaders flag.
|
||||||
|
func (w *TabWriter) HideHeaders(b bool) {
|
||||||
w.hideHeaders = b
|
w.hideHeaders = b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *tabWriter) WriteHeaders(h ...string) {
|
// WriteHeaders will write headers.
|
||||||
|
func (w *TabWriter) WriteHeaders(h ...string) {
|
||||||
w.headers = h
|
w.headers = h
|
||||||
if !w.hideHeaders {
|
if !w.hideHeaders {
|
||||||
fmt.Fprintln(w.writer, strings.Join(h, "\t"))
|
fmt.Fprintln(w.writer, strings.Join(h, "\t"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *tabWriter) Write(m map[string]interface{}) {
|
// Write will write the map into embed tab writer.
|
||||||
|
func (w *TabWriter) Write(m map[string]interface{}) {
|
||||||
body := make([]interface{}, len(w.headers))
|
body := make([]interface{}, len(w.headers))
|
||||||
types := make([]string, len(w.headers))
|
types := make([]string, len(w.headers))
|
||||||
for i, h := range w.headers {
|
for i, h := range w.headers {
|
||||||
|
@ -45,7 +50,11 @@ func (w *tabWriter) Write(m map[string]interface{}) {
|
||||||
fmt.Fprintf(w.writer, formatString+"\n", body...)
|
fmt.Fprintf(w.writer, formatString+"\n", body...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *tabWriter) Flush() {
|
// Flush should be called after the last call to Write to ensure
|
||||||
|
// that any data buffered in the Writer is written to output. Any
|
||||||
|
// incomplete escape sequence at the end is considered
|
||||||
|
// complete for formatting purposes.
|
||||||
|
func (w *TabWriter) Flush() {
|
||||||
w.writer.Flush()
|
w.writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,10 @@ func (o genericCLIOpts) newCmd(use string, runE func(*cobra.Command, []string) e
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o genericCLIOpts) newTabWriter() *internal.TabWriter {
|
||||||
|
return internal.NewTabWriter(o.w)
|
||||||
|
}
|
||||||
|
|
||||||
func in(r io.Reader) genericCLIOptFn {
|
func in(r io.Reader) genericCLIOptFn {
|
||||||
return func(o *genericCLIOpts) {
|
return func(o *genericCLIOpts) {
|
||||||
o.in = r
|
o.in = r
|
||||||
|
@ -128,6 +132,7 @@ func influxCmd(opts ...genericCLIOptFn) *cobra.Command {
|
||||||
cmdQuery(),
|
cmdQuery(),
|
||||||
cmdTranspile(),
|
cmdTranspile(),
|
||||||
cmdREPL(),
|
cmdREPL(),
|
||||||
|
cmdSecret(runEWrapper),
|
||||||
cmdSetup(),
|
cmdSetup(),
|
||||||
cmdTask(),
|
cmdTask(),
|
||||||
cmdUser(runEWrapper),
|
cmdUser(runEWrapper),
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/influxdata/influxdb/http"
|
"github.com/influxdata/influxdb/http"
|
||||||
|
|
||||||
"github.com/influxdata/influxdb"
|
"github.com/influxdata/influxdb"
|
||||||
"github.com/influxdata/influxdb/cmd/influx/internal"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ func (b *cmdOrgBuilder) createRunEFn(cmd *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("failed to create organization: %v", err)
|
return fmt.Errorf("failed to create organization: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders("ID", "Name")
|
w.WriteHeaders("ID", "Name")
|
||||||
w.Write(map[string]interface{}{
|
w.Write(map[string]interface{}{
|
||||||
"ID": org.ID.String(),
|
"ID": org.ID.String(),
|
||||||
|
@ -138,7 +137,7 @@ func (b *cmdOrgBuilder) deleteRunEFn(cmd *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("failed to delete org with id %q: %v", id, err)
|
return fmt.Errorf("failed to delete org with id %q: %v", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders("ID", "Name", "Deleted")
|
w.WriteHeaders("ID", "Name", "Deleted")
|
||||||
w.Write(map[string]interface{}{
|
w.Write(map[string]interface{}{
|
||||||
"ID": o.ID.String(),
|
"ID": o.ID.String(),
|
||||||
|
@ -199,7 +198,7 @@ func (b *cmdOrgBuilder) findRunEFn(cmd *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("failed find orgs: %v", err)
|
return fmt.Errorf("failed find orgs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders("ID", "Name")
|
w.WriteHeaders("ID", "Name")
|
||||||
for _, o := range orgs {
|
for _, o := range orgs {
|
||||||
w.Write(map[string]interface{}{
|
w.Write(map[string]interface{}{
|
||||||
|
@ -269,7 +268,7 @@ func (b *cmdOrgBuilder) updateRunEFn(cmd *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("failed to update org: %v", err)
|
return fmt.Errorf("failed to update org: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders("ID", "Name")
|
w.WriteHeaders("ID", "Name")
|
||||||
w.Write(map[string]interface{}{
|
w.Write(map[string]interface{}{
|
||||||
"ID": o.ID.String(),
|
"ID": o.ID.String(),
|
||||||
|
@ -348,7 +347,7 @@ func (b *cmdOrgBuilder) memberListRunEFn(cmd *cobra.Command, args []string) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
return memberList(ctx, b.w, urmSVC, userSVC, influxdb.UserResourceMappingFilter{
|
return memberList(ctx, b, urmSVC, userSVC, influxdb.UserResourceMappingFilter{
|
||||||
ResourceType: influxdb.OrgsResourceType,
|
ResourceType: influxdb.OrgsResourceType,
|
||||||
ResourceID: organization.ID,
|
ResourceID: organization.ID,
|
||||||
UserType: influxdb.Member,
|
UserType: influxdb.Member,
|
||||||
|
@ -538,7 +537,7 @@ func newOrganizationService() (influxdb.OrganizationService, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func memberList(ctx context.Context, w io.Writer, urmSVC influxdb.UserResourceMappingService, userSVC influxdb.UserService, f influxdb.UserResourceMappingFilter) error {
|
func memberList(ctx context.Context, b *cmdOrgBuilder, urmSVC influxdb.UserResourceMappingService, userSVC influxdb.UserService, f influxdb.UserResourceMappingFilter) error {
|
||||||
mps, _, err := urmSVC.FindUserResourceMappings(ctx, f)
|
mps, _, err := urmSVC.FindUserResourceMappings(ctx, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find members: %v", err)
|
return fmt.Errorf("failed to find members: %v", err)
|
||||||
|
@ -582,7 +581,7 @@ func memberList(ctx context.Context, w io.Writer, urmSVC influxdb.UserResourceMa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tw := internal.NewTabWriter(w)
|
tw := b.newTabWriter()
|
||||||
tw.WriteHeaders("ID", "Name", "Status")
|
tw.WriteHeaders("ID", "Name", "Status")
|
||||||
for _, m := range urs {
|
for _, m := range urs {
|
||||||
tw.Write(map[string]interface{}{
|
tw.Write(map[string]interface{}{
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/influxdata/influxdb"
|
||||||
|
"github.com/influxdata/influxdb/http"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
input "github.com/tcnksm/go-input"
|
||||||
|
)
|
||||||
|
|
||||||
|
type secretSVCsFn func() (influxdb.SecretService, influxdb.OrganizationService, func(*input.UI) string, error)
|
||||||
|
|
||||||
|
func cmdSecret(opts ...genericCLIOptFn) *cobra.Command {
|
||||||
|
return newCmdSecretBuilder(newSecretSVCs, opts...).cmd()
|
||||||
|
}
|
||||||
|
|
||||||
|
type cmdSecretBuilder struct {
|
||||||
|
genericCLIOpts
|
||||||
|
|
||||||
|
svcFn secretSVCsFn
|
||||||
|
|
||||||
|
key string
|
||||||
|
org organization
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCmdSecretBuilder(svcsFn secretSVCsFn, opts ...genericCLIOptFn) *cmdSecretBuilder {
|
||||||
|
opt := genericCLIOpts{
|
||||||
|
in: os.Stdin,
|
||||||
|
w: os.Stdout,
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cmdSecretBuilder{
|
||||||
|
genericCLIOpts: opt,
|
||||||
|
svcFn: svcsFn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cmdSecretBuilder) cmd() *cobra.Command {
|
||||||
|
cmd := b.newCmd("secret", nil)
|
||||||
|
cmd.Short = "Secret management commands"
|
||||||
|
cmd.Run = seeHelp
|
||||||
|
cmd.AddCommand(
|
||||||
|
b.cmdDelete(),
|
||||||
|
b.cmdFind(),
|
||||||
|
b.cmdUpdate(),
|
||||||
|
)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cmdSecretBuilder) cmdUpdate() *cobra.Command {
|
||||||
|
cmd := b.newCmd("update", b.cmdUpdateRunEFn)
|
||||||
|
cmd.Short = "Update secret"
|
||||||
|
cmd.Flags().StringVarP(&b.key, "key", "k", "", "The secret key (required)")
|
||||||
|
cmd.MarkFlagRequired("key")
|
||||||
|
b.org.register(cmd, false)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cmdSecretBuilder) cmdDelete() *cobra.Command {
|
||||||
|
cmd := b.newCmd("delete", b.cmdDeleteRunEFn)
|
||||||
|
cmd.Short = "Delete secret"
|
||||||
|
|
||||||
|
cmd.Flags().StringVarP(&b.key, "key", "k", "", "The secret key (required)")
|
||||||
|
cmd.MarkFlagRequired("key")
|
||||||
|
b.org.register(cmd, false)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cmdSecretBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) error {
|
||||||
|
scrSVC, orgSVC, getSecretFn, err := b.svcFn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
orgID, err := b.org.getID(orgSVC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
ui := &input.UI{
|
||||||
|
Writer: b.genericCLIOpts.w,
|
||||||
|
Reader: b.genericCLIOpts.in,
|
||||||
|
}
|
||||||
|
secret := getSecretFn(ui)
|
||||||
|
|
||||||
|
if err := scrSVC.PatchSecrets(ctx, orgID, map[string]string{b.key: secret}); err != nil {
|
||||||
|
return fmt.Errorf("failed to update secret with key %q: %v", b.key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := b.newTabWriter()
|
||||||
|
w.WriteHeaders("Key", "OrgID", "Updated")
|
||||||
|
w.Write(map[string]interface{}{
|
||||||
|
"Key": b.key,
|
||||||
|
"OrgID": orgID,
|
||||||
|
"Updated": true,
|
||||||
|
})
|
||||||
|
w.Flush()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cmdSecretBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) error {
|
||||||
|
scrSVC, orgSVC, _, err := b.svcFn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
orgID, err := b.org.getID(orgSVC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if err := scrSVC.DeleteSecret(ctx, orgID, b.key); err != nil {
|
||||||
|
return fmt.Errorf("failed to delete secret with key %q: %v", b.key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := b.newTabWriter()
|
||||||
|
w.WriteHeaders("Key", "OrgID", "Deleted")
|
||||||
|
w.Write(map[string]interface{}{
|
||||||
|
"Key": b.key,
|
||||||
|
"OrgID": orgID,
|
||||||
|
"Deleted": true,
|
||||||
|
})
|
||||||
|
w.Flush()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cmdSecretBuilder) cmdFind() *cobra.Command {
|
||||||
|
cmd := b.newCmd("find", b.cmdFindRunEFn)
|
||||||
|
cmd.Short = "Find secrets"
|
||||||
|
b.org.register(cmd, false)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cmdSecretBuilder) cmdFindRunEFn(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
scrSVC, orgSVC, _, err := b.svcFn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID, err := b.org.getID(orgSVC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets, err := scrSVC.GetSecretKeys(context.Background(), orgID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve secret keys: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := b.newTabWriter()
|
||||||
|
w.WriteHeaders("Key", "OrganizationID")
|
||||||
|
for _, s := range secrets {
|
||||||
|
w.Write(map[string]interface{}{
|
||||||
|
"Key": s,
|
||||||
|
"OrganizationID": orgID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSecretSVCs() (influxdb.SecretService, influxdb.OrganizationService, func(*input.UI) string, error) {
|
||||||
|
httpClient, err := newHTTPClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
orgSvc := &http.OrganizationService{Client: httpClient}
|
||||||
|
|
||||||
|
return &http.SecretService{Client: httpClient}, orgSvc, getSecret, nil
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/influxdata/influxdb"
|
||||||
|
"github.com/influxdata/influxdb/mock"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
input "github.com/tcnksm/go-input"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCmdSecret(t *testing.T) {
|
||||||
|
setViperOptions()
|
||||||
|
|
||||||
|
orgID := influxdb.ID(9000)
|
||||||
|
|
||||||
|
fakeSVCFn := func(svc influxdb.SecretService, fn func(*input.UI) string) secretSVCsFn {
|
||||||
|
return func() (influxdb.SecretService, influxdb.OrganizationService, func(*input.UI) string, error) {
|
||||||
|
return svc, &mock.OrganizationService{
|
||||||
|
FindOrganizationF: func(ctx context.Context, filter influxdb.OrganizationFilter) (*influxdb.Organization, error) {
|
||||||
|
return &influxdb.Organization{ID: orgID, Name: "influxdata"}, nil
|
||||||
|
},
|
||||||
|
}, fn, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("find", func(t *testing.T) {
|
||||||
|
type called []string
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expected called
|
||||||
|
flags []string
|
||||||
|
envVars map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "org id",
|
||||||
|
flags: []string{"--org-id=" + influxdb.ID(3).String()},
|
||||||
|
envVars: envVarsZeroMap,
|
||||||
|
expected: called{"k1", "k2", "k3"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "org",
|
||||||
|
flags: []string{"--org=rg"},
|
||||||
|
envVars: envVarsZeroMap,
|
||||||
|
expected: called{"k1", "k2", "k3"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "env vars",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"INFLUX_ORG": "rg",
|
||||||
|
},
|
||||||
|
flags: []string{},
|
||||||
|
expected: called{"k1", "k2", "k3"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdFn := func() (*cobra.Command, *called) {
|
||||||
|
calls := new(called)
|
||||||
|
svc := mock.NewSecretService()
|
||||||
|
svc.GetSecretKeysFn = func(ctx context.Context, organizationID influxdb.ID) ([]string, error) {
|
||||||
|
if !organizationID.Valid() {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
*calls = []string{"k1", "k2", "k3"}
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := newCmdSecretBuilder(fakeSVCFn(svc, nil), in(new(bytes.Buffer)), out(ioutil.Discard))
|
||||||
|
cmd := builder.cmdFind()
|
||||||
|
cmd.RunE = builder.cmdFindRunEFn
|
||||||
|
return cmd, calls
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
fn := func(t *testing.T) {
|
||||||
|
defer addEnvVars(t, tt.envVars)()
|
||||||
|
|
||||||
|
cmd, calls := cmdFn()
|
||||||
|
cmd.SetArgs(tt.flags)
|
||||||
|
|
||||||
|
require.NoError(t, cmd.Execute())
|
||||||
|
assert.Equal(t, tt.expected, *calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(tt.name, fn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("delete", func(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expectedKey string
|
||||||
|
flags []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "with key",
|
||||||
|
expectedKey: "key1",
|
||||||
|
flags: []string{
|
||||||
|
"--org=org name", "--key=key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shorts",
|
||||||
|
expectedKey: "key1",
|
||||||
|
flags: []string{"-o=" + orgID.String(), "-k=key1"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdFn := func(expectedKey string) *cobra.Command {
|
||||||
|
svc := mock.NewSecretService()
|
||||||
|
svc.DeleteSecretFn = func(ctx context.Context, orgID influxdb.ID, ks ...string) error {
|
||||||
|
if expectedKey != ks[0] {
|
||||||
|
return fmt.Errorf("unexpected id:\n\twant= %s\n\tgot= %s", expectedKey, ks[0])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := newCmdSecretBuilder(fakeSVCFn(svc, nil), out(ioutil.Discard))
|
||||||
|
cmd := builder.cmdDelete()
|
||||||
|
cmd.RunE = builder.cmdDeleteRunEFn
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
fn := func(t *testing.T) {
|
||||||
|
cmd := cmdFn(tt.expectedKey)
|
||||||
|
cmd.SetArgs(tt.flags)
|
||||||
|
require.NoError(t, cmd.Execute())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(tt.name, fn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("update", func(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expectedKey string
|
||||||
|
flags []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "with key",
|
||||||
|
expectedKey: "key1",
|
||||||
|
flags: []string{
|
||||||
|
"--org=org name", "--key=key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shorts",
|
||||||
|
expectedKey: "key1",
|
||||||
|
flags: []string{"-o=" + orgID.String(), "-k=key1"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdFn := func(expectedKey string) *cobra.Command {
|
||||||
|
svc := mock.NewSecretService()
|
||||||
|
svc.PatchSecretsFn = func(ctx context.Context, orgID influxdb.ID, m map[string]string) error {
|
||||||
|
var key string
|
||||||
|
for k := range m {
|
||||||
|
key = k
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if expectedKey != key {
|
||||||
|
return fmt.Errorf("unexpected id:\n\twant= %s\n\tgot= %s", expectedKey, key)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
getSctFn := func(*input.UI) string {
|
||||||
|
return "ss"
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := newCmdSecretBuilder(fakeSVCFn(svc, getSctFn), out(ioutil.Discard))
|
||||||
|
cmd := builder.cmdUpdate()
|
||||||
|
cmd.RunE = builder.cmdUpdateRunEFn
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
fn := func(t *testing.T) {
|
||||||
|
cmd := cmdFn(tt.expectedKey)
|
||||||
|
cmd.SetArgs(tt.flags)
|
||||||
|
require.NoError(t, cmd.Execute())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(tt.name, fn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/influxdata/influxdb"
|
||||||
platform "github.com/influxdata/influxdb"
|
platform "github.com/influxdata/influxdb"
|
||||||
"github.com/influxdata/influxdb/cmd/influx/internal"
|
"github.com/influxdata/influxdb/cmd/influx/internal"
|
||||||
"github.com/influxdata/influxdb/http"
|
"github.com/influxdata/influxdb/http"
|
||||||
|
@ -236,28 +237,42 @@ You have entered:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func errSecretIsNotMatch(title string) error {
|
||||||
errPasswordIsNotMatch = fmt.Errorf("passwords do not match")
|
return fmt.Errorf(title + "s do not match")
|
||||||
errPasswordIsTooShort = fmt.Errorf("passwords is too short")
|
}
|
||||||
)
|
|
||||||
|
func errSecretIsTooShort(title string) error {
|
||||||
|
return &influxdb.Error{
|
||||||
|
Code: influxdb.EUnprocessableEntity,
|
||||||
|
Msg: title + " is too short",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSecret(ui *input.UI) (secret string) {
|
||||||
|
return getSecretInput(ui, false, "secret")
|
||||||
|
}
|
||||||
|
|
||||||
func getPassword(ui *input.UI, showNew bool) (password string) {
|
func getPassword(ui *input.UI, showNew bool) (password string) {
|
||||||
|
return getSecretInput(ui, showNew, "password")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSecretInput(ui *input.UI, showNew bool, title string) (secret string) {
|
||||||
newStr := ""
|
newStr := ""
|
||||||
if showNew {
|
if showNew {
|
||||||
newStr = " new"
|
newStr = " new"
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
enterPasswd:
|
enterSecret:
|
||||||
query := string(promptWithColor("Please type your"+newStr+" password", colorCyan))
|
query := string(promptWithColor("Please type your"+newStr+" "+title, colorCyan))
|
||||||
for {
|
for {
|
||||||
password, err = ui.Ask(query, &input.Options{
|
secret, err = ui.Ask(query, &input.Options{
|
||||||
Required: true,
|
Required: true,
|
||||||
HideOrder: true,
|
HideOrder: true,
|
||||||
Hide: true,
|
Hide: true,
|
||||||
Mask: false,
|
Mask: false,
|
||||||
ValidateFunc: func(s string) error {
|
ValidateFunc: func(s string) error {
|
||||||
if len(s) < 8 {
|
if len(s) < 8 {
|
||||||
return errPasswordIsTooShort
|
return errSecretIsTooShort(title)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -265,25 +280,25 @@ enterPasswd:
|
||||||
switch err {
|
switch err {
|
||||||
case input.ErrInterrupted:
|
case input.ErrInterrupted:
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
case errPasswordIsTooShort:
|
|
||||||
ui.Writer.Write(promptWithColor("Password too short - minimum length is 8 characters!", colorRed))
|
|
||||||
goto enterPasswd
|
|
||||||
default:
|
default:
|
||||||
if password = strings.TrimSpace(password); password == "" {
|
if influxdb.ErrorCode(err) == influxdb.EUnprocessableEntity {
|
||||||
|
ui.Writer.Write(promptWithColor(strings.ToTitle(title)+" too short - minimum length is 8 characters!\n\r", colorRed))
|
||||||
|
goto enterSecret
|
||||||
|
} else if secret = strings.TrimSpace(secret); secret == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
query = string(promptWithColor("Please type your"+newStr+" password again", colorCyan))
|
query = string(promptWithColor("Please type your"+newStr+" "+title+" again", colorCyan))
|
||||||
for {
|
for {
|
||||||
_, err = ui.Ask(query, &input.Options{
|
_, err = ui.Ask(query, &input.Options{
|
||||||
Required: true,
|
Required: true,
|
||||||
HideOrder: true,
|
HideOrder: true,
|
||||||
Hide: true,
|
Hide: true,
|
||||||
ValidateFunc: func(s string) error {
|
ValidateFunc: func(s string) error {
|
||||||
if s != password {
|
if s != secret {
|
||||||
return errPasswordIsNotMatch
|
return errSecretIsNotMatch(title)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -294,12 +309,12 @@ enterPasswd:
|
||||||
case nil:
|
case nil:
|
||||||
// Nothing.
|
// Nothing.
|
||||||
default:
|
default:
|
||||||
ui.Writer.Write(promptWithColor("Passwords do not match!\n", colorRed))
|
ui.Writer.Write(promptWithColor(strings.ToTitle(title)+"s do not match!\n", colorRed))
|
||||||
goto enterPasswd
|
goto enterSecret
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return password
|
return secret
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInput(ui *input.UI, prompt, defaultValue string) string {
|
func getInput(ui *input.UI, prompt, defaultValue string) string {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/influxdata/influxdb"
|
"github.com/influxdata/influxdb"
|
||||||
"github.com/influxdata/influxdb/cmd/influx/internal"
|
|
||||||
"github.com/influxdata/influxdb/http"
|
"github.com/influxdata/influxdb/http"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
input "github.com/tcnksm/go-input"
|
input "github.com/tcnksm/go-input"
|
||||||
|
@ -184,7 +183,7 @@ func (b *cmdUserBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders(
|
w.WriteHeaders(
|
||||||
"ID",
|
"ID",
|
||||||
"Name",
|
"Name",
|
||||||
|
@ -247,7 +246,7 @@ func (b *cmdUserBuilder) cmdCreateRunEFn(*cobra.Command, []string) error {
|
||||||
for i, h := range headers {
|
for i, h := range headers {
|
||||||
m[h] = vals[i]
|
m[h] = vals[i]
|
||||||
}
|
}
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders(headers...)
|
w.WriteHeaders(headers...)
|
||||||
w.Write(m)
|
w.Write(m)
|
||||||
w.Flush()
|
w.Flush()
|
||||||
|
@ -319,7 +318,7 @@ func (b *cmdUserBuilder) cmdFindRunEFn(*cobra.Command, []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders(
|
w.WriteHeaders(
|
||||||
"ID",
|
"ID",
|
||||||
"Name",
|
"Name",
|
||||||
|
@ -366,7 +365,7 @@ func (b *cmdUserBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w := internal.NewTabWriter(b.w)
|
w := b.newTabWriter()
|
||||||
w.WriteHeaders(
|
w.WriteHeaders(
|
||||||
"ID",
|
"ID",
|
||||||
"Name",
|
"Name",
|
||||||
|
|
Loading…
Reference in New Issue