github.com/uber/kraken@v0.1.4/lib/dockerregistry/uploads.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package dockerregistry 15 16 import ( 17 "bytes" 18 "errors" 19 "fmt" 20 "io" 21 stdpath "path" 22 "time" 23 24 "github.com/uber/kraken/lib/dockerregistry/transfer" 25 "github.com/uber/kraken/lib/store" 26 "github.com/uber/kraken/lib/store/metadata" 27 28 storagedriver "github.com/docker/distribution/registry/storage/driver" 29 ) 30 31 type uploads interface { 32 reader(path string, subtype PathSubType, offset int64) (io.ReadCloser, error) 33 getContent(path string, subtype PathSubType) ([]byte, error) 34 putContent(path string, subtype PathSubType, content []byte) error 35 putBlobContent(path string, content []byte) error 36 writer(path string, subtype PathSubType) (store.FileReadWriter, error) 37 stat(path string) (storagedriver.FileInfo, error) 38 list(path string, subtype PathSubType) ([]string, error) 39 move(uploadPath, blobPath string) error 40 } 41 42 type casUploads struct { 43 cas *store.CAStore 44 transferer transfer.ImageTransferer 45 } 46 47 func newCASUploads(cas *store.CAStore, transferer transfer.ImageTransferer) *casUploads { 48 return &casUploads{cas, transferer} 49 } 50 51 func (u *casUploads) getContent(path string, subtype PathSubType) ([]byte, error) { 52 uuid, err := GetUploadUUID(path) 53 if err != nil { 54 return nil, err 55 } 56 switch subtype { 57 case _startedat: 58 var s startedAtMetadata 59 if err := u.cas.GetUploadFileMetadata(uuid, &s); err != nil { 60 return nil, err 61 } 62 return s.Serialize() 63 case _hashstates: 64 algo, offset, err := GetUploadAlgoAndOffset(path) 65 if err != nil { 66 return nil, err 67 } 68 hs := newHashStateMetadata(algo, offset) 69 if err := u.cas.GetUploadFileMetadata(uuid, hs); err != nil { 70 return nil, err 71 } 72 return hs.Serialize() 73 } 74 return nil, InvalidRequestError{path} 75 } 76 77 func (u *casUploads) reader(path string, subtype PathSubType, offset int64) (io.ReadCloser, error) { 78 switch subtype { 79 case _data: 80 uuid, err := GetUploadUUID(path) 81 if err != nil { 82 return nil, fmt.Errorf("get upload uuid: %s", err) 83 } 84 r, err := u.cas.GetUploadFileReader(uuid) 85 if err != nil { 86 return nil, fmt.Errorf("get reader: %s", err) 87 } 88 if _, err := r.Seek(offset, io.SeekStart); err != nil { 89 return nil, fmt.Errorf("seek: %s", err) 90 } 91 return r, nil 92 } 93 return nil, InvalidRequestError{path} 94 } 95 96 func (u *casUploads) putContent(path string, subtype PathSubType, content []byte) error { 97 uuid, err := GetUploadUUID(path) 98 if err != nil { 99 return err 100 } 101 switch subtype { 102 case _startedat: 103 if err := u.cas.CreateUploadFile(uuid, 0); err != nil { 104 return fmt.Errorf("create upload file: %s", err) 105 } 106 s := newStartedAtMetadata(time.Now()) 107 if err := u.cas.SetUploadFileMetadata(uuid, s); err != nil { 108 return fmt.Errorf("set started at: %s", err) 109 } 110 return nil 111 case _hashstates: 112 algo, offset, err := GetUploadAlgoAndOffset(path) 113 if err != nil { 114 return err 115 } 116 hs := newHashStateMetadata(algo, offset) 117 if err := hs.Deserialize(content); err != nil { 118 return fmt.Errorf("deserialize hash state: %s", err) 119 } 120 return u.cas.SetUploadFileMetadata(uuid, hs) 121 } 122 return InvalidRequestError{path} 123 } 124 125 func (u *casUploads) putBlobContent(path string, content []byte) error { 126 d, err := GetBlobDigest(path) 127 if err != nil { 128 return fmt.Errorf("get digest: %s", err) 129 } 130 if err := u.cas.CreateCacheFile(d.Hex(), bytes.NewReader(content)); err != nil { 131 return fmt.Errorf("create cache file: %s", err) 132 } 133 if err := u.transferer.Upload("TODO", d, store.NewBufferFileReader(content)); err != nil { 134 return fmt.Errorf("upload: %s", err) 135 } 136 return nil 137 } 138 139 func (u *casUploads) writer(path string, subtype PathSubType) (store.FileReadWriter, error) { 140 uuid, err := GetUploadUUID(path) 141 if err != nil { 142 return nil, err 143 } 144 switch subtype { 145 case _data: 146 return u.cas.GetUploadFileReadWriter(uuid) 147 } 148 return nil, InvalidRequestError{path} 149 } 150 151 func (u *casUploads) stat(path string) (storagedriver.FileInfo, error) { 152 uuid, err := GetUploadUUID(path) 153 if err != nil { 154 return nil, err 155 } 156 info, err := u.cas.GetUploadFileStat(uuid) 157 if err != nil { 158 return nil, err 159 } 160 // Hacking the path, since kraken storage driver is also the consumer of this info. 161 // Instead of the relative path from root that docker registry expected, just use uuid. 162 return storagedriver.FileInfoInternal{ 163 FileInfoFields: storagedriver.FileInfoFields{ 164 Path: uuid, 165 Size: info.Size(), 166 ModTime: info.ModTime(), 167 IsDir: info.IsDir(), 168 }, 169 }, nil 170 } 171 172 func (u *casUploads) list(path string, subtype PathSubType) ([]string, error) { 173 uuid, err := GetUploadUUID(path) 174 if err != nil { 175 return nil, err 176 } 177 switch subtype { 178 case _hashstates: 179 var paths []string 180 u.cas.RangeUploadMetadata(uuid, func(md metadata.Metadata) error { 181 if hs, ok := md.(*hashStateMetadata); ok { 182 p := stdpath.Join("localstore", "_uploads", uuid, hs.dockerPath()) 183 paths = append(paths, p) 184 } 185 return nil 186 }) 187 return paths, nil 188 } 189 return nil, InvalidRequestError{path} 190 } 191 192 func (u *casUploads) move(uploadPath, blobPath string) error { 193 uuid, err := GetUploadUUID(uploadPath) 194 if err != nil { 195 return fmt.Errorf("get upload uuid: %s", err) 196 } 197 d, err := GetBlobDigest(blobPath) 198 if err != nil { 199 return fmt.Errorf("get blob uuid: %s", err) 200 } 201 if err := u.cas.MoveUploadFileToCache(uuid, d.Hex()); err != nil { 202 return fmt.Errorf("move upload file to cache: %s", err) 203 } 204 f, err := u.cas.GetCacheFileReader(d.Hex()) 205 if err != nil { 206 return fmt.Errorf("get cache file: %s", err) 207 } 208 if err := u.transferer.Upload("TODO", d, f); err != nil { 209 return fmt.Errorf("upload: %s", err) 210 } 211 return nil 212 } 213 214 var errUploadsDisabled = errors.New("uploads are disabled") 215 216 type disabledUploads struct{} 217 218 func (u disabledUploads) reader(path string, subtype PathSubType, offset int64) (io.ReadCloser, error) { 219 return nil, errUploadsDisabled 220 } 221 222 func (u disabledUploads) getContent(path string, subtype PathSubType) ([]byte, error) { 223 return nil, errUploadsDisabled 224 } 225 226 func (u disabledUploads) putContent(path string, subtype PathSubType, content []byte) error { 227 return errUploadsDisabled 228 } 229 230 func (u disabledUploads) putBlobContent(path string, content []byte) error { 231 return errUploadsDisabled 232 } 233 234 func (u disabledUploads) writer(path string, subtype PathSubType) (store.FileReadWriter, error) { 235 return nil, errUploadsDisabled 236 } 237 238 func (u disabledUploads) stat(path string) (storagedriver.FileInfo, error) { 239 return nil, errUploadsDisabled 240 } 241 242 func (u disabledUploads) list(path string, subtype PathSubType) ([]string, error) { 243 return nil, errUploadsDisabled 244 } 245 246 func (u disabledUploads) move(uploadPath, blobPath string) error { 247 return errUploadsDisabled 248 }