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:
Andrew Starr-Bochicchio 2018-11-13 14:55:25 -05:00 committed by GitHub
parent 8f0fd9c1e6
commit c4ae66932b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 136 additions and 20 deletions

View File

@ -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
}

View File

@ -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)
}