~mfoord/gomaasapi/devices

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2013 Canonical Ltd.  This software is licensed under the
// GNU Lesser General Public License version 3 (see the file COPYING).

package gomaasapi

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
)

// MAASObject represents a MAAS object as returned by the MAAS API, such as a
// Node or a Tag.
// You can extract a MAASObject out of a JSONObject using
// JSONObject.GetMAASObject.  A MAAS API call will usually return either a
// MAASObject or a list of MAASObjects.  The list itself would be wrapped in
// a JSONObject, so if an API call returns a list of objects "l," you first
// obtain the array using l.GetArray().  Then, for each item "i" in the array,
// obtain the matching MAASObject using i.GetMAASObject().
type MAASObject struct {
	values map[string]JSONObject
	client Client
	uri    *url.URL
}

// newJSONMAASObject creates a new MAAS object.  It will panic if the given map
// does not contain a valid URL for the 'resource_uri' key.
func newJSONMAASObject(jmap map[string]interface{}, client Client) MAASObject {
	obj, err := maasify(client, jmap).GetMAASObject()
	if err != nil {
		panic(err)
	}
	return obj
}

// MarshalJSON tells the standard json package how to serialize a MAASObject.
func (obj MAASObject) MarshalJSON() ([]byte, error) {
	return json.Marshal(obj.GetMap())
}

// With MarshalJSON, MAASObject implements json.Marshaler.
var _ json.Marshaler = (*MAASObject)(nil)

func marshalNode(node MAASObject) string {
	res, _ := json.Marshal(node)
	return string(res)

}

var noResourceURI = errors.New("not a MAAS object: no 'resource_uri' key")

// extractURI obtains the "resource_uri" string from a JSONObject map.
func extractURI(attrs map[string]JSONObject) (*url.URL, error) {
	uriEntry, ok := attrs[resourceURI]
	if !ok {
		return nil, noResourceURI
	}
	uri, err := uriEntry.GetString()
	if err != nil {
		return nil, fmt.Errorf("invalid resource_uri: %v", uri)
	}
	resourceURL, err := url.Parse(uri)
	if err != nil {
		return nil, fmt.Errorf("resource_uri does not contain a valid URL: %v", uri)
	}
	return resourceURL, nil
}

// JSONObject getter for a MAAS object.  From a decoding perspective, a
// MAASObject is just like a map except it contains a key "resource_uri", and
// it keeps track of the Client you got it from so that you can invoke API
// methods directly on their MAAS objects.
func (obj JSONObject) GetMAASObject() (MAASObject, error) {
	attrs, err := obj.GetMap()
	if err != nil {
		return MAASObject{}, err
	}
	uri, err := extractURI(attrs)
	if err != nil {
		return MAASObject{}, err
	}
	return MAASObject{values: attrs, client: obj.client, uri: uri}, nil
}

// GetField extracts a string field from this MAAS object.
func (obj MAASObject) GetField(name string) (string, error) {
	return obj.values[name].GetString()
}

// URI is the resource URI for this MAAS object.  It is an absolute path, but
// without a network part.
func (obj MAASObject) URI() *url.URL {
	// Duplicate the URL.
	uri, err := url.Parse(obj.uri.String())
	if err != nil {
		panic(err)
	}
	return uri
}

// URL returns a full absolute URL (including network part) for this MAAS
// object on the API.
func (obj MAASObject) URL() *url.URL {
	return obj.client.GetURL(obj.URI())
}

// GetMap returns all of the object's attributes in the form of a map.
func (obj MAASObject) GetMap() map[string]JSONObject {
	return obj.values
}

// GetSubObject returns a new MAASObject representing the API resource found
// at a given sub-path of the current object's resource URI.
func (obj MAASObject) GetSubObject(name string) MAASObject {
	uri := obj.URI()
	newURL := url.URL{Path: name}
	resUrl := uri.ResolveReference(&newURL)
	resUrl.Path = EnsureTrailingSlash(resUrl.Path)
	input := map[string]interface{}{resourceURI: resUrl.String()}
	return newJSONMAASObject(input, obj.client)
}

var NotImplemented = errors.New("Not implemented")

// Get retrieves a fresh copy of this MAAS object from the API.
func (obj MAASObject) Get() (MAASObject, error) {
	uri := obj.URI()
	result, err := obj.client.Get(uri, "", url.Values{})
	if err != nil {
		return MAASObject{}, err
	}
	jsonObj, err := Parse(obj.client, result)
	if err != nil {
		return MAASObject{}, err
	}
	return jsonObj.GetMAASObject()
}

// Post overwrites this object's existing value on the API with those given
// in "params."  It returns the object's new value as received from the API.
func (obj MAASObject) Post(params url.Values) (JSONObject, error) {
	uri := obj.URI()
	result, err := obj.client.Post(uri, "", params, nil)
	if err != nil {
		return JSONObject{}, err
	}
	return Parse(obj.client, result)
}

// Update modifies this object on the API, based on the values given in
// "params."  It returns the object's new value as received from the API.
func (obj MAASObject) Update(params url.Values) (MAASObject, error) {
	uri := obj.URI()
	result, err := obj.client.Put(uri, params)
	if err != nil {
		return MAASObject{}, err
	}
	jsonObj, err := Parse(obj.client, result)
	if err != nil {
		return MAASObject{}, err
	}
	return jsonObj.GetMAASObject()
}

// Delete removes this object on the API.
func (obj MAASObject) Delete() error {
	uri := obj.URI()
	return obj.client.Delete(uri)
}

// CallGet invokes an idempotent API method on this object.
func (obj MAASObject) CallGet(operation string, params url.Values) (JSONObject, error) {
	uri := obj.URI()
	result, err := obj.client.Get(uri, operation, params)
	if err != nil {
		return JSONObject{}, err
	}
	return Parse(obj.client, result)
}

// CallPost invokes a non-idempotent API method on this object.
func (obj MAASObject) CallPost(operation string, params url.Values) (JSONObject, error) {
	return obj.CallPostFiles(operation, params, nil)
}

// CallPostFiles invokes a non-idempotent API method on this object.  It is
// similar to CallPost but has an extra parameter, 'files', which should
// contain the files that will be uploaded to the API.
func (obj MAASObject) CallPostFiles(operation string, params url.Values, files map[string][]byte) (JSONObject, error) {
	uri := obj.URI()
	result, err := obj.client.Post(uri, operation, params, files)
	if err != nil {
		return JSONObject{}, err
	}
	return Parse(obj.client, result)
}