mirror of
https://codeberg.org/git-pages/git-pages.git
synced 2026-05-14 03:01:48 +00:00
Thread context argument through the backend interface. NFC
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 != "" {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
22
src/pages.go
22
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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user