Merge pull request #413 from doubledutch/personality
Server personality
diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go
index 7b928e9..f6c7c05 100644
--- a/acceptance/openstack/compute/v2/servers_test.go
+++ b/acceptance/openstack/compute/v2/servers_test.go
@@ -107,6 +107,12 @@
servers.Network{UUID: network.ID},
},
AdminPass: pwd,
+ Personality: servers.Personality{
+ &servers.File{
+ Path: "/etc/test",
+ Contents: []byte("hello world"),
+ },
+ },
}).Extract()
if err != nil {
t.Fatalf("Unable to create server: %v", err)
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index aa8c1a8..af77546 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -2,6 +2,7 @@
import (
"encoding/base64"
+ "encoding/json"
"errors"
"fmt"
@@ -14,6 +15,7 @@
type ListOptsBuilder interface {
ToServerListQuery() (string, error)
}
+
// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the server attributes you want to see returned. Marker and Limit are used
@@ -95,6 +97,31 @@
FixedIP string
}
+// Personality is an array of files that are injected into the server at launch.
+type Personality []*File
+
+// File is used within CreateOpts and RebuildOpts to inject a file into the server at launch.
+// File implements the json.Marshaler interface, so when a Create or Rebuild operation is requested,
+// json.Marshal will call File's MarshalJSON method.
+type File struct {
+ // Path of the file
+ Path string
+ // Contents of the file. Maximum content size is 255 bytes.
+ Contents []byte
+}
+
+// MarshalJSON marshals the escaped file, base64 encoding the contents.
+func (f *File) MarshalJSON() ([]byte, error) {
+ file := struct {
+ Path string `json:"path"`
+ Contents string `json:"contents"`
+ }{
+ Path: f.Path,
+ Contents: base64.StdEncoding.EncodeToString(f.Contents),
+ }
+ return json.Marshal(file)
+}
+
// CreateOpts specifies server creation parameters.
type CreateOpts struct {
// Name [required] is the name to assign to the newly launched server.
@@ -124,9 +151,9 @@
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
Metadata map[string]string
- // 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
+ // Personality [optional] includes files to inject into the server at launch.
+ // Create will base64-encode file contents for you.
+ Personality Personality
// ConfigDrive [optional] enables metadata injection through a configuration drive.
ConfigDrive bool
@@ -154,10 +181,6 @@
encoded := base64.StdEncoding.EncodeToString(opts.UserData)
server["user_data"] = &encoded
}
- if opts.Personality != nil {
- encoded := base64.StdEncoding.EncodeToString(opts.Personality)
- server["personality"] = &encoded
- }
if opts.ConfigDrive {
server["config_drive"] = "true"
}
@@ -202,6 +225,10 @@
server["networks"] = networks
}
+ if len(opts.Personality) > 0 {
+ server["personality"] = opts.Personality
+ }
+
return map[string]interface{}{"server": server}, nil
}
@@ -391,9 +418,9 @@
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
Metadata map[string]string
- // 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
+ // Personality [optional] includes files to inject into the server at launch.
+ // Rebuild will base64-encode file contents for you.
+ Personality Personality
}
// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
@@ -429,9 +456,8 @@
server["metadata"] = opts.Metadata
}
- if opts.Personality != nil {
- encoded := base64.StdEncoding.EncodeToString(opts.Personality)
- server["personality"] = &encoded
+ if len(opts.Personality) > 0 {
+ server["personality"] = opts.Personality
}
return map[string]interface{}{"rebuild": server}, nil
@@ -741,5 +767,5 @@
})
res.Err = err
res.Header = response.Header
- return res
+ return res
}
diff --git a/openstack/compute/v2/servers/requests_test.go b/openstack/compute/v2/servers/requests_test.go
index 1f39fe1..88cb54d 100644
--- a/openstack/compute/v2/servers/requests_test.go
+++ b/openstack/compute/v2/servers/requests_test.go
@@ -1,6 +1,8 @@
package servers
import (
+ "encoding/base64"
+ "encoding/json"
"net/http"
"testing"
@@ -334,3 +336,38 @@
_, err := CreateImage(client.ServiceClient(), "serverimage", CreateImageOpts{Name: "test"}).ExtractImageID()
th.AssertNoErr(t, err)
}
+
+func TestMarshalPersonality(t *testing.T) {
+ name := "/etc/test"
+ contents := []byte("asdfasdf")
+
+ personality := Personality{
+ &File{
+ Path: name,
+ Contents: contents,
+ },
+ }
+
+ data, err := json.Marshal(personality)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var actual []map[string]string
+ err = json.Unmarshal(data, &actual)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(actual) != 1 {
+ t.Fatal("expected personality length 1")
+ }
+
+ if actual[0]["path"] != name {
+ t.Fatal("file path incorrect")
+ }
+
+ if actual[0]["contents"] != base64.StdEncoding.EncodeToString(contents) {
+ t.Fatal("file contents incorrect")
+ }
+}
diff --git a/rackspace/compute/v2/servers/requests.go b/rackspace/compute/v2/servers/requests.go
index 809183e..1ebb897 100644
--- a/rackspace/compute/v2/servers/requests.go
+++ b/rackspace/compute/v2/servers/requests.go
@@ -36,9 +36,9 @@
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
Metadata map[string]string
- // 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
+ // Personality [optional] includes files to inject into the server at launch.
+ // Create will base64-encode file contents for you.
+ Personality os.Personality
// ConfigDrive [optional] enables metadata injection through a configuration drive.
ConfigDrive bool
@@ -130,9 +130,9 @@
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
Metadata map[string]string
- // 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
+ // Personality [optional] includes files to inject into the server at launch.
+ // Rebuild will base64-encode file contents for you.
+ Personality os.Personality
// Rackspace-specific stuff begins here.