github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/pkg/providers/common/hub_client.go (about)

     1  package common
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  
    10  	"github.com/djannot/aws-sdk-go/aws"
    11  	"github.com/djannot/aws-sdk-go/aws/session"
    12  	"github.com/djannot/aws-sdk-go/service/s3"
    13  	"github.com/emc-advanced-dev/pkg/errors"
    14  	"github.com/layer-x/layerx-commons/lxhttpclient"
    15  	"github.com/sirupsen/logrus"
    16  	"github.com/solo-io/unik/pkg/config"
    17  	"github.com/solo-io/unik/pkg/types"
    18  )
    19  
    20  const (
    21  	unik_hub_region = "us-east-1"
    22  	unik_hub_bucket = "unik-hub"
    23  	unik_image_info = "Unik-Image-Info"
    24  )
    25  
    26  func PullImage(config config.HubConfig, imageName string, writer io.Writer) (*types.Image, error) {
    27  	//to trigger modified djannot/aws-sdk
    28  	os.Setenv("S3_AUTH_PROXY_URL", config.URL)
    29  
    30  	//search available images, get user for image name
    31  	resp, body, err := lxhttpclient.Get(config.URL, "/images", nil)
    32  	if err != nil {
    33  		return nil, errors.New("performing GET request", err)
    34  	}
    35  	if resp.StatusCode != http.StatusOK {
    36  		return nil, errors.New(fmt.Sprintf("failed GETting image list status %v: %s", resp.StatusCode, string(body)), nil)
    37  	}
    38  	var images []*types.UserImage
    39  	if err := json.Unmarshal(body, &images); err != nil {
    40  		logrus.Fatal(err)
    41  	}
    42  	var user string
    43  	for _, image := range images {
    44  		if image.Name == imageName {
    45  			user = image.Owner
    46  			break
    47  		}
    48  	}
    49  	if user == "" {
    50  		return nil, errors.New("could not find image "+imageName, nil)
    51  	}
    52  
    53  	metadata, err := s3Download(imageKey(user, imageName), config.Password, writer)
    54  	if err != nil {
    55  		return nil, errors.New("downloading image", err)
    56  	}
    57  	var image types.Image
    58  	if err := json.Unmarshal([]byte(metadata), &image); err != nil {
    59  		return nil, errors.New("unmarshalling metadata for image", err)
    60  	}
    61  	logrus.Infof("downloaded image %v", image)
    62  	return &image, nil
    63  }
    64  
    65  func PushImage(config config.HubConfig, image *types.Image, imagePath string) error {
    66  	//to trigger modified djannot/aws-sdk
    67  	os.Setenv("S3_AUTH_PROXY_URL", config.URL)
    68  	metadata, err := json.Marshal(image)
    69  	if err != nil {
    70  		return errors.New("converting image metadata to json", err)
    71  	}
    72  	//upload image
    73  	reader, err := os.Open(imagePath)
    74  	if err != nil {
    75  		return errors.New("opening file", err)
    76  	}
    77  	defer reader.Close()
    78  	fileInfo, err := reader.Stat()
    79  	if err != nil {
    80  		return errors.New("getting file info", err)
    81  	}
    82  	if err := s3Upload(config, imageKey(config.Username, image.Name), string(metadata), reader, fileInfo.Size()); err != nil {
    83  		return errors.New("uploading image file", err)
    84  	}
    85  	logrus.Infof("Image %v pushed to %s", image, config.URL)
    86  	return nil
    87  }
    88  
    89  func RemoteDeleteImage(config config.HubConfig, imageName string) error {
    90  	//to trigger modified djannot/aws-sdk
    91  	os.Setenv("S3_AUTH_PROXY_URL", config.URL)
    92  	if err := s3Delete(config, imageKey(config.Username, imageName)); err != nil {
    93  		return errors.New("deleting image file", err)
    94  	}
    95  	logrus.Infof("Image %v deleted from %s", imageName, config.URL)
    96  	return nil
    97  }
    98  
    99  func s3Download(key, password string, writer io.Writer) (string, error) {
   100  	params := &s3.GetObjectInput{
   101  		Bucket:   aws.String(unik_hub_bucket),
   102  		Key:      aws.String(key),
   103  		Password: aws.String(password),
   104  	}
   105  	result, err := s3.New(session.New(&aws.Config{Region: aws.String(unik_hub_region)})).GetObject(params)
   106  	if err != nil {
   107  		return "", errors.New("failed to download from s3", err)
   108  	}
   109  	n, err := io.Copy(writer, result.Body)
   110  	if err != nil {
   111  		return "", errors.New("copying image bytes", err)
   112  	}
   113  	logrus.Infof("downloaded %v bytes", n)
   114  	if result.Metadata[unik_image_info] == nil {
   115  		return "", errors.New(fmt.Sprintf(unik_image_info+" was empty. full metadata: %+v", result.Metadata), nil)
   116  	}
   117  	return *result.Metadata[unik_image_info], nil
   118  }
   119  
   120  func s3Upload(config config.HubConfig, key, metadata string, body io.ReadSeeker, length int64) error {
   121  	params := &s3.PutObjectInput{
   122  		Body:   body,
   123  		Bucket: aws.String(unik_hub_bucket),
   124  		Key:    aws.String(key),
   125  		Metadata: map[string]*string{
   126  			"unik-password": aws.String(config.Password),
   127  			"unik-email":    aws.String(config.Username),
   128  			"unik-access":   aws.String("public"),
   129  			unik_image_info: aws.String(metadata),
   130  		},
   131  	}
   132  	result, err := s3.New(session.New(&aws.Config{Region: aws.String(unik_hub_region)})).PutObject(params)
   133  	if err != nil {
   134  		return errors.New("uploading image to s3 backend", err)
   135  	}
   136  	logrus.Infof("uploaded %v bytes: %v", length, result)
   137  	return nil
   138  }
   139  
   140  // unik hub has to do it itself to validate user
   141  func s3Delete(config config.HubConfig, key string) error {
   142  	deleteMessage := struct {
   143  		Username string `json:"user"`
   144  		Password string `json:"pass"`
   145  		Key      string `json:"key"`
   146  	}{
   147  		Username: config.Username,
   148  		Password: config.Password,
   149  		Key:      key,
   150  	}
   151  	resp, body, err := lxhttpclient.Post(config.URL, "/delete_image", nil, deleteMessage)
   152  	if err != nil {
   153  		return errors.New("failed to perform delete request", err)
   154  	}
   155  	if resp.StatusCode != 204 {
   156  		return errors.New(fmt.Sprintf("expected status code 204, got %v: %s", resp.StatusCode, string(body)), nil)
   157  	}
   158  	return nil
   159  }
   160  
   161  func imageKey(username, imageName string) string {
   162  	return "/" + username + "/" + imageName + "/latest" //TODO: support image versioning
   163  }