Thread context argument through the backend interface. NFC

This commit is contained in:
Catherine
2025-09-29 23:10:33 +00:00
parent f533d84de9
commit b1c50c10de
9 changed files with 121 additions and 89 deletions

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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{

View File

@@ -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)

View File

@@ -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 != "" {

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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)
}
}