github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/model.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package description 5 6 import ( 7 "net" 8 "sort" 9 "strings" 10 11 "github.com/juju/errors" 12 "github.com/juju/schema" 13 "github.com/juju/utils/set" 14 "github.com/juju/version" 15 "gopkg.in/juju/names.v2" 16 "gopkg.in/yaml.v2" 17 ) 18 19 // ModelArgs represent the bare minimum information that is needed 20 // to represent a model. 21 type ModelArgs struct { 22 Owner names.UserTag 23 Config map[string]interface{} 24 LatestToolsVersion version.Number 25 Blocks map[string]string 26 Cloud string 27 CloudRegion string 28 CloudCredential string 29 } 30 31 // NewModel returns a Model based on the args specified. 32 func NewModel(args ModelArgs) Model { 33 m := &model{ 34 Version: 1, 35 Owner_: args.Owner.Id(), 36 Config_: args.Config, 37 LatestToolsVersion_: args.LatestToolsVersion, 38 Sequences_: make(map[string]int), 39 Blocks_: args.Blocks, 40 Cloud_: args.Cloud, 41 CloudRegion_: args.CloudRegion, 42 CloudCredential_: args.CloudCredential, 43 } 44 m.setUsers(nil) 45 m.setMachines(nil) 46 m.setApplications(nil) 47 m.setRelations(nil) 48 m.setSpaces(nil) 49 m.setLinkLayerDevices(nil) 50 m.setSubnets(nil) 51 m.setIPAddresses(nil) 52 m.setSSHHostKeys(nil) 53 m.setCloudImageMetadatas(nil) 54 m.setActions(nil) 55 m.setVolumes(nil) 56 m.setFilesystems(nil) 57 m.setStorages(nil) 58 m.setStoragePools(nil) 59 return m 60 } 61 62 // Serialize mirrors the Deserialize method, and makes sure that 63 // the same serialization method is used. 64 func Serialize(model Model) ([]byte, error) { 65 return yaml.Marshal(model) 66 } 67 68 // Deserialize constructs a Model from a serialized YAML byte stream. The 69 // normal use for this is to construct the Model representation after getting 70 // the byte stream from an API connection or read from a file. 71 func Deserialize(bytes []byte) (Model, error) { 72 var source map[string]interface{} 73 err := yaml.Unmarshal(bytes, &source) 74 if err != nil { 75 return nil, errors.Trace(err) 76 } 77 78 model, err := importModel(source) 79 if err != nil { 80 return nil, errors.Trace(err) 81 } 82 return model, nil 83 } 84 85 // parseLinkLayerDeviceGlobalKey is used to validate that the parent device 86 // referenced by a LinkLayerDevice exists. Copied from state to avoid exporting 87 // and will be replaced by device.ParentMachineID() at some point. 88 func parseLinkLayerDeviceGlobalKey(globalKey string) (machineID, deviceName string, canBeGlobalKey bool) { 89 if !strings.Contains(globalKey, "#") { 90 // Can't be a global key. 91 return "", "", false 92 } 93 keyParts := strings.Split(globalKey, "#") 94 if len(keyParts) != 4 || (keyParts[0] != "m" && keyParts[2] != "d") { 95 // Invalid global key format. 96 return "", "", true 97 } 98 machineID, deviceName = keyParts[1], keyParts[3] 99 return machineID, deviceName, true 100 } 101 102 // parentId returns the id of the host machine if machineId a container id, or "" 103 // if machineId is not for a container. 104 func parentId(machineId string) string { 105 idParts := strings.Split(machineId, "/") 106 if len(idParts) < 3 { 107 return "" 108 } 109 return strings.Join(idParts[:len(idParts)-2], "/") 110 } 111 112 type model struct { 113 Version int `yaml:"version"` 114 115 Owner_ string `yaml:"owner"` 116 Config_ map[string]interface{} `yaml:"config"` 117 Blocks_ map[string]string `yaml:"blocks,omitempty"` 118 119 LatestToolsVersion_ version.Number `yaml:"latest-tools,omitempty"` 120 121 Users_ users `yaml:"users"` 122 Machines_ machines `yaml:"machines"` 123 Applications_ applications `yaml:"applications"` 124 Relations_ relations `yaml:"relations"` 125 Spaces_ spaces `yaml:"spaces"` 126 LinkLayerDevices_ linklayerdevices `yaml:"link-layer-devices"` 127 IPAddresses_ ipaddresses `yaml:"ip-addresses"` 128 Subnets_ subnets `yaml:"subnets"` 129 130 CloudImageMetadata_ cloudimagemetadataset `yaml:"cloud-image-metadata"` 131 132 Actions_ actions `yaml:"actions"` 133 134 SSHHostKeys_ sshHostKeys `yaml:"ssh-host-keys"` 135 136 Sequences_ map[string]int `yaml:"sequences"` 137 138 Annotations_ `yaml:"annotations,omitempty"` 139 140 Constraints_ *constraints `yaml:"constraints,omitempty"` 141 142 Cloud_ string `yaml:"cloud"` 143 CloudRegion_ string `yaml:"cloud-region,omitempty"` 144 CloudCredential_ string `yaml:"cloud-credential,omitempty"` 145 146 Volumes_ volumes `yaml:"volumes"` 147 Filesystems_ filesystems `yaml:"filesystems"` 148 Storages_ storages `yaml:"storages"` 149 StoragePools_ storagepools `yaml:"storage-pools"` 150 } 151 152 func (m *model) Tag() names.ModelTag { 153 // Here we make the assumption that the environment UUID is set 154 // correctly in the Config. 155 value := m.Config_["uuid"] 156 // Explicitly ignore the 'ok' aspect of the cast. If we don't have it 157 // and it is wrong, we panic. Here we fully expect it to exist, but 158 // paranoia says 'never panic', so worst case is we have an empty string. 159 uuid, _ := value.(string) 160 return names.NewModelTag(uuid) 161 } 162 163 // Owner implements Model. 164 func (m *model) Owner() names.UserTag { 165 return names.NewUserTag(m.Owner_) 166 } 167 168 // Config implements Model. 169 func (m *model) Config() map[string]interface{} { 170 // TODO: consider returning a deep copy. 171 return m.Config_ 172 } 173 174 // UpdateConfig implements Model. 175 func (m *model) UpdateConfig(config map[string]interface{}) { 176 for key, value := range config { 177 m.Config_[key] = value 178 } 179 } 180 181 // LatestToolsVersion implements Model. 182 func (m *model) LatestToolsVersion() version.Number { 183 return m.LatestToolsVersion_ 184 } 185 186 // Blocks implements Model. 187 func (m *model) Blocks() map[string]string { 188 return m.Blocks_ 189 } 190 191 // Implement length-based sort with ByLen type. 192 type ByName []User 193 194 func (a ByName) Len() int { return len(a) } 195 func (a ByName) Less(i, j int) bool { return a[i].Name().Id() < a[j].Name().Id() } 196 func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 197 198 // Users implements Model. 199 func (m *model) Users() []User { 200 var result []User 201 for _, user := range m.Users_.Users_ { 202 result = append(result, user) 203 } 204 sort.Sort(ByName(result)) 205 return result 206 } 207 208 // AddUser implements Model. 209 func (m *model) AddUser(args UserArgs) { 210 m.Users_.Users_ = append(m.Users_.Users_, newUser(args)) 211 } 212 213 func (m *model) setUsers(userList []*user) { 214 m.Users_ = users{ 215 Version: 1, 216 Users_: userList, 217 } 218 } 219 220 // Machines implements Model. 221 func (m *model) Machines() []Machine { 222 var result []Machine 223 for _, machine := range m.Machines_.Machines_ { 224 result = append(result, machine) 225 } 226 return result 227 } 228 229 // AddMachine implements Model. 230 func (m *model) AddMachine(args MachineArgs) Machine { 231 machine := newMachine(args) 232 m.Machines_.Machines_ = append(m.Machines_.Machines_, machine) 233 return machine 234 } 235 236 func (m *model) setMachines(machineList []*machine) { 237 m.Machines_ = machines{ 238 Version: 1, 239 Machines_: machineList, 240 } 241 } 242 243 // Applications implements Model. 244 func (m *model) Applications() []Application { 245 var result []Application 246 for _, application := range m.Applications_.Applications_ { 247 result = append(result, application) 248 } 249 return result 250 } 251 252 func (m *model) application(name string) *application { 253 for _, application := range m.Applications_.Applications_ { 254 if application.Name() == name { 255 return application 256 } 257 } 258 return nil 259 } 260 261 // AddApplication implements Model. 262 func (m *model) AddApplication(args ApplicationArgs) Application { 263 application := newApplication(args) 264 m.Applications_.Applications_ = append(m.Applications_.Applications_, application) 265 return application 266 } 267 268 func (m *model) setApplications(applicationList []*application) { 269 m.Applications_ = applications{ 270 Version: 1, 271 Applications_: applicationList, 272 } 273 } 274 275 // Relations implements Model. 276 func (m *model) Relations() []Relation { 277 var result []Relation 278 for _, relation := range m.Relations_.Relations_ { 279 result = append(result, relation) 280 } 281 return result 282 } 283 284 // AddRelation implements Model. 285 func (m *model) AddRelation(args RelationArgs) Relation { 286 relation := newRelation(args) 287 m.Relations_.Relations_ = append(m.Relations_.Relations_, relation) 288 return relation 289 } 290 291 func (m *model) setRelations(relationList []*relation) { 292 m.Relations_ = relations{ 293 Version: 1, 294 Relations_: relationList, 295 } 296 } 297 298 // Spaces implements Model. 299 func (m *model) Spaces() []Space { 300 var result []Space 301 for _, space := range m.Spaces_.Spaces_ { 302 result = append(result, space) 303 } 304 return result 305 } 306 307 // AddSpace implements Model. 308 func (m *model) AddSpace(args SpaceArgs) Space { 309 space := newSpace(args) 310 m.Spaces_.Spaces_ = append(m.Spaces_.Spaces_, space) 311 return space 312 } 313 314 func (m *model) setSpaces(spaceList []*space) { 315 m.Spaces_ = spaces{ 316 Version: 1, 317 Spaces_: spaceList, 318 } 319 } 320 321 // LinkLayerDevices implements Model. 322 func (m *model) LinkLayerDevices() []LinkLayerDevice { 323 var result []LinkLayerDevice 324 for _, device := range m.LinkLayerDevices_.LinkLayerDevices_ { 325 result = append(result, device) 326 } 327 return result 328 } 329 330 // AddLinkLayerDevice implements Model. 331 func (m *model) AddLinkLayerDevice(args LinkLayerDeviceArgs) LinkLayerDevice { 332 device := newLinkLayerDevice(args) 333 m.LinkLayerDevices_.LinkLayerDevices_ = append(m.LinkLayerDevices_.LinkLayerDevices_, device) 334 return device 335 } 336 337 func (m *model) setLinkLayerDevices(devicesList []*linklayerdevice) { 338 m.LinkLayerDevices_ = linklayerdevices{ 339 Version: 1, 340 LinkLayerDevices_: devicesList, 341 } 342 } 343 344 // Subnets implements Model. 345 func (m *model) Subnets() []Subnet { 346 var result []Subnet 347 for _, subnet := range m.Subnets_.Subnets_ { 348 result = append(result, subnet) 349 } 350 return result 351 } 352 353 // AddSubnet implemets Model. 354 func (m *model) AddSubnet(args SubnetArgs) Subnet { 355 subnet := newSubnet(args) 356 m.Subnets_.Subnets_ = append(m.Subnets_.Subnets_, subnet) 357 return subnet 358 } 359 360 func (m *model) setSubnets(subnetList []*subnet) { 361 m.Subnets_ = subnets{ 362 Version: 1, 363 Subnets_: subnetList, 364 } 365 } 366 367 // IPAddresses implements Model. 368 func (m *model) IPAddresses() []IPAddress { 369 var result []IPAddress 370 for _, addr := range m.IPAddresses_.IPAddresses_ { 371 result = append(result, addr) 372 } 373 return result 374 } 375 376 // AddIPAddress implements Model. 377 func (m *model) AddIPAddress(args IPAddressArgs) IPAddress { 378 addr := newIPAddress(args) 379 m.IPAddresses_.IPAddresses_ = append(m.IPAddresses_.IPAddresses_, addr) 380 return addr 381 } 382 383 func (m *model) setIPAddresses(addressesList []*ipaddress) { 384 m.IPAddresses_ = ipaddresses{ 385 Version: 1, 386 IPAddresses_: addressesList, 387 } 388 } 389 390 // SSHHostKeys implements Model. 391 func (m *model) SSHHostKeys() []SSHHostKey { 392 var result []SSHHostKey 393 for _, addr := range m.SSHHostKeys_.SSHHostKeys_ { 394 result = append(result, addr) 395 } 396 return result 397 } 398 399 // AddSSHHostKey implements Model. 400 func (m *model) AddSSHHostKey(args SSHHostKeyArgs) SSHHostKey { 401 addr := newSSHHostKey(args) 402 m.SSHHostKeys_.SSHHostKeys_ = append(m.SSHHostKeys_.SSHHostKeys_, addr) 403 return addr 404 } 405 406 func (m *model) setSSHHostKeys(addressesList []*sshHostKey) { 407 m.SSHHostKeys_ = sshHostKeys{ 408 Version: 1, 409 SSHHostKeys_: addressesList, 410 } 411 } 412 413 // CloudImageMetadatas implements Model. 414 func (m *model) CloudImageMetadata() []CloudImageMetadata { 415 var result []CloudImageMetadata 416 for _, addr := range m.CloudImageMetadata_.CloudImageMetadata_ { 417 result = append(result, addr) 418 } 419 return result 420 } 421 422 // Actions implements Model. 423 func (m *model) Actions() []Action { 424 var result []Action 425 for _, addr := range m.Actions_.Actions_ { 426 result = append(result, addr) 427 } 428 return result 429 } 430 431 // AddCloudImageMetadata implements Model. 432 func (m *model) AddCloudImageMetadata(args CloudImageMetadataArgs) CloudImageMetadata { 433 addr := newCloudImageMetadata(args) 434 m.CloudImageMetadata_.CloudImageMetadata_ = append(m.CloudImageMetadata_.CloudImageMetadata_, addr) 435 return addr 436 } 437 438 func (m *model) setCloudImageMetadatas(cloudimagemetadataList []*cloudimagemetadata) { 439 m.CloudImageMetadata_ = cloudimagemetadataset{ 440 Version: 1, 441 CloudImageMetadata_: cloudimagemetadataList, 442 } 443 } 444 445 // AddAction implements Model. 446 func (m *model) AddAction(args ActionArgs) Action { 447 addr := newAction(args) 448 m.Actions_.Actions_ = append(m.Actions_.Actions_, addr) 449 return addr 450 } 451 452 func (m *model) setActions(actionsList []*action) { 453 m.Actions_ = actions{ 454 Version: 1, 455 Actions_: actionsList, 456 } 457 } 458 459 // Sequences implements Model. 460 func (m *model) Sequences() map[string]int { 461 return m.Sequences_ 462 } 463 464 // SetSequence implements Model. 465 func (m *model) SetSequence(name string, value int) { 466 m.Sequences_[name] = value 467 } 468 469 // Constraints implements HasConstraints. 470 func (m *model) Constraints() Constraints { 471 if m.Constraints_ == nil { 472 return nil 473 } 474 return m.Constraints_ 475 } 476 477 // SetConstraints implements HasConstraints. 478 func (m *model) SetConstraints(args ConstraintsArgs) { 479 m.Constraints_ = newConstraints(args) 480 } 481 482 // Cloud implements Model. 483 func (m *model) Cloud() string { 484 return m.Cloud_ 485 } 486 487 // CloudRegion implements Model. 488 func (m *model) CloudRegion() string { 489 return m.CloudRegion_ 490 } 491 492 // CloudCredential implements Model. 493 func (m *model) CloudCredential() string { 494 return m.CloudCredential_ 495 } 496 497 // Volumes implements Model. 498 func (m *model) Volumes() []Volume { 499 var result []Volume 500 for _, volume := range m.Volumes_.Volumes_ { 501 result = append(result, volume) 502 } 503 return result 504 } 505 506 // AddVolume implemets Model. 507 func (m *model) AddVolume(args VolumeArgs) Volume { 508 volume := newVolume(args) 509 m.Volumes_.Volumes_ = append(m.Volumes_.Volumes_, volume) 510 return volume 511 } 512 513 func (m *model) setVolumes(volumeList []*volume) { 514 m.Volumes_ = volumes{ 515 Version: 1, 516 Volumes_: volumeList, 517 } 518 } 519 520 // Filesystems implements Model. 521 func (m *model) Filesystems() []Filesystem { 522 var result []Filesystem 523 for _, filesystem := range m.Filesystems_.Filesystems_ { 524 result = append(result, filesystem) 525 } 526 return result 527 } 528 529 // AddFilesystem implemets Model. 530 func (m *model) AddFilesystem(args FilesystemArgs) Filesystem { 531 filesystem := newFilesystem(args) 532 m.Filesystems_.Filesystems_ = append(m.Filesystems_.Filesystems_, filesystem) 533 return filesystem 534 } 535 536 func (m *model) setFilesystems(filesystemList []*filesystem) { 537 m.Filesystems_ = filesystems{ 538 Version: 1, 539 Filesystems_: filesystemList, 540 } 541 } 542 543 // Storages implements Model. 544 func (m *model) Storages() []Storage { 545 var result []Storage 546 for _, storage := range m.Storages_.Storages_ { 547 result = append(result, storage) 548 } 549 return result 550 } 551 552 // AddStorage implemets Model. 553 func (m *model) AddStorage(args StorageArgs) Storage { 554 storage := newStorage(args) 555 m.Storages_.Storages_ = append(m.Storages_.Storages_, storage) 556 return storage 557 } 558 559 func (m *model) setStorages(storageList []*storage) { 560 m.Storages_ = storages{ 561 Version: 1, 562 Storages_: storageList, 563 } 564 } 565 566 // StoragePools implements Model. 567 func (m *model) StoragePools() []StoragePool { 568 var result []StoragePool 569 for _, pool := range m.StoragePools_.Pools_ { 570 result = append(result, pool) 571 } 572 return result 573 } 574 575 // AddStoragePool implemets Model. 576 func (m *model) AddStoragePool(args StoragePoolArgs) StoragePool { 577 pool := newStoragePool(args) 578 m.StoragePools_.Pools_ = append(m.StoragePools_.Pools_, pool) 579 return pool 580 } 581 582 func (m *model) setStoragePools(poolList []*storagepool) { 583 m.StoragePools_ = storagepools{ 584 Version: 1, 585 Pools_: poolList, 586 } 587 } 588 589 // Validate implements Model. 590 func (m *model) Validate() error { 591 // A model needs an owner. 592 if m.Owner_ == "" { 593 return errors.NotValidf("missing model owner") 594 } 595 allMachines := set.NewStrings() 596 unitsWithOpenPorts := set.NewStrings() 597 for _, machine := range m.Machines_.Machines_ { 598 if err := m.validateMachine(machine, allMachines, unitsWithOpenPorts); err != nil { 599 return errors.Trace(err) 600 } 601 } 602 allApplications := set.NewStrings() 603 allUnits := set.NewStrings() 604 for _, application := range m.Applications_.Applications_ { 605 if err := application.Validate(); err != nil { 606 return errors.Trace(err) 607 } 608 allApplications.Add(application.Name()) 609 allUnits = allUnits.Union(application.unitNames()) 610 } 611 // Make sure that all the unit names specified in machine opened ports 612 // exist as units of applications. 613 unknownUnitsWithPorts := unitsWithOpenPorts.Difference(allUnits) 614 if len(unknownUnitsWithPorts) > 0 { 615 return errors.Errorf("unknown unit names in open ports: %s", unknownUnitsWithPorts.SortedValues()) 616 } 617 618 err := m.validateRelations() 619 if err != nil { 620 return errors.Trace(err) 621 } 622 623 err = m.validateSubnets() 624 if err != nil { 625 return errors.Trace(err) 626 } 627 628 err = m.validateLinkLayerDevices() 629 if err != nil { 630 return errors.Trace(err) 631 } 632 err = m.validateAddresses() 633 if err != nil { 634 return errors.Trace(err) 635 } 636 637 err = m.validateStorage(allMachines, allApplications, allUnits) 638 if err != nil { 639 return errors.Trace(err) 640 } 641 642 return nil 643 } 644 645 func (m *model) validateMachine(machine Machine, allMachineIDs, unitsWithOpenPorts set.Strings) error { 646 if err := machine.Validate(); err != nil { 647 return errors.Trace(err) 648 } 649 allMachineIDs.Add(machine.Id()) 650 for _, op := range machine.OpenedPorts() { 651 for _, pr := range op.OpenPorts() { 652 unitsWithOpenPorts.Add(pr.UnitName()) 653 } 654 } 655 for _, container := range machine.Containers() { 656 err := m.validateMachine(container, allMachineIDs, unitsWithOpenPorts) 657 if err != nil { 658 return errors.Trace(err) 659 } 660 } 661 return nil 662 } 663 664 func (m *model) validateStorage(allMachineIDs, allApplications, allUnits set.Strings) error { 665 appsAndUnits := allApplications.Union(allUnits) 666 allStorage := set.NewStrings() 667 for i, storage := range m.Storages_.Storages_ { 668 if err := storage.Validate(); err != nil { 669 return errors.Annotatef(err, "storage[%d]", i) 670 } 671 allStorage.Add(storage.Tag().Id()) 672 owner, err := storage.Owner() 673 if err != nil { 674 return errors.Wrap(err, errors.NotValidf("storage[%d] owner (%s)", i, owner)) 675 } 676 ownerID := owner.Id() 677 if !appsAndUnits.Contains(ownerID) { 678 return errors.NotValidf("storage[%d] owner (%s)", i, ownerID) 679 } 680 for _, unit := range storage.Attachments() { 681 if !allUnits.Contains(unit.Id()) { 682 return errors.NotValidf("storage[%d] attachment referencing unknown unit %q", i, unit) 683 } 684 } 685 } 686 allVolumes := set.NewStrings() 687 for i, volume := range m.Volumes_.Volumes_ { 688 if err := volume.Validate(); err != nil { 689 return errors.Annotatef(err, "volume[%d]", i) 690 } 691 allVolumes.Add(volume.Tag().Id()) 692 if storeID := volume.Storage().Id(); storeID != "" && !allStorage.Contains(storeID) { 693 return errors.NotValidf("volume[%d] referencing unknown storage %q", i, storeID) 694 } 695 for j, attachment := range volume.Attachments() { 696 if machineID := attachment.Machine().Id(); !allMachineIDs.Contains(machineID) { 697 return errors.NotValidf("volume[%d].attachment[%d] referencing unknown machine %q", i, j, machineID) 698 } 699 } 700 } 701 for i, filesystem := range m.Filesystems_.Filesystems_ { 702 if err := filesystem.Validate(); err != nil { 703 return errors.Annotatef(err, "filesystem[%d]", i) 704 } 705 if storeID := filesystem.Storage().Id(); storeID != "" && !allStorage.Contains(storeID) { 706 return errors.NotValidf("filesystem[%d] referencing unknown storage %q", i, storeID) 707 } 708 if volID := filesystem.Volume().Id(); volID != "" && !allVolumes.Contains(volID) { 709 return errors.NotValidf("filesystem[%d] referencing unknown volume %q", i, volID) 710 } 711 for j, attachment := range filesystem.Attachments() { 712 if machineID := attachment.Machine().Id(); !allMachineIDs.Contains(machineID) { 713 return errors.NotValidf("filesystem[%d].attachment[%d] referencing unknown machine %q", i, j, machineID) 714 } 715 } 716 } 717 718 return nil 719 } 720 721 // validateSubnets makes sure that any spaces referenced by subnets exist. 722 func (m *model) validateSubnets() error { 723 spaceNames := set.NewStrings() 724 for _, space := range m.Spaces_.Spaces_ { 725 spaceNames.Add(space.Name()) 726 } 727 for _, subnet := range m.Subnets_.Subnets_ { 728 if subnet.SpaceName() == "" { 729 continue 730 } 731 if !spaceNames.Contains(subnet.SpaceName()) { 732 return errors.Errorf("subnet %q references non-existent space %q", subnet.CIDR(), subnet.SpaceName()) 733 } 734 } 735 736 return nil 737 } 738 739 func (m *model) machineMaps() (map[string]Machine, map[string]map[string]LinkLayerDevice) { 740 machineIDs := make(map[string]Machine) 741 for _, machine := range m.Machines_.Machines_ { 742 addMachinesToMap(machine, machineIDs) 743 } 744 745 // Build a map of all devices for each machine. 746 machineDevices := make(map[string]map[string]LinkLayerDevice) 747 for _, device := range m.LinkLayerDevices_.LinkLayerDevices_ { 748 _, ok := machineDevices[device.MachineID()] 749 if !ok { 750 machineDevices[device.MachineID()] = make(map[string]LinkLayerDevice) 751 } 752 machineDevices[device.MachineID()][device.Name()] = device 753 } 754 return machineIDs, machineDevices 755 } 756 757 func addMachinesToMap(machine Machine, machineIDs map[string]Machine) { 758 machineIDs[machine.Id()] = machine 759 for _, container := range machine.Containers() { 760 addMachinesToMap(container, machineIDs) 761 } 762 } 763 764 // validateAddresses makes sure that the machine and device referenced by IP 765 // addresses exist. 766 func (m *model) validateAddresses() error { 767 machineIDs, machineDevices := m.machineMaps() 768 for _, addr := range m.IPAddresses_.IPAddresses_ { 769 _, ok := machineIDs[addr.MachineID()] 770 if !ok { 771 return errors.Errorf("ip address %q references non-existent machine %q", addr.Value(), addr.MachineID()) 772 } 773 _, ok = machineDevices[addr.MachineID()][addr.DeviceName()] 774 if !ok { 775 return errors.Errorf("ip address %q references non-existent device %q", addr.Value(), addr.DeviceName()) 776 } 777 if ip := net.ParseIP(addr.Value()); ip == nil { 778 return errors.Errorf("ip address has invalid value %q", addr.Value()) 779 } 780 if addr.SubnetCIDR() == "" { 781 return errors.Errorf("ip address %q has empty subnet CIDR", addr.Value()) 782 } 783 if _, _, err := net.ParseCIDR(addr.SubnetCIDR()); err != nil { 784 return errors.Errorf("ip address %q has invalid subnet CIDR %q", addr.Value(), addr.SubnetCIDR()) 785 } 786 787 if addr.GatewayAddress() != "" { 788 if ip := net.ParseIP(addr.GatewayAddress()); ip == nil { 789 return errors.Errorf("ip address %q has invalid gateway address %q", addr.Value(), addr.GatewayAddress()) 790 } 791 } 792 } 793 return nil 794 } 795 796 // validateLinkLayerDevices makes sure that any machines referenced by link 797 // layer devices exist. 798 func (m *model) validateLinkLayerDevices() error { 799 machineIDs, machineDevices := m.machineMaps() 800 for _, device := range m.LinkLayerDevices_.LinkLayerDevices_ { 801 machine, ok := machineIDs[device.MachineID()] 802 if !ok { 803 return errors.Errorf("device %q references non-existent machine %q", device.Name(), device.MachineID()) 804 } 805 if device.Name() == "" { 806 return errors.Errorf("device has empty name: %#v", device) 807 } 808 if device.MACAddress() != "" { 809 if _, err := net.ParseMAC(device.MACAddress()); err != nil { 810 return errors.Errorf("device %q has invalid MACAddress %q", device.Name(), device.MACAddress()) 811 } 812 } 813 if device.ParentName() == "" { 814 continue 815 } 816 hostMachineID, parentDeviceName, canBeGlobalKey := parseLinkLayerDeviceGlobalKey(device.ParentName()) 817 if !canBeGlobalKey { 818 hostMachineID = device.MachineID() 819 parentDeviceName = device.ParentName() 820 } 821 parentDevice, ok := machineDevices[hostMachineID][parentDeviceName] 822 if !ok { 823 return errors.Errorf("device %q has non-existent parent %q", device.Name(), parentDeviceName) 824 } 825 if !canBeGlobalKey { 826 if device.Name() == parentDeviceName { 827 return errors.Errorf("device %q is its own parent", device.Name()) 828 } 829 continue 830 } 831 // The device is on a container. 832 if parentDevice.Type() != "bridge" { 833 return errors.Errorf("device %q on a container but not a bridge", device.Name()) 834 } 835 parentId := parentId(machine.Id()) 836 if parentId == "" { 837 return errors.Errorf("ParentName %q for non-container machine %q", device.ParentName(), machine.Id()) 838 } 839 if parentDevice.MachineID() != parentId { 840 return errors.Errorf("parent machine of device %q not host machine %q", device.Name(), parentId) 841 } 842 } 843 return nil 844 } 845 846 // validateRelations makes sure that for each endpoint in each relation there 847 // are settings for all units of that application for that endpoint. 848 func (m *model) validateRelations() error { 849 for _, relation := range m.Relations_.Relations_ { 850 for _, ep := range relation.Endpoints_.Endpoints_ { 851 // Check application exists. 852 application := m.application(ep.ApplicationName()) 853 if application == nil { 854 return errors.Errorf("unknown application %q for relation id %d", ep.ApplicationName(), relation.Id()) 855 } 856 // Check that all units have settings. 857 applicationUnits := application.unitNames() 858 epUnits := ep.unitNames() 859 if missingSettings := applicationUnits.Difference(epUnits); len(missingSettings) > 0 { 860 return errors.Errorf("missing relation settings for units %s in relation %d", missingSettings.SortedValues(), relation.Id()) 861 } 862 if extraSettings := epUnits.Difference(applicationUnits); len(extraSettings) > 0 { 863 return errors.Errorf("settings for unknown units %s in relation %d", extraSettings.SortedValues(), relation.Id()) 864 } 865 } 866 } 867 return nil 868 } 869 870 // importModel constructs a new Model from a map that in normal usage situations 871 // will be the result of interpreting a large YAML document. 872 // 873 // This method is a package internal serialisation method. 874 func importModel(source map[string]interface{}) (*model, error) { 875 version, err := getVersion(source) 876 if err != nil { 877 return nil, errors.Trace(err) 878 } 879 880 importFunc, ok := modelDeserializationFuncs[version] 881 if !ok { 882 return nil, errors.NotValidf("version %d", version) 883 } 884 885 return importFunc(source) 886 } 887 888 type modelDeserializationFunc func(map[string]interface{}) (*model, error) 889 890 var modelDeserializationFuncs = map[int]modelDeserializationFunc{ 891 1: importModelV1, 892 } 893 894 func importModelV1(source map[string]interface{}) (*model, error) { 895 fields := schema.Fields{ 896 "owner": schema.String(), 897 "cloud": schema.String(), 898 "cloud-region": schema.String(), 899 "config": schema.StringMap(schema.Any()), 900 "latest-tools": schema.String(), 901 "blocks": schema.StringMap(schema.String()), 902 "users": schema.StringMap(schema.Any()), 903 "machines": schema.StringMap(schema.Any()), 904 "applications": schema.StringMap(schema.Any()), 905 "relations": schema.StringMap(schema.Any()), 906 "ssh-host-keys": schema.StringMap(schema.Any()), 907 "cloud-image-metadata": schema.StringMap(schema.Any()), 908 "actions": schema.StringMap(schema.Any()), 909 "ip-addresses": schema.StringMap(schema.Any()), 910 "spaces": schema.StringMap(schema.Any()), 911 "subnets": schema.StringMap(schema.Any()), 912 "link-layer-devices": schema.StringMap(schema.Any()), 913 "volumes": schema.StringMap(schema.Any()), 914 "filesystems": schema.StringMap(schema.Any()), 915 "storages": schema.StringMap(schema.Any()), 916 "storage-pools": schema.StringMap(schema.Any()), 917 "sequences": schema.StringMap(schema.Int()), 918 } 919 // Some values don't have to be there. 920 defaults := schema.Defaults{ 921 "latest-tools": schema.Omit, 922 "blocks": schema.Omit, 923 "cloud-region": schema.Omit, 924 } 925 addAnnotationSchema(fields, defaults) 926 addConstraintsSchema(fields, defaults) 927 checker := schema.FieldMap(fields, defaults) 928 929 coerced, err := checker.Coerce(source, nil) 930 if err != nil { 931 return nil, errors.Annotatef(err, "model v1 schema check failed") 932 } 933 valid := coerced.(map[string]interface{}) 934 // From here we know that the map returned from the schema coercion 935 // contains fields of the right type. 936 937 result := &model{ 938 Version: 1, 939 Owner_: valid["owner"].(string), 940 Config_: valid["config"].(map[string]interface{}), 941 Sequences_: make(map[string]int), 942 Blocks_: convertToStringMap(valid["blocks"]), 943 Cloud_: valid["cloud"].(string), 944 } 945 result.importAnnotations(valid) 946 sequences := valid["sequences"].(map[string]interface{}) 947 for key, value := range sequences { 948 result.SetSequence(key, int(value.(int64))) 949 } 950 951 if constraintsMap, ok := valid["constraints"]; ok { 952 constraints, err := importConstraints(constraintsMap.(map[string]interface{})) 953 if err != nil { 954 return nil, errors.Trace(err) 955 } 956 result.Constraints_ = constraints 957 } 958 959 if availableTools, ok := valid["latest-tools"]; ok { 960 num, err := version.Parse(availableTools.(string)) 961 if err != nil { 962 return nil, errors.Trace(err) 963 } 964 result.LatestToolsVersion_ = num 965 } 966 967 if region, ok := valid["cloud-region"]; ok { 968 result.CloudRegion_ = region.(string) 969 } 970 971 if credential, ok := valid["cloud-credential"]; ok { 972 result.CloudCredential_ = credential.(string) 973 } 974 975 userMap := valid["users"].(map[string]interface{}) 976 users, err := importUsers(userMap) 977 if err != nil { 978 return nil, errors.Annotate(err, "users") 979 } 980 result.setUsers(users) 981 982 machineMap := valid["machines"].(map[string]interface{}) 983 machines, err := importMachines(machineMap) 984 if err != nil { 985 return nil, errors.Annotate(err, "machines") 986 } 987 result.setMachines(machines) 988 989 applicationMap := valid["applications"].(map[string]interface{}) 990 applications, err := importApplications(applicationMap) 991 if err != nil { 992 return nil, errors.Annotate(err, "applications") 993 } 994 result.setApplications(applications) 995 996 relationMap := valid["relations"].(map[string]interface{}) 997 relations, err := importRelations(relationMap) 998 if err != nil { 999 return nil, errors.Annotate(err, "relations") 1000 } 1001 result.setRelations(relations) 1002 1003 spaceMap := valid["spaces"].(map[string]interface{}) 1004 spaces, err := importSpaces(spaceMap) 1005 if err != nil { 1006 return nil, errors.Annotate(err, "spaces") 1007 } 1008 result.setSpaces(spaces) 1009 1010 deviceMap := valid["link-layer-devices"].(map[string]interface{}) 1011 devices, err := importLinkLayerDevices(deviceMap) 1012 if err != nil { 1013 return nil, errors.Annotate(err, "link-layer-devices") 1014 } 1015 result.setLinkLayerDevices(devices) 1016 1017 subnetsMap := valid["subnets"].(map[string]interface{}) 1018 subnets, err := importSubnets(subnetsMap) 1019 if err != nil { 1020 return nil, errors.Annotate(err, "subnets") 1021 } 1022 result.setSubnets(subnets) 1023 1024 addressMap := valid["ip-addresses"].(map[string]interface{}) 1025 addresses, err := importIPAddresses(addressMap) 1026 if err != nil { 1027 return nil, errors.Annotate(err, "ip-addresses") 1028 } 1029 result.setIPAddresses(addresses) 1030 1031 sshHostKeyMap := valid["ssh-host-keys"].(map[string]interface{}) 1032 hostKeys, err := importSSHHostKeys(sshHostKeyMap) 1033 if err != nil { 1034 return nil, errors.Annotate(err, "ssh-host-keys") 1035 } 1036 result.setSSHHostKeys(hostKeys) 1037 1038 cloudimagemetadataMap := valid["cloud-image-metadata"].(map[string]interface{}) 1039 cloudimagemetadata, err := importCloudImageMetadata(cloudimagemetadataMap) 1040 if err != nil { 1041 return nil, errors.Annotate(err, "cloud-image-metadata") 1042 } 1043 result.setCloudImageMetadatas(cloudimagemetadata) 1044 1045 actionsMap := valid["actions"].(map[string]interface{}) 1046 actions, err := importActions(actionsMap) 1047 if err != nil { 1048 return nil, errors.Annotate(err, "actions") 1049 } 1050 result.setActions(actions) 1051 1052 volumes, err := importVolumes(valid["volumes"].(map[string]interface{})) 1053 if err != nil { 1054 return nil, errors.Annotate(err, "volumes") 1055 } 1056 result.setVolumes(volumes) 1057 1058 filesystems, err := importFilesystems(valid["filesystems"].(map[string]interface{})) 1059 if err != nil { 1060 return nil, errors.Annotate(err, "filesystems") 1061 } 1062 result.setFilesystems(filesystems) 1063 1064 storages, err := importStorages(valid["storages"].(map[string]interface{})) 1065 if err != nil { 1066 return nil, errors.Annotate(err, "storages") 1067 } 1068 result.setStorages(storages) 1069 1070 pools, err := importStoragePools(valid["storage-pools"].(map[string]interface{})) 1071 if err != nil { 1072 return nil, errors.Annotate(err, "storage-pools") 1073 } 1074 result.setStoragePools(pools) 1075 1076 return result, nil 1077 }