2018-01-16 12:48:25 -05:00
|
|
|
//
|
|
|
|
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
//
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
package compute
|
2017-04-26 15:12:57 -04:00
|
|
|
|
|
|
|
import (
|
2017-05-05 10:00:09 -04:00
|
|
|
"context"
|
2017-04-26 15:12:57 -04:00
|
|
|
"encoding/json"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2018-01-16 12:48:25 -05:00
|
|
|
"path"
|
2017-04-26 15:12:57 -04:00
|
|
|
"time"
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
"github.com/joyent/triton-go/client"
|
2018-01-16 12:48:25 -05:00
|
|
|
"github.com/pkg/errors"
|
2017-04-26 15:12:57 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
type ImagesClient struct {
|
2017-10-31 11:02:15 -04:00
|
|
|
client *client.Client
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type ImageFile struct {
|
|
|
|
Compression string `json:"compression"`
|
|
|
|
SHA1 string `json:"sha1"`
|
|
|
|
Size int64 `json:"size"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Image struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
OS string `json:"os"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Version string `json:"version"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
Requirements map[string]interface{} `json:"requirements"`
|
|
|
|
Homepage string `json:"homepage"`
|
|
|
|
Files []*ImageFile `json:"files"`
|
|
|
|
PublishedAt time.Time `json:"published_at"`
|
|
|
|
Owner string `json:"owner"`
|
|
|
|
Public bool `json:"public"`
|
|
|
|
State string `json:"state"`
|
|
|
|
Tags map[string]string `json:"tags"`
|
|
|
|
EULA string `json:"eula"`
|
|
|
|
ACL []string `json:"acl"`
|
|
|
|
}
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
type ListImagesInput struct {
|
|
|
|
Name string
|
|
|
|
OS string
|
|
|
|
Version string
|
|
|
|
Public bool
|
|
|
|
State string
|
|
|
|
Owner string
|
|
|
|
Type string
|
|
|
|
}
|
2017-04-26 15:12:57 -04:00
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
func (c *ImagesClient) List(ctx context.Context, input *ListImagesInput) ([]*Image, error) {
|
2018-01-16 12:48:25 -05:00
|
|
|
fullPath := path.Join("/", c.client.AccountName, "images")
|
2017-10-31 11:02:15 -04:00
|
|
|
|
|
|
|
query := &url.Values{}
|
|
|
|
if input.Name != "" {
|
|
|
|
query.Set("name", input.Name)
|
|
|
|
}
|
|
|
|
if input.OS != "" {
|
|
|
|
query.Set("os", input.OS)
|
|
|
|
}
|
|
|
|
if input.Version != "" {
|
|
|
|
query.Set("version", input.Version)
|
|
|
|
}
|
|
|
|
if input.Public {
|
|
|
|
query.Set("public", "true")
|
|
|
|
}
|
|
|
|
if input.State != "" {
|
|
|
|
query.Set("state", input.State)
|
|
|
|
}
|
|
|
|
if input.Owner != "" {
|
|
|
|
query.Set("owner", input.Owner)
|
|
|
|
}
|
|
|
|
if input.Type != "" {
|
|
|
|
query.Set("type", input.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
reqInputs := client.RequestInput{
|
|
|
|
Method: http.MethodGet,
|
2018-01-16 12:48:25 -05:00
|
|
|
Path: fullPath,
|
2017-10-31 11:02:15 -04:00
|
|
|
Query: query,
|
|
|
|
}
|
|
|
|
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
2017-04-26 15:12:57 -04:00
|
|
|
if respReader != nil {
|
|
|
|
defer respReader.Close()
|
|
|
|
}
|
|
|
|
if err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to list images")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var result []*Image
|
|
|
|
decoder := json.NewDecoder(respReader)
|
|
|
|
if err = decoder.Decode(&result); err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to decode list images response")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type GetImageInput struct {
|
|
|
|
ImageID string
|
|
|
|
}
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
func (c *ImagesClient) Get(ctx context.Context, input *GetImageInput) (*Image, error) {
|
2018-01-16 12:48:25 -05:00
|
|
|
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
2017-10-31 11:02:15 -04:00
|
|
|
reqInputs := client.RequestInput{
|
|
|
|
Method: http.MethodGet,
|
2018-01-16 12:48:25 -05:00
|
|
|
Path: fullPath,
|
2017-10-31 11:02:15 -04:00
|
|
|
}
|
|
|
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
2017-04-26 15:12:57 -04:00
|
|
|
if respReader != nil {
|
|
|
|
defer respReader.Close()
|
|
|
|
}
|
|
|
|
if err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to get image")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var result *Image
|
|
|
|
decoder := json.NewDecoder(respReader)
|
|
|
|
if err = decoder.Decode(&result); err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to decode get image response")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type DeleteImageInput struct {
|
|
|
|
ImageID string
|
|
|
|
}
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
func (c *ImagesClient) Delete(ctx context.Context, input *DeleteImageInput) error {
|
2018-01-16 12:48:25 -05:00
|
|
|
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
2017-10-31 11:02:15 -04:00
|
|
|
reqInputs := client.RequestInput{
|
|
|
|
Method: http.MethodDelete,
|
2018-01-16 12:48:25 -05:00
|
|
|
Path: fullPath,
|
2017-10-31 11:02:15 -04:00
|
|
|
}
|
|
|
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
2017-04-26 15:12:57 -04:00
|
|
|
if respReader != nil {
|
|
|
|
defer respReader.Close()
|
|
|
|
}
|
|
|
|
if err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return errors.Wrap(err, "unable to delete image")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type ExportImageInput struct {
|
|
|
|
ImageID string
|
|
|
|
MantaPath string
|
|
|
|
}
|
|
|
|
|
|
|
|
type MantaLocation struct {
|
|
|
|
MantaURL string `json:"manta_url"`
|
|
|
|
ImagePath string `json:"image_path"`
|
|
|
|
ManifestPath string `json:"manifest_path"`
|
|
|
|
}
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
func (c *ImagesClient) Export(ctx context.Context, input *ExportImageInput) (*MantaLocation, error) {
|
2018-01-16 12:48:25 -05:00
|
|
|
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
2017-04-26 15:12:57 -04:00
|
|
|
query := &url.Values{}
|
|
|
|
query.Set("action", "export")
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
reqInputs := client.RequestInput{
|
2018-01-16 12:48:25 -05:00
|
|
|
Method: http.MethodPost,
|
|
|
|
Path: fullPath,
|
2017-10-31 11:02:15 -04:00
|
|
|
Query: query,
|
|
|
|
}
|
|
|
|
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
2017-04-26 15:12:57 -04:00
|
|
|
if respReader != nil {
|
|
|
|
defer respReader.Close()
|
|
|
|
}
|
|
|
|
if err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to export image")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var result *MantaLocation
|
|
|
|
decoder := json.NewDecoder(respReader)
|
|
|
|
if err = decoder.Decode(&result); err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to decode export image response")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type CreateImageFromMachineInput struct {
|
|
|
|
MachineID string `json:"machine"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Version string `json:"version,omitempty"`
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
HomePage string `json:"homepage,omitempty"`
|
|
|
|
EULA string `json:"eula,omitempty"`
|
|
|
|
ACL []string `json:"acl,omitempty"`
|
|
|
|
Tags map[string]string `json:"tags,omitempty"`
|
|
|
|
}
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
func (c *ImagesClient) CreateFromMachine(ctx context.Context, input *CreateImageFromMachineInput) (*Image, error) {
|
2018-01-16 12:48:25 -05:00
|
|
|
fullPath := path.Join("/", c.client.AccountName, "images")
|
2017-10-31 11:02:15 -04:00
|
|
|
reqInputs := client.RequestInput{
|
|
|
|
Method: http.MethodPost,
|
2018-01-16 12:48:25 -05:00
|
|
|
Path: fullPath,
|
2017-10-31 11:02:15 -04:00
|
|
|
Body: input,
|
|
|
|
}
|
|
|
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
2017-04-26 15:12:57 -04:00
|
|
|
if respReader != nil {
|
|
|
|
defer respReader.Close()
|
|
|
|
}
|
|
|
|
if err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to create machine from image")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var result *Image
|
|
|
|
decoder := json.NewDecoder(respReader)
|
|
|
|
if err = decoder.Decode(&result); err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to decode create machine from image response")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type UpdateImageInput struct {
|
|
|
|
ImageID string `json:"-"`
|
2018-01-16 12:48:25 -05:00
|
|
|
Name string `json:"name,omitempty"`
|
2017-04-26 15:12:57 -04:00
|
|
|
Version string `json:"version,omitempty"`
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
HomePage string `json:"homepage,omitempty"`
|
|
|
|
EULA string `json:"eula,omitempty"`
|
|
|
|
ACL []string `json:"acl,omitempty"`
|
|
|
|
Tags map[string]string `json:"tags,omitempty"`
|
|
|
|
}
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
func (c *ImagesClient) Update(ctx context.Context, input *UpdateImageInput) (*Image, error) {
|
2018-01-16 12:48:25 -05:00
|
|
|
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
2017-04-26 15:12:57 -04:00
|
|
|
query := &url.Values{}
|
|
|
|
query.Set("action", "update")
|
|
|
|
|
2017-10-31 11:02:15 -04:00
|
|
|
reqInputs := client.RequestInput{
|
|
|
|
Method: http.MethodPost,
|
2018-01-16 12:48:25 -05:00
|
|
|
Path: fullPath,
|
2017-10-31 11:02:15 -04:00
|
|
|
Query: query,
|
|
|
|
Body: input,
|
|
|
|
}
|
|
|
|
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
2017-04-26 15:12:57 -04:00
|
|
|
if respReader != nil {
|
|
|
|
defer respReader.Close()
|
|
|
|
}
|
|
|
|
if err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to update image")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var result *Image
|
|
|
|
decoder := json.NewDecoder(respReader)
|
|
|
|
if err = decoder.Decode(&result); err != nil {
|
2018-01-16 12:48:25 -05:00
|
|
|
return nil, errors.Wrap(err, "unable to decode update image response")
|
2017-04-26 15:12:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|