github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/api/uniter/unit.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/juju/names" 11 12 "github.com/juju/juju/charm" 13 "github.com/juju/juju/state/api/params" 14 "github.com/juju/juju/state/api/watcher" 15 ) 16 17 // Unit represents a juju unit as seen by a uniter worker. 18 type Unit struct { 19 st *State 20 tag string 21 life params.Life 22 } 23 24 // Tag returns the unit's tag. 25 func (u *Unit) Tag() string { 26 return u.tag 27 } 28 29 // Name returns the name of the unit. 30 func (u *Unit) Name() string { 31 _, unitName, err := names.ParseTag(u.tag, names.UnitTagKind) 32 if err != nil { 33 panic(fmt.Sprintf("%q is not a valid unit tag", u.tag)) 34 } 35 return unitName 36 } 37 38 // String returns the unit as a string. 39 func (u *Unit) String() string { 40 return u.Name() 41 } 42 43 // Life returns the unit's lifecycle value. 44 func (u *Unit) Life() params.Life { 45 return u.life 46 } 47 48 // Refresh updates the cached local copy of the unit's data. 49 func (u *Unit) Refresh() error { 50 life, err := u.st.life(u.tag) 51 if err != nil { 52 return err 53 } 54 u.life = life 55 return nil 56 } 57 58 // SetStatus sets the status of the unit. 59 func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error { 60 var result params.ErrorResults 61 args := params.SetStatus{ 62 Entities: []params.EntityStatus{ 63 {Tag: u.tag, Status: status, Info: info, Data: data}, 64 }, 65 } 66 err := u.st.call("SetStatus", args, &result) 67 if err != nil { 68 return err 69 } 70 return result.OneError() 71 } 72 73 // EnsureDead sets the unit lifecycle to Dead if it is Alive or 74 // Dying. It does nothing otherwise. 75 func (u *Unit) EnsureDead() error { 76 var result params.ErrorResults 77 args := params.Entities{ 78 Entities: []params.Entity{{Tag: u.tag}}, 79 } 80 err := u.st.call("EnsureDead", args, &result) 81 if err != nil { 82 return err 83 } 84 return result.OneError() 85 } 86 87 // Watch returns a watcher for observing changes to the unit. 88 func (u *Unit) Watch() (watcher.NotifyWatcher, error) { 89 var results params.NotifyWatchResults 90 args := params.Entities{ 91 Entities: []params.Entity{{Tag: u.tag}}, 92 } 93 err := u.st.call("Watch", args, &results) 94 if err != nil { 95 return nil, err 96 } 97 if len(results.Results) != 1 { 98 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 99 } 100 result := results.Results[0] 101 if result.Error != nil { 102 return nil, result.Error 103 } 104 w := watcher.NewNotifyWatcher(u.st.caller, result) 105 return w, nil 106 } 107 108 // Service returns the service. 109 func (u *Unit) Service() (*Service, error) { 110 serviceTag := names.ServiceTag(u.ServiceName()) 111 service := &Service{ 112 st: u.st, 113 tag: serviceTag, 114 } 115 // Call Refresh() immediately to get the up-to-date 116 // life and other needed locally cached fields. 117 err := service.Refresh() 118 if err != nil { 119 return nil, err 120 } 121 return service, nil 122 } 123 124 // ConfigSettings returns the complete set of service charm config settings 125 // available to the unit. Unset values will be replaced with the default 126 // value for the associated option, and may thus be nil when no default is 127 // specified. 128 func (u *Unit) ConfigSettings() (charm.Settings, error) { 129 var results params.ConfigSettingsResults 130 args := params.Entities{ 131 Entities: []params.Entity{{Tag: u.tag}}, 132 } 133 err := u.st.call("ConfigSettings", args, &results) 134 if err != nil { 135 return nil, err 136 } 137 if len(results.Results) != 1 { 138 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 139 } 140 result := results.Results[0] 141 if result.Error != nil { 142 return nil, result.Error 143 } 144 return charm.Settings(result.Settings), nil 145 } 146 147 // ServiceName returns the service name. 148 func (u *Unit) ServiceName() string { 149 return names.UnitService(u.Name()) 150 } 151 152 // ServiceTag returns the service tag. 153 func (u *Unit) ServiceTag() string { 154 return names.ServiceTag(u.ServiceName()) 155 } 156 157 // Destroy, when called on a Alive unit, advances its lifecycle as far as 158 // possible; it otherwise has no effect. In most situations, the unit's 159 // life is just set to Dying; but if a principal unit that is not assigned 160 // to a provisioned machine is Destroyed, it will be removed from state 161 // directly. 162 func (u *Unit) Destroy() error { 163 var result params.ErrorResults 164 args := params.Entities{ 165 Entities: []params.Entity{{Tag: u.tag}}, 166 } 167 err := u.st.call("Destroy", args, &result) 168 if err != nil { 169 return err 170 } 171 return result.OneError() 172 } 173 174 // DestroyAllSubordinates destroys all subordinates of the unit. 175 func (u *Unit) DestroyAllSubordinates() error { 176 var result params.ErrorResults 177 args := params.Entities{ 178 Entities: []params.Entity{{Tag: u.tag}}, 179 } 180 err := u.st.call("DestroyAllSubordinates", args, &result) 181 if err != nil { 182 return err 183 } 184 return result.OneError() 185 } 186 187 // Resolved returns the resolved mode for the unit. 188 // 189 // NOTE: This differs from state.Unit.Resolved() by returning an 190 // error as well, because it needs to make an API call 191 func (u *Unit) Resolved() (params.ResolvedMode, error) { 192 var results params.ResolvedModeResults 193 args := params.Entities{ 194 Entities: []params.Entity{{Tag: u.tag}}, 195 } 196 err := u.st.call("Resolved", args, &results) 197 if err != nil { 198 return "", err 199 } 200 if len(results.Results) != 1 { 201 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 202 } 203 result := results.Results[0] 204 if result.Error != nil { 205 return "", result.Error 206 } 207 return result.Mode, nil 208 } 209 210 // IsPrincipal returns whether the unit is deployed in its own container, 211 // and can therefore have subordinate services deployed alongside it. 212 // 213 // NOTE: This differs from state.Unit.IsPrincipal() by returning an 214 // error as well, because it needs to make an API call. 215 func (u *Unit) IsPrincipal() (bool, error) { 216 var results params.StringBoolResults 217 args := params.Entities{ 218 Entities: []params.Entity{{Tag: u.tag}}, 219 } 220 err := u.st.call("GetPrincipal", args, &results) 221 if err != nil { 222 return false, err 223 } 224 if len(results.Results) != 1 { 225 return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 226 } 227 result := results.Results[0] 228 if result.Error != nil { 229 return false, result.Error 230 } 231 // GetPrincipal returns false when the unit is subordinate. 232 return !result.Ok, nil 233 } 234 235 // HasSubordinates returns the tags of any subordinate units. 236 func (u *Unit) HasSubordinates() (bool, error) { 237 var results params.BoolResults 238 args := params.Entities{ 239 Entities: []params.Entity{{Tag: u.tag}}, 240 } 241 err := u.st.call("HasSubordinates", args, &results) 242 if err != nil { 243 return false, err 244 } 245 if len(results.Results) != 1 { 246 return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 247 } 248 result := results.Results[0] 249 if result.Error != nil { 250 return false, result.Error 251 } 252 return result.Result, nil 253 } 254 255 // PublicAddress returns the public address of the unit and whether it 256 // is valid. 257 // 258 // NOTE: This differs from state.Unit.PublicAddres() by returning 259 // an error instead of a bool, because it needs to make an API call. 260 // 261 // TODO(dimitern): We might be able to drop this, once we have machine 262 // addresses implemented fully. See also LP bug 1221798. 263 func (u *Unit) PublicAddress() (string, error) { 264 var results params.StringResults 265 args := params.Entities{ 266 Entities: []params.Entity{{Tag: u.tag}}, 267 } 268 err := u.st.call("PublicAddress", args, &results) 269 if err != nil { 270 return "", err 271 } 272 if len(results.Results) != 1 { 273 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 274 } 275 result := results.Results[0] 276 if result.Error != nil { 277 return "", result.Error 278 } 279 return result.Result, nil 280 } 281 282 // PrivateAddress returns the private address of the unit and whether 283 // it is valid. 284 // 285 // NOTE: This differs from state.Unit.PrivateAddress() by returning 286 // an error instead of a bool, because it needs to make an API call. 287 // 288 // TODO(dimitern): We might be able to drop this, once we have machine 289 // addresses implemented fully. See also LP bug 1221798. 290 func (u *Unit) PrivateAddress() (string, error) { 291 var results params.StringResults 292 args := params.Entities{ 293 Entities: []params.Entity{{Tag: u.tag}}, 294 } 295 err := u.st.call("PrivateAddress", args, &results) 296 if err != nil { 297 return "", err 298 } 299 if len(results.Results) != 1 { 300 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 301 } 302 result := results.Results[0] 303 if result.Error != nil { 304 return "", result.Error 305 } 306 return result.Result, nil 307 } 308 309 // OpenPort sets the policy of the port with protocol and number to be 310 // opened. 311 // 312 // TODO: We should really be opening and closing ports on machines, 313 // rather than units. 314 func (u *Unit) OpenPort(protocol string, number int) error { 315 var result params.ErrorResults 316 args := params.EntitiesPorts{ 317 Entities: []params.EntityPort{ 318 {Tag: u.tag, Protocol: protocol, Port: number}, 319 }, 320 } 321 err := u.st.call("OpenPort", args, &result) 322 if err != nil { 323 return err 324 } 325 return result.OneError() 326 } 327 328 // ClosePort sets the policy of the port with protocol and number to 329 // be closed. 330 // 331 // TODO: We should really be opening and closing ports on machines, 332 // rather than units. 333 func (u *Unit) ClosePort(protocol string, number int) error { 334 var result params.ErrorResults 335 args := params.EntitiesPorts{ 336 Entities: []params.EntityPort{ 337 {Tag: u.tag, Protocol: protocol, Port: number}, 338 }, 339 } 340 err := u.st.call("ClosePort", args, &result) 341 if err != nil { 342 return err 343 } 344 return result.OneError() 345 } 346 347 var ErrNoCharmURLSet = errors.New("unit has no charm url set") 348 349 // CharmURL returns the charm URL this unit is currently using. 350 // 351 // NOTE: This differs from state.Unit.CharmURL() by returning 352 // an error instead of a bool, because it needs to make an API call. 353 func (u *Unit) CharmURL() (*charm.URL, error) { 354 var results params.StringBoolResults 355 args := params.Entities{ 356 Entities: []params.Entity{{Tag: u.tag}}, 357 } 358 err := u.st.call("CharmURL", args, &results) 359 if err != nil { 360 return nil, err 361 } 362 if len(results.Results) != 1 { 363 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 364 } 365 result := results.Results[0] 366 if result.Error != nil { 367 return nil, result.Error 368 } 369 if result.Result != "" { 370 curl, err := charm.ParseURL(result.Result) 371 if err != nil { 372 return nil, err 373 } 374 return curl, nil 375 } 376 return nil, ErrNoCharmURLSet 377 } 378 379 // SetCharmURL marks the unit as currently using the supplied charm URL. 380 // An error will be returned if the unit is dead, or the charm URL not known. 381 func (u *Unit) SetCharmURL(curl *charm.URL) error { 382 if curl == nil { 383 return fmt.Errorf("charm URL cannot be nil") 384 } 385 var result params.ErrorResults 386 args := params.EntitiesCharmURL{ 387 Entities: []params.EntityCharmURL{ 388 {Tag: u.tag, CharmURL: curl.String()}, 389 }, 390 } 391 err := u.st.call("SetCharmURL", args, &result) 392 if err != nil { 393 return err 394 } 395 return result.OneError() 396 } 397 398 // ClearResolved removes any resolved setting on the unit. 399 func (u *Unit) ClearResolved() error { 400 var result params.ErrorResults 401 args := params.Entities{ 402 Entities: []params.Entity{{Tag: u.tag}}, 403 } 404 err := u.st.call("ClearResolved", args, &result) 405 if err != nil { 406 return err 407 } 408 return result.OneError() 409 } 410 411 // WatchConfigSettings returns a watcher for observing changes to the 412 // unit's service configuration settings. The unit must have a charm URL 413 // set before this method is called, and the returned watcher will be 414 // valid only while the unit's charm URL is not changed. 415 func (u *Unit) WatchConfigSettings() (watcher.NotifyWatcher, error) { 416 var results params.NotifyWatchResults 417 args := params.Entities{ 418 Entities: []params.Entity{{Tag: u.tag}}, 419 } 420 err := u.st.call("WatchConfigSettings", args, &results) 421 if err != nil { 422 return nil, err 423 } 424 if len(results.Results) != 1 { 425 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 426 } 427 result := results.Results[0] 428 if result.Error != nil { 429 return nil, result.Error 430 } 431 w := watcher.NewNotifyWatcher(u.st.caller, result) 432 return w, nil 433 } 434 435 // JoinedRelations returns the tags of the relations the unit has joined. 436 func (u *Unit) JoinedRelations() ([]string, error) { 437 var results params.StringsResults 438 args := params.Entities{ 439 Entities: []params.Entity{{Tag: u.tag}}, 440 } 441 err := u.st.call("JoinedRelations", args, &results) 442 if err != nil { 443 return nil, err 444 } 445 if len(results.Results) != 1 { 446 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 447 } 448 result := results.Results[0] 449 if result.Error != nil { 450 return nil, result.Error 451 } 452 return result.Result, nil 453 }