drone/registry/app/driver/base/base.go

244 lines
8.0 KiB
Go

// Source: https://github.com/distribution/distribution
// Copyright 2014 https://github.com/distribution/distribution Authors
//
// 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
//
// http://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.
// Package base provides a base implementation of the storage driver that can
// be used to implement common checks. The goal is to increase the amount of
// code sharing.
//
// The canonical approach to use this class is to embed in the exported driver
// struct such that calls are proxied through this implementation. First,
// declare the internal driver, as follows:
//
// type driver struct { ... internal ...}
//
// The resulting type should implement StorageDriver such that it can be the
// target of a Base struct. The exported type can then be declared as follows:
//
// type Driver struct {
// Base
// }
//
// Because Driver embeds Base, it effectively implements Base. If the driver
// needs to intercept a call, before going to base, Driver should implement
// that method. Effectively, Driver can intercept calls before coming in and
// driver implements the actual logic.
//
// To further shield the embed from other packages, it is recommended to
// employ a private embed struct:
//
// type baseEmbed struct {
// base.Base
// }
//
// Then, declare driver to embed baseEmbed, rather than Base directly:
//
// type Driver struct {
// baseEmbed
// }
//
// The type now implements StorageDriver, proxying through Base, without
// exporting an unnecessary field.
package base
import (
"context"
"errors"
"io"
"github.com/harness/gitness/registry/app/dist_temp/dcontext"
"github.com/harness/gitness/registry/app/driver"
"github.com/rs/zerolog/log"
)
func init() {
}
// Base provides a wrapper around a storagedriver implementation that provides
// common path and bounds checking.
type Base struct {
driver.StorageDriver
}
// Format errors received from the storage driver.
func (base *Base) setDriverName(e error) error {
if e == nil {
return nil
}
switch {
case errors.As(e, &driver.UnsupportedMethodError{}):
var e1 driver.UnsupportedMethodError
errors.As(e, &e1)
e1.DriverName = base.StorageDriver.Name()
return e1
case errors.As(e, &driver.PathNotFoundError{}):
var e2 driver.PathNotFoundError
errors.As(e, &e2)
e2.DriverName = base.StorageDriver.Name()
return e2
case errors.As(e, &driver.InvalidPathError{}):
var e3 driver.InvalidPathError
errors.As(e, &e3)
e3.DriverName = base.StorageDriver.Name()
return e3
case errors.As(e, &driver.InvalidOffsetError{}):
var e4 driver.InvalidOffsetError
errors.As(e, &e4)
e4.DriverName = base.StorageDriver.Name()
return e4
default:
return driver.Error{
DriverName: base.StorageDriver.Name(),
Detail: e,
}
}
}
// GetContent wraps GetContent of underlying storage driver.
func (base *Base) GetContent(ctx context.Context, path string) ([]byte, error) {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.GetContent(%q)", base.Name(), path)
if !driver.PathRegexp.MatchString(path) {
return nil, driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
b, e := base.StorageDriver.GetContent(ctx, path)
return b, base.setDriverName(e)
}
// PutContent wraps PutContent of underlying storage driver.
func (base *Base) PutContent(ctx context.Context, path string, content []byte) error {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.PutContent(%q)", base.Name(), path)
if !driver.PathRegexp.MatchString(path) {
return driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
err := base.setDriverName(base.StorageDriver.PutContent(ctx, path, content))
return err
}
// Reader wraps Reader of underlying storage driver.
func (base *Base) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.Reader(%q, %d)", base.Name(), path, offset)
if offset < 0 {
return nil, driver.InvalidOffsetError{Path: path, Offset: offset, DriverName: base.StorageDriver.Name()}
}
if !driver.PathRegexp.MatchString(path) {
return nil, driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
rc, e := base.StorageDriver.Reader(ctx, path, offset)
return rc, base.setDriverName(e)
}
// Writer wraps Writer of underlying storage driver.
func (base *Base) Writer(ctx context.Context, path string, a bool) (driver.FileWriter, error) {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.Writer(%q, %v)", base.Name(), path, a)
if !driver.PathRegexp.MatchString(path) {
return nil, driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
writer, e := base.StorageDriver.Writer(ctx, path, a)
return writer, base.setDriverName(e)
}
// Stat wraps Stat of underlying storage driver.
func (base *Base) Stat(ctx context.Context, path string) (driver.FileInfo, error) {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.Stat(%q)", base.Name(), path)
if !driver.PathRegexp.MatchString(path) && path != "/" {
return nil, driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
fi, e := base.StorageDriver.Stat(ctx, path)
return fi, base.setDriverName(e)
}
// List wraps List of underlying storage driver.
func (base *Base) List(ctx context.Context, path string) ([]string, error) {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.List(%q)", base.Name(), path)
if !driver.PathRegexp.MatchString(path) && path != "/" {
return nil, driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
str, e := base.StorageDriver.List(ctx, path)
return str, base.setDriverName(e)
}
// Move wraps Move of underlying storage driver.
func (base *Base) Move(ctx context.Context, sourcePath string, destPath string) error {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.Move(%q, %q", base.Name(), sourcePath, destPath)
if !driver.PathRegexp.MatchString(sourcePath) {
return driver.InvalidPathError{Path: sourcePath, DriverName: base.StorageDriver.Name()}
} else if !driver.PathRegexp.MatchString(destPath) {
return driver.InvalidPathError{Path: destPath, DriverName: base.StorageDriver.Name()}
}
err := base.setDriverName(base.StorageDriver.Move(ctx, sourcePath, destPath))
return err
}
// Delete wraps Delete of underlying storage driver.
func (base *Base) Delete(ctx context.Context, path string) error {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.Delete(%q)", base.Name(), path)
if !driver.PathRegexp.MatchString(path) {
return driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
err := base.setDriverName(base.StorageDriver.Delete(ctx, path))
return err
}
// RedirectURL wraps RedirectURL of the underlying storage driver.
func (base *Base) RedirectURL(ctx context.Context, method string, path string) (string, error) {
log.Ctx(ctx).Info().Msgf("RedirectURL(%q, %q)", method, path)
if !driver.PathRegexp.MatchString(path) {
return "", driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
str, e := base.StorageDriver.RedirectURL(ctx, method, path)
log.Ctx(ctx).Info().Msgf("Redirect URL generated %s", str)
return str, base.setDriverName(e)
}
// Walk wraps Walk of underlying storage driver.
func (base *Base) Walk(ctx context.Context, path string, f driver.WalkFn, options ...func(*driver.WalkOptions)) error {
ctx, done := dcontext.WithTrace(ctx)
defer done("%s.Walk(%q)", base.Name(), path)
if !driver.PathRegexp.MatchString(path) && path != "/" {
return driver.InvalidPathError{Path: path, DriverName: base.StorageDriver.Name()}
}
return base.setDriverName(base.StorageDriver.Walk(ctx, path, f, options...))
}