Add support for creating custom images and listing images by tag. (#186)
* Add support for creating custom images. * Add support for listing images by tag. * Use http method constants.
This commit is contained in:
parent
8f0fd9c1e6
commit
c4ae66932b
63
images.go
63
images.go
|
@ -16,8 +16,10 @@ type ImagesService interface {
|
|||
ListDistribution(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||
ListApplication(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||
ListUser(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||
ListByTag(ctx context.Context, tag string, opt *ListOptions) ([]Image, *Response, error)
|
||||
GetByID(context.Context, int) (*Image, *Response, error)
|
||||
GetBySlug(context.Context, string) (*Image, *Response, error)
|
||||
Create(context.Context, *CustomImageCreateRequest) (*Image, *Response, error)
|
||||
Update(context.Context, int, *ImageUpdateRequest) (*Image, *Response, error)
|
||||
Delete(context.Context, int) (*Response, error)
|
||||
}
|
||||
|
@ -32,15 +34,20 @@ var _ ImagesService = &ImagesServiceOp{}
|
|||
|
||||
// Image represents a DigitalOcean Image
|
||||
type Image struct {
|
||||
ID int `json:"id,float64,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Distribution string `json:"distribution,omitempty"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Public bool `json:"public,omitempty"`
|
||||
Regions []string `json:"regions,omitempty"`
|
||||
MinDiskSize int `json:"min_disk_size,omitempty"`
|
||||
Created string `json:"created_at,omitempty"`
|
||||
ID int `json:"id,float64,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Distribution string `json:"distribution,omitempty"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Public bool `json:"public,omitempty"`
|
||||
Regions []string `json:"regions,omitempty"`
|
||||
MinDiskSize int `json:"min_disk_size,omitempty"`
|
||||
SizeGigaBytes float64 `json:"size_gigabytes,omitempty"`
|
||||
Created string `json:"created_at,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
}
|
||||
|
||||
// ImageUpdateRequest represents a request to update an image.
|
||||
|
@ -48,6 +55,16 @@ type ImageUpdateRequest struct {
|
|||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// CustomImageCreateRequest represents a request to create a custom image.
|
||||
type CustomImageCreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Region string `json:"region"`
|
||||
Distribution string `json:"distribution,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
type imageRoot struct {
|
||||
Image *Image
|
||||
}
|
||||
|
@ -60,6 +77,7 @@ type imagesRoot struct {
|
|||
type listImageOptions struct {
|
||||
Private bool `url:"private,omitempty"`
|
||||
Type string `url:"type,omitempty"`
|
||||
Tag string `url:"tag_name,omitempty"`
|
||||
}
|
||||
|
||||
func (i Image) String() string {
|
||||
|
@ -89,6 +107,12 @@ func (s *ImagesServiceOp) ListUser(ctx context.Context, opt *ListOptions) ([]Ima
|
|||
return s.list(ctx, opt, &listOpt)
|
||||
}
|
||||
|
||||
// ListByTag lists all images with a specific tag applied.
|
||||
func (s *ImagesServiceOp) ListByTag(ctx context.Context, tag string, opt *ListOptions) ([]Image, *Response, error) {
|
||||
listOpt := listImageOptions{Tag: tag}
|
||||
return s.list(ctx, opt, &listOpt)
|
||||
}
|
||||
|
||||
// GetByID retrieves an image by id.
|
||||
func (s *ImagesServiceOp) GetByID(ctx context.Context, imageID int) (*Image, *Response, error) {
|
||||
if imageID < 1 {
|
||||
|
@ -107,6 +131,25 @@ func (s *ImagesServiceOp) GetBySlug(ctx context.Context, slug string) (*Image, *
|
|||
return s.get(ctx, interface{}(slug))
|
||||
}
|
||||
|
||||
func (s *ImagesServiceOp) Create(ctx context.Context, createRequest *CustomImageCreateRequest) (*Image, *Response, error) {
|
||||
if createRequest == nil {
|
||||
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest(ctx, http.MethodPost, imageBasePath, createRequest)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(imageRoot)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Image, resp, err
|
||||
}
|
||||
|
||||
// Update an image name.
|
||||
func (s *ImagesServiceOp) Update(ctx context.Context, imageID int, updateRequest *ImageUpdateRequest) (*Image, *Response, error) {
|
||||
if imageID < 1 {
|
||||
|
@ -118,7 +161,7 @@ func (s *ImagesServiceOp) Update(ctx context.Context, imageID int, updateRequest
|
|||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
|
||||
req, err := s.client.NewRequest(ctx, "PUT", path, updateRequest)
|
||||
req, err := s.client.NewRequest(ctx, http.MethodPut, path, updateRequest)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -104,6 +104,32 @@ func TestImages_ListUser(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestImages_ListByTag(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/images", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
expected := "foo"
|
||||
actual := r.URL.Query().Get("tag_name")
|
||||
if actual != expected {
|
||||
t.Errorf("'tag_name' query = %v, expected %v", actual, expected)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, `{"images":[{"id":1},{"id":2}]}`)
|
||||
})
|
||||
|
||||
images, _, err := client.Images.ListByTag(ctx, "foo", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Images.ListByTag returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := []Image{{ID: 1}, {ID: 2}}
|
||||
if !reflect.DeepEqual(images, expected) {
|
||||
t.Errorf("Images.ListByTag returned %+v, expected %+v", images, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImages_ListImagesMultiplePages(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
@ -191,6 +217,52 @@ func TestImages_GetImageBySlug(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestImages_Create(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
createRequest := &CustomImageCreateRequest{
|
||||
Name: "my-new-image",
|
||||
Url: "http://example.com/distro-amd64.img",
|
||||
Region: "nyc3",
|
||||
Distribution: "Ubuntu",
|
||||
Description: "My new custom image",
|
||||
Tags: []string{"foo", "bar"},
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v2/images", func(w http.ResponseWriter, r *http.Request) {
|
||||
expected := map[string]interface{}{
|
||||
"name": "my-new-image",
|
||||
"url": "http://example.com/distro-amd64.img",
|
||||
"region": "nyc3",
|
||||
"distribution": "Ubuntu",
|
||||
"description": "My new custom image",
|
||||
"tags": []interface{}{"foo", "bar"},
|
||||
}
|
||||
|
||||
var v map[string]interface{}
|
||||
err := json.NewDecoder(r.Body).Decode(&v)
|
||||
if err != nil {
|
||||
t.Fatalf("decode json: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(v, expected) {
|
||||
t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, `{"image": {"id": 1,"created_at": "2018-09-20T19:28:00Z","description": "A custom image","distribution": "Ubuntu","error_message": "","regions": [],"type": "custom","tags":["foo","bar"],"status": "NEW"}}`)
|
||||
})
|
||||
|
||||
image, _, err := client.Images.Create(ctx, createRequest)
|
||||
if err != nil {
|
||||
t.Errorf("Images.Create returned error: %v", err)
|
||||
}
|
||||
|
||||
if id := image.ID; id != 1 {
|
||||
t.Errorf("expected id '%d', received '%d'", 1, id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImages_Update(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
@ -243,19 +315,20 @@ func TestImages_Destroy(t *testing.T) {
|
|||
|
||||
func TestImage_String(t *testing.T) {
|
||||
image := &Image{
|
||||
ID: 1,
|
||||
Name: "Image",
|
||||
Type: "snapshot",
|
||||
Distribution: "Ubuntu",
|
||||
Slug: "image",
|
||||
Public: true,
|
||||
Regions: []string{"one", "two"},
|
||||
MinDiskSize: 20,
|
||||
Created: "2013-11-27T09:24:55Z",
|
||||
ID: 1,
|
||||
Name: "Image",
|
||||
Type: "snapshot",
|
||||
Distribution: "Ubuntu",
|
||||
Slug: "image",
|
||||
Public: true,
|
||||
Regions: []string{"one", "two"},
|
||||
MinDiskSize: 20,
|
||||
SizeGigaBytes: 2.36,
|
||||
Created: "2013-11-27T09:24:55Z",
|
||||
}
|
||||
|
||||
stringified := image.String()
|
||||
expected := `godo.Image{ID:1, Name:"Image", Type:"snapshot", Distribution:"Ubuntu", Slug:"image", Public:true, Regions:["one" "two"], MinDiskSize:20, Created:"2013-11-27T09:24:55Z"}`
|
||||
expected := `godo.Image{ID:1, Name:"Image", Type:"snapshot", Distribution:"Ubuntu", Slug:"image", Public:true, Regions:["one" "two"], MinDiskSize:20, SizeGigaBytes:2.36, Created:"2013-11-27T09:24:55Z", Description:"", Status:"", ErrorMessage:""}`
|
||||
if expected != stringified {
|
||||
t.Errorf("Image.String returned %+v, expected %+v", stringified, expected)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue