github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/block/list.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package block 5 6 import ( 7 "fmt" 8 "io" 9 "sort" 10 "strings" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/gnuflag" 15 "gopkg.in/juju/names.v2" 16 17 "github.com/juju/juju/api" 18 "github.com/juju/juju/api/controller" 19 "github.com/juju/juju/apiserver/params" 20 jujucmd "github.com/juju/juju/cmd" 21 "github.com/juju/juju/cmd/modelcmd" 22 "github.com/juju/juju/cmd/output" 23 ) 24 25 // NewListCommand returns the command that lists the disabled 26 // commands for the model. 27 func NewListCommand() cmd.Command { 28 return modelcmd.Wrap(&listCommand{ 29 apiFunc: func(c newAPIRoot) (blockListAPI, error) { 30 return getBlockAPI(c) 31 }, 32 controllerAPIFunc: func(c newControllerAPIRoot) (controllerListAPI, error) { 33 return getControllerAPI(c) 34 }, 35 }) 36 } 37 38 const listCommandDoc = ` 39 List disabled commands for the model. 40 ` + commandSets + ` 41 See also: 42 disable-command 43 enable-command 44 ` 45 46 // listCommand list blocks. 47 type listCommand struct { 48 modelcmd.ModelCommandBase 49 apiFunc func(newAPIRoot) (blockListAPI, error) 50 controllerAPIFunc func(newControllerAPIRoot) (controllerListAPI, error) 51 all bool 52 out cmd.Output 53 } 54 55 // Init implements Command.Init. 56 func (c *listCommand) Init(args []string) (err error) { 57 return cmd.CheckEmpty(args) 58 } 59 60 // Info implements Command.Info. 61 func (c *listCommand) Info() *cmd.Info { 62 return jujucmd.Info(&cmd.Info{ 63 Name: "disabled-commands", 64 Purpose: "List disabled commands.", 65 Doc: listCommandDoc, 66 Aliases: []string{"list-disabled-commands"}, 67 }) 68 } 69 70 // SetFlags implements Command.SetFlags. 71 func (c *listCommand) SetFlags(f *gnuflag.FlagSet) { 72 c.ModelCommandBase.SetFlags(f) 73 f.BoolVar(&c.all, "all", false, "Lists for all models (administrative users only)") 74 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 75 "yaml": cmd.FormatYaml, 76 "json": cmd.FormatJson, 77 "tabular": c.formatter, 78 }) 79 } 80 81 // Run implements Command.Run. 82 func (c *listCommand) Run(ctx *cmd.Context) (err error) { 83 if c.all { 84 return c.listForController(ctx) 85 } 86 return c.listForModel(ctx) 87 } 88 89 const noBlocks = "No commands are currently disabled." 90 91 func (c *listCommand) listForModel(ctx *cmd.Context) (err error) { 92 api, err := c.apiFunc(c) 93 if err != nil { 94 return errors.Trace(err) 95 } 96 defer api.Close() 97 98 result, err := api.List() 99 if err != nil { 100 return errors.Trace(err) 101 } 102 if len(result) == 0 && c.out.Name() == "tabular" { 103 ctx.Infof(noBlocks) 104 return nil 105 } 106 return c.out.Write(ctx, formatBlockInfo(result)) 107 } 108 109 func (c *listCommand) listForController(ctx *cmd.Context) (err error) { 110 api, err := c.controllerAPIFunc(c) 111 if err != nil { 112 return errors.Trace(err) 113 } 114 defer api.Close() 115 116 result, err := api.ListBlockedModels() 117 if err != nil { 118 return errors.Trace(err) 119 } 120 if len(result) == 0 && c.out.Name() == "tabular" { 121 ctx.Infof(noBlocks) 122 return nil 123 } 124 info, err := FormatModelBlockInfo(result) 125 if err != nil { 126 return errors.Trace(err) 127 } 128 return c.out.Write(ctx, info) 129 } 130 131 func (c *listCommand) formatter(writer io.Writer, value interface{}) error { 132 if c.all { 133 return FormatTabularBlockedModels(writer, value) 134 } 135 return formatBlocks(writer, value) 136 } 137 138 // blockListAPI defines the client API methods that block list command uses. 139 type blockListAPI interface { 140 Close() error 141 List() ([]params.Block, error) 142 } 143 144 // controllerListAPI defines the methods on the controller API endpoint 145 // that the blocks command calls. 146 type controllerListAPI interface { 147 Close() error 148 ListBlockedModels() ([]params.ModelBlockInfo, error) 149 } 150 151 // BlockInfo defines the serialization behaviour of the block information. 152 type BlockInfo struct { 153 Commands string `yaml:"command-set" json:"command-set"` 154 Message string `yaml:"message,omitempty" json:"message,omitempty"` 155 } 156 157 // formatBlockInfo takes a set of Block and creates a 158 // mapping to information structures. 159 func formatBlockInfo(all []params.Block) []BlockInfo { 160 output := make([]BlockInfo, len(all)) 161 for i, one := range all { 162 set, ok := toCmdValue[one.Type] 163 if !ok { 164 set = "<unknown>" 165 } 166 output[i] = BlockInfo{ 167 Commands: set, 168 Message: one.Message, 169 } 170 } 171 return output 172 } 173 174 // formatBlocks writes block list representation. 175 func formatBlocks(writer io.Writer, value interface{}) error { 176 blocks, ok := value.([]BlockInfo) 177 if !ok { 178 return errors.Errorf("expected value of type %T, got %T", blocks, value) 179 } 180 181 if len(blocks) == 0 { 182 fmt.Fprintf(writer, "No commands are currently disabled.") 183 return nil 184 } 185 186 tw := output.TabWriter(writer) 187 w := output.Wrapper{tw} 188 w.Println("Disabled commands", "Message") 189 for _, info := range blocks { 190 w.Println(info.Commands, info.Message) 191 } 192 tw.Flush() 193 194 return nil 195 } 196 197 type newControllerAPIRoot interface { 198 NewControllerAPIRoot() (api.Connection, error) 199 } 200 201 // getControllerAPI returns a block api for block manipulation. 202 func getControllerAPI(c newControllerAPIRoot) (*controller.Client, error) { 203 root, err := c.NewControllerAPIRoot() 204 if err != nil { 205 return nil, errors.Trace(err) 206 } 207 return controller.NewClient(root), nil 208 } 209 210 type modelBlockInfo struct { 211 Name string `yaml:"name" json:"name"` 212 UUID string `yaml:"model-uuid" json:"model-uuid"` 213 Owner string `yaml:"owner" json:"owner"` 214 CommandSets []string `yaml:"disabled-commands,omitempty" json:"disabled-commands,omitempty"` 215 } 216 217 func FormatModelBlockInfo(all []params.ModelBlockInfo) ([]modelBlockInfo, error) { 218 output := make([]modelBlockInfo, len(all)) 219 for i, one := range all { 220 tag, err := names.ParseUserTag(one.OwnerTag) 221 if err != nil { 222 return nil, errors.Trace(err) 223 } 224 output[i] = modelBlockInfo{ 225 Name: one.Name, 226 UUID: one.UUID, 227 Owner: tag.Id(), 228 CommandSets: blocksToStr(one.Blocks), 229 } 230 } 231 return output, nil 232 } 233 234 // FormatTabularBlockedModels writes out tabular format for blocked models. 235 // This method is exported as it is also used by destroy-model. 236 func FormatTabularBlockedModels(writer io.Writer, value interface{}) error { 237 models, ok := value.([]modelBlockInfo) 238 if !ok { 239 return errors.Errorf("expected value of type %T, got %T", models, value) 240 } 241 242 tw := output.TabWriter(writer) 243 w := output.Wrapper{tw} 244 w.Println("Name", "Model UUID", "Owner", "Disabled commands") 245 for _, model := range models { 246 w.Println(model.Name, model.UUID, model.Owner, strings.Join(model.CommandSets, ", ")) 247 } 248 tw.Flush() 249 return nil 250 } 251 252 func blocksToStr(blocks []string) []string { 253 result := make([]string, len(blocks)) 254 for i, val := range blocks { 255 result[i] = operationFromType(val) 256 } 257 sort.Strings(result) 258 return result 259 }