github.com/uber/kraken@v0.1.4/origin/blobserver/uploader.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 blobserver 15 16 import ( 17 "io" 18 "net/http" 19 "os" 20 21 "github.com/uber/kraken/core" 22 "github.com/uber/kraken/lib/store" 23 "github.com/uber/kraken/utils/handler" 24 "github.com/docker/distribution/uuid" 25 ) 26 27 // uploader executes a chunked upload. 28 type uploader struct { 29 cas *store.CAStore 30 } 31 32 func newUploader(cas *store.CAStore) *uploader { 33 return &uploader{cas} 34 } 35 36 func (u *uploader) start(d core.Digest) (uid string, err error) { 37 if ok, err := blobExists(u.cas, d); err != nil { 38 return "", err 39 } else if ok { 40 return "", handler.ErrorStatus(http.StatusConflict) 41 } 42 uid = uuid.Generate().String() 43 if err := u.cas.CreateUploadFile(uid, 0); err != nil { 44 return "", handler.Errorf("create upload file: %s", err) 45 } 46 return uid, nil 47 } 48 49 func (u *uploader) patch( 50 d core.Digest, uid string, chunk io.Reader, start, end int64) error { 51 52 if ok, err := blobExists(u.cas, d); err != nil { 53 return err 54 } else if ok { 55 return handler.ErrorStatus(http.StatusConflict) 56 } 57 f, err := u.cas.GetUploadFileReadWriter(uid) 58 if err != nil { 59 if os.IsNotExist(err) { 60 return handler.ErrorStatus(http.StatusNotFound) 61 } 62 return handler.Errorf("get upload file: %s", err) 63 } 64 defer f.Close() 65 if _, err := f.Seek(start, 0); err != nil { 66 return handler.Errorf("seek offset %d: %s", start, err).Status(http.StatusBadRequest) 67 } 68 if _, err := io.CopyN(f, chunk, end-start); err != nil { 69 return handler.Errorf("copy: %s", err) 70 } 71 return nil 72 } 73 74 func (u *uploader) verify(d core.Digest, uid string) error { 75 digester := core.NewDigester() 76 f, err := u.cas.GetUploadFileReader(uid) 77 if err != nil { 78 if os.IsNotExist(err) { 79 return handler.ErrorStatus(http.StatusNotFound) 80 } 81 return handler.Errorf("get upload file: %s", err) 82 } 83 defer f.Close() 84 computedDigest, err := digester.FromReader(f) 85 if err != nil { 86 return handler.Errorf("calculate digest: %s", err) 87 } 88 if computedDigest != d { 89 return handler. 90 Errorf("computed digest %s doesn't match parameter %s", computedDigest, d). 91 Status(http.StatusBadRequest) 92 } 93 return nil 94 } 95 96 func (u *uploader) commit(d core.Digest, uid string) error { 97 if err := u.cas.MoveUploadFileToCache(uid, d.Hex()); err != nil { 98 if os.IsExist(err) { 99 return handler.ErrorStatus(http.StatusConflict) 100 } 101 return handler.Errorf("move upload file to cache: %s", err) 102 } 103 return nil 104 }