blob: 9e930e1b94bf971474d425067051634c2c5ef5e6 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2016 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4package gomaasapi
5
6import (
7 "github.com/juju/errors"
8 "github.com/juju/schema"
9 "github.com/juju/version"
10)
11
12type link struct {
13 id int
14 mode string
15 subnet *subnet
16 ipAddress string
17}
18
19// NOTE: not using lowercase L as the receiver as it is a horrible idea.
20// Instead using 'k'.
21
22// ID implements Link.
23func (k *link) ID() int {
24 return k.id
25}
26
27// Mode implements Link.
28func (k *link) Mode() string {
29 return k.mode
30}
31
32// Subnet implements Link.
33func (k *link) Subnet() Subnet {
34 if k.subnet == nil {
35 return nil
36 }
37 return k.subnet
38}
39
40// IPAddress implements Link.
41func (k *link) IPAddress() string {
42 return k.ipAddress
43}
44
45func readLinks(controllerVersion version.Number, source interface{}) ([]*link, error) {
46 checker := schema.List(schema.StringMap(schema.Any()))
47 coerced, err := checker.Coerce(source, nil)
48 if err != nil {
49 return nil, WrapWithDeserializationError(err, "link base schema check failed")
50 }
51 valid := coerced.([]interface{})
52
53 var deserialisationVersion version.Number
54 for v := range linkDeserializationFuncs {
55 if v.Compare(deserialisationVersion) > 0 && v.Compare(controllerVersion) <= 0 {
56 deserialisationVersion = v
57 }
58 }
59 if deserialisationVersion == version.Zero {
60 return nil, NewUnsupportedVersionError("no link read func for version %s", controllerVersion)
61 }
62 readFunc := linkDeserializationFuncs[deserialisationVersion]
63 return readLinkList(valid, readFunc)
64}
65
66// readLinkList expects the values of the sourceList to be string maps.
67func readLinkList(sourceList []interface{}, readFunc linkDeserializationFunc) ([]*link, error) {
68 result := make([]*link, 0, len(sourceList))
69 for i, value := range sourceList {
70 source, ok := value.(map[string]interface{})
71 if !ok {
72 return nil, NewDeserializationError("unexpected value for link %d, %T", i, value)
73 }
74 link, err := readFunc(source)
75 if err != nil {
76 return nil, errors.Annotatef(err, "link %d", i)
77 }
78 result = append(result, link)
79 }
80 return result, nil
81}
82
83type linkDeserializationFunc func(map[string]interface{}) (*link, error)
84
85var linkDeserializationFuncs = map[version.Number]linkDeserializationFunc{
86 twoDotOh: link_2_0,
87}
88
89func link_2_0(source map[string]interface{}) (*link, error) {
90 fields := schema.Fields{
91 "id": schema.ForceInt(),
92 "mode": schema.String(),
93 "subnet": schema.StringMap(schema.Any()),
94 "ip_address": schema.String(),
95 }
96 defaults := schema.Defaults{
97 "ip_address": "",
98 "subnet": schema.Omit,
99 }
100 checker := schema.FieldMap(fields, defaults)
101 coerced, err := checker.Coerce(source, nil)
102 if err != nil {
103 return nil, WrapWithDeserializationError(err, "link 2.0 schema check failed")
104 }
105 valid := coerced.(map[string]interface{})
106 // From here we know that the map returned from the schema coercion
107 // contains fields of the right type.
108
109 var subnet *subnet
110 if value, ok := valid["subnet"]; ok {
111 subnet, err = subnet_2_0(value.(map[string]interface{}))
112 if err != nil {
113 return nil, errors.Trace(err)
114 }
115 }
116
117 result := &link{
118 id: valid["id"].(int),
119 mode: valid["mode"].(string),
120 subnet: subnet,
121 ipAddress: valid["ip_address"].(string),
122 }
123 return result, nil
124}