Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute v2: Create aggregate #739

Merged
merged 3 commits into from
Jan 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion openstack/compute/v2/extensions/aggregates/doc.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
/*
Package aggregates returns information about the host aggregates in the
Package aggregates manages information about the host aggregates in the
OpenStack cloud.

Example of Create an aggregate

opts := aggregates.CreateOpts{
Name: "name",
AvailabilityZone: "london",
}

aggregate, err := aggregates.Create(computeClient, opts).Extract()
if err != nil {
panic(err)
}

fmt.Printf("%+v\n", aggregate)

Example of Retrieving list of all aggregates

allPages, err := aggregates.List(computeClient).AllPages()
Expand Down
28 changes: 28 additions & 0 deletions openstack/compute/v2/extensions/aggregates/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,31 @@ func List(client *gophercloud.ServiceClient) pagination.Pager {
return AggregatesPage{pagination.SinglePageBase(r)}
})
}

type CreateOpts struct {
// The name of the host aggregate.
Name string `json:"name" required:"true"`

// The availability zone of the host aggregate.
// You should use a custom availability zone rather than
// the default returned by the os-availability-zone API.
// The availability zone must not include ‘:’ in its name.
AvailabilityZone string `json:"availability_zone,omitempty"`
}

func (opts CreateOpts) ToAggregatesCreateMap() (map[string]interface{}, error) {
return gophercloud.BuildRequestBody(opts, "aggregate")
}

// Create makes a request against the API to create an aggregate.
func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) {
b, err := opts.ToAggregatesCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = client.Post(aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return
}
51 changes: 50 additions & 1 deletion openstack/compute/v2/extensions/aggregates/results.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package aggregates

import "github.com/gophercloud/gophercloud/pagination"
import (
"encoding/json"
"time"

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)

// Aggregate represents a host aggregate in the OpenStack cloud.
type Aggregate struct {
Expand All @@ -18,6 +24,33 @@ type Aggregate struct {

// Name of the aggregate.
Name string `json:"name"`

// The date and time when the resource was created.
CreatedAt time.Time `json:"-"`

// The date and time when the resource was updated,
// if the resource has not been updated, this field will show as null.
UpdatedAt time.Time `json:"-"`
}

// UnmarshalJSON to override default
func (r *Aggregate) UnmarshalJSON(b []byte) error {
type tmp Aggregate
var s struct {
tmp
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
}
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
*r = Aggregate(s.tmp)

r.CreatedAt = time.Time(s.CreatedAt)
r.UpdatedAt = time.Time(s.UpdatedAt)

return nil
}

// AggregatesPage represents a single page of all Aggregates from a List
Expand All @@ -40,3 +73,19 @@ func ExtractAggregates(p pagination.Page) ([]Aggregate, error) {
err := (p.(AggregatesPage)).ExtractInto(&a)
return a.Aggregates, err
}

type aggregatesResult struct {
gophercloud.Result
}

type CreateResult struct {
aggregatesResult
}

func (r CreateResult) Extract() (*Aggregate, error) {
var s struct {
Aggregate *Aggregate `json:"aggregate"`
}
err := r.ExtractInto(&s)
return s.Aggregate, err
}
39 changes: 39 additions & 0 deletions openstack/compute/v2/extensions/aggregates/testing/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"testing"
"time"

"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates"
th "github.com/gophercloud/gophercloud/testhelper"
Expand Down Expand Up @@ -44,13 +45,29 @@ const AggregateListBody = `
}
`

const AggregateCreateBody = `
{
"aggregate": {
"availability_zone": "london",
"created_at": "2016-12-27T22:51:32.000000",
"deleted": false,
"deleted_at": null,
"id": 32,
"name": "name",
"updated_at": null
}
}
`

// First aggregate from the AggregateListBody
var FirstFakeAggregate = aggregates.Aggregate{
AvailabilityZone: "",
Hosts: []string{},
ID: 1,
Metadata: map[string]string{},
Name: "test-aggregate1",
CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC),
UpdatedAt: time.Time{},
}

// Second aggregate from the AggregateListBody
Expand All @@ -60,6 +77,18 @@ var SecondFakeAggregate = aggregates.Aggregate{
ID: 4,
Metadata: map[string]string{"availability_zone": "test-az"},
Name: "test-aggregate2",
CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC),
UpdatedAt: time.Time{},
}

var CreatedAggregate = aggregates.Aggregate{
AvailabilityZone: "london",
Hosts: nil,
ID: 32,
Metadata: nil,
Name: "name",
CreatedAt: time.Date(2016, 12, 27, 22, 51, 32, 0, time.UTC),
UpdatedAt: time.Time{},
}

// HandleListSuccessfully configures the test server to respond to a List request.
Expand All @@ -72,3 +101,13 @@ func HandleListSuccessfully(t *testing.T) {
fmt.Fprintf(w, AggregateListBody)
})
}

func HandleCreateSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-aggregates", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)

w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, AggregateCreateBody)
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (

"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates"
"github.com/gophercloud/gophercloud/pagination"
"github.com/gophercloud/gophercloud/testhelper"
th "github.com/gophercloud/gophercloud/testhelper"
"github.com/gophercloud/gophercloud/testhelper/client"
)

func TestListAggregates(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
th.SetupHTTP()
defer th.TeardownHTTP()
HandleListSuccessfully(t)

pages := 0
Expand All @@ -26,15 +26,33 @@ func TestListAggregates(t *testing.T) {
if len(actual) != 2 {
t.Fatalf("Expected 2 aggregates, got %d", len(actual))
}
testhelper.CheckDeepEquals(t, FirstFakeAggregate, actual[0])
testhelper.CheckDeepEquals(t, SecondFakeAggregate, actual[1])
th.CheckDeepEquals(t, FirstFakeAggregate, actual[0])
th.CheckDeepEquals(t, SecondFakeAggregate, actual[1])

return true, nil
})

testhelper.AssertNoErr(t, err)
th.AssertNoErr(t, err)

if pages != 1 {
t.Errorf("Expected 1 page, saw %d", pages)
}
}

func TestCreateAggregates(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleCreateSuccessfully(t)

expected := CreatedAggregate

opts := aggregates.CreateOpts{
Name: "name",
AvailabilityZone: "london",
}

actual, err := aggregates.Create(client.ServiceClient(), opts).Extract()
th.AssertNoErr(t, err)

th.AssertDeepEquals(t, &expected, actual)
}
4 changes: 4 additions & 0 deletions openstack/compute/v2/extensions/aggregates/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud"
func aggregatesListURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("os-aggregates")
}

func aggregatesCreateURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("os-aggregates")
}