Merge pull request #16537 from influxdata/cli_update_password
feat(cmd/influx): update password in clipull/16553/head
commit
540e785eb4
|
@ -44,6 +44,7 @@
|
|||
1. [16509](https://github.com/influxdata/influxdb/pull/16509): Add support for applying an influx package via a public facing URL
|
||||
1. [16511](https://github.com/influxdata/influxdb/pull/16511): Add jsonnet support for influx packages
|
||||
1. [14782](https://github.com/influxdata/influxdb/pull/16336): Add view page for Check
|
||||
1. [16537](https://github.com/influxdata/influxdb/pull/16537): Add update password for CLI
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
@ -68,6 +69,7 @@
|
|||
1. [16491](https://github.com/influxdata/influxdb/pull/16491): Add missing env vals to influx cli usage and fixes precedence of flag/env var priority
|
||||
|
||||
### UI Improvements
|
||||
|
||||
1. [16444](https://github.com/influxdata/influxdb/pull/16444): Add honeybadger reporting to create checks
|
||||
|
||||
## v2.0.0-alpha.21 [2019-12-13]
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/influxdb"
|
||||
|
@ -102,7 +103,7 @@ func TestCmdBucket(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer)))
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(ioutil.Discard))
|
||||
cmd := builder.cmdCreate()
|
||||
cmd.RunE = builder.cmdCreateRunEFn
|
||||
return cmd
|
||||
|
@ -151,7 +152,7 @@ func TestCmdBucket(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer)))
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(ioutil.Discard))
|
||||
cmd := builder.cmdDelete()
|
||||
cmd.RunE = builder.cmdDeleteRunEFn
|
||||
return cmd
|
||||
|
@ -258,7 +259,7 @@ func TestCmdBucket(t *testing.T) {
|
|||
return nil, 0, nil
|
||||
}
|
||||
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), in(new(bytes.Buffer)), out(ioutil.Discard))
|
||||
cmd := builder.cmdFind()
|
||||
cmd.RunE = builder.cmdFindRunEFn
|
||||
return cmd, calls
|
||||
|
@ -352,7 +353,7 @@ func TestCmdBucket(t *testing.T) {
|
|||
return &influxdb.Bucket{}, nil
|
||||
}
|
||||
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer)))
|
||||
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(ioutil.Discard))
|
||||
cmd := builder.cmdUpdate()
|
||||
cmd.RunE = builder.cmdUpdateRunEFn
|
||||
return cmd
|
||||
|
|
|
@ -128,7 +128,7 @@ func influxCmd(opts ...genericCLIOptFn) *cobra.Command {
|
|||
cmdREPL(),
|
||||
cmdSetup(),
|
||||
cmdTask(),
|
||||
cmdUser(),
|
||||
cmdUser(runEWrapper),
|
||||
cmdWrite(),
|
||||
)
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -63,7 +64,7 @@ func TestCmdOrg(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(new(bytes.Buffer)))
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(ioutil.Discard))
|
||||
cmd := builder.cmdCreate()
|
||||
return cmd
|
||||
}
|
||||
|
@ -109,7 +110,7 @@ func TestCmdOrg(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(new(bytes.Buffer)))
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(ioutil.Discard))
|
||||
cmd := builder.cmdDelete()
|
||||
return cmd
|
||||
}
|
||||
|
@ -181,7 +182,7 @@ func TestCmdOrg(t *testing.T) {
|
|||
return nil, 0, nil
|
||||
}
|
||||
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(ioutil.Discard))
|
||||
cmd := builder.cmdFind()
|
||||
return cmd, calls
|
||||
}
|
||||
|
@ -268,7 +269,7 @@ func TestCmdOrg(t *testing.T) {
|
|||
return &influxdb.Organization{}, nil
|
||||
}
|
||||
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(new(bytes.Buffer)))
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(ioutil.Discard))
|
||||
cmd := builder.cmdUpdate()
|
||||
return cmd
|
||||
}
|
||||
|
@ -366,7 +367,7 @@ func TestCmdOrg(t *testing.T) {
|
|||
return &influxdb.Organization{ID: 1}, nil
|
||||
}
|
||||
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
|
||||
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(ioutil.Discard))
|
||||
cmd := builder.cmdMemberList()
|
||||
return cmd, calls
|
||||
}
|
||||
|
@ -394,7 +395,7 @@ func TestCmdOrg(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
|
||||
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(ioutil.Discard))
|
||||
cmd := builder.cmdMemberAdd()
|
||||
return cmd, calls
|
||||
}
|
||||
|
@ -457,7 +458,7 @@ func TestCmdOrg(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
|
||||
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(ioutil.Discard))
|
||||
cmd := builder.cmdMemberRemove()
|
||||
return cmd, calls
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ func setupF(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to write token to path %q: %v", dPath, err)
|
||||
}
|
||||
|
||||
fmt.Println(promptWithColor("Your token has been stored in "+dPath+".", colorCyan))
|
||||
fmt.Println(string(promptWithColor("Your token has been stored in "+dPath+".", colorCyan)))
|
||||
|
||||
w := internal.NewTabWriter(os.Stdout)
|
||||
w.WriteHeaders(
|
||||
|
@ -144,7 +144,7 @@ func interactive() (req *platform.OnboardingRequest, err error) {
|
|||
Reader: os.Stdin,
|
||||
}
|
||||
req = new(platform.OnboardingRequest)
|
||||
fmt.Println(promptWithColor(`Welcome to InfluxDB 2.0!`, colorYellow))
|
||||
fmt.Println(string(promptWithColor(`Welcome to InfluxDB 2.0!`, colorYellow)))
|
||||
if setupFlags.username != "" {
|
||||
req.User = setupFlags.username
|
||||
} else {
|
||||
|
@ -153,7 +153,7 @@ func interactive() (req *platform.OnboardingRequest, err error) {
|
|||
if setupFlags.password != "" {
|
||||
req.Password = setupFlags.password
|
||||
} else {
|
||||
req.Password = getPassword(ui)
|
||||
req.Password = getPassword(ui, false)
|
||||
}
|
||||
if setupFlags.token != "" {
|
||||
req.Token = setupFlags.token
|
||||
|
@ -200,8 +200,9 @@ var (
|
|||
keyReset = []byte{keyEscape, '[', '0', 'm'}
|
||||
)
|
||||
|
||||
func promptWithColor(s string, color []byte) string {
|
||||
return string(color) + s + string(keyReset)
|
||||
func promptWithColor(s string, color []byte) []byte {
|
||||
bb := append(color, []byte(s)...)
|
||||
return append(bb, keyReset...)
|
||||
}
|
||||
|
||||
func getConfirm(ui *input.UI, or *platform.OnboardingRequest) bool {
|
||||
|
@ -211,14 +212,14 @@ func getConfirm(ui *input.UI, or *platform.OnboardingRequest) bool {
|
|||
if or.RetentionPeriod > 0 {
|
||||
rp = fmt.Sprintf("%d hrs", or.RetentionPeriod)
|
||||
}
|
||||
fmt.Print(promptWithColor(fmt.Sprintf(`
|
||||
ui.Writer.Write(promptWithColor(fmt.Sprintf(`
|
||||
You have entered:
|
||||
Username: %s
|
||||
Organization: %s
|
||||
Bucket: %s
|
||||
Retention Period: %s
|
||||
`, or.User, or.Org, or.Bucket, rp), colorCyan))
|
||||
result, err := ui.Ask(prompt, &input.Options{
|
||||
result, err := ui.Ask(string(prompt), &input.Options{
|
||||
HideOrder: true,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -240,15 +241,20 @@ var (
|
|||
errPasswordIsTooShort = fmt.Errorf("passwords is too short")
|
||||
)
|
||||
|
||||
func getPassword(ui *input.UI) (password string) {
|
||||
func getPassword(ui *input.UI, showNew bool) (password string) {
|
||||
newStr := ""
|
||||
if showNew {
|
||||
newStr = " new"
|
||||
}
|
||||
var err error
|
||||
enterPasswd:
|
||||
query := promptWithColor("Please type your password", colorCyan)
|
||||
query := string(promptWithColor("Please type your"+newStr+" password", colorCyan))
|
||||
for {
|
||||
password, err = ui.Ask(query, &input.Options{
|
||||
Required: true,
|
||||
HideOrder: true,
|
||||
Hide: true,
|
||||
Mask: false,
|
||||
ValidateFunc: func(s string) error {
|
||||
if len(s) < 8 {
|
||||
return errPasswordIsTooShort
|
||||
|
@ -260,7 +266,7 @@ enterPasswd:
|
|||
case input.ErrInterrupted:
|
||||
os.Exit(1)
|
||||
case errPasswordIsTooShort:
|
||||
fmt.Println(promptWithColor("Password too short - minimum length is 8 characters!", colorRed))
|
||||
ui.Writer.Write(promptWithColor("Password too short - minimum length is 8 characters!", colorRed))
|
||||
goto enterPasswd
|
||||
default:
|
||||
if password = strings.TrimSpace(password); password == "" {
|
||||
|
@ -269,7 +275,7 @@ enterPasswd:
|
|||
}
|
||||
break
|
||||
}
|
||||
query = promptWithColor("Please type your password again", colorCyan)
|
||||
query = string(promptWithColor("Please type your"+newStr+" password again", colorCyan))
|
||||
for {
|
||||
_, err = ui.Ask(query, &input.Options{
|
||||
Required: true,
|
||||
|
@ -288,7 +294,7 @@ enterPasswd:
|
|||
case nil:
|
||||
// Nothing.
|
||||
default:
|
||||
fmt.Println(promptWithColor("Passwords do not match!", colorRed))
|
||||
ui.Writer.Write(promptWithColor("Passwords do not match!\n", colorRed))
|
||||
goto enterPasswd
|
||||
}
|
||||
break
|
||||
|
@ -305,7 +311,7 @@ func getInput(ui *input.UI, prompt, defaultValue string) string {
|
|||
option.Default = defaultValue
|
||||
option.HideDefault = true
|
||||
}
|
||||
prompt = promptWithColor(prompt, colorCyan)
|
||||
prompt = string(promptWithColor(prompt, colorCyan))
|
||||
for {
|
||||
line, err := ui.Ask(prompt, option)
|
||||
switch err {
|
||||
|
|
|
@ -3,50 +3,96 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
platform "github.com/influxdata/influxdb"
|
||||
"github.com/influxdata/influxdb"
|
||||
"github.com/influxdata/influxdb/cmd/influx/internal"
|
||||
"github.com/influxdata/influxdb/http"
|
||||
"github.com/spf13/cobra"
|
||||
input "github.com/tcnksm/go-input"
|
||||
)
|
||||
|
||||
func cmdUser() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "user",
|
||||
Short: "User management commands",
|
||||
Run: seeHelp,
|
||||
type userSVCsFn func() (
|
||||
cmdUserDeps,
|
||||
error,
|
||||
)
|
||||
|
||||
type cmdUserDeps struct {
|
||||
userSVC influxdb.UserService
|
||||
orgSvc influxdb.OrganizationService
|
||||
passSVC influxdb.PasswordsService
|
||||
urmSVC influxdb.UserResourceMappingService
|
||||
getPassFn func(*input.UI, bool) string
|
||||
}
|
||||
|
||||
func cmdUser(opts ...genericCLIOptFn) *cobra.Command {
|
||||
return newCmdUserBuilder(newUserSVC, opts...).cmd()
|
||||
}
|
||||
|
||||
type cmdUserBuilder struct {
|
||||
genericCLIOpts
|
||||
|
||||
svcFn userSVCsFn
|
||||
|
||||
id string
|
||||
name string
|
||||
password string
|
||||
org organization
|
||||
}
|
||||
|
||||
func newCmdUserBuilder(svcsFn userSVCsFn, opts ...genericCLIOptFn) *cmdUserBuilder {
|
||||
opt := genericCLIOpts{
|
||||
in: os.Stdin,
|
||||
w: os.Stdout,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return &cmdUserBuilder{
|
||||
genericCLIOpts: opt,
|
||||
svcFn: svcsFn,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *cmdUserBuilder) cmd() *cobra.Command {
|
||||
cmd := b.newCmd("user", nil)
|
||||
cmd.Short = "User management commands"
|
||||
cmd.Run = seeHelp
|
||||
cmd.AddCommand(
|
||||
userCreateCmd(),
|
||||
userDeleteCmd(),
|
||||
userFindCmd(),
|
||||
userUpdateCmd(),
|
||||
b.cmdCreate(),
|
||||
b.cmdDelete(),
|
||||
b.cmdFind(),
|
||||
b.cmdUpdate(),
|
||||
b.cmdPassword(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
var userUpdateFlags struct {
|
||||
id string
|
||||
name string
|
||||
func (b *cmdUserBuilder) cmdPassword() *cobra.Command {
|
||||
cmd := b.newCmd("password", b.cmdPasswordRunEFn)
|
||||
cmd.Short = "Update user password"
|
||||
|
||||
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID")
|
||||
cmd.Flags().StringVarP(&b.name, "name", "n", "", "The user name")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func userUpdateCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Update user",
|
||||
RunE: wrapCheckSetup(userUpdateF),
|
||||
}
|
||||
func (b *cmdUserBuilder) cmdUpdate() *cobra.Command {
|
||||
cmd := b.newCmd("update", b.cmdUpdateRunEFn)
|
||||
cmd.Short = "Update user"
|
||||
|
||||
cmd.Flags().StringVarP(&userUpdateFlags.id, "id", "i", "", "The user ID (required)")
|
||||
cmd.Flags().StringVarP(&userUpdateFlags.name, "name", "n", "", "The user name")
|
||||
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID (required)")
|
||||
cmd.Flags().StringVarP(&b.name, "name", "n", "", "The user name")
|
||||
cmd.MarkFlagRequired("id")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newUserService() (platform.UserService, error) {
|
||||
func newUserService() (influxdb.UserService, error) {
|
||||
if flags.local {
|
||||
return newLocalKVService()
|
||||
}
|
||||
|
@ -60,28 +106,85 @@ func newUserService() (platform.UserService, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func userUpdateF(cmd *cobra.Command, args []string) error {
|
||||
s, err := newUserService()
|
||||
func newUserSVC() (
|
||||
cmdUserDeps,
|
||||
error) {
|
||||
httpClient, err := newHTTPClient()
|
||||
if err != nil {
|
||||
return cmdUserDeps{}, err
|
||||
}
|
||||
userSvc := &http.UserService{Client: httpClient}
|
||||
orgSvc := &http.OrganizationService{Client: httpClient}
|
||||
passSvc := &http.PasswordService{Client: httpClient}
|
||||
urmSvc := &http.UserResourceMappingService{Client: httpClient}
|
||||
getPassFn := getPassword
|
||||
|
||||
return cmdUserDeps{
|
||||
userSVC: userSvc,
|
||||
orgSvc: orgSvc,
|
||||
passSVC: passSvc,
|
||||
urmSVC: urmSvc,
|
||||
getPassFn: getPassFn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *cmdUserBuilder) cmdPasswordRunEFn(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.Background()
|
||||
dep, err := b.svcFn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var id platform.ID
|
||||
if err := id.DecodeFromString(userUpdateFlags.id); err != nil {
|
||||
filter := influxdb.UserFilter{}
|
||||
if b.name != "" {
|
||||
filter.Name = &b.name
|
||||
}
|
||||
if b.id != "" {
|
||||
id, err := influxdb.IDFromString(b.id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
update := platform.UserUpdate{}
|
||||
if userUpdateFlags.name != "" {
|
||||
update.Name = &userUpdateFlags.name
|
||||
filter.ID = id
|
||||
}
|
||||
u, err := dep.userSVC.FindUser(ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ui := &input.UI{
|
||||
Writer: b.genericCLIOpts.w,
|
||||
Reader: b.genericCLIOpts.in,
|
||||
}
|
||||
password := dep.getPassFn(ui, true)
|
||||
|
||||
user, err := s.UpdateUser(context.Background(), id, update)
|
||||
if err = dep.passSVC.SetPassword(ctx, u.ID, password); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(b.w, "Your password has been successfully updated.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *cmdUserBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) error {
|
||||
dep, err := b.svcFn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := internal.NewTabWriter(os.Stdout)
|
||||
var id influxdb.ID
|
||||
if err := id.DecodeFromString(b.id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
update := influxdb.UserUpdate{}
|
||||
if b.name != "" {
|
||||
update.Name = &b.name
|
||||
}
|
||||
|
||||
user, err := dep.userSVC.UpdateUser(context.Background(), id, update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := internal.NewTabWriter(b.w)
|
||||
w.WriteHeaders(
|
||||
"ID",
|
||||
"Name",
|
||||
|
@ -95,42 +198,43 @@ func userUpdateF(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var userCreateFlags struct {
|
||||
name string
|
||||
password string
|
||||
org organization
|
||||
}
|
||||
func (b *cmdUserBuilder) cmdCreate() *cobra.Command {
|
||||
cmd := b.newCmd("create", b.cmdCreateRunEFn)
|
||||
cmd.Short = "Create user"
|
||||
|
||||
func userCreateCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create user",
|
||||
RunE: wrapCheckSetup(userCreateF),
|
||||
opts := flagOpts{
|
||||
{
|
||||
DestP: &b.name,
|
||||
Flag: "name",
|
||||
Short: 'n',
|
||||
Desc: "The user name (required)",
|
||||
Required: true,
|
||||
},
|
||||
}
|
||||
opts.mustRegister(cmd)
|
||||
|
||||
userCreateFlags.org.register(cmd, false)
|
||||
cmd.Flags().StringVarP(&userCreateFlags.name, "name", "n", "", "The user name (required)")
|
||||
cmd.MarkFlagRequired("name")
|
||||
cmd.Flags().StringVarP(&userCreateFlags.password, "password", "p", "", "The user password")
|
||||
cmd.Flags().StringVarP(&b.password, "password", "p", "", "The user password")
|
||||
b.org.register(cmd, false)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func userCreateF(cmd *cobra.Command, args []string) error {
|
||||
if err := userCreateFlags.org.validOrgFlags(); err != nil {
|
||||
func (b *cmdUserBuilder) cmdCreateRunEFn(*cobra.Command, []string) error {
|
||||
ctx := context.Background()
|
||||
if err := b.org.validOrgFlags(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s, err := newUserService()
|
||||
dep, err := b.svcFn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := &platform.User{
|
||||
Name: userCreateFlags.name,
|
||||
user := &influxdb.User{
|
||||
Name: b.name,
|
||||
}
|
||||
|
||||
if err := s.CreateUser(context.Background(), user); err != nil {
|
||||
if err := dep.userSVC.CreateUser(ctx, user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -143,7 +247,7 @@ func userCreateF(cmd *cobra.Command, args []string) error {
|
|||
for i, h := range headers {
|
||||
m[h] = vals[i]
|
||||
}
|
||||
w := internal.NewTabWriter(os.Stdout)
|
||||
w := internal.NewTabWriter(b.w)
|
||||
w.WriteHeaders(headers...)
|
||||
w.Write(m)
|
||||
w.Flush()
|
||||
|
@ -151,17 +255,12 @@ func userCreateF(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
orgSVC, err := newOrganizationService()
|
||||
orgID, err := b.org.getID(dep.orgSvc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orgID, err := userCreateFlags.org.getID(orgSVC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pass := userCreateFlags.password
|
||||
pass := b.password
|
||||
if orgID == 0 && pass == "" {
|
||||
return writeOutput([]string{"ID", "Name"}, user.ID.String(), user.Name)
|
||||
}
|
||||
|
@ -170,77 +269,57 @@ func userCreateF(cmd *cobra.Command, args []string) error {
|
|||
return errors.New("an org id is required when providing a user password")
|
||||
}
|
||||
|
||||
c, err := newHTTPClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userResMapSVC := &http.UserResourceMappingService{
|
||||
Client: c,
|
||||
}
|
||||
|
||||
err = userResMapSVC.CreateUserResourceMapping(context.Background(), &platform.UserResourceMapping{
|
||||
err = dep.urmSVC.CreateUserResourceMapping(context.Background(), &influxdb.UserResourceMapping{
|
||||
UserID: user.ID,
|
||||
UserType: platform.Member,
|
||||
ResourceType: platform.OrgsResourceType,
|
||||
UserType: influxdb.Member,
|
||||
ResourceType: influxdb.OrgsResourceType,
|
||||
ResourceID: orgID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passSVC := &http.PasswordService{Client: c}
|
||||
|
||||
ctx := context.Background()
|
||||
if err := passSVC.SetPassword(ctx, user.ID, pass); err != nil {
|
||||
if err := dep.passSVC.SetPassword(ctx, user.ID, pass); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeOutput([]string{"ID", "Name", "Organization ID"}, user.ID.String(), user.Name, orgID.String())
|
||||
}
|
||||
|
||||
var userFindFlags struct {
|
||||
id string
|
||||
name string
|
||||
}
|
||||
func (b *cmdUserBuilder) cmdFind() *cobra.Command {
|
||||
cmd := b.newCmd("find", b.cmdFindRunEFn)
|
||||
cmd.Short = "Find user"
|
||||
|
||||
func userFindCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "find",
|
||||
Short: "Find user",
|
||||
RunE: wrapCheckSetup(userFindF),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&userFindFlags.id, "id", "i", "", "The user ID")
|
||||
cmd.Flags().StringVarP(&userFindFlags.name, "name", "n", "", "The user name")
|
||||
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID")
|
||||
cmd.Flags().StringVarP(&b.name, "name", "n", "", "The user name")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func userFindF(cmd *cobra.Command, args []string) error {
|
||||
s, err := newUserService()
|
||||
func (b *cmdUserBuilder) cmdFindRunEFn(*cobra.Command, []string) error {
|
||||
dep, err := b.svcFn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filter := platform.UserFilter{}
|
||||
if userFindFlags.name != "" {
|
||||
filter.Name = &userFindFlags.name
|
||||
filter := influxdb.UserFilter{}
|
||||
if b.name != "" {
|
||||
filter.Name = &b.name
|
||||
}
|
||||
if userFindFlags.id != "" {
|
||||
id, err := platform.IDFromString(userFindFlags.id)
|
||||
if b.id != "" {
|
||||
id, err := influxdb.IDFromString(b.id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter.ID = id
|
||||
}
|
||||
|
||||
users, _, err := s.FindUsers(context.Background(), filter)
|
||||
users, _, err := dep.userSVC.FindUsers(context.Background(), filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := internal.NewTabWriter(os.Stdout)
|
||||
w := internal.NewTabWriter(b.w)
|
||||
w.WriteHeaders(
|
||||
"ID",
|
||||
"Name",
|
||||
|
@ -256,45 +335,38 @@ func userFindF(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var userDeleteFlags struct {
|
||||
id string
|
||||
}
|
||||
func (b *cmdUserBuilder) cmdDelete() *cobra.Command {
|
||||
cmd := b.newCmd("delete", b.cmdDeleteRunEFn)
|
||||
cmd.Short = "Delete user"
|
||||
|
||||
func userDeleteCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete user",
|
||||
RunE: wrapCheckSetup(userDeleteF),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&userDeleteFlags.id, "id", "i", "", "The user ID (required)")
|
||||
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID (required)")
|
||||
cmd.MarkFlagRequired("id")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func userDeleteF(cmd *cobra.Command, args []string) error {
|
||||
s, err := newUserService()
|
||||
func (b *cmdUserBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) error {
|
||||
dep, err := b.svcFn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var id platform.ID
|
||||
if err := id.DecodeFromString(userDeleteFlags.id); err != nil {
|
||||
var id influxdb.ID
|
||||
if err := id.DecodeFromString(b.id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
u, err := s.FindUserByID(ctx, id)
|
||||
u, err := dep.userSVC.FindUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.DeleteUser(ctx, id); err != nil {
|
||||
if err := dep.userSVC.DeleteUser(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := internal.NewTabWriter(os.Stdout)
|
||||
w := internal.NewTabWriter(b.w)
|
||||
w.WriteHeaders(
|
||||
"ID",
|
||||
"Name",
|
||||
|
|
|
@ -0,0 +1,399 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/influxdb"
|
||||
platform "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 newCMDUserDeps(
|
||||
userSVC influxdb.UserService,
|
||||
passSVC influxdb.PasswordsService,
|
||||
getPassFn func(*input.UI, bool) string,
|
||||
) cmdUserDeps {
|
||||
return cmdUserDeps{
|
||||
userSVC: userSVC,
|
||||
orgSvc: &mock.OrganizationService{
|
||||
FindOrganizationF: func(ctx context.Context, filter influxdb.OrganizationFilter) (*influxdb.Organization, error) {
|
||||
return &influxdb.Organization{ID: influxdb.ID(9000), Name: "influxdata"}, nil
|
||||
},
|
||||
},
|
||||
passSVC: passSVC,
|
||||
urmSVC: &mock.UserResourceMappingService{
|
||||
CreateMappingFn: func(context.Context, *platform.UserResourceMapping) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
getPassFn: getPassFn,
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmdUser(t *testing.T) {
|
||||
setViperOptions()
|
||||
|
||||
type userResult struct {
|
||||
user influxdb.User
|
||||
password string
|
||||
}
|
||||
|
||||
fakeSVCFn := func(dep cmdUserDeps) userSVCsFn {
|
||||
return func() (
|
||||
cmdUserDeps,
|
||||
error) {
|
||||
return dep, nil
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("create", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expected userResult
|
||||
flags []string
|
||||
envVars map[string]string
|
||||
}{
|
||||
{
|
||||
name: "basic just name",
|
||||
flags: []string{"--name=new name", "--org=org name"},
|
||||
expected: userResult{
|
||||
user: influxdb.User{
|
||||
Name: "new name",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with password",
|
||||
flags: []string{
|
||||
"--name=new name",
|
||||
"--password=pass1",
|
||||
"--org=org name",
|
||||
},
|
||||
expected: userResult{
|
||||
user: influxdb.User{
|
||||
Name: "new name",
|
||||
},
|
||||
password: "pass1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with password and env",
|
||||
flags: []string{
|
||||
"--name=new name",
|
||||
"--password=pass1",
|
||||
},
|
||||
envVars: map[string]string{
|
||||
"INFLUX_ORG_ID": influxdb.ID(1).String(),
|
||||
},
|
||||
expected: userResult{
|
||||
user: influxdb.User{
|
||||
Name: "new name",
|
||||
},
|
||||
password: "pass1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "shorts",
|
||||
flags: []string{
|
||||
"-n=new name",
|
||||
"-o=org name",
|
||||
},
|
||||
expected: userResult{
|
||||
user: influxdb.User{
|
||||
Name: "new name",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cmdFn := func(expected userResult) *cobra.Command {
|
||||
svc := mock.NewUserService()
|
||||
svc.CreateUserFn = func(ctx context.Context, User *influxdb.User) error {
|
||||
if expected.user != *User {
|
||||
return fmt.Errorf("unexpected User;\n\twant= %+v\n\tgot= %+v", expected, *User)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
passSVC := mock.NewPasswordsService()
|
||||
passSVC.SetPasswordFn = func(ctx context.Context, id influxdb.ID, password string) error {
|
||||
if expected.password != password {
|
||||
return fmt.Errorf("unexpected password;\n\twant= %+v\n\tgot= %+v", expected.password, password)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, passSVC, nil)), out(ioutil.Discard))
|
||||
cmd := builder.cmdCreate()
|
||||
cmd.RunE = builder.cmdCreateRunEFn
|
||||
return cmd
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
fn := func(t *testing.T) {
|
||||
defer addEnvVars(t, tt.envVars)()
|
||||
cmd := cmdFn(tt.expected)
|
||||
cmd.LocalFlags().Parse(tt.flags)
|
||||
err := cmd.Execute()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
t.Run(tt.name, fn)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("delete", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expectedID influxdb.ID
|
||||
flag string
|
||||
}{
|
||||
{
|
||||
name: "long id",
|
||||
expectedID: influxdb.ID(1),
|
||||
flag: "--id=",
|
||||
},
|
||||
{
|
||||
name: "shorts",
|
||||
expectedID: influxdb.ID(1),
|
||||
flag: "-i=",
|
||||
},
|
||||
}
|
||||
|
||||
cmdFn := func(expectedID influxdb.ID) *cobra.Command {
|
||||
svc := mock.NewUserService()
|
||||
svc.FindUserByIDFn = func(ctx context.Context, id influxdb.ID) (*influxdb.User, error) {
|
||||
return &influxdb.User{ID: id}, nil
|
||||
}
|
||||
svc.DeleteUserFn = func(ctx context.Context, id influxdb.ID) error {
|
||||
if expectedID != id {
|
||||
return fmt.Errorf("unexpected id:\n\twant= %s\n\tgot= %s", expectedID, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, nil, 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.expectedID)
|
||||
idFlag := tt.flag + tt.expectedID.String()
|
||||
cmd.LocalFlags().Parse([]string{idFlag})
|
||||
require.NoError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
t.Run(tt.name, fn)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("find", func(t *testing.T) {
|
||||
type called struct {
|
||||
name string
|
||||
id influxdb.ID
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expected called
|
||||
flags []string
|
||||
}{
|
||||
{
|
||||
name: "id",
|
||||
flags: []string{
|
||||
"--id=" + influxdb.ID(2).String(),
|
||||
},
|
||||
expected: called{
|
||||
id: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "name",
|
||||
flags: []string{"--name=name1"},
|
||||
expected: called{name: "name1"},
|
||||
},
|
||||
{
|
||||
name: "shorts",
|
||||
flags: []string{
|
||||
"-n=name1",
|
||||
"-i=" + influxdb.ID(1).String(),
|
||||
},
|
||||
expected: called{name: "name1", id: 1},
|
||||
},
|
||||
}
|
||||
|
||||
cmdFn := func() (*cobra.Command, *called) {
|
||||
calls := new(called)
|
||||
|
||||
svc := mock.NewUserService()
|
||||
svc.FindUsersFn = func(ctx context.Context, f influxdb.UserFilter, opt ...influxdb.FindOptions) ([]*influxdb.User, int, error) {
|
||||
if f.ID != nil {
|
||||
calls.id = *f.ID
|
||||
}
|
||||
if f.Name != nil {
|
||||
calls.name = *f.Name
|
||||
}
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, nil, 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) {
|
||||
cmd, calls := cmdFn()
|
||||
cmd.LocalFlags().Parse(tt.flags)
|
||||
|
||||
require.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, tt.expected, *calls)
|
||||
}
|
||||
|
||||
t.Run(tt.name, fn)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("update", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expected influxdb.UserUpdate
|
||||
flags []string
|
||||
}{
|
||||
{
|
||||
name: "basic just name",
|
||||
flags: []string{
|
||||
"--id=" + influxdb.ID(3).String(),
|
||||
"--name=new name",
|
||||
},
|
||||
expected: influxdb.UserUpdate{
|
||||
Name: strPtr("new name"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with all fields",
|
||||
flags: []string{
|
||||
"--id=" + influxdb.ID(3).String(),
|
||||
"--name=new name",
|
||||
},
|
||||
expected: influxdb.UserUpdate{
|
||||
Name: strPtr("new name"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "shorts",
|
||||
flags: []string{
|
||||
"-i=" + influxdb.ID(3).String(),
|
||||
"-n=new name",
|
||||
},
|
||||
expected: influxdb.UserUpdate{
|
||||
Name: strPtr("new name"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cmdFn := func(expectedUpdate influxdb.UserUpdate) *cobra.Command {
|
||||
svc := mock.NewUserService()
|
||||
svc.UpdateUserFn = func(ctx context.Context, id influxdb.ID, upd influxdb.UserUpdate) (*influxdb.User, error) {
|
||||
if id != 3 {
|
||||
return nil, fmt.Errorf("unexpecte id:\n\twant= %s\n\tgot= %s", influxdb.ID(3), id)
|
||||
}
|
||||
if !reflect.DeepEqual(expectedUpdate, upd) {
|
||||
return nil, fmt.Errorf("unexpected User update;\n\twant= %+v\n\tgot= %+v", expectedUpdate, upd)
|
||||
}
|
||||
return &influxdb.User{}, nil
|
||||
}
|
||||
|
||||
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, nil, nil)), out(ioutil.Discard))
|
||||
cmd := builder.cmdUpdate()
|
||||
cmd.RunE = builder.cmdUpdateRunEFn
|
||||
return cmd
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
fn := func(t *testing.T) {
|
||||
cmd := cmdFn(tt.expected)
|
||||
cmd.LocalFlags().Parse(tt.flags)
|
||||
require.NoError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
t.Run(tt.name, fn)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("password", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expected string
|
||||
flags []string
|
||||
}{
|
||||
{
|
||||
name: "basic id",
|
||||
flags: []string{
|
||||
"--id=" + influxdb.ID(3).String(),
|
||||
},
|
||||
expected: "pass1",
|
||||
},
|
||||
{
|
||||
name: "shorts",
|
||||
flags: []string{
|
||||
"-i=" + influxdb.ID(3).String(),
|
||||
"-n=new name",
|
||||
},
|
||||
expected: "pass1",
|
||||
},
|
||||
}
|
||||
|
||||
cmdFn := func(expected string) *cobra.Command {
|
||||
svc := mock.NewUserService()
|
||||
svc.FindUserFn = func(ctx context.Context, f influxdb.UserFilter) (*influxdb.User, error) {
|
||||
usr := new(influxdb.User)
|
||||
if id := f.ID; id != nil {
|
||||
usr.ID = *id
|
||||
}
|
||||
if name := f.Name; name != nil {
|
||||
usr.Name = *name
|
||||
}
|
||||
return usr, nil
|
||||
}
|
||||
passSVC := mock.NewPasswordsService()
|
||||
passSVC.SetPasswordFn = func(ctx context.Context, id influxdb.ID, pass string) error {
|
||||
if pass != expected {
|
||||
return fmt.Errorf("unexpecte id:\n\twant= %s\n\tgot= %s", pass, expected)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
getPassFn := func(*input.UI, bool) string {
|
||||
return expected
|
||||
}
|
||||
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, passSVC, getPassFn)),
|
||||
out(ioutil.Discard))
|
||||
cmd := builder.cmdPassword()
|
||||
cmd.RunE = builder.cmdPasswordRunEFn
|
||||
return cmd
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
fn := func(t *testing.T) {
|
||||
cmd := cmdFn(tt.expected)
|
||||
cmd.LocalFlags().Parse(tt.flags)
|
||||
require.NoError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
t.Run(tt.name, fn)
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue