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 }