-
Notifications
You must be signed in to change notification settings - Fork 323
/
cli.go
135 lines (124 loc) · 3.85 KB
/
cli.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing
// permissions and limitations under the License.
// Package cli represents the entry point of the ssm agent cli.
package cli
import (
"errors"
"fmt"
"io"
"strings"
"github.com/aws/amazon-ssm-agent/agent/cli/cliutil"
"github.com/twinj/uuid"
)
// TODO:MF: make errors more like ssm-cli: error: <arg type>: <error>?
// RunCommand parses and executes a single command line, please refer the aws cli exit code
// https://docs.aws.amazon.com/cli/latest/topic/return-codes.html
func RunCommand(args []string, out io.Writer) (exitCode int) {
uuid.SwitchFormat(uuid.CleanHyphen)
if len(args) < 2 {
displayUsage(out)
// Customer doesn't provide enough arguments
return cliutil.CLI_PARSE_FAIL_EXITCODE
}
err, _, command, subcommands, parameters := parseCommand(args)
if err != nil {
displayUsage(out)
fmt.Fprintln(out, "\nerror: "+err.Error())
// Exit with 2 if parseCommand error occurs
return cliutil.CLI_PARSE_FAIL_EXITCODE
}
if cmd, exists := cliutil.CliCommands[command]; exists {
if cliutil.IsHelp(subcommands, parameters) {
fmt.Fprint(out, cmd.Help())
} else {
cmdErr, result := cmd.Execute(subcommands, parameters)
if cmdErr != nil {
displayUsage(out)
fmt.Fprintln(out, "\nerror: "+cmdErr.Error())
// Exit 255 if command failed
return cliutil.CLI_COMMAND_FAIL_EXITCODE
} else {
fmt.Fprintln(out, result)
}
}
} else if command == cliutil.HelpFlag {
displayHelp(out)
} else {
displayUsage(out)
fmt.Fprintf(out, "\nInvalid command %v. The following commands are supported:\n\n", command)
displayValidCommands(out)
// Customer input command is invalid
return cliutil.CLI_PARSE_FAIL_EXITCODE
}
return cliutil.CLI_SUCCESS_EXITCODE
}
// parseCommand turns the command line arguments into a command name and a map of flag names and values
// args format should be ssm-cli [options] <command> <subcommand> [<subcommand> ...] [parameters]
func parseCommand(args []string) (err error, options []string, command string, subcommands []string, parameters map[string][]string) {
// TODO:MF: aws cli is case-sensitive on things other than parameter value, I propose we be case-insensitive on all non-values
argCount := len(args)
pos := 1
// Options
options = make([]string, 0)
for _, val := range args[pos:] {
if !cliutil.IsFlag(val) {
break
}
options = append(options, cliutil.GetFlag(val))
pos++
}
// Command
if pos >= argCount {
err = errors.New("command is required")
return
}
command = strings.ToLower(args[pos])
pos++
// Subcommands
if pos >= argCount {
return
}
subcommands = make([]string, 0)
for _, val := range args[pos:] {
if cliutil.IsFlag(val) {
break
}
subcommands = append(options, strings.ToLower(val))
pos++
}
// Parameters
if pos >= argCount {
return
}
parameters = make(map[string][]string)
var parameterName string
for _, val := range args[2:] {
if cliutil.IsFlag(val) {
parameterName = cliutil.GetFlag(val)
if parameterName == "" {
// aws cli doesn't valid this
err = fmt.Errorf("input contains parameter with no name")
return
}
if _, exists := parameters[parameterName]; exists {
// aws cli doesn't valid this
err = fmt.Errorf("duplicate parameter %v", parameterName)
return
}
parameters[parameterName] = make([]string, 0)
} else {
parameters[parameterName] = append(parameters[parameterName], val)
}
}
return
}