Merge pull request #81 from markpeek/markpeek-image
Image changes to support packer.io
diff --git a/images.go b/images.go
index 38c73e1..61a3369 100644
--- a/images.go
+++ b/images.go
@@ -21,6 +21,22 @@
return is, err
}
+func (gsp *genericServersProvider) ImageById(id string) (*Image, error) {
+ var is *Image
+
+ err := gsp.context.WithReauth(gsp.access, func() error {
+ url := gsp.endpoint + "/images/" + id
+ return perigee.Get(url, perigee.Options{
+ CustomClient: gsp.context.httpClient,
+ Results: &struct{ Image **Image }{&is},
+ MoreHeaders: map[string]string{
+ "X-Auth-Token": gsp.access.AuthToken(),
+ },
+ })
+ })
+ return is, err
+}
+
// ImageLink provides a reference to a image by either ID or by direct URL.
// Some services use just the ID, others use just the URL.
// This structure provides a common means of expressing both in a single field.
diff --git a/interfaces.go b/interfaces.go
index 725b602..6786d39 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -128,6 +128,9 @@
// Other providers may reserve the right to act on additional fields.
RebuildServer(id string, ns NewServer) (*Server, error)
+ // CreateImage will create a new image from the specified server id returning the id of the new image.
+ CreateImage(id string, ci CreateImage) (string, error)
+
// Addresses
// ListAddresses yields the list of available addresses for the server.
@@ -142,6 +145,9 @@
// returns full details for each image, if available.
ListImages() ([]Image, error)
+ // ImageById yields details about a specific image.
+ ImageById(id string) (*Image, error)
+
// Flavors
// ListFlavors yields the list of available system flavors. This function
diff --git a/reauth.go b/reauth.go
index eb9ac1e..342aca4 100644
--- a/reauth.go
+++ b/reauth.go
@@ -21,3 +21,16 @@
}
return err
}
+
+// This is like WithReauth above but returns a perigee Response object
+func (c *Context) ResponseWithReauth(ap AccessProvider, f func() (*perigee.Response, error)) (*perigee.Response, error) {
+ response, err := f()
+ cause, ok := err.(*perigee.UnexpectedResponseCodeError)
+ if ok && cause.Actual == 401 {
+ err = c.reauthHandler(ap)
+ if err == nil {
+ response, err = f()
+ }
+ }
+ return response, err
+}
diff --git a/servers.go b/servers.go
index 6de42b0..798666a 100644
--- a/servers.go
+++ b/servers.go
@@ -6,6 +6,7 @@
import (
"fmt"
"github.com/racker/perigee"
+ "strings"
)
// genericServersProvider structures provide the implementation for generic OpenStack-compatible
@@ -314,6 +315,35 @@
return *pas, err
}
+// See the CloudServersProvider interface for details.
+func (gsp *genericServersProvider) CreateImage(id string, ci CreateImage) (string, error) {
+ response, err := gsp.context.ResponseWithReauth(gsp.access, func() (*perigee.Response, error) {
+ ep := fmt.Sprintf("%s/servers/%s/action", gsp.endpoint, id)
+ return perigee.Request("POST", ep, perigee.Options{
+ ReqBody: &struct {
+ CreateImage *CreateImage `json:"createImage"`
+ }{&ci},
+ MoreHeaders: map[string]string{
+ "X-Auth-Token": gsp.access.AuthToken(),
+ },
+ OkCodes: []int{200, 202},
+ DumpReqJson: true,
+ })
+ })
+
+ if err != nil {
+ return "", err
+ }
+ location, err := response.HttpResponse.Location()
+ if err != nil {
+ return "", err
+ }
+
+ // Return the last element of the location which is the image id
+ locationArr := strings.Split(location.Path, "/")
+ return locationArr[len(locationArr)-1], err
+}
+
// RaxBandwidth provides measurement of server bandwidth consumed over a given audit interval.
type RaxBandwidth struct {
AuditPeriodEnd string `json:"audit_period_end"`
@@ -355,7 +385,7 @@
// The HostId field represents the host your server runs on and
// can be used to determine this scenario if it is relevant to your application.
// Note that HostId is unique only per account; it is not globally unique.
-//
+//
// Id provides the server's unique identifier.
// This field must be treated opaquely.
//
@@ -416,27 +446,27 @@
// http://docs.rackspace.com/servers/api/v2/cs-devguide/content/ch_extensions.html#ext_status
// for more details. It's too lengthy to include here.
type Server struct {
- AccessIPv4 string `json:"accessIPv4"`
- AccessIPv6 string `json:"accessIPv6"`
- Addresses AddressSet `json:"addresses"`
- Created string `json:"created"`
- Flavor FlavorLink `json:"flavor"`
- HostId string `json:"hostId"`
- Id string `json:"id"`
- Image ImageLink `json:"image"`
- Links []Link `json:"links"`
- Metadata interface{} `json:"metadata"`
- Name string `json:"name"`
- Progress int `json:"progress"`
- Status string `json:"status"`
- TenantId string `json:"tenant_id"`
- Updated string `json:"updated"`
- UserId string `json:"user_id"`
- OsDcfDiskConfig string `json:"OS-DCF:diskConfig"`
- RaxBandwidth []RaxBandwidth `json:"rax-bandwidth:bandwidth"`
- OsExtStsPowerState int `json:"OS-EXT-STS:power_state"`
- OsExtStsTaskState string `json:"OS-EXT-STS:task_state"`
- OsExtStsVmState string `json:"OS-EXT-STS:vm_state"`
+ AccessIPv4 string `json:"accessIPv4"`
+ AccessIPv6 string `json:"accessIPv6"`
+ Addresses AddressSet `json:"addresses"`
+ Created string `json:"created"`
+ Flavor FlavorLink `json:"flavor"`
+ HostId string `json:"hostId"`
+ Id string `json:"id"`
+ Image ImageLink `json:"image"`
+ Links []Link `json:"links"`
+ Metadata map[string]string `json:"metadata"`
+ Name string `json:"name"`
+ Progress int `json:"progress"`
+ Status string `json:"status"`
+ TenantId string `json:"tenant_id"`
+ Updated string `json:"updated"`
+ UserId string `json:"user_id"`
+ OsDcfDiskConfig string `json:"OS-DCF:diskConfig"`
+ RaxBandwidth []RaxBandwidth `json:"rax-bandwidth:bandwidth"`
+ OsExtStsPowerState int `json:"OS-EXT-STS:power_state"`
+ OsExtStsTaskState string `json:"OS-EXT-STS:task_state"`
+ OsExtStsVmState string `json:"OS-EXT-STS:vm_state"`
}
// NewServerSettings structures record those fields of the Server structure to change
@@ -493,16 +523,17 @@
// Any Links provided are used to refer to the server specifically by URL.
// These links are useful for making additional REST calls not explicitly supported by Gorax.
type NewServer struct {
- Name string `json:"name,omitempty"`
- ImageRef string `json:"imageRef,omitempty"`
- FlavorRef string `json:"flavorRef,omitempty"`
- Metadata interface{} `json:"metadata,omitempty"`
- Personality []FileConfig `json:"personality,omitempty"`
- Networks []NetworkConfig `json:"networks,omitempty"`
- AdminPass string `json:"adminPass,omitempty"`
- Id string `json:"id,omitempty"`
- Links []Link `json:"links,omitempty"`
- OsDcfDiskConfig string `json:"OS-DCF:diskConfig,omitempty"`
+ Name string `json:"name,omitempty"`
+ ImageRef string `json:"imageRef,omitempty"`
+ FlavorRef string `json:"flavorRef,omitempty"`
+ Metadata map[string]string `json:"metadata,omitempty"`
+ Personality []FileConfig `json:"personality,omitempty"`
+ Networks []NetworkConfig `json:"networks,omitempty"`
+ AdminPass string `json:"adminPass,omitempty"`
+ KeyPairName string `json:"key_name,omitempty"`
+ Id string `json:"id,omitempty"`
+ Links []Link `json:"links,omitempty"`
+ OsDcfDiskConfig string `json:"OS-DCF:diskConfig,omitempty"`
}
// ResizeRequest structures are used internally to encode to JSON the parameters required to resize a server instance.
@@ -513,3 +544,8 @@
FlavorRef string `json:"flavorRef"`
DiskConfig string `json:"OS-DCF:diskConfig,omitempty"`
}
+
+type CreateImage struct {
+ Name string `json:"name"`
+ Metadata map[string]string `json:"metadata,omitempty"`
+}