tvl-depot/tools/nixery/storage/filesystem.go
Vincent Ambo cc35bf0fc3 feat(storage): Add support for content-types (GCS only)
Extends storage.Persist to accept a Content-Type argument, which in
the GCS backend is persisted with the object to ensure that the object
is served back with this content-type.

This is not yet implemented for the filesystem backend, where the
parameter is simply ignored.

This should help in the case of clients which expect the returned
objects to have content-types set when, for example, fetching layers
by digest.
2020-10-29 17:07:52 +01:00

97 lines
2.4 KiB
Go

// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
// Filesystem storage backend for Nixery.
package storage
import (
"context"
"fmt"
"io"
"net/http"
"os"
"path"
log "github.com/sirupsen/logrus"
)
type FSBackend struct {
path string
}
func NewFSBackend() (*FSBackend, error) {
p := os.Getenv("STORAGE_PATH")
if p == "" {
return nil, fmt.Errorf("STORAGE_PATH must be set for filesystem storage")
}
p = path.Clean(p)
err := os.MkdirAll(p, 0755)
if err != nil {
return nil, fmt.Errorf("failed to create storage dir: %s", err)
}
return &FSBackend{p}, nil
}
func (b *FSBackend) Name() string {
return fmt.Sprintf("Filesystem (%s)", b.path)
}
// TODO(tazjin): Implement support for persisting content-types for the filesystem backend.
func (b *FSBackend) Persist(ctx context.Context, key, _type string, f Persister) (string, int64, error) {
full := path.Join(b.path, key)
dir := path.Dir(full)
err := os.MkdirAll(dir, 0755)
if err != nil {
log.WithError(err).WithField("path", dir).Error("failed to create storage directory")
return "", 0, err
}
file, err := os.OpenFile(full, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.WithError(err).WithField("file", full).Error("failed to write file")
return "", 0, err
}
defer file.Close()
return f(file)
}
func (b *FSBackend) Fetch(ctx context.Context, key string) (io.ReadCloser, error) {
full := path.Join(b.path, key)
return os.Open(full)
}
func (b *FSBackend) Move(ctx context.Context, old, new string) error {
newpath := path.Join(b.path, new)
err := os.MkdirAll(path.Dir(newpath), 0755)
if err != nil {
return err
}
return os.Rename(path.Join(b.path, old), newpath)
}
func (b *FSBackend) Serve(digest string, r *http.Request, w http.ResponseWriter) error {
p := path.Join(b.path, "layers", digest)
log.WithFields(log.Fields{
"digest": digest,
"path": p,
}).Info("serving blob from filesystem")
http.ServeFile(w, r, p)
return nil
}