├── entity ├── node │ ├── node.go │ ├── status_test.go │ └── status.go ├── event │ ├── event.go │ └── types.go ├── subnet │ ├── subnet.go │ ├── reserved_ip_range.go │ ├── ip_range.go │ ├── statistics.go │ ├── ip_address.go │ └── statistics_test.go ├── notification │ ├── notification.go │ └── categories.go ├── ssl_key.go ├── version.go ├── ssh_key.go ├── zone_test.go ├── domain_test.go ├── space.go ├── license_key.go ├── zone.go ├── node_test.go ├── resource_pool.go ├── fabric.go ├── vm_host_test.go ├── bcache_cache_set.go ├── block_device_test.go ├── resource_pool_test.go ├── tag.go ├── volume_group_test.go ├── account.go ├── rack_controller_test.go ├── static_route.go ├── entity.go ├── user.go ├── ip_range.go ├── boot_source.go ├── domain.go ├── boot_source_selection.go ├── raid_test.go ├── dns_resource_record.go ├── ip_address.go ├── dns_resource.go ├── bcache.go ├── discovery.go ├── notification.go ├── block_device_partition.go ├── package_repository.go ├── vlan.go ├── events.go ├── device.go ├── subnet.go ├── block_device.go ├── boot_resource.go ├── volume_group.go ├── vlan_test.go └── boot_resource_test.go ├── test ├── testdata │ └── maas │ │ ├── zone.json │ │ ├── resource_pool.json │ │ ├── resource_pools.json │ │ ├── subnets │ │ ├── statistics.json │ │ └── ipranges.json │ │ ├── vlan.json │ │ ├── interface.json │ │ ├── boot_resources.json │ │ ├── domains.json │ │ ├── subnet.json │ │ ├── boot_resource.json │ │ ├── volume_group.json │ │ ├── volume_groups.json │ │ ├── vlans.json │ │ ├── block_device.json │ │ └── vm_host.json └── helper │ ├── testdata.go │ └── helper.go ├── api ├── discovery.go ├── events.go ├── version.go ├── maas_server.go ├── node_results.go ├── node_devices.go ├── ssh_key.go ├── ssl_key.go ├── user.go ├── spaces.go ├── subnets.go ├── tags.go ├── ssl_keys.go ├── zones.go ├── vlans.go ├── boot_resource.go ├── node_device.go ├── users.go ├── vm_hosts.go ├── fabrics.go ├── ip_ranges.go ├── license_keys.go ├── devices.go ├── notifications.go ├── space.go ├── resource_pools.go ├── boot_sources.go ├── zone.go ├── domains.go ├── ip_range.go ├── ssh_keys.go ├── static_routes.go ├── bcaches.go ├── fabric.go ├── boot_source.go ├── raids.go ├── vlan.go ├── block_devices.go ├── dns_resource.go ├── dns_resources.go ├── static_route.go ├── volume_groups.go ├── package_repositories.go ├── domain.go ├── ip_addresses.go ├── notification.go ├── package_repository.go ├── node_scripts.go ├── bcache.go ├── bcache_cache_sets.go ├── discoveries.go ├── account.go ├── dns_resource_record.go ├── dns_resource_records.go ├── boot_source_selections.go ├── raid.go ├── license_key.go ├── boot_resources.go ├── bcache_cache_set.go ├── device.go ├── block_device_partitions.go ├── boot_source_selection.go ├── tag.go ├── vm_host.go ├── rack_controllers.go ├── resource_pool.go ├── subnet.go ├── volume_group.go ├── machines.go ├── node_script.go ├── network_interfaces.go ├── api.go ├── block_device.go ├── network_interface.go ├── block_device_partition.go ├── rack_controller.go └── machine.go ├── .github ├── workflows │ ├── cla-check.yml │ ├── stale-cron.yaml │ ├── release.yml │ └── test.yml ├── dependabot.yml └── pull_request_template.md ├── Makefile ├── .goreleaser.yaml ├── client ├── version.go ├── discovery.go ├── events.go ├── ssh_key.go ├── ssl_key.go ├── boot_resource.go ├── node_results.go ├── node_devices.go ├── user.go ├── maas_server.go ├── spaces.go ├── ssl_keys.go ├── node_device.go ├── tags.go ├── zones.go ├── space.go ├── devices.go ├── fabrics.go ├── ip_ranges.go ├── vlans.go ├── utils.go ├── vm_hosts.go ├── subnets.go ├── zone.go ├── boot_sources.go ├── license_keys.go ├── raids.go ├── static_routes.go ├── resource_pools.go ├── notifications.go ├── bcaches.go ├── fabric.go ├── ip_range.go ├── dns_resources.go ├── utils_test.go ├── users.go ├── domains.go ├── ssh_keys.go ├── static_route.go ├── block_devices.go ├── dns_resource.go ├── package_repositories.go ├── raid.go ├── volume_groups.go ├── boot_source.go ├── vlan.go ├── node_scripts.go ├── bcache_cache_sets.go ├── bcache.go ├── dns_resource_records.go ├── boot_source_selections.go ├── license_key.go ├── dns_resource_record.go ├── domain.go ├── package_repository.go ├── block_device_partitions.go ├── ip_addresses.go ├── notification.go ├── bcache_cache_set.go ├── boot_source_selection.go ├── device.go ├── account.go ├── boot_resources.go ├── tag.go ├── vm_host.go └── resource_pool.go ├── go.mod ├── examples └── clientdemo │ ├── go.mod │ └── main.go ├── RELEASING.md └── README.md /entity/node/node.go: -------------------------------------------------------------------------------- 1 | // Package node contains entities related to nodes. 2 | package node 3 | -------------------------------------------------------------------------------- /entity/event/event.go: -------------------------------------------------------------------------------- 1 | // Package event contains entities related to events. 2 | package event 3 | -------------------------------------------------------------------------------- /entity/subnet/subnet.go: -------------------------------------------------------------------------------- 1 | // Package subnet contains entities related to subnets. 2 | package subnet 3 | -------------------------------------------------------------------------------- /entity/notification/notification.go: -------------------------------------------------------------------------------- 1 | // Package notification contains entities related to notifications. 2 | package notification 3 | -------------------------------------------------------------------------------- /test/testdata/maas/zone.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "default", 3 | "description": "", 4 | "id": 1, 5 | "resource_uri": "/MAAS/api/2.0/zones/default/" 6 | } -------------------------------------------------------------------------------- /api/discovery.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | type Discovery interface { 6 | Get(id string) (*entity.Discovery, error) 7 | } 8 | -------------------------------------------------------------------------------- /test/testdata/maas/resource_pool.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "default", 3 | "description": "Default pool", 4 | "id": 0, 5 | "resource_uri": "/MAAS/api/2.0/resourcepool/0/" 6 | } -------------------------------------------------------------------------------- /entity/ssl_key.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type SSLKey struct { 4 | Key string `json:"key,omitempty"` 5 | ResourceURI string `json:"resource_uri,omitempty"` 6 | ID int `json:"id,omitempty"` 7 | } 8 | -------------------------------------------------------------------------------- /test/testdata/maas/resource_pools.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "default", 4 | "description": "Default pool", 5 | "id": 0, 6 | "resource_uri": "/MAAS/api/2.0/resourcepool/0/" 7 | } 8 | ] -------------------------------------------------------------------------------- /api/events.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // Events is an interface for node events 6 | type Events interface { 7 | Get(params *entity.EventParams) (*entity.EventsResp, error) 8 | } 9 | -------------------------------------------------------------------------------- /entity/version.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type Version struct { 4 | Subversion string `json:"subversion,omitempty"` 5 | Version string `json:"version,omitempty"` 6 | Capabilities []string `json:"capabilities,omitempty"` 7 | } 8 | -------------------------------------------------------------------------------- /entity/subnet/reserved_ip_range.go: -------------------------------------------------------------------------------- 1 | package subnet 2 | 3 | // ReservedIPRange represents an IP range from a Subnet's GetReservedIPRanges() 4 | type ReservedIPRange struct { 5 | Purpose []string `json:"purpose,omitempty"` 6 | IPRange 7 | } 8 | -------------------------------------------------------------------------------- /api/version.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Version is an interface for listing MAAS version details 8 | type Version interface { 9 | Get() (*entity.Version, error) 10 | } 11 | -------------------------------------------------------------------------------- /api/maas_server.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // MAASServer represents the MAAS Server endpoint for changing global configuration settings 4 | type MAASServer interface { 5 | Get(name string) (value []byte, err error) 6 | Post(name, value string) error 7 | } 8 | -------------------------------------------------------------------------------- /.github/workflows/cla-check.yml: -------------------------------------------------------------------------------- 1 | name: cla-check 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | cla-check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Check if CLA signed 10 | uses: canonical/has-signed-canonical-cla@v2 11 | -------------------------------------------------------------------------------- /.github/workflows/stale-cron.yaml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | uses: canonical/maas-github-workflows/.github/workflows/stale-cron.yaml@v0 9 | secrets: inherit 10 | -------------------------------------------------------------------------------- /entity/ssh_key.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type SSHKey struct { 4 | Key string `json:"key,omitempty"` 5 | Keysource string `json:"keysource,omitempty"` 6 | ResourceURI string `json:"resource_uri,omitempty"` 7 | ID int `json:"id,omitempty"` 8 | } 9 | -------------------------------------------------------------------------------- /api/node_results.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // NodeResults is an interface for node script results 6 | type NodeResults interface { 7 | Get(systemID string, params *entity.NodeResultParams) ([]entity.NodeResult, error) 8 | } 9 | -------------------------------------------------------------------------------- /api/node_devices.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // NodeDevices is an interface for listing Node Devices objects 6 | type NodeDevices interface { 7 | Get(systemID string, param *entity.NodeDeviceParams) ([]entity.NodeDevice, error) 8 | } 9 | -------------------------------------------------------------------------------- /api/ssh_key.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // SSHKey is an interface defining API behaviour for SSHKey objects 8 | type SSHKey interface { 9 | Get(id int) (*entity.SSHKey, error) 10 | Delete(id int) error 11 | } 12 | -------------------------------------------------------------------------------- /api/ssl_key.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // SSLKey is an interface defining API behaviour for SSLKey objects 8 | type SSLKey interface { 9 | Get(id int) (*entity.SSLKey, error) 10 | Delete(id int) error 11 | } 12 | -------------------------------------------------------------------------------- /api/user.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // User is an interface for getting and deleting users 6 | type User interface { 7 | Get(userName string) (*entity.User, error) 8 | Delete(params *entity.UserDeleteParams) error 9 | } 10 | -------------------------------------------------------------------------------- /api/spaces.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Spaces is an interface for listing and creating Space objects 8 | type Spaces interface { 9 | Get() ([]entity.Space, error) 10 | Create(name string) (*entity.Space, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/subnets.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Subnets represents the MAAS Subnets endpoint 8 | type Subnets interface { 9 | Get() ([]entity.Subnet, error) 10 | Create(params *entity.SubnetParams) (*entity.Subnet, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/tags.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Tags is an interface for listing and creating Tag objects 8 | type Tags interface { 9 | Get() ([]entity.Tag, error) 10 | Create(tagParams *entity.TagParams) (*entity.Tag, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/ssl_keys.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // SSLKeys is an interface for listing and creating SSLKey objects 8 | type SSLKeys interface { 9 | Get() ([]entity.SSLKey, error) 10 | Create(key string) (*entity.SSLKey, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/zones.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Zones is an interface for listing and creating Zone objects 8 | type Zones interface { 9 | Get() ([]entity.Zone, error) 10 | Create(params *entity.ZoneParams) (*entity.Zone, error) 11 | } 12 | -------------------------------------------------------------------------------- /entity/notification/categories.go: -------------------------------------------------------------------------------- 1 | package notification 2 | 3 | // Category correlates to a MAAS notification category. 4 | type Category string 5 | 6 | const ( 7 | ERROR Category = "error" 8 | WARNING Category = "warning" 9 | SUCCESS Category = "success" 10 | INFO Category = "info" 11 | ) 12 | -------------------------------------------------------------------------------- /api/vlans.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // VLANs represents the MAAS Vlans endpoint 8 | type VLANs interface { 9 | Get(fabricID int) ([]entity.VLAN, error) 10 | Create(fabricID int, params *entity.VLANParams) (*entity.VLAN, error) 11 | } 12 | -------------------------------------------------------------------------------- /entity/zone_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestZonet(t *testing.T) { 10 | zone := new(Zone) 11 | if err := helper.TestdataFromJSON("maas/zone.json", zone); err != nil { 12 | t.Fatal(err) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /api/boot_resource.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BootResource is an interface defining API behaviour for 8 | // boot resources 9 | type BootResource interface { 10 | Get(id int) (*entity.BootResource, error) 11 | Delete(id int) error 12 | } 13 | -------------------------------------------------------------------------------- /api/node_device.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // NodeDevice is an interface defining API behaviour for Node Devices 6 | type NodeDevice interface { 7 | Get(systemID string, id int) (*entity.NodeDevice, error) 8 | Delete(systemID string, id int) error 9 | } 10 | -------------------------------------------------------------------------------- /api/users.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // Users is an interface for listing and creating users 6 | type Users interface { 7 | Get() ([]entity.User, error) 8 | Create(params *entity.UserParams) (*entity.User, error) 9 | Whoami() (*entity.User, error) 10 | } 11 | -------------------------------------------------------------------------------- /api/vm_hosts.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // VMHosts is an interface for listing and creating VMHost objects 8 | type VMHosts interface { 9 | Get() ([]entity.VMHost, error) 10 | Create(params *entity.VMHostParams) (*entity.VMHost, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/fabrics.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Fabrics is an interface for listing and creating Fabric records 8 | type Fabrics interface { 9 | Get() ([]entity.Fabric, error) 10 | Create(fabricParams *entity.FabricParams) (*entity.Fabric, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/ip_ranges.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // IPRanges is an interface for listing and creating IPRange records 8 | type IPRanges interface { 9 | Get() ([]entity.IPRange, error) 10 | Create(params *entity.IPRangeParams) (*entity.IPRange, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/license_keys.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // LicenseKeys is an interface for listing and creating LicenseKey objects 6 | type LicenseKeys interface { 7 | Get() ([]entity.LicenseKey, error) 8 | Create(params *entity.LicenseKeyParams) (*entity.LicenseKey, error) 9 | } 10 | -------------------------------------------------------------------------------- /entity/domain_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestDomaint(t *testing.T) { 10 | domains := new([]Domain) 11 | if err := helper.TestdataFromJSON("maas/domains.json", domains); err != nil { 12 | t.Fatal(err) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /entity/space.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type Space struct { 4 | Name string `json:"name,omitempty"` 5 | ResourceURI string `json:"resource_uri,omitempty"` 6 | Subnets []Subnet `json:"subnets,omitempty"` 7 | VLANs []VLAN `json:"vlans,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | } 10 | -------------------------------------------------------------------------------- /api/devices.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Devices is an interface for listing and creating 8 | // Device objects 9 | type Devices interface { 10 | Get() ([]entity.Device, error) 11 | Create(deviceParams *entity.DeviceCreateParams) (*entity.Device, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/notifications.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // Notifications is an interface for listing and creating Notification objects 6 | type Notifications interface { 7 | Get() ([]entity.Notification, error) 8 | Create(params *entity.NotificationParams) (*entity.Notification, error) 9 | } 10 | -------------------------------------------------------------------------------- /api/space.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Space is an interface defining API behaviour for 8 | // Space objects 9 | type Space interface { 10 | Get(id int) (*entity.Space, error) 11 | Update(id int, name string) (*entity.Space, error) 12 | Delete(id int) error 13 | } 14 | -------------------------------------------------------------------------------- /api/resource_pools.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // ResourcePools is an interface for listing and creating ResourcePool records 6 | type ResourcePools interface { 7 | Get() ([]entity.ResourcePool, error) 8 | Create(params *entity.ResourcePoolParams) (*entity.ResourcePool, error) 9 | } 10 | -------------------------------------------------------------------------------- /api/boot_sources.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BootSources is an interface for listing and creating 8 | // BootSource objects 9 | type BootSources interface { 10 | Get() ([]entity.BootSource, error) 11 | Create(params *entity.BootSourceParams) (*entity.BootSource, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/zone.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Zone is an interface defining API behaviour for zones 8 | type Zone interface { 9 | Get(name string) (*entity.Zone, error) 10 | Update(name string, params *entity.ZoneParams) (*entity.Zone, error) 11 | Delete(name string) error 12 | } 13 | -------------------------------------------------------------------------------- /entity/subnet/ip_range.go: -------------------------------------------------------------------------------- 1 | package subnet 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // IPRange represents an IP range from a Subnet's GetUnreservedIPRanges() 8 | type IPRange struct { 9 | Start net.IP `json:"start,omitempty"` 10 | End net.IP `json:"end,omitempty"` 11 | NumAddresses int `json:"num_addresses,omitempty"` 12 | } 13 | -------------------------------------------------------------------------------- /api/domains.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Domains is an interface for listing and creaing 8 | // Domain records 9 | type Domains interface { 10 | Get() ([]entity.Domain, error) 11 | Create(params *entity.DomainParams) (*entity.Domain, error) 12 | SetSerial(serial int) error 13 | } 14 | -------------------------------------------------------------------------------- /api/ip_range.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // IPRange is an interface defining API behaviour for IP ranges 8 | type IPRange interface { 9 | Get(id int) (*entity.IPRange, error) 10 | Update(id int, params *entity.IPRangeParams) (*entity.IPRange, error) 11 | Delete(id int) error 12 | } 13 | -------------------------------------------------------------------------------- /api/ssh_keys.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // SSHKeys is an interface for listing and creating SSHKey objects 8 | type SSHKeys interface { 9 | Get() ([]entity.SSHKey, error) 10 | Create(key string) (*entity.SSHKey, error) 11 | Import(keysource string) ([]entity.SSHKey, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/static_routes.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // StaticRoutes is an interface for listing and creating 8 | // StaticRoutes records 9 | type StaticRoutes interface { 10 | Get() ([]entity.StaticRoute, error) 11 | Create(params *entity.StaticRouteParams) (*entity.StaticRoute, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/bcaches.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BCaches declares the API operations related to MAAS `nodes/{systemID}/bcaches` endpoint. 8 | type BCaches interface { 9 | Get(systemID string) ([]entity.BCache, error) 10 | Create(systemID string, params *entity.BCacheParams) (*entity.BCache, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/fabric.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Fabric is an interface defining API behaviour for 8 | // fabric objects 9 | type Fabric interface { 10 | Get(id int) (*entity.Fabric, error) 11 | Update(id int, fabricParams *entity.FabricParams) (*entity.Fabric, error) 12 | Delete(id int) error 13 | } 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TEST?=$$(go list ./... | grep -v 'vendor') 2 | 3 | .PHONY: test 4 | test: 5 | go test $(TEST) -v $(TESTARGS) -timeout=5m -parallel=4 6 | 7 | .PHONY: lint 8 | lint: lint-go 9 | 10 | .PHONY: lint-go 11 | lint-go: 12 | golangci-lint run $(if $(LINT_AUTOFIX),--fix,) ./... 13 | 14 | .PHONY: lint-go-fix 15 | lint-go-fix: LINT_AUTOFIX=true 16 | lint-go-fix: lint-go 17 | -------------------------------------------------------------------------------- /test/testdata/maas/subnets/statistics.json: -------------------------------------------------------------------------------- 1 | { 2 | "num_available": 232, 3 | "largest_available": 41, 4 | "num_unavailable": 22, 5 | "total_addresses": 254, 6 | "usage": 0.08661417322834646, 7 | "usage_string": "9%", 8 | "available_string": "91%", 9 | "first_address": "172.16.1.1", 10 | "last_address": "172.16.1.254", 11 | "ip_version": 4 12 | } -------------------------------------------------------------------------------- /api/boot_source.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BootSource is an interface defining API behaviour for 8 | // boot sources 9 | type BootSource interface { 10 | Get(id int) (*entity.BootSource, error) 11 | Update(id int, params *entity.BootSourceParams) (*entity.BootSource, error) 12 | Delete(id int) error 13 | } 14 | -------------------------------------------------------------------------------- /api/raids.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // RAIDs declares the API operations related to MAAS `nodes/{systemID}/raids` endpoint. 8 | type RAIDs interface { 9 | Get(systemID string) (raids []entity.RAID, err error) 10 | Create(systemID string, params *entity.RAIDCreateParams) (raid *entity.RAID, err error) 11 | } 12 | -------------------------------------------------------------------------------- /api/vlan.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // VLAN is an interface defining API behaviour for VLAN objects 8 | type VLAN interface { 9 | Get(fabricID int, vid int) (*entity.VLAN, error) 10 | Update(fabricID int, vid int, params *entity.VLANParams) (*entity.VLAN, error) 11 | Delete(fabricID int, vid int) error 12 | } 13 | -------------------------------------------------------------------------------- /api/block_devices.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BlockDevices is an interface for listing and creating 8 | // BlockDevice records 9 | type BlockDevices interface { 10 | Get(systemID string) ([]entity.BlockDevice, error) 11 | Create(systemID string, params *entity.BlockDeviceParams) (*entity.BlockDevice, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/dns_resource.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // DNSResource is an interface defining API behaviour 8 | // for DNS resources 9 | type DNSResource interface { 10 | Get(id int) (*entity.DNSResource, error) 11 | Update(id int, params *entity.DNSResourceParams) (*entity.DNSResource, error) 12 | Delete(id int) error 13 | } 14 | -------------------------------------------------------------------------------- /api/dns_resources.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // DNSResources is an interface for listing and creating 8 | // DNSResource records 9 | type DNSResources interface { 10 | Get(params *entity.DNSResourcesParams) ([]entity.DNSResource, error) 11 | Create(params *entity.DNSResourceParams) (*entity.DNSResource, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/static_route.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // StaticRoute is an interface defining API behavior for 8 | // StaticRoute objects 9 | type StaticRoute interface { 10 | Get(id int) (*entity.StaticRoute, error) 11 | Update(id int, params *entity.StaticRouteParams) (*entity.StaticRoute, error) 12 | Delete(id int) error 13 | } 14 | -------------------------------------------------------------------------------- /api/volume_groups.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // VolumeGroups is an interface for listing and creating 8 | // VolumeGroup records 9 | type VolumeGroups interface { 10 | Get(systemID string) ([]entity.VolumeGroup, error) 11 | Create(systemID string, params *entity.VolumeGroupCreateParams) (*entity.VolumeGroup, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/package_repositories.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // PackageRepositories is an interface for listing and creating 8 | // Package Repository records 9 | type PackageRepositories interface { 10 | Get() ([]entity.PackageRepository, error) 11 | Create(params *entity.PackageRepositoryParams) (*entity.PackageRepository, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/domain.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Domain is an interface defining API behaviour for 8 | // domain objects 9 | type Domain interface { 10 | Get(id int) (*entity.Domain, error) 11 | SetDefault(id int) (*entity.Domain, error) 12 | Update(id int, params *entity.DomainParams) (*entity.Domain, error) 13 | Delete(id int) error 14 | } 15 | -------------------------------------------------------------------------------- /api/ip_addresses.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // IPAddresses is an interface defining API behaviour for IP addresses 6 | type IPAddresses interface { 7 | Get(params *entity.IPAddressesParams) ([]entity.IPAddress, error) 8 | Release(params *entity.IPAddressesParams) error 9 | Reserve(params *entity.IPAddressesParams) (*entity.IPAddress, error) 10 | } 11 | -------------------------------------------------------------------------------- /test/testdata/maas/vlan.json: -------------------------------------------------------------------------------- 1 | { 2 | "vid": 10, 3 | "mtu": 1500, 4 | "dhcp_on": false, 5 | "external_dhcp": null, 6 | "relay_vlan": null, 7 | "fabric_id": 0, 8 | "name": "10", 9 | "primary_rack": "7xtf67", 10 | "space": "internal", 11 | "secondary_rack": "76y7pg", 12 | "id": 5002, 13 | "fabric": "fabric-0", 14 | "resource_uri": "/MAAS/api/2.0/vlans/5002/" 15 | } -------------------------------------------------------------------------------- /api/notification.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // Notification is an interface defining API behavior for 6 | // Notification objects 7 | type Notification interface { 8 | Get(id int) (*entity.Notification, error) 9 | Update(id int, params *entity.NotificationParams) (*entity.Notification, error) 10 | Delete(id int) error 11 | Dismiss(id int) error 12 | } 13 | -------------------------------------------------------------------------------- /api/package_repository.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // PackageRepository represents the MAAS Server Package Repository endpoint 8 | type PackageRepository interface { 9 | Get(id int) (*entity.PackageRepository, error) 10 | Update(id int, params *entity.PackageRepositoryParams) (*entity.PackageRepository, error) 11 | Delete(id int) error 12 | } 13 | -------------------------------------------------------------------------------- /api/node_scripts.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // NodeScripts is an interface for listing and creating 8 | // node script objects 9 | type NodeScripts interface { 10 | Get(nodeScriptParams *entity.NodeScriptReadParams) ([]entity.NodeScript, error) 11 | Create(nodeScriptParams *entity.NodeScriptParams, script []byte) (*entity.NodeScript, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/bcache.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BCache declares the API operations related to MAAS `nodes/{systemID}/bcache/{id}` endpoint. 8 | type BCache interface { 9 | Get(systemID string, id int) (*entity.BCache, error) 10 | Update(systemID string, id int, params *entity.BCacheParams) (*entity.BCache, error) 11 | Delete(systemID string, id int) error 12 | } 13 | -------------------------------------------------------------------------------- /api/bcache_cache_sets.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BCacheCacheSets declares the API operations related to MAAS `nodes/{systemID}/bcache-cache-sets` endpoint. 8 | type BCacheCacheSets interface { 9 | Get(systemID string) ([]entity.BCacheCacheSet, error) 10 | Create(systemID string, params *entity.BCacheCacheSetParams) (*entity.BCacheCacheSet, error) 11 | } 12 | -------------------------------------------------------------------------------- /api/discoveries.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | type Discoveries interface { 6 | Get() ([]entity.Discovery, error) 7 | GetByUnknownIP() ([]entity.Discovery, error) 8 | GetByUnknownMAC() ([]entity.Discovery, error) 9 | GetByUnknownIpAndMAC() ([]entity.Discovery, error) 10 | Clear(params *entity.DiscoveryClearParams) error 11 | ClearByMACAndIP(mac, ip string) error 12 | } 13 | -------------------------------------------------------------------------------- /api/account.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // Account is an interface for managing user account 6 | type Account interface { 7 | CreateAuthorisationToken(name string) (*entity.AuthorisationToken, error) 8 | DeleteAuthorisationToken(key string) error 9 | ListAuthorisationTokens() ([]entity.AuthorisationTokenListItem, error) 10 | UpdateTokenName(name, token string) error 11 | } 12 | -------------------------------------------------------------------------------- /api/dns_resource_record.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // DNSResourceRecord is an interface defining API behaviour for 8 | // DNS resource records 9 | type DNSResourceRecord interface { 10 | Get(id int) (*entity.DNSResourceRecord, error) 11 | Update(id int, params *entity.DNSResourceRecordParams) (*entity.DNSResourceRecord, error) 12 | Delete(id int) error 13 | } 14 | -------------------------------------------------------------------------------- /api/dns_resource_records.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // DNSResourceRecords is an interface for listing and creaing 8 | // DNSResourceRecord records 9 | type DNSResourceRecords interface { 10 | Get(params *entity.DNSResourceRecordsParams) ([]entity.DNSResourceRecord, error) 11 | Create(params *entity.DNSResourceRecordParams) (*entity.DNSResourceRecord, error) 12 | } 13 | -------------------------------------------------------------------------------- /entity/license_key.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type LicenseKey struct { 4 | DistroSeries string `json:"distro_series"` 5 | LicenseKey string `json:"license_key"` 6 | OSystem string `json:"osystem"` 7 | ResourceURI string `json:"resource_uri"` 8 | } 9 | 10 | type LicenseKeyParams struct { 11 | DistroSeries string `url:"distro_series"` 12 | LicenseKey string `url:"license_key"` 13 | OSystem string `url:"osystem"` 14 | } 15 | -------------------------------------------------------------------------------- /api/boot_source_selections.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BootSourceSelections is an interface for listing and creating 8 | // BootSourceSelection objects 9 | type BootSourceSelections interface { 10 | Get(bootSourceID int) ([]entity.BootSourceSelection, error) 11 | Create(bootSourceID int, params *entity.BootSourceSelectionParams) (*entity.BootSourceSelection, error) 12 | } 13 | -------------------------------------------------------------------------------- /api/raid.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // RAID declares the API operations related to MAAS `nodes/{systemID}/raid/{id}` endpoint. 8 | type RAID interface { 9 | Get(systemID string, id int) (raid *entity.RAID, err error) 10 | Update(systemID string, id int, params *entity.RAIDUpdateParams) (raid *entity.RAID, err error) 11 | Delete(systemID string, id int) (err error) 12 | } 13 | -------------------------------------------------------------------------------- /api/license_key.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // LicenseKey is an interface defining API behavior for 6 | // LicenseKey objects 7 | type LicenseKey interface { 8 | Get(osystem string, distroSeries string) (*entity.LicenseKey, error) 9 | Update(osystem string, distroSeries string, params *entity.LicenseKeyParams) (*entity.LicenseKey, error) 10 | Delete(osystem string, distroSeries string) error 11 | } 12 | -------------------------------------------------------------------------------- /entity/zone.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // Zone represents the MAAS Zone endpoint 4 | type Zone struct { 5 | Name string `json:"name,omitempty"` 6 | Description string `json:"description,omitempty"` 7 | ResourceURI string `json:"resource_uri,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | } 10 | 11 | type ZoneParams struct { 12 | Name string `url:"name,omitempty"` 13 | Description string `url:"description,omitempty"` 14 | } 15 | -------------------------------------------------------------------------------- /entity/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | // LogLevel correlates to a log level for MAAS events. 4 | type LogLevel string 5 | 6 | // Log levels referring from MAAS server https://github.com/maas/maas/blob/master/src/maasserver/models/eventtype.py#L25 7 | const ( 8 | AUDIT LogLevel = "AUDIT" 9 | CRITICAL LogLevel = "CRITICAL" 10 | DEBUG LogLevel = "DEBUG" 11 | ERROR LogLevel = "ERROR" 12 | WARNING LogLevel = "WARNING" 13 | INFO LogLevel = "INFO" 14 | ) 15 | -------------------------------------------------------------------------------- /api/boot_resources.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BootResources is an interface for listing and creating 8 | // BootResource objects 9 | type BootResources interface { 10 | Get(params *entity.BootResourcesReadParams) ([]entity.BootResource, error) 11 | Create(params *entity.BootResourceParams) (*entity.BootResource, error) 12 | Import() error 13 | IsImporting() (bool, error) 14 | StopImport() error 15 | } 16 | -------------------------------------------------------------------------------- /api/bcache_cache_set.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BCacheCacheSet declares the API operations related to MAAS `nodes/{systemID}/bcache-cache-set/{id}` endpoint. 8 | type BCacheCacheSet interface { 9 | Get(systemID string, id int) (*entity.BCacheCacheSet, error) 10 | Update(systemID string, id int, params *entity.BCacheCacheSetParams) (*entity.BCacheCacheSet, error) 11 | Delete(systemID string, id int) error 12 | } 13 | -------------------------------------------------------------------------------- /api/device.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Device is an interface defining API behaviour for 8 | // devices 9 | type Device interface { 10 | Get(systemID string) (*entity.Device, error) 11 | Update(systemID string, deviceParams *entity.DeviceUpdateParams) (*entity.Device, error) 12 | Delete(systemID string) error 13 | SetWorkloadAnnotations(systemID string, params map[string]string) (*entity.Device, error) 14 | } 15 | -------------------------------------------------------------------------------- /api/block_device_partitions.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BlockDevicePartitions is an interface for listing and creating 8 | // BlockDevicePartition records 9 | type BlockDevicePartitions interface { 10 | Get(systemID string, blockDeviceID int) ([]entity.BlockDevicePartition, error) 11 | Create(systemID string, blockDeviceID int, params *entity.BlockDevicePartitionParams) (*entity.BlockDevicePartition, error) 12 | } 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See GitHub's documentation for more information on this file: 2 | # https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates 3 | version: 2 4 | updates: 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "daily" 9 | - package-ecosystem: "gomod" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | -------------------------------------------------------------------------------- /api/boot_source_selection.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BootSourceSelection is an interface defining API behaviour for 8 | // boot source selections 9 | type BootSourceSelection interface { 10 | Get(bootSourceID int, id int) (*entity.BootSourceSelection, error) 11 | Update(bootSourceID int, id int, params *entity.BootSourceSelectionParams) (*entity.BootSourceSelection, error) 12 | Delete(bootSourceID int, id int) error 13 | } 14 | -------------------------------------------------------------------------------- /entity/node_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestNodet(t *testing.T) { 10 | node := new(Node) 11 | nodes := new([]Node) 12 | 13 | // Unmarshal sample data into the types 14 | if err := helper.TestdataFromJSON("maas/node.json", node); err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | if err := helper.TestdataFromJSON("maas/nodes.json", nodes); err != nil { 19 | t.Fatal(err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /entity/resource_pool.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // ResourcePool represents the MAAS ResourcePool endpoint 4 | type ResourcePool struct { 5 | Name string `json:"name,omitempty"` 6 | Description string `json:"description,omitempty"` 7 | ResourceURI string `json:"resource_uri,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | } 10 | 11 | type ResourcePoolParams struct { 12 | Name string `url:"name,omitempty"` 13 | Description string `url:"description,omitempty"` 14 | } 15 | -------------------------------------------------------------------------------- /api/tag.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Tag is an interface defining API behaviour for Tag objects 8 | type Tag interface { 9 | Get(name string) (*entity.Tag, error) 10 | Update(name string, tagParams *entity.TagParams) (*entity.Tag, error) 11 | Delete(name string) error 12 | GetMachines(name string) ([]entity.Machine, error) 13 | AddMachines(name string, machineIds []string) error 14 | RemoveMachines(name string, machineIds []string) error 15 | } 16 | -------------------------------------------------------------------------------- /entity/fabric.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type Fabric struct { 4 | Name string `json:"name,omitempty"` 5 | ClassType string `json:"class_type,omitempty"` 6 | ResourceURI string `json:"resource_uri,omitempty"` 7 | VLANs []VLAN `json:"vlans,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | } 10 | 11 | type FabricParams struct { 12 | Name string `url:"name,omitempty"` 13 | Description string `url:"description,omitempty"` 14 | ClassType string `url:"class_type,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /entity/vm_host_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestVMHostt(t *testing.T) { 10 | vmHost := new(VMHost) 11 | vmHosts := new([]VMHost) 12 | 13 | // Unmarshal sample data into the types 14 | if err := helper.TestdataFromJSON("maas/vm_host.json", vmHost); err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | if err := helper.TestdataFromJSON("maas/vm_hosts.json", vmHosts); err != nil { 19 | t.Fatal(err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api/vm_host.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // VMHost is an interface defining API behaviour for VMHost objects 8 | type VMHost interface { 9 | Get(id int) (*entity.VMHost, error) 10 | Update(id int, params *entity.VMHostParams) (*entity.VMHost, error) 11 | Delete(id int) error 12 | Compose(id int, params *entity.VMHostMachineParams) (*entity.Machine, error) 13 | Refresh(id int) (*entity.VMHost, error) 14 | GetParameters(id int) (map[string]string, error) 15 | } 16 | -------------------------------------------------------------------------------- /api/rack_controllers.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // RackControllers represents the MAAS Rack Controllers endpoint 8 | type RackControllers interface { 9 | DescribePowerTypes() ([]entity.PowerType, error) 10 | IsRegistered(macAddress string) (bool, error) 11 | GetPowerParameters(systemIDs []string) (map[string]interface{}, error) 12 | Get(*entity.RackControllersGetParams) ([]entity.RackController, error) 13 | SetZone(*entity.RackControllerSetZoneParams) error 14 | } 15 | -------------------------------------------------------------------------------- /entity/bcache_cache_set.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type BCacheCacheSet struct { 4 | Name string `json:"name:omitempty"` 5 | SystemID string `json:"system_id,omitempty"` 6 | ResourceURI string `json:"resource_uri,omitempty"` 7 | CacheDevice BlockDevice `json:"cache_device,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | } 10 | 11 | type BCacheCacheSetParams struct { 12 | CacheDevice string `url:"cache_device,omitempty"` 13 | CachePartition string `url:"cache_partition,omitempty"` 14 | } 15 | -------------------------------------------------------------------------------- /entity/block_device_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestBlockDevicet(t *testing.T) { 10 | device := new(BlockDevice) 11 | devices := new([]BlockDevice) 12 | 13 | // Unmarshal sample data into the types 14 | if err := helper.TestdataFromJSON("maas/block_device.json", device); err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | if err := helper.TestdataFromJSON("maas/block_devices.json", devices); err != nil { 19 | t.Fatal(err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /entity/resource_pool_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestResourcePoolt(t *testing.T) { 10 | pool := new(ResourcePool) 11 | pools := new([]ResourcePool) 12 | 13 | // Unmarshal sample data into the types 14 | if err := helper.TestdataFromJSON("maas/resource_pool.json", pool); err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | if err := helper.TestdataFromJSON("maas/resource_pools.json", pools); err != nil { 19 | t.Fatal(err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | builds: 5 | - skip: true 6 | release: 7 | name_template: "v{{ .Version }}" 8 | draft: true 9 | changelog: 10 | sort: asc 11 | use: github 12 | filters: 13 | exclude: 14 | - '^docs:' 15 | - '^test:' 16 | groups: 17 | - title: Features 18 | regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' 19 | order: 0 20 | - title: "Bug fixes" 21 | regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' 22 | order: 1 23 | - title: Others 24 | order: 999 25 | -------------------------------------------------------------------------------- /entity/tag.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type Tag struct { 4 | Name string `json:"name,omitempty"` 5 | Definition string `json:"definition,omitempty"` 6 | Comment string `json:"comment,omitempty"` 7 | KernelOpts string `json:"kernel_opts,omitempty"` 8 | ResourceURI string `json:"resource_uri,omitempty"` 9 | } 10 | 11 | type TagParams struct { 12 | Name string `url:"name"` 13 | Definition string `url:"definition,omitempty"` 14 | Comment string `url:"comment,omitempty"` 15 | KernelOpts string `url:"kernel_opts,omitempty"` 16 | } 17 | -------------------------------------------------------------------------------- /entity/volume_group_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestVolumeGroup(t *testing.T) { 10 | volumeGroup := new(VolumeGroup) 11 | volumeGroups := new([]VolumeGroup) 12 | 13 | // Unmarshal sample data into the types 14 | if err := helper.TestdataFromJSON("maas/volume_group.json", volumeGroup); err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | if err := helper.TestdataFromJSON("maas/volume_groups.json", volumeGroups); err != nil { 19 | t.Fatal(err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/testdata/maas/interface.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eth0", 3 | "children": [ 4 | "newbond" 5 | ], 6 | "mac_address": "00:01:02:03:04:55", 7 | "links": [], 8 | "product": null, 9 | "parents": [], 10 | "enabled": true, 11 | "vlan": null, 12 | "firmware_version": null, 13 | "system_id": "thr3am", 14 | "tags": [], 15 | "params": {}, 16 | "type": "physical", 17 | "discovered": [], 18 | "effective_mtu": 1500, 19 | "vendor": null, 20 | "id": 138, 21 | "resource_uri": "/MAAS/api/2.0/nodes/thr3am/interfaces/138/" 22 | } -------------------------------------------------------------------------------- /api/resource_pool.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // ResourcePool is an interface defining API behaviour for ResourcePool 8 | // objects 9 | type ResourcePool interface { 10 | Get(id int) (*entity.ResourcePool, error) 11 | Update(id int, params *entity.ResourcePoolParams) (*entity.ResourcePool, error) 12 | Delete(id int) error 13 | GetByName(name string) (*entity.ResourcePool, error) 14 | UpdateByName(name string, params *entity.ResourcePoolParams) (*entity.ResourcePool, error) 15 | DeleteByName(name string) error 16 | } 17 | -------------------------------------------------------------------------------- /entity/account.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // AuthorisationToken represents user account authorisation Token 4 | type AuthorisationToken struct { 5 | Name string `json:"name,omitempty"` 6 | TokenKey string `json:"token_key,omitempty"` 7 | TokenSecret string `json:"token_secret,omitempty"` 8 | ConsumerKey string `json:"consumer_key,omitempty"` 9 | } 10 | 11 | // AuthorisationTokenListItem represents user account authorisation token in list API 12 | type AuthorisationTokenListItem struct { 13 | Name string `json:"name,omitempty"` 14 | Token string `json:"token,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /entity/rack_controller_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | ) 8 | 9 | func TestRackController(t *testing.T) { 10 | rackController := new(RackController) 11 | rackControllers := new([]RackController) 12 | 13 | // Unmarshal sample data into the types 14 | if err := helper.TestdataFromJSON("maas/rack_controller.json", rackController); err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | if err := helper.TestdataFromJSON("maas/rack_controllers.json", rackControllers); err != nil { 19 | t.Fatal(err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api/subnet.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | "github.com/canonical/gomaasclient/entity/subnet" 6 | ) 7 | 8 | // Subnet represents the MAAS Subnet endpoint 9 | type Subnet interface { 10 | Delete(id int) error 11 | Get(id int) (*entity.Subnet, error) 12 | GetIPAddresses(id int) ([]subnet.IPAddress, error) 13 | GetReservedIPRanges(id int) ([]subnet.ReservedIPRange, error) 14 | GetStatistics(id int) (*subnet.Statistics, error) 15 | GetUnreservedIPRanges(id int) ([]subnet.IPRange, error) 16 | Update(id int, params *entity.SubnetParams) (*entity.Subnet, error) 17 | } 18 | -------------------------------------------------------------------------------- /api/volume_group.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // VolumeGroup is an interface providing API behaviour for 8 | // volume groups 9 | type VolumeGroup interface { 10 | Get(systemID string, id int) (*entity.VolumeGroup, error) 11 | Update(systemID string, id int, params *entity.VolumeGroupUpdateParams) (*entity.VolumeGroup, error) 12 | Delete(systemID string, id int) error 13 | CreateLogicalVolume(systemID string, id int, params *entity.LogicalVolumeParams) (*entity.BlockDevice, error) 14 | DeleteLogicalVolume(ssytemID string, id int, logicalVolumeID int) error 15 | } 16 | -------------------------------------------------------------------------------- /entity/static_route.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type StaticRoute struct { 4 | GatewayIP string `json:"gateway_ip,omitempty"` 5 | ResourceURI string `json:"resource_uri,omitempty"` 6 | Destination Subnet `json:"destination,omitempty"` 7 | Source Subnet `json:"source,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | Metric int `json:"metric,omitempty"` 10 | } 11 | 12 | type StaticRouteParams struct { 13 | Source string `url:"source,omitempty"` 14 | Destination string `url:"destination,omitempty"` 15 | GatewayIP string `url:"gateway_ip,omitempty"` 16 | Metric int `url:"metric,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /client/version.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | ) 9 | 10 | // Version implements api.Version 11 | type Version struct { 12 | APIClient APIClient 13 | } 14 | 15 | func (v *Version) client() APIClient { 16 | return v.APIClient.GetSubObject("version") 17 | } 18 | 19 | // Get fetches MAAS version details 20 | func (v *Version) Get() (*entity.Version, error) { 21 | version := new(entity.Version) 22 | err := v.client().Get("", url.Values{}, func(data []byte) error { 23 | return json.Unmarshal(data, version) 24 | }) 25 | 26 | return version, err 27 | } 28 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description of changes 2 | 3 | *A clear and concise description of your changes here.* 4 | 5 | ## Issue or ticket link (if applicable) 6 | 7 | *A link to an issue or ticket that this PR is addressing.* 8 | 9 | ## Checklist 10 | 11 | - [ ] I have written a PR title that follows the advice in DEVELOPMENT.md with the title format `type: title` 12 | - [ ] I have written a description of the changes in the PR that is clear and concise. 13 | - [ ] I have updated the documentation to reflect the changes. 14 | - [ ] I have added or updated the tests to reflect the changes. 15 | - [ ] I have run the unit tests and they pass. 16 | -------------------------------------------------------------------------------- /entity/entity.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package entity defines types for the MAAS API endpoints' return types. 3 | 4 | Each endpoint returns JSON that describes the object it represents. For example, 5 | GETting the Subnets endpoint will return an array of Subnet, while GETting the 6 | Subnet endpoint (ie subnets/) will return one Subnet. 7 | 8 | Some endpoints expose operations that return metadata about the object, such as as 9 | Subnet's GetStatistics(), which contains statistics about the subnet, but does not 10 | actually describe the subnet: these alternative types can be found in subpackages 11 | named after the endpoint. 12 | */ 13 | package entity 14 | -------------------------------------------------------------------------------- /api/machines.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Machines is an interface for listing, creating, allocating, 8 | // bulk-accepting enlistments and releasing Machine records 9 | type Machines interface { 10 | Get(machinesParams *entity.MachinesParams) ([]entity.Machine, error) 11 | Create(machineParams *entity.MachineCreateParams, powerParams map[string]interface{}) (*entity.Machine, error) 12 | Allocate(params *entity.MachineAllocateParams) (*entity.Machine, error) 13 | AcceptAll() error 14 | Release(systemID []string, comment string) error 15 | ListAllocated() ([]entity.Machine, error) 16 | } 17 | -------------------------------------------------------------------------------- /entity/user.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type User struct { 4 | UserName string `json:"username"` 5 | Email string `json:"email"` 6 | ResourceURI string `json:"resource_uri"` 7 | IsSuperUser bool `json:"is_superuser"` 8 | IsLocal bool `json:"is_local"` 9 | } 10 | 11 | type UserParams struct { 12 | UserName string `url:"username"` 13 | Password string `url:"password"` 14 | Email string `url:"email"` 15 | IsSuperUser bool `url:"is_superuser,int"` 16 | } 17 | 18 | type UserDeleteParams struct { 19 | UserName string `url:"username"` 20 | TransferResourcesTo string `url:"transfer_resources_to,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /entity/ip_range.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type IPRange struct { 8 | Type string `json:"type"` 9 | Comment string `json:"comment,omitempty"` 10 | ResourceURI string `json:"resource_uri"` 11 | User User `json:"user,omitempty"` 12 | StartIP net.IP `json:"start_ip"` 13 | EndIP net.IP `json:"end_ip"` 14 | Subnet Subnet `json:"subnet"` 15 | ID int `json:"id"` 16 | } 17 | 18 | type IPRangeParams struct { 19 | Type string `url:"type"` 20 | Subnet string `url:"subnet"` 21 | StartIP string `url:"start_ip"` 22 | EndIP string `url:"end_ip"` 23 | Comment string `url:"comment,omitempty"` 24 | } 25 | -------------------------------------------------------------------------------- /api/node_script.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // NodeScript is an interface defining API behaviour for 8 | // node scripts 9 | type NodeScript interface { 10 | Get(name string, includeScript bool) (*entity.NodeScript, error) 11 | Update(name string, nodeScriptParams *entity.NodeScriptParams, script []byte) (*entity.NodeScript, error) 12 | Delete(name string) error 13 | AddTag(name string, tag string) (*entity.NodeScript, error) 14 | RemoveTag(name string, tag string) (*entity.NodeScript, error) 15 | Download(name string, revision int) ([]byte, error) 16 | Revert(name string, to int) (*entity.NodeScript, error) 17 | } 18 | -------------------------------------------------------------------------------- /entity/boot_source.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type BootSource struct { 4 | Created string `json:"created,omitempty"` 5 | Updated string `json:"updated,omitempty"` 6 | URL string `json:"url,omitempty"` 7 | KeyringFilename string `json:"keyring_filename,omitempty"` 8 | KeyringData string `json:"keyring_data,omitempty"` 9 | ResourceURI string `json:"resource_uri,omitempty"` 10 | ID int `json:"id,omitempty"` 11 | } 12 | 13 | type BootSourceParams struct { 14 | KeyringData string `url:"keyring_data,omitempty"` 15 | KeyringFilename string `url:"keyring_filename,omitempty"` 16 | URL string `url:"url,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /entity/domain.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // Domain represents the MAAS Domain endpoint 4 | type Domain struct { 5 | Name string `json:"name,omitempty"` 6 | ResourceURI string `json:"resource_uri,omitempty"` 7 | ID int `json:"id,omitempty"` 8 | TTL int `json:"ttl,omitempty"` 9 | ResourceRecordCount int `json:"resource_record_count,omitempty"` 10 | Authoritative bool `json:"authoritative,omitempty"` 11 | IsDefault bool `json:"is_default,omitempty"` 12 | } 13 | 14 | type DomainParams struct { 15 | Name string `url:"name,omitempty"` 16 | TTL int `url:"ttl,omitempty"` 17 | Authoritative bool `url:"authoritative"` 18 | } 19 | -------------------------------------------------------------------------------- /api/network_interfaces.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // NetworkInterfaces represents the MAAS Server Interfaces endpoint 8 | type NetworkInterfaces interface { 9 | Get(systemID string) ([]entity.NetworkInterface, error) 10 | CreateBond(systemID string, params *entity.NetworkInterfaceBondParams) (*entity.NetworkInterface, error) 11 | CreateBridge(systemID string, params *entity.NetworkInterfaceBridgeParams) (*entity.NetworkInterface, error) 12 | CreatePhysical(systemID string, params *entity.NetworkInterfacePhysicalParams) (*entity.NetworkInterface, error) 13 | CreateVLAN(systemID string, params *entity.NetworkInterfaceVLANParams) (*entity.NetworkInterface, error) 14 | } 15 | -------------------------------------------------------------------------------- /client/discovery.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // Discovery implements api.Discovery 12 | type Discovery struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (d *Discovery) client(id string) APIClient { 17 | return d.APIClient.GetSubObject("discovery"). 18 | GetSubObject(fmt.Sprintf("%v", id)) 19 | } 20 | 21 | // Get discovery by id 22 | func (d *Discovery) Get(id string) (*entity.Discovery, error) { 23 | deviceDiscovery := new(entity.Discovery) 24 | err := d.client(id).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &deviceDiscovery) 26 | }) 27 | 28 | return deviceDiscovery, err 29 | } 30 | -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package api defines an interface to each MAAS API endpoint. 3 | 4 | Each interface correlates to one endpoint, such as Subnets for the Subnets 5 | endpoint (ie /subnets) and Subnet for the Subnet endpoint (eg subnets/). 6 | API clients are expected to implement these interfaces to provide a normalized way 7 | of accessing the MAAS API with normalized results (eg the types defined in the 8 | endpoint package). 9 | 10 | Some endpoint operations require multiple parameters, such as the Rack Controllers 11 | GET operation, which takes a number of QSP that can be used to filter results. These 12 | parameters are encapsulated in the params subpackage, providing a quick reference 13 | for performing API operations. 14 | */ 15 | package api 16 | -------------------------------------------------------------------------------- /client/events.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/canonical/gomaasclient/entity" 7 | "github.com/google/go-querystring/query" 8 | ) 9 | 10 | // Events implements api.Events 11 | type Events struct { 12 | APIClient APIClient 13 | } 14 | 15 | func (e *Events) client() APIClient { 16 | return e.APIClient.GetSubObject("events") 17 | } 18 | 19 | // Get events for nodes 20 | func (e *Events) Get(params *entity.EventParams) (*entity.EventsResp, error) { 21 | qsp, err := query.Values(params) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | events := &entity.EventsResp{} 27 | err = e.client().Get("query", qsp, func(data []byte) error { 28 | return json.Unmarshal(data, events) 29 | }) 30 | 31 | return events, err 32 | } 33 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/canonical/gomaasclient 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/google/go-cmp v0.7.0 7 | github.com/google/go-querystring v1.1.0 8 | github.com/juju/gomaasapi/v2 v2.3.0 9 | github.com/stretchr/testify v1.11.1 10 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 11 | gopkg.in/yaml.v3 v3.0.1 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/juju/collections v1.0.4 // indirect 17 | github.com/juju/errors v1.0.0 // indirect 18 | github.com/juju/loggo v1.0.0 // indirect 19 | github.com/juju/mgo/v2 v2.0.2 // indirect 20 | github.com/juju/schema v1.0.1 // indirect 21 | github.com/juju/version v0.0.0-20210303051006-2015802527a8 // indirect 22 | github.com/pmezard/go-difflib v1.0.0 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /test/testdata/maas/boot_resources.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 95, 4 | "type": "Synced", 5 | "name": "ubuntu/bionic", 6 | "architecture": "amd64/ga-18.04", 7 | "resource_uri": "/MAAS/api/2.0/boot-resources/95/", 8 | "last_deployed": null, 9 | "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04" 10 | }, 11 | { 12 | "id": 13, 13 | "type": "Uploaded", 14 | "name": "alma9", 15 | "architecture": "amd64/generic", 16 | "resource_uri": "/MAAS/api/2.0/boot-resources/13/", 17 | "last_deployed": null, 18 | "title": "Alma 9 Custom", 19 | "subarches": "generic", 20 | "base_image": "rhel/9" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /entity/subnet/statistics.go: -------------------------------------------------------------------------------- 1 | package subnet 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // Statistics represents a Subnet's GetStatistics() 8 | type Statistics struct { 9 | UsageString string `json:"usage_string,omitempty"` 10 | AvailableString string `json:"available_string,omitempty"` 11 | FirstAddress net.IP `json:"first_address,omitempty"` 12 | LastAddress net.IP `json:"last_address,omitempty"` 13 | NumAvailable int `json:"num_available,omitempty"` 14 | LargestAvailable int `json:"largest_available,omitempty"` 15 | NumUnavailable int `json:"num_unavailable,omitempty"` 16 | TotalAddresses int `json:"total_addresses,omitempty"` 17 | Usage float64 `json:"usage,omitempty"` 18 | IPVersion int `json:"ip_version,omitempty"` 19 | } 20 | -------------------------------------------------------------------------------- /test/testdata/maas/domains.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "authoritative": true, 4 | "ttl": null, 5 | "id": 0, 6 | "name": "maas", 7 | "is_default": true, 8 | "resource_record_count": 0, 9 | "resource_uri": "/MAAS/api/2.0/domains/0/" 10 | }, 11 | { 12 | "authoritative": true, 13 | "ttl": null, 14 | "id": 1, 15 | "name": "sample", 16 | "is_default": false, 17 | "resource_record_count": 0, 18 | "resource_uri": "/MAAS/api/2.0/domains/1/" 19 | }, 20 | { 21 | "authoritative": true, 22 | "ttl": null, 23 | "id": 2, 24 | "name": "ubnt", 25 | "is_default": false, 26 | "resource_record_count": 0, 27 | "resource_uri": "/MAAS/api/2.0/domains/2/" 28 | } 29 | ] -------------------------------------------------------------------------------- /entity/boot_source_selection.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type BootSourceSelection struct { 4 | OS string `json:"os,omitempty"` 5 | Release string `json:"release,omitempty"` 6 | ResourceURI string `json:"resource_uri,omitempty"` 7 | Arches []string `json:"arches,omitempty"` 8 | Subarches []string `json:"subarches,omitempty"` 9 | Labels []string `json:"labels,omitempty"` 10 | ID int `json:"id,omitempty"` 11 | BootSourceID int `json:"boot_source_id,omitempty"` 12 | } 13 | 14 | type BootSourceSelectionParams struct { 15 | OS string `url:"os,omitempty"` 16 | Release string `url:"release,omitempty"` 17 | Arches []string `url:"arches,omitempty"` 18 | Subarches []string `url:"subarches,omitempty"` 19 | Labels []string `url:"labels,omitempty"` 20 | } 21 | -------------------------------------------------------------------------------- /client/ssh_key.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // SSHKey implements api.SSHKey 12 | type SSHKey struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (s *SSHKey) client(id int) APIClient { 17 | return s.APIClient.GetSubObject("account/prefs/sshkeys").GetSubObject(fmt.Sprintf("%v", id)) 18 | } 19 | 20 | // Get fetches a given SSHKey 21 | func (s *SSHKey) Get(id int) (*entity.SSHKey, error) { 22 | sshKey := new(entity.SSHKey) 23 | err := s.client(id).Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, sshKey) 25 | }) 26 | 27 | return sshKey, err 28 | } 29 | 30 | // Delete deletes a given SSHKey 31 | func (s *SSHKey) Delete(id int) error { 32 | return s.client(id).Delete() 33 | } 34 | -------------------------------------------------------------------------------- /client/ssl_key.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // SSLKey implements api.SSLKey 12 | type SSLKey struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (s *SSLKey) client(id int) APIClient { 17 | return s.APIClient.GetSubObject("account/prefs/sslkeys").GetSubObject(fmt.Sprintf("%v", id)) 18 | } 19 | 20 | // Get fetches a given SSLKey 21 | func (s *SSLKey) Get(id int) (*entity.SSLKey, error) { 22 | sslKey := new(entity.SSLKey) 23 | err := s.client(id).Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, sslKey) 25 | }) 26 | 27 | return sslKey, err 28 | } 29 | 30 | // Delete deletes a given SSLKey 31 | func (s *SSLKey) Delete(id int) error { 32 | return s.client(id).Delete() 33 | } 34 | -------------------------------------------------------------------------------- /test/testdata/maas/subnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "172.16.5.0/24", 3 | "vlan": { 4 | "vid": 0, 5 | "mtu": 1500, 6 | "dhcp_on": false, 7 | "external_dhcp": null, 8 | "relay_vlan": null, 9 | "fabric_id": 0, 10 | "secondary_rack": "76y7pg", 11 | "id": 5001, 12 | "fabric": "fabric-0", 13 | "name": "untagged", 14 | "space": "management", 15 | "primary_rack": "7xtf67", 16 | "resource_uri": "/MAAS/api/2.0/vlans/5001/" 17 | }, 18 | "cidr": "172.16.5.0/24", 19 | "rdns_mode": 2, 20 | "gateway_ip": null, 21 | "dns_servers": [], 22 | "allow_dns": true, 23 | "allow_proxy": true, 24 | "active_discovery": false, 25 | "managed": true, 26 | "id": 9, 27 | "space": "management", 28 | "resource_uri": "/MAAS/api/2.0/subnets/9/" 29 | } 30 | -------------------------------------------------------------------------------- /entity/node/status_test.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/go-cmp/cmp" 7 | ) 8 | 9 | func TestStatus(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | got Status 13 | want Status 14 | }{ 15 | {name: "new", got: StatusNew, want: 0}, 16 | {name: "default", got: StatusDefault, want: 0}, 17 | {name: "commissioning", got: StatusCommissioning, want: 1}, 18 | {name: "ready", got: StatusReady, want: 4}, 19 | {name: "deployed", got: StatusDeployed, want: 6}, 20 | {name: "deploying", got: StatusDeploying, want: 9}, 21 | {name: "allocated", got: StatusAllocated, want: 10}, 22 | } 23 | 24 | for _, testCase := range tests { 25 | tc := testCase 26 | t.Run(tc.name, func(t *testing.T) { 27 | diff := cmp.Diff(tc.want, tc.got) 28 | if diff != "" { 29 | t.Fatal(diff) 30 | } 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /entity/raid_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestRaid(t *testing.T) { 11 | raid := new(RAID) 12 | 13 | // Unmarshal sample data into the types 14 | if err := helper.TestdataFromJSON("maas/raid.json", raid); err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | t.Run("Devices", func(t *testing.T) { 19 | assert.Equal(t, 0, len(raid.SpareDevices)) 20 | assert.Equal(t, 2, len(raid.Devices)) 21 | assert.Equal(t, 124, raid.Devices[0].ID) 22 | assert.Equal(t, "qkhm8f", raid.Devices[0].SystemID) 23 | }) 24 | 25 | t.Run("VirtualDevice", func(t *testing.T) { 26 | assert.Equal(t, "md0", raid.VirtualDevice.Name) 27 | assert.Equal(t, 63, raid.VirtualDevice.ID) 28 | assert.Equal(t, "qkhm8f", raid.VirtualDevice.SystemID) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /entity/dns_resource_record.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type DNSResourceRecord struct { 4 | RRType string `json:"rrtype,omitempty"` 5 | RRData string `json:"rrdata,omitempty"` 6 | FQDN string `json:"fqdn,omitempty"` 7 | ResourceURI string `json:"resource_uri,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | TTL int `json:"ttl,omitempty"` 10 | } 11 | 12 | type DNSResourceRecordParams struct { 13 | FQDN string `url:"fqdn,omitempty"` 14 | Name string `url:"name,omitempty"` 15 | Domain string `url:"domain,omitempty"` 16 | RRType string `url:"rrtype,omitempty"` 17 | RRData string `url:"rrdata,omitempty"` 18 | TTL int `url:"ttl"` 19 | } 20 | 21 | type DNSResourceRecordsParams struct { 22 | Domain string `url:"domain,omitempty"` 23 | FQDN string `url:"fqdn,omitempty"` 24 | Name string `url:"name,omitempty"` 25 | RRType string `url:"rrtype,omitempty"` 26 | } 27 | -------------------------------------------------------------------------------- /client/boot_resource.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // BootResource implements api.BootResource 12 | type BootResource struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (b *BootResource) client(id int) APIClient { 17 | return b.APIClient.GetSubObject("boot-resources").GetSubObject(fmt.Sprintf("%v", id)) 18 | } 19 | 20 | // Get fetches a boot resource with a given id 21 | func (b *BootResource) Get(id int) (*entity.BootResource, error) { 22 | bootResource := new(entity.BootResource) 23 | err := b.client(id).Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, bootResource) 25 | }) 26 | 27 | return bootResource, err 28 | } 29 | 30 | // Delete deletes a given boot resource 31 | func (b *BootResource) Delete(id int) error { 32 | return b.client(id).Delete() 33 | } 34 | -------------------------------------------------------------------------------- /client/node_results.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // NodeResults implements api.NodeResults 12 | type NodeResults struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (c *NodeResults) client(systemID string) APIClient { 17 | return c.APIClient.GetSubObject("nodes").GetSubObject(fmt.Sprintf("%v", systemID)).GetSubObject("results") 18 | } 19 | 20 | // Get node results 21 | func (c *NodeResults) Get(systemID string, params *entity.NodeResultParams) ([]entity.NodeResult, error) { 22 | qsp, err := query.Values(params) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | results := make([]entity.NodeResult, 0) 28 | err = c.client(systemID).Get("", qsp, func(data []byte) error { 29 | return json.Unmarshal(data, &results) 30 | }) 31 | 32 | return results, err 33 | } 34 | -------------------------------------------------------------------------------- /test/testdata/maas/boot_resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 13, 3 | "type": "Uploaded", 4 | "name": "alma9", 5 | "architecture": "amd64/generic", 6 | "resource_uri": "/MAAS/api/2.0/boot-resources/13/", 7 | "last_deployed": null, 8 | "title": "Alma 9 Custom", 9 | "subarches": "generic", 10 | "base_image": "rhel/9", 11 | "sets": { 12 | "20240828": { 13 | "version": "20240828", 14 | "label": "uploaded", 15 | "size": 935047294, 16 | "complete": true, 17 | "files": { 18 | "root.tgz": { 19 | "filename": "root.tgz", 20 | "filetype": "root-tgz", 21 | "sha256": "5b341be98b05d409a41ce8c2d881dbd80df9de07da7613d6722d4f0b5e99e0f1", 22 | "size": 935047294, 23 | "complete": true 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /entity/ip_address.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "net" 4 | 5 | type IPAddress struct { 6 | AllocTypeName string `json:"alloc_type_name,omitempty"` 7 | Created string `json:"created,omitempty"` 8 | ResourceURI string `json:"resource_uri,omitempty"` 9 | Owner User `json:"owner,omitempty"` 10 | IP net.IP `json:"ip,omitempty"` 11 | InterfaceSet []NetworkInterface `json:"interface_set,omitempty"` 12 | Subnet Subnet `json:"subnet,omitempty"` 13 | AllocType int `json:"alloc_type,omitempty"` 14 | } 15 | 16 | type IPAddressesParams struct { 17 | Subnet string `url:"subnet,omitempty"` 18 | IP string `url:"ip,omitempty"` 19 | Owner string `url:"owner,omitempty"` 20 | All bool `url:"all,omitempty"` 21 | Discovered bool `url:"discovered,omitempty"` 22 | Force bool `url:"force,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /api/block_device.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // BlockDevice is an interface providing API behaviour for 8 | // block devices 9 | type BlockDevice interface { 10 | Get(systemID string, id int) (*entity.BlockDevice, error) 11 | Update(systemID string, id int, params *entity.BlockDeviceParams) (*entity.BlockDevice, error) 12 | Delete(systemID string, id int) error 13 | AddTag(systemID string, id int, tag string) (*entity.BlockDevice, error) 14 | RemoveTag(systemID string, id int, tag string) (*entity.BlockDevice, error) 15 | Format(systemID string, id int, fsType string) (*entity.BlockDevice, error) 16 | Unformat(systemID string, id int) (*entity.BlockDevice, error) 17 | Mount(systemID string, id int, mountPoint string, mountOptions string) (*entity.BlockDevice, error) 18 | Unmount(systemID string, id int) (*entity.BlockDevice, error) 19 | SetBootDisk(systemID string, id int) error 20 | } 21 | -------------------------------------------------------------------------------- /examples/clientdemo/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/clientdemo 2 | 3 | go 1.21.1 4 | 5 | require github.com/canonical/gomaasclient v0.0.0-20230912053103-eb0ecc7ab134 6 | 7 | require ( 8 | github.com/google/go-querystring v1.1.0 // indirect 9 | github.com/juju/collections v1.0.4 // indirect 10 | github.com/juju/errors v1.0.0 // indirect 11 | github.com/juju/gomaasapi/v2 v2.3.0 // indirect 12 | github.com/juju/loggo v1.0.0 // indirect 13 | github.com/juju/mgo/v2 v2.0.2 // indirect 14 | github.com/juju/schema v1.0.1 // indirect 15 | github.com/juju/version v0.0.0-20210303051006-2015802527a8 // indirect 16 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect 17 | gopkg.in/yaml.v3 v3.0.1 // indirect 18 | ) 19 | 20 | // Replace the path after => with the path to your local copy of the repository, if required. 21 | // e.g. /home/username/repos/gomaasclient 22 | replace github.com/canonical/gomaasclient v0.0.0-20230912053103-eb0ecc7ab134 => ../../../gomaasclient 23 | -------------------------------------------------------------------------------- /client/node_devices.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // NodeDevices implements api.NodeDevices 12 | type NodeDevices struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (a *NodeDevices) client(systemID string) APIClient { 17 | return a.APIClient.GetSubObject("nodes"). 18 | GetSubObject(fmt.Sprintf("%v", systemID)). 19 | GetSubObject("devices") 20 | } 21 | 22 | // Get fetches a list of NodeDevice objects 23 | func (a *NodeDevices) Get(systemID string, param *entity.NodeDeviceParams) ([]entity.NodeDevice, error) { 24 | qsp, err := query.Values(param) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | nodeDevices := make([]entity.NodeDevice, 0) 30 | err = a.client(systemID).Get("", qsp, func(data []byte) error { 31 | return json.Unmarshal(data, &nodeDevices) 32 | }) 33 | 34 | return nodeDevices, err 35 | } 36 | -------------------------------------------------------------------------------- /api/network_interface.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // NetworkInterface represents the MAAS Server Interface endpoint 8 | type NetworkInterface interface { 9 | Get(systemID string, id int) (*entity.NetworkInterface, error) 10 | Update(systemID string, id int, params *entity.NetworkInterfaceUpdateParams) (*entity.NetworkInterface, error) 11 | Delete(systemID string, id int) error 12 | Disconnect(systemID string, id int) (*entity.NetworkInterface, error) 13 | AddTag(systemID string, id int, tag string) (*entity.NetworkInterface, error) 14 | RemoveTag(systemID string, id int, tag string) (*entity.NetworkInterface, error) 15 | LinkSubnet(systemID string, id int, params *entity.NetworkInterfaceLinkParams) (*entity.NetworkInterface, error) 16 | UnlinkSubnet(systemID string, id int, linkID int) (*entity.NetworkInterface, error) 17 | SetDefaultGateway(systemID string, id int, linkID int) (*entity.NetworkInterface, error) 18 | } 19 | -------------------------------------------------------------------------------- /examples/clientdemo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "fmt" 7 | 8 | gomaasclient "github.com/canonical/gomaasclient/client" 9 | ) 10 | 11 | const ( 12 | MAAS_URL = "..." // "http://10.10.0.22:5240/MAAS/" 13 | MAAS_API_KEY = "..." //"xxxxxxxxxxxxxxxxx:yyyyyyyyyyyyyyyyyy:zzzzzzzzzzzzzzzzzz" 14 | MAAS_API_VERSION = "2.0" 15 | ) 16 | 17 | func main() { 18 | client, err := gomaasclient.GetClient( 19 | MAAS_URL, 20 | MAAS_API_KEY, 21 | MAAS_API_VERSION, 22 | ) 23 | if err != nil { 24 | log.Fatalf("Error getting client: %v", err) 25 | } 26 | // Get all machines 27 | machines, err := client.Machines.Get(nil) 28 | if err != nil { 29 | log.Fatalf("Error getting machines: %v", err) 30 | } 31 | 32 | // Print machine details 33 | fmt.Println("MAAS Machines:") 34 | fmt.Println("-------------") 35 | for _, machine := range machines { 36 | fmt.Printf("System ID: %s, Hostname: %s\n", 37 | machine.SystemID, machine.Hostname) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/user.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // User implements api.User 13 | type User struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (u *User) client(userName string) APIClient { 18 | return u.APIClient.GetSubObject(fmt.Sprintf("users/%s", userName)) 19 | } 20 | 21 | // Get fetches a User by username 22 | func (u *User) Get(userName string) (*entity.User, error) { 23 | user := new(entity.User) 24 | err := u.client(userName).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, user) 26 | }) 27 | 28 | return user, err 29 | } 30 | 31 | // Delete deletes a given User 32 | func (u *User) Delete(params *entity.UserDeleteParams) error { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | return u.client(params.UserName).DeleteWithParams(qsp) 39 | } 40 | -------------------------------------------------------------------------------- /entity/subnet/ip_address.go: -------------------------------------------------------------------------------- 1 | package subnet 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // IPAddress represents an IP address from a Subnet's GetIPAddresses() 8 | type IPAddress struct { 9 | Created string `json:"created,omitempty"` 10 | Updated string `json:"updated,omitempty"` 11 | User string `json:"user,omitempty"` 12 | IP net.IP `json:"ip,omitempty"` 13 | NodeSummary NodeSummary `json:"node_summary,omitempty"` 14 | AllocType int `json:"alloc_type,omitempty"` 15 | } 16 | 17 | // NodeSummary represents the optional node_summary from GetIPAddresses(). 18 | // This type should not be used directly. 19 | type NodeSummary struct { 20 | SystemID string `json:"system_id,omitempty"` 21 | FQDN string `json:"fqdn,omitempty"` 22 | Hostname string `json:"hostname,omitempty"` 23 | Via string `json:"via,omitempty"` 24 | NodeType int `json:"node_type,omitempty"` 25 | IsContainer bool `json:"is_container,omitempty"` 26 | } 27 | -------------------------------------------------------------------------------- /client/maas_server.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | // MAASServer contains functionality for manipulating the MAASServer entity. 8 | type MAASServer struct { 9 | APIClient APIClient 10 | } 11 | 12 | func (m *MAASServer) client() APIClient { 13 | return m.APIClient.GetSubObject("maas") 14 | } 15 | 16 | // Get MAAS server configuration value. 17 | func (m *MAASServer) Get(name string) ([]byte, error) { 18 | qsp := url.Values{} 19 | qsp.Set("name", name) 20 | 21 | value := new([]byte) 22 | err := m.client().Get("get_config", qsp, func(data []byte) error { 23 | *value = data 24 | return nil 25 | }) 26 | 27 | return *value, err 28 | } 29 | 30 | // Post (Set) MAAS server configuration value. 31 | func (m *MAASServer) Post(name, value string) error { 32 | qsp := url.Values{} 33 | qsp.Set("name", name) 34 | qsp.Set("value", value) 35 | 36 | err := m.client().Post("set_config", qsp, func(data []byte) error { 37 | return nil 38 | }) 39 | 40 | return err 41 | } 42 | -------------------------------------------------------------------------------- /client/spaces.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // Spaces implements api.Spaces 12 | type Spaces struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (s *Spaces) client() APIClient { 17 | return s.APIClient.GetSubObject("spaces") 18 | } 19 | 20 | // Get fetches a list of Space objects 21 | func (s *Spaces) Get() ([]entity.Space, error) { 22 | spaces := make([]entity.Space, 0) 23 | err := s.client().Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, &spaces) 25 | }) 26 | 27 | return spaces, err 28 | } 29 | 30 | // Create creates a new Space 31 | func (s *Spaces) Create(name string) (*entity.Space, error) { 32 | space := new(entity.Space) 33 | qsp := url.Values{} 34 | qsp.Set("name", name) 35 | err := s.client().Post("", qsp, func(data []byte) error { 36 | return json.Unmarshal(data, space) 37 | }) 38 | 39 | return space, err 40 | } 41 | -------------------------------------------------------------------------------- /test/helper/testdata.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | // Testdata fetches a testdata file. 12 | // The path should be relative to the /test/testdata directory. 13 | func Testdata(path string) (io.ReadCloser, error) { 14 | path = filepath.Join(RepoBaseDir, "test", "testdata", filepath.FromSlash(path)) 15 | return os.Open(filepath.Clean(path)) 16 | } 17 | 18 | // TestdataFromJSON fetches a testdata file and decodes it into a type. 19 | // The path should be compatible with Testdata(), and the target should 20 | // be compatible with Golang's json.Unmarshal(). 21 | func TestdataFromJSON(path string, target interface{}) error { 22 | rc, err := Testdata(path) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | defer func() { 28 | if err := rc.Close(); err != nil { 29 | fmt.Println("Error when closing:", err) 30 | } 31 | }() 32 | 33 | dec := json.NewDecoder(rc) 34 | dec.DisallowUnknownFields() 35 | 36 | return dec.Decode(target) 37 | } 38 | -------------------------------------------------------------------------------- /entity/dns_resource.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type DNSResource struct { 4 | FQDN string `json:"fqdn,omitempty"` 5 | ResourceURI string `json:"resource_uri,omitempty"` 6 | IPAddresses []IPAddress `json:"ip_addresses,omitempty"` 7 | ResourceRecords []DNSResourceRecord `json:"resource_records,omitempty"` 8 | ID int `json:"id,omitempty"` 9 | AddressTTL int `json:"address_ttl,omitempty"` 10 | } 11 | 12 | type DNSResourceParams struct { 13 | FQDN string `url:"fqdn,omitempty"` 14 | Name string `url:"name,omitempty"` 15 | Domain string `url:"domain,omitempty"` 16 | IPAddresses string `url:"ip_addresses,omitempty"` 17 | AddressTTL int `url:"address_ttl"` 18 | } 19 | 20 | type DNSResourcesParams struct { 21 | Domain string `url:"domain,omitempty"` 22 | FQDN string `url:"fqdn,omitempty"` 23 | Name string `url:"name,omitempty"` 24 | RRType string `url:"rrtype,omitempty"` 25 | All bool `url:"all,omitempty"` 26 | } 27 | -------------------------------------------------------------------------------- /entity/subnet/statistics_test.go: -------------------------------------------------------------------------------- 1 | package subnet 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/google/go-cmp/cmp" 8 | 9 | "github.com/canonical/gomaasclient/test/helper" 10 | ) 11 | 12 | var sampleStatistics Statistics = Statistics{ 13 | NumAvailable: 232, 14 | LargestAvailable: 41, 15 | NumUnavailable: 22, 16 | TotalAddresses: 254, 17 | Usage: 0.08661417322834646, 18 | UsageString: "9%", 19 | AvailableString: "91%", 20 | FirstAddress: net.ParseIP("172.16.1.1"), 21 | LastAddress: net.ParseIP("172.16.1.254"), 22 | IPVersion: 4, 23 | } 24 | 25 | func TestStatistics(t *testing.T) { 26 | stats := new(Statistics) 27 | 28 | // Unmarshal sample data into the types 29 | if err := helper.TestdataFromJSON("maas/subnets/statistics.json", stats); err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | // Verify the values are correct 34 | if diff := cmp.Diff(&sampleStatistics, stats); diff != "" { 35 | t.Fatalf("json.Decode(Statistics) mismatch (-want +got):\n%s", diff) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/block_device_partition.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/canonical/gomaasclient/entity" 4 | 5 | // BlockDevicePartition is an interface providing API behaviour for 6 | // block device partitions 7 | type BlockDevicePartition interface { 8 | Get(systemID string, blockDeviceID int, id int) (*entity.BlockDevicePartition, error) 9 | Delete(systemID string, blockDeviceID int, id int) error 10 | AddTag(systemID string, blockDeviceID int, id int, tag string) (*entity.BlockDevicePartition, error) 11 | RemoveTag(systemID string, blockDeviceID int, id int, tag string) (*entity.BlockDevicePartition, error) 12 | Format(systemID string, blockDeviceID int, id int, fsType string, label string) (*entity.BlockDevicePartition, error) 13 | Unformat(systemID string, blockDeviceID int, id int) (*entity.BlockDevicePartition, error) 14 | Mount(systemID string, blockDeviceID int, id int, mountPoint string, mountOptions string) (*entity.BlockDevicePartition, error) 15 | Unmount(systemID string, blockDeviceID int, id int) (*entity.BlockDevicePartition, error) 16 | } 17 | -------------------------------------------------------------------------------- /client/ssl_keys.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // SSLKeys implements api.SSLKeys 12 | type SSLKeys struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (s *SSLKeys) client() APIClient { 17 | return s.APIClient.GetSubObject("account/prefs/sslkeys") 18 | } 19 | 20 | // Get fetches a list of SSLKey objects 21 | func (s *SSLKeys) Get() ([]entity.SSLKey, error) { 22 | sslKeys := make([]entity.SSLKey, 0) 23 | err := s.client().Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, &sslKeys) 25 | }) 26 | 27 | return sslKeys, err 28 | } 29 | 30 | // Create creates a new SSLKey 31 | func (s *SSLKeys) Create(key string) (*entity.SSLKey, error) { 32 | sslKey := new(entity.SSLKey) 33 | qsp := url.Values{} 34 | qsp.Set("key", key) 35 | err := s.client().Post("", qsp, func(data []byte) error { 36 | return json.Unmarshal(data, sslKey) 37 | }) 38 | 39 | return sslKey, err 40 | } 41 | -------------------------------------------------------------------------------- /client/node_device.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // NodeDevice implements api.NodeDevice 12 | type NodeDevice struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (a *NodeDevice) client(systemID string, id int) APIClient { 17 | return a.APIClient.GetSubObject("nodes"). 18 | GetSubObject(fmt.Sprintf("%v", systemID)). 19 | GetSubObject("devices"). 20 | GetSubObject(fmt.Sprintf("%v", id)) 21 | } 22 | 23 | // Get fetches NodeDevice object with id for given systemID 24 | func (a *NodeDevice) Get(systemID string, id int) (*entity.NodeDevice, error) { 25 | nodeDevice := new(entity.NodeDevice) 26 | err := a.client(systemID, id).Get("", url.Values{}, func(data []byte) error { 27 | return json.Unmarshal(data, nodeDevice) 28 | }) 29 | 30 | return nodeDevice, err 31 | } 32 | 33 | // Delete deletes NodeDevice object with id for given systemID 34 | func (a *NodeDevice) Delete(systemID string, id int) error { 35 | return a.client(systemID, id).Delete() 36 | } 37 | -------------------------------------------------------------------------------- /test/helper/helper.go: -------------------------------------------------------------------------------- 1 | // Package helper contains helper functions for unit tests. 2 | package helper 3 | 4 | import ( 5 | "os" 6 | "path" 7 | "path/filepath" 8 | "reflect" 9 | "runtime" 10 | ) 11 | 12 | func init() { 13 | _, file, _, ok := runtime.Caller(0) 14 | if !ok { 15 | panic("error getting runtime information") 16 | } 17 | 18 | RepoBaseDir = func(fpath string) string { 19 | for fpath != "." { 20 | fpath = filepath.Dir(fpath) 21 | if _, err := os.Stat(filepath.Join(fpath, "go.mod")); err == nil { 22 | return fpath 23 | } 24 | } 25 | 26 | panic("could not find go.mod") 27 | }(file) 28 | 29 | RepoBasePkg = func(fpath string) string { 30 | type foo struct{} 31 | 32 | pkgPath := reflect.TypeOf(foo{}).PkgPath() 33 | fpath = filepath.Dir(fpath) 34 | 35 | for path.Base(pkgPath) == filepath.Base(fpath) && filepath.Base(fpath) != filepath.Base(RepoBaseDir) { 36 | pkgPath = path.Dir(pkgPath) 37 | fpath = filepath.Dir(fpath) 38 | } 39 | 40 | return pkgPath 41 | }(file) 42 | } 43 | 44 | var RepoBaseDir string 45 | var RepoBasePkg string 46 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # GO MAAS Client release workflow. 2 | name: Release 3 | 4 | # This GitHub action creates a release when a tag that matches the pattern 5 | # "v*" (e.g. v0.1.0) is created. 6 | on: 7 | push: 8 | tags: 9 | - 'v*' 10 | 11 | # Releases need permissions to read and write the repository contents. 12 | # GitHub considers creating releases and uploading assets as writing contents. 13 | permissions: 14 | contents: write 15 | 16 | jobs: 17 | goreleaser: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v6 21 | with: 22 | # Allow goreleaser to access older tag information. 23 | fetch-depth: 0 24 | - uses: actions/setup-go@v6 25 | with: 26 | go-version-file: 'go.mod' 27 | cache: true 28 | - name: Run GoReleaser 29 | uses: goreleaser/goreleaser-action@v6 30 | with: 31 | args: release --clean 32 | env: 33 | # GitHub sets the GITHUB_TOKEN secret automatically. 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /client/tags.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Tags implements api.Tags 13 | type Tags struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (t *Tags) client() APIClient { 18 | return t.APIClient.GetSubObject("tags") 19 | } 20 | 21 | // Get fetches a list of Tag objects 22 | func (t *Tags) Get() ([]entity.Tag, error) { 23 | tags := make([]entity.Tag, 0) 24 | err := t.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &tags) 26 | }) 27 | 28 | return tags, err 29 | } 30 | 31 | // Create creates a new Tag 32 | func (t *Tags) Create(tagParams *entity.TagParams) (*entity.Tag, error) { 33 | qsp, err := query.Values(tagParams) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | tag := new(entity.Tag) 39 | err = t.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, tag) 41 | }) 42 | 43 | return tag, err 44 | } 45 | -------------------------------------------------------------------------------- /entity/bcache.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type BCache struct { 4 | HumanSize string `json:"human_size,omitempty"` 5 | UUID string `json:"uuid,omitempty"` 6 | Name string `json:"name,omitempty"` 7 | SystemID string `json:"system_id"` 8 | CacheMode string `json:"cache_mode,omitempty"` 9 | ResourceURI string `json:"resource_uri,omitempty"` 10 | VirtualDevice BlockDevice `json:"virtual_device,omitempty"` 11 | BackingDevice BlockDevice `json:"backing_device,omitempty"` 12 | CacheSet BCacheCacheSet `json:"cache_set"` 13 | ID int `json:"id,omitempty"` 14 | Size int64 `json:"size,omitempty"` 15 | } 16 | 17 | type BCacheParams struct { 18 | Name string `url:"name,omitempty"` 19 | UUID string `url:"uuid,omitempty"` 20 | CacheSet string `url:"cache_set,omitempty"` 21 | BackingDevice string `url:"backing_device,omitempty"` 22 | BackingPartition string `url:"backing_partition,omitempty"` 23 | CacheMode string `url:"cache_mode,omitempty"` 24 | } 25 | -------------------------------------------------------------------------------- /client/zones.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Zones implements api.Zones 13 | type Zones struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (z *Zones) client() APIClient { 18 | return z.APIClient.GetSubObject("zones") 19 | } 20 | 21 | // Get fetches a list of Zone objects 22 | func (z *Zones) Get() ([]entity.Zone, error) { 23 | zones := make([]entity.Zone, 0) 24 | err := z.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &zones) 26 | }) 27 | 28 | return zones, err 29 | } 30 | 31 | // Create creates a new Zone 32 | func (z *Zones) Create(params *entity.ZoneParams) (*entity.Zone, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | zone := new(entity.Zone) 39 | err = z.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, zone) 41 | }) 42 | 43 | return zone, err 44 | } 45 | -------------------------------------------------------------------------------- /api/rack_controller.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // RackController represents the MAAS Rack Controller endpoint 8 | type RackController interface { 9 | Get(systemID string) (*entity.RackController, error) 10 | Update(systemID string, machineParams *entity.RackControllerParams, powerParams map[string]interface{}) (*entity.RackController, error) 11 | Delete(systemID string) error 12 | GetPowerParameters(systemID string) (map[string]interface{}, error) 13 | PowerOn(systemID string, params *entity.RackControllerPowerOnParams) (*entity.RackController, error) 14 | PowerOff(systemID string, params *entity.RackControllerPowerOffParams) (*entity.RackController, error) 15 | GetPowerState(systemID string) (*entity.RackControllerPowerState, error) 16 | Abort(systemID string, comment string) (*entity.RackController, error) 17 | Details(systemID string) (*entity.RackControllerDetails, error) 18 | OverrideFailedTesting(systemID string, comment string) (*entity.RackController, error) 19 | Test(systemID string, params map[string]interface{}) (*entity.RackController, error) 20 | } 21 | -------------------------------------------------------------------------------- /client/space.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | ) 10 | 11 | // Space implements api.Space 12 | type Space struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (s *Space) client(id int) APIClient { 17 | return s.APIClient.GetSubObject("spaces").GetSubObject(fmt.Sprintf("%v", id)) 18 | } 19 | 20 | // Get fetches a given Space 21 | func (s *Space) Get(id int) (*entity.Space, error) { 22 | space := new(entity.Space) 23 | err := s.client(id).Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, space) 25 | }) 26 | 27 | return space, err 28 | } 29 | 30 | // Update updates a given Space 31 | func (s *Space) Update(id int, name string) (*entity.Space, error) { 32 | space := new(entity.Space) 33 | qsp := url.Values{} 34 | qsp.Set("name", name) 35 | err := s.client(id).Put(qsp, func(data []byte) error { 36 | return json.Unmarshal(data, space) 37 | }) 38 | 39 | return space, err 40 | } 41 | 42 | // Delete deletes a given Space 43 | func (s *Space) Delete(id int) error { 44 | return s.client(id).Delete() 45 | } 46 | -------------------------------------------------------------------------------- /entity/discovery.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // Discovery represents device discovery in MAAS 4 | type Discovery struct { 5 | DiscoveryID string `json:"discovery_id,omitempty"` 6 | IP string `json:"ip,omitempty"` 7 | MACAddress string `json:"mac_address,omitempty"` 8 | LastSeen string `json:"last_seen,omitempty"` 9 | Hostname string `json:"hostname,omitempty"` 10 | FabricName string `json:"fabric_name,omitempty"` 11 | Vid string `json:"vid,omitempty"` 12 | MacOrganization string `json:"mac_organization,omitempty"` 13 | ResourceURI string `json:"resource_uri,omitempty"` 14 | Observer Observer `json:"observer,omitempty"` 15 | } 16 | 17 | type Observer struct { 18 | SystemID string `json:"system_id,omitempty"` 19 | Hostname string `json:"hostname,omitempty"` 20 | InterfaceName string `json:"interface_name,omitempty"` 21 | InterfaceID int `json:"interface_id,omitempty"` 22 | } 23 | 24 | type DiscoveryClearParams struct { 25 | All bool `url:"all,omitempty"` 26 | MDNS bool `url:"mdns,omitempty"` 27 | Neighbours bool `url:"neighbours,omitempty"` 28 | } 29 | -------------------------------------------------------------------------------- /client/devices.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Devices implements api.Devices 13 | type Devices struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (d *Devices) client() APIClient { 18 | return d.APIClient.GetSubObject("devices") 19 | } 20 | 21 | // Get fetches a list of Devices 22 | func (d *Devices) Get() ([]entity.Device, error) { 23 | devices := make([]entity.Device, 0) 24 | err := d.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &devices) 26 | }) 27 | 28 | return devices, err 29 | } 30 | 31 | // Create creates a new Device 32 | func (d *Devices) Create(deviceParams *entity.DeviceCreateParams) (*entity.Device, error) { 33 | qsp, err := query.Values(deviceParams) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | device := new(entity.Device) 39 | err = d.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, device) 41 | }) 42 | 43 | return device, err 44 | } 45 | -------------------------------------------------------------------------------- /client/fabrics.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Fabrics implements api.Fabrics 13 | type Fabrics struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (f *Fabrics) client() APIClient { 18 | return f.APIClient.GetSubObject("fabrics") 19 | } 20 | 21 | // Get fetches a list of Fabric objects 22 | func (f *Fabrics) Get() ([]entity.Fabric, error) { 23 | fabrics := make([]entity.Fabric, 0) 24 | err := f.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &fabrics) 26 | }) 27 | 28 | return fabrics, err 29 | } 30 | 31 | // Create creates a new Fabric object 32 | func (f *Fabrics) Create(fabricParams *entity.FabricParams) (*entity.Fabric, error) { 33 | qsp, err := query.Values(fabricParams) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | fabric := new(entity.Fabric) 39 | err = f.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, fabric) 41 | }) 42 | 43 | return fabric, err 44 | } 45 | -------------------------------------------------------------------------------- /client/ip_ranges.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // IPRanges implements api.IPRanges 13 | type IPRanges struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (i *IPRanges) client() APIClient { 18 | return i.APIClient.GetSubObject("ipranges") 19 | } 20 | 21 | // Get fetches a list of IPRange objects 22 | func (i *IPRanges) Get() ([]entity.IPRange, error) { 23 | ipRanges := make([]entity.IPRange, 0) 24 | err := i.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &ipRanges) 26 | }) 27 | 28 | return ipRanges, err 29 | } 30 | 31 | // Create creates a new IPRange object 32 | func (i *IPRanges) Create(params *entity.IPRangeParams) (*entity.IPRange, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | ipRange := new(entity.IPRange) 39 | err = i.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, ipRange) 41 | }) 42 | 43 | return ipRange, err 44 | } 45 | -------------------------------------------------------------------------------- /client/vlans.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // VLANs implements api.VLANs 13 | type VLANs struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (v *VLANs) client(fabricID int) APIClient { 18 | return v.APIClient.GetSubObject("fabrics").GetSubObject(fmt.Sprintf("%v", fabricID)).GetSubObject("vlans") 19 | } 20 | 21 | // Get fetches a list of VLAN objects 22 | func (v *VLANs) Get(fabricID int) ([]entity.VLAN, error) { 23 | vlans := make([]entity.VLAN, 0) 24 | err := v.client(fabricID).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &vlans) 26 | }) 27 | 28 | return vlans, err 29 | } 30 | 31 | // Create creates a new VLAN 32 | func (v *VLANs) Create(fabricID int, params *entity.VLANParams) (*entity.VLAN, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | vlan := new(entity.VLAN) 39 | err = v.client(fabricID).Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, vlan) 41 | }) 42 | 43 | return vlan, err 44 | } 45 | -------------------------------------------------------------------------------- /client/utils.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "reflect" 7 | ) 8 | 9 | // Converts parameters to url.Values. 10 | // Supported parameter value types: `string`, `int`, `bool`, `[]string`, `[]int`, `[]bool` 11 | func paramsToURLValues(params map[string]interface{}) url.Values { 12 | qsp := url.Values{} 13 | 14 | for k, v := range params { 15 | val := reflect.ValueOf(v) 16 | switch val.Kind() { 17 | case reflect.String: 18 | if val, ok := v.(string); ok { 19 | qsp.Add(k, val) 20 | } 21 | case reflect.Int: 22 | if val, ok := v.(int); ok { 23 | qsp.Add(k, fmt.Sprintf("%d", val)) 24 | } 25 | case reflect.Bool: 26 | if val, ok := v.(bool); ok { 27 | qsp.Add(k, fmt.Sprintf("%t", val)) 28 | } 29 | case reflect.Slice: 30 | for i := 0; i < val.Len(); i++ { 31 | switch val.Index(0).Elem().Kind() { 32 | case reflect.String: 33 | qsp.Add(k, val.Index(i).Elem().String()) 34 | case reflect.Int: 35 | qsp.Add(k, fmt.Sprintf("%d", val.Index(i).Elem().Int())) 36 | case reflect.Bool: 37 | qsp.Add(k, fmt.Sprintf("%t", val.Index(i).Elem().Bool())) 38 | } 39 | } 40 | } 41 | } 42 | 43 | return qsp 44 | } 45 | -------------------------------------------------------------------------------- /client/vm_hosts.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // VMHosts contains functionality for manipulating the VMHosts entity. 13 | type VMHosts struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (p *VMHosts) client() APIClient { 18 | return p.APIClient.GetSubObject("pods") 19 | } 20 | 21 | // Get fetches a list of VMHost objects 22 | func (p *VMHosts) Get() ([]entity.VMHost, error) { 23 | vmHosts := make([]entity.VMHost, 0) 24 | err := p.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &vmHosts) 26 | }) 27 | 28 | return vmHosts, err 29 | } 30 | 31 | // Create creates a new VMHost 32 | func (p *VMHosts) Create(params *entity.VMHostParams) (*entity.VMHost, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | vmHost := new(entity.VMHost) 39 | err = p.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, vmHost) 41 | }) 42 | 43 | return vmHost, err 44 | } 45 | -------------------------------------------------------------------------------- /client/subnets.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Subnets contains functionality for manipulating the Subnets entity. 13 | type Subnets struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (s *Subnets) client() APIClient { 18 | return s.APIClient.GetSubObject("subnets") 19 | } 20 | 21 | // Get fetches a list of Subnet objects 22 | func (s *Subnets) Get() ([]entity.Subnet, error) { 23 | subnets := make([]entity.Subnet, 0) 24 | err := s.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &subnets) 26 | }) 27 | 28 | return subnets, err 29 | } 30 | 31 | // Create creates a new Subnet 32 | func (s *Subnets) Create(params *entity.SubnetParams) (*entity.Subnet, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | subnet := new(entity.Subnet) 39 | err = s.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, subnet) 41 | }) 42 | 43 | return subnet, err 44 | } 45 | -------------------------------------------------------------------------------- /client/zone.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // Zone Contains functionality for manipulating the Zone entity. 12 | type Zone struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (z *Zone) client(name string) APIClient { 17 | return z.APIClient.GetSubObject("zones").GetSubObject(name) 18 | } 19 | 20 | // Get Zone details. 21 | func (z *Zone) Get(name string) (*entity.Zone, error) { 22 | zone := new(entity.Zone) 23 | err := z.client(name).Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, zone) 25 | }) 26 | 27 | return zone, err 28 | } 29 | 30 | // Update Zone. 31 | func (z *Zone) Update(name string, params *entity.ZoneParams) (*entity.Zone, error) { 32 | qsp, err := query.Values(params) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | zone := new(entity.Zone) 38 | err = z.client(name).Put(qsp, func(data []byte) error { 39 | return json.Unmarshal(data, zone) 40 | }) 41 | 42 | return zone, err 43 | } 44 | 45 | // Delete Zone. 46 | func (z *Zone) Delete(name string) error { 47 | return z.client(name).Delete() 48 | } 49 | -------------------------------------------------------------------------------- /test/testdata/maas/volume_group.json: -------------------------------------------------------------------------------- 1 | { 2 | "used_size": 0, 3 | "uuid": "aa69c453-b713-40fb-8149-c7708619a895", 4 | "name": "vg0", 5 | "human_available_size": "4.8 GB", 6 | "devices": [ 7 | { 8 | "uuid": "1d18f4fc-656a-45bf-a831-cb6176d72969", 9 | "size": 4836032512, 10 | "bootable": false, 11 | "tags": [], 12 | "device_id": 3, 13 | "system_id": "6hntar", 14 | "type": "partition", 15 | "id": 2, 16 | "filesystem": { 17 | "fstype": "lvm-pv", 18 | "label": null, 19 | "uuid": "da76be2f-c18e-4bf3-9e16-8aeed6dc42d9", 20 | "mount_point": null, 21 | "mount_options": null 22 | }, 23 | "used_for": "LVM volume for vg0", 24 | "path": "/dev/disk/by-dname/sda-part2", 25 | "resource_uri": "/MAAS/api/2.0/nodes/6hntar/blockdevices/3/partition/2" 26 | } 27 | ], 28 | "human_used_size": "0 bytes", 29 | "size": 4831838208, 30 | "system_id": "6hntar", 31 | "available_size": 4831838208, 32 | "id": 1, 33 | "human_size": "4.8 GB", 34 | "logical_volumes": [], 35 | "resource_uri": "/MAAS/api/2.0/nodes/6hntar/volume-group/1/" 36 | } 37 | -------------------------------------------------------------------------------- /client/boot_sources.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // BootSources implements api.BootSources 13 | type BootSources struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (b *BootSources) client() APIClient { 18 | return b.APIClient.GetSubObject("boot-sources") 19 | } 20 | 21 | // Get fetches a list of boot sources 22 | func (b *BootSources) Get() ([]entity.BootSource, error) { 23 | bootSources := make([]entity.BootSource, 0) 24 | err := b.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &bootSources) 26 | }) 27 | 28 | return bootSources, err 29 | } 30 | 31 | // Create creates a new boot source 32 | func (b *BootSources) Create(params *entity.BootSourceParams) (*entity.BootSource, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | bootSource := new(entity.BootSource) 39 | err = b.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, bootSource) 41 | }) 42 | 43 | return bootSource, err 44 | } 45 | -------------------------------------------------------------------------------- /client/license_keys.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // LicenseKeys implements api.LicenseKeys 13 | type LicenseKeys struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (l *LicenseKeys) client() APIClient { 18 | return l.APIClient.GetSubObject("license-keys") 19 | } 20 | 21 | // Get fetches a list of LicenseKey objects 22 | func (l *LicenseKeys) Get() ([]entity.LicenseKey, error) { 23 | licenseKeys := make([]entity.LicenseKey, 0) 24 | err := l.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &licenseKeys) 26 | }) 27 | 28 | return licenseKeys, err 29 | } 30 | 31 | // Create creates a new LicenseKey object 32 | func (l *LicenseKeys) Create(params *entity.LicenseKeyParams) (*entity.LicenseKey, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | licenseKey := new(entity.LicenseKey) 39 | err = l.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, licenseKey) 41 | }) 42 | 43 | return licenseKey, err 44 | } 45 | -------------------------------------------------------------------------------- /client/raids.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/google/go-querystring/query" 9 | 10 | "github.com/canonical/gomaasclient/entity" 11 | ) 12 | 13 | // RAIDs contains functionality for manipulating the RAIDs entity. 14 | type RAIDs struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (r *RAIDs) client(systemID string) APIClient { 19 | return r.APIClient.GetSubObject("nodes").GetSubObject(systemID).GetSubObject("raids") 20 | } 21 | 22 | // Get RAIDs of a machine. 23 | func (r *RAIDs) Get(systemID string) ([]entity.RAID, error) { 24 | raids := make([]entity.RAID, 0) 25 | err := r.client(systemID).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, &raids) 27 | }) 28 | 29 | return raids, err 30 | } 31 | 32 | // Create a RAID of a machine. 33 | func (r *RAIDs) Create(systemID string, params *entity.RAIDCreateParams) (*entity.RAID, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | raid := new(entity.RAID) 40 | err = r.client(systemID).Post("", qsp, func(data []byte) error { 41 | return json.Unmarshal(data, raid) 42 | }) 43 | 44 | return raid, err 45 | } 46 | -------------------------------------------------------------------------------- /client/static_routes.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // StaticRoutes implements api.StaticRoutes 13 | type StaticRoutes struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (s *StaticRoutes) client() APIClient { 18 | return s.APIClient.GetSubObject("static-routes") 19 | } 20 | 21 | // Get fetches a list of StaticRoutes objects 22 | func (s *StaticRoutes) Get() ([]entity.StaticRoute, error) { 23 | staticRoutes := make([]entity.StaticRoute, 0) 24 | err := s.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &staticRoutes) 26 | }) 27 | 28 | return staticRoutes, err 29 | } 30 | 31 | // Create creates a new StaticRoute 32 | func (s *StaticRoutes) Create(params *entity.StaticRouteParams) (*entity.StaticRoute, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | staticRoute := new(entity.StaticRoute) 39 | err = s.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, staticRoute) 41 | }) 42 | 43 | return staticRoute, err 44 | } 45 | -------------------------------------------------------------------------------- /entity/notification.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/canonical/gomaasclient/entity/notification" 4 | 5 | type Notification struct { 6 | Context map[string]interface{} `json:"context"` 7 | User *User `json:"user"` 8 | ResourceURI string `json:"resource_uri"` 9 | Ident string `json:"ident"` 10 | Message string `json:"message"` 11 | Category notification.Category `json:"category"` 12 | ID int `json:"id"` 13 | Users bool `json:"users"` 14 | Admins bool `json:"admins"` 15 | Dismissable bool `json:"dismissable"` 16 | } 17 | 18 | type NotificationParams struct { 19 | Context map[string]interface{} `url:"context,omitempty"` 20 | Ident string `url:"ident,omitempty"` 21 | Message string `url:"message"` 22 | Category notification.Category `url:"category,omitempty"` 23 | User int `url:"user,omitempty"` 24 | Users bool `url:"users,omitempty"` 25 | Admins bool `url:"admins,omitempty"` 26 | Dismissable bool `url:"dismissable,omitempty"` 27 | } 28 | -------------------------------------------------------------------------------- /client/resource_pools.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // ResourcePools implements api.ResourcePools 13 | type ResourcePools struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (r *ResourcePools) client() APIClient { 18 | return r.APIClient.GetSubObject("resourcepools") 19 | } 20 | 21 | // Get fetches a list of ResourcePool objects 22 | func (r *ResourcePools) Get() ([]entity.ResourcePool, error) { 23 | resourcePools := make([]entity.ResourcePool, 0) 24 | err := r.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &resourcePools) 26 | }) 27 | 28 | return resourcePools, err 29 | } 30 | 31 | // Create creates a new ResourcePool 32 | func (r *ResourcePools) Create(params *entity.ResourcePoolParams) (*entity.ResourcePool, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | resourcePool := new(entity.ResourcePool) 39 | err = r.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, resourcePool) 41 | }) 42 | 43 | return resourcePool, err 44 | } 45 | -------------------------------------------------------------------------------- /client/notifications.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Notifications implements api.Notifications 13 | type Notifications struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (n *Notifications) client() APIClient { 18 | return n.APIClient.GetSubObject("notifications") 19 | } 20 | 21 | // Get fetches a list of Notification objects 22 | func (n *Notifications) Get() ([]entity.Notification, error) { 23 | notifications := make([]entity.Notification, 0) 24 | err := n.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, ¬ifications) 26 | }) 27 | 28 | return notifications, err 29 | } 30 | 31 | // Create creates a new Notification object 32 | func (n *Notifications) Create(params *entity.NotificationParams) (*entity.Notification, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | notification := new(entity.Notification) 39 | err = n.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, notification) 41 | }) 42 | 43 | return notification, err 44 | } 45 | -------------------------------------------------------------------------------- /entity/block_device_partition.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // BlockDevicePartition represents the MAAS block device partition endpoint. 4 | type BlockDevicePartition struct { 5 | FileSystem PartitionFileSystem `json:"filesystem"` 6 | UUID string `json:"uuid"` 7 | SystemID string `json:"system_id"` 8 | UsedFor string `json:"used_for,omitempty"` 9 | Type string `json:"type"` 10 | Path string `json:"path"` 11 | ResourceURI string `json:"resource_uri"` 12 | Tags []string `json:"tags,omitempty"` 13 | Size int64 `json:"size"` 14 | ID int `json:"id"` 15 | DeviceID int `json:"device_id"` 16 | Bootable bool `json:"bootable"` 17 | } 18 | 19 | type PartitionFileSystem struct { 20 | FSType string `json:"fstype"` 21 | UUID string `json:"uuid"` 22 | MountPoint string `json:"mount_point"` 23 | Label string `json:"label,omitempty"` 24 | MountOptions string `json:"mount_options,omitempty"` 25 | } 26 | 27 | type BlockDevicePartitionParams struct { 28 | UUID string `url:"uuid,omitempty"` 29 | Size int64 `url:"size,omitempty"` 30 | Bootable bool `url:"bootable"` 31 | } 32 | -------------------------------------------------------------------------------- /client/bcaches.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/google/go-querystring/query" 9 | 10 | "github.com/canonical/gomaasclient/entity" 11 | ) 12 | 13 | // BCaches contains functionality for manipulating the BCaches entity. 14 | type BCaches struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (b *BCaches) client(systemID string) APIClient { 19 | return b.APIClient.GetSubObject("nodes").GetSubObject(systemID).GetSubObject("bcaches") 20 | } 21 | 22 | // Get BCaches of a machine. 23 | func (b *BCaches) Get(systemID string) ([]entity.BCache, error) { 24 | bCaches := make([]entity.BCache, 0) 25 | err := b.client(systemID).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, &bCaches) 27 | }) 28 | 29 | return bCaches, err 30 | } 31 | 32 | // Create a BCache of a machine. 33 | func (b *BCaches) Create(systemID string, params *entity.BCacheParams) (*entity.BCache, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | bCache := new(entity.BCache) 40 | err = b.client(systemID).Post("", qsp, func(data []byte) error { 41 | return json.Unmarshal(data, bCache) 42 | }) 43 | 44 | return bCache, err 45 | } 46 | -------------------------------------------------------------------------------- /test/testdata/maas/volume_groups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "used_size": 0, 4 | "uuid": "aa69c453-b713-40fb-8149-c7708619a895", 5 | "name": "vg0", 6 | "human_available_size": "4.8 GB", 7 | "devices": [ 8 | { 9 | "uuid": "1d18f4fc-656a-45bf-a831-cb6176d72969", 10 | "size": 4836032512, 11 | "bootable": false, 12 | "tags": [], 13 | "device_id": 3, 14 | "system_id": "6hntar", 15 | "type": "partition", 16 | "id": 2, 17 | "filesystem": { 18 | "fstype": "lvm-pv", 19 | "label": null, 20 | "uuid": "da76be2f-c18e-4bf3-9e16-8aeed6dc42d9", 21 | "mount_point": null, 22 | "mount_options": null 23 | }, 24 | "used_for": "LVM volume for vg0", 25 | "path": "/dev/disk/by-dname/sda-part2", 26 | "resource_uri": "/MAAS/api/2.0/nodes/6hntar/blockdevices/3/partition/2" 27 | } 28 | ], 29 | "human_used_size": "0 bytes", 30 | "size": 4831838208, 31 | "system_id": "6hntar", 32 | "available_size": 4831838208, 33 | "id": 1, 34 | "human_size": "4.8 GB", 35 | "logical_volumes": [], 36 | "resource_uri": "/MAAS/api/2.0/nodes/6hntar/volume-group/1/" 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /client/fabric.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // Fabric implments api.Fabric 14 | type Fabric struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (f *Fabric) client(id int) APIClient { 19 | return f.APIClient.GetSubObject("fabrics").GetSubObject(fmt.Sprintf("%v", id)) 20 | } 21 | 22 | // Get fetchs a Fabric object 23 | func (f *Fabric) Get(id int) (*entity.Fabric, error) { 24 | fabric := new(entity.Fabric) 25 | err := f.client(id).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, fabric) 27 | }) 28 | 29 | return fabric, err 30 | } 31 | 32 | // Update updates a given Fabric 33 | func (f *Fabric) Update(id int, fabricParams *entity.FabricParams) (*entity.Fabric, error) { 34 | qsp, err := query.Values(fabricParams) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | fabric := new(entity.Fabric) 40 | err = f.client(id).Put(qsp, func(data []byte) error { 41 | return json.Unmarshal(data, fabric) 42 | }) 43 | 44 | return fabric, err 45 | } 46 | 47 | // Delete deletes a given Fabric 48 | func (f *Fabric) Delete(id int) error { 49 | return f.client(id).Delete() 50 | } 51 | -------------------------------------------------------------------------------- /client/ip_range.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // IPRange implements api.IPRange 14 | type IPRange struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (i *IPRange) client(id int) APIClient { 19 | return i.APIClient.GetSubObject("ipranges").GetSubObject(fmt.Sprintf("%v", id)) 20 | } 21 | 22 | // Get fetches a given IPRange 23 | func (i *IPRange) Get(id int) (*entity.IPRange, error) { 24 | ipRange := new(entity.IPRange) 25 | err := i.client(id).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, ipRange) 27 | }) 28 | 29 | return ipRange, err 30 | } 31 | 32 | // Update updates a given IPRange 33 | func (i *IPRange) Update(id int, params *entity.IPRangeParams) (*entity.IPRange, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | ipRange := new(entity.IPRange) 40 | err = i.client(id).Put(qsp, func(data []byte) error { 41 | return json.Unmarshal(data, ipRange) 42 | }) 43 | 44 | return ipRange, err 45 | } 46 | 47 | // Delete deletes a given IPRange 48 | func (i *IPRange) Delete(id int) error { 49 | return i.client(id).Delete() 50 | } 51 | -------------------------------------------------------------------------------- /client/dns_resources.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // DNSResources implements api.DNSResources 12 | type DNSResources struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (d *DNSResources) client() APIClient { 17 | return d.APIClient.GetSubObject("dnsresources") 18 | } 19 | 20 | // Get fetches a list of DNSResource objects 21 | func (d *DNSResources) Get(params *entity.DNSResourcesParams) ([]entity.DNSResource, error) { 22 | qsp, err := query.Values(params) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | dnsresources := make([]entity.DNSResource, 0) 28 | err = d.client().Get("", qsp, func(data []byte) error { 29 | return json.Unmarshal(data, &dnsresources) 30 | }) 31 | 32 | return dnsresources, err 33 | } 34 | 35 | // Create creates a new DNSResource 36 | func (d *DNSResources) Create(params *entity.DNSResourceParams) (*entity.DNSResource, error) { 37 | qsp, err := query.Values(params) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | dnsResource := new(entity.DNSResource) 43 | err = d.client().Post("", qsp, func(data []byte) error { 44 | return json.Unmarshal(data, dnsResource) 45 | }) 46 | 47 | return dnsResource, err 48 | } 49 | -------------------------------------------------------------------------------- /client/utils_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestPowerParamsToURLValues(t *testing.T) { 10 | testcases := map[string]struct { 11 | in map[string]interface{} 12 | out string 13 | }{ 14 | "strings": { 15 | in: map[string]interface{}{ 16 | "a": "b", 17 | "c": "d", 18 | }, 19 | out: "a=b&c=d", 20 | }, 21 | "strings array": { 22 | in: map[string]interface{}{ 23 | "a": []interface{}{"b", "c"}, 24 | }, 25 | out: "a=b&a=c", 26 | }, 27 | "numbers": { 28 | in: map[string]interface{}{ 29 | "a": 1, 30 | "b": 2, 31 | }, 32 | out: "a=1&b=2", 33 | }, 34 | "numbers array": { 35 | in: map[string]interface{}{ 36 | "a": []interface{}{1, 2}, 37 | }, 38 | out: "a=1&a=2", 39 | }, 40 | "boolean": { 41 | in: map[string]interface{}{ 42 | "a": true, 43 | "b": false, 44 | }, 45 | out: "a=true&b=false", 46 | }, 47 | "boolean array": { 48 | in: map[string]interface{}{ 49 | "a": []interface{}{true, false}, 50 | }, 51 | out: "a=true&a=false", 52 | }, 53 | } 54 | 55 | for name, tc := range testcases { 56 | tc := tc 57 | 58 | t.Run(name, func(t *testing.T) { 59 | t.Parallel() 60 | 61 | res := paramsToURLValues(tc.in).Encode() 62 | assert.Equal(t, tc.out, res) 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /client/users.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // Users implements api.Users 12 | type Users struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (u *Users) client() APIClient { 17 | return u.APIClient.GetSubObject("users") 18 | } 19 | 20 | // Get fetches a list of User objects 21 | func (u *Users) Get() ([]entity.User, error) { 22 | users := make([]entity.User, 0) 23 | err := u.client().Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, &users) 25 | }) 26 | 27 | return users, err 28 | } 29 | 30 | // Create creates a new User 31 | func (u *Users) Create(params *entity.UserParams) (*entity.User, error) { 32 | qsp, err := query.Values(params) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | user := new(entity.User) 38 | err = u.client().Post("", qsp, func(data []byte) error { 39 | return json.Unmarshal(data, user) 40 | }) 41 | 42 | return user, err 43 | } 44 | 45 | // Whoami fetches a reference to the currently logged in user 46 | func (u *Users) Whoami() (*entity.User, error) { 47 | user := new(entity.User) 48 | err := u.client().Get("whoami", url.Values{}, func(data []byte) error { 49 | return json.Unmarshal(data, user) 50 | }) 51 | 52 | return user, err 53 | } 54 | -------------------------------------------------------------------------------- /client/domains.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Domains implements api.Domains 13 | type Domains struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (d *Domains) client() APIClient { 18 | return d.APIClient.GetSubObject("domains") 19 | } 20 | 21 | // Get fetches a list of Domain objects 22 | func (d *Domains) Get() ([]entity.Domain, error) { 23 | domains := make([]entity.Domain, 0) 24 | err := d.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &domains) 26 | }) 27 | 28 | return domains, err 29 | } 30 | 31 | // Create creates a new Domain 32 | func (d *Domains) Create(params *entity.DomainParams) (*entity.Domain, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | domain := new(entity.Domain) 39 | err = d.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, domain) 41 | }) 42 | 43 | return domain, err 44 | } 45 | 46 | // SetSerial sets the SOA serial for all domains 47 | func (d *Domains) SetSerial(serial int) error { 48 | qsp := url.Values{} 49 | qsp.Set("serial", fmt.Sprintf("%v", serial)) 50 | 51 | return d.client().Post("", qsp, func(data []byte) error { return nil }) 52 | } 53 | -------------------------------------------------------------------------------- /client/ssh_keys.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | ) 9 | 10 | // SSHKeys implements api.SSHKeys 11 | type SSHKeys struct { 12 | APIClient APIClient 13 | } 14 | 15 | func (s *SSHKeys) client() APIClient { 16 | return s.APIClient.GetSubObject("account/prefs/sshkeys") 17 | } 18 | 19 | // Get fetches a list of SSHKey objects 20 | func (s *SSHKeys) Get() ([]entity.SSHKey, error) { 21 | sshKeys := make([]entity.SSHKey, 0) 22 | err := s.client().Get("", url.Values{}, func(data []byte) error { 23 | return json.Unmarshal(data, &sshKeys) 24 | }) 25 | 26 | return sshKeys, err 27 | } 28 | 29 | // Create creates a new SSHKey 30 | func (s *SSHKeys) Create(key string) (*entity.SSHKey, error) { 31 | sshKey := new(entity.SSHKey) 32 | qsp := url.Values{} 33 | qsp.Set("key", key) 34 | err := s.client().Post("", qsp, func(data []byte) error { 35 | return json.Unmarshal(data, sshKey) 36 | }) 37 | 38 | return sshKey, err 39 | } 40 | 41 | // Import creates a new SSHKey 42 | func (s *SSHKeys) Import(keysource string) ([]entity.SSHKey, error) { 43 | sshKeys := make([]entity.SSHKey, 0) 44 | qsp := url.Values{} 45 | qsp.Set("keysource", keysource) 46 | err := s.client().Post("import", qsp, func(data []byte) error { 47 | return json.Unmarshal(data, &sshKeys) 48 | }) 49 | 50 | return sshKeys, err 51 | } 52 | -------------------------------------------------------------------------------- /client/static_route.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // StaticRoute implements api.StaticRoute 13 | type StaticRoute struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (s *StaticRoute) client(id int) APIClient { 18 | return s.APIClient.GetSubObject("static-routes").GetSubObject(fmt.Sprintf("%v", id)) 19 | } 20 | 21 | // Delete deletes a given StaticRoute 22 | func (s *StaticRoute) Delete(id int) error { 23 | return s.client(id).Delete() 24 | } 25 | 26 | // Get fetches a given StaticRoute 27 | func (s *StaticRoute) Get(id int) (*entity.StaticRoute, error) { 28 | staticRoute := new(entity.StaticRoute) 29 | err := s.client(id).Get("", url.Values{}, func(data []byte) error { 30 | return json.Unmarshal(data, &staticRoute) 31 | }) 32 | 33 | return staticRoute, err 34 | } 35 | 36 | // Update updates the given StaticRoute 37 | func (s *StaticRoute) Update(id int, params *entity.StaticRouteParams) (*entity.StaticRoute, error) { 38 | qsp, err := query.Values(params) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | staticRoute := new(entity.StaticRoute) 44 | err = s.client(id).Put(qsp, func(data []byte) error { 45 | return json.Unmarshal(data, staticRoute) 46 | }) 47 | 48 | return staticRoute, err 49 | } 50 | -------------------------------------------------------------------------------- /client/block_devices.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // BlockDevices implements api.BlockDevices 13 | type BlockDevices struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (b *BlockDevices) client(systemID string) APIClient { 18 | return b.APIClient.GetSubObject("nodes").GetSubObject(systemID).GetSubObject("blockdevices") 19 | } 20 | 21 | // Get fetches a list of BlockDevices for a given system_id 22 | func (b *BlockDevices) Get(systemID string) ([]entity.BlockDevice, error) { 23 | blockDevices := make([]entity.BlockDevice, 0) 24 | err := b.client(systemID).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &blockDevices) 26 | }) 27 | 28 | return blockDevices, err 29 | } 30 | 31 | // Create creates a new BlockDevice for a given system_id 32 | func (b *BlockDevices) Create(systemID string, params *entity.BlockDeviceParams) (*entity.BlockDevice, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | blockDevice := new(entity.BlockDevice) 39 | err = b.client(systemID).Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, blockDevice) 41 | }) 42 | 43 | return blockDevice, err 44 | } 45 | -------------------------------------------------------------------------------- /test/testdata/maas/vlans.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "vid": 0, 4 | "mtu": 1500, 5 | "dhcp_on": false, 6 | "external_dhcp": null, 7 | "relay_vlan": null, 8 | "space": "undefined", 9 | "fabric_id": 10, 10 | "secondary_rack": null, 11 | "fabric": "fabric-10", 12 | "id": 5014, 13 | "primary_rack": null, 14 | "name": "untagged", 15 | "resource_uri": "/MAAS/api/2.0/vlans/5014/" 16 | }, 17 | { 18 | "vid": 1, 19 | "mtu": 1500, 20 | "dhcp_on": false, 21 | "external_dhcp": null, 22 | "relay_vlan": { 23 | "vid": 0, 24 | "mtu": 1500, 25 | "dhcp_on": false, 26 | "external_dhcp": null, 27 | "relay_vlan": null, 28 | "space": "undefined", 29 | "fabric_id": 10, 30 | "secondary_rack": null, 31 | "fabric": "fabric-10", 32 | "id": 5014, 33 | "primary_rack": null, 34 | "name": "untagged", 35 | "resource_uri": "/MAAS/api/2.0/vlans/5014/" 36 | }, 37 | "space": "undefined", 38 | "fabric_id": 10, 39 | "secondary_rack": null, 40 | "fabric": "fabric-10", 41 | "id": 5015, 42 | "primary_rack": null, 43 | "name": "untagged", 44 | "resource_uri": "/MAAS/api/2.0/vlans/5015/" 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /client/dns_resource.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // DNSResource implements api.DNSResource 14 | type DNSResource struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (d *DNSResource) client(id int) APIClient { 19 | return d.APIClient.GetSubObject(fmt.Sprintf("dnsresources/%v", id)) 20 | } 21 | 22 | // Get fetches a given DNSResource 23 | func (d *DNSResource) Get(id int) (*entity.DNSResource, error) { 24 | dnsResource := new(entity.DNSResource) 25 | err := d.client(id).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, dnsResource) 27 | }) 28 | 29 | return dnsResource, err 30 | } 31 | 32 | // Update updates a given DNSResource 33 | func (d *DNSResource) Update(id int, params *entity.DNSResourceParams) (*entity.DNSResource, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | dnsResource := new(entity.DNSResource) 40 | err = d.client(id).Put(qsp, func(data []byte) error { 41 | return json.Unmarshal(data, dnsResource) 42 | }) 43 | 44 | return dnsResource, err 45 | } 46 | 47 | // Delete deletes a given DNSResource 48 | func (d *DNSResource) Delete(id int) error { 49 | return d.client(id).Delete() 50 | } 51 | -------------------------------------------------------------------------------- /client/package_repositories.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // PackageRepositories implements api.PackageRepositories 13 | type PackageRepositories struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (p *PackageRepositories) client() APIClient { 18 | return p.APIClient.GetSubObject("package-repositories") 19 | } 20 | 21 | // Get fetches a list of PackageRepositories 22 | func (p *PackageRepositories) Get() ([]entity.PackageRepository, error) { 23 | packageRepositories := make([]entity.PackageRepository, 0) 24 | err := p.client().Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &packageRepositories) 26 | }) 27 | 28 | return packageRepositories, err 29 | } 30 | 31 | // Create creates a new PackageRepository 32 | func (p *PackageRepositories) Create(packageRepositoryParams *entity.PackageRepositoryParams) (*entity.PackageRepository, error) { 33 | qsp, err := query.Values(packageRepositoryParams) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | packageRepository := new(entity.PackageRepository) 39 | err = p.client().Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, packageRepository) 41 | }) 42 | 43 | return packageRepository, err 44 | } 45 | -------------------------------------------------------------------------------- /client/raid.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // RAID Contains functionality for manipulating the RAID entity. 14 | type RAID struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (r *RAID) client(systemID string, id int) APIClient { 19 | return r.APIClient.GetSubObject("nodes").GetSubObject(systemID).GetSubObject("raid").GetSubObject(fmt.Sprintf("%v", id)) 20 | } 21 | 22 | // Get RAID details. 23 | func (r *RAID) Get(systemID string, id int) (*entity.RAID, error) { 24 | raid := new(entity.RAID) 25 | err := r.client(systemID, id).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, raid) 27 | }) 28 | 29 | return raid, err 30 | } 31 | 32 | // Update RAID. 33 | func (r *RAID) Update(systemID string, id int, params *entity.RAIDUpdateParams) (*entity.RAID, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | raid := new(entity.RAID) 40 | err = r.client(systemID, id).Put(qsp, func(data []byte) error { 41 | return json.Unmarshal(data, raid) 42 | }) 43 | 44 | return raid, err 45 | } 46 | 47 | // Delete RAID. 48 | func (r *RAID) Delete(systemID string, id int) error { 49 | return r.client(systemID, id).Delete() 50 | } 51 | -------------------------------------------------------------------------------- /client/volume_groups.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // VolumeGroups implements the api.VolumeGroups interface 13 | type VolumeGroups struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (v *VolumeGroups) client(systemID string) APIClient { 18 | return v.APIClient.GetSubObject("nodes").GetSubObject(systemID).GetSubObject("volume-groups") 19 | } 20 | 21 | // Get fetches a list of VolumeGroups for a given system_id 22 | func (v *VolumeGroups) Get(systemID string) ([]entity.VolumeGroup, error) { 23 | volumeGroups := make([]entity.VolumeGroup, 0) 24 | err := v.client(systemID).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &volumeGroups) 26 | }) 27 | 28 | return volumeGroups, err 29 | } 30 | 31 | // Create creates a new VolumeGroup for a given system_id 32 | func (v *VolumeGroups) Create(systemID string, params *entity.VolumeGroupCreateParams) (*entity.VolumeGroup, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | volumeGroup := new(entity.VolumeGroup) 39 | err = v.client(systemID).Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, volumeGroup) 41 | }) 42 | 43 | return volumeGroup, err 44 | } 45 | -------------------------------------------------------------------------------- /client/boot_source.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // BootSource implements api.BootSource 14 | type BootSource struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (b *BootSource) client(id int) APIClient { 19 | return b.APIClient.GetSubObject("boot-sources").GetSubObject(fmt.Sprintf("%v", id)) 20 | } 21 | 22 | // Get fetches a boot source with a given id 23 | func (b *BootSource) Get(id int) (*entity.BootSource, error) { 24 | bootSource := new(entity.BootSource) 25 | err := b.client(id).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, bootSource) 27 | }) 28 | 29 | return bootSource, err 30 | } 31 | 32 | // Update updates a given boot source 33 | func (b *BootSource) Update(id int, params *entity.BootSourceParams) (*entity.BootSource, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | bootSource := new(entity.BootSource) 40 | err = b.client(id).Put(qsp, func(data []byte) error { 41 | return json.Unmarshal(data, bootSource) 42 | }) 43 | 44 | return bootSource, err 45 | } 46 | 47 | // Delete deletes a given boot source 48 | func (b *BootSource) Delete(id int) error { 49 | return b.client(id).Delete() 50 | } 51 | -------------------------------------------------------------------------------- /client/vlan.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // VLAN implements api.VLAN 13 | type VLAN struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (v *VLAN) client(fabricID int, vid int) APIClient { 18 | return v.APIClient. 19 | GetSubObject("fabrics"). 20 | GetSubObject(fmt.Sprintf("%v", fabricID)). 21 | GetSubObject("vlans"). 22 | GetSubObject(fmt.Sprintf("%v", vid)) 23 | } 24 | 25 | // Get fetches a VLAN by fabric id and VLAN id 26 | func (v *VLAN) Get(fabricID int, vid int) (*entity.VLAN, error) { 27 | vlan := new(entity.VLAN) 28 | err := v.client(fabricID, vid).Get("", url.Values{}, func(data []byte) error { 29 | return json.Unmarshal(data, vlan) 30 | }) 31 | 32 | return vlan, err 33 | } 34 | 35 | // Update updates a given VLAN 36 | func (v *VLAN) Update(fabricID int, vid int, params *entity.VLANParams) (*entity.VLAN, error) { 37 | qsp, err := query.Values(params) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | vlan := new(entity.VLAN) 43 | err = v.client(fabricID, vid).Put(qsp, func(data []byte) error { 44 | return json.Unmarshal(data, vlan) 45 | }) 46 | 47 | return vlan, err 48 | } 49 | 50 | // Delete deletes a given VLAN 51 | func (v *VLAN) Delete(fabricID int, vid int) error { 52 | return v.client(fabricID, vid).Delete() 53 | } 54 | -------------------------------------------------------------------------------- /client/node_scripts.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/canonical/gomaasclient/entity" 7 | "github.com/google/go-querystring/query" 8 | ) 9 | 10 | // NodeScripts implements api.NodeScripts 11 | type NodeScripts struct { 12 | APIClient APIClient 13 | } 14 | 15 | func (ns *NodeScripts) client() APIClient { 16 | return ns.APIClient.GetSubObject("scripts") 17 | } 18 | 19 | // Get fetches a list of NodeScripts 20 | func (ns *NodeScripts) Get(nodeScriptParams *entity.NodeScriptReadParams) ([]entity.NodeScript, error) { 21 | qsp, err := query.Values(nodeScriptParams) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | nodeScripts := make([]entity.NodeScript, 0) 27 | err = ns.client().Get("", qsp, func(data []byte) error { 28 | return json.Unmarshal(data, &nodeScripts) 29 | }) 30 | 31 | return nodeScripts, err 32 | } 33 | 34 | // Create creates a new NodeScript 35 | func (ns *NodeScripts) Create(nodeScriptParams *entity.NodeScriptParams, script []byte) (*entity.NodeScript, error) { 36 | qsp, err := query.Values(nodeScriptParams) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | files := map[string][]byte{} 42 | if script != nil { 43 | files["script"] = script 44 | } 45 | 46 | nodeScript := new(entity.NodeScript) 47 | err = ns.client().PostFiles("", qsp, files, func(data []byte) error { 48 | return json.Unmarshal(data, nodeScript) 49 | }) 50 | 51 | return nodeScript, err 52 | } 53 | -------------------------------------------------------------------------------- /client/bcache_cache_sets.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/google/go-querystring/query" 9 | 10 | "github.com/canonical/gomaasclient/entity" 11 | ) 12 | 13 | // BCacheCacheSets contains functionality for manipulating the BCacheCacheSets entity. 14 | type BCacheCacheSets struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (b *BCacheCacheSets) client(systemID string) APIClient { 19 | return b.APIClient.GetSubObject("nodes").GetSubObject(systemID).GetSubObject("bcache-cache-sets") 20 | } 21 | 22 | // Get BCacheCacheSets of a machine. 23 | func (b *BCacheCacheSets) Get(systemID string) ([]entity.BCacheCacheSet, error) { 24 | bCacheCacheSets := make([]entity.BCacheCacheSet, 0) 25 | err := b.client(systemID).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, &bCacheCacheSets) 27 | }) 28 | 29 | return bCacheCacheSets, err 30 | } 31 | 32 | // Create a BCacheCacheSet of a machine. 33 | func (b *BCacheCacheSets) Create(systemID string, params *entity.BCacheCacheSetParams) (*entity.BCacheCacheSet, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | bCacheCacheSet := new(entity.BCacheCacheSet) 40 | err = b.client(systemID).Post("", qsp, func(data []byte) error { 41 | return json.Unmarshal(data, bCacheCacheSet) 42 | }) 43 | 44 | return bCacheCacheSet, err 45 | } 46 | -------------------------------------------------------------------------------- /entity/package_repository.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type PackageRepository struct { 4 | Name string `json:"name,omitempty"` 5 | URL string `json:"url,omitempty"` 6 | Key string `json:"key,omitempty"` 7 | ResourceURI string `json:"resource_uri,omitempty"` 8 | Distributions []string `json:"distributions,omitempty"` 9 | DisabledPockets []string `json:"disabled_pockets,omitempty"` 10 | DisabledComponents []string `json:"disabled_components,omitempty"` 11 | Components []string `json:"components,omitempty"` 12 | Arches []string `json:"arches,omitempty"` 13 | ID int `json:"id,omitempty"` 14 | DisableSources bool `json:"disable_sources,omitempty"` 15 | Enabled bool `json:"enabled,omitempty"` 16 | } 17 | 18 | type PackageRepositoryParams struct { 19 | Name string `url:"name,omitempty"` 20 | URL string `url:"url,omitempty"` 21 | Distributions string `url:"distributions,omitempty"` 22 | DisabledPockets string `url:"disabled_pockets,omitempty"` 23 | DisabledComponents string `url:"disabled_components,omitempty"` 24 | Components string `url:"components,omitempty"` 25 | Arches string `url:"arches,omitempty"` 26 | Key string `url:"key,omitempty"` 27 | DisableSources bool `url:"disable_sources,omitempty"` 28 | Enabled bool `url:"enabled,omitempty"` 29 | } 30 | -------------------------------------------------------------------------------- /client/bcache.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // BCache Contains functionality for manipulating the BCache entity. 14 | type BCache struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (b *BCache) client(systemID string, id int) APIClient { 19 | return b.APIClient. 20 | GetSubObject("nodes").GetSubObject(systemID). 21 | GetSubObject("bcache").GetSubObject(fmt.Sprintf("%v", id)) 22 | } 23 | 24 | // Get BCache details. 25 | func (b *BCache) Get(systemID string, id int) (*entity.BCache, error) { 26 | bCache := new(entity.BCache) 27 | err := b.client(systemID, id).Get("", url.Values{}, func(data []byte) error { 28 | return json.Unmarshal(data, bCache) 29 | }) 30 | 31 | return bCache, err 32 | } 33 | 34 | // Update BCache. 35 | func (b *BCache) Update(systemID string, id int, params *entity.BCacheParams) (*entity.BCache, error) { 36 | qsp, err := query.Values(params) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | bCache := new(entity.BCache) 42 | err = b.client(systemID, id).Put(qsp, func(data []byte) error { 43 | return json.Unmarshal(data, bCache) 44 | }) 45 | 46 | return bCache, err 47 | } 48 | 49 | // Delete BCache. 50 | func (b *BCache) Delete(systemID string, id int) error { 51 | return b.client(systemID, id).Delete() 52 | } 53 | -------------------------------------------------------------------------------- /client/dns_resource_records.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // DNSResourceRecords implements api.DNSResourceRecords 12 | type DNSResourceRecords struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (d *DNSResourceRecords) client() APIClient { 17 | return d.APIClient.GetSubObject("dnsresourcerecords") 18 | } 19 | 20 | // Get fetches a list of DNSResourceRecord objectts 21 | func (d *DNSResourceRecords) Get(params *entity.DNSResourceRecordsParams) ([]entity.DNSResourceRecord, error) { 22 | qsp, err := query.Values(params) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | dnsResourceRecords := make([]entity.DNSResourceRecord, 0) 28 | err = d.client().Get("", qsp, func(data []byte) error { 29 | return json.Unmarshal(data, &dnsResourceRecords) 30 | }) 31 | 32 | return dnsResourceRecords, err 33 | } 34 | 35 | // Create creates a new DNSResourceRecord 36 | func (d *DNSResourceRecords) Create(params *entity.DNSResourceRecordParams) (*entity.DNSResourceRecord, error) { 37 | qsp, err := query.Values(params) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | dnsResourceRecord := new(entity.DNSResourceRecord) 43 | err = d.client().Post("", qsp, func(data []byte) error { 44 | return json.Unmarshal(data, dnsResourceRecord) 45 | }) 46 | 47 | return dnsResourceRecord, err 48 | } 49 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | Releases are automated with GitHub Actions and GoReleaser. There are several manual steps to complete: 4 | 5 | 1. Decide on the version number by reviewing commits since the last release. The provider versions follow [semantic versioning](https://semver.org/). 6 | 7 | 1. Start the release action: 8 | 9 | Assuming `upstream` is the remote name pointing to the `canonical/gomaasclient` repository: 10 | 1. First, checkout the master branch and pull the latest changes: 11 | 12 | ```shell 13 | git switch master 14 | git pull upstream master 15 | ``` 16 | 1. Verify the latest commit is the same as the latest on the remote master branch. 17 | 18 | 1. Create a new tag and push it to the remote repository to trigger the release action: 19 | 20 | ```shell 21 | git tag vX.Y.Z 22 | git push upstream tag vX.Y.Z 23 | ``` 24 | The release action can be viewed under [Actions](https://github.com/canonical/gomaasclient/actions/workflows/release.yml). 25 | 26 | 1. Publish the release: 27 | 28 | The GitHub Action creates a "draft" release. Go to [Releases](https://github.com/canonical/gomaasclient/releases) to open it, select `edit`, and select `Publish release` if you are happy. 29 | 30 | 1. Verify the release is published: 31 | 1. Check the release is now the latest published under [Releases](https://github.com/canonical/gomaasclient/releases). 32 | 33 | You should be able to install the new version after a few minutes. 34 | -------------------------------------------------------------------------------- /test/testdata/maas/block_device.json: -------------------------------------------------------------------------------- 1 | { 2 | "firmware_version": null, 3 | "system_id": "y7388k", 4 | "block_size": 1024000, 5 | "available_size": 1000000000, 6 | "model": "fakemodel", 7 | "serial": "123", 8 | "used_size": 0, 9 | "tags": [], 10 | "partition_table_type": null, 11 | "partitions": [ 12 | { 13 | "uuid": "95c8571a-9477-4b83-ae15-f160ab162815", 14 | "size": 7990149120, 15 | "bootable": false, 16 | "tags": [], 17 | "system_id": "yh4r38", 18 | "id": 13, 19 | "device_id": 17, 20 | "filesystem": { 21 | "fstype": "ext4", 22 | "label": "root", 23 | "uuid": "89d2a569-d481-493f-9d67-308f05f6d1fb", 24 | "mount_point": "/", 25 | "mount_options": null 26 | }, 27 | "type": "partition", 28 | "used_for": "ext4 formatted filesystem mounted at /", 29 | "path": "/dev/disk/by-dname/vda-part2", 30 | "resource_uri": "/MAAS/api/2.0/nodes/yh4r38/blockdevices/17/partition/13" 31 | } 32 | ], 33 | "path": "/dev/disk/by-dname/newblockdevice", 34 | "size": 1000000000, 35 | "id_path": "", 36 | "filesystem": null, 37 | "storage_pool": null, 38 | "name": "newblockdevice", 39 | "used_for": "Unused", 40 | "id": 73, 41 | "type": "physical", 42 | "uuid": null, 43 | "resource_uri": "/MAAS/api/2.0/nodes/y7388k/blockdevices/73/" 44 | } -------------------------------------------------------------------------------- /entity/node/status.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | // Status correlates to a status code returned from the MAAS API. 4 | type Status int 5 | 6 | // The statuses are defined in src/maasserver/enum.py as of 06-2018, which can 7 | // be found at https://github.com/maas/maas/blob/master/src/maasserver/enum.py 8 | // The definitions are in `class NODE_STATUS`, which starts on L48 right now. 9 | // The statuses map to sequential ints, hence the iota - if a status is added or 10 | // changed, presumably they will maintain the sequential numbering. This way we only 11 | // have to maintain the correct order instead of having to ensure each value is 12 | // mapped correctly. 13 | // StatusDefault, defined at the bottom, is an exception - it has the same 14 | // value as StatusNew. 15 | // There are some smoke tests in node_test.go to verify some of the more 16 | // relevant statuses such as "commissioning" and "deployed". 17 | const ( 18 | StatusNew Status = iota 19 | StatusCommissioning 20 | StatusFailedCommissioning 21 | StatusMissing 22 | StatusReady 23 | StatusReserved 24 | StatusDeployed 25 | StatusRetired 26 | StatusBroken 27 | StatusDeploying 28 | StatusAllocated 29 | StatusFailedDeployment 30 | StatusReleasing 31 | StatusFailedReleasing 32 | StatusDiskErasing 33 | StatusFailedDiskErasing 34 | StatusRescueMode 35 | StatusEnteringRescureMode 36 | StatusFailedEnteringRescueMode 37 | StatusExitingRescueMode 38 | StatusFailedExitingRescueMode 39 | StatusTesting 40 | StatusFailedTesting 41 | 42 | StatusDefault Status = 0 43 | ) 44 | -------------------------------------------------------------------------------- /client/boot_source_selections.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // BootSourceSelections implements api.BootSourceSelections 13 | type BootSourceSelections struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (b *BootSourceSelections) client(bootSourceID int) APIClient { 18 | return b.APIClient.GetSubObject("boot-sources"). 19 | GetSubObject(fmt.Sprintf("%v", bootSourceID)). 20 | GetSubObject("selections") 21 | } 22 | 23 | // Get fetches a list of BootSourceSelection objects 24 | func (b *BootSourceSelections) Get(bootSourceID int) ([]entity.BootSourceSelection, error) { 25 | bootSourceSelections := make([]entity.BootSourceSelection, 0) 26 | err := b.client(bootSourceID).Get("", url.Values{}, func(data []byte) error { 27 | return json.Unmarshal(data, &bootSourceSelections) 28 | }) 29 | 30 | return bootSourceSelections, err 31 | } 32 | 33 | // Create creates a BootSourceSelection object 34 | func (b *BootSourceSelections) Create(bootSourceID int, params *entity.BootSourceSelectionParams) (*entity.BootSourceSelection, error) { 35 | qsp, err := query.Values(params) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | bootSourceSelection := new(entity.BootSourceSelection) 41 | err = b.client(bootSourceID).Post("", qsp, func(data []byte) error { 42 | return json.Unmarshal(data, bootSourceSelection) 43 | }) 44 | 45 | return bootSourceSelection, err 46 | } 47 | -------------------------------------------------------------------------------- /client/license_key.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // LicenseKey implements api.LicenseKey 12 | type LicenseKey struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (l *LicenseKey) client(osystem string, distroSeries string) APIClient { 17 | return l.APIClient. 18 | GetSubObject("license-key"). 19 | GetSubObject(osystem). 20 | GetSubObject(distroSeries) 21 | } 22 | 23 | // Get fetches a LicenseKey object 24 | func (l *LicenseKey) Get(osystem string, distroSeries string) (*entity.LicenseKey, error) { 25 | licenseKey := new(entity.LicenseKey) 26 | err := l.client(osystem, distroSeries).Get("", url.Values{}, func(data []byte) error { 27 | return json.Unmarshal(data, licenseKey) 28 | }) 29 | 30 | return licenseKey, err 31 | } 32 | 33 | // Update updates a given LicenseKey 34 | func (l *LicenseKey) Update(osystem string, distroSeries string, params *entity.LicenseKeyParams) (*entity.LicenseKey, error) { 35 | qsp, err := query.Values(params) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | licenseKey := new(entity.LicenseKey) 41 | err = l.client(osystem, distroSeries).Put(qsp, func(data []byte) error { 42 | return json.Unmarshal(data, licenseKey) 43 | }) 44 | 45 | return licenseKey, err 46 | } 47 | 48 | // Delete deletes a given LicenseKey 49 | func (l *LicenseKey) Delete(osystem string, distroSeries string) error { 50 | return l.client(osystem, distroSeries).Delete() 51 | } 52 | -------------------------------------------------------------------------------- /client/dns_resource_record.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // DNSResourceRecord implements api.DNSResourceRecord 14 | type DNSResourceRecord struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (d *DNSResourceRecord) client(id int) APIClient { 19 | return d.APIClient.GetSubObject(fmt.Sprintf("dnsresourcerecords/%v", id)) 20 | } 21 | 22 | // Get fetches a given DNSResourceRecord 23 | func (d *DNSResourceRecord) Get(id int) (*entity.DNSResourceRecord, error) { 24 | dnsResourceRecord := new(entity.DNSResourceRecord) 25 | err := d.client(id).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, dnsResourceRecord) 27 | }) 28 | 29 | return dnsResourceRecord, err 30 | } 31 | 32 | // Update updates a given DNSResourceRecord 33 | func (d *DNSResourceRecord) Update(id int, params *entity.DNSResourceRecordParams) (*entity.DNSResourceRecord, error) { 34 | qsp, err := query.Values(params) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | dnsResourceRecord := new(entity.DNSResourceRecord) 40 | err = d.client(id).Put(qsp, func(data []byte) error { 41 | return json.Unmarshal(data, dnsResourceRecord) 42 | }) 43 | 44 | return dnsResourceRecord, err 45 | } 46 | 47 | // Delete deletes a given DNSResourceRecord 48 | func (d *DNSResourceRecord) Delete(id int) error { 49 | return d.client(id).Delete() 50 | } 51 | -------------------------------------------------------------------------------- /entity/vlan.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // VLAN represents the MAAS VLAN endpoint. 4 | type VLAN struct { 5 | RelayVLAN *VLAN `json:"relay_vlan,omitempty"` 6 | Name string `json:"name,omitempty"` 7 | ExternalDHCP string `json:"external_dhcp,omitempty"` 8 | Description string `json:"description,omitempty"` 9 | PrimaryRack string `json:"primary_rack,omitempty"` 10 | SecondaryRack string `json:"secondary_rack,omitempty"` 11 | Space string `json:"space,omitempty"` 12 | Fabric string `json:"fabric,omitempty"` 13 | ResourceURI string `json:"resource_uri,omitempty"` 14 | MTU int `json:"mtu,omitempty"` 15 | FabricID int `json:"fabric_id,omitempty"` 16 | VID int `json:"vid,omitempty"` 17 | ID int `json:"id,omitempty"` 18 | DHCPOn bool `json:"dhcp_on,omitempty"` 19 | } 20 | 21 | // VLANParams contains the options for a POST request to the vlans endpoint. 22 | // Only the VID field is required. If Space is empty or the string "undefined", 23 | // the VLAN will be created in the 'undefined' space. 24 | type VLANParams struct { 25 | PrimaryRack *string `url:"primary_rack,omitempty"` 26 | SecondaryRack *string `url:"secondary_rack,omitempty"` 27 | RelayVLAN *string `url:"relay_vlan,omitempty"` 28 | Name string `url:"name,omitempty"` 29 | Description string `url:"description,omitempty"` 30 | Space string `url:"space,omitempty"` 31 | VID int `url:"vid,omitempty"` 32 | MTU int `url:"mtu,omitempty"` 33 | DHCPOn bool `url:"dhcp_on"` 34 | } 35 | -------------------------------------------------------------------------------- /client/domain.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Domain implements api.Domain 13 | type Domain struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (d *Domain) client(id int) APIClient { 18 | return d.APIClient.GetSubObject(fmt.Sprintf("domains/%v", id)) 19 | } 20 | 21 | // Get fetches a given Domain 22 | func (d *Domain) Get(id int) (*entity.Domain, error) { 23 | domain := new(entity.Domain) 24 | err := d.client(id).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, domain) 26 | }) 27 | 28 | return domain, err 29 | } 30 | 31 | // SetDefault sets the given Domain as the default Domain 32 | func (d *Domain) SetDefault(id int) (*entity.Domain, error) { 33 | domain := new(entity.Domain) 34 | err := d.client(id).Post("set_default", url.Values{}, func(data []byte) error { 35 | return json.Unmarshal(data, domain) 36 | }) 37 | 38 | return domain, err 39 | } 40 | 41 | // Update updates the given Domain 42 | func (d *Domain) Update(id int, params *entity.DomainParams) (*entity.Domain, error) { 43 | qsp, err := query.Values(params) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | domain := new(entity.Domain) 49 | err = d.client(id).Put(qsp, func(data []byte) error { 50 | return json.Unmarshal(data, domain) 51 | }) 52 | 53 | return domain, err 54 | } 55 | 56 | // Delete deletes a given Domain 57 | func (d *Domain) Delete(id int) error { 58 | return d.client(id).Delete() 59 | } 60 | -------------------------------------------------------------------------------- /entity/events.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/canonical/gomaasclient/entity/event" 4 | 5 | // Event represents an event for MAAS node 6 | type Event struct { 7 | UserName string `json:"username,omitempty"` 8 | Node string `json:"node,omitempty"` 9 | Hostname string `json:"hostname,omitempty"` 10 | Created string `json:"created,omitempty"` 11 | Type string `json:"type,omitempty"` 12 | Description string `json:"description,omitempty"` 13 | Level event.LogLevel `json:"level,omitempty"` 14 | ID int `json:"id,omitempty"` 15 | } 16 | 17 | // EventsResp represents the MAAS Events endpoint 18 | type EventsResp struct { 19 | NextURI string `json:"next_uri,omitempty"` 20 | PrevURI string `json:"prev_uri,omitempty"` 21 | Events []Event `json:"events,omitempty"` 22 | Count int `json:"count,omitempty"` 23 | } 24 | 25 | // EventParams enumerates the parameters for the event get operation 26 | type EventParams struct { 27 | Hostname string `url:"hostname,omitempty"` 28 | MACAddress string `url:"mac_address,omitempty"` 29 | ID string `url:"id,omitempty"` 30 | Zone string `url:"zone,omitempty"` 31 | AgentName string `url:"agent_name,omitempty"` 32 | Limit string `url:"limit,omitempty"` 33 | Before string `url:"before,omitempty"` 34 | After string `url:"after,omitempty"` 35 | Owner string `url:"owner,omitempty"` 36 | Level event.LogLevel `url:"level,omitempty"` 37 | } 38 | -------------------------------------------------------------------------------- /client/package_repository.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // PackageRepository implements api.PackageRepository 14 | type PackageRepository struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (p *PackageRepository) client(id int) APIClient { 19 | return p.APIClient.GetSubObject("package-repositories").GetSubObject(fmt.Sprintf("%v", id)) 20 | } 21 | 22 | // Get fetches a packageRepository 23 | func (p *PackageRepository) Get(id int) (*entity.PackageRepository, error) { 24 | packageRepository := new(entity.PackageRepository) 25 | err := p.client(id).Get("", url.Values{}, func(data []byte) error { 26 | return json.Unmarshal(data, packageRepository) 27 | }) 28 | 29 | return packageRepository, err 30 | } 31 | 32 | // Update updates a given PackageRepository 33 | func (p *PackageRepository) Update(id int, packageRepositoryParams *entity.PackageRepositoryParams) (*entity.PackageRepository, error) { 34 | qsp, err := query.Values(packageRepositoryParams) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | packageRepository := new(entity.PackageRepository) 40 | err = p.client(id).Put(qsp, func(data []byte) error { 41 | return json.Unmarshal(data, packageRepository) 42 | }) 43 | 44 | return packageRepository, err 45 | } 46 | 47 | // Delete deletes a given PackageRepository 48 | func (p *PackageRepository) Delete(id int) error { 49 | return p.client(id).Delete() 50 | } 51 | -------------------------------------------------------------------------------- /client/block_device_partitions.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // BlockDevicePartitions implements api.BlockDevicePartitions 13 | type BlockDevicePartitions struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (p *BlockDevicePartitions) client(systemID string, blockDeviceID int) APIClient { 18 | return p.APIClient.GetSubObject(fmt.Sprintf("nodes/%s/blockdevices/%v/partitions", systemID, blockDeviceID)) 19 | } 20 | 21 | // Get lists the BlockDevicePartition objects for a given system_id and BlockDevice id 22 | func (p *BlockDevicePartitions) Get(systemID string, blockDeviceID int) ([]entity.BlockDevicePartition, error) { 23 | partitions := make([]entity.BlockDevicePartition, 0) 24 | err := p.client(systemID, blockDeviceID).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, &partitions) 26 | }) 27 | 28 | return partitions, err 29 | } 30 | 31 | // Create creats a new BlockDevicePartition for a given system_id and BlockDevice id 32 | func (p *BlockDevicePartitions) Create(systemID string, blockDeviceID int, params *entity.BlockDevicePartitionParams) (*entity.BlockDevicePartition, error) { 33 | qsp, err := query.Values(params) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | partition := new(entity.BlockDevicePartition) 39 | err = p.client(systemID, blockDeviceID).Post("", qsp, func(data []byte) error { 40 | return json.Unmarshal(data, partition) 41 | }) 42 | 43 | return partition, err 44 | } 45 | -------------------------------------------------------------------------------- /client/ip_addresses.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/canonical/gomaasclient/entity" 7 | "github.com/google/go-querystring/query" 8 | ) 9 | 10 | // IPAddresses implements api.IPAddresses 11 | type IPAddresses struct { 12 | APIClient APIClient 13 | } 14 | 15 | func (i *IPAddresses) client() APIClient { 16 | return i.APIClient.GetSubObject("ipaddresses") 17 | } 18 | 19 | // Get fetches a specified set of IPAddresses 20 | func (i *IPAddresses) Get(params *entity.IPAddressesParams) ([]entity.IPAddress, error) { 21 | qsp, err := query.Values(params) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | ipAddresses := make([]entity.IPAddress, 0) 27 | err = i.client().Get("", qsp, func(data []byte) error { 28 | return json.Unmarshal(data, &ipAddresses) 29 | }) 30 | 31 | return ipAddresses, err 32 | } 33 | 34 | // Release releases a set of allocated IPAddresses 35 | func (i *IPAddresses) Release(params *entity.IPAddressesParams) error { 36 | qsp, err := query.Values(params) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | return i.client().Post("release", qsp, func(data []byte) error { return nil }) 42 | } 43 | 44 | // Reserve reserves a set of IPAddresses 45 | func (i *IPAddresses) Reserve(params *entity.IPAddressesParams) (*entity.IPAddress, error) { 46 | qsp, err := query.Values(params) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | ipAddress := new(entity.IPAddress) 52 | err = i.client().Post("reserve", qsp, func(data []byte) error { 53 | return json.Unmarshal(data, ipAddress) 54 | }) 55 | 56 | return ipAddress, err 57 | } 58 | -------------------------------------------------------------------------------- /client/notification.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Notification implements api.Notification 13 | type Notification struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (n *Notification) client(id int) APIClient { 18 | return n.APIClient. 19 | GetSubObject("notifications"). 20 | GetSubObject(fmt.Sprintf("%v", id)) 21 | } 22 | 23 | // Get fetches a Notification object 24 | func (n *Notification) Get(id int) (*entity.Notification, error) { 25 | notification := new(entity.Notification) 26 | err := n.client(id).Get("", url.Values{}, func(data []byte) error { 27 | return json.Unmarshal(data, notification) 28 | }) 29 | 30 | return notification, err 31 | } 32 | 33 | // Update updates a given Notification 34 | func (n *Notification) Update(id int, params *entity.NotificationParams) (*entity.Notification, error) { 35 | qsp, err := query.Values(params) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | notification := new(entity.Notification) 41 | err = n.client(id).Put(qsp, func(data []byte) error { 42 | return json.Unmarshal(data, notification) 43 | }) 44 | 45 | return notification, err 46 | } 47 | 48 | // Delete deletes a given Notification 49 | func (n *Notification) Delete(id int) error { 50 | return n.client(id).Delete() 51 | } 52 | 53 | // Dismiss dismisses a given Notification 54 | func (n *Notification) Dismiss(id int) error { 55 | return n.client(id).Post("dismiss", url.Values{}, func(data []byte) error { 56 | return nil 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /client/bcache_cache_set.go: -------------------------------------------------------------------------------- 1 | //nolint:dupl // disable dupl check on client for now 2 | package client 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/url" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // BCacheCacheSet Contains functionality for manipulating the BCacheCacheSet entity. 14 | type BCacheCacheSet struct { 15 | APIClient APIClient 16 | } 17 | 18 | func (b *BCacheCacheSet) client(systemID string, id int) APIClient { 19 | return b.APIClient. 20 | GetSubObject("nodes").GetSubObject(systemID). 21 | GetSubObject("bcache-cache-set").GetSubObject(fmt.Sprintf("%v", id)) 22 | } 23 | 24 | // Get BCacheCacheSet details. 25 | func (b *BCacheCacheSet) Get(systemID string, id int) (*entity.BCacheCacheSet, error) { 26 | bCacheCacheSet := new(entity.BCacheCacheSet) 27 | err := b.client(systemID, id).Get("", url.Values{}, func(data []byte) error { 28 | return json.Unmarshal(data, bCacheCacheSet) 29 | }) 30 | 31 | return bCacheCacheSet, err 32 | } 33 | 34 | // Update BCacheCacheSet. 35 | func (b *BCacheCacheSet) Update(systemID string, id int, params *entity.BCacheCacheSetParams) (*entity.BCacheCacheSet, error) { 36 | qsp, err := query.Values(params) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | bCacheCacheSet := new(entity.BCacheCacheSet) 42 | err = b.client(systemID, id).Put(qsp, func(data []byte) error { 43 | return json.Unmarshal(data, bCacheCacheSet) 44 | }) 45 | 46 | return bCacheCacheSet, err 47 | } 48 | 49 | // Delete BCacheCacheSet. 50 | func (b *BCacheCacheSet) Delete(systemID string, id int) error { 51 | return b.client(systemID, id).Delete() 52 | } 53 | -------------------------------------------------------------------------------- /test/testdata/maas/vm_host.json: -------------------------------------------------------------------------------- 1 | { 2 | "cpu_over_commit_ratio": 1.0, 3 | "zone": { 4 | "name": "default", 5 | "description": "", 6 | "id": 1, 7 | "resource_uri": "/MAAS/api/2.0/zones/default/" 8 | }, 9 | "pool": { 10 | "name": "default", 11 | "description": "Default pool", 12 | "id": 0, 13 | "resource_uri": "/MAAS/api/2.0/resourcepool/0/" 14 | }, 15 | "storage_pools": [ 16 | { 17 | "id": "70e0f355-ea3b-4cdd-b4b4-20a01a35617d", 18 | "name": "maas", 19 | "type": "dir", 20 | "path": "/var/lib/libvirt/maas-images", 21 | "total": 47751942144, 22 | "used": 8000000000, 23 | "available": 39751942144, 24 | "default": true 25 | } 26 | ], 27 | "name": "newpodname", 28 | "memory_over_commit_ratio": 1.0, 29 | "type": "virsh", 30 | "host": { 31 | "system_id": null, 32 | "__incomplete__": true 33 | }, 34 | "architectures": [ 35 | "amd64/generic" 36 | ], 37 | "tags": [ 38 | "virtual" 39 | ], 40 | "default_macvlan_mode": "", 41 | "used": { 42 | "cores": 1, 43 | "memory": 1024, 44 | "local_storage": 8000000000 45 | }, 46 | "available": { 47 | "cores": 7, 48 | "memory": 14892, 49 | "local_storage": 39751942144 50 | }, 51 | "id": 1, 52 | "capabilities": [ 53 | "composable", 54 | "dynamic_local_storage", 55 | "over_commit", 56 | "storage_pools" 57 | ], 58 | "total": { 59 | "cores": 8, 60 | "memory": 15916, 61 | "local_storage": 47751942144 62 | }, 63 | "resource_uri": "/MAAS/api/2.0/pods/1/" 64 | } -------------------------------------------------------------------------------- /client/boot_source_selection.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // BootSourceSelection implements api.BootSourceSelection 13 | type BootSourceSelection struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (b *BootSourceSelection) client(bootSourceID int, id int) APIClient { 18 | return b.APIClient.GetSubObject("boot-sources"). 19 | GetSubObject(fmt.Sprintf("%v", bootSourceID)). 20 | GetSubObject("selections"). 21 | GetSubObject(fmt.Sprintf("%v", id)) 22 | } 23 | 24 | // Get fetches a BootSourceSelection for the given bootSourceID and BootSourceSelection id 25 | func (b *BootSourceSelection) Get(bootSourceID int, id int) (*entity.BootSourceSelection, error) { 26 | bootSourceSelection := new(entity.BootSourceSelection) 27 | err := b.client(bootSourceID, id).Get("", url.Values{}, func(data []byte) error { 28 | return json.Unmarshal(data, bootSourceSelection) 29 | }) 30 | 31 | return bootSourceSelection, err 32 | } 33 | 34 | // Update updates a given BootSourceSelection 35 | func (b *BootSourceSelection) Update(bootSourceID int, id int, params *entity.BootSourceSelectionParams) (*entity.BootSourceSelection, error) { 36 | qsp, err := query.Values(params) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | bootSourceSelection := new(entity.BootSourceSelection) 42 | err = b.client(bootSourceID, id).Put(qsp, func(data []byte) error { 43 | return json.Unmarshal(data, bootSourceSelection) 44 | }) 45 | 46 | return bootSourceSelection, err 47 | } 48 | 49 | // Delete deletes a given BootSourceSelection 50 | func (b *BootSourceSelection) Delete(bootSourceID int, id int) error { 51 | return b.client(bootSourceID, id).Delete() 52 | } 53 | -------------------------------------------------------------------------------- /client/device.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // Device implements api.Device 13 | type Device struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (d *Device) client(systemID string) APIClient { 18 | return d.APIClient.GetSubObject("devices").GetSubObject(fmt.Sprintf("%v", systemID)) 19 | } 20 | 21 | // Get fetches a device with a given system_id 22 | func (d *Device) Get(systemID string) (*entity.Device, error) { 23 | device := new(entity.Device) 24 | err := d.client(systemID).Get("", url.Values{}, func(data []byte) error { 25 | return json.Unmarshal(data, device) 26 | }) 27 | 28 | return device, err 29 | } 30 | 31 | // Update updates a given Device 32 | func (d *Device) Update(systemID string, deviceParams *entity.DeviceUpdateParams) (*entity.Device, error) { 33 | qsp, err := query.Values(deviceParams) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | device := new(entity.Device) 39 | err = d.client(systemID).Put(qsp, func(data []byte) error { 40 | return json.Unmarshal(data, device) 41 | }) 42 | 43 | return device, err 44 | } 45 | 46 | // Delete deletes a given Device 47 | func (d *Device) Delete(systemID string) error { 48 | return d.client(systemID).Delete() 49 | } 50 | 51 | // SetWorkloadAnnotations add, modify or remove workload annotations for given Device 52 | func (d *Device) SetWorkloadAnnotations(systemID string, params map[string]string) (*entity.Device, error) { 53 | qsp := url.Values{} 54 | for k, v := range params { 55 | qsp.Add(k, v) 56 | } 57 | 58 | device := new(entity.Device) 59 | err := d.client(systemID).Post("set_workload_annotations", qsp, func(data []byte) error { 60 | return json.Unmarshal(data, &device) 61 | }) 62 | 63 | return device, err 64 | } 65 | -------------------------------------------------------------------------------- /client/account.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | ) 9 | 10 | // Account implements api.Account 11 | type Account struct { 12 | APIClient APIClient 13 | } 14 | 15 | func (a *Account) client() APIClient { 16 | return a.APIClient.GetSubObject("account") 17 | } 18 | 19 | // CreateAuthorisationToken creates authorisation token with provided name 20 | func (a *Account) CreateAuthorisationToken(name string) (*entity.AuthorisationToken, error) { 21 | qsp := url.Values{} 22 | qsp.Set("name", name) 23 | 24 | authToken := new(entity.AuthorisationToken) 25 | err := a.client().Post("create_authorisation_token", qsp, func(data []byte) error { 26 | return json.Unmarshal(data, authToken) 27 | }) 28 | 29 | return authToken, err 30 | } 31 | 32 | // DeleteAuthorisationToken deletes authorisation token with provided key 33 | func (a *Account) DeleteAuthorisationToken(key string) error { 34 | qsp := url.Values{} 35 | qsp.Set("token_key", key) 36 | err := a.client().Post("delete_authorisation_token", qsp, func(data []byte) error { return nil }) 37 | 38 | return err 39 | } 40 | 41 | // ListAuthorisationTokens Lists authorisation tokens 42 | func (a *Account) ListAuthorisationTokens() ([]entity.AuthorisationTokenListItem, error) { 43 | authToken := make([]entity.AuthorisationTokenListItem, 0) 44 | err := a.client().Get("list_authorisation_tokens", url.Values{}, func(data []byte) error { 45 | return json.Unmarshal(data, &authToken) 46 | }) 47 | 48 | return authToken, err 49 | } 50 | 51 | // UpdateTokenName updates given token with provided name 52 | func (a *Account) UpdateTokenName(name, token string) error { 53 | qsp := url.Values{} 54 | qsp.Set("name", name) 55 | qsp.Set("token", token) 56 | err := a.client().Post("update_token_name", qsp, func(data []byte) error { return nil }) 57 | 58 | return err 59 | } 60 | -------------------------------------------------------------------------------- /test/testdata/maas/subnets/ipranges.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "start": "172.16.2.2", 4 | "end": "172.16.2.2", 5 | "num_addresses": 1 6 | }, 7 | { 8 | "start": "172.16.2.5", 9 | "end": "172.16.2.10", 10 | "num_addresses": 6 11 | }, 12 | { 13 | "start": "172.16.2.12", 14 | "end": "172.16.2.25", 15 | "num_addresses": 14 16 | }, 17 | { 18 | "start": "172.16.2.27", 19 | "end": "172.16.2.61", 20 | "num_addresses": 35 21 | }, 22 | { 23 | "start": "172.16.2.64", 24 | "end": "172.16.2.100", 25 | "num_addresses": 37 26 | }, 27 | { 28 | "start": "172.16.2.102", 29 | "end": "172.16.2.108", 30 | "num_addresses": 7 31 | }, 32 | { 33 | "start": "172.16.2.110", 34 | "end": "172.16.2.110", 35 | "num_addresses": 1 36 | }, 37 | { 38 | "start": "172.16.2.112", 39 | "end": "172.16.2.115", 40 | "num_addresses": 4 41 | }, 42 | { 43 | "start": "172.16.2.117", 44 | "end": "172.16.2.133", 45 | "num_addresses": 17 46 | }, 47 | { 48 | "start": "172.16.2.135", 49 | "end": "172.16.2.173", 50 | "num_addresses": 39 51 | }, 52 | { 53 | "start": "172.16.2.175", 54 | "end": "172.16.2.205", 55 | "num_addresses": 31 56 | }, 57 | { 58 | "start": "172.16.2.207", 59 | "end": "172.16.2.234", 60 | "num_addresses": 28 61 | }, 62 | { 63 | "start": "172.16.2.236", 64 | "end": "172.16.2.236", 65 | "num_addresses": 1 66 | }, 67 | { 68 | "start": "172.16.2.238", 69 | "end": "172.16.2.251", 70 | "num_addresses": 14 71 | }, 72 | { 73 | "start": "172.16.2.253", 74 | "end": "172.16.2.254", 75 | "num_addresses": 2 76 | } 77 | ] -------------------------------------------------------------------------------- /client/boot_resources.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // BootResources implements api.BootResources 13 | type BootResources struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (b *BootResources) client() APIClient { 18 | return b.APIClient.GetSubObject("boot-resources") 19 | } 20 | 21 | // Get fetches a list of boot resources 22 | func (b *BootResources) Get(params *entity.BootResourcesReadParams) ([]entity.BootResource, error) { 23 | qsp, err := query.Values(params) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | bootResources := make([]entity.BootResource, 0) 29 | err = b.client().Get("", qsp, func(data []byte) error { 30 | return json.Unmarshal(data, &bootResources) 31 | }) 32 | 33 | return bootResources, err 34 | } 35 | 36 | // Create creates a new boot source 37 | func (b *BootResources) Create(params *entity.BootResourceParams) (*entity.BootResource, error) { 38 | return nil, fmt.Errorf("not implemented") 39 | } 40 | 41 | // Import imports boot resources to rack controllers 42 | func (b *BootResources) Import() error { 43 | return b.client().Post("import", url.Values{}, func(data []byte) error { 44 | return nil 45 | }) 46 | } 47 | 48 | // IsImporting returns importing status of boot resources importing to rack controllers 49 | func (b *BootResources) IsImporting() (bool, error) { 50 | isImporting := new(bool) 51 | err := b.client().Get("is_importing", url.Values{}, func(data []byte) error { 52 | return json.Unmarshal(data, isImporting) 53 | }) 54 | 55 | return *isImporting, err 56 | } 57 | 58 | // StopImport stops importing boot resources to rack controllers 59 | func (b *BootResources) StopImport() error { 60 | return b.client().Post("stop_import", url.Values{}, func(data []byte) error { 61 | return nil 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang MAAS Client 2 | 3 | ## :warning: Repository ownership and module name change 4 | 5 | The GoMAASClient repository now lives under the [Canonical GitHub organisation](https://github.com/canonical) with a new module name `github.com/canonical/gomaasclient`. 6 | 7 | If you are not using `GOPROXY=https://proxy.golang.org/cached-only` or not caching 8 | modules, your development environment or CI might encounter failures when trying 9 | to retrieve the module by its old name. 10 | 11 | 12 | If you encounter an error to the likes of: 13 | ```go 14 | module declares its path as: github.com/canonical/gomaasclient 15 | but was required as: github.com/maas/gomaasclient 16 | ``` 17 | 18 | Ensure you are pointing at the new module URL, which is `github.com/canonical/gomaasclient`. 19 | You can use the [replace](https://go.dev/ref/mod#go-mod-file-replace) directive, so 20 | you don't have to change old imports everywhere. 21 | 22 | --- 23 | 24 | This repository contains the following packages: 25 | 26 | * `api` - defines an interface to each MAAS API endpoint. 27 | * `entity` - defines types for the MAAS API endpoints' return types. 28 | * `client` - contains the MAAS client source code. 29 | 30 | ## Usage 31 | 32 | ```Go 33 | import ( 34 | gomaasclient "github.com/canonical/gomaasclient/client" 35 | "github.com/canonical/gomaasclient/entity" 36 | ) 37 | 38 | c, _ := gomaasclient.GetClient("", "", "2.0") 39 | 40 | // List MAAS machines 41 | machines, _ := c.Machines.Get(&entity.MachinesParams{}) 42 | 43 | // Get MAAS machine details 44 | machine, _ := c.Machine.Get(machines[0].SystemID) 45 | 46 | // List MAAS VM hosts 47 | vmHosts, _ := c.VMHosts.Get() 48 | ``` 49 | 50 | ## Credit 51 | 52 | This work was initially started by [Brian Hazeltine (@onwsk8r)](https://github.com/onwsk8r) as part of his [Terraform MAAS provider](https://github.com/Roblox/terraform-provider-maas) implementation. 53 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # gomaasclient testing workflow. 2 | name: Tests 3 | 4 | # This GitHub action runs your tests for each pull request and push. 5 | # Optionally, you can turn it on using a schedule for regular testing. 6 | on: 7 | pull_request: 8 | paths-ignore: 9 | - 'README.md' 10 | push: 11 | branches: 12 | - "master" 13 | paths-ignore: 14 | - 'README.md' 15 | 16 | # Testing only needs permissions to read the repository contents. 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | golangci: 22 | name: lint 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v6 26 | - uses: actions/setup-go@v6 27 | with: 28 | go-version: '1.20' 29 | cache: false 30 | - name: golangci-lint 31 | uses: golangci/golangci-lint-action@v9 32 | with: 33 | version: v2.1 34 | # Note: By default, the `.golangci.yml` file should be at the root of the repository. 35 | # The location of the configuration file can be changed by using `--config=` 36 | # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 37 | skip-cache: true 38 | install-mode: binary 39 | 40 | # Ensure project builds before running testing matrix 41 | build: 42 | name: Build 43 | runs-on: ubuntu-latest 44 | timeout-minutes: 5 45 | steps: 46 | - uses: actions/checkout@v6 47 | - uses: actions/setup-go@v6 48 | with: 49 | go-version-file: 'go.mod' 50 | cache: true 51 | - run: go mod download 52 | - run: go build -v ./... 53 | 54 | # Run unit tests 55 | test: 56 | name: gomaasclient Unit Tests 57 | needs: build 58 | runs-on: ubuntu-latest 59 | steps: 60 | - uses: actions/checkout@v6 61 | - uses: actions/setup-go@v6 62 | with: 63 | go-version-file: 'go.mod' 64 | cache: true 65 | - run: make test 66 | -------------------------------------------------------------------------------- /entity/device.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "net" 4 | 5 | type Device struct { 6 | OwnerData interface{} `json:"owner_data,omitempty"` 7 | Zone Zone `json:"zone,omitempty"` 8 | FQDN string `json:"fqdn,omitempty"` 9 | Hostname string `json:"hostname,omitempty"` 10 | SystemID string `json:"system_id,omitempty"` 11 | Owner string `json:"owner,omitempty"` 12 | Description string `json:"description,omitempty"` 13 | Parent string `json:"parent,omitempty"` 14 | ResourceURI string `json:"resource_uri,omitempty"` 15 | NodeTypeName string `json:"node_type_name,omitempty"` 16 | WorkloadAnnotations map[string]string `json:"workload_annotations,omitempty"` 17 | IPAddresses []net.IP `json:"ip_addresses,omitempty"` 18 | InterfaceSet []NetworkInterface `json:"interface_set,omitempty"` 19 | TagNames []string `json:"tag_names,omitempty"` 20 | Domain Domain `json:"domain,omitempty"` 21 | NodeType int `json:"node_type,omitempty"` 22 | AddressTTL int `json:"address_ttl,omitempty"` 23 | } 24 | 25 | type DeviceCreateParams struct { 26 | Hostname string `url:"hostname,omitempty"` 27 | Description string `url:"description,omitempty"` 28 | Domain string `url:"domain,omitempty"` 29 | Parent string `url:"parent,omitempty"` 30 | Zone string `url:"zone,omitempty"` 31 | MacAddresses []string `url:"mac_addresses,omitempty"` 32 | } 33 | 34 | type DeviceUpdateParams struct { 35 | Hostname string `url:"hostname,omitempty"` 36 | Description string `url:"description,omitempty"` 37 | Domain string `url:"domain,omitempty"` 38 | Parent string `url:"parent,omitempty"` 39 | Zone string `url:"zone,omitempty"` 40 | } 41 | -------------------------------------------------------------------------------- /entity/subnet.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // Subnet represents the MAAS Subnet endpoint. 8 | type Subnet struct { 9 | Space string `json:"space,omitempty"` 10 | CIDR string `json:"cidr,omitempty"` 11 | Name string `json:"name,omitempty"` 12 | ResourceURI string `json:"resource_uri,omitempty"` 13 | Description string `json:"description,omitempty"` 14 | GatewayIP net.IP `json:"gateway_ip,omitempty"` 15 | DNSServers []net.IP `json:"dns_servers,omitempty"` 16 | DisabledBootArchitectures []string `json:"disabled_boot_architectures,omitempty"` 17 | VLAN VLAN `json:"vlan,omitempty"` 18 | ID int `json:"id,omitempty"` 19 | RDNSMode int `json:"rdns_mode,omitempty"` 20 | AllowDNS bool `json:"allow_dns,omitempty"` 21 | Managed bool `json:"managed,omitempty"` 22 | ActiveDiscovery bool `json:"active_discovery,omitempty"` 23 | AllowProxy bool `json:"allow_proxy,omitempty"` 24 | } 25 | 26 | // SubnetParams contains the parameters for the POST operation on the Subnets endpoint. 27 | type SubnetParams struct { 28 | CIDR string `url:"cidr"` 29 | Name string `url:"name,omitempty"` 30 | Description string `url:"description,omitempty"` 31 | VLAN string `url:"vlan,omitempty"` 32 | Fabric string `url:"fabric,omitempty"` 33 | GatewayIP string `url:"gateway_ip,omitempty"` 34 | DNSServers []string `url:"dns_servers,omitempty"` 35 | VID int `url:"vid,omitempty"` 36 | RDNSMode int `url:"rdns_mode"` 37 | AllowDNS bool `url:"allow_dns"` 38 | AllowProxy bool `url:"allow_proxy"` 39 | Managed bool `url:"managed"` 40 | ActiveDiscovery bool `url:"active_discovery"` 41 | } 42 | -------------------------------------------------------------------------------- /api/machine.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/canonical/gomaasclient/entity" 5 | ) 6 | 7 | // Machine is an interface defining API behaviour for Machine objects 8 | type Machine interface { 9 | Get(systemID string) (*entity.Machine, error) 10 | Update(systemID string, machineParams *entity.MachineUpdateParams, powerParams map[string]interface{}) (*entity.Machine, error) 11 | Delete(systemID string) error 12 | Commission(systemID string, params *entity.MachineCommissionParams) (*entity.Machine, error) 13 | Deploy(systemID string, params *entity.MachineDeployParams) (*entity.Machine, error) 14 | Release(systemID string, params *entity.MachineReleaseParams) (*entity.Machine, error) 15 | Lock(systemID string, comment string) (*entity.Machine, error) 16 | Unlock(systemID string, comment string) (*entity.Machine, error) 17 | ClearDefaultGateways(systemID string) (*entity.Machine, error) 18 | GetPowerParameters(systemID string) (map[string]interface{}, error) 19 | PowerOn(systemID string, params *entity.MachinePowerOnParams) (*entity.Machine, error) 20 | PowerOff(systemID string, params *entity.MachinePowerOffParams) (*entity.Machine, error) 21 | GetPowerState(systemID string) (*entity.MachinePowerState, error) 22 | SetWorkloadAnnotations(systemID string, params map[string]string) (*entity.Machine, error) 23 | RescueMode(systemID string) (*entity.Machine, error) 24 | ExitRescueMode(systemID string) (*entity.Machine, error) 25 | Abort(systemID string, comment string) (*entity.Machine, error) 26 | MarkBroken(systemID string, comment string) (*entity.Machine, error) 27 | MarkFixed(systemID string, comment string) (*entity.Machine, error) 28 | GetToken(systemID string) (*entity.MachineToken, error) 29 | Details(systemID string) (*entity.MachineDetails, error) 30 | RestoreDefaultConfiguration(systemID string) error 31 | RestoreNetworkingConfiguration(systemID string) error 32 | RestoreStorageConfiguration(systemID string) error 33 | GetCurtinConfig(systemID string) (map[string]interface{}, error) 34 | } 35 | -------------------------------------------------------------------------------- /entity/block_device.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // BlockDevice represents the MAAS BlockDevice endpoint. 4 | type BlockDevice struct { 5 | Filesystem PartitionFileSystem `json:"filesystem,omitempty"` 6 | Path string `json:"path,omitempty"` 7 | Model string `json:"model,omitempty"` 8 | ResourceURI string `json:"resource_uri,omitempty"` 9 | PartitionTableType string `json:"partition_table_type,omitempty"` 10 | UUID string `json:"uuid,omitempty"` 11 | Serial string `json:"serial,omitempty"` 12 | IDPath string `json:"id_path,omitempty"` 13 | UsedFor string `json:"used_for,omitempty"` 14 | FirmwareVersion string `json:"firmware_version,omitempty"` 15 | SystemID string `json:"system_id,omitempty"` 16 | StoragePool string `json:"storage_pool,omitempty"` 17 | Type string `json:"type,omitempty"` 18 | Name string `json:"name,omitempty"` 19 | Partitions []BlockDevicePartition `json:"partitions,omitempty"` 20 | Tags []string `json:"tags,omitempty"` 21 | Size int64 `json:"size,omitempty"` 22 | ID int `json:"id,omitempty"` 23 | BlockSize int `json:"block_size,omitempty"` 24 | UsedSize int64 `json:"used_size,omitempty"` 25 | AvailableSize int64 `json:"available_size,omitempty"` 26 | NUMANode int `json:"numa_node,omitempty"` 27 | } 28 | 29 | type BlockDeviceParams struct { 30 | Name string `url:"name,omitempty"` 31 | UUID string `url:"uuid,omitempty"` 32 | Model string `url:"model,omitempty"` 33 | Serial string `url:"serial,omitempty"` 34 | IDPath string `url:"id_path,omitempty"` 35 | Size int64 `url:"size,omitempty"` 36 | BlockSize int `url:"block_size,omitempty"` 37 | } 38 | -------------------------------------------------------------------------------- /client/tag.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | 7 | "github.com/canonical/gomaasclient/entity" 8 | "github.com/google/go-querystring/query" 9 | ) 10 | 11 | // Tag implements api.Tag 12 | type Tag struct { 13 | APIClient APIClient 14 | } 15 | 16 | func (t *Tag) client(name string) APIClient { 17 | return t.APIClient.GetSubObject("tags").GetSubObject(name) 18 | } 19 | 20 | // Get fetches a given tag by name 21 | func (t *Tag) Get(name string) (*entity.Tag, error) { 22 | tag := new(entity.Tag) 23 | err := t.client(name).Get("", url.Values{}, func(data []byte) error { 24 | return json.Unmarshal(data, tag) 25 | }) 26 | 27 | return tag, err 28 | } 29 | 30 | // Update updates a given tag 31 | func (t *Tag) Update(name string, tagParams *entity.TagParams) (*entity.Tag, error) { 32 | qsp, err := query.Values(tagParams) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | tag := new(entity.Tag) 38 | err = t.client(name).Put(qsp, func(data []byte) error { 39 | return json.Unmarshal(data, tag) 40 | }) 41 | 42 | return tag, err 43 | } 44 | 45 | // Delete deletes a given tag 46 | func (t *Tag) Delete(name string) error { 47 | return t.client(name).Delete() 48 | } 49 | 50 | // GetMachines fetches a list of machines with a given tag 51 | func (t *Tag) GetMachines(name string) ([]entity.Machine, error) { 52 | machines := make([]entity.Machine, 0) 53 | err := t.client(name).Get("machines", url.Values{}, func(data []byte) error { 54 | return json.Unmarshal(data, &machines) 55 | }) 56 | 57 | return machines, err 58 | } 59 | 60 | // AddMachines adds a set of Machines to a given tag 61 | func (t *Tag) AddMachines(name string, machineIds []string) error { 62 | return t.updateNodes(name, machineIds, "add") 63 | } 64 | 65 | // RemoveMachines removes a set of Machines 66 | func (t *Tag) RemoveMachines(name string, machineIds []string) error { 67 | return t.updateNodes(name, machineIds, "remove") 68 | } 69 | 70 | func (t *Tag) updateNodes(name string, machineIds []string, op string) error { 71 | qsp := url.Values{} 72 | for _, id := range machineIds { 73 | qsp.Add(op, id) 74 | } 75 | 76 | return t.client(name).Post("update_nodes", qsp, func(data []byte) error { return nil }) 77 | } 78 | -------------------------------------------------------------------------------- /entity/boot_resource.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type BootResource struct { 4 | Sets map[string]BootResourceSet `json:"sets,omitempty"` 5 | Type string `json:"type,omitempty"` 6 | Name string `json:"name,omitempty"` 7 | Architecture string `json:"architecture,omitempty"` 8 | BaseImage string `json:"base_image,omitempty"` 9 | ResourceURI string `json:"resource_uri,omitempty"` 10 | LastDeployed string `json:"last_deployed,omitempty"` 11 | Subarches string `json:"subarches,omitempty"` 12 | Title string `json:"title,omitempty"` 13 | ID int `json:"id,omitempty"` 14 | } 15 | 16 | type BootResourceParams struct { 17 | Name string `url:"name,omitempty"` 18 | Architecture string `url:"architecture,omitempty"` 19 | SHA256 string `url:"sha256,omitempty"` 20 | Size string `url:"size,omitempty"` 21 | Title string `url:"title,omitempty"` 22 | Filetype string `url:"filetype,omitempty"` 23 | BaseImage string `url:"base_image,omitempty"` 24 | Content string `url:"content,omitempty"` 25 | } 26 | 27 | type BootResourcesReadParams struct { 28 | Type string `json:"type,omitempty"` 29 | } 30 | 31 | // BootResourceSet represents a BootResource's "set". 32 | // This type should not be used directly. 33 | type BootResourceSet struct { 34 | Files map[string]BootResourceSetFile `json:"files,omitempty"` 35 | Version string `json:"version,omitempty"` 36 | Label string `json:"label,omitempty"` 37 | Size int64 `json:"size,omitempty"` 38 | Complete bool `json:"complete,omitempty"` 39 | } 40 | 41 | // BootResourceSetFile represents a BootResource set's "file". 42 | // This type should not be used directly. 43 | type BootResourceSetFile struct { 44 | Filename string `json:"filename,omitempty"` 45 | Filetype string `json:"filetype,omitempty"` 46 | SHA256 string `json:"sha256,omitempty"` 47 | Size int64 `json:"size,omitempty"` 48 | Complete bool `json:"complete,omitempty"` 49 | } 50 | -------------------------------------------------------------------------------- /entity/volume_group.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // VolumeGroup represents the MAAS VolumeGroup endpoint. 4 | type VolumeGroup struct { 5 | Devices interface{} `json:"devices,omitempty"` 6 | ResourceURI string `json:"resource_uri,omitempty"` 7 | HumanSize string `json:"human_size,omitempty"` 8 | UUID string `json:"uuid,omitempty"` 9 | HumanAvailableSize string `json:"human_available_size,omitempty"` 10 | SystemID string `json:"system_id,omitempty"` 11 | HumanUsedSize string `json:"human_used_size,omitempty"` 12 | Name string `json:"name,omitempty"` 13 | LogicalVolumes []VirtualBlockDevice `json:"logical_volumes,omitempty"` 14 | Size int64 `json:"size,omitempty"` 15 | UsedSize int64 `json:"used_size,omitempty"` 16 | AvailableSize int64 `json:"available_size,omitempty"` 17 | ID int `json:"id,omitempty"` 18 | } 19 | 20 | // VolumeGroupCreateParams enumerates the parameters for the volume group create operation. 21 | type VolumeGroupCreateParams struct { 22 | Name string `url:"name,omitempty"` 23 | UUID string `url:"uuid,omitempty"` 24 | BlockDevices []string `url:"block_devices,omitempty"` 25 | Partitions []string `url:"partitions,omitempty"` 26 | } 27 | 28 | // VolumeGroupUpdateParams enumerates the parameters for the volume group update operation. 29 | type VolumeGroupUpdateParams struct { 30 | Name string `url:"name,omitempty"` 31 | UUID string `url:"uuid,omitempty"` 32 | AddBlockDevices []string `url:"add_block_devices,omitempty"` 33 | RemoveBlockDevices []string `url:"remove_block_devices,omitempty"` 34 | AddPartitions []string `url:"add_partitions,omitempty"` 35 | RemovePartitions []string `url:"remove_partitions,omitempty"` 36 | } 37 | 38 | // LogicalVolumeParams enumerates the parameters for the logical volume operation. 39 | type LogicalVolumeParams struct { 40 | Name string `url:"name,omitempty"` 41 | UUID string `url:"uuid,omitempty"` 42 | Size int64 `url:"size,omitempty"` 43 | } 44 | 45 | // VirtualBlockDevice represents a logical volume and extends BlockDevice. 46 | type VirtualBlockDevice struct { 47 | BlockDevice 48 | } 49 | -------------------------------------------------------------------------------- /entity/vlan_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/go-cmp/cmp" 7 | 8 | "github.com/canonical/gomaasclient/test/helper" 9 | ) 10 | 11 | var sampleVLAN VLAN = VLAN{ 12 | VID: 10, 13 | MTU: 1500, 14 | DHCPOn: false, 15 | FabricID: 0, 16 | Name: "10", 17 | PrimaryRack: "7xtf67", 18 | Space: "internal", 19 | SecondaryRack: "76y7pg", 20 | ID: 5002, 21 | Fabric: "fabric-0", 22 | ResourceURI: "/MAAS/api/2.0/vlans/5002/", 23 | } 24 | 25 | var sampleVLANs []VLAN = []VLAN{ 26 | { 27 | VID: 0, 28 | MTU: 1500, 29 | DHCPOn: false, 30 | ExternalDHCP: "", 31 | Space: "undefined", 32 | FabricID: 10, 33 | SecondaryRack: "", 34 | Fabric: "fabric-10", 35 | ID: 5014, 36 | PrimaryRack: "", 37 | Name: "untagged", 38 | ResourceURI: "/MAAS/api/2.0/vlans/5014/", 39 | }, 40 | { 41 | VID: 1, 42 | MTU: 1500, 43 | DHCPOn: false, 44 | ExternalDHCP: "", 45 | RelayVLAN: &VLAN{ 46 | VID: 0, 47 | MTU: 1500, 48 | DHCPOn: false, 49 | ExternalDHCP: "", 50 | Space: "undefined", 51 | FabricID: 10, 52 | SecondaryRack: "", 53 | Fabric: "fabric-10", 54 | ID: 5014, 55 | PrimaryRack: "", 56 | Name: "untagged", 57 | ResourceURI: "/MAAS/api/2.0/vlans/5014/", 58 | }, 59 | Space: "undefined", 60 | FabricID: 10, 61 | SecondaryRack: "", 62 | Fabric: "fabric-10", 63 | ID: 5015, 64 | PrimaryRack: "", 65 | Name: "untagged", 66 | ResourceURI: "/MAAS/api/2.0/vlans/5015/", 67 | }, 68 | } 69 | 70 | func TestVLAN(t *testing.T) { 71 | vlan := new(VLAN) 72 | vlans := new([]VLAN) 73 | 74 | // Unmarshal sample data into the types 75 | if err := helper.TestdataFromJSON("maas/vlan.json", vlan); err != nil { 76 | t.Fatal(err) 77 | } 78 | 79 | if err := helper.TestdataFromJSON("maas/vlans.json", vlans); err != nil { 80 | t.Fatal(err) 81 | } 82 | 83 | // Verify the values are correct 84 | if diff := cmp.Diff(&sampleVLAN, vlan); diff != "" { 85 | t.Fatalf("json.Decode(VLAN) mismatch (-want +got):\n%s", diff) 86 | } 87 | 88 | if diff := cmp.Diff(&sampleVLANs, vlans); diff != "" { 89 | t.Fatalf("json.Decode([]VLAN) mismatch (-want +got):\n%s", diff) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /entity/boot_resource_test.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/canonical/gomaasclient/test/helper" 7 | "github.com/google/go-cmp/cmp" 8 | ) 9 | 10 | var sampleBootResource BootResource = BootResource{ 11 | Sets: map[string]BootResourceSet{ 12 | "20240828": { 13 | Files: map[string]BootResourceSetFile{ 14 | "root.tgz": { 15 | Filename: "root.tgz", 16 | Filetype: "root-tgz", 17 | SHA256: "5b341be98b05d409a41ce8c2d881dbd80df9de07da7613d6722d4f0b5e99e0f1", 18 | Size: 935047294, 19 | Complete: true, 20 | }, 21 | }, 22 | Version: "20240828", 23 | Label: "uploaded", 24 | Size: 935047294, 25 | Complete: true, 26 | }, 27 | }, 28 | Type: "Uploaded", 29 | Name: "alma9", 30 | Architecture: "amd64/generic", 31 | BaseImage: "rhel/9", 32 | ResourceURI: "/MAAS/api/2.0/boot-resources/13/", 33 | Subarches: "generic", 34 | Title: "Alma 9 Custom", 35 | ID: 13, 36 | } 37 | 38 | var sampleBootResources []BootResource = []BootResource{ 39 | { 40 | Type: "Synced", 41 | Name: "ubuntu/bionic", 42 | Architecture: "amd64/ga-18.04", 43 | ResourceURI: "/MAAS/api/2.0/boot-resources/95/", 44 | Subarches: "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04", 45 | ID: 95, 46 | }, 47 | { 48 | Type: "Uploaded", 49 | Name: "alma9", 50 | Architecture: "amd64/generic", 51 | BaseImage: "rhel/9", 52 | ResourceURI: "/MAAS/api/2.0/boot-resources/13/", 53 | Subarches: "generic", 54 | Title: "Alma 9 Custom", 55 | ID: 13, 56 | }, 57 | } 58 | 59 | func TestBootResource(t *testing.T) { 60 | br := new(BootResource) 61 | brs := new([]BootResource) 62 | 63 | // Unmarshal sample data into the types 64 | if err := helper.TestdataFromJSON("maas/boot_resource.json", br); err != nil { 65 | t.Fatal(err) 66 | } 67 | 68 | if err := helper.TestdataFromJSON("maas/boot_resources.json", brs); err != nil { 69 | t.Fatal(err) 70 | } 71 | 72 | // Verify the values are correct 73 | if diff := cmp.Diff(&sampleBootResource, br); diff != "" { 74 | t.Fatalf("json.Decode(NetworkInterface) mismatch (-want +got):\n%s", diff) 75 | } 76 | 77 | if diff := cmp.Diff(&sampleBootResources, brs); diff != "" { 78 | t.Fatalf("json.Decode([]NetworkInterface) mismatch (-want +got):\n%s", diff) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /client/vm_host.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "sync" 8 | 9 | "github.com/canonical/gomaasclient/entity" 10 | "github.com/google/go-querystring/query" 11 | ) 12 | 13 | // VMHost contains functionality for manipulating the VMHost entity. 14 | type VMHost struct { 15 | APIClient APIClient 16 | mutex sync.Mutex 17 | } 18 | 19 | func (p *VMHost) client(id int) APIClient { 20 | return p.APIClient.GetSubObject("pods").GetSubObject(fmt.Sprintf("%v", id)) 21 | } 22 | 23 | // Get fetches a given VMHost 24 | func (p *VMHost) Get(id int) (*entity.VMHost, error) { 25 | vmHost := new(entity.VMHost) 26 | err := p.client(id).Get("", url.Values{}, func(data []byte) error { 27 | return json.Unmarshal(data, vmHost) 28 | }) 29 | 30 | return vmHost, err 31 | } 32 | 33 | // Update updates a given VMHost 34 | func (p *VMHost) Update(id int, params *entity.VMHostParams) (*entity.VMHost, error) { 35 | qsp, err := query.Values(params) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | vmHost := new(entity.VMHost) 41 | err = p.client(id).Put(qsp, func(data []byte) error { 42 | return json.Unmarshal(data, vmHost) 43 | }) 44 | 45 | return vmHost, err 46 | } 47 | 48 | // Delete deletes a given VMHost 49 | func (p *VMHost) Delete(id int) error { 50 | return p.client(id).Delete() 51 | } 52 | 53 | // Compose composes a VM on the given VMHost 54 | func (p *VMHost) Compose(id int, params *entity.VMHostMachineParams) (*entity.Machine, error) { 55 | p.mutex.Lock() 56 | defer p.mutex.Unlock() 57 | 58 | qsp, err := query.Values(params) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | machine := new(entity.Machine) 64 | err = p.client(id).Post("compose", qsp, func(data []byte) error { 65 | return json.Unmarshal(data, machine) 66 | }) 67 | 68 | return machine, err 69 | } 70 | 71 | // Refresh refreshes resource info and VM status of a given VMHost 72 | func (p *VMHost) Refresh(id int) (*entity.VMHost, error) { 73 | vmHost := new(entity.VMHost) 74 | err := p.client(id).Post("refresh", url.Values{}, func(data []byte) error { 75 | return json.Unmarshal(data, vmHost) 76 | }) 77 | 78 | return vmHost, err 79 | } 80 | 81 | // GetParameters fetches the configured parameters of a given VMHost 82 | func (p *VMHost) GetParameters(id int) (map[string]string, error) { 83 | params := map[string]string{} 84 | err := p.client(id).Get("parameters", url.Values{}, func(data []byte) error { 85 | return json.Unmarshal(data, ¶ms) 86 | }) 87 | 88 | return params, err 89 | } 90 | -------------------------------------------------------------------------------- /client/resource_pool.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | 8 | "github.com/canonical/gomaasclient/entity" 9 | "github.com/google/go-querystring/query" 10 | ) 11 | 12 | // ResourcePool implements api.ResourcePool 13 | type ResourcePool struct { 14 | APIClient APIClient 15 | } 16 | 17 | func (r *ResourcePool) client(id int) APIClient { 18 | return r.APIClient.GetSubObject(fmt.Sprintf("resourcepool/%v", id)) 19 | } 20 | 21 | func (r *ResourcePool) clientByName(name string) APIClient { 22 | return r.APIClient.GetSubObject(fmt.Sprintf("resourcepool/%v", name)) 23 | } 24 | 25 | // GetByName fetches a given ResourcePool 26 | func (r *ResourcePool) GetByName(name string) (*entity.ResourcePool, error) { 27 | resourcePool := new(entity.ResourcePool) 28 | err := r.clientByName(name).Get("", url.Values{}, func(data []byte) error { 29 | return json.Unmarshal(data, resourcePool) 30 | }) 31 | 32 | return resourcePool, err 33 | } 34 | 35 | // UpdateByName updates a given ResourcePool 36 | func (r *ResourcePool) UpdateByName(name string, params *entity.ResourcePoolParams) (*entity.ResourcePool, error) { 37 | qsp, err := query.Values(params) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | resourcePool := new(entity.ResourcePool) 43 | err = r.clientByName(name).Put(qsp, func(data []byte) error { 44 | return json.Unmarshal(data, resourcePool) 45 | }) 46 | 47 | return resourcePool, err 48 | } 49 | 50 | // DeleteByName deletes a given ResourcePool 51 | func (r *ResourcePool) DeleteByName(name string) error { 52 | return r.clientByName(name).Delete() 53 | } 54 | 55 | // Get fetches a given ResourcePool 56 | func (r *ResourcePool) Get(id int) (*entity.ResourcePool, error) { 57 | resourcePool := new(entity.ResourcePool) 58 | err := r.client(id).Get("", url.Values{}, func(data []byte) error { 59 | return json.Unmarshal(data, resourcePool) 60 | }) 61 | 62 | return resourcePool, err 63 | } 64 | 65 | // Update updates a given ResourcePool 66 | func (r *ResourcePool) Update(id int, params *entity.ResourcePoolParams) (*entity.ResourcePool, error) { 67 | qsp, err := query.Values(params) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | resourcePool := new(entity.ResourcePool) 73 | err = r.client(id).Put(qsp, func(data []byte) error { 74 | return json.Unmarshal(data, resourcePool) 75 | }) 76 | 77 | return resourcePool, err 78 | } 79 | 80 | // Delete deletes a given ResourcePool 81 | func (r *ResourcePool) Delete(id int) error { 82 | return r.client(id).Delete() 83 | } 84 | --------------------------------------------------------------------------------