github.com/cloudfoundry-attic/ltc@v0.0.0-20151123212628-098adc7919fc/blob_store/dav_blob_store/blob_store.go (about)

     1  package dav_blob_store
     2  
     3  import (
     4  	"encoding/xml"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"path"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/cloudfoundry-incubator/bbs/models"
    15  	"github.com/cloudfoundry-incubator/ltc/blob_store/blob"
    16  	config_package "github.com/cloudfoundry-incubator/ltc/config"
    17  )
    18  
    19  type BlobStore struct {
    20  	URL    *url.URL
    21  	Client *http.Client
    22  }
    23  
    24  func New(config config_package.BlobStoreConfig) *BlobStore {
    25  	return &BlobStore{
    26  		URL: &url.URL{
    27  			Scheme: "http",
    28  			Host:   fmt.Sprintf("%s:%s", config.Host, config.Port),
    29  			User:   url.UserPassword(config.Username, config.Password),
    30  		},
    31  		Client: http.DefaultClient,
    32  	}
    33  }
    34  
    35  type xmlTime time.Time
    36  
    37  func (t *xmlTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    38  	var v string
    39  	d.DecodeElement(&v, &start)
    40  	parse, err := time.Parse(time.RFC1123, v)
    41  	if err != nil {
    42  		return err
    43  	}
    44  	*t = xmlTime(parse)
    45  	return nil
    46  }
    47  
    48  type listResponse struct {
    49  	Responses []struct {
    50  		HREF          string  `xml:"href"`
    51  		LastModified  xmlTime `xml:"propstat>prop>getlastmodified"`
    52  		ContentLength int64   `xml:"propstat>prop>getcontentlength"`
    53  	} `xml:"response"`
    54  }
    55  
    56  func (b *BlobStore) List() ([]blob.Blob, error) {
    57  	baseURL := &url.URL{
    58  		Scheme: b.URL.Scheme,
    59  		Host:   b.URL.Host,
    60  		User:   b.URL.User,
    61  		Path:   "/blobs",
    62  	}
    63  
    64  	req, err := http.NewRequest("PROPFIND", baseURL.String(), nil)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	req.Header.Add("Depth", "1")
    70  
    71  	resp, err := b.Client.Do(req)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	defer resp.Body.Close()
    77  
    78  	if resp.StatusCode != 207 {
    79  		return nil, errors.New(resp.Status)
    80  	}
    81  
    82  	decoder := xml.NewDecoder(resp.Body)
    83  
    84  	var listResp listResponse
    85  	if err := decoder.Decode(&listResp); err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	var blobFiles []blob.Blob
    90  	for _, resp := range listResp.Responses {
    91  		u, err := url.Parse(resp.HREF)
    92  		if err != nil {
    93  			return nil, err
    94  		}
    95  
    96  		if path.Clean(u.Path) == path.Clean(baseURL.Path) {
    97  			continue
    98  		}
    99  
   100  		blobFiles = append(blobFiles, blob.Blob{
   101  			Path:    strings.Replace(path.Clean(u.Path), "/blobs/", "", 1),
   102  			Created: time.Time(resp.LastModified),
   103  			Size:    resp.ContentLength,
   104  		})
   105  	}
   106  
   107  	return blobFiles, nil
   108  }
   109  
   110  func (b *BlobStore) Upload(path string, contents io.ReadSeeker) error {
   111  	baseURL := &url.URL{
   112  		Scheme: b.URL.Scheme,
   113  		Host:   b.URL.Host,
   114  		User:   b.URL.User,
   115  		Path:   "/blobs/" + path,
   116  	}
   117  
   118  	length, err := contents.Seek(0, 2)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	contents.Seek(0, 0)
   123  
   124  	req, err := http.NewRequest("PUT", baseURL.String(), contents)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	req.ContentLength = length
   130  
   131  	resp, err := b.Client.Do(req)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
   137  		return errors.New(resp.Status)
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  func (b *BlobStore) Download(path string) (io.ReadCloser, error) {
   144  	baseURL := &url.URL{
   145  		Scheme: b.URL.Scheme,
   146  		Host:   b.URL.Host,
   147  		User:   b.URL.User,
   148  		Path:   "/blobs/" + path,
   149  	}
   150  
   151  	req, err := http.NewRequest("GET", baseURL.String(), nil)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	resp, err := b.Client.Do(req)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	if resp.StatusCode != http.StatusOK {
   162  		return nil, errors.New(resp.Status)
   163  	}
   164  
   165  	return resp.Body, nil
   166  }
   167  
   168  func (b *BlobStore) Delete(path string) error {
   169  	baseURL := &url.URL{
   170  		Scheme: b.URL.Scheme,
   171  		Host:   b.URL.Host,
   172  		User:   b.URL.User,
   173  		Path:   "/blobs/" + path,
   174  	}
   175  
   176  	req, err := http.NewRequest("DELETE", baseURL.String(), nil)
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	resp, err := b.Client.Do(req)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	if resp.StatusCode != http.StatusNoContent {
   187  		return errors.New(resp.Status)
   188  	}
   189  
   190  	return nil
   191  }
   192  
   193  func (b *BlobStore) DownloadAppBitsAction(dropletName string) *models.Action {
   194  	return models.WrapAction(&models.DownloadAction{
   195  		From:      b.URL.String() + "/blobs/" + dropletName + "-bits.zip",
   196  		To:        "/tmp/app",
   197  		User:      "vcap",
   198  		LogSource: "DROPLET",
   199  	})
   200  }
   201  
   202  func (b *BlobStore) DeleteAppBitsAction(dropletName string) *models.Action {
   203  	return models.WrapAction(&models.RunAction{
   204  		Path:      "/tmp/davtool",
   205  		Dir:       "/",
   206  		Args:      []string{"delete", b.URL.String() + "/blobs/" + dropletName + "-bits.zip"},
   207  		User:      "vcap",
   208  		LogSource: "DROPLET",
   209  	})
   210  }
   211  
   212  func (b *BlobStore) UploadDropletAction(dropletName string) *models.Action {
   213  	return models.WrapAction(&models.RunAction{
   214  		Path:      "/tmp/davtool",
   215  		Dir:       "/",
   216  		Args:      []string{"put", b.URL.String() + "/blobs/" + dropletName + "-droplet.tgz", "/tmp/droplet"},
   217  		User:      "vcap",
   218  		LogSource: "DROPLET",
   219  	})
   220  }
   221  
   222  func (b *BlobStore) DownloadDropletAction(dropletName string) *models.Action {
   223  	return models.WrapAction(&models.DownloadAction{
   224  		From:      b.URL.String() + "/blobs/" + dropletName + "-droplet.tgz",
   225  		To:        "/home/vcap",
   226  		User:      "vcap",
   227  		LogSource: "DROPLET",
   228  	})
   229  }