github.com/hernad/nomad@v1.6.112/command/namespace_status.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package command 5 6 import ( 7 "fmt" 8 "sort" 9 "strings" 10 11 "github.com/hernad/nomad/api" 12 "github.com/posener/complete" 13 ) 14 15 type NamespaceStatusCommand struct { 16 Meta 17 } 18 19 func (c *NamespaceStatusCommand) Help() string { 20 helpText := ` 21 Usage: nomad namespace status [options] <namespace> 22 23 Status is used to view the status of a particular namespace. 24 25 If ACLs are enabled, this command requires a management ACL token or a token 26 that has a capability associated with the namespace. 27 28 General Options: 29 30 ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + ` 31 32 Status Specific Options: 33 34 -json 35 Output the latest namespace status information in a JSON format. 36 37 -t 38 Format and display namespace status information using a Go template. 39 ` 40 41 return strings.TrimSpace(helpText) 42 } 43 44 func (c *NamespaceStatusCommand) AutocompleteFlags() complete.Flags { 45 return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), 46 complete.Flags{ 47 "-json": complete.PredictNothing, 48 "-t": complete.PredictAnything, 49 }) 50 } 51 52 func (c *NamespaceStatusCommand) AutocompleteArgs() complete.Predictor { 53 return NamespacePredictor(c.Meta.Client, nil) 54 } 55 56 func (c *NamespaceStatusCommand) Synopsis() string { 57 return "Display a namespace's status" 58 } 59 60 func (c *NamespaceStatusCommand) Name() string { return "namespace status" } 61 62 func (c *NamespaceStatusCommand) Run(args []string) int { 63 var json bool 64 var tmpl string 65 66 flags := c.Meta.FlagSet(c.Name(), FlagSetClient) 67 flags.BoolVar(&json, "json", false, "") 68 flags.StringVar(&tmpl, "t", "", "") 69 flags.Usage = func() { c.Ui.Output(c.Help()) } 70 71 if err := flags.Parse(args); err != nil { 72 return 1 73 } 74 75 // Check that we got one arguments 76 args = flags.Args() 77 if l := len(args); l != 1 { 78 c.Ui.Error("This command takes one argument: <namespace>") 79 c.Ui.Error(commandErrorText(c)) 80 return 1 81 } 82 83 name := args[0] 84 85 // Get the HTTP client 86 client, err := c.Meta.Client() 87 if err != nil { 88 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 89 return 1 90 } 91 92 // Do a prefix lookup 93 ns, possible, err := getNamespace(client.Namespaces(), name) 94 if err != nil { 95 c.Ui.Error(fmt.Sprintf("Error retrieving namespaces: %s", err)) 96 return 1 97 } 98 99 if len(possible) != 0 { 100 c.Ui.Error(fmt.Sprintf("Prefix matched multiple namespaces\n\n%s", formatNamespaces(possible))) 101 return 1 102 } 103 104 if json || len(tmpl) > 0 { 105 out, err := Format(json, tmpl, ns) 106 if err != nil { 107 c.Ui.Error(err.Error()) 108 return 1 109 } 110 111 c.Ui.Output(out) 112 return 0 113 } 114 115 c.Ui.Output(formatNamespaceBasics(ns)) 116 117 if len(ns.Meta) > 0 { 118 c.Ui.Output(c.Colorize().Color("\n[bold]Metadata[reset]")) 119 var meta []string 120 for k := range ns.Meta { 121 meta = append(meta, fmt.Sprintf("%s|%s", k, ns.Meta[k])) 122 } 123 sort.Strings(meta) 124 c.Ui.Output(formatKV(meta)) 125 } 126 127 if ns.Quota != "" { 128 quotas := client.Quotas() 129 spec, _, err := quotas.Info(ns.Quota, nil) 130 if err != nil { 131 c.Ui.Error(fmt.Sprintf("Error retrieving quota spec: %s", err)) 132 return 1 133 } 134 135 // Get the quota usages 136 usages, failures := quotaUsages(spec, quotas) 137 138 // Format the limits 139 c.Ui.Output(c.Colorize().Color("\n[bold]Quota Limits[reset]")) 140 c.Ui.Output(formatQuotaLimits(spec, usages)) 141 142 // Display any failures 143 if len(failures) != 0 { 144 c.Ui.Error(c.Colorize().Color("\n[bold][red]Lookup Failures[reset]")) 145 for region, failure := range failures { 146 c.Ui.Error(fmt.Sprintf(" * Failed to retrieve quota usage for region %q: %v", region, failure)) 147 return 1 148 } 149 } 150 } 151 152 if ns.NodePoolConfiguration != nil { 153 c.Ui.Output(c.Colorize().Color("\n[bold]Node Pool Configuration[reset]")) 154 npConfig := ns.NodePoolConfiguration 155 npConfigOut := []string{ 156 fmt.Sprintf("Default|%s", npConfig.Default), 157 } 158 if len(npConfig.Allowed) > 0 { 159 npConfigOut = append(npConfigOut, fmt.Sprintf("Allowed|%s", strings.Join(npConfig.Allowed, ", "))) 160 } 161 if len(npConfig.Denied) > 0 { 162 npConfigOut = append(npConfigOut, fmt.Sprintf("Denied|%s", strings.Join(npConfig.Denied, ", "))) 163 } 164 c.Ui.Output(formatKV(npConfigOut)) 165 } 166 167 return 0 168 } 169 170 // formatNamespaceBasics formats the basic information of the namespace 171 func formatNamespaceBasics(ns *api.Namespace) string { 172 enabled_drivers := "*" 173 disabled_drivers := "" 174 if ns.Capabilities != nil { 175 if len(ns.Capabilities.EnabledTaskDrivers) != 0 { 176 enabled_drivers = strings.Join(ns.Capabilities.EnabledTaskDrivers, ",") 177 } 178 if len(ns.Capabilities.DisabledTaskDrivers) != 0 { 179 disabled_drivers = strings.Join(ns.Capabilities.DisabledTaskDrivers, ",") 180 } 181 } 182 basic := []string{ 183 fmt.Sprintf("Name|%s", ns.Name), 184 fmt.Sprintf("Description|%s", ns.Description), 185 fmt.Sprintf("Quota|%s", ns.Quota), 186 fmt.Sprintf("EnabledDrivers|%s", enabled_drivers), 187 fmt.Sprintf("DisabledDrivers|%s", disabled_drivers), 188 } 189 190 return formatKV(basic) 191 } 192 193 func getNamespace(client *api.Namespaces, ns string) (match *api.Namespace, possible []*api.Namespace, err error) { 194 // Do a prefix lookup 195 namespaces, _, err := client.PrefixList(ns, nil) 196 if err != nil { 197 return nil, nil, err 198 } 199 200 l := len(namespaces) 201 switch { 202 case l == 0: 203 return nil, nil, fmt.Errorf("Namespace %q matched no namespaces", ns) 204 case l == 1: 205 return namespaces[0], nil, nil 206 default: 207 // search for an exact match in the returned namespaces 208 for _, namespace := range namespaces { 209 if namespace.Name == ns { 210 return namespace, nil, nil 211 } 212 } 213 // if not found, return the fuzzy matches. 214 return nil, namespaces, nil 215 } 216 }