Bring partially https://github.com/gophercloud/gophercloud/pull/1221

Change-Id: I185e1991b68a22b387d043bc32bef79cd4c3dc4e
Related-To: PRODX-8893
diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go
index 9eb6018..bc1c838 100644
--- a/openstack/orchestration/v1/stacks/requests.go
+++ b/openstack/orchestration/v1/stacks/requests.go
@@ -30,7 +30,7 @@
 	// A structure that contains details for the environment of the stack.
 	EnvironmentOpts *Environment `json:"-"`
 	// User-defined parameters to pass to the template.
-	Parameters map[string]string `json:"parameters,omitempty"`
+	Parameters map[string]interface{} `json:"parameters,omitempty"`
 	// The timeout for stack creation in minutes.
 	Timeout int `json:"timeout_mins,omitempty"`
 	// A list of tags to assosciate with the Stack
@@ -127,7 +127,7 @@
 	// A structure that contains details for the environment of the stack.
 	EnvironmentOpts *Environment `json:"-"`
 	// User-defined parameters to pass to the template.
-	Parameters map[string]string `json:"parameters,omitempty"`
+	Parameters map[string]interface{} `json:"parameters,omitempty"`
 }
 
 // ToStackAdoptMap casts a CreateOpts struct to a map.
@@ -265,42 +265,66 @@
 	ToStackUpdateMap() (map[string]interface{}, error)
 }
 
+// UpdatePatchOptsBuilder is the interface options structs have to satisfy in order
+// to be used in the UpdatePatch operation in this package
+type UpdatePatchOptsBuilder interface {
+	ToStackUpdatePatchMap() (map[string]interface{}, error)
+}
+
 // UpdateOpts contains the common options struct used in this package's Update
-// operation.
+// and UpdatePatch operations.
 type UpdateOpts struct {
 	// A structure that contains either the template file or url. Call the
 	// associated methods to extract the information relevant to send in a create request.
-	TemplateOpts *Template `json:"-" required:"true"`
+	TemplateOpts *Template `json:"-"`
 	// A structure that contains details for the environment of the stack.
 	EnvironmentOpts *Environment `json:"-"`
 	// User-defined parameters to pass to the template.
-	Parameters map[string]string `json:"parameters,omitempty"`
+	Parameters map[string]interface{} `json:"parameters,omitempty"`
 	// The timeout for stack creation in minutes.
 	Timeout int `json:"timeout_mins,omitempty"`
-	// A list of tags to assosciate with the Stack
+	// A list of tags to associate with the Stack
 	Tags []string `json:"-"`
 }
 
-// ToStackUpdateMap casts a CreateOpts struct to a map.
+// ToStackUpdateMap validates that a template was supplied and calls
+// the toStackUpdateMap private function.
 func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
+	if opts.TemplateOpts == nil {
+		return nil, ErrTemplateRequired{}
+	}
+	return toStackUpdateMap(opts)
+}
+
+// ToStackUpdatePatchMap calls the private function toStackUpdateMap
+// directly.
+func (opts UpdateOpts) ToStackUpdatePatchMap() (map[string]interface{}, error) {
+	return toStackUpdateMap(opts)
+}
+
+// ToStackUpdateMap casts a CreateOpts struct to a map.
+func toStackUpdateMap(opts UpdateOpts) (map[string]interface{}, error) {
 	b, err := gophercloud.BuildRequestBody(opts, "")
 	if err != nil {
 		return nil, err
 	}
 
-	if err := opts.TemplateOpts.Parse(); err != nil {
-		return nil, err
-	}
-
-	if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
-		return nil, err
-	}
-	opts.TemplateOpts.fixFileRefs()
-	b["template"] = string(opts.TemplateOpts.Bin)
-
 	files := make(map[string]string)
-	for k, v := range opts.TemplateOpts.Files {
-		files[k] = v
+
+	if opts.TemplateOpts != nil {
+		if err := opts.TemplateOpts.Parse(); err != nil {
+			return nil, err
+		}
+
+		if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
+			return nil, err
+		}
+		opts.TemplateOpts.fixFileRefs()
+		b["template"] = string(opts.TemplateOpts.Bin)
+
+		for k, v := range opts.TemplateOpts.Files {
+			files[k] = v
+		}
 	}
 
 	if opts.EnvironmentOpts != nil {
@@ -328,8 +352,8 @@
 	return b, nil
 }
 
-// Update accepts an UpdateOpts struct and updates an existing stack using the values
-// provided.
+// Update accepts an UpdateOpts struct and updates an existing stack using the
+//  http PUT verb with the values provided. opts.TemplateOpts is required.
 func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) (r UpdateResult) {
 	b, err := opts.ToStackUpdateMap()
 	if err != nil {
@@ -340,6 +364,18 @@
 	return
 }
 
+// Update accepts an UpdateOpts struct and updates an existing stack using the
+//  http PATCH verb with the values provided. opts.TemplateOpts is not required.
+func UpdatePatch(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdatePatchOptsBuilder) (r UpdateResult) {
+	b, err := opts.ToStackUpdatePatchMap()
+	if err != nil {
+		r.Err = err
+		return
+	}
+	_, r.Err = c.Patch(updateURL(c, stackName, stackID), b, nil, nil)
+	return
+}
+
 // Delete deletes a stack based on the stack name and stack ID.
 func Delete(c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) {
 	_, r.Err = c.Delete(deleteURL(c, stackName, stackID), nil)
@@ -369,7 +405,7 @@
 	// A structure that contains details for the environment of the stack.
 	EnvironmentOpts *Environment `json:"-"`
 	// User-defined parameters to pass to the template.
-	Parameters map[string]string `json:"parameters,omitempty"`
+	Parameters map[string]interface{} `json:"parameters,omitempty"`
 }
 
 // ToStackPreviewMap casts a PreviewOpts struct to a map.