computer/v2/servers: Check if opts.UserData is already Base64 Encoded (#170)

* computer/v2/servers: Check if opts.UserData is already Base64 Encoded

* Tweaks following review

* Add tests for UserData generation using both string and Base64 encoded values
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index 4ec2cf0..0ec5b0f 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -145,7 +145,7 @@
 	SecurityGroups []string `json:"-"`
 
 	// UserData contains configuration information or scripts to use upon launch.
-	// Create will base64-encode it for you.
+	// Create will base64-encode it for you, if it isn't already.
 	UserData []byte `json:"-"`
 
 	// AvailabilityZone in which to launch the server.
@@ -190,8 +190,13 @@
 	}
 
 	if opts.UserData != nil {
-		encoded := base64.StdEncoding.EncodeToString(opts.UserData)
-		b["user_data"] = &encoded
+		var userData string
+		if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil {
+			userData = base64.StdEncoding.EncodeToString(opts.UserData)
+		} else {
+			userData = string(opts.UserData)
+		}
+		b["user_data"] = &userData
 	}
 
 	if len(opts.SecurityGroups) > 0 {
diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go
index bb25643..adedb46 100644
--- a/openstack/compute/v2/servers/testing/fixtures.go
+++ b/openstack/compute/v2/servers/testing/fixtures.go
@@ -603,6 +603,27 @@
 	})
 }
 
+// HandleServerCreationWithUserdata sets up the test server to respond to a server creation request
+// with a given response.
+func HandleServerCreationWithUserdata(t *testing.T, response string) {
+	th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+			"server": {
+				"name": "derp",
+				"imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb",
+				"flavorRef": "1",
+				"user_data": "dXNlcmRhdGEgc3RyaW5n"
+			}
+		}`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
+
 // HandleServerCreationWithMetadata sets up the test server to respond to a server creation request
 // with a given response.
 func HandleServerCreationWithMetadata(t *testing.T, response string) {
diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go
index 418a730..15d6eb5 100644
--- a/openstack/compute/v2/servers/testing/requests_test.go
+++ b/openstack/compute/v2/servers/testing/requests_test.go
@@ -107,6 +107,40 @@
 	th.CheckDeepEquals(t, ServerDerp, *actual)
 }
 
+func TestCreateServerWithUserdataString(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerCreationWithUserdata(t, SingleServerBody)
+
+	actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{
+		Name:      "derp",
+		ImageRef:  "f90f6034-2570-4974-8351-6b49732ef2eb",
+		FlavorRef: "1",
+		UserData:  []byte("userdata string"),
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
+func TestCreateServerWithUserdataEncoded(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleServerCreationWithUserdata(t, SingleServerBody)
+
+	encoded := base64.StdEncoding.EncodeToString([]byte("userdata string"))
+
+	actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{
+		Name:      "derp",
+		ImageRef:  "f90f6034-2570-4974-8351-6b49732ef2eb",
+		FlavorRef: "1",
+		UserData:  []byte(encoded),
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, ServerDerp, *actual)
+}
+
 func TestCreateServerWithImageNameAndFlavorName(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()