Adding RebuildOpts to clarify arg ordering
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index 20ca52e..8d8dcdd 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -289,72 +289,102 @@
return err
}
-// Rebuild requests that the Openstack provider reprovision the server.
-// The rebuild will need to know the server's name and new image reference or ID.
-// In addition, and unlike building a server with Create(), you must provide an administrator password.
-//
-// Additional options may be specified with the additional map.
-// This function treats a nil map the same as an empty map.
-//
-// Rebuild returns a server result as though you had called GetDetail() on the server's ID.
-// The information, however, refers to the new server, not the old.
-func Rebuild(client *gophercloud.ServiceClient, id, name, password, imageRef string, additional map[string]interface{}) RebuildResult {
+// RebuildOptsBuilder is an interface that allows extensions to override the
+// default behaviour of rebuild options
+type RebuildOptsBuilder interface {
+ ToServerRebuildMap() (map[string]interface{}, error)
+}
+
+// RebuildOpts represents the configuration options used in a server rebuild
+// operation
+type RebuildOpts struct {
+ // Required. The ID of the image you want your server to be provisioned on
+ ImageID string `json:"imageRef"`
+
+ // Name to set the server to
+ Name string `json:"name"`
+
+ // Required. The server's admin password
+ AdminPass string `json:"adminPass"`
+
+ // AccessIPv4 [optional] provides a new IPv4 address for the instance.
+ AccessIPv4 string `json:"accessIPv4"`
+
+ // AccessIPv6 [optional] provides a new IPv6 address for the instance.
+ AccessIPv6 string `json:"accessIPv6"`
+
+ // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
+ Metadata map[string]string `json:"metadata"`
+
+ // Personality [optional] includes the path and contents of a file to inject into the server at launch.
+ // The maximum size of the file is 255 bytes (decoded).
+ Personality []byte `json:"personality"`
+}
+
+// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
+func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
+ var err error
+ server := make(map[string]interface{})
+
+ if opts.AdminPass == "" {
+ err = fmt.Errorf("AdminPass is required")
+ }
+
+ if opts.ImageID == "" {
+ err = fmt.Errorf("ImageID is required")
+ }
+
+ if err != nil {
+ return server, err
+ }
+
+ server["name"] = opts.Name
+ server["adminPass"] = opts.AdminPass
+ server["imageRef"] = opts.ImageID
+
+ if opts.AccessIPv4 != "" {
+ server["accessIPv4"] = opts.AccessIPv4
+ }
+
+ if opts.AccessIPv6 != "" {
+ server["accessIPv6"] = opts.AccessIPv6
+ }
+
+ if opts.Metadata != nil {
+ server["metadata"] = opts.Metadata
+ }
+
+ if opts.Personality != nil {
+ encoded := base64.StdEncoding.EncodeToString(opts.Personality)
+ server["personality"] = &encoded
+ }
+
+ return map[string]interface{}{"rebuild": server}, nil
+}
+
+// Rebuild will reprovision the server according to the configuration options
+// provided in the RebuildOpts struct.
+func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) RebuildResult {
var result RebuildResult
if id == "" {
- result.Err = &ErrArgument{
- Function: "Rebuild",
- Argument: "id",
- Value: "",
- }
+ result.Err = fmt.Errorf("ID is required")
return result
}
- if name == "" {
- result.Err = &ErrArgument{
- Function: "Rebuild",
- Argument: "name",
- Value: "",
- }
+ reqBody, err := opts.ToServerRebuildMap()
+ if err != nil {
+ result.Err = err
return result
}
- if password == "" {
- result.Err = &ErrArgument{
- Function: "Rebuild",
- Argument: "password",
- Value: "",
- }
- return result
- }
-
- if imageRef == "" {
- result.Err = &ErrArgument{
- Function: "Rebuild",
- Argument: "imageRef",
- Value: "",
- }
- return result
- }
-
- if additional == nil {
- additional = make(map[string]interface{}, 0)
- }
-
- additional["name"] = name
- additional["imageRef"] = imageRef
- additional["adminPass"] = password
-
_, result.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
- ReqBody: struct {
- R map[string]interface{} `json:"rebuild"`
- }{
- additional,
- },
+ ReqBody: &reqBody,
Results: &result.Resp,
MoreHeaders: client.Provider.AuthenticatedHeaders(),
OkCodes: []int{202},
})
+
return result
}
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
index 01c4cdb..806efd3 100644
--- a/openstack/compute/v2/servers/requests_test.go
+++ b/openstack/compute/v2/servers/requests_test.go
@@ -223,16 +223,16 @@
fmt.Fprintf(w, singleServerBody)
})
- client := serviceClient()
- actual, err := Rebuild(client,
- "1234asdf", "new-name", "swordfish",
- "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
- map[string]interface{}{"accessIPv4": "1.2.3.4"},
- ).Extract()
- if err != nil {
- t.Fatalf("Unexpected Rebuild error: %v", err)
+ opts := RebuildOpts{
+ Name: "new-name",
+ AdminPass: "swordfish",
+ ImageID: "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb",
+ AccessIPv4: "1.2.3.4",
}
+ actual, err := Rebuild(serviceClient(), "1234asdf", opts).Extract()
+ testhelper.AssertNoErr(t, err)
+
equalServers(t, serverDerp, *actual)
}