github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/application/flags.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package application 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/core/devices" 14 "github.com/juju/juju/storage" 15 ) 16 17 type storageFlag struct { 18 stores *map[string]storage.Constraints 19 bundleStores *map[string]map[string]storage.Constraints 20 } 21 22 // Set implements gnuflag.Value.Set. 23 func (f storageFlag) Set(s string) error { 24 fields := strings.SplitN(s, "=", 2) 25 if len(fields) < 2 { 26 if f.bundleStores != nil { 27 return errors.New("expected [<application>:]<store>=<constraints>") 28 } 29 return errors.New("expected <store>=<constraints>") 30 } 31 var applicationName, storageName string 32 if colon := strings.IndexRune(fields[0], ':'); colon >= 0 { 33 if f.bundleStores == nil { 34 return errors.New("expected <store>=<constraints>") 35 } 36 applicationName = fields[0][:colon] 37 storageName = fields[0][colon+1:] 38 } else { 39 storageName = fields[0] 40 } 41 cons, err := storage.ParseConstraints(fields[1]) 42 if err != nil { 43 return errors.Annotate(err, "cannot parse disk constraints") 44 } 45 var stores map[string]storage.Constraints 46 if applicationName != "" { 47 if *f.bundleStores == nil { 48 *f.bundleStores = make(map[string]map[string]storage.Constraints) 49 } 50 stores = (*f.bundleStores)[applicationName] 51 if stores == nil { 52 stores = make(map[string]storage.Constraints) 53 (*f.bundleStores)[applicationName] = stores 54 } 55 } else { 56 if *f.stores == nil { 57 *f.stores = make(map[string]storage.Constraints) 58 } 59 stores = *f.stores 60 } 61 stores[storageName] = cons 62 return nil 63 } 64 65 // String implements gnuflag.Value.String. 66 func (f storageFlag) String() string { 67 strs := make([]string, 0, len(*f.stores)) 68 for store, cons := range *f.stores { 69 strs = append(strs, fmt.Sprintf("%s=%v", store, cons)) 70 } 71 if f.bundleStores != nil { 72 for application, stores := range *f.bundleStores { 73 for store, cons := range stores { 74 strs = append(strs, fmt.Sprintf("%s:%s=%v", application, store, cons)) 75 } 76 } 77 } 78 return strings.Join(strs, " ") 79 } 80 81 type devicesFlag struct { 82 devices *map[string]devices.Constraints 83 bundleDevices *map[string]map[string]devices.Constraints 84 } 85 86 // Set implements gnuflag.Value.Set. 87 func (f devicesFlag) Set(s string) error { 88 fields := strings.SplitN(s, "=", 2) 89 if len(fields) < 2 { 90 if f.bundleDevices != nil { 91 return errors.New("expected [<application>:]<device>=<constraints>") 92 } 93 return errors.New("expected <device>=<constraints>") 94 } 95 var applicationName, deviceName string 96 if colon := strings.IndexRune(fields[0], ':'); colon >= 0 { 97 if f.bundleDevices == nil { 98 return errors.New("expected <device>=<constraints>") 99 } 100 applicationName = fields[0][:colon] 101 deviceName = fields[0][colon+1:] 102 } else { 103 deviceName = fields[0] 104 } 105 cons, err := devices.ParseConstraints(fields[1]) 106 if err != nil { 107 return errors.Annotate(err, "cannot parse device constraints") 108 } 109 var devs map[string]devices.Constraints 110 if applicationName != "" { 111 if *f.bundleDevices == nil { 112 *f.bundleDevices = make(map[string]map[string]devices.Constraints) 113 } 114 devs = (*f.bundleDevices)[applicationName] 115 if devs == nil { 116 devs = make(map[string]devices.Constraints) 117 (*f.bundleDevices)[applicationName] = devs 118 } 119 } else { 120 if *f.devices == nil { 121 *f.devices = make(map[string]devices.Constraints) 122 } 123 devs = *f.devices 124 } 125 devs[deviceName] = cons 126 return nil 127 } 128 129 // String implements gnuflag.Value.String. 130 func (f devicesFlag) String() string { 131 strs := make([]string, 0, len(*f.devices)) 132 for device, cons := range *f.devices { 133 strs = append(strs, fmt.Sprintf("%s=%v", device, cons)) 134 } 135 if f.bundleDevices != nil { 136 for application, devices := range *f.bundleDevices { 137 for device, cons := range devices { 138 strs = append(strs, fmt.Sprintf("%s:%s=%v", application, device, cons)) 139 } 140 } 141 } 142 return strings.Join(strs, " ") 143 } 144 145 type attachStorageFlag struct { 146 storageIDs *[]string 147 } 148 149 // Set implements gnuflag.Value.Set. 150 func (f attachStorageFlag) Set(s string) error { 151 if s == "" { 152 return nil 153 } 154 for _, id := range strings.Split(s, ",") { 155 if !names.IsValidStorage(id) { 156 return errors.NotValidf("storage ID %q", id) 157 } 158 *f.storageIDs = append(*f.storageIDs, id) 159 } 160 return nil 161 } 162 163 // String implements gnuflag.Value.String. 164 func (f attachStorageFlag) String() string { 165 return strings.Join(*f.storageIDs, ",") 166 } 167 168 // stringMap is a type that deserializes a CLI string using gnuflag's Value 169 // semantics. It expects a name=value pair, and supports multiple copies of the 170 // flag adding more pairs, though the names must be unique. 171 type stringMap struct { 172 mapping *map[string]string 173 } 174 175 // Set implements gnuflag.Value's Set method. 176 func (m stringMap) Set(s string) error { 177 if *m.mapping == nil { 178 *m.mapping = map[string]string{} 179 } 180 // make a copy so the following code is less ugly with dereferencing. 181 mapping := *m.mapping 182 183 vals := strings.SplitN(s, "=", 2) 184 if len(vals) != 2 { 185 return errors.NewNotValid(nil, "badly formatted name value pair: "+s) 186 } 187 name, value := vals[0], vals[1] 188 if _, ok := mapping[name]; ok { 189 return errors.Errorf("duplicate name specified: %q", name) 190 } 191 mapping[name] = value 192 return nil 193 } 194 195 // String implements gnuflag.Value's String method 196 func (m stringMap) String() string { 197 pairs := make([]string, 0, len(*m.mapping)) 198 for name, value := range *m.mapping { 199 pairs = append(pairs, name+"="+value) 200 } 201 return strings.Join(pairs, ";") 202 }