Skip to content

Commit

Permalink
Add Capsule List Operation
Browse files Browse the repository at this point in the history
Depends-On: gophercloud#944

Change-Id: I145b9de18c85a5d6cb802689c620bab521ab9782
Signed-off-by: Kevin Zhao <kevin.zhao@arm.com>
  • Loading branch information
Kevin Zhao committed Apr 20, 2018
1 parent 10a32ce commit b0b959c
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 13 deletions.
22 changes: 22 additions & 0 deletions acceptance/openstack/container/v1/capsules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,25 @@ func TestCapsuleGet(t *testing.T) {
th.AssertEquals(t, capsule.MetaName, "template")
th.AssertEquals(t, capsule.CPU, float64(2.0))
}

func TestCapsuleList(t *testing.T) {
client, err := clients.NewContainerV1Client()
if err != nil {
t.Fatalf("Unable to create an container v1 client: %v", err)
}
th.AssertNoErr(t, err)
pager := capsules.List(client, nil)
err = pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")

CapsuleList, err := capsules.ExtractCapsules(page)
th.AssertNoErr(t, err)

for _, m := range CapsuleList {
t.Logf("Capsule: ID [%s] Name [%s] Status [%s] CPU [%s] Memory [%s]",
m.ID, m.MetaName, m.Status, m.CPU, m.Memory)
}
return true, nil
})
th.CheckNoErr(t, err)
}
39 changes: 39 additions & 0 deletions openstack/container/v1/capsules/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,51 @@ package capsules

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

// ListOptsBuilder allows extensions to add additional parameters to the
// List request.
type ListOptsBuilder interface {
ToCapsuleListQuery() (string, error)
}

// Get requests details on a single capsule, by ID.
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
_, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 203},
})
return
}

// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the server attributes you want to see returned. Marker and Limit are used
// for pagination.
type ListOpts struct {
Marker string `q:"marker"`
Limit int `q:"limit"`
SortKey string `q:"sort_key"`
SortDir string `q:"sort_dir"`
}

// ToServerListQuery formats a ListOpts into a query string.
func (opts ListOpts) ToCapsuleListQuery() (string, error) {
q, err := gophercloud.BuildQueryString(opts)
return q.String(), err
}

// List makes a request against the API to list servers accessible to you.
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
url := listURL(client)
if opts != nil {
query, err := opts.ToCapsuleListQuery()
if err != nil {
return pagination.Pager{Err: err}
}
url += query
}
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
return CapsulePage{pagination.LinkedPageBase{PageResult: r}}
})
}
36 changes: 36 additions & 0 deletions openstack/container/v1/capsules/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

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

type commonResult struct {
Expand Down Expand Up @@ -105,3 +106,38 @@ func (r *Capsule) UnmarshalJSON(b []byte) error {

return nil
}

type CapsulePage struct {
pagination.LinkedPageBase
}

// NextPageURL is invoked when a paginated collection of baymodels has reached
// the end of a page and the pager seeks to traverse over a new one. In order
// to do this, it needs to construct the next page's URL.
func (r CapsulePage) NextPageURL() (string, error) {
var s struct {
Next string `json:"next"`
}
err := r.ExtractInto(&s)
if err != nil {
return "", err
}
return s.Next, nil
}

// IsEmpty checks whether a BayModelPage struct is empty.
func (r CapsulePage) IsEmpty() (bool, error) {
is, err := ExtractCapsules(r)
return len(is) == 0, err
}

// ExtractBayModels accepts a Page struct, specifically a BayModelPage struct,
// and extracts the elements into a slice of BayModel structs. In other words,
// a generic collection is mapped into a relevant slice.
func ExtractCapsules(r pagination.Page) ([]Capsule, error) {
var s struct {
Capsules []Capsule `json:"capsules"`
}
err := (r.(CapsulePage)).ExtractInto(&s)
return s.Capsules, err
}
86 changes: 73 additions & 13 deletions openstack/container/v1/capsules/testing/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,56 @@ import (
fakeclient "github.com/gophercloud/gophercloud/testhelper/client"
)

type imageEntry struct {
ID string
JSON string
const CapsuleGetBody = `
{
"uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e",
"status": "Running",
"id": 1,
"user_id": "d33b18c384574fd2a3299447aac285f0",
"project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68",
"cpu": 1,
"memory": "1024M",
"meta_name": "test",
"meta_labels": {"web": "app"},
"created_at": "2018-01-12 09:37:25+00:00",
"updated_at": "2018-01-12 09:37:25+01:00",
"links": [
{
"href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e",
"rel": "self"
},
{
"href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e",
"rel": "bookmark"
}
],
"capsule_version": "beta",
"restart_policy": "always",
"containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "d1469e8d-bcbc-43fc-b163-8b9b6a740930"],
"addresses": {
"b1295212-64e1-471d-aa01-25ff46f9818d": [
{
"version": 4,
"preserve_on_delete": false,
"addr": "172.24.4.11",
"port": "8439060f-381a-4386-a518-33d5a4058636",
"subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a"
}
]
},
"volumes_info": {
"67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [
"4b725a92-2197-497b-b6b1-fb8caa4cb99b"
]
}
}
`

// HandleImageGetSuccessfully test setup
func HandleCapsuleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID)

w.WriteHeader(http.StatusOK)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, `{
const CapsuleListBody =
`
{
"capsules": [
{
"uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e",
"status": "Running",
"id": 1,
Expand Down Expand Up @@ -63,6 +99,30 @@ func HandleCapsuleGetSuccessfully(t *testing.T) {
"4b725a92-2197-497b-b6b1-fb8caa4cb99b"
]
}
}`)
}
]
}`

// HandleCapsuleGetSuccessfully test setup
func HandleCapsuleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID)

w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, CapsuleGetBody)
})
}

// HandleCapsuleListSuccessfully test setup
func HandleCapsuleListSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/capsules/", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID)

w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, CapsuleListBody)
})
}
95 changes: 95 additions & 0 deletions openstack/container/v1/capsules/testing/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/container/v1/capsules"
"github.com/gophercloud/gophercloud/pagination"
th "github.com/gophercloud/gophercloud/testhelper"
fakeclient "github.com/gophercloud/gophercloud/testhelper/client"
)
Expand Down Expand Up @@ -89,3 +90,97 @@ func TestGetCapsule(t *testing.T) {

th.AssertDeepEquals(t, &expectedCapsule, actualCapsule)
}

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

HandleCapsuleListSuccessfully(t)

count := 0
results := capsules.List(fakeclient.ServiceClient(), nil)
err := results.EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := capsules.ExtractCapsules(page)
if err != nil {
t.Errorf("Failed to extract capsules: %v", err)
return false, err
}
uuid := "cc654059-1a77-47a3-bfcf-715bde5aad9e"
status := "Running"
id := 1
userID := "d33b18c384574fd2a3299447aac285f0"
projectID := "6b8ffef2a0ac42ee87887b9cc98bdf68"
cpu := float64(1)
memory := "1024M"
metaName := "test"

createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00")
updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00")
links := []interface{}{
map[string]interface{}{
"href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e",
"rel": "self",
},
map[string]interface{}{
"href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e",
"rel": "bookmark",
},
}
capsuleVersion := "beta"
restartPolicy := "always"
metaLabels := map[string]string{
"web": "app",
}
containersUUIDs := []string{
"1739e28a-d391-4fd9-93a5-3ba3f29a4c9b",
"d1469e8d-bcbc-43fc-b163-8b9b6a740930",
}
addresses := map[string][]capsules.Address{
"b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{
{
PreserveOnDelete: false,
Addr: "172.24.4.11",
Port: "8439060f-381a-4386-a518-33d5a4058636",
Version: float64(4),
SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a",
},
},
}
volumesInfo := map[string][]string{
"67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{
"4b725a92-2197-497b-b6b1-fb8caa4cb99b",
},
}

expected := []capsules.Capsule{
{
UUID: uuid,
ID: id,
UserID: userID,
ProjectID: projectID,
CPU: cpu,
Status: status,
Memory: memory,
MetaName: metaName,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
Links: links,
CapsuleVersion: capsuleVersion,
RestartPolicy: restartPolicy,
MetaLabels: metaLabels,
ContainersUUIDs: containersUUIDs,
Addresses: addresses,
VolumesInfo: volumesInfo,
},
}
th.CheckDeepEquals(t, expected, actual)

return true, nil
})
th.AssertNoErr(t, err)

if count != 1 {
t.Errorf("Expected 1 page, got %d", count)
}
}
6 changes: 6 additions & 0 deletions openstack/container/v1/capsules/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ import "github.com/gophercloud/gophercloud"
func getURL(client *gophercloud.ServiceClient, id string) string {
return client.ServiceURL("capsules", id)
}

// `listURL` is a pure function. `listURL(c)` is a URL for which a GET
// request will respond with a list of capsules in the service `c`.
func listURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("capsules")
}

0 comments on commit b0b959c

Please sign in to comment.