1package testing
2
3import (
4	"fmt"
5	"net/http"
6	"testing"
7	"time"
8
9	"github.com/gophercloud/gophercloud"
10	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources"
11	th "github.com/gophercloud/gophercloud/testhelper"
12	fake "github.com/gophercloud/gophercloud/testhelper/client"
13)
14
15var Create_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:57:17Z")
16var Updated_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z")
17
18// FindExpected represents the expected object from a Find request.
19var FindExpected = []stackresources.Resource{
20	{
21		Name: "hello_world",
22		Links: []gophercloud.Link{
23			{
24				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
25				Rel:  "self",
26			},
27			{
28				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
29				Rel:  "stack",
30			},
31		},
32		LogicalID:    "hello_world",
33		StatusReason: "state changed",
34		UpdatedTime:  Updated_time,
35		CreationTime: Create_time,
36		RequiredBy:   []interface{}{},
37		Status:       "CREATE_IN_PROGRESS",
38		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
39		Type:         "OS::Nova::Server",
40		Attributes:   map[string]interface{}{"SXSW": "atx"},
41		Description:  "Some resource",
42	},
43}
44
45// FindOutput represents the response body from a Find request.
46const FindOutput = `
47{
48  "resources": [
49  {
50  	"description": "Some resource",
51  	"attributes": {"SXSW": "atx"},
52    "resource_name": "hello_world",
53    "links": [
54      {
55      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
56      "rel": "self"
57      },
58      {
59        "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
60        "rel": "stack"
61      }
62    ],
63    "logical_resource_id": "hello_world",
64    "resource_status_reason": "state changed",
65    "updated_time": "2018-06-26T07:58:17Z",
66	"creation_time": "2018-06-26T07:57:17Z",
67    "required_by": [],
68    "resource_status": "CREATE_IN_PROGRESS",
69    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
70    "resource_type": "OS::Nova::Server"
71  }
72  ]
73}`
74
75// HandleFindSuccessfully creates an HTTP handler at `/stacks/hello_world/resources`
76// on the test handler mux that responds with a `Find` response.
77func HandleFindSuccessfully(t *testing.T, output string) {
78	th.Mux.HandleFunc("/stacks/hello_world/resources", func(w http.ResponseWriter, r *http.Request) {
79		th.TestMethod(t, r, "GET")
80		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
81		th.TestHeader(t, r, "Accept", "application/json")
82
83		w.Header().Set("Content-Type", "application/json")
84		w.WriteHeader(http.StatusOK)
85		fmt.Fprintf(w, output)
86	})
87}
88
89// ListExpected represents the expected object from a List request.
90var ListExpected = []stackresources.Resource{
91	{
92		Name: "hello_world",
93		Links: []gophercloud.Link{
94			{
95				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
96				Rel:  "self",
97			},
98			{
99				Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
100				Rel:  "stack",
101			},
102		},
103		LogicalID:    "hello_world",
104		StatusReason: "state changed",
105		UpdatedTime:  Updated_time,
106		CreationTime: Create_time,
107		RequiredBy:   []interface{}{},
108		Status:       "CREATE_IN_PROGRESS",
109		PhysicalID:   "49181cd6-169a-4130-9455-31185bbfc5bf",
110		Type:         "OS::Nova::Server",
111		Attributes:   map[string]interface{}{"SXSW": "atx"},
112		Description:  "Some resource",
113	},
114}
115
116// ListOutput represents the response body from a List request.
117const ListOutput = `{
118  "resources": [
119  {
120    "resource_name": "hello_world",
121    "links": [
122    {
123      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world",
124      "rel": "self"
125    },
126    {
127      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b",
128      "rel": "stack"
129    }
130    ],
131    "logical_resource_id": "hello_world",
132    "resource_status_reason": "state changed",
133    "updated_time": "2018-06-26T07:58:17Z",
134    "creation_time": "2018-06-26T07:57:17Z",
135    "required_by": [],
136    "resource_status": "CREATE_IN_PROGRESS",
137    "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
138    "resource_type": "OS::Nova::Server",
139	"attributes": {"SXSW": "atx"},
140	"description": "Some resource"
141  }
142]
143}`
144
145// HandleListSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources`
146// on the test handler mux that responds with a `List` response.
147func HandleListSuccessfully(t *testing.T, output string) {
148	th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources", func(w http.ResponseWriter, r *http.Request) {
149		th.TestMethod(t, r, "GET")
150		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
151		th.TestHeader(t, r, "Accept", "application/json")
152
153		w.Header().Set("Content-Type", "application/json")
154		r.ParseForm()
155		marker := r.Form.Get("marker")
156		switch marker {
157		case "":
158			fmt.Fprintf(w, output)
159		case "49181cd6-169a-4130-9455-31185bbfc5bf":
160			fmt.Fprintf(w, `{"resources":[]}`)
161		default:
162			t.Fatalf("Unexpected marker: [%s]", marker)
163		}
164	})
165}
166
167// GetExpected represents the expected object from a Get request.
168var GetExpected = &stackresources.Resource{
169	Name: "wordpress_instance",
170	Links: []gophercloud.Link{
171		{
172			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance",
173			Rel:  "self",
174		},
175		{
176			Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e",
177			Rel:  "stack",
178		},
179	},
180	LogicalID:    "wordpress_instance",
181	Attributes:   map[string]interface{}{"SXSW": "atx"},
182	StatusReason: "state changed",
183	UpdatedTime:  Updated_time,
184	RequiredBy:   []interface{}{},
185	Status:       "CREATE_COMPLETE",
186	PhysicalID:   "00e3a2fe-c65d-403c-9483-4db9930dd194",
187	Type:         "OS::Nova::Server",
188}
189
190// GetOutput represents the response body from a Get request.
191const GetOutput = `
192{
193  "resource": {
194    "description": "Some resource",
195    "attributes": {"SXSW": "atx"},
196    "resource_name": "wordpress_instance",
197    "description": "",
198    "links": [
199    {
200      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance",
201      "rel": "self"
202    },
203    {
204      "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e",
205      "rel": "stack"
206    }
207    ],
208    "logical_resource_id": "wordpress_instance",
209    "resource_status": "CREATE_COMPLETE",
210    "updated_time": "2018-06-26T07:58:17Z",
211    "required_by": [],
212    "resource_status_reason": "state changed",
213    "physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194",
214    "resource_type": "OS::Nova::Server"
215  }
216}`
217
218// HandleGetSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance`
219// on the test handler mux that responds with a `Get` response.
220func HandleGetSuccessfully(t *testing.T, output string) {
221	th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", func(w http.ResponseWriter, r *http.Request) {
222		th.TestMethod(t, r, "GET")
223		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
224		th.TestHeader(t, r, "Accept", "application/json")
225
226		w.Header().Set("Content-Type", "application/json")
227		w.WriteHeader(http.StatusOK)
228		fmt.Fprintf(w, output)
229	})
230}
231
232// MetadataExpected represents the expected object from a Metadata request.
233var MetadataExpected = map[string]string{
234	"number": "7",
235	"animal": "auk",
236}
237
238// MetadataOutput represents the response body from a Metadata request.
239const MetadataOutput = `
240{
241    "metadata": {
242      "number": "7",
243      "animal": "auk"
244    }
245}`
246
247// HandleMetadataSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata`
248// on the test handler mux that responds with a `Metadata` response.
249func HandleMetadataSuccessfully(t *testing.T, output string) {
250	th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata", func(w http.ResponseWriter, r *http.Request) {
251		th.TestMethod(t, r, "GET")
252		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
253		th.TestHeader(t, r, "Accept", "application/json")
254
255		w.Header().Set("Content-Type", "application/json")
256		w.WriteHeader(http.StatusOK)
257		fmt.Fprintf(w, output)
258	})
259}
260
261// ListTypesExpected represents the expected object from a ListTypes request.
262var ListTypesExpected = stackresources.ResourceTypes{
263	"OS::Nova::Server",
264	"OS::Heat::RandomString",
265	"OS::Swift::Container",
266	"OS::Trove::Instance",
267	"OS::Nova::FloatingIPAssociation",
268	"OS::Cinder::VolumeAttachment",
269	"OS::Nova::FloatingIP",
270	"OS::Nova::KeyPair",
271}
272
273// same as above, but sorted
274var SortedListTypesExpected = stackresources.ResourceTypes{
275	"OS::Cinder::VolumeAttachment",
276	"OS::Heat::RandomString",
277	"OS::Nova::FloatingIP",
278	"OS::Nova::FloatingIPAssociation",
279	"OS::Nova::KeyPair",
280	"OS::Nova::Server",
281	"OS::Swift::Container",
282	"OS::Trove::Instance",
283}
284
285// ListTypesOutput represents the response body from a ListTypes request.
286const ListTypesOutput = `
287{
288  "resource_types": [
289    "OS::Nova::Server",
290    "OS::Heat::RandomString",
291    "OS::Swift::Container",
292    "OS::Trove::Instance",
293    "OS::Nova::FloatingIPAssociation",
294    "OS::Cinder::VolumeAttachment",
295    "OS::Nova::FloatingIP",
296    "OS::Nova::KeyPair"
297  ]
298}`
299
300// HandleListTypesSuccessfully creates an HTTP handler at `/resource_types`
301// on the test handler mux that responds with a `ListTypes` response.
302func HandleListTypesSuccessfully(t *testing.T, output string) {
303	th.Mux.HandleFunc("/resource_types", func(w http.ResponseWriter, r *http.Request) {
304		th.TestMethod(t, r, "GET")
305		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
306		th.TestHeader(t, r, "Accept", "application/json")
307
308		w.Header().Set("Content-Type", "application/json")
309		w.WriteHeader(http.StatusOK)
310		fmt.Fprintf(w, output)
311	})
312}
313
314// GetSchemaExpected represents the expected object from a Schema request.
315var GetSchemaExpected = &stackresources.TypeSchema{
316	Attributes: map[string]interface{}{
317		"an_attribute": map[string]interface{}{
318			"description": "An attribute description .",
319		},
320	},
321	Properties: map[string]interface{}{
322		"a_property": map[string]interface{}{
323			"update_allowed": false,
324			"required":       true,
325			"type":           "string",
326			"description":    "A resource description.",
327		},
328	},
329	ResourceType: "OS::Heat::AResourceName",
330	SupportStatus: map[string]interface{}{
331		"message": "A status message",
332		"status":  "SUPPORTED",
333		"version": "2014.1",
334	},
335}
336
337// GetSchemaOutput represents the response body from a Schema request.
338const GetSchemaOutput = `
339{
340  "attributes": {
341    "an_attribute": {
342      "description": "An attribute description ."
343    }
344  },
345  "properties": {
346    "a_property": {
347      "update_allowed": false,
348      "required": true,
349      "type": "string",
350      "description": "A resource description."
351    }
352  },
353  "resource_type": "OS::Heat::AResourceName",
354  "support_status": {
355	"message": "A status message",
356	"status": "SUPPORTED",
357	"version": "2014.1"
358  }
359}`
360
361// HandleGetSchemaSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName`
362// on the test handler mux that responds with a `Schema` response.
363func HandleGetSchemaSuccessfully(t *testing.T, output string) {
364	th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName", func(w http.ResponseWriter, r *http.Request) {
365		th.TestMethod(t, r, "GET")
366		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
367		th.TestHeader(t, r, "Accept", "application/json")
368
369		w.Header().Set("Content-Type", "application/json")
370		w.WriteHeader(http.StatusOK)
371		fmt.Fprintf(w, output)
372	})
373}
374
375// GetTemplateExpected represents the expected object from a Template request.
376var GetTemplateExpected = "{\n  \"HeatTemplateFormatVersion\": \"2012-12-12\",\n  \"Outputs\": {\n    \"private_key\": {\n      \"Description\": \"The private key if it has been saved.\",\n      \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"private_key\\\"]}\"\n    },\n    \"public_key\": {\n      \"Description\": \"The public key.\",\n      \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"public_key\\\"]}\"\n    }\n  },\n  \"Parameters\": {\n    \"name\": {\n      \"Description\": \"The name of the key pair.\",\n      \"Type\": \"String\"\n    },\n    \"public_key\": {\n      \"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.\",\n      \"Type\": \"String\"\n    },\n    \"save_private_key\": {\n      \"AllowedValues\": [\n        \"True\",\n        \"true\",\n        \"False\",\n        \"false\"\n      ],\n      \"Default\": false,\n      \"Description\": \"True if the system should remember a generated private key; False otherwise.\",\n      \"Type\": \"String\"\n    }\n  },\n  \"Resources\": {\n    \"KeyPair\": {\n      \"Properties\": {\n        \"name\": {\n          \"Ref\": \"name\"\n        },\n        \"public_key\": {\n          \"Ref\": \"public_key\"\n        },\n        \"save_private_key\": {\n          \"Ref\": \"save_private_key\"\n        }\n      },\n      \"Type\": \"OS::Nova::KeyPair\"\n    }\n  }\n}"
377
378// GetTemplateOutput represents the response body from a Template request.
379const GetTemplateOutput = `
380{
381  "HeatTemplateFormatVersion": "2012-12-12",
382  "Outputs": {
383    "private_key": {
384      "Description": "The private key if it has been saved.",
385      "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}"
386    },
387    "public_key": {
388      "Description": "The public key.",
389      "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}"
390    }
391  },
392  "Parameters": {
393    "name": {
394      "Description": "The name of the key pair.",
395      "Type": "String"
396    },
397    "public_key": {
398      "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.",
399      "Type": "String"
400    },
401    "save_private_key": {
402      "AllowedValues": [
403      "True",
404      "true",
405      "False",
406      "false"
407      ],
408      "Default": false,
409      "Description": "True if the system should remember a generated private key; False otherwise.",
410      "Type": "String"
411    }
412  },
413  "Resources": {
414    "KeyPair": {
415      "Properties": {
416        "name": {
417          "Ref": "name"
418        },
419        "public_key": {
420          "Ref": "public_key"
421        },
422        "save_private_key": {
423          "Ref": "save_private_key"
424        }
425      },
426      "Type": "OS::Nova::KeyPair"
427    }
428  }
429}`
430
431// HandleGetTemplateSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName/template`
432// on the test handler mux that responds with a `Template` response.
433func HandleGetTemplateSuccessfully(t *testing.T, output string) {
434	th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName/template", func(w http.ResponseWriter, r *http.Request) {
435		th.TestMethod(t, r, "GET")
436		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
437		th.TestHeader(t, r, "Accept", "application/json")
438
439		w.Header().Set("Content-Type", "application/json")
440		w.WriteHeader(http.StatusOK)
441		fmt.Fprintf(w, output)
442	})
443}
444
445// HandleMarkUnhealthySuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance`
446// on the test handler mux that responds with a `MarkUnhealthy` response.
447func HandleMarkUnhealthySuccessfully(t *testing.T) {
448	th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", func(w http.ResponseWriter, r *http.Request) {
449		th.TestMethod(t, r, "PATCH")
450		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
451		th.TestHeader(t, r, "Accept", "application/json")
452
453		w.Header().Set("Content-Type", "application/json")
454		w.WriteHeader(http.StatusOK)
455	})
456}
457