github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/machine.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 "github.com/juju/errors" 8 "github.com/juju/schema" 9 "github.com/juju/version" 10 "gopkg.in/juju/names.v2" 11 ) 12 13 type machines struct { 14 Version int `yaml:"version"` 15 Machines_ []*machine `yaml:"machines"` 16 } 17 18 type machine struct { 19 Id_ string `yaml:"id"` 20 Nonce_ string `yaml:"nonce"` 21 PasswordHash_ string `yaml:"password-hash"` 22 Placement_ string `yaml:"placement,omitempty"` 23 Instance_ *cloudInstance `yaml:"instance,omitempty"` 24 Series_ string `yaml:"series"` 25 ContainerType_ string `yaml:"container-type,omitempty"` 26 27 Status_ *status `yaml:"status"` 28 StatusHistory_ `yaml:"status-history"` 29 30 ProviderAddresses_ []*address `yaml:"provider-addresses,omitempty"` 31 MachineAddresses_ []*address `yaml:"machine-addresses,omitempty"` 32 33 PreferredPublicAddress_ *address `yaml:"preferred-public-address,omitempty"` 34 PreferredPrivateAddress_ *address `yaml:"preferred-private-address,omitempty"` 35 36 Tools_ *agentTools `yaml:"tools"` 37 Jobs_ []string `yaml:"jobs"` 38 39 SupportedContainers_ *[]string `yaml:"supported-containers,omitempty"` 40 41 Containers_ []*machine `yaml:"containers"` 42 43 OpenedPorts_ *versionedOpenedPorts `yaml:"opened-ports,omitempty"` 44 45 Annotations_ `yaml:"annotations,omitempty"` 46 47 Constraints_ *constraints `yaml:"constraints,omitempty"` 48 49 BlockDevices_ blockdevices `yaml:"block-devices,omitempty"` 50 } 51 52 // MachineArgs is an argument struct used to add a machine to the Model. 53 type MachineArgs struct { 54 Id names.MachineTag 55 Nonce string 56 PasswordHash string 57 Placement string 58 Series string 59 ContainerType string 60 Jobs []string 61 // A null value means that we don't yet know which containers 62 // are supported. An empty slice means 'no containers are supported'. 63 SupportedContainers *[]string 64 } 65 66 func newMachine(args MachineArgs) *machine { 67 var jobs []string 68 if count := len(args.Jobs); count > 0 { 69 jobs = make([]string, count) 70 copy(jobs, args.Jobs) 71 } 72 m := &machine{ 73 Id_: args.Id.Id(), 74 Nonce_: args.Nonce, 75 PasswordHash_: args.PasswordHash, 76 Placement_: args.Placement, 77 Series_: args.Series, 78 ContainerType_: args.ContainerType, 79 Jobs_: jobs, 80 StatusHistory_: newStatusHistory(), 81 } 82 if args.SupportedContainers != nil { 83 supported := make([]string, len(*args.SupportedContainers)) 84 copy(supported, *args.SupportedContainers) 85 m.SupportedContainers_ = &supported 86 } 87 m.setBlockDevices(nil) 88 return m 89 } 90 91 // Id implements Machine. 92 func (m *machine) Id() string { 93 return m.Id_ 94 } 95 96 // Tag implements Machine. 97 func (m *machine) Tag() names.MachineTag { 98 return names.NewMachineTag(m.Id_) 99 } 100 101 // Nonce implements Machine. 102 func (m *machine) Nonce() string { 103 return m.Nonce_ 104 } 105 106 // PasswordHash implements Machine. 107 func (m *machine) PasswordHash() string { 108 return m.PasswordHash_ 109 } 110 111 // Placement implements Machine. 112 func (m *machine) Placement() string { 113 return m.Placement_ 114 } 115 116 // Instance implements Machine. 117 func (m *machine) Instance() CloudInstance { 118 // To avoid typed nils check nil here. 119 if m.Instance_ == nil { 120 return nil 121 } 122 return m.Instance_ 123 } 124 125 // SetInstance implements Machine. 126 func (m *machine) SetInstance(args CloudInstanceArgs) { 127 m.Instance_ = newCloudInstance(args) 128 } 129 130 // Series implements Machine. 131 func (m *machine) Series() string { 132 return m.Series_ 133 } 134 135 // ContainerType implements Machine. 136 func (m *machine) ContainerType() string { 137 return m.ContainerType_ 138 } 139 140 // Status implements Machine. 141 func (m *machine) Status() Status { 142 // To avoid typed nils check nil here. 143 if m.Status_ == nil { 144 return nil 145 } 146 return m.Status_ 147 } 148 149 // SetStatus implements Machine. 150 func (m *machine) SetStatus(args StatusArgs) { 151 m.Status_ = newStatus(args) 152 } 153 154 // ProviderAddresses implements Machine. 155 func (m *machine) ProviderAddresses() []Address { 156 var result []Address 157 for _, addr := range m.ProviderAddresses_ { 158 result = append(result, addr) 159 } 160 return result 161 } 162 163 // MachineAddresses implements Machine. 164 func (m *machine) MachineAddresses() []Address { 165 var result []Address 166 for _, addr := range m.MachineAddresses_ { 167 result = append(result, addr) 168 } 169 return result 170 } 171 172 // SetAddresses implements Machine. 173 func (m *machine) SetAddresses(margs []AddressArgs, pargs []AddressArgs) { 174 m.MachineAddresses_ = nil 175 m.ProviderAddresses_ = nil 176 for _, args := range margs { 177 if args.Value != "" { 178 m.MachineAddresses_ = append(m.MachineAddresses_, newAddress(args)) 179 } 180 } 181 for _, args := range pargs { 182 if args.Value != "" { 183 m.ProviderAddresses_ = append(m.ProviderAddresses_, newAddress(args)) 184 } 185 } 186 } 187 188 // PreferredPublicAddress implements Machine. 189 func (m *machine) PreferredPublicAddress() Address { 190 // To avoid typed nils check nil here. 191 if m.PreferredPublicAddress_ == nil { 192 return nil 193 } 194 return m.PreferredPublicAddress_ 195 } 196 197 // PreferredPrivateAddress implements Machine. 198 func (m *machine) PreferredPrivateAddress() Address { 199 // To avoid typed nils check nil here. 200 if m.PreferredPrivateAddress_ == nil { 201 return nil 202 } 203 return m.PreferredPrivateAddress_ 204 } 205 206 // SetPreferredAddresses implements Machine. 207 func (m *machine) SetPreferredAddresses(public AddressArgs, private AddressArgs) { 208 if public.Value != "" { 209 m.PreferredPublicAddress_ = newAddress(public) 210 } 211 if private.Value != "" { 212 m.PreferredPrivateAddress_ = newAddress(private) 213 } 214 } 215 216 // Tools implements Machine. 217 func (m *machine) Tools() AgentTools { 218 // To avoid a typed nil, check before returning. 219 if m.Tools_ == nil { 220 return nil 221 } 222 return m.Tools_ 223 } 224 225 // SetTools implements Machine. 226 func (m *machine) SetTools(args AgentToolsArgs) { 227 m.Tools_ = newAgentTools(args) 228 } 229 230 // Jobs implements Machine. 231 func (m *machine) Jobs() []string { 232 return m.Jobs_ 233 } 234 235 // SupportedContainers implements Machine. 236 func (m *machine) SupportedContainers() ([]string, bool) { 237 if m.SupportedContainers_ == nil { 238 return nil, false 239 } 240 return *m.SupportedContainers_, true 241 } 242 243 // Containers implements Machine. 244 func (m *machine) Containers() []Machine { 245 var result []Machine 246 for _, container := range m.Containers_ { 247 result = append(result, container) 248 } 249 return result 250 } 251 252 // BlockDevices implements Machine. 253 func (m *machine) BlockDevices() []BlockDevice { 254 var result []BlockDevice 255 for _, device := range m.BlockDevices_.BlockDevices_ { 256 result = append(result, device) 257 } 258 return result 259 } 260 261 // AddBlockDevice implements Machine. 262 func (m *machine) AddBlockDevice(args BlockDeviceArgs) BlockDevice { 263 return m.BlockDevices_.add(args) 264 } 265 266 func (m *machine) setBlockDevices(devices []*blockdevice) { 267 m.BlockDevices_ = blockdevices{ 268 Version: 1, 269 BlockDevices_: devices, 270 } 271 } 272 273 // AddContainer implements Machine. 274 func (m *machine) AddContainer(args MachineArgs) Machine { 275 container := newMachine(args) 276 m.Containers_ = append(m.Containers_, container) 277 return container 278 } 279 280 // OpenedPorts implements Machine. 281 func (m *machine) OpenedPorts() []OpenedPorts { 282 if m.OpenedPorts_ == nil { 283 return nil 284 } 285 var result []OpenedPorts 286 for _, ports := range m.OpenedPorts_.OpenedPorts_ { 287 result = append(result, ports) 288 } 289 return result 290 } 291 292 // AddOpenedPorts implements Machine. 293 func (m *machine) AddOpenedPorts(args OpenedPortsArgs) OpenedPorts { 294 if m.OpenedPorts_ == nil { 295 m.OpenedPorts_ = &versionedOpenedPorts{Version: 1} 296 } 297 ports := newOpenedPorts(args) 298 m.OpenedPorts_.OpenedPorts_ = append(m.OpenedPorts_.OpenedPorts_, ports) 299 return ports 300 } 301 302 func (m *machine) setOpenedPorts(portsList []*openedPorts) { 303 m.OpenedPorts_ = &versionedOpenedPorts{ 304 Version: 1, 305 OpenedPorts_: portsList, 306 } 307 } 308 309 // Constraints implements HasConstraints. 310 func (m *machine) Constraints() Constraints { 311 if m.Constraints_ == nil { 312 return nil 313 } 314 return m.Constraints_ 315 } 316 317 // SetConstraints implements HasConstraints. 318 func (m *machine) SetConstraints(args ConstraintsArgs) { 319 m.Constraints_ = newConstraints(args) 320 } 321 322 // Validate implements Machine. 323 func (m *machine) Validate() error { 324 if m.Id_ == "" { 325 return errors.NotValidf("machine missing id") 326 } 327 if m.Status_ == nil { 328 return errors.NotValidf("machine %q missing status", m.Id_) 329 } 330 // Since all exports should be done when machines are stable, 331 // there should always be tools and cloud instance. 332 if m.Tools_ == nil { 333 return errors.NotValidf("machine %q missing tools", m.Id_) 334 } 335 if m.Instance_ == nil { 336 return errors.NotValidf("machine %q missing instance", m.Id_) 337 } 338 for _, container := range m.Containers_ { 339 if err := container.Validate(); err != nil { 340 return errors.Trace(err) 341 } 342 } 343 344 return nil 345 } 346 347 func importMachines(source map[string]interface{}) ([]*machine, error) { 348 checker := versionedChecker("machines") 349 coerced, err := checker.Coerce(source, nil) 350 if err != nil { 351 return nil, errors.Annotatef(err, "machines version schema check failed") 352 } 353 valid := coerced.(map[string]interface{}) 354 355 version := int(valid["version"].(int64)) 356 importFunc, ok := machineDeserializationFuncs[version] 357 if !ok { 358 return nil, errors.NotValidf("version %d", version) 359 } 360 sourceList := valid["machines"].([]interface{}) 361 return importMachineList(sourceList, importFunc) 362 } 363 364 func importMachineList(sourceList []interface{}, importFunc machineDeserializationFunc) ([]*machine, error) { 365 result := make([]*machine, 0, len(sourceList)) 366 for i, value := range sourceList { 367 source, ok := value.(map[string]interface{}) 368 if !ok { 369 return nil, errors.Errorf("unexpected value for machine %d, %T", i, value) 370 } 371 machine, err := importFunc(source) 372 if err != nil { 373 return nil, errors.Annotatef(err, "machine %d", i) 374 } 375 result = append(result, machine) 376 } 377 return result, nil 378 } 379 380 type machineDeserializationFunc func(map[string]interface{}) (*machine, error) 381 382 var machineDeserializationFuncs = map[int]machineDeserializationFunc{ 383 1: importMachineV1, 384 } 385 386 func importMachineV1(source map[string]interface{}) (*machine, error) { 387 fields := schema.Fields{ 388 "id": schema.String(), 389 "nonce": schema.String(), 390 "password-hash": schema.String(), 391 "placement": schema.String(), 392 "instance": schema.StringMap(schema.Any()), 393 "series": schema.String(), 394 "container-type": schema.String(), 395 "jobs": schema.List(schema.String()), 396 "status": schema.StringMap(schema.Any()), 397 "supported-containers": schema.List(schema.String()), 398 "tools": schema.StringMap(schema.Any()), 399 "containers": schema.List(schema.StringMap(schema.Any())), 400 "opened-ports": schema.StringMap(schema.Any()), 401 402 "provider-addresses": schema.List(schema.StringMap(schema.Any())), 403 "machine-addresses": schema.List(schema.StringMap(schema.Any())), 404 "preferred-public-address": schema.StringMap(schema.Any()), 405 "preferred-private-address": schema.StringMap(schema.Any()), 406 407 "block-devices": schema.StringMap(schema.Any()), 408 } 409 410 defaults := schema.Defaults{ 411 "placement": "", 412 "container-type": "", 413 // Even though we are expecting instance data for every machine, 414 // it isn't strictly necessary, so we allow it to not exist here. 415 "instance": schema.Omit, 416 "supported-containers": schema.Omit, 417 "opened-ports": schema.Omit, 418 "block-devices": schema.Omit, 419 "provider-addresses": schema.Omit, 420 "machine-addresses": schema.Omit, 421 "preferred-public-address": schema.Omit, 422 "preferred-private-address": schema.Omit, 423 } 424 addAnnotationSchema(fields, defaults) 425 addConstraintsSchema(fields, defaults) 426 addStatusHistorySchema(fields) 427 checker := schema.FieldMap(fields, defaults) 428 429 coerced, err := checker.Coerce(source, nil) 430 if err != nil { 431 return nil, errors.Annotatef(err, "machine v1 schema check failed") 432 } 433 valid := coerced.(map[string]interface{}) 434 // From here we know that the map returned from the schema coercion 435 // contains fields of the right type. 436 result := &machine{ 437 Id_: valid["id"].(string), 438 Nonce_: valid["nonce"].(string), 439 PasswordHash_: valid["password-hash"].(string), 440 Placement_: valid["placement"].(string), 441 Series_: valid["series"].(string), 442 ContainerType_: valid["container-type"].(string), 443 StatusHistory_: newStatusHistory(), 444 Jobs_: convertToStringSlice(valid["jobs"]), 445 } 446 result.importAnnotations(valid) 447 if err := result.importStatusHistory(valid); err != nil { 448 return nil, errors.Trace(err) 449 } 450 451 if constraintsMap, ok := valid["constraints"]; ok { 452 constraints, err := importConstraints(constraintsMap.(map[string]interface{})) 453 if err != nil { 454 return nil, errors.Trace(err) 455 } 456 result.Constraints_ = constraints 457 } 458 459 if supported, ok := valid["supported-containers"]; ok { 460 supportedList := supported.([]interface{}) 461 s := make([]string, len(supportedList)) 462 for i, containerType := range supportedList { 463 s[i] = containerType.(string) 464 } 465 result.SupportedContainers_ = &s 466 } 467 468 if instanceMap, ok := valid["instance"]; ok { 469 instance, err := importCloudInstance(instanceMap.(map[string]interface{})) 470 if err != nil { 471 return nil, errors.Trace(err) 472 } 473 result.Instance_ = instance 474 } 475 476 if blockDeviceMap, ok := valid["block-devices"]; ok { 477 devices, err := importBlockDevices(blockDeviceMap.(map[string]interface{})) 478 if err != nil { 479 return nil, errors.Trace(err) 480 } 481 result.setBlockDevices(devices) 482 } else { 483 result.setBlockDevices(nil) 484 } 485 486 // Tools and status are required, so we expect them to be there. 487 tools, err := importAgentTools(valid["tools"].(map[string]interface{})) 488 if err != nil { 489 return nil, errors.Trace(err) 490 } 491 result.Tools_ = tools 492 493 status, err := importStatus(valid["status"].(map[string]interface{})) 494 if err != nil { 495 return nil, errors.Trace(err) 496 } 497 result.Status_ = status 498 499 if addresses, ok := valid["provider-addresses"]; ok { 500 providerAddresses, err := importAddresses(addresses.([]interface{})) 501 if err != nil { 502 return nil, errors.Trace(err) 503 } 504 result.ProviderAddresses_ = providerAddresses 505 } 506 507 if addresses, ok := valid["machine-addresses"]; ok { 508 machineAddresses, err := importAddresses(addresses.([]interface{})) 509 if err != nil { 510 return nil, errors.Trace(err) 511 } 512 result.MachineAddresses_ = machineAddresses 513 } 514 515 if address, ok := valid["preferred-public-address"]; ok { 516 publicAddress, err := importAddress(address.(map[string]interface{})) 517 if err != nil { 518 return nil, errors.Trace(err) 519 } 520 result.PreferredPublicAddress_ = publicAddress 521 } 522 523 if address, ok := valid["preferred-private-address"]; ok { 524 privateAddress, err := importAddress(address.(map[string]interface{})) 525 if err != nil { 526 return nil, errors.Trace(err) 527 } 528 result.PreferredPrivateAddress_ = privateAddress 529 } 530 531 machineList := valid["containers"].([]interface{}) 532 machines, err := importMachineList(machineList, importMachineV1) 533 if err != nil { 534 return nil, errors.Annotatef(err, "containers") 535 } 536 result.Containers_ = machines 537 538 if portsMap, ok := valid["opened-ports"]; ok { 539 portsList, err := importOpenedPorts(portsMap.(map[string]interface{})) 540 if err != nil { 541 return nil, errors.Trace(err) 542 } 543 result.setOpenedPorts(portsList) 544 } 545 546 return result, nil 547 548 } 549 550 // CloudInstanceArgs is an argument struct used to add information about the 551 // cloud instance to a Machine. 552 type CloudInstanceArgs struct { 553 InstanceId string 554 Status string 555 Architecture string 556 Memory uint64 557 RootDisk uint64 558 CpuCores uint64 559 CpuPower uint64 560 Tags []string 561 AvailabilityZone string 562 } 563 564 func newCloudInstance(args CloudInstanceArgs) *cloudInstance { 565 tags := make([]string, len(args.Tags)) 566 copy(tags, args.Tags) 567 return &cloudInstance{ 568 Version: 1, 569 InstanceId_: args.InstanceId, 570 Status_: args.Status, 571 Architecture_: args.Architecture, 572 Memory_: args.Memory, 573 RootDisk_: args.RootDisk, 574 CpuCores_: args.CpuCores, 575 CpuPower_: args.CpuPower, 576 Tags_: tags, 577 AvailabilityZone_: args.AvailabilityZone, 578 } 579 } 580 581 type cloudInstance struct { 582 Version int `yaml:"version"` 583 584 InstanceId_ string `yaml:"instance-id"` 585 Status_ string `yaml:"status"` 586 // For all the optional values, empty values make no sense, and 587 // it would be better to have them not set rather than set with 588 // a nonsense value. 589 Architecture_ string `yaml:"architecture,omitempty"` 590 Memory_ uint64 `yaml:"memory,omitempty"` 591 RootDisk_ uint64 `yaml:"root-disk,omitempty"` 592 CpuCores_ uint64 `yaml:"cores,omitempty"` 593 CpuPower_ uint64 `yaml:"cpu-power,omitempty"` 594 Tags_ []string `yaml:"tags,omitempty"` 595 AvailabilityZone_ string `yaml:"availability-zone,omitempty"` 596 } 597 598 // InstanceId implements CloudInstance. 599 func (c *cloudInstance) InstanceId() string { 600 return c.InstanceId_ 601 } 602 603 // Status implements CloudInstance. 604 func (c *cloudInstance) Status() string { 605 return c.Status_ 606 } 607 608 // Architecture implements CloudInstance. 609 func (c *cloudInstance) Architecture() string { 610 return c.Architecture_ 611 } 612 613 // Memory implements CloudInstance. 614 func (c *cloudInstance) Memory() uint64 { 615 return c.Memory_ 616 } 617 618 // RootDisk implements CloudInstance. 619 func (c *cloudInstance) RootDisk() uint64 { 620 return c.RootDisk_ 621 } 622 623 // CpuCores implements CloudInstance. 624 func (c *cloudInstance) CpuCores() uint64 { 625 return c.CpuCores_ 626 } 627 628 // CpuPower implements CloudInstance. 629 func (c *cloudInstance) CpuPower() uint64 { 630 return c.CpuPower_ 631 } 632 633 // Tags implements CloudInstance. 634 func (c *cloudInstance) Tags() []string { 635 tags := make([]string, len(c.Tags_)) 636 copy(tags, c.Tags_) 637 return tags 638 } 639 640 // AvailabilityZone implements CloudInstance. 641 func (c *cloudInstance) AvailabilityZone() string { 642 return c.AvailabilityZone_ 643 } 644 645 func importCloudInstance(source map[string]interface{}) (*cloudInstance, error) { 646 version, err := getVersion(source) 647 if err != nil { 648 return nil, errors.Annotate(err, "cloudInstance version schema check failed") 649 } 650 651 importFunc, ok := cloudInstanceDeserializationFuncs[version] 652 if !ok { 653 return nil, errors.NotValidf("version %d", version) 654 } 655 656 return importFunc(source) 657 } 658 659 type cloudInstanceDeserializationFunc func(map[string]interface{}) (*cloudInstance, error) 660 661 var cloudInstanceDeserializationFuncs = map[int]cloudInstanceDeserializationFunc{ 662 1: importCloudInstanceV1, 663 } 664 665 func importCloudInstanceV1(source map[string]interface{}) (*cloudInstance, error) { 666 fields := schema.Fields{ 667 "instance-id": schema.String(), 668 "status": schema.String(), 669 "architecture": schema.String(), 670 "memory": schema.ForceUint(), 671 "root-disk": schema.ForceUint(), 672 "cores": schema.ForceUint(), 673 "cpu-power": schema.ForceUint(), 674 "tags": schema.List(schema.String()), 675 "availability-zone": schema.String(), 676 } 677 // Some values don't have to be there. 678 defaults := schema.Defaults{ 679 "architecture": "", 680 "memory": uint64(0), 681 "root-disk": uint64(0), 682 "cores": uint64(0), 683 "cpu-power": uint64(0), 684 "tags": schema.Omit, 685 "availability-zone": "", 686 } 687 checker := schema.FieldMap(fields, defaults) 688 689 coerced, err := checker.Coerce(source, nil) 690 if err != nil { 691 return nil, errors.Annotatef(err, "cloudInstance v1 schema check failed") 692 } 693 valid := coerced.(map[string]interface{}) 694 // From here we know that the map returned from the schema coercion 695 // contains fields of the right type. 696 697 return &cloudInstance{ 698 Version: 1, 699 InstanceId_: valid["instance-id"].(string), 700 Status_: valid["status"].(string), 701 Architecture_: valid["architecture"].(string), 702 Memory_: valid["memory"].(uint64), 703 RootDisk_: valid["root-disk"].(uint64), 704 CpuCores_: valid["cores"].(uint64), 705 CpuPower_: valid["cpu-power"].(uint64), 706 Tags_: convertToStringSlice(valid["tags"]), 707 AvailabilityZone_: valid["availability-zone"].(string), 708 }, nil 709 } 710 711 // AgentToolsArgs is an argument struct used to add information about the 712 // tools the agent is using to a Machine. 713 type AgentToolsArgs struct { 714 Version version.Binary 715 URL string 716 SHA256 string 717 Size int64 718 } 719 720 func newAgentTools(args AgentToolsArgs) *agentTools { 721 return &agentTools{ 722 Version_: 1, 723 ToolsVersion_: args.Version, 724 URL_: args.URL, 725 SHA256_: args.SHA256, 726 Size_: args.Size, 727 } 728 } 729 730 // Keeping the agentTools with the machine code, because we hope 731 // that one day we will succeed in merging the unit agents with the 732 // machine agents. 733 type agentTools struct { 734 Version_ int `yaml:"version"` 735 ToolsVersion_ version.Binary `yaml:"tools-version"` 736 URL_ string `yaml:"url"` 737 SHA256_ string `yaml:"sha256"` 738 Size_ int64 `yaml:"size"` 739 } 740 741 // Version implements AgentTools. 742 func (a *agentTools) Version() version.Binary { 743 return a.ToolsVersion_ 744 } 745 746 // URL implements AgentTools. 747 func (a *agentTools) URL() string { 748 return a.URL_ 749 } 750 751 // SHA256 implements AgentTools. 752 func (a *agentTools) SHA256() string { 753 return a.SHA256_ 754 } 755 756 // Size implements AgentTools. 757 func (a *agentTools) Size() int64 { 758 return a.Size_ 759 } 760 761 func importAgentTools(source map[string]interface{}) (*agentTools, error) { 762 version, err := getVersion(source) 763 if err != nil { 764 return nil, errors.Annotate(err, "agentTools version schema check failed") 765 } 766 767 importFunc, ok := agentToolsDeserializationFuncs[version] 768 if !ok { 769 return nil, errors.NotValidf("version %d", version) 770 } 771 772 return importFunc(source) 773 } 774 775 type agentToolsDeserializationFunc func(map[string]interface{}) (*agentTools, error) 776 777 var agentToolsDeserializationFuncs = map[int]agentToolsDeserializationFunc{ 778 1: importAgentToolsV1, 779 } 780 781 func importAgentToolsV1(source map[string]interface{}) (*agentTools, error) { 782 fields := schema.Fields{ 783 "tools-version": schema.String(), 784 "url": schema.String(), 785 "sha256": schema.String(), 786 "size": schema.Int(), 787 } 788 checker := schema.FieldMap(fields, nil) // no defaults 789 790 coerced, err := checker.Coerce(source, nil) 791 if err != nil { 792 return nil, errors.Annotatef(err, "agentTools v1 schema check failed") 793 } 794 valid := coerced.(map[string]interface{}) 795 // From here we know that the map returned from the schema coercion 796 // contains fields of the right type. 797 798 verString := valid["tools-version"].(string) 799 toolsVersion, err := version.ParseBinary(verString) 800 if err != nil { 801 return nil, errors.Annotatef(err, "agentTools tools-version") 802 } 803 804 return &agentTools{ 805 Version_: 1, 806 ToolsVersion_: toolsVersion, 807 URL_: valid["url"].(string), 808 SHA256_: valid["sha256"].(string), 809 Size_: valid["size"].(int64), 810 }, nil 811 }