github.com/hernad/nomad@v1.6.112/command/acl_role_update.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package command 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/hernad/nomad/api" 11 "github.com/mitchellh/cli" 12 "github.com/posener/complete" 13 ) 14 15 // Ensure ACLRoleUpdateCommand satisfies the cli.Command interface. 16 var _ cli.Command = &ACLRoleUpdateCommand{} 17 18 // ACLRoleUpdateCommand implements cli.Command. 19 type ACLRoleUpdateCommand struct { 20 Meta 21 22 name string 23 description string 24 policyNames []string 25 noMerge bool 26 json bool 27 tmpl string 28 } 29 30 // Help satisfies the cli.Command Help function. 31 func (a *ACLRoleUpdateCommand) Help() string { 32 helpText := ` 33 Usage: nomad acl role update [options] <acl_role_id> 34 35 Update is used to update an existing ACL token. Requires a management token. 36 37 General Options: 38 39 ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + ` 40 41 Update Options: 42 43 -name 44 Sets the human readable name for the ACL role. The name must be between 45 1-128 characters. 46 47 -description 48 A free form text description of the role that must not exceed 256 49 characters. 50 51 -policy 52 Specifies a policy to associate with the role identified by their name. This 53 flag can be specified multiple times. 54 55 -no-merge 56 Do not merge the current role information with what is provided to the 57 command. Instead overwrite all fields with the exception of the role ID 58 which is immutable. 59 60 -json 61 Output the ACL role in a JSON format. 62 63 -t 64 Format and display the ACL role using a Go template. 65 ` 66 67 return strings.TrimSpace(helpText) 68 } 69 70 func (a *ACLRoleUpdateCommand) AutocompleteFlags() complete.Flags { 71 return mergeAutocompleteFlags(a.Meta.AutocompleteFlags(FlagSetClient), 72 complete.Flags{ 73 "-name": complete.PredictAnything, 74 "-description": complete.PredictAnything, 75 "-no-merge": complete.PredictNothing, 76 "-policy": complete.PredictAnything, 77 "-json": complete.PredictNothing, 78 "-t": complete.PredictAnything, 79 }) 80 } 81 82 func (a *ACLRoleUpdateCommand) AutocompleteArgs() complete.Predictor { return complete.PredictNothing } 83 84 // Synopsis satisfies the cli.Command Synopsis function. 85 func (a *ACLRoleUpdateCommand) Synopsis() string { return "Update an existing ACL role" } 86 87 // Name returns the name of this command. 88 func (*ACLRoleUpdateCommand) Name() string { return "acl role update" } 89 90 // Run satisfies the cli.Command Run function. 91 func (a *ACLRoleUpdateCommand) Run(args []string) int { 92 93 flags := a.Meta.FlagSet(a.Name(), FlagSetClient) 94 flags.Usage = func() { a.Ui.Output(a.Help()) } 95 flags.StringVar(&a.name, "name", "", "") 96 flags.StringVar(&a.description, "description", "", "") 97 flags.Var((funcVar)(func(s string) error { 98 a.policyNames = append(a.policyNames, s) 99 return nil 100 }), "policy", "") 101 flags.BoolVar(&a.noMerge, "no-merge", false, "") 102 flags.BoolVar(&a.json, "json", false, "") 103 flags.StringVar(&a.tmpl, "t", "", "") 104 if err := flags.Parse(args); err != nil { 105 return 1 106 } 107 108 // Check that we got exactly one argument which is expected to be the ACL 109 // role ID. 110 if len(flags.Args()) != 1 { 111 a.Ui.Error("This command takes one argument: <acl_role_id>") 112 a.Ui.Error(commandErrorText(a)) 113 return 1 114 } 115 116 // Get the HTTP client. 117 client, err := a.Meta.Client() 118 if err != nil { 119 a.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 120 return 1 121 } 122 123 aclRoleID := flags.Args()[0] 124 125 // Read the current role in both cases, so we can fail better if not found. 126 currentRole, _, err := client.ACLRoles().Get(aclRoleID, nil) 127 if err != nil { 128 a.Ui.Error(fmt.Sprintf("Error when retrieving ACL role: %v", err)) 129 return 1 130 } 131 132 var updatedRole api.ACLRole 133 134 // Depending on whether we are merging or not, we need to take a different 135 // approach. 136 switch a.noMerge { 137 case true: 138 139 // Perform some basic validation on the submitted role information to 140 // avoid sending API and RPC requests which will fail basic validation. 141 if a.name == "" { 142 a.Ui.Error("ACL role name must be specified using the -name flag") 143 return 1 144 } 145 if len(a.policyNames) < 1 { 146 a.Ui.Error("At least one policy name must be specified using the -policy flag") 147 return 1 148 } 149 150 updatedRole = api.ACLRole{ 151 ID: aclRoleID, 152 Name: a.name, 153 Description: a.description, 154 Policies: aclRolePolicyNamesToPolicyLinks(a.policyNames), 155 } 156 default: 157 // Check that the operator specified at least one flag to update the ACL 158 // role with. 159 if len(a.policyNames) == 0 && a.name == "" && a.description == "" { 160 a.Ui.Error("Please provide at least one flag to update the ACL role") 161 a.Ui.Error(commandErrorText(a)) 162 return 1 163 } 164 165 updatedRole = *currentRole 166 167 // If the operator specified a name or description, overwrite the 168 // existing value as these are simple strings. 169 if a.name != "" { 170 updatedRole.Name = a.name 171 } 172 if a.description != "" { 173 updatedRole.Description = a.description 174 } 175 176 // In order to merge the policy updates, we need to identify if the 177 // specified policy names already exist within the ACL role linking. 178 for _, policyName := range a.policyNames { 179 180 // Track whether we found the policy name already in the ACL role 181 // linking. 182 var found bool 183 184 for _, existingLinkedPolicy := range currentRole.Policies { 185 if policyName == existingLinkedPolicy.Name { 186 found = true 187 break 188 } 189 } 190 191 // If the policy name was not found, append this new link to the 192 // updated role. 193 if !found { 194 updatedRole.Policies = append(updatedRole.Policies, &api.ACLRolePolicyLink{Name: policyName}) 195 } 196 } 197 } 198 199 // Update the ACL role with the new information via the API. 200 updatedACLRoleRead, _, err := client.ACLRoles().Update(&updatedRole, nil) 201 if err != nil { 202 a.Ui.Error(fmt.Sprintf("Error updating ACL role: %s", err)) 203 return 1 204 } 205 206 if a.json || len(a.tmpl) > 0 { 207 out, err := Format(a.json, a.tmpl, updatedACLRoleRead) 208 if err != nil { 209 a.Ui.Error(err.Error()) 210 return 1 211 } 212 213 a.Ui.Output(out) 214 return 0 215 } 216 217 // Format the output 218 a.Ui.Output(formatACLRole(updatedACLRoleRead)) 219 return 0 220 }