diff --git a/controller.go b/controller.go index 3501a28..998b6d4 100644 --- a/controller.go +++ b/controller.go @@ -221,6 +221,23 @@ func (c *controller) Zones() ([]Zone, error) { return result, nil } +// Domains implements Controller +func (c *controller) Domains() ([]Domain, error) { + source, err := c.get("domains") + if err != nil { + return nil, NewUnexpectedError(err) + } + domains, err := readDomains(c.apiVersion, source) + if err != nil { + return nil, errors.Trace(err) + } + var result []Domain + for _, domain := range domains { + result = append(result, domain) + } + return result, nil +} + // DevicesArgs is a argument struct for selecting Devices. // Only devices that match the specified criteria are returned. type DevicesArgs struct { diff --git a/domain.go b/domain.go new file mode 100644 index 0000000..79aa510 --- /dev/null +++ b/domain.go @@ -0,0 +1,83 @@ +// Copyright 2018 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package gomaasapi + +import ( + "github.com/juju/errors" + "github.com/juju/schema" + "github.com/juju/version" +) + +type domain struct { + authoritative bool + resourceRecordCount int + ttl int + resourceURI string + id int + name string +} + +// implements Domain +func (domain *domain) Name() string { + return domain.name +} + +func readDomains(controllerVersion version.Number, source interface{}) ([]*domain, error) { + checker := schema.List(schema.StringMap(schema.Any())) + coerced, err := checker.Coerce(source, nil) + if err != nil { + return nil, errors.Annotatef(err, "domain base schema check failed") + } + valid := coerced.([]interface{}) + return readDomainList(valid) +} + +func domain_(source map[string]interface{}) (*domain, error) { + fields := schema.Fields{ + "authoritative": schema.Bool(), + "resource_record_count": schema.ForceInt(), + "ttl": schema.OneOf(schema.Nil("null"), schema.ForceInt()), + "resource_uri": schema.String(), + "id": schema.ForceInt(), + "name": schema.String(), + } + checker := schema.FieldMap(fields, nil) // no defaults + coerced, err := checker.Coerce(source, nil) + if err != nil { + return nil, errors.Annotatef(err, "domain schema check failed") + } + valid := coerced.(map[string]interface{}) + + // ttl live is going to be the zero value if it is null. Thus the time + // to live will be zero. Would rather user's check for zero or for nil? + ttl, _ := valid["gateway_ip"].(int) + + result := &domain{ + authoritative: valid["authoritative"].(bool), + id: valid["id"].(int), + name: valid["name"].(string), + resourceRecordCount: valid["resource_record_count"].(int), + resourceURI: valid["resource_uri"].(string), + ttl: ttl, + } + + return result, nil +} + +// readDomainList expects the values of the sourceList to be string maps. +func readDomainList(sourceList []interface{}) ([]*domain, error) { + result := make([]*domain, 0, len(sourceList)) + for i, value := range sourceList { + source, ok := value.(map[string]interface{}) + if !ok { + return nil, errors.Errorf("unexpected value for domain %d, %T", i, value) + } + domain, err := domain_(source) + if err != nil { + return nil, errors.Annotatef(err, "domain %d", i) + } + result = append(result, domain) + } + return result, nil +} diff --git a/domain_test.go b/domain_test.go new file mode 100644 index 0000000..0081631 --- /dev/null +++ b/domain_test.go @@ -0,0 +1,46 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the LGPLv3, see LICENCE file for details. + +package gomaasapi + +import ( + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" +) + +type domainSuite struct{} + +var _ = gc.Suite(&domainSuite{}) + +func (*domainSuite) TestReadDomainsBadSchema(c *gc.C) { + _, err := readDomains(twoDotOh, "something") + c.Assert(err.Error(), gc.Equals, `domain base schema check failed: expected list, got string("something")`) +} + +func (*domainSuite) TestReadDomains(c *gc.C) { + domains, err := readDomains(twoDotOh, parseJSON(c, domainResponse)) + c.Assert(err, jc.ErrorIsNil) + c.Assert(domains, gc.HasLen, 2) + c.Assert(domains[0].Name(), gc.Equals, "maas") + c.Assert(domains[1].Name(), gc.Equals, "anotherDomain.com") +} + +var domainResponse = ` +[ + { + "authoritative": "true", + "resource_uri": "/MAAS/api/2.0/domains/0/", + "name": "maas", + "id": 0, + "ttl": null, + "resource_record_count": 3 + }, { + "authoritative": "true", + "resource_uri": "/MAAS/api/2.0/domains/1/", + "name": "anotherDomain.com", + "id": 1, + "ttl": 10, + "resource_record_count": 3 + } +] +` diff --git a/interfaces.go b/interfaces.go index 0d19cae..83be5fe 100644 --- a/interfaces.go +++ b/interfaces.go @@ -66,6 +66,9 @@ type Controller interface { // file without sending the content of the file, we can return a File // instance here too. AddFile(AddFileArgs) error + + // Returns the DNS Domain Managed By MAAS + Domains() ([]Domain, error) } // File represents a file stored in the MAAS controller. @@ -142,6 +145,10 @@ type Zone interface { Description() string } +type Domain interface { + Name() string +} + // BootResource is the bomb... find something to say here. type BootResource interface { ID() int