1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"github.com/juju/schema"
9
"github.com/juju/utils/set"
12
type relations struct {
13
Version int `yaml:"version"`
14
Relations_ []*relation `yaml:"relations"`
17
type relation struct {
19
Key_ string `yaml:"key"`
20
Endpoints_ *endpoints `yaml:"endpoints"`
23
// RelationArgs is an argument struct used to specify a relation.
24
type RelationArgs struct {
29
func newRelation(args RelationArgs) *relation {
30
relation := &relation{
34
relation.setEndpoints(nil)
38
// Id implements Relation.
39
func (r *relation) Id() int {
43
// Key implements Relation.
44
func (r *relation) Key() string {
48
// Endpoints implements Relation.
49
func (r *relation) Endpoints() []Endpoint {
50
result := make([]Endpoint, len(r.Endpoints_.Endpoints_))
51
for i, ep := range r.Endpoints_.Endpoints_ {
57
// AddEndpoint implements Relation.
58
func (r *relation) AddEndpoint(args EndpointArgs) Endpoint {
59
ep := newEndpoint(args)
60
r.Endpoints_.Endpoints_ = append(r.Endpoints_.Endpoints_, ep)
64
func (r *relation) setEndpoints(endpointList []*endpoint) {
65
r.Endpoints_ = &endpoints{
67
Endpoints_: endpointList,
71
func importRelations(source map[string]interface{}) ([]*relation, error) {
72
checker := versionedChecker("relations")
73
coerced, err := checker.Coerce(source, nil)
75
return nil, errors.Annotatef(err, "relations version schema check failed")
77
valid := coerced.(map[string]interface{})
79
version := int(valid["version"].(int64))
80
importFunc, ok := relationDeserializationFuncs[version]
82
return nil, errors.NotValidf("version %d", version)
84
relationList := valid["relations"].([]interface{})
85
return importRelationList(relationList, importFunc)
88
func importRelationList(sourceList []interface{}, importFunc relationDeserializationFunc) ([]*relation, error) {
89
result := make([]*relation, 0, len(sourceList))
90
for i, value := range sourceList {
91
source, ok := value.(map[string]interface{})
93
return nil, errors.Errorf("unexpected value for relation %d, %T", i, value)
95
relation, err := importFunc(source)
97
return nil, errors.Annotatef(err, "relation %d", i)
99
result = append(result, relation)
104
type relationDeserializationFunc func(map[string]interface{}) (*relation, error)
106
var relationDeserializationFuncs = map[int]relationDeserializationFunc{
110
func importRelationV1(source map[string]interface{}) (*relation, error) {
111
fields := schema.Fields{
113
"key": schema.String(),
114
"endpoints": schema.StringMap(schema.Any()),
117
checker := schema.FieldMap(fields, nil) // no defaults
119
coerced, err := checker.Coerce(source, nil)
121
return nil, errors.Annotatef(err, "relation v1 schema check failed")
123
valid := coerced.(map[string]interface{})
124
// From here we know that the map returned from the schema coercion
125
// contains fields of the right type.
127
Id_: int(valid["id"].(int64)),
128
Key_: valid["key"].(string),
131
endpoints, err := importEndpoints(valid["endpoints"].(map[string]interface{}))
133
return nil, errors.Trace(err)
135
result.setEndpoints(endpoints)
140
type endpoints struct {
141
Version int `yaml:"version"`
142
Endpoints_ []*endpoint `yaml:"endpoints"`
145
type endpoint struct {
146
ServiceName_ string `yaml:"service-name"`
147
Name_ string `yaml:"name"`
148
Role_ string `yaml:"role"`
149
Interface_ string `yaml:"interface"`
150
Optional_ bool `yaml:"optional"`
151
Limit_ int `yaml:"limit"`
152
Scope_ string `yaml:"scope"`
154
UnitSettings_ map[string]map[string]interface{} `yaml:"unit-settings"`
157
// EndpointArgs is an argument struct used to specify a relation.
158
type EndpointArgs struct {
168
func newEndpoint(args EndpointArgs) *endpoint {
170
ServiceName_: args.ServiceName,
173
Interface_: args.Interface,
174
Optional_: args.Optional,
177
UnitSettings_: make(map[string]map[string]interface{}),
181
func (e *endpoint) unitNames() set.Strings {
182
result := set.NewStrings()
183
for key := range e.UnitSettings_ {
189
// ServiceName implements Endpoint.
190
func (e *endpoint) ServiceName() string {
191
return e.ServiceName_
194
// Name implements Endpoint.
195
func (e *endpoint) Name() string {
199
// Role implements Endpoint.
200
func (e *endpoint) Role() string {
204
// Interface implements Endpoint.
205
func (e *endpoint) Interface() string {
209
// Optional implements Endpoint.
210
func (e *endpoint) Optional() bool {
214
// Limit implements Endpoint.
215
func (e *endpoint) Limit() int {
219
// Scope implements Endpoint.
220
func (e *endpoint) Scope() string {
224
// UnitCount implements Endpoint.
225
func (e *endpoint) UnitCount() int {
226
return len(e.UnitSettings_)
229
// Settings implements Endpoint.
230
func (e *endpoint) Settings(unitName string) map[string]interface{} {
231
return e.UnitSettings_[unitName]
234
// SetUnitSettings implements Endpoint.
235
func (e *endpoint) SetUnitSettings(unitName string, settings map[string]interface{}) {
236
e.UnitSettings_[unitName] = settings
239
func importEndpoints(source map[string]interface{}) ([]*endpoint, error) {
240
checker := versionedChecker("endpoints")
241
coerced, err := checker.Coerce(source, nil)
243
return nil, errors.Annotatef(err, "endpoints version schema check failed")
245
valid := coerced.(map[string]interface{})
247
version := int(valid["version"].(int64))
248
importFunc, ok := endpointDeserializationFuncs[version]
250
return nil, errors.NotValidf("version %d", version)
252
endpointList := valid["endpoints"].([]interface{})
253
return importEndpointList(endpointList, importFunc)
256
func importEndpointList(sourceList []interface{}, importFunc endpointDeserializationFunc) ([]*endpoint, error) {
257
result := make([]*endpoint, 0, len(sourceList))
258
for i, value := range sourceList {
259
source, ok := value.(map[string]interface{})
261
return nil, errors.Errorf("unexpected value for endpoint %d, %T", i, value)
263
service, err := importFunc(source)
265
return nil, errors.Annotatef(err, "endpoint %d", i)
267
result = append(result, service)
272
type endpointDeserializationFunc func(map[string]interface{}) (*endpoint, error)
274
var endpointDeserializationFuncs = map[int]endpointDeserializationFunc{
278
func importEndpointV1(source map[string]interface{}) (*endpoint, error) {
279
fields := schema.Fields{
280
"service-name": schema.String(),
281
"name": schema.String(),
282
"role": schema.String(),
283
"interface": schema.String(),
284
"optional": schema.Bool(),
285
"limit": schema.Int(),
286
"scope": schema.String(),
287
"unit-settings": schema.StringMap(schema.StringMap(schema.Any())),
290
checker := schema.FieldMap(fields, nil) // No defaults.
292
coerced, err := checker.Coerce(source, nil)
294
return nil, errors.Annotatef(err, "endpoint v1 schema check failed")
296
valid := coerced.(map[string]interface{})
297
// From here we know that the map returned from the schema coercion
298
// contains fields of the right type.
301
ServiceName_: valid["service-name"].(string),
302
Name_: valid["name"].(string),
303
Role_: valid["role"].(string),
304
Interface_: valid["interface"].(string),
305
Optional_: valid["optional"].(bool),
306
Limit_: int(valid["limit"].(int64)),
307
Scope_: valid["scope"].(string),
308
UnitSettings_: make(map[string]map[string]interface{}),
311
for unitname, settings := range valid["unit-settings"].(map[string]interface{}) {
312
result.UnitSettings_[unitname] = settings.(map[string]interface{})