Fix api interfaces for orchestration resources
Some of the interfaces don't correspond well to the values
expected by the requests and returned by api.
diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go
index 3a621da..f884d51 100644
--- a/openstack/orchestration/v1/stacks/fixtures.go
+++ b/openstack/orchestration/v1/stacks/fixtures.go
@@ -63,6 +63,7 @@
CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
Status: "CREATE_COMPLETE",
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+ Tags: []string{"rackspace", "atx"},
},
ListedStack{
Description: "Simple template to test heat commands",
@@ -78,6 +79,7 @@
UpdatedTime: time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC),
Status: "UPDATE_COMPLETE",
ID: "db6977b2-27aa-4775-9ae7-6213212d4ada",
+ Tags: []string{"sfo", "satx"},
},
}
@@ -98,7 +100,8 @@
"creation_time": "2015-02-03T20:07:39",
"updated_time": null,
"stack_status": "CREATE_COMPLETE",
- "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87"
+ "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
+ "tags": ["rackspace", "atx"]
},
{
"description": "Simple template to test heat commands",
@@ -113,7 +116,8 @@
"creation_time": "2014-12-11T17:39:16",
"updated_time": "2014-12-11T17:40:37",
"stack_status": "UPDATE_COMPLETE",
- "id": "db6977b2-27aa-4775-9ae7-6213212d4ada"
+ "id": "db6977b2-27aa-4775-9ae7-6213212d4ada",
+ "tags": ["sfo", "satx"]
}
]
}
@@ -165,6 +169,7 @@
Status: "CREATE_COMPLETE",
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
TemplateDescription: "Simple template to test heat commands",
+ Tags: []string{"rackspace", "atx"},
}
// GetOutput represents the response body from a Get request.
@@ -194,7 +199,8 @@
"stack_status": "CREATE_COMPLETE",
"updated_time": null,
"id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
- "template_description": "Simple template to test heat commands"
+ "template_description": "Simple template to test heat commands",
+ "tags": ["rackspace", "atx"]
}
}
`
@@ -248,7 +254,6 @@
"OS::stack_name": "postman_stack",
"OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
},
- StatusReason: "Stack CREATE completed successfully",
Name: "postman_stack",
CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
Links: []gophercloud.Link{
@@ -259,7 +264,6 @@
},
Capabilities: []interface{}{},
NotificationTopics: []interface{}{},
- Status: "CREATE_COMPLETE",
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
TemplateDescription: "Simple template to test heat commands",
}
@@ -316,6 +320,20 @@
"type": "OS::Nova::Server",
},
},
+ Files: map[string]string{
+ "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n flavor:\n type: string\n description: Flavor for the server to be created\n default: 4353\n hidden: true\nresources:\n test_server:\n type: \"OS::Nova::Server\"\n properties:\n name: test-server\n flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n",
+ },
+ StackUserProjectID: "897686",
+ ProjectID: "897686",
+ Environment: map[string]interface{}{
+ "encrypted_param_names": make([]map[string]interface{}, 0),
+ "parameter_defaults": make(map[string]interface{}),
+ "parameters": make(map[string]interface{}),
+ "resource_registry": map[string]interface{}{
+ "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
+ "resources": make(map[string]interface{}),
+ },
+ },
}
// AbandonOutput represents the response body from an Abandon request.
@@ -354,21 +372,35 @@
"name": "hello_world",
"resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
"action": "CREATE",
- "type": "OS::Nova::Server",
+ "type": "OS::Nova::Server"
}
- }
+ },
+ "files": {
+ "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n flavor:\n type: string\n description: Flavor for the server to be created\n default: 4353\n hidden: true\nresources:\n test_server:\n type: \"OS::Nova::Server\"\n properties:\n name: test-server\n flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n"
+},
+ "environment": {
+ "encrypted_param_names": [],
+ "parameter_defaults": {},
+ "parameters": {},
+ "resource_registry": {
+ "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
+ "resources": {}
+ }
+ },
+ "stack_user_project_id": "897686",
+ "project_id": "897686"
}`
// HandleAbandonSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon`
// on the test handler mux that responds with an `Abandon` response.
-func HandleAbandonSuccessfully(t *testing.T) {
- th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon", func(w http.ResponseWriter, r *http.Request) {
+func HandleAbandonSuccessfully(t *testing.T, output string) {
+ th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c8/abandon", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Accept", "application/json")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
- fmt.Fprintf(w, AbandonOutput)
+ fmt.Fprintf(w, output)
})
}
diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go
index 0dd6af2..bd8e59e 100644
--- a/openstack/orchestration/v1/stacks/requests.go
+++ b/openstack/orchestration/v1/stacks/requests.go
@@ -2,6 +2,7 @@
import (
"errors"
+ "strings"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
@@ -60,6 +61,8 @@
Parameters map[string]string
// (OPTIONAL) The timeout for stack creation in minutes.
Timeout int
+ // (OPTIONAL) A list of tags to assosciate with the Stack
+ Tags []string
}
// ToStackCreateMap casts a CreateOpts struct to a map.
@@ -97,6 +100,9 @@
s["timeout_mins"] = opts.Timeout
}
+ if opts.Tags != nil {
+ s["tags"] = strings.Join(opts.Tags, ",")
+ }
return s, nil
}
@@ -197,12 +203,12 @@
s["parameters"] = opts.Parameters
}
- if opts.Timeout == 0 {
- return nil, errors.New("Required field 'Timeout' not provided.")
+ if opts.Timeout != 0 {
+ s["timeout"] = opts.Timeout
}
s["timeout_mins"] = opts.Timeout
- return map[string]interface{}{"stack": s}, nil
+ return s, nil
}
// Adopt accepts an AdoptOpts struct and creates a new stack using the resources
@@ -329,6 +335,8 @@
Parameters map[string]string
// (OPTIONAL) The timeout for stack creation in minutes.
Timeout int
+ // (OPTIONAL) A list of tags to assosciate with the Stack
+ Tags []string
}
// ToStackUpdateMap casts a CreateOpts struct to a map.
@@ -359,6 +367,10 @@
s["timeout_mins"] = opts.Timeout
}
+ if opts.Tags != nil {
+ s["tags"] = strings.Join(opts.Tags, ",")
+ }
+
return s, nil
}
diff --git a/openstack/orchestration/v1/stacks/requests_test.go b/openstack/orchestration/v1/stacks/requests_test.go
index 1e32ca2..1606d98 100644
--- a/openstack/orchestration/v1/stacks/requests_test.go
+++ b/openstack/orchestration/v1/stacks/requests_test.go
@@ -215,3 +215,15 @@
expected := PreviewExpected
th.AssertDeepEquals(t, expected, actual)
}
+
+func TestAbandonStack(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleAbandonSuccessfully(t, AbandonOutput)
+
+ actual, err := Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c8").Extract()
+ th.AssertNoErr(t, err)
+
+ expected := AbandonExpected
+ th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go
index dca06e4..432bc8e 100644
--- a/openstack/orchestration/v1/stacks/results.go
+++ b/openstack/orchestration/v1/stacks/results.go
@@ -69,6 +69,7 @@
Name string `mapstructure:"stack_name"`
Status string `mapstructure:"stack_status"`
StatusReason string `mapstructure:"stack_status_reason"`
+ Tags []string `mapstructure:"tags"`
UpdatedTime time.Time `mapstructure:"-"`
}
@@ -81,7 +82,7 @@
Stacks []ListedStack `mapstructure:"stacks"`
}
- err := mapstructure.Decode(page.(StackPage).Body, &res)
+ err := mapstructure.Decode(casted, &res)
if err != nil {
return nil, err
}
@@ -133,6 +134,7 @@
Name string `mapstructure:"stack_name"`
Status string `mapstructure:"stack_status"`
StatusReason string `mapstructure:"stack_status_reason"`
+ Tags []string `mapstructure:"tags"`
TemplateDescription string `mapstructure:"template_description"`
Timeout int `mapstructure:"timeout_mins"`
UpdatedTime time.Time `mapstructure:"-"`
@@ -200,21 +202,19 @@
// PreviewedStack represents the result of a Preview operation.
type PreviewedStack struct {
- Capabilities []interface{} `mapstructure:"capabilities"`
- CreationTime time.Time `mapstructure:"-"`
- Description string `mapstructure:"description"`
- DisableRollback bool `mapstructure:"disable_rollback"`
- ID string `mapstructure:"id"`
- Links []gophercloud.Link `mapstructure:"links"`
- Name string `mapstructure:"stack_name"`
- NotificationTopics []interface{} `mapstructure:"notification_topics"`
- Parameters map[string]string `mapstructure:"parameters"`
- Resources []map[string]interface{} `mapstructure:"resources"`
- Status string `mapstructure:"stack_status"`
- StatusReason string `mapstructure:"stack_status_reason"`
- TemplateDescription string `mapstructure:"template_description"`
- Timeout int `mapstructure:"timeout_mins"`
- UpdatedTime time.Time `mapstructure:"-"`
+ Capabilities []interface{} `mapstructure:"capabilities"`
+ CreationTime time.Time `mapstructure:"-"`
+ Description string `mapstructure:"description"`
+ DisableRollback bool `mapstructure:"disable_rollback"`
+ ID string `mapstructure:"id"`
+ Links []gophercloud.Link `mapstructure:"links"`
+ Name string `mapstructure:"stack_name"`
+ NotificationTopics []interface{} `mapstructure:"notification_topics"`
+ Parameters map[string]string `mapstructure:"parameters"`
+ Resources []interface{} `mapstructure:"resources"`
+ TemplateDescription string `mapstructure:"template_description"`
+ Timeout int `mapstructure:"timeout_mins"`
+ UpdatedTime time.Time `mapstructure:"-"`
}
// PreviewResult represents the result of a Preview operation.
@@ -269,12 +269,16 @@
// AbandonedStack represents the result of an Abandon operation.
type AbandonedStack struct {
- Status string `mapstructure:"status"`
- Name string `mapstructure:"name"`
- Template map[string]interface{} `mapstructure:"template"`
- Action string `mapstructure:"action"`
- ID string `mapstructure:"id"`
- Resources map[string]interface{} `mapstructure:"resources"`
+ Status string `mapstructure:"status"`
+ Name string `mapstructure:"name"`
+ Template map[string]interface{} `mapstructure:"template"`
+ Action string `mapstructure:"action"`
+ ID string `mapstructure:"id"`
+ Resources map[string]interface{} `mapstructure:"resources"`
+ Files map[string]string `mapstructure:"files"`
+ StackUserProjectID string `mapstructure:"stack_user_project_id"`
+ ProjectID string `mapstructure:"project_id"`
+ Environment map[string]interface{} `mapstructure:"environment"`
}
// AbandonResult represents the result of an Abandon operation.