openstack stack templates ops and unit tests
diff --git a/openstack/orchestration/v1/stacktemplates/fixtures.go b/openstack/orchestration/v1/stacktemplates/fixtures.go
new file mode 100644
index 0000000..dc354ec
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/fixtures.go
@@ -0,0 +1,116 @@
+package stacktemplates
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ th "github.com/rackspace/gophercloud/testhelper"
+ fake "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+// GetExpected represents the expected object from a Get request.
+var GetExpected = &Template{
+ Description: "Simple template to test heat commands",
+ HeatTemplateVersion: "2013-05-23",
+ Parameters: map[string]interface{}{
+ "flavor": map[string]interface{}{
+ "default": "m1.tiny",
+ "type": "string",
+ },
+ },
+ Resources: map[string]interface{}{
+ "hello_world": map[string]interface{}{
+ "type": "OS::Nova::Server",
+ "properties": map[string]interface{}{
+ "key_name": "heat_key",
+ "flavor": map[string]interface{}{
+ "get_param": "flavor",
+ },
+ "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+ "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n",
+ },
+ },
+ },
+}
+
+// GetOutput represents the response body from a Get request.
+const GetOutput = `
+{
+ "heat_template_version": "2013-05-23",
+ "description": "Simple template to test heat commands",
+ "parameters": {
+ "flavor": {
+ "default": "m1.tiny",
+ "type": "string"
+ }
+ },
+ "resources": {
+ "hello_world": {
+ "type": "OS::Nova::Server",
+ "properties": {
+ "key_name": "heat_key",
+ "flavor": {
+ "get_param": "flavor"
+ },
+ "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+ "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n"
+ }
+ }
+ }
+}`
+
+// HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template`
+// on the test handler mux that responds with a `Get` response.
+func HandleGetSuccessfully(t *testing.T, output string) {
+ th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ 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, output)
+ })
+}
+
+var ValidateExpected = &ValidatedTemplate{
+ Description: "Simple template to test heat commands",
+ Parameters: map[string]interface{}{
+ "flavor": map[string]interface{}{
+ "Default": "m1.tiny",
+ "Type": "String",
+ "NoEcho": "false",
+ "Description": "",
+ "Label": "flavor",
+ },
+ },
+}
+
+const ValidateOutput = `
+{
+ "Description": "Simple template to test heat commands",
+ "Parameters": {
+ "flavor": {
+ "Default": "m1.tiny",
+ "Type": "String",
+ "NoEcho": "false",
+ "Description": "",
+ "Label": "flavor"
+ }
+ }
+}`
+
+// HandleValidateSuccessfully creates an HTTP handler at `/validate`
+// on the test handler mux that responds with a `Validate` response.
+func HandleValidateSuccessfully(t *testing.T, output string) {
+ th.Mux.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "POST")
+ 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, output)
+ })
+}
diff --git a/openstack/orchestration/v1/stacktemplates/requests.go b/openstack/orchestration/v1/stacktemplates/requests.go
new file mode 100644
index 0000000..5f8aba9
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/requests.go
@@ -0,0 +1,64 @@
+package stacktemplates
+
+import (
+ "fmt"
+
+ "github.com/racker/perigee"
+ "github.com/rackspace/gophercloud"
+)
+
+// Get retreives data for the given stack template.
+func Get(c *gophercloud.ServiceClient, stackName, stackID string) GetResult {
+ var res GetResult
+ _, res.Err = perigee.Request("GET", getURL(c, stackName, stackID), perigee.Options{
+ MoreHeaders: c.AuthenticatedHeaders(),
+ Results: &res.Body,
+ OkCodes: []int{200},
+ })
+ return res
+}
+
+// ValidateOptsBuilder describes struct types that can be accepted by the Validate call.
+// The ValidateOpts struct in this package does.
+type ValidateOptsBuilder interface {
+ ToStackTemplateValidateMap() (map[string]interface{}, error)
+}
+
+// ValidateOpts specifies the template validation parameters.
+type ValidateOpts struct {
+ Template map[string]interface{}
+ TemplateURL string
+}
+
+// ToStackTemplateValidateMap assembles a request body based on the contents of a ValidateOpts.
+func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]interface{}, error) {
+ vo := make(map[string]interface{})
+ if opts.Template != nil {
+ vo["template"] = opts.Template
+ return vo, nil
+ }
+ if opts.TemplateURL != "" {
+ vo["template_url"] = opts.TemplateURL
+ return vo, nil
+ }
+ return vo, fmt.Errorf("One of Template or TemplateURL is required.")
+}
+
+// Validate validates the given stack template.
+func Validate(c *gophercloud.ServiceClient, opts ValidateOptsBuilder) ValidateResult {
+ var res ValidateResult
+
+ reqBody, err := opts.ToStackTemplateValidateMap()
+ if err != nil {
+ res.Err = err
+ return res
+ }
+
+ _, res.Err = perigee.Request("POST", validateURL(c), perigee.Options{
+ MoreHeaders: c.AuthenticatedHeaders(),
+ ReqBody: reqBody,
+ Results: &res.Body,
+ OkCodes: []int{200},
+ })
+ return res
+}
diff --git a/openstack/orchestration/v1/stacktemplates/requests_test.go b/openstack/orchestration/v1/stacktemplates/requests_test.go
new file mode 100644
index 0000000..d31c4ac
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/requests_test.go
@@ -0,0 +1,57 @@
+package stacktemplates
+
+import (
+ "testing"
+
+ th "github.com/rackspace/gophercloud/testhelper"
+ fake "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+func TestGetTemplate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleGetSuccessfully(t, GetOutput)
+
+ actual, err := Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract()
+ th.AssertNoErr(t, err)
+
+ expected := GetExpected
+ th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestValidateTemplate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleValidateSuccessfully(t, ValidateOutput)
+
+ opts := ValidateOpts{
+ Template: map[string]interface{}{
+ "heat_template_version": "2013-05-23",
+ "description": "Simple template to test heat commands",
+ "parameters": map[string]interface{}{
+ "flavor": map[string]interface{}{
+ "default": "m1.tiny",
+ "type": "string",
+ },
+ },
+ "resources": map[string]interface{}{
+ "hello_world": map[string]interface{}{
+ "type": "OS::Nova::Server",
+ "properties": map[string]interface{}{
+ "key_name": "heat_key",
+ "flavor": map[string]interface{}{
+ "get_param": "flavor",
+ },
+ "image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
+ "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n",
+ },
+ },
+ },
+ },
+ }
+ actual, err := Validate(fake.ServiceClient(), opts).Extract()
+ th.AssertNoErr(t, err)
+
+ expected := ValidateExpected
+ th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/stacktemplates/results.go b/openstack/orchestration/v1/stacktemplates/results.go
new file mode 100644
index 0000000..968a3ec
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/results.go
@@ -0,0 +1,52 @@
+package stacktemplates
+
+import (
+ "github.com/mitchellh/mapstructure"
+ "github.com/rackspace/gophercloud"
+)
+
+type Template struct {
+ Description string `mapstructure:"description"`
+ HeatTemplateVersion string `mapstructure:"heat_template_version"`
+ Parameters map[string]interface{} `mapstructure:"parameters"`
+ Resources map[string]interface{} `mapstructure:"resources"`
+}
+
+type GetResult struct {
+ gophercloud.Result
+}
+
+func (r GetResult) Extract() (*Template, error) {
+ if r.Err != nil {
+ return nil, r.Err
+ }
+
+ var res Template
+ if err := mapstructure.Decode(r.Body, &res); err != nil {
+ return nil, err
+ }
+
+ return &res, nil
+}
+
+type ValidatedTemplate struct {
+ Description string
+ Parameters map[string]interface{}
+}
+
+type ValidateResult struct {
+ gophercloud.Result
+}
+
+func (r ValidateResult) Extract() (*ValidatedTemplate, error) {
+ if r.Err != nil {
+ return nil, r.Err
+ }
+
+ var res ValidatedTemplate
+ if err := mapstructure.Decode(r.Body, &res); err != nil {
+ return nil, err
+ }
+
+ return &res, nil
+}
diff --git a/openstack/orchestration/v1/stacktemplates/urls.go b/openstack/orchestration/v1/stacktemplates/urls.go
new file mode 100644
index 0000000..c30b7ca
--- /dev/null
+++ b/openstack/orchestration/v1/stacktemplates/urls.go
@@ -0,0 +1,11 @@
+package stacktemplates
+
+import "github.com/rackspace/gophercloud"
+
+func getURL(c *gophercloud.ServiceClient, stackName, stackID string) string {
+ return c.ServiceURL("stacks", stackName, stackID, "template")
+}
+
+func validateURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("validate")
+}