Skip to content
This repository has been archived by the owner on Jul 24, 2021. It is now read-only.

Commit

Permalink
restore admin command tree
Browse files Browse the repository at this point in the history
This restores the admin command tree. Once again you're able to:

* list all users
* view a specific user
* add/remove users
* update a specific user's details
* list a specific user's tokens
* remove a specific user's tokens
  • Loading branch information
perigrin committed Nov 19, 2020
1 parent 4144095 commit b1fd227
Show file tree
Hide file tree
Showing 20 changed files with 425 additions and 261 deletions.
157 changes: 157 additions & 0 deletions cli/admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package cli

import (
cli "github.com/jawher/mow.cli"
"github.com/joyent/kosh/conch"
"github.com/joyent/kosh/conch/types"
)

func adminCmd(cmd *cli.Cmd) {
cmd.Before = func() {
config.requireAuth()
config.requireSysAdmin()
}
cmd.Command("users", "List all Users", adminUsersCmd)
cmd.Command("user u", "Administrate a single User", adminUserCmd)
}

func adminUsersCmd(cmd *cli.Cmd) {
var conch *conch.Client
var display Renderer

cmd.Before = func() {
conch = config.ConchClient()
display = config.Renderer()
}

cmd.Action = func() { display(conch.GetAllUsers()) }

cmd.Command("get ls", "display all users", func(cmd *cli.Cmd) {
cmd.Action = func() { display(conch.GetAllUsers()) }
})

cmd.Command("create new add", "Add a new user to the system", func(cmd *cli.Cmd) {
email := cmd.StringArg("EMAIL", "", "A user's email")
name := cmd.StringArg("NAME", "", "A user's name")
password := cmd.StringArg("PASS", "", "A user's initial password")
admin := cmd.BoolOpt("admin", false, "make user a system admin")
notify := cmd.BoolOpt("send-email", false, "notify the user via email")

cmd.Spec = "EMAIL NAME PASS"

cmd.Action = func() {
display(conch.CreateUser(types.NewUser{
Email: types.EmailAddress(*email),
IsAdmin: *admin,
Name: types.NonEmptyString(*name),
Password: types.NonEmptyString(*password),
}, *notify))
}
})

cmd.Command("import", "Import a new user from the JSON output", func(cmd *cli.Cmd) {
notify := cmd.BoolOpt("send-email", false, "notify the user via email")
filePathArg := cmd.StringArg("FILE", "-", "Path to a JSON file that defines the user. '-' indicates STDIN")

cmd.Action = func() {
input, e := getInputReader(*filePathArg)
fatalIf(e)

u := conch.ReadUser(input)
display(
conch.CreateUser(types.NewUser{
Email: u.Email,
IsAdmin: u.IsAdmin,
Name: u.Name,
}, *notify),
)
}
})
}

func adminUserCmd(cmd *cli.Cmd) {
var conch *conch.Client
var display Renderer
var user types.UserDetailed

email := cmd.StringArg("EMAIL", "", "A user's email")
// cmd.Spec = "EMAIL"

cmd.Before = func() {
conch = config.ConchClient()
display = config.Renderer()

var e error
user, e = conch.GetUserByEmail(*email)
fatalIf(e)
// TODO check to see if user is empty
}

cmd.Action = func() { display(user, nil) }

cmd.Command("get", "display all users", func(cmd *cli.Cmd) {
cmd.Action = func() { display(user, nil) }
})

cmd.Command("update", "update the information for a user", func(cmd *cli.Cmd) {
email := cmd.StringArg("EMAIL", "", "A user's email")
name := cmd.StringArg("NAME", "", "A user's name")
admin := cmd.BoolOpt("admin", false, "make user a system admin")
notify := cmd.BoolOpt("send-email", false, "notify the user via email")

cmd.Spec = "EMAIL NAME"
cmd.Action = func() {
update := types.UpdateUser{}
if *email != "" && types.EmailAddress(*email) != user.Email {
update.Email = types.EmailAddress(*email)
}
if *name != "" && types.NonEmptyString(*name) != user.Name {
update.Name = types.NonEmptyString(*name)
}
if *admin != user.IsAdmin {
user.IsAdmin = *admin
}
conch.UpdateUser(string(user.Email), update, *notify)
}
})

cmd.Command("delete rm", "remove the specified user", func(cmd *cli.Cmd) {
cmd.Action = func() {
conch.DeleteUser(string(user.Email))
display(conch.GetAllUsers())
}
})

cmd.Command("tokens", "operate on the user's tokens", func(cmd *cli.Cmd) {
cmd.Action = func() { display(conch.GetUserTokens(string(user.Email))) }

cmd.Command("get ls", "list the tokens for the current user", func(cmd *cli.Cmd) {
cmd.Action = func() { display(conch.GetUserTokens(string(user.Email))) }
})
})

cmd.Command("token", "operate on a user's tokens", func(cmd *cli.Cmd) {
var token types.UserToken

name := cmd.StringArg("NAME", "", "The string name of a setting")
cmd.Spec = "NAME"
cmd.Before = func() {
var e error
token, e = conch.GetUserTokenByName(string(user.Email), *name)
fatalIf(e)
}

cmd.Action = func() { display(token, nil) }

cmd.Command("get", "information about a single token for the given user", func(cmd *cli.Cmd) {
cmd.Action = func() { display(token, nil) }
})

cmd.Command("delete rm", "remove a token for the given user", func(cmd *cli.Cmd) {
cmd.Action = func() {
conch.DeleteUserToken(string(user.Email), token.Name)
display(conch.GetUserTokens(string(user.Email)))
}
})
})
}
64 changes: 27 additions & 37 deletions cli/builds.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@ func buildsCmd(cmd *cli.Cmd) {
var conch *conch.Client
var display func(interface{}, error)

cmd.Before = config.Before(
requireAuth,
func(config Config) {
conch = config.ConchClient()
display = config.Renderer()
},
)
cmd.Before = func() {
config.requireAuth()
conch = config.ConchClient()
display = config.Renderer()
}

var startedSetByUser bool
var completedSetByUser bool
Expand Down Expand Up @@ -112,19 +110,16 @@ func buildCmd(cmd *cli.Cmd) {
buildNameArg := cmd.StringArg("NAME", "", "Name or ID of the build")
cmd.Spec = "NAME"

cmd.Before = config.Before(
requireAuth,
func(config Config) {
conch = config.ConchClient()
display = config.Renderer()
cmd.Before = func() {
config.requireAuth()

var e error
build, e = conch.GetBuildByName(*buildNameArg)
if e != nil {
fatal(e)
}
},
)
conch = config.ConchClient()
display = config.Renderer()

var e error
build, e = conch.GetBuildByName(*buildNameArg)
fatalIf(e)
}

cmd.Action = func() { display(build, nil) }

Expand All @@ -134,11 +129,10 @@ func buildCmd(cmd *cli.Cmd) {

cmd.Command("start", "Mark the build as started", func(cmd *cli.Cmd) {
cmd.Action = func() {
if e := conch.UpdateBuildByID(build.ID, types.BuildUpdate{
e := conch.UpdateBuildByID(build.ID, types.BuildUpdate{
Started: time.Now(),
}); e != nil {
fatal(e)
}
})
fatalIf(e)
display(conch.GetBuildByID(build.ID))
}
})
Expand All @@ -147,9 +141,9 @@ func buildCmd(cmd *cli.Cmd) {
cmd.Action = func() {
update := types.BuildUpdate{Completed: time.Now()}

if e := conch.UpdateBuildByID(build.ID, update); e != nil {
fatal(e)
}
e := conch.UpdateBuildByID(build.ID, update)
fatalIf(e)

display(conch.GetBuildByID(build.ID))
}
})
Expand Down Expand Up @@ -186,7 +180,7 @@ func buildCmd(cmd *cli.Cmd) {
cmd.Spec = "EMAIL [OPTIONS]"
cmd.Action = func() {
if !okBuildRole(*roleOpt) {
fatal(fmt.Errorf(
fatalIf(fmt.Errorf(
"'role' value must be one of: %s",
prettyBuildRoleList(),
))
Expand Down Expand Up @@ -252,15 +246,13 @@ func buildCmd(cmd *cli.Cmd) {
cmd.Spec = "NAME [OPTIONS]"
cmd.Action = func() {
if !okBuildRole(*roleOpt) {
fatal(fmt.Errorf(
fatalIf(fmt.Errorf(
"'role' value must be one of: %s",
prettyBuildRoleList(),
))
}
org, e := conch.GetOrganizationByName(*orgNameArg)
if e != nil {
fatal(e)
}
fatalIf(e)

conch.AddBuildOrganization(*buildNameArg, types.BuildAddOrganization{
org.ID,
Expand Down Expand Up @@ -327,13 +319,11 @@ func buildCmd(cmd *cli.Cmd) {
cmd.Spec = "ID [OPTIONS]"
cmd.Action = func() {
b, e := conch.GetBuildByName(*buildNameArg)
if e != nil {
fatal(e)
}
fatalIf(e)

d, e := conch.GetDeviceBySerial(*deviceIDArg)
if e != nil {
fatal(e)
}
fatalIf(e)

conch.DeleteBuildDeviceByID(b.ID, d.ID)
display(conch.GetAllBuildDevices(*buildNameArg))
}
Expand Down
27 changes: 16 additions & 11 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ const (
stagingURL = "https://staging.conch.joyent.us"
)

func fatal(e error) {
fmt.Println(e)
cli.Exit(1)
func fatalIf(e error) {
if e != nil {
fmt.Println(e)
cli.Exit(1)
}
}

func getInputReader(filePathArg string) (io.Reader, error) {
Expand All @@ -26,24 +28,26 @@ func getInputReader(filePathArg string) (io.Reader, error) {
return os.Open(filePathArg)
}

func requireAuth(c Config) {
var config Config

func (c Config) requireAuth() {
if c.ConchToken == "" {
fmt.Println("Need to provide --token or set KOSH_TOKEN")
cli.Exit(1)
}
}

func requireSysAdmin(c Config) {
func (c Config) requireSysAdmin() {
if !c.ConchClient().IsSysAdmin() {
fmt.Println("This action requires Conch systems administrator privileges")
cli.Exit(1)
}
}

var config Config

// NewApp creates a new kosh app, takes a cli.Config and returns an instance of cli.Cli
func NewApp(config Config) *cli.Cli {
func NewApp(c Config) *cli.Cli {
config = c

app := cli.App("kosh", "Command line interface for Conch")
app.Spec = "[-dejutvV]"

Expand Down Expand Up @@ -80,17 +84,18 @@ func NewApp(config Config) *cli.Cli {
app.BoolPtr(&config.Logger.LevelDebug, cli.BoolOpt{
Name: "d debug",
Value: false,
Desc: "Enable Debugging output (for debugging purposes *very* noisy). ",
Desc: "Enable Debugging output (*very* noisy). ",
EnvVar: "KOSH_DEBUG_MODE KOSH_DEBUG", // TODO in 4.0 remove KOSH_DEBUG_MODE
})

app.BoolPtr(&config.Logger.LevelInfo, cli.BoolOpt{
Name: "v verbose",
Value: false,
Desc: "Enable Verbose Output",
Desc: "Enable Verbose output",
EnvVar: "KOSH_VERBOSE_MODE KOSH_VERBOSE", // TODO in 4.0 remove KOSH_VERBOSE_MODE
})

app.Command("admin", "System Administration Commands", adminCmd)
app.Command("build b", "Work with a specific build", buildCmd)
app.Command("builds bs", "Work with builds", buildsCmd)
app.Command("datacenter dc", "Deal with a single datacenter", datacenterCmd)
Expand Down Expand Up @@ -135,7 +140,7 @@ func NewApp(config Config) *cli.Cli {
case "staging":
config.ConchURL = stagingURL
default:
fatal(errors.New("environment not one of production, staging, edge: perhaps you want --url?"))
fatalIf(errors.New("environment not one of production, staging, edge: perhaps you want --url?"))
}
}

Expand Down
19 changes: 3 additions & 16 deletions cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,9 @@ func (c Config) Renderer() Renderer {
return c.RenderTo(os.Stdout)
}

// Before takes a list of checks and initializers and returns a function
// suitable for running in a commmand's before block
func (c Config) Before(checks ...func(c Config)) func() {
return func() {
for _, check := range checks {
check(config)
}
}
}

func renderJSON(i interface{}) string {
b, e := json.Marshal(i)
if e != nil {
fatal(e)
}
fatalIf(e)
return string(b)
}

Expand All @@ -124,9 +112,8 @@ func (c Config) RenderTo(w io.Writer) func(interface{}, error) {
switch t := i.(type) {
case template.Templated:
s, e := template.Render(t)
if e != nil {
fatal(e)
}
fatalIf(e)

fmt.Fprintln(w, s)
case tables.Tabulable:
fmt.Fprintln(w, tables.Render(t))
Expand Down
Loading

0 comments on commit b1fd227

Please sign in to comment.