github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/caasunitprovisioner/client.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasunitprovisioner 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/api/base" 11 apiwatcher "github.com/juju/juju/api/watcher" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/core/application" 14 "github.com/juju/juju/core/constraints" 15 "github.com/juju/juju/core/devices" 16 "github.com/juju/juju/core/life" 17 "github.com/juju/juju/core/status" 18 "github.com/juju/juju/core/watcher" 19 "github.com/juju/juju/storage" 20 ) 21 22 // Client allows access to the CAAS unit provisioner API endpoint. 23 type Client struct { 24 facade base.FacadeCaller 25 } 26 27 // NewClient returns a client used to access the CAAS unit provisioner API. 28 func NewClient(caller base.APICaller) *Client { 29 facadeCaller := base.NewFacadeCaller(caller, "CAASUnitProvisioner") 30 return &Client{ 31 facade: facadeCaller, 32 } 33 } 34 35 func applicationTag(application string) (names.ApplicationTag, error) { 36 if !names.IsValidApplication(application) { 37 return names.ApplicationTag{}, errors.NotValidf("application name %q", application) 38 } 39 return names.NewApplicationTag(application), nil 40 } 41 42 func entities(tags ...names.Tag) params.Entities { 43 entities := params.Entities{ 44 Entities: make([]params.Entity, len(tags)), 45 } 46 for i, tag := range tags { 47 entities.Entities[i].Tag = tag.String() 48 } 49 return entities 50 } 51 52 // WatchApplications returns a StringsWatcher that notifies of 53 // changes to the lifecycles of CAAS applications in the current model. 54 func (c *Client) WatchApplications() (watcher.StringsWatcher, error) { 55 var result params.StringsWatchResult 56 if err := c.facade.FacadeCall("WatchApplications", nil, &result); err != nil { 57 return nil, err 58 } 59 if err := result.Error; err != nil { 60 return nil, result.Error 61 } 62 w := apiwatcher.NewStringsWatcher(c.facade.RawAPICaller(), result) 63 return w, nil 64 } 65 66 // ApplicationConfig returns the config for the specified application. 67 func (c *Client) ApplicationConfig(applicationName string) (application.ConfigAttributes, error) { 68 var results params.ApplicationGetConfigResults 69 args := params.Entities{ 70 Entities: []params.Entity{{Tag: names.NewApplicationTag(applicationName).String()}}, 71 } 72 err := c.facade.FacadeCall("ApplicationsConfig", args, &results) 73 if err != nil { 74 return nil, errors.Trace(err) 75 } 76 if len(results.Results) != len(args.Entities) { 77 return nil, errors.Errorf("expected %d result(s), got %d", len(args.Entities), len(results.Results)) 78 } 79 return application.ConfigAttributes(results.Results[0].Config), nil 80 } 81 82 // WatchApplicationScale returns a NotifyWatcher that notifies of 83 // changes to the lifecycles of units of the specified 84 // CAAS application in the current model. 85 func (c *Client) WatchApplicationScale(application string) (watcher.NotifyWatcher, error) { 86 applicationTag, err := applicationTag(application) 87 if err != nil { 88 return nil, errors.Trace(err) 89 } 90 args := entities(applicationTag) 91 92 var results params.NotifyWatchResults 93 if err := c.facade.FacadeCall("WatchApplicationsScale", args, &results); err != nil { 94 return nil, err 95 } 96 if n := len(results.Results); n != 1 { 97 return nil, errors.Errorf("expected 1 result, got %d", n) 98 } 99 if err := results.Results[0].Error; err != nil { 100 return nil, errors.Trace(err) 101 } 102 w := apiwatcher.NewNotifyWatcher(c.facade.RawAPICaller(), results.Results[0]) 103 return w, nil 104 } 105 106 // ApplicationScale returns the scale for the specified application. 107 func (c *Client) ApplicationScale(applicationName string) (int, error) { 108 var results params.IntResults 109 args := params.Entities{ 110 Entities: []params.Entity{{Tag: names.NewApplicationTag(applicationName).String()}}, 111 } 112 err := c.facade.FacadeCall("ApplicationsScale", args, &results) 113 if err != nil { 114 return 0, errors.Trace(err) 115 } 116 if len(results.Results) != len(args.Entities) { 117 return 0, errors.Errorf("expected %d result(s), got %d", len(args.Entities), len(results.Results)) 118 } 119 return results.Results[0].Result, nil 120 } 121 122 // WatchPodSpec returns a NotifyWatcher that notifies of 123 // changes to the pod spec of the specified CAAS application in 124 // the current model. 125 func (c *Client) WatchPodSpec(application string) (watcher.NotifyWatcher, error) { 126 appTag, err := applicationTag(application) 127 if err != nil { 128 return nil, errors.Trace(err) 129 } 130 args := entities(appTag) 131 132 var results params.NotifyWatchResults 133 if err := c.facade.FacadeCall("WatchPodSpec", args, &results); err != nil { 134 return nil, err 135 } 136 if n := len(results.Results); n != 1 { 137 return nil, errors.Errorf("expected 1 result, got %d", n) 138 } 139 if err := results.Results[0].Error; err != nil { 140 return nil, errors.Trace(err) 141 } 142 w := apiwatcher.NewNotifyWatcher(c.facade.RawAPICaller(), results.Results[0]) 143 return w, nil 144 } 145 146 // ProvisioningInfo holds unit provisioning info. 147 type ProvisioningInfo struct { 148 PodSpec string 149 Placement string 150 Constraints constraints.Value 151 Filesystems []storage.KubernetesFilesystemParams 152 Devices []devices.KubernetesDeviceParams 153 Tags map[string]string 154 } 155 156 // ErrNoUnits is returned when trying to provision a caas app but 157 // there are no units defined in the model. 158 var ErrNoUnits = errors.New("no units to provision") 159 160 // ProvisioningInfo returns the provisioning info for the specified CAAS 161 // application in the current model. 162 func (c *Client) ProvisioningInfo(appName string) (*ProvisioningInfo, error) { 163 appTag, err := applicationTag(appName) 164 if err != nil { 165 return nil, errors.Trace(err) 166 } 167 args := entities(appTag) 168 169 var results params.KubernetesProvisioningInfoResults 170 if err := c.facade.FacadeCall("ProvisioningInfo", args, &results); err != nil { 171 return nil, err 172 } 173 if n := len(results.Results); n != 1 { 174 return nil, errors.Errorf("expected 1 result, got %d", n) 175 } 176 if err := results.Results[0].Error; err != nil { 177 return nil, maybeNotFound(err) 178 } 179 result := results.Results[0].Result 180 if result == nil { 181 return nil, ErrNoUnits 182 } 183 info := &ProvisioningInfo{ 184 PodSpec: result.PodSpec, 185 Placement: result.Placement, 186 Constraints: result.Constraints, 187 Tags: result.Tags, 188 } 189 190 for _, fs := range result.Filesystems { 191 fsInfo, err := filesystemFromParams(fs) 192 if err != nil { 193 return nil, errors.Trace(err) 194 } 195 info.Filesystems = append(info.Filesystems, *fsInfo) 196 } 197 198 var devs []devices.KubernetesDeviceParams 199 for _, device := range result.Devices { 200 devs = append(devs, devices.KubernetesDeviceParams{ 201 Type: devices.DeviceType(device.Type), 202 Count: device.Count, 203 Attributes: device.Attributes, 204 }) 205 } 206 info.Devices = devs 207 return info, nil 208 } 209 210 func filesystemFromParams(in params.KubernetesFilesystemParams) (*storage.KubernetesFilesystemParams, error) { 211 var attachment *storage.KubernetesFilesystemAttachmentParams 212 if in.Attachment != nil { 213 var err error 214 attachment, err = filesystemAttachmentFromParams(*in.Attachment) 215 if err != nil { 216 return nil, errors.Trace(err) 217 } 218 } 219 return &storage.KubernetesFilesystemParams{ 220 StorageName: in.StorageName, 221 Provider: storage.ProviderType(in.Provider), 222 Size: in.Size, 223 Attributes: in.Attributes, 224 ResourceTags: in.Tags, 225 Attachment: attachment, 226 }, nil 227 } 228 229 func filesystemAttachmentFromParams(in params.KubernetesFilesystemAttachmentParams) (*storage.KubernetesFilesystemAttachmentParams, error) { 230 return &storage.KubernetesFilesystemAttachmentParams{ 231 AttachmentParams: storage.AttachmentParams{ 232 Provider: storage.ProviderType(in.Provider), 233 ReadOnly: in.ReadOnly, 234 }, 235 Path: in.MountPoint, 236 }, nil 237 } 238 239 // Life returns the lifecycle state for the specified CAAS application 240 // or unit in the current model. 241 func (c *Client) Life(entityName string) (life.Value, error) { 242 var tag names.Tag 243 switch { 244 case names.IsValidApplication(entityName): 245 tag = names.NewApplicationTag(entityName) 246 case names.IsValidUnit(entityName): 247 tag = names.NewUnitTag(entityName) 248 default: 249 return "", errors.NotValidf("application or unit name %q", entityName) 250 } 251 args := entities(tag) 252 253 var results params.LifeResults 254 if err := c.facade.FacadeCall("Life", args, &results); err != nil { 255 return "", err 256 } 257 if n := len(results.Results); n != 1 { 258 return "", errors.Errorf("expected 1 result, got %d", n) 259 } 260 if err := results.Results[0].Error; err != nil { 261 return "", maybeNotFound(err) 262 } 263 return life.Value(results.Results[0].Life), nil 264 } 265 266 // maybeNotFound returns an error satisfying errors.IsNotFound 267 // if the supplied error has a CodeNotFound error. 268 func maybeNotFound(err *params.Error) error { 269 if err == nil || !params.IsCodeNotFound(err) { 270 return err 271 } 272 return errors.NewNotFound(err, "") 273 } 274 275 // UpdateUnits updates the state model to reflect the state of the units 276 // as reported by the cloud. 277 func (c *Client) UpdateUnits(arg params.UpdateApplicationUnits) error { 278 var result params.ErrorResults 279 args := params.UpdateApplicationUnitArgs{Args: []params.UpdateApplicationUnits{arg}} 280 err := c.facade.FacadeCall("UpdateApplicationsUnits", args, &result) 281 if err != nil { 282 return errors.Trace(err) 283 } 284 if len(result.Results) != len(args.Args) { 285 return errors.Errorf("expected %d result(s), got %d", len(args.Args), len(result.Results)) 286 } 287 if result.Results[0].Error == nil { 288 return nil 289 } 290 return maybeNotFound(result.Results[0].Error) 291 } 292 293 // UpdateApplicationService updates the state model to reflect the state of the application's 294 // service as reported by the cloud. 295 func (c *Client) UpdateApplicationService(arg params.UpdateApplicationServiceArg) error { 296 var result params.ErrorResults 297 args := params.UpdateApplicationServiceArgs{Args: []params.UpdateApplicationServiceArg{arg}} 298 if err := c.facade.FacadeCall("UpdateApplicationsService", args, &result); err != nil { 299 return errors.Trace(err) 300 } 301 if len(result.Results) != len(args.Args) { 302 return errors.Errorf("expected %d result(s), got %d", len(args.Args), len(result.Results)) 303 } 304 if result.Results[0].Error == nil { 305 return nil 306 } 307 return maybeNotFound(result.Results[0].Error) 308 } 309 310 // SetOperatorStatus updates the provisioning status of an operator. 311 func (c *Client) SetOperatorStatus(appName string, status status.Status, message string, data map[string]interface{}) error { 312 var result params.ErrorResults 313 args := params.SetStatus{Entities: []params.EntityStatusArgs{ 314 {Tag: names.NewApplicationTag(appName).String(), Status: status.String(), Info: message, Data: data}, 315 }} 316 err := c.facade.FacadeCall("SetOperatorStatus", args, &result) 317 if err != nil { 318 return err 319 } 320 return result.OneError() 321 }