diff --git a/src/backend.go b/src/backend.go index d65b0c3..09bfdad 100644 --- a/src/backend.go +++ b/src/backend.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "io" @@ -22,33 +23,33 @@ func splitBlobName(name string) []string { type Backend interface { // Retrieve a blob. Returns `reader, size, mtime, err`. - GetBlob(name string) (reader io.ReadSeeker, size uint64, mtime time.Time, err error) + GetBlob(ctx context.Context, name string) (reader io.ReadSeeker, size uint64, mtime time.Time, err error) // Store a blob. If a blob called `name` already exists, this function returns `nil` without // regards to the old or new contents. It is expected that blobs are content-addressed, i.e. // the `name` contains a cryptographic hash of `data`, but the backend is ignorant of this. - PutBlob(name string, data []byte) error + PutBlob(ctx context.Context, name string, data []byte) error // Delete a blob. This is an unconditional operation that can break integrity of manifests. - DeleteBlob(name string) error + DeleteBlob(ctx context.Context, name string) error // Retrieve a manifest. - GetManifest(name string) (*Manifest, error) + GetManifest(ctx context.Context, name string) (*Manifest, error) // Stage a manifest. This operation stores a new version of a manifest, locking any blobs // referenced from it in place (for garbage collection purposes) but without any other side // effects. - StageManifest(manifest *Manifest) error + StageManifest(ctx context.Context, manifest *Manifest) error // Commit a manifest. This is an atomic operation; `GetManifest` calls will return either // the old version or the new version of the manifest, never anything else. - CommitManifest(name string, manifest *Manifest) error + CommitManifest(ctx context.Context, name string, manifest *Manifest) error // Delete a manifest. - DeleteManifest(name string) error + DeleteManifest(ctx context.Context, name string) error // Check whether a domain has any deployments. - CheckDomain(domain string) (found bool, err error) + CheckDomain(ctx context.Context, domain string) (found bool, err error) } var backend Backend @@ -61,7 +62,7 @@ func ConfigureBackend(config *StorageConfig) (err error) { } case "s3": - if backend, err = NewS3Backend(&config.S3); err != nil { + if backend, err = NewS3Backend(context.Background(), &config.S3); err != nil { err = fmt.Errorf("s3 backend: %w", err) } diff --git a/src/backend_fs.go b/src/backend_fs.go index 73334d5..cc58a17 100644 --- a/src/backend_fs.go +++ b/src/backend_fs.go @@ -1,6 +1,7 @@ package main import ( + "context" "crypto/sha256" "errors" "fmt" @@ -15,6 +16,8 @@ type FSBackend struct { siteRoot *os.Root } +var _ Backend = (*FSBackend)(nil) + func maybeCreateOpenRoot(dir string, name string) (*os.Root, error) { dirName := filepath.Join(dir, name) @@ -65,7 +68,15 @@ func (fs *FSBackend) Backend() Backend { return fs } -func (fs *FSBackend) GetBlob(name string) (reader io.ReadSeeker, size uint64, mtime time.Time, err error) { +func (fs *FSBackend) GetBlob( + ctx context.Context, + name string, +) ( + reader io.ReadSeeker, + size uint64, + mtime time.Time, + err error, +) { blobPath := filepath.Join(splitBlobName(name)...) stat, err := fs.blobRoot.Stat(blobPath) if errors.Is(err, os.ErrNotExist) { @@ -83,7 +94,7 @@ func (fs *FSBackend) GetBlob(name string) (reader io.ReadSeeker, size uint64, mt return file, uint64(stat.Size()), stat.ModTime(), nil } -func (fs *FSBackend) PutBlob(name string, data []byte) error { +func (fs *FSBackend) PutBlob(ctx context.Context, name string, data []byte) error { blobPath := filepath.Join(splitBlobName(name)...) blobDir := filepath.Dir(blobPath) @@ -117,12 +128,12 @@ again: return nil } -func (fs *FSBackend) DeleteBlob(name string) error { +func (fs *FSBackend) DeleteBlob(ctx context.Context, name string) error { blobPath := filepath.Join(splitBlobName(name)...) return fs.blobRoot.Remove(blobPath) } -func (fs *FSBackend) GetManifest(name string) (*Manifest, error) { +func (fs *FSBackend) GetManifest(ctx context.Context, name string) (*Manifest, error) { data, err := fs.siteRoot.ReadFile(name) if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("%w: %s", errNotFound, err.(*os.PathError).Path) @@ -137,7 +148,7 @@ func stagedManifestName(manifestData []byte) string { return fmt.Sprintf(".%x", sha256.Sum256(manifestData)) } -func (fs *FSBackend) StageManifest(manifest *Manifest) error { +func (fs *FSBackend) StageManifest(ctx context.Context, manifest *Manifest) error { manifestData := EncodeManifest(manifest) tempPath, err := createTempInRoot(fs.siteRoot, ".manifest", manifestData) @@ -152,7 +163,7 @@ func (fs *FSBackend) StageManifest(manifest *Manifest) error { return nil } -func (fs *FSBackend) CommitManifest(name string, manifest *Manifest) error { +func (fs *FSBackend) CommitManifest(ctx context.Context, name string, manifest *Manifest) error { manifestData := EncodeManifest(manifest) manifestHashName := stagedManifestName(manifestData) @@ -171,7 +182,7 @@ func (fs *FSBackend) CommitManifest(name string, manifest *Manifest) error { return nil } -func (fs *FSBackend) DeleteManifest(name string) error { +func (fs *FSBackend) DeleteManifest(ctx context.Context, name string) error { err := fs.siteRoot.Remove(name) if errors.Is(err, os.ErrNotExist) { return nil @@ -180,7 +191,7 @@ func (fs *FSBackend) DeleteManifest(name string) error { } } -func (fs *FSBackend) CheckDomain(domain string) (bool, error) { +func (fs *FSBackend) CheckDomain(ctx context.Context, domain string) (bool, error) { _, err := fs.siteRoot.Stat(domain) if errors.Is(err, os.ErrNotExist) { return false, nil diff --git a/src/backend_observer.go b/src/backend_observer.go index b98ee63..4bb2c1f 100644 --- a/src/backend_observer.go +++ b/src/backend_observer.go @@ -1,6 +1,7 @@ package main import ( + "context" "io" "time" @@ -37,12 +38,22 @@ type observedBackend struct { backend Backend } +var _ Backend = (*observedBackend)(nil) + func NewObservedBackend(backend Backend) Backend { return &observedBackend{backend: backend} } -func (b *observedBackend) GetBlob(name string) (reader io.ReadSeeker, size uint64, mtime time.Time, err error) { - reader, size, mtime, err = b.backend.GetBlob(name) +func (b *observedBackend) GetBlob( + ctx context.Context, + name string, +) ( + reader io.ReadSeeker, + size uint64, + mtime time.Time, + err error, +) { + reader, size, mtime, err = b.backend.GetBlob(ctx, name) if err != nil { return } @@ -51,8 +62,8 @@ func (b *observedBackend) GetBlob(name string) (reader io.ReadSeeker, size uint6 return } -func (b *observedBackend) PutBlob(name string, data []byte) error { - err := b.backend.PutBlob(name, data) +func (b *observedBackend) PutBlob(ctx context.Context, name string, data []byte) error { + err := b.backend.PutBlob(ctx, name, data) if err != nil { return err } @@ -61,12 +72,12 @@ func (b *observedBackend) PutBlob(name string, data []byte) error { return nil } -func (b *observedBackend) DeleteBlob(name string) error { - return b.backend.DeleteBlob(name) +func (b *observedBackend) DeleteBlob(ctx context.Context, name string) error { + return b.backend.DeleteBlob(ctx, name) } -func (b *observedBackend) GetManifest(name string) (manifest *Manifest, err error) { - manifest, err = b.backend.GetManifest(name) +func (b *observedBackend) GetManifest(ctx context.Context, name string) (manifest *Manifest, err error) { + manifest, err = b.backend.GetManifest(ctx, name) if err != nil { return } @@ -74,18 +85,18 @@ func (b *observedBackend) GetManifest(name string) (manifest *Manifest, err erro return } -func (b *observedBackend) StageManifest(manifest *Manifest) error { - return b.backend.StageManifest(manifest) +func (b *observedBackend) StageManifest(ctx context.Context, manifest *Manifest) error { + return b.backend.StageManifest(ctx, manifest) } -func (b *observedBackend) CommitManifest(name string, manifest *Manifest) error { - return b.backend.CommitManifest(name, manifest) +func (b *observedBackend) CommitManifest(ctx context.Context, name string, manifest *Manifest) error { + return b.backend.CommitManifest(ctx, name, manifest) } -func (b *observedBackend) DeleteManifest(name string) error { - return b.backend.DeleteManifest(name) +func (b *observedBackend) DeleteManifest(ctx context.Context, name string) error { + return b.backend.DeleteManifest(ctx, name) } -func (b *observedBackend) CheckDomain(domain string) (found bool, err error) { - return b.backend.CheckDomain(domain) +func (b *observedBackend) CheckDomain(ctx context.Context, domain string) (found bool, err error) { + return b.backend.CheckDomain(ctx, domain) } diff --git a/src/backend_s3.go b/src/backend_s3.go index be19a56..2e9afa6 100644 --- a/src/backend_s3.go +++ b/src/backend_s3.go @@ -103,13 +103,14 @@ type CachedManifest struct { func (c *CachedManifest) Weight() uint32 { return c.weight } type S3Backend struct { - ctx context.Context client *minio.Client bucket string blobCache *observedCache[string, *CachedBlob] siteCache *observedCache[string, *CachedManifest] } +var _ Backend = (*S3Backend)(nil) + func makeCacheOptions[K comparable, V any]( config *CacheConfig, weigher func(K, V) uint32, @@ -125,11 +126,7 @@ func makeCacheOptions[K comparable, V any]( return options } -func NewS3Backend( - config *S3Config, -) (*S3Backend, error) { - ctx := context.Background() - +func NewS3Backend(ctx context.Context, config *S3Config) (*S3Backend, error) { client, err := minio.New(config.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4( config.AccessKeyID, @@ -185,7 +182,7 @@ func NewS3Backend( return nil, err } - return &S3Backend{ctx, client, bucket, blobCache, siteCache}, nil + return &S3Backend{client, bucket, blobCache, siteCache}, nil } func (s3 *S3Backend) Backend() Backend { @@ -196,11 +193,19 @@ func blobObjectName(name string) string { return fmt.Sprintf("blob/%s", path.Join(splitBlobName(name)...)) } -func (s3 *S3Backend) GetBlob(name string) (io.ReadSeeker, uint64, time.Time, error) { +func (s3 *S3Backend) GetBlob( + ctx context.Context, + name string, +) ( + reader io.ReadSeeker, + size uint64, + mtime time.Time, + err error, +) { loader := func(ctx context.Context, name string) (*CachedBlob, error) { log.Printf("s3: get blob %s\n", name) - object, err := s3.client.GetObject(s3.ctx, s3.bucket, blobObjectName(name), + object, err := s3.client.GetObject(ctx, s3.bucket, blobObjectName(name), minio.GetObjectOptions{}) // Note that many errors (e.g. NoSuchKey) will be reported only after this point. if err != nil { @@ -221,25 +226,28 @@ func (s3 *S3Backend) GetBlob(name string) (io.ReadSeeker, uint64, time.Time, err return &CachedBlob{data, stat.LastModified}, nil } - cached, err := s3.blobCache.Get(s3.ctx, name, otter.LoaderFunc[string, *CachedBlob](loader)) + var cached *CachedBlob + cached, err = s3.blobCache.Get(ctx, name, otter.LoaderFunc[string, *CachedBlob](loader)) if err != nil { if errResp := minio.ToErrorResponse(err); errResp.Code == "NoSuchKey" { err = fmt.Errorf("%w: %s", errNotFound, errResp.Key) } - return nil, 0, time.Time{}, err } else { - return bytes.NewReader(cached.blob), uint64(len(cached.blob)), cached.mtime, err + reader = bytes.NewReader(cached.blob) + size = uint64(len(cached.blob)) + mtime = cached.mtime } + return } -func (s3 *S3Backend) PutBlob(name string, data []byte) error { +func (s3 *S3Backend) PutBlob(ctx context.Context, name string, data []byte) error { log.Printf("s3: put blob %s (%d bytes)\n", name, len(data)) - _, err := s3.client.StatObject(s3.ctx, s3.bucket, blobObjectName(name), + _, err := s3.client.StatObject(ctx, s3.bucket, blobObjectName(name), minio.GetObjectOptions{}) if err != nil { if errResp := minio.ToErrorResponse(err); errResp.Code == "NoSuchKey" { - _, err := s3.client.PutObject(s3.ctx, s3.bucket, blobObjectName(name), + _, err := s3.client.PutObject(ctx, s3.bucket, blobObjectName(name), bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{}) if err != nil { return err @@ -258,10 +266,10 @@ func (s3 *S3Backend) PutBlob(name string, data []byte) error { } } -func (s3 *S3Backend) DeleteBlob(name string) error { +func (s3 *S3Backend) DeleteBlob(ctx context.Context, name string) error { log.Printf("s3: delete blob %s\n", name) - return s3.client.RemoveObject(s3.ctx, s3.bucket, blobObjectName(name), + return s3.client.RemoveObject(ctx, s3.bucket, blobObjectName(name), minio.RemoveObjectOptions{}) } @@ -273,12 +281,12 @@ func stagedManifestObjectName(manifestData []byte) string { return fmt.Sprintf("dirty/%x", sha256.Sum256(manifestData)) } -func (s3 *S3Backend) GetManifest(name string) (*Manifest, error) { +func (s3 *S3Backend) GetManifest(ctx context.Context, name string) (*Manifest, error) { loader := func(ctx context.Context, name string) (*CachedManifest, error) { manifest, size, err := func() (*Manifest, uint32, error) { log.Printf("s3: get manifest %s\n", name) - object, err := s3.client.GetObject(s3.ctx, s3.bucket, manifestObjectName(name), + object, err := s3.client.GetObject(ctx, s3.bucket, manifestObjectName(name), minio.GetObjectOptions{}) // Note that many errors (e.g. NoSuchKey) will be reported only after this point. if err != nil { @@ -309,7 +317,7 @@ func (s3 *S3Backend) GetManifest(name string) (*Manifest, error) { } } - cached, err := s3.siteCache.Get(s3.ctx, name, otter.LoaderFunc[string, *CachedManifest](loader)) + cached, err := s3.siteCache.Get(ctx, name, otter.LoaderFunc[string, *CachedManifest](loader)) if err != nil { return nil, err } else { @@ -317,24 +325,24 @@ func (s3 *S3Backend) GetManifest(name string) (*Manifest, error) { } } -func (s3 *S3Backend) StageManifest(manifest *Manifest) error { +func (s3 *S3Backend) StageManifest(ctx context.Context, manifest *Manifest) error { data := EncodeManifest(manifest) log.Printf("s3: stage manifest %x\n", sha256.Sum256(data)) - _, err := s3.client.PutObject(s3.ctx, s3.bucket, stagedManifestObjectName(data), + _, err := s3.client.PutObject(ctx, s3.bucket, stagedManifestObjectName(data), bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{}) return err } -func (s3 *S3Backend) CommitManifest(name string, manifest *Manifest) error { +func (s3 *S3Backend) CommitManifest(ctx context.Context, name string, manifest *Manifest) error { data := EncodeManifest(manifest) log.Printf("s3: commit manifest %x -> %s", sha256.Sum256(data), name) // Remove staged object unconditionally (whether commit succeeded or failed), since // the upper layer has to retry the complete operation anyway. - _, putErr := s3.client.PutObject(s3.ctx, s3.bucket, manifestObjectName(name), + _, putErr := s3.client.PutObject(ctx, s3.bucket, manifestObjectName(name), bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{}) - removeErr := s3.client.RemoveObject(s3.ctx, s3.bucket, stagedManifestObjectName(data), + removeErr := s3.client.RemoveObject(ctx, s3.bucket, stagedManifestObjectName(data), minio.RemoveObjectOptions{}) s3.siteCache.Cache.Invalidate(name) if putErr != nil { @@ -346,19 +354,19 @@ func (s3 *S3Backend) CommitManifest(name string, manifest *Manifest) error { } } -func (s3 *S3Backend) DeleteManifest(name string) error { +func (s3 *S3Backend) DeleteManifest(ctx context.Context, name string) error { log.Printf("s3: delete manifest %s\n", name) - err := s3.client.RemoveObject(s3.ctx, s3.bucket, manifestObjectName(name), + err := s3.client.RemoveObject(ctx, s3.bucket, manifestObjectName(name), minio.RemoveObjectOptions{}) s3.siteCache.Cache.Invalidate(name) return err } -func (s3 *S3Backend) CheckDomain(domain string) (bool, error) { +func (s3 *S3Backend) CheckDomain(ctx context.Context, domain string) (bool, error) { log.Printf("s3: check domain %s\n", domain) - ctx, cancel := context.WithCancel(s3.ctx) + ctx, cancel := context.WithCancel(ctx) defer cancel() for object := range s3.client.ListObjectsIter(ctx, s3.bucket, minio.ListObjectsOptions{ diff --git a/src/caddy.go b/src/caddy.go index 7bb0d63..5025474 100644 --- a/src/caddy.go +++ b/src/caddy.go @@ -25,7 +25,7 @@ func ServeCaddy(w http.ResponseWriter, r *http.Request) { return } - found, err := backend.CheckDomain(strings.ToLower(query)) + found, err := backend.CheckDomain(r.Context(), strings.ToLower(query)) if found { log.Println("caddy:", query, 200) w.WriteHeader(http.StatusOK) diff --git a/src/main.go b/src/main.go index ec8092a..e01721b 100644 --- a/src/main.go +++ b/src/main.go @@ -141,7 +141,7 @@ func main() { webRoot += "/.index" } - manifest, err := backend.GetManifest(webRoot) + manifest, err := backend.GetManifest(context.Background(), webRoot) if err != nil { log.Fatalln(err) } @@ -152,7 +152,7 @@ func main() { log.Fatalln(err) } - reader, _, _, err := backend.GetBlob(*getBlob) + reader, _, _, err := backend.GetBlob(context.Background(), *getBlob) if err != nil { log.Fatalln(err) } @@ -195,7 +195,7 @@ func main() { log.Fatalf("cannot determine content type from filename %q\n", sourceURL) } - result = UpdateFromArchive(webRoot, contentType, file) + result = UpdateFromArchive(context.Background(), webRoot, contentType, file) } else { branch := "pages" if sourceURL.Fragment != "" { diff --git a/src/manifest.go b/src/manifest.go index eac5d19..502d6bd 100644 --- a/src/manifest.go +++ b/src/manifest.go @@ -4,6 +4,7 @@ package main import ( "bytes" + "context" "crypto/sha256" "errors" "fmt" @@ -166,7 +167,7 @@ var ErrManifestTooLarge = errors.New("manifest too large") // Uploads inline file data over certain size to the storage backend. Returns a copy of // the manifest updated to refer to an external content-addressable store. -func StoreManifest(name string, manifest *Manifest) (*Manifest, error) { +func StoreManifest(ctx context.Context, name string, manifest *Manifest) (*Manifest, error) { // Replace inline files over certain size with references to external data. extManifest := Manifest{ RepoUrl: manifest.RepoUrl, @@ -203,7 +204,7 @@ func StoreManifest(name string, manifest *Manifest) (*Manifest, error) { ) } - if err := backend.StageManifest(&extManifest); err != nil { + if err := backend.StageManifest(ctx, &extManifest); err != nil { return nil, fmt.Errorf("stage manifest: %w", err) } @@ -212,7 +213,7 @@ func StoreManifest(name string, manifest *Manifest) (*Manifest, error) { for name, entry := range extManifest.Contents { if entry.GetType() == Type_ExternalFile { wg.Go(func() { - err := backend.PutBlob(string(entry.Data), manifest.Contents[name].Data) + err := backend.PutBlob(ctx, string(entry.Data), manifest.Contents[name].Data) if err != nil { ch <- fmt.Errorf("put blob %s: %w", name, err) } @@ -225,7 +226,7 @@ func StoreManifest(name string, manifest *Manifest) (*Manifest, error) { return nil, err // currently ignores all but 1st error } - if err := backend.CommitManifest(name, &extManifest); err != nil { + if err := backend.CommitManifest(ctx, name, &extManifest); err != nil { return nil, fmt.Errorf("commit manifest: %w", err) } diff --git a/src/pages.go b/src/pages.go index 6476da2..764be88 100644 --- a/src/pages.go +++ b/src/pages.go @@ -77,13 +77,13 @@ func getPage(w http.ResponseWriter, r *http.Request) error { sitePath = strings.TrimPrefix(r.URL.Path, "/") if projectName, projectPath, found := strings.Cut(sitePath, "/"); found { - projectManifest, err := backend.GetManifest(makeWebRoot(host, projectName)) + projectManifest, err := backend.GetManifest(r.Context(), makeWebRoot(host, projectName)) if err == nil { sitePath, manifest = projectPath, projectManifest } } if manifest == nil { - manifest, err = backend.GetManifest(makeWebRoot(host, ".index")) + manifest, err = backend.GetManifest(r.Context(), makeWebRoot(host, ".index")) if manifest == nil { if found, fallbackErr := HandleWildcardFallback(w, r); found { return fallbackErr @@ -172,7 +172,7 @@ func getPage(w http.ResponseWriter, r *http.Request) error { w.WriteHeader(http.StatusNotModified) return nil } else { - reader, _, mtime, err = backend.GetBlob(string(entry.Data)) + reader, _, mtime, err = backend.GetBlob(r.Context(), string(entry.Data)) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "internal server error: %s\n", err) @@ -266,6 +266,9 @@ func putPage(w http.ResponseWriter, r *http.Request) error { return fmt.Errorf("content type: %w", err) } + updateCtx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout)) + defer cancel() + if contentType == "application/x-www-form-urlencoded" { auth, err := AuthorizeUpdateFromRepository(r) if err != nil { @@ -291,9 +294,7 @@ func putPage(w http.ResponseWriter, r *http.Request) error { return err } - ctx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout)) - defer cancel() - result = UpdateFromRepository(ctx, webRoot, repoURL, branch) + result = UpdateFromRepository(updateCtx, webRoot, repoURL, branch) } else { _, err := AuthorizeUpdateFromArchive(r) if err != nil { @@ -302,7 +303,7 @@ func putPage(w http.ResponseWriter, r *http.Request) error { // request body contains archive reader := http.MaxBytesReader(w, r.Body, int64(config.Limits.MaxSiteSize.Bytes())) - result = UpdateFromArchive(webRoot, contentType, reader) + result = UpdateFromArchive(updateCtx, webRoot, contentType, reader) } switch result.outcome { @@ -361,7 +362,7 @@ func deletePage(w http.ResponseWriter, r *http.Request) error { return err } - err = backend.DeleteManifest(makeWebRoot(host, projectName)) + err = backend.DeleteManifest(r.Context(), makeWebRoot(host, projectName)) if err != nil { w.WriteHeader(http.StatusInternalServerError) } else { @@ -447,9 +448,10 @@ func postPage(w http.ResponseWriter, r *http.Request) error { return err } - ctx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout)) + updateCtx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout)) defer cancel() - result := UpdateFromRepository(ctx, webRoot, repoURL, auth.branch) + + result := UpdateFromRepository(updateCtx, webRoot, repoURL, auth.branch) switch result.outcome { case UpdateError: w.WriteHeader(http.StatusServiceUnavailable) diff --git a/src/update.go b/src/update.go index 37e85de..30c528c 100644 --- a/src/update.go +++ b/src/update.go @@ -25,17 +25,14 @@ type UpdateResult struct { err error } -func Update( - webRoot string, - manifest *Manifest, -) UpdateResult { +func Update(ctx context.Context, webRoot string, manifest *Manifest) UpdateResult { var oldManifest, newManifest *Manifest var err error outcome := UpdateError - oldManifest, _ = backend.GetManifest(webRoot) + oldManifest, _ = backend.GetManifest(ctx, webRoot) if IsManifestEmpty(manifest) { - newManifest, err = manifest, backend.DeleteManifest(webRoot) + newManifest, err = manifest, backend.DeleteManifest(ctx, webRoot) if err == nil { if oldManifest == nil { outcome = UpdateNoChange @@ -44,7 +41,7 @@ func Update( } } } else if err = PrepareManifest(manifest); err == nil { - newManifest, err = StoreManifest(webRoot, manifest) + newManifest, err = StoreManifest(ctx, webRoot, manifest) if err == nil { if oldManifest == nil { outcome = UpdateCreated @@ -94,13 +91,14 @@ func UpdateFromRepository( } else if err != nil { return UpdateResult{UpdateError, nil, err} } else { - return Update(webRoot, manifest) + return Update(ctx, webRoot, manifest) } } var errArchiveFormat = errors.New("unsupported archive format") func UpdateFromArchive( + ctx context.Context, webRoot string, contentType string, reader io.Reader, @@ -129,6 +127,6 @@ func UpdateFromArchive( log.Printf("update %s err: %s", webRoot, err) return UpdateResult{UpdateError, nil, err} } else { - return Update(webRoot, manifest) + return Update(ctx, webRoot, manifest) } }