openstack/rackspace stack resources template op/unit test
diff --git a/openstack/orchestration/v1/stackresources/fixtures.go b/openstack/orchestration/v1/stackresources/fixtures.go
index d1b41c4..18a8de5 100644
--- a/openstack/orchestration/v1/stackresources/fixtures.go
+++ b/openstack/orchestration/v1/stackresources/fixtures.go
@@ -317,237 +317,119 @@
})
}
+var GetTemplateExpected = &TypeTemplate{
+ HeatTemplateFormatVersion: "2012-12-12",
+ Outputs: map[string]interface{}{
+ "private_key": map[string]interface{}{
+ "Description": "The private key if it has been saved.",
+ "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}",
+ },
+ "public_key": map[string]interface{}{
+ "Description": "The public key.",
+ "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}",
+ },
+ },
+ Parameters: map[string]interface{}{
+ "name": map[string]interface{}{
+ "Description": "The name of the key pair.",
+ "Type": "String",
+ },
+ "public_key": map[string]interface{}{
+ "Description": "The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.",
+ "Type": "String",
+ },
+ "save_private_key": map[string]interface{}{
+ "AllowedValues": []string{
+ "True",
+ "true",
+ "False",
+ "false",
+ },
+ "Default": false,
+ "Description": "True if the system should remember a generated private key; False otherwise.",
+ "Type": "String",
+ },
+ },
+ Resources: map[string]interface{}{
+ "KeyPair": map[string]interface{}{
+ "Properties": map[string]interface{}{
+ "name": map[string]interface{}{
+ "Ref": "name",
+ },
+ "public_key": map[string]interface{}{
+ "Ref": "public_key",
+ },
+ "save_private_key": map[string]interface{}{
+ "Ref": "save_private_key",
+ },
+ },
+ "Type": "OS::Nova::KeyPair",
+ },
+ },
+}
+
const GetTemplateOutput = `
{
+ "HeatTemplateFormatVersion": "2012-12-12",
"Outputs": {
- "addresses": {
- "Description": "A dict of all network addresses with correspondingport_id.",
- "Value": "{\"Fn::GetAtt\": [\"Server\", \"addresses\"]}"
+ "private_key": {
+ "Description": "The private key if it has been saved.",
+ "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}"
},
- "first_address": {
- "Description": "Convenience attribute to fetch the first assigned network address, or an empty string if nothing has been assigned at this time. Result may not be predictable if the server has addresses from more than one network.",
- "Value": "{\"Fn::GetAtt\": [\"Server\", \"first_address\"]}"
- },
- "show": {
- "Description": "A dict of all server details as returned by the API.",
- "Value": "{\"Fn::GetAtt\": [\"Server\", \"show\"]}"
- },
- "instance_name": {
- "Description": "AWS compatible instance name.",
- "Value": "{\"Fn::GetAtt\": [\"Server\", \"instance_name\"]}"
- },
- "accessIPv4": {
- "Description": "The manually assigned alternative public IPv4 address of the server.",
- "Value": "{\"Fn::GetAtt\": [\"Server\", \"accessIPv4\"]}"
- },
- "accessIPv6": {
- "Description": "The manually assigned alternative public IPv6 address of the server.",
- "Value": "{\"Fn::GetAtt\": [\"Server\", \"accessIPv6\"]}"
- },
- "networks": {
- "Description": "A dict of assigned network addresses of the form: {\"public\": [ip1, ip2...], \"private\": [ip3, ip4]}.",
- "Value": "{\"Fn::GetAtt\": [\"Server\", \"networks\"]}"
+ "public_key": {
+ "Description": "The public key.",
+ "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}"
}
},
- "HeatTemplateFormatVersion": "2012-12-12",
"Parameters": {
- "scheduler_hints": {
- "Type": "Json",
- "Description": "Arbitrary key-value pairs specified by the client to help boot a server."
- },
- "admin_pass": {
- "Type": "String",
- "Description": "The administrator password for the server."
- },
- "user_data_format": {
- "Default": "HEAT_CFNTOOLS",
- "Type": "String",
- "Description": "How the user_data should be formatted for the server. For HEAT_CFNTOOLS, the user_data is bundled as part of the heat-cfntools cloud-init boot configuration data. For RAW the user_data is passed to Nova unmodified. For SOFTWARE_CONFIG user_data is bundled as part of the software config data, and metadata is derived from any associated SoftwareDeployment resources.",
- "AllowedValues": [
- "HEAT_CFNTOOLS",
- "RAW",
- "SOFTWARE_CONFIG"
- ]
- },
- "admin_user": {
- "Type": "String",
- "Description": "Name of the administrative user to use on the server. This property will be removed from Juno in favor of the default cloud-init user set up for each image (e.g. \"ubuntu\" for Ubuntu 12.04+, \"fedora\" for Fedora 19+ and \"cloud-user\" for CentOS/RHEL 6.5)."
- },
"name": {
- "Type": "String",
- "Description": "Server name."
+ "Description": "The name of the key pair.",
+ "Type": "String"
},
- "block_device_mapping": {
- "Type": "CommaDelimitedList",
- "Description": "Block device mappings for this server."
+ "public_key": {
+ "Description": "The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.",
+ "Type": "String"
},
- "key_name": {
- "Type": "String",
- "Description": "Name of keypair to inject into the server."
- },
- "image": {
- "Type": "String",
- "Description": "The ID or name of the image to boot with."
- },
- "availability_zone": {
- "Type": "String",
- "Description": "Name of the availability zone for server placement."
- },
- "image_update_policy": {
- "Default": "REPLACE",
- "Type": "String",
- "Description": "Policy on how to apply an image-id update; either by requesting a server rebuild or by replacing the entire server",
+ "save_private_key": {
"AllowedValues": [
- "REBUILD",
- "REPLACE",
- "REBUILD_PRESERVE_EPHEMERAL"
- ]
- },
- "software_config_transport": {
- "Default": "POLL_SERVER_CFN",
- "Type": "String",
- "Description": "How the server should receive the metadata required for software configuration. POLL_SERVER_CFN will allow calls to the cfn API action DescribeStackResource authenticated with the provided keypair. POLL_SERVER_HEAT will allow calls to the Heat API resource-show using the provided keystone credentials.",
- "AllowedValues": [
- "POLL_SERVER_CFN",
- "POLL_SERVER_HEAT"
- ]
- },
- "metadata": {
- "Type": "Json",
- "Description": "Arbitrary key/value metadata to store for this server. Both keys and values must be 255 characters or less. Non-string values will be serialized to JSON (and the serialized string must be 255 characters or less)."
- },
- "personality": {
- "Default": {},
- "Type": "Json",
- "Description": "A map of files to create/overwrite on the server upon boot. Keys are file names and values are the file contents."
- },
- "user_data": {
- "Default": "",
- "Type": "String",
- "Description": "User data script to be executed by cloud-init."
- },
- "flavor_update_policy": {
- "Default": "RESIZE",
- "Type": "String",
- "Description": "Policy on how to apply a flavor update; either by requesting a server resize or by replacing the entire server.",
- "AllowedValues": [
- "RESIZE",
- "REPLACE"
- ]
- },
- "flavor": {
- "Type": "String",
- "Description": "The ID or name of the flavor to boot onto."
- },
- "diskConfig": {
- "Type": "String",
- "Description": "Control how the disk is partitioned when the server is created.",
- "AllowedValues": [
- "AUTO",
- "MANUAL"
- ]
- },
- "reservation_id": {
- "Type": "String",
- "Description": "A UUID for the set of servers being requested."
- },
- "networks": {
- "Type": "CommaDelimitedList",
- "Description": "An ordered list of nics to be added to this server, with information about connected networks, fixed ips, port etc."
- },
- "security_groups": {
- "Default": [],
- "Type": "CommaDelimitedList",
- "Description": "List of security group names or IDs. Cannot be used if neutron ports are associated with this server; assign security groups to the ports instead."
- },
- "config_drive": {
- "Type": "String",
- "Description": "value for config drive either boolean, or volume-id."
+ "True",
+ "true",
+ "False",
+ "false"
+ ],
+ "Default": false,
+ "Description": "True if the system should remember a generated private key; False otherwise.",
+ "Type": "String"
}
},
"Resources": {
- "Server": {
- "Type": "OS::Nova::Server",
+ "KeyPair": {
"Properties": {
- "scheduler_hints": {
- "Ref": "scheduler_hints"
- },
- "admin_pass": {
- "Ref": "admin_pass"
- },
- "user_data_format": {
- "Ref": "user_data_format"
- },
- "admin_user": {
- "Ref": "admin_user"
- },
"name": {
"Ref": "name"
},
- "block_device_mapping": {
- "Fn::Split": [
- ",",
- {
- "Ref": "block_device_mapping"
- }
- ]
+ "public_key": {
+ "Ref": "public_key"
},
- "key_name": {
- "Ref": "key_name"
- },
- "image": {
- "Ref": "image"
- },
- "availability_zone": {
- "Ref": "availability_zone"
- },
- "image_update_policy": {
- "Ref": "image_update_policy"
- },
- "software_config_transport": {
- "Ref": "software_config_transport"
- },
- "metadata": {
- "Ref": "metadata"
- },
- "personality": {
- "Ref": "personality"
- },
- "user_data": {
- "Ref": "user_data"
- },
- "flavor_update_policy": {
- "Ref": "flavor_update_policy"
- },
- "flavor": {
- "Ref": "flavor"
- },
- "diskConfig": {
- "Ref": "diskConfig"
- },
- "reservation_id": {
- "Ref": "reservation_id"
- },
- "networks": {
- "Fn::Split": [
- ",",
- {
- "Ref": "networks"
- }
- ]
- },
- "security_groups": {
- "Fn::Split": [
- ",",
- {
- "Ref": "security_groups"
- }
- ]
- },
- "config_drive": {
- "Ref": "config_drive"
+ "save_private_key": {
+ "Ref": "save_private_key"
}
- }
+ },
+ "Type": "OS::Nova::KeyPair"
}
}
}`
+
+// HandleGetTemplateSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName/template`
+// on the test handler mux that responds with a `Template` response.
+func HandleGetTemplateSuccessfully(t *testing.T, output string) {
+ th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName/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)
+ })
+}
diff --git a/openstack/orchestration/v1/stackresources/requests.go b/openstack/orchestration/v1/stackresources/requests.go
index 9ef8424..628cecc 100644
--- a/openstack/orchestration/v1/stackresources/requests.go
+++ b/openstack/orchestration/v1/stackresources/requests.go
@@ -115,3 +115,16 @@
})
return res
}
+
+// Template retreives the template representation for the given resource type.
+func Template(c *gophercloud.ServiceClient, resourceType string) TemplateResult {
+ var res TemplateResult
+
+ // Send request to API
+ _, res.Err = perigee.Request("GET", templateURL(c, resourceType), perigee.Options{
+ MoreHeaders: c.AuthenticatedHeaders(),
+ Results: &res.Body,
+ OkCodes: []int{200},
+ })
+ return res
+}
diff --git a/openstack/orchestration/v1/stackresources/requests_test.go b/openstack/orchestration/v1/stackresources/requests_test.go
index b0fb83a..f137878 100644
--- a/openstack/orchestration/v1/stackresources/requests_test.go
+++ b/openstack/orchestration/v1/stackresources/requests_test.go
@@ -93,3 +93,15 @@
expected := GetSchemaExpected
th.AssertDeepEquals(t, expected, actual)
}
+
+func TestGetResourceTemplate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ HandleGetTemplateSuccessfully(t, GetTemplateOutput)
+
+ actual, err := Template(fake.ServiceClient(), "OS::Heat::AResourceName").Extract()
+ th.AssertNoErr(t, err)
+
+ expected := GetTemplateExpected
+ th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go
index 5674a61..15b1988 100644
--- a/openstack/orchestration/v1/stackresources/results.go
+++ b/openstack/orchestration/v1/stackresources/results.go
@@ -208,3 +208,28 @@
return &res, nil
}
+
+type TypeTemplate struct {
+ HeatTemplateFormatVersion string
+ Outputs map[string]interface{}
+ Parameters map[string]interface{}
+ Resources map[string]interface{}
+}
+
+type TemplateResult struct {
+ gophercloud.Result
+}
+
+func (r TemplateResult) Extract() (*TypeTemplate, error) {
+ if r.Err != nil {
+ return nil, r.Err
+ }
+
+ var res TypeTemplate
+
+ if err := mapstructure.Decode(r.Body, &res); err != nil {
+ return nil, err
+ }
+
+ return &res, nil
+}