Merge pull request #1550 from linuxdataflow/feat/pls/gcs-support

Add support for GCS.
This commit is contained in:
André Roth
2026-06-18 09:33:48 +02:00
committed by GitHub
22 changed files with 1786 additions and 54 deletions
+2
View File
@@ -1,4 +1,6 @@
version: "2"
run:
timeout: 5m
linters:
settings:
staticcheck:
+36
View File
@@ -8,6 +8,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"sort"
"strings"
"testing"
@@ -41,6 +42,17 @@ func createTestConfig() *os.File {
jsonString, err := json.Marshal(gin.H{
"architectures": []string{},
"enableMetricsEndpoint": true,
"S3PublishEndpoints": map[string]map[string]string{
"test-s3": {
"region": "us-east-1",
"bucket": "bucket-s3",
},
},
"GcsPublishEndpoints": map[string]map[string]string{
"test-gcs": {
"bucket": "bucket-gcs",
},
},
})
if err != nil {
return nil
@@ -173,3 +185,27 @@ func (s *APISuite) TestTruthy(c *C) {
c.Check(truthy(-1), Equals, true)
c.Check(truthy(gin.H{}), Equals, true)
}
func (s *APISuite) TestGetS3Endpoints(c *C) {
response, err := s.HTTPRequest("GET", "/api/s3", nil)
c.Assert(err, IsNil)
c.Check(response.Code, Equals, 200)
var endpoints []string
err = json.Unmarshal(response.Body.Bytes(), &endpoints)
c.Assert(err, IsNil)
sort.Strings(endpoints)
c.Check(endpoints, DeepEquals, []string{"test-s3"})
}
func (s *APISuite) TestGetGCSEndpoints(c *C) {
response, err := s.HTTPRequest("GET", "/api/gcs", nil)
c.Assert(err, IsNil)
c.Check(response.Code, Equals, 200)
var endpoints []string
err = json.Unmarshal(response.Body.Bytes(), &endpoints)
c.Assert(err, IsNil)
sort.Strings(endpoints)
c.Check(endpoints, DeepEquals, []string{"test-gcs"})
}
+21
View File
@@ -0,0 +1,21 @@
package api
import (
"github.com/gin-gonic/gin"
)
// @Summary GCS buckets
// @Description **Get list of GCS buckets**
// @Description
// @Description List configured GCS buckets.
// @Tags Status
// @Produce json
// @Success 200 {array} string "List of GCS buckets"
// @Router /api/gcs [get]
func apiGCSList(c *gin.Context) {
keys := []string{}
for k := range context.Config().GCSPublishRoots {
keys = append(keys, k)
}
c.JSON(200, keys)
}
+1
View File
@@ -177,6 +177,7 @@ func Router(c *ctx.AptlyContext) http.Handler {
{
api.GET("/s3", apiS3List)
api.GET("/gcs", apiGCSList)
}
{
+15
View File
@@ -23,6 +23,7 @@ import (
"github.com/aptly-dev/aptly/database/goleveldb"
"github.com/aptly-dev/aptly/deb"
"github.com/aptly-dev/aptly/files"
"github.com/aptly-dev/aptly/gcs"
"github.com/aptly-dev/aptly/http"
"github.com/aptly-dev/aptly/pgp"
"github.com/aptly-dev/aptly/s3"
@@ -437,6 +438,20 @@ func (context *AptlyContext) GetPublishedStorage(name string) aptly.PublishedSto
if err != nil {
Fatal(err)
}
} else if strings.HasPrefix(name, "gcs:") {
params, ok := context.config().GCSPublishRoots[name[4:]]
if !ok {
Fatal(fmt.Errorf("published GCS storage %v not configured", name[4:]))
}
var err error
publishedStorage, err = gcs.NewPublishedStorage(
params.Bucket, params.Prefix, params.CredentialsFile, params.ServiceAccountJSON,
params.Project, params.Endpoint, params.ACL, params.StorageClass, params.EncryptionKey,
params.DisableMultiDel, params.Debug)
if err != nil {
Fatal(err)
}
} else if strings.HasPrefix(name, "swift:") {
params, ok := context.config().SwiftPublishRoots[name[6:]]
if !ok {
+52
View File
@@ -256,6 +256,58 @@ s3_publish_endpoints:
# # Enables detailed request/response dump for each S3 operation
# debug: false
# GCS Endpoint Support
#
# aptly can be configured to publish repositories directly to Google Cloud
# Storage. First, publishing endpoints should be described in the aptly
# configuration file. Each endpoint has a name and associated settings.
#
# In order to publish to GCS, specify endpoint as `gcs:endpoint-name:` before
# publishing prefix on the command line, e.g.:
#
# `aptly publish snapshot wheezy-main gcs:test:`
#
gcs_publish_endpoints:
# # Endpoint Name
# test:
# # Bucket name
# bucket: test-bucket
# # Prefix (optional)
# # publishing under specified prefix in the bucket, defaults to
# # no prefix (bucket root)
# prefix: ""
# # Credentials File (optional)
# # Path to a service account credentials JSON file
# credentials_file: ""
# # Service Account JSON (optional)
# # Inline service account credentials JSON payload
# service_account_json: ""
# # Project (optional)
# # Quota project used for GCS requests
# project: ""
# # Endpoint (optional)
# # Override the GCS endpoint (e.g. for staging or a fake server);
# # leave empty to use the default GCS endpoint
# endpoint: ""
# # Default ACLs (optional)
# # assign ACL to published files:
# # * private (default)
# # * public-read (public repository)
# # * none (don't set ACL)
# acl: private
# # Storage Class (optional)
# # GCS storage class, e.g. `STANDARD`
# storage_class: STANDARD
# # Encryption Key (optional)
# # Customer-supplied encryption key (32-byte AES-256 key)
# encryption_key: ""
# # Disable MultiDel (optional)
# # Kept for parity with S3 settings; GCS deletes are one-by-one
# disable_multidel: false
# # Debug (optional)
# # Enables detailed logs for each GCS operation
# debug: false
# Swift Endpoint Support
#
# aptly can publish a repository directly to OpenStack Swift.
+2
View File
@@ -0,0 +1,2 @@
// Package gcs handles publishing to Google Cloud Storage.
package gcs
+12
View File
@@ -0,0 +1,12 @@
package gcs
import (
"testing"
. "gopkg.in/check.v1"
)
// Launch gocheck tests.
func Test(t *testing.T) {
TestingT(t)
}
+422
View File
@@ -0,0 +1,422 @@
package gcs
import (
"context"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"cloud.google.com/go/storage"
"github.com/aptly-dev/aptly/aptly"
"github.com/aptly-dev/aptly/utils"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"google.golang.org/api/googleapi"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
// PublishedStorage abstracts published files hosted on GCS.
type PublishedStorage struct {
client *storage.Client
bucket *storage.BucketHandle
bucketName string
prefix string
acl string
storageClass string
encryptionKey string
disableMultiDel bool
debug bool
pathCache map[string]string
}
var (
_ aptly.PublishedStorage = (*PublishedStorage)(nil)
)
// NewPublishedStorage creates a GCS-backed published storage.
func NewPublishedStorage(bucket, prefix, credentialsFile, serviceAccountJSON,
project, endpoint, defaultACL, storageClass, encryptionKey string,
disableMultiDel, debug bool) (*PublishedStorage, error) {
ctx := context.TODO()
opts := make([]option.ClientOption, 0, 4)
if endpoint != "" {
opts = append(opts, option.WithEndpoint(endpoint), option.WithoutAuthentication())
} else if credentialsFile != "" {
opts = append(opts, option.WithAuthCredentialsFile(option.ServiceAccount, credentialsFile))
} else if serviceAccountJSON != "" {
opts = append(opts, option.WithAuthCredentialsJSON(option.ServiceAccount, []byte(serviceAccountJSON)))
}
if project != "" {
opts = append(opts, option.WithQuotaProject(project))
}
// When pointing at a non-production endpoint (an explicit override or the
// STORAGE_EMULATOR_HOST hook the GCS Go client honours natively), force
// JSON reads. The default XML download API uses virtual-host-style URLs
// (https://<bucket>.storage.googleapis.com/...) that emulators and most
// dev/staging endpoints can't serve under a single listener; the JSON API
// uses path-based URLs that work the same against real GCS or an emulator.
if endpoint != "" || os.Getenv("STORAGE_EMULATOR_HOST") != "" {
opts = append(opts, storage.WithJSONReads())
}
client, err := storage.NewClient(ctx, opts...)
if err != nil {
return nil, err
}
result := &PublishedStorage{
client: client,
bucket: client.Bucket(bucket),
bucketName: bucket,
prefix: prefix,
acl: defaultACL,
storageClass: storageClass,
encryptionKey: encryptionKey,
disableMultiDel: disableMultiDel,
debug: debug,
}
return result, nil
}
func (g *PublishedStorage) String() string {
return fmt.Sprintf("GCS: %s/%s", g.bucketName, g.prefix)
}
// MkDir creates directory recursively under public path.
func (g *PublishedStorage) MkDir(_ string) error {
// no-op for GCS
return nil
}
func (g *PublishedStorage) objectPath(path string) string {
return filepath.Join(g.prefix, path)
}
func (g *PublishedStorage) objectHandle(path string) *storage.ObjectHandle {
obj := g.bucket.Object(g.objectPath(path))
if g.encryptionKey != "" {
obj = obj.Key([]byte(g.encryptionKey))
}
return obj
}
// PutFile puts file into published storage at specified path.
func (g *PublishedStorage) PutFile(path string, sourceFilename string) error {
source, err := os.Open(sourceFilename)
if err != nil {
return err
}
defer func() { _ = source.Close() }()
if g.debug {
log.Debug().Msgf("GCS: PutFile '%s'", path)
}
err = g.putFile(path, source, "")
if err != nil {
return errors.Wrap(err, fmt.Sprintf("error uploading %s to %s", sourceFilename, g))
}
return nil
}
func (g *PublishedStorage) applyACL(obj *storage.ObjectHandle) error {
switch g.acl {
case "", "none", "private":
return nil
case "public-read":
return obj.ACL().Set(context.TODO(), storage.AllUsers, storage.RoleReader)
default:
return fmt.Errorf("unsupported GCS ACL value: %s", g.acl)
}
}
func (g *PublishedStorage) putFile(path string, source io.Reader, sourceMD5 string) error {
obj := g.objectHandle(path)
writer := obj.NewWriter(context.TODO())
if g.storageClass != "" {
writer.StorageClass = g.storageClass
}
if sourceMD5 != "" {
writer.Metadata = map[string]string{"Md5": sourceMD5}
}
if _, err := io.Copy(writer, source); err != nil {
_ = writer.Close()
return err
}
if err := writer.Close(); err != nil {
return err
}
return g.applyACL(obj)
}
func (g *PublishedStorage) getMD5(path string) (string, error) {
attrs, err := g.objectHandle(path).Attrs(context.TODO())
if err != nil {
return "", err
}
if attrs.Metadata != nil {
if md5, ok := attrs.Metadata["Md5"]; ok && md5 != "" {
return strings.ToLower(md5), nil
}
}
return strings.ToLower(hex.EncodeToString(attrs.MD5)), nil
}
// Remove removes single file under public path.
func (g *PublishedStorage) Remove(path string) error {
if g.debug {
log.Debug().Msgf("GCS: Remove '%s'", path)
}
err := g.objectHandle(path).Delete(context.TODO())
if err != nil {
if err == storage.ErrObjectNotExist {
return nil
}
var apiErr *googleapi.Error
if errors.As(err, &apiErr) && apiErr.Code == 404 {
return nil
}
return errors.Wrap(err, fmt.Sprintf("error deleting %s from %s", path, g))
}
delete(g.pathCache, path)
return nil
}
// RemoveDirs removes directory structure under public path.
func (g *PublishedStorage) RemoveDirs(path string, _ aptly.Progress) error {
filelist, _, err := g.internalFilelist(path)
if err != nil {
return err
}
if g.debug {
log.Debug().Msgf("GCS: RemoveDirs '%s'", path)
}
for _, file := range filelist {
objPath := filepath.Join(path, file)
if err := g.Remove(objPath); err != nil {
return err
}
}
_ = g.disableMultiDel
return nil
}
// LinkFromPool links package file from pool to dist's pool location.
func (g *PublishedStorage) LinkFromPool(publishedPrefix, publishedRelPath, fileName string, sourcePool aptly.PackagePool,
sourcePath string, sourceChecksums utils.ChecksumInfo, force bool) error {
publishedDirectory := filepath.Join(publishedPrefix, publishedRelPath)
relPath := filepath.Join(publishedDirectory, fileName)
poolPath := filepath.Join(g.prefix, relPath)
if g.pathCache == nil {
paths, md5s, err := g.internalFilelist(filepath.Join(publishedPrefix, "pool"))
if err != nil {
return errors.Wrap(err, "error caching paths under prefix")
}
g.pathCache = make(map[string]string, len(paths))
for i := range paths {
g.pathCache[filepath.Join(publishedPrefix, "pool", paths[i])] = md5s[i]
}
}
destinationMD5, exists := g.pathCache[relPath]
sourceMD5 := strings.ToLower(sourceChecksums.MD5)
if exists {
if sourceMD5 == "" {
return fmt.Errorf("unable to compare object, MD5 checksum missing")
}
if len(destinationMD5) != 32 {
var err error
destinationMD5, err = g.getMD5(relPath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("error verifying MD5 for %s: %s", g, poolPath))
}
g.pathCache[relPath] = destinationMD5
}
if destinationMD5 == sourceMD5 {
return nil
}
if !force {
return fmt.Errorf("error putting file to %s: file already exists and is different: %s", poolPath, g)
}
}
source, err := sourcePool.Open(sourcePath)
if err != nil {
return err
}
defer func() { _ = source.Close() }()
if g.debug {
log.Debug().Msgf("GCS: LinkFromPool '%s'", relPath)
}
err = g.putFile(relPath, source, sourceMD5)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("error uploading %s to %s: %s", sourcePath, g, poolPath))
}
g.pathCache[relPath] = sourceMD5
return nil
}
// Filelist returns list of files under prefix.
func (g *PublishedStorage) Filelist(prefix string) ([]string, error) {
paths, _, err := g.internalFilelist(prefix)
return paths, err
}
func (g *PublishedStorage) internalFilelist(prefix string) ([]string, []string, error) {
paths := make([]string, 0, 1024)
md5s := make([]string, 0, 1024)
fullPrefix := filepath.Join(g.prefix, prefix)
if fullPrefix != "" {
fullPrefix += "/"
}
it := g.bucket.Objects(context.TODO(), &storage.Query{Prefix: fullPrefix})
for {
attrs, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, nil, errors.WithMessagef(err, "error listing under prefix %s in %s", fullPrefix, g)
}
path := attrs.Name
if fullPrefix != "" {
path = strings.TrimPrefix(path, fullPrefix)
}
paths = append(paths, path)
if attrs.Metadata != nil {
if md5, ok := attrs.Metadata["Md5"]; ok && md5 != "" {
md5s = append(md5s, strings.ToLower(md5))
continue
}
}
md5s = append(md5s, strings.ToLower(hex.EncodeToString(attrs.MD5)))
}
return paths, md5s, nil
}
// RenameFile renames (moves) file.
func (g *PublishedStorage) RenameFile(oldName, newName string) error {
src := g.objectHandle(oldName)
dst := g.objectHandle(newName)
if g.debug {
log.Debug().Msgf("GCS: RenameFile %s -> %s", oldName, newName)
}
_, err := dst.CopierFrom(src).Run(context.TODO())
if err != nil {
return fmt.Errorf("error copying %s -> %s in %s: %s", oldName, newName, g, err)
}
err = g.applyACL(dst)
if err != nil {
return err
}
return g.Remove(oldName)
}
// SymLink creates a copy of src file and stores link information in metadata.
func (g *PublishedStorage) SymLink(src string, dst string) error {
source := g.objectHandle(src)
dest := g.objectHandle(dst)
if g.debug {
log.Debug().Msgf("GCS: SymLink %s -> %s", src, dst)
}
_, err := dest.CopierFrom(source).Run(context.TODO())
if err != nil {
return fmt.Errorf("error symlinking %s -> %s in %s: %s", src, dst, g, err)
}
_, err = dest.Update(context.TODO(), storage.ObjectAttrsToUpdate{
Metadata: map[string]string{"SymLink": src},
})
if err != nil {
return fmt.Errorf("error updating symlink metadata %s -> %s in %s: %s", src, dst, g, err)
}
return g.applyACL(dest)
}
// HardLink uses symlink functionality as hard links do not exist on object stores.
func (g *PublishedStorage) HardLink(src string, dst string) error {
if g.debug {
log.Debug().Msgf("GCS: HardLink %s -> %s", src, dst)
}
return g.SymLink(src, dst)
}
// FileExists returns true if path exists.
func (g *PublishedStorage) FileExists(path string) (bool, error) {
_, err := g.objectHandle(path).Attrs(context.TODO())
if err != nil {
if err == storage.ErrObjectNotExist {
return false, nil
}
var apiErr *googleapi.Error
if errors.As(err, &apiErr) && apiErr.Code == 404 {
return false, nil
}
return false, err
}
return true, nil
}
// ReadLink returns symbolic link target from metadata.
func (g *PublishedStorage) ReadLink(path string) (string, error) {
attrs, err := g.objectHandle(path).Attrs(context.TODO())
if err != nil {
return "", err
}
return attrs.Metadata["SymLink"], nil
}
+530
View File
@@ -0,0 +1,530 @@
package gcs
import (
"context"
"errors"
"io"
"os"
"path/filepath"
"sort"
"cloud.google.com/go/storage"
"github.com/fsouza/fake-gcs-server/fakestorage"
. "gopkg.in/check.v1"
"github.com/aptly-dev/aptly/files"
"github.com/aptly-dev/aptly/utils"
)
type PublishedStorageSuite struct {
srv *fakestorage.Server
prevEmulatorHost string
prevEmulatorHostSet bool
storage, prefixedStorage *PublishedStorage
noSuchBucketStorage *PublishedStorage
}
var _ = Suite(&PublishedStorageSuite{})
func (s *PublishedStorageSuite) SetUpTest(c *C) {
var err error
s.srv, err = fakestorage.NewServerWithOptions(fakestorage.Options{
Scheme: "http",
Host: "127.0.0.1",
})
c.Assert(err, IsNil)
c.Assert(s.srv, NotNil)
s.srv.CreateBucketWithOpts(fakestorage.CreateBucketOpts{Name: "test"})
// The cloud.google.com/go/storage client honors STORAGE_EMULATOR_HOST and
// will route all requests (including media uploads) to the fake server.
s.prevEmulatorHost, s.prevEmulatorHostSet = os.LookupEnv("STORAGE_EMULATOR_HOST")
c.Assert(os.Setenv("STORAGE_EMULATOR_HOST", s.srv.URL()), IsNil)
s.storage, err = NewPublishedStorage("test", "", "", "", "", "", "", "", "", false, false)
c.Assert(err, IsNil)
s.prefixedStorage, err = NewPublishedStorage("test", "lala", "", "", "", "", "", "", "", false, false)
c.Assert(err, IsNil)
s.noSuchBucketStorage, err = NewPublishedStorage("no-bucket", "", "", "", "", "", "", "", "", false, false)
c.Assert(err, IsNil)
}
func (s *PublishedStorageSuite) TearDownTest(c *C) {
if s.prevEmulatorHostSet {
_ = os.Setenv("STORAGE_EMULATOR_HOST", s.prevEmulatorHost)
} else {
_ = os.Unsetenv("STORAGE_EMULATOR_HOST")
}
s.srv.Stop()
}
func (s *PublishedStorageSuite) GetFile(c *C, path string) []byte {
r, err := s.storage.bucket.Object(path).NewReader(context.TODO())
c.Assert(err, IsNil)
defer func() { _ = r.Close() }()
body, err := io.ReadAll(r)
c.Assert(err, IsNil)
return body
}
func (s *PublishedStorageSuite) AssertNoFile(c *C, path string) {
_, err := s.storage.bucket.Object(path).Attrs(context.TODO())
c.Assert(errors.Is(err, storage.ErrObjectNotExist), Equals, true)
}
func (s *PublishedStorageSuite) PutFile(c *C, path string, data []byte) {
w := s.storage.bucket.Object(path).NewWriter(context.TODO())
_, err := w.Write(data)
c.Assert(err, IsNil)
c.Assert(w.Close(), IsNil)
}
func (s *PublishedStorageSuite) TestString(c *C) {
c.Check(s.storage.String(), Equals, "GCS: test/")
c.Check(s.prefixedStorage.String(), Equals, "GCS: test/lala")
}
func (s *PublishedStorageSuite) TestMkDir(c *C) {
c.Check(s.storage.MkDir("anything"), IsNil)
}
func (s *PublishedStorageSuite) TestApplyACLNoOpModes(c *C) {
for _, acl := range []string{"", "none", "private"} {
st := &PublishedStorage{acl: acl}
c.Check(st.applyACL(nil), IsNil)
}
}
func (s *PublishedStorageSuite) TestApplyACLUnsupported(c *C) {
st := &PublishedStorage{acl: "bucket-owner-full-control"}
err := st.applyACL(nil)
c.Assert(err, NotNil)
c.Check(err, ErrorMatches, "unsupported GCS ACL value: bucket-owner-full-control")
}
func (s *PublishedStorageSuite) TestPutFile(c *C) {
dir := c.MkDir()
err := os.WriteFile(filepath.Join(dir, "a"), []byte("welcome to gcs!"), 0644)
c.Assert(err, IsNil)
err = s.storage.PutFile("a/b.txt", filepath.Join(dir, "a"))
c.Check(err, IsNil)
c.Check(s.GetFile(c, "a/b.txt"), DeepEquals, []byte("welcome to gcs!"))
err = s.prefixedStorage.PutFile("a/b.txt", filepath.Join(dir, "a"))
c.Check(err, IsNil)
c.Check(s.GetFile(c, "lala/a/b.txt"), DeepEquals, []byte("welcome to gcs!"))
}
func (s *PublishedStorageSuite) TestPutFileMissingSource(c *C) {
err := s.storage.PutFile("a/b.txt", filepath.Join(c.MkDir(), "does-not-exist"))
c.Check(err, ErrorMatches, ".*no such file or directory.*")
}
func (s *PublishedStorageSuite) TestFilelist(c *C) {
paths := []string{"a", "b", "c", "testa", "test/a", "test/b", "lala/a", "lala/b", "lala/c"}
for _, path := range paths {
s.PutFile(c, path, []byte("test"))
}
list, err := s.storage.Filelist("")
c.Check(err, IsNil)
sort.Strings(list)
c.Check(list, DeepEquals, []string{"a", "b", "c", "lala/a", "lala/b", "lala/c", "test/a", "test/b", "testa"})
list, err = s.storage.Filelist("test")
c.Check(err, IsNil)
sort.Strings(list)
c.Check(list, DeepEquals, []string{"a", "b"})
list, err = s.storage.Filelist("test2")
c.Check(err, IsNil)
c.Check(list, DeepEquals, []string{})
list, err = s.prefixedStorage.Filelist("")
c.Check(err, IsNil)
sort.Strings(list)
c.Check(list, DeepEquals, []string{"a", "b", "c"})
}
func (s *PublishedStorageSuite) TestRemove(c *C) {
s.PutFile(c, "a/b", []byte("test"))
err := s.storage.Remove("a/b")
c.Check(err, IsNil)
s.AssertNoFile(c, "a/b")
s.PutFile(c, "lala/xyz", []byte("test"))
err = s.prefixedStorage.Remove("xyz")
c.Check(err, IsNil)
s.AssertNoFile(c, "lala/xyz")
}
func (s *PublishedStorageSuite) TestRemoveMissing(c *C) {
c.Check(s.storage.Remove("does/not/exist"), IsNil)
}
func (s *PublishedStorageSuite) TestRemoveDirs(c *C) {
paths := []string{"a", "b", "c", "testa", "test/a", "test/b", "lala/a", "lala/b", "lala/c"}
for _, path := range paths {
s.PutFile(c, path, []byte("test"))
}
err := s.storage.RemoveDirs("test", nil)
c.Check(err, IsNil)
list, err := s.storage.Filelist("")
c.Check(err, IsNil)
sort.Strings(list)
c.Check(list, DeepEquals, []string{"a", "b", "c", "lala/a", "lala/b", "lala/c", "testa"})
}
func (s *PublishedStorageSuite) TestRenameFile(c *C) {
s.PutFile(c, "src", []byte("payload"))
err := s.storage.RenameFile("src", "dst")
c.Check(err, IsNil)
c.Check(s.GetFile(c, "dst"), DeepEquals, []byte("payload"))
s.AssertNoFile(c, "src")
}
func (s *PublishedStorageSuite) TestSymLink(c *C) {
s.PutFile(c, "a/b", []byte("test"))
err := s.storage.SymLink("a/b", "a/b.link")
c.Check(err, IsNil)
link, err := s.storage.ReadLink("a/b.link")
c.Check(err, IsNil)
c.Check(link, Equals, "a/b")
c.Check(s.GetFile(c, "a/b.link"), DeepEquals, []byte("test"))
}
func (s *PublishedStorageSuite) TestHardLink(c *C) {
s.PutFile(c, "a/b", []byte("test"))
err := s.storage.HardLink("a/b", "a/b.hard")
c.Check(err, IsNil)
link, err := s.storage.ReadLink("a/b.hard")
c.Check(err, IsNil)
c.Check(link, Equals, "a/b")
}
func (s *PublishedStorageSuite) TestFileExists(c *C) {
s.PutFile(c, "a/b", []byte("test"))
exists, err := s.storage.FileExists("a/b")
c.Check(err, IsNil)
c.Check(exists, Equals, true)
exists, err = s.storage.FileExists("a/b.invalid")
c.Check(err, IsNil)
c.Check(exists, Equals, false)
}
func (s *PublishedStorageSuite) TestObjectPath(c *C) {
st := &PublishedStorage{prefix: "root"}
c.Check(st.objectPath("dists/stable/Release"), Equals, filepath.Join("root", "dists/stable/Release"))
}
func (s *PublishedStorageSuite) TestLinkFromPool(c *C) {
root := c.MkDir()
pool := files.NewPackagePool(root, false)
cs := files.NewMockChecksumStorage()
tmpFile1 := filepath.Join(c.MkDir(), "mars-invaders_1.03.deb")
c.Assert(os.WriteFile(tmpFile1, []byte("Contents"), 0644), IsNil)
cksum1 := utils.ChecksumInfo{MD5: "c1df1da7a1ce305a3b60af9d5733ac1d"}
tmpFile2 := filepath.Join(c.MkDir(), "mars-invaders_1.03.deb")
c.Assert(os.WriteFile(tmpFile2, []byte("Spam"), 0644), IsNil)
cksum2 := utils.ChecksumInfo{MD5: "e9dfd31cc505d51fc26975250750deab"}
tmpFile3 := filepath.Join(c.MkDir(), "netboot/boot.img.gz")
_ = os.MkdirAll(filepath.Dir(tmpFile3), 0777)
c.Assert(os.WriteFile(tmpFile3, []byte("Contents"), 0644), IsNil)
cksum3 := utils.ChecksumInfo{MD5: "c1df1da7a1ce305a3b60af9d5733ac1d"}
src1, err := pool.Import(tmpFile1, "mars-invaders_1.03.deb", &cksum1, true, cs)
c.Assert(err, IsNil)
src2, err := pool.Import(tmpFile2, "mars-invaders_1.03.deb", &cksum2, true, cs)
c.Assert(err, IsNil)
src3, err := pool.Import(tmpFile3, "netboot/boot.img.gz", &cksum3, true, cs)
c.Assert(err, IsNil)
// first link from pool
err = s.storage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, src1, cksum1, false)
c.Check(err, IsNil)
c.Check(s.GetFile(c, "pool/main/m/mars-invaders/mars-invaders_1.03.deb"), DeepEquals, []byte("Contents"))
// duplicate link from pool (same MD5 → no-op)
err = s.storage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, src1, cksum1, false)
c.Check(err, IsNil)
c.Check(s.GetFile(c, "pool/main/m/mars-invaders/mars-invaders_1.03.deb"), DeepEquals, []byte("Contents"))
// link from pool with conflict, no force
err = s.storage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, src2, cksum2, false)
c.Check(err, ErrorMatches, ".*file already exists and is different.*")
c.Check(s.GetFile(c, "pool/main/m/mars-invaders/mars-invaders_1.03.deb"), DeepEquals, []byte("Contents"))
// link from pool with conflict and force
err = s.storage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, src2, cksum2, true)
c.Check(err, IsNil)
c.Check(s.GetFile(c, "pool/main/m/mars-invaders/mars-invaders_1.03.deb"), DeepEquals, []byte("Spam"))
// for prefixed storage:
err = s.prefixedStorage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, src1, cksum1, false)
c.Check(err, IsNil)
// 2nd link from pool, providing wrong path for source file:
// should hit the path cache and skip upload (which would otherwise fail).
err = s.prefixedStorage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, "wrong-looks-like-pathcache-doesnt-work", cksum1, false)
c.Check(err, IsNil)
c.Check(s.GetFile(c, "lala/pool/main/m/mars-invaders/mars-invaders_1.03.deb"), DeepEquals, []byte("Contents"))
// link from pool with nested file name
err = s.storage.LinkFromPool("", "dists/jessie/non-free/installer-i386/current/images", "netboot/boot.img.gz", pool, src3, cksum3, false)
c.Check(err, IsNil)
c.Check(s.GetFile(c, "dists/jessie/non-free/installer-i386/current/images/netboot/boot.img.gz"), DeepEquals, []byte("Contents"))
}
func (s *PublishedStorageSuite) TestLinkFromPoolMissingMD5(c *C) {
publishedPrefix := "repo"
publishedRelPath := "pool/main/a/aptly"
fileName := "pkg.deb"
relPath := filepath.Join(filepath.Join(publishedPrefix, publishedRelPath), fileName)
st := &PublishedStorage{pathCache: map[string]string{relPath: "0123456789abcdef0123456789abcdef"}}
err := st.LinkFromPool(publishedPrefix, publishedRelPath, fileName, nil, "", utils.ChecksumInfo{}, false)
c.Assert(err, NotNil)
c.Check(err, ErrorMatches, "unable to compare object, MD5 checksum missing")
}
func (s *PublishedStorageSuite) TestLinkFromPoolDifferentMD5NoForce(c *C) {
publishedPrefix := "repo"
publishedRelPath := "pool/main/a/aptly"
fileName := "pkg.deb"
relPath := filepath.Join(filepath.Join(publishedPrefix, publishedRelPath), fileName)
st := &PublishedStorage{pathCache: map[string]string{relPath: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}
err := st.LinkFromPool(publishedPrefix, publishedRelPath, fileName, nil, "", utils.ChecksumInfo{MD5: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}, false)
c.Assert(err, NotNil)
c.Check(err, ErrorMatches, ".*file already exists and is different.*")
}
func (s *PublishedStorageSuite) TestLinkFromPoolSameMD5NoUpload(c *C) {
publishedPrefix := "repo"
publishedRelPath := "pool/main/a/aptly"
fileName := "pkg.deb"
relPath := filepath.Join(filepath.Join(publishedPrefix, publishedRelPath), fileName)
st := &PublishedStorage{pathCache: map[string]string{relPath: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}
err := st.LinkFromPool(publishedPrefix, publishedRelPath, fileName, nil, "", utils.ChecksumInfo{MD5: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}, false)
c.Check(err, IsNil)
}
// putWithMetadata uploads an object with arbitrary metadata directly via the
// storage client, bypassing the production putFile path. Used to seed objects
// that exercise getMD5 / metadata-handling branches.
func (s *PublishedStorageSuite) putWithMetadata(c *C, path string, data []byte, metadata map[string]string) {
w := s.storage.bucket.Object(path).NewWriter(context.TODO())
w.Metadata = metadata
_, err := w.Write(data)
c.Assert(err, IsNil)
c.Assert(w.Close(), IsNil)
}
// TestLinkFromPoolShortCachedMD5 exercises the LinkFromPool branch where the
// path cache holds a non-32-char checksum (so getMD5 must be called to fetch
// the real MD5 from object attrs), and along the way covers getMD5 plus the
// Md5-metadata branch in internalFilelist.
func (s *PublishedStorageSuite) TestLinkFromPoolShortCachedMD5(c *C) {
root := c.MkDir()
pool := files.NewPackagePool(root, false)
cs := files.NewMockChecksumStorage()
tmpFile := filepath.Join(c.MkDir(), "mars-invaders_1.03.deb")
c.Assert(os.WriteFile(tmpFile, []byte("Contents"), 0644), IsNil)
cksum := utils.ChecksumInfo{MD5: "c1df1da7a1ce305a3b60af9d5733ac1d"}
src, err := pool.Import(tmpFile, "mars-invaders_1.03.deb", &cksum, true, cs)
c.Assert(err, IsNil)
// Seed the bucket with a short Md5 metadata value so internalFilelist
// returns it (covering the metadata branch) and LinkFromPool then has to
// re-fetch via getMD5 because len != 32.
relPath := filepath.Join("pool/main/m/mars-invaders", "mars-invaders_1.03.deb")
s.putWithMetadata(c, relPath, []byte("Contents"), map[string]string{"Md5": "short"})
// force=true so the conflict (the seeded "short" md5 will never match
// sourceMD5 once getMD5 normalises) resolves into a fresh upload rather
// than an error — what matters here is that we traverse the getMD5 +
// short-cache + internalFilelist md5-metadata branches.
err = s.storage.LinkFromPool("", "pool/main/m/mars-invaders", "mars-invaders_1.03.deb", pool, src, cksum, true)
c.Check(err, IsNil)
}
// TestLinkFromPoolMissingSource covers the source-pool open error path.
func (s *PublishedStorageSuite) TestLinkFromPoolMissingSource(c *C) {
pool := files.NewPackagePool(c.MkDir(), false)
err := s.storage.LinkFromPool("", "pool/x", "y.deb", pool, "non-existent-pool-key", utils.ChecksumInfo{MD5: "33333333333333333333333333333333"}, false)
c.Check(err, ErrorMatches, ".*no such file or directory.*")
}
// TestPutFilePublicReadACL covers the applyACL public-read branch and the
// ACL().Set call against the fake server.
func (s *PublishedStorageSuite) TestPutFilePublicReadACL(c *C) {
st, err := NewPublishedStorage("test", "", "", "", "", "", "public-read", "", "", false, false)
c.Assert(err, IsNil)
dir := c.MkDir()
src := filepath.Join(dir, "f")
c.Assert(os.WriteFile(src, []byte("hello"), 0644), IsNil)
c.Check(st.PutFile("a/b.txt", src), IsNil)
c.Check(s.GetFile(c, "a/b.txt"), DeepEquals, []byte("hello"))
}
// TestPutFileUnsupportedACL covers the default (error) branch of applyACL
// when invoked from the production putFile flow.
func (s *PublishedStorageSuite) TestPutFileUnsupportedACL(c *C) {
st, err := NewPublishedStorage("test", "", "", "", "", "", "bucket-owner-full-control", "", "", false, false)
c.Assert(err, IsNil)
dir := c.MkDir()
src := filepath.Join(dir, "f")
c.Assert(os.WriteFile(src, []byte("hello"), 0644), IsNil)
err = st.PutFile("a/b.txt", src)
c.Assert(err, NotNil)
c.Check(err, ErrorMatches, ".*unsupported GCS ACL value.*")
}
// TestPutFileWithStorageClass covers the storageClass branch in putFile.
func (s *PublishedStorageSuite) TestPutFileWithStorageClass(c *C) {
st, err := NewPublishedStorage("test", "", "", "", "", "", "", "NEARLINE", "", false, false)
c.Assert(err, IsNil)
dir := c.MkDir()
src := filepath.Join(dir, "f")
c.Assert(os.WriteFile(src, []byte("hi"), 0644), IsNil)
c.Check(st.PutFile("a/b.txt", src), IsNil)
attrs, err := s.storage.bucket.Object("a/b.txt").Attrs(context.TODO())
c.Assert(err, IsNil)
c.Check(attrs.StorageClass, Equals, "NEARLINE")
}
// TestRemoveDirsNoSuchBucket covers the internalFilelist error path inside
// RemoveDirs (and the iterator error branch in internalFilelist itself).
func (s *PublishedStorageSuite) TestRemoveDirsNoSuchBucket(c *C) {
err := s.noSuchBucketStorage.RemoveDirs("a/b", nil)
c.Check(err, ErrorMatches, ".*error listing under prefix.*")
}
// TestFilelistNoSuchBucket also covers the iterator error path.
func (s *PublishedStorageSuite) TestFilelistNoSuchBucket(c *C) {
_, err := s.noSuchBucketStorage.Filelist("")
c.Check(err, ErrorMatches, ".*error listing under prefix.*")
}
// TestRemoveCacheEviction verifies that a successful Remove evicts the entry
// from pathCache (covers the delete(g.pathCache, ...) line).
func (s *PublishedStorageSuite) TestRemoveCacheEviction(c *C) {
s.PutFile(c, "a/b", []byte("test"))
s.storage.pathCache = map[string]string{"a/b": "deadbeefdeadbeefdeadbeefdeadbeef"}
c.Check(s.storage.Remove("a/b"), IsNil)
_, present := s.storage.pathCache["a/b"]
c.Check(present, Equals, false)
}
// TestDebugMode exercises the if-debug log branches across the main verbs.
func (s *PublishedStorageSuite) TestDebugMode(c *C) {
st, err := NewPublishedStorage("test", "", "", "", "", "", "", "", "", false, true)
c.Assert(err, IsNil)
dir := c.MkDir()
src := filepath.Join(dir, "f")
c.Assert(os.WriteFile(src, []byte("dbg"), 0644), IsNil)
c.Check(st.PutFile("d/a", src), IsNil)
c.Check(st.RenameFile("d/a", "d/b"), IsNil)
c.Check(st.SymLink("d/b", "d/b.link"), IsNil)
c.Check(st.HardLink("d/b", "d/b.hard"), IsNil)
c.Check(st.Remove("d/b"), IsNil)
c.Check(st.RemoveDirs("d", nil), IsNil)
pool := files.NewPackagePool(c.MkDir(), false)
cs := files.NewMockChecksumStorage()
tmp := filepath.Join(c.MkDir(), "x.deb")
c.Assert(os.WriteFile(tmp, []byte("Contents"), 0644), IsNil)
cksum := utils.ChecksumInfo{MD5: "c1df1da7a1ce305a3b60af9d5733ac1d"}
srcKey, err := pool.Import(tmp, "x.deb", &cksum, true, cs)
c.Assert(err, IsNil)
c.Check(st.LinkFromPool("", "pool/x", "x.deb", pool, srcKey, cksum, false), IsNil)
}
// TestObjectHandleWithEncryptionKey covers the encryptionKey branch in
// objectHandle. fsouza doesn't enforce CSEK headers but we just need to walk
// the code path.
func (s *PublishedStorageSuite) TestObjectHandleWithEncryptionKey(c *C) {
st := &PublishedStorage{
bucket: s.storage.bucket,
bucketName: "test",
encryptionKey: "0123456789abcdef0123456789abcdef",
}
c.Check(st.objectHandle("a/b"), NotNil)
}
// TestReadLinkMissing covers the Attrs-error return in ReadLink.
func (s *PublishedStorageSuite) TestReadLinkMissing(c *C) {
_, err := s.storage.ReadLink("does/not/exist")
c.Check(err, ErrorMatches, ".*object doesn't exist.*")
}
// TestFileExistsNoSuchBucket exercises FileExists' wrapped-googleapi-404 path.
func (s *PublishedStorageSuite) TestFileExistsNoSuchBucket(c *C) {
exists, err := s.noSuchBucketStorage.FileExists("a/b")
c.Check(err, IsNil)
c.Check(exists, Equals, false)
}
// TestNewPublishedStorageWithEndpoint exercises the endpoint-injection branch
// in NewPublishedStorage (the production knob, separate from the env-var path).
func (s *PublishedStorageSuite) TestNewPublishedStorageWithEndpoint(c *C) {
saved := os.Getenv("STORAGE_EMULATOR_HOST")
c.Assert(os.Unsetenv("STORAGE_EMULATOR_HOST"), IsNil)
defer func() { _ = os.Setenv("STORAGE_EMULATOR_HOST", saved) }()
st, err := NewPublishedStorage("test", "", "", "", "", s.srv.URL()+"/storage/v1/", "", "", "", false, false)
c.Assert(err, IsNil)
_, err = st.Filelist("")
c.Check(err, IsNil)
}
// TestNewPublishedStorageWithProject covers the project!="" → WithQuotaProject
// branch. WithQuotaProject is incompatible with WithEndpoint, so this test
// relies on the STORAGE_EMULATOR_HOST env var (still set from SetUpTest) for
// fake-server routing.
func (s *PublishedStorageSuite) TestNewPublishedStorageWithProject(c *C) {
st, err := NewPublishedStorage("test", "", "", "", "fake-project", "", "", "", "", false, false)
c.Assert(err, IsNil)
_, err = st.Filelist("")
c.Check(err, IsNil)
}
+55 -15
View File
@@ -13,7 +13,7 @@ require (
github.com/h2non/filetype v1.1.3
github.com/jlaffaye/ftp v0.2.0 // indirect
github.com/kjk/lzma v0.0.0-20120628231508-2a7c55cad4a2
github.com/klauspost/compress v1.17.9
github.com/klauspost/compress v1.18.2
github.com/klauspost/pgzip v1.2.5
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
@@ -32,17 +32,27 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/ugorji/go/codec v1.2.11
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/sys v0.39.0
golang.org/x/term v0.38.0
golang.org/x/time v0.5.0
google.golang.org/protobuf v1.36.10 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/sys v0.40.0
golang.org/x/term v0.39.0
golang.org/x/time v0.14.0
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
)
require (
cel.dev/expr v0.25.1 // indirect
cloud.google.com/go v0.123.0 // indirect
cloud.google.com/go/auth v0.18.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
cloud.google.com/go/iam v1.5.3 // indirect
cloud.google.com/go/monitoring v1.24.3 // indirect
cloud.google.com/go/pubsub/v2 v2.4.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
@@ -64,11 +74,18 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
@@ -79,11 +96,17 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
@@ -93,29 +116,44 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/pkg/xattr v0.4.12 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.59.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
go.etcd.io/etcd/api/v3 v3.5.15 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/sdk v1.39.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/net v0.49.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
golang.org/x/text v0.33.0 // indirect
golang.org/x/tools v0.40.0 // indirect
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect
google.golang.org/grpc v1.79.3 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
require (
cloud.google.com/go/storage v1.60.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1
github.com/ProtonMail/go-crypto v1.4.0
@@ -124,11 +162,13 @@ require (
github.com/aws/aws-sdk-go-v2/credentials v1.17.46
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3
github.com/aws/smithy-go v1.24.2
github.com/fsouza/fake-gcs-server v1.53.1
github.com/google/uuid v1.6.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3
go.etcd.io/etcd/client/v3 v3.5.15
golang.org/x/oauth2 v0.34.0
golang.org/x/oauth2 v0.35.0
google.golang.org/api v0.266.0
gopkg.in/yaml.v3 v3.0.1
)
+197 -39
View File
@@ -1,5 +1,28 @@
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY=
cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw=
cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=
cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=
cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=
cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=
cloud.google.com/go/pubsub/v2 v2.4.0 h1:oMKNiBQpXImRWnHYla9uSU66ZzByZwBSCJOEs/pTKVg=
cloud.google.com/go/pubsub/v2 v2.4.0/go.mod h1:2lS/XQKq5qtOMs6kHBK+WX1ytUC36kLl2ig3zqsGUx8=
cloud.google.com/go/storage v1.60.0 h1:oBfZrSOCimggVNz9Y/bXY35uUcts7OViubeddTTVzQ8=
cloud.google.com/go/storage v1.60.0/go.mod h1:q+5196hXfejkctrnx+VYU8RKQr/L3c0cBIlrjmiAKE0=
cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=
cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
@@ -14,8 +37,17 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 h1:cf+OIKbkmMHBaC3u7
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1/go.mod h1:ap1dmS6vQKJxSMNiGJcq4QuUQkOynyD93gLw6MDF7ek=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DisposaBoy/JsonConfigReader v0.0.0-20171218180944-5ea4d0ddac55 h1:jbGlDKdzAZ92NzK65hUP98ri0/r50vVVvmZsFP/nIqo=
github.com/DisposaBoy/JsonConfigReader v0.0.0-20171218180944-5ea4d0ddac55/go.mod h1:GCzqZQHydohgVLSIqRKZeTt8IGb1Y4NaFfim3H40uUI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ=
@@ -69,6 +101,7 @@ github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.25 h1:tFpebHTkI7QZx1q1rWGOKhbunhZ3fMaxTvHDWn1bH/4=
@@ -79,22 +112,45 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsouza/fake-gcs-server v1.53.1 h1:/gjEYut23/MMhe4daYJ5yIBGPUmLAYupgITuoWG3+jI=
github.com/fsouza/fake-gcs-server v1.53.1/go.mod h1:kF+DadfinC7mlc1/2d/ZDHS9VyUk1hTcXJ6VwLSlzfM=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
@@ -103,6 +159,11 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -133,29 +194,55 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -175,11 +262,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kjk/lzma v0.0.0-20120628231508-2a7c55cad4a2 h1:TVZQgMi+I83S3rCuE65HnmDO6+wFPRi3n2LOzr+tr68=
github.com/kjk/lzma v0.0.0-20120628231508-2a7c55cad4a2/go.mod h1:phT/jsRPBAEqjAibu1BurrabCBNTYiVI+zbmyCZJY6Q=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -209,6 +298,12 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.98 h1:MeAVKjLVz+XJ28zFcuYyImNSAh8Mq725uNW4beRisi0=
github.com/minio/minio-go/v7 v7.0.98/go.mod h1:cY0Y+W7yozf0mdIclrttzo1Iiu7mEf9y7nk2uXqMOvM=
github.com/mkrautz/goar v0.0.0-20150919110319-282caa8bd9da h1:Iu5QFXIMK/YrHJ0NgUnK0rqYTTyb0ldt/rqNenAj39U=
github.com/mkrautz/goar v0.0.0-20150919110319-282caa8bd9da/go.mod h1:NfnmoBY0gGkr3/NmI+DP/UXbZvOCurCUYAzOdYJjlOc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -239,17 +334,25 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0=
github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
@@ -258,9 +361,11 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
github.com/saracen/walker v0.1.2 h1:/o1TxP82n8thLvmL4GpJXduYaRmJ7qXp8u9dSlV0zmo=
@@ -273,6 +378,8 @@ github.com/smira/go-ftp-protocol v0.0.0-20140829150050-066b75c2b70d h1:rvtR4+9N2
github.com/smira/go-ftp-protocol v0.0.0-20140829150050-066b75c2b70d/go.mod h1:Jm7yHrROA5tC42gyJ5EwiR8EWp0PUy0qOc4sE7Y8Uzo=
github.com/smira/go-xz v0.1.0 h1:1zVLT1sITUKcWNysfHMLZWJ2Yh7yJfhREsgmUdK4zb0=
github.com/smira/go-xz v0.1.0/go.mod h1:OmdEWnIIkuLzRLHGF4YtjDzF9VFUevEcP6YxDPRqVrs=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -286,8 +393,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
@@ -296,6 +403,8 @@ github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
@@ -305,16 +414,28 @@ github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdms
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.einride.tech/aip v0.79.0 h1:19zdPlZzlUvxOA8syAFw4LkdJdXepzyTl6gt9XEeqdU=
go.einride.tech/aip v0.79.0/go.mod h1:E8+wdTApA70odnpFzJgsGogHozC2JCIhFJBKPr8bVig=
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0 h1:5gn2urDL/FBnK8OkCfD1j3/ER79rUuTYmCvlXBKeYL8=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0/go.mod h1:0fBG6ZJxhqByfFZDwSwpZGzJU671HkwpWaNe2t4VUPI=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
@@ -323,12 +444,14 @@ go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2W
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -336,19 +459,28 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
@@ -356,11 +488,13 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -368,6 +502,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -385,37 +520,41 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -423,10 +562,24 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/api v0.266.0 h1:hco+oNCf9y7DmLeAtHJi/uBAY7n/7XC9mZPxu1ROiyk=
google.golang.org/api v0.266.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0=
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -434,11 +587,14 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -458,4 +614,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+60
View File
@@ -307,6 +307,66 @@ The legacy json configuration is still supported (and also supports comments):
// }
},
// GCS Endpoint Support
//
// aptly can be configured to publish repositories directly to Google Cloud
// Storage\. First, publishing endpoints should be described in the aptly
// configuration file\. Each endpoint has a name and associated settings\.
//
// In order to publish to GCS, specify endpoint as `gcs:endpoint\-name:` before
// publishing prefix on the command line, e\.g\.:
//
// `aptly publish snapshot wheezy\-main gcs:test:`
//
"GcsPublishEndpoints": {
// // Endpoint Name
// "test": {
// // Bucket name
// "bucket": "test\-bucket",
// // Prefix (optional)
// // publishing under specified prefix in the bucket, defaults to
// // no prefix (bucket root)
// "prefix": "",
// // Credentials file (optional)
// // Path to a service account credentials JSON file\. If omitted,
// // Application Default Credentials are used\.
// "credentialsFile": "",
// // Service Account JSON (optional)
// // Inline service account credentials JSON content\.
// "serviceAccountJSON": "",
// // Project (optional)
// // Quota project to bill requests to\.
// "project": "",
// // Default ACLs (optional)
// // assign ACL to published files\. Useful values: `private` (default),
// // `public\-read` (public repository), or `none` (don't set ACL)\.
// "acl": "private",
// // Storage Class (optional)
// // GCS storage class, e\.g\. `STANDARD`\.
// "storageClass": "STANDARD",
// // Encryption Key (optional)
// // Customer-supplied encryption key (32-byte AES-256 key) for object operations\.
// "encryptionKey": "",
// // Disable MultiDel (optional)
// // Kept for parity with S3 settings\.
// // GCS deletes are currently performed one object at a time\.
// "disableMultiDel": false,
// // Debug (optional)
// // Enables detailed GCS operation logs
// "debug": false
// }
},
// Swift Endpoint Support
//
// aptly could be configured to publish repository directly to OpenStack Swift\. First,
+60
View File
@@ -296,6 +296,66 @@ The legacy json configuration is still supported (and also supports comments):
// }
},
// GCS Endpoint Support
//
// aptly can be configured to publish repositories directly to Google Cloud
// Storage. First, publishing endpoints should be described in the aptly
// configuration file. Each endpoint has a name and associated settings.
//
// In order to publish to GCS, specify endpoint as `gcs:endpoint-name:` before
// publishing prefix on the command line, e.g.:
//
// `aptly publish snapshot wheezy-main gcs:test:`
//
"GcsPublishEndpoints": {
// // Endpoint Name
// "test": {
// // Bucket name
// "bucket": "test-bucket",
// // Prefix (optional)
// // publishing under specified prefix in the bucket, defaults to
// // no prefix (bucket root)
// "prefix": "",
// // Credentials file (optional)
// // Path to a service account credentials JSON file. If omitted,
// // Application Default Credentials are used.
// "credentialsFile": "",
// // Service Account JSON (optional)
// // Inline service account credentials JSON content.
// "serviceAccountJSON": "",
// // Project (optional)
// // Quota project to bill requests to.
// "project": "",
// // Default ACLs (optional)
// // assign ACL to published files. Useful values: `private` (default),
// // `public-read` (public repository), or `none` (don't set ACL).
// "acl": "private",
// // Storage Class (optional)
// // GCS storage class, e.g. `STANDARD`.
// "storageClass": "STANDARD",
// // Encryption Key (optional)
// // Customer-supplied encryption key (32-byte AES-256 key) for object operations.
// "encryptionKey": "",
// // Disable MultiDel (optional)
// // Kept for parity with S3 settings.
// // GCS deletes are currently performed one object at a time.
// "disableMultiDel": false,
// // Debug (optional)
// // Enables detailed GCS operation logs
// "debug": false
// }
},
// Swift Endpoint Support
//
// aptly could be configured to publish repository directly to OpenStack Swift. First,
+101
View File
@@ -0,0 +1,101 @@
from lib import BaseTest
import os
import uuid
try:
from google.cloud import storage
gcs_project = os.environ.get('GCS_PROJECT')
if gcs_project:
gcs_client = storage.Client(project=gcs_project)
else:
print('GCS tests disabled: GCS_PROJECT is not set')
gcs_client = None
except ImportError as e:
print("GCS tests disabled: can't import google.cloud.storage", e)
gcs_client = None
except Exception as e:
print('GCS tests disabled: unable to initialize GCS client', e)
gcs_client = None
class GCSTest(BaseTest):
"""
BaseTest + support for GCS
"""
gcsOverrides = {}
def __init__(self) -> None:
super(GCSTest, self).__init__()
self.bucket_name = None
self.bucket = None
self.bucket_contents = None
def fixture_available(self):
return super(GCSTest, self).fixture_available() and gcs_client is not None
def prepare(self):
# GCS bucket names must be globally unique and lower-case.
self.bucket_name = 'aptly-sys-test-' + str(uuid.uuid4()).replace('_', '-').lower()
self.bucket = gcs_client.create_bucket(self.bucket_name)
self.configOverride = {
'GcsPublishEndpoints': {
'test1': {
'bucket': self.bucket_name,
'project': gcs_project,
},
},
}
self.configOverride['GcsPublishEndpoints']['test1'].update(**self.gcsOverrides)
super(GCSTest, self).prepare()
def shutdown(self):
if self.bucket is not None:
for blob in self.bucket.list_blobs():
blob.delete()
self.bucket.delete(force=True)
super(GCSTest, self).shutdown()
def _normalize_path(self, path):
if path.startswith('public/'):
return path[7:]
return path
def check_path(self, path):
if self.bucket_contents is None:
self.bucket_contents = [blob.name for blob in self.bucket.list_blobs()]
path = self._normalize_path(path)
if path in self.bucket_contents:
return True
if not path.endswith('/'):
path = path + '/'
for item in self.bucket_contents:
if item.startswith(path):
return True
return False
def check_exists(self, path):
if not self.check_path(path):
raise Exception("path %s doesn't exist" % (path, ))
def check_not_exists(self, path):
if self.check_path(path):
raise Exception("path %s exists" % (path, ))
def read_file(self, path, mode=''):
assert not mode
path = self._normalize_path(path)
blob = self.bucket.blob(path)
return blob.download_as_text()
+1
View File
@@ -1,4 +1,5 @@
azure-storage-blob
google-cloud-storage
boto
requests==2.33.0
requests-unixsocket
+1
View File
@@ -35,6 +35,7 @@
"skipBz2Publishing": false,
"FileSystemPublishEndpoints": {},
"S3PublishEndpoints": {},
"GcsPublishEndpoints": {},
"SwiftPublishEndpoints": {},
"AzurePublishEndpoints": {},
"packagePoolStorage": {}
@@ -33,6 +33,7 @@ skip_contents_publishing: false
skip_bz2_publishing: false
filesystem_publish_endpoints: {}
s3_publish_endpoints: {}
gcs_publish_endpoints: {}
swift_publish_endpoints: {}
azure_publish_endpoints: {}
packagepool_storage: {}
+52
View File
@@ -256,6 +256,58 @@ s3_publish_endpoints:
# # Enables detailed request/response dump for each S3 operation
# debug: false
# GCS Endpoint Support
#
# aptly can be configured to publish repositories directly to Google Cloud
# Storage. First, publishing endpoints should be described in the aptly
# configuration file. Each endpoint has a name and associated settings.
#
# In order to publish to GCS, specify endpoint as `gcs:endpoint-name:` before
# publishing prefix on the command line, e.g.:
#
# `aptly publish snapshot wheezy-main gcs:test:`
#
gcs_publish_endpoints:
# # Endpoint Name
# test:
# # Bucket name
# bucket: test-bucket
# # Prefix (optional)
# # publishing under specified prefix in the bucket, defaults to
# # no prefix (bucket root)
# prefix: ""
# # Credentials File (optional)
# # Path to a service account credentials JSON file
# credentials_file: ""
# # Service Account JSON (optional)
# # Inline service account credentials JSON payload
# service_account_json: ""
# # Project (optional)
# # Quota project used for GCS requests
# project: ""
# # Endpoint (optional)
# # Override the GCS endpoint (e.g. for staging or a fake server);
# # leave empty to use the default GCS endpoint
# endpoint: ""
# # Default ACLs (optional)
# # assign ACL to published files:
# # * private (default)
# # * public-read (public repository)
# # * none (don't set ACL)
# acl: private
# # Storage Class (optional)
# # GCS storage class, e.g. `STANDARD`
# storage_class: STANDARD
# # Encryption Key (optional)
# # Customer-supplied encryption key (32-byte AES-256 key)
# encryption_key: ""
# # Disable MultiDel (optional)
# # Kept for parity with S3 settings; GCS deletes are one-by-one
# disable_multidel: false
# # Debug (optional)
# # Enables detailed logs for each GCS operation
# debug: false
# Swift Endpoint Support
#
# aptly can publish a repository directly to OpenStack Swift.
+116
View File
@@ -0,0 +1,116 @@
from gcs_lib import GCSTest
def strip_processor(output):
return '\n'.join(
[
l
for l in output.split('\n')
if not l.startswith(' ') and not l.startswith('Date:')
]
)
class GCSPublish1Test(GCSTest):
"""
publish to GCS: from repo
"""
fixtureCmds = [
'aptly repo create -distribution=maverick local-repo',
'aptly repo add local-repo ${files}',
'aptly repo remove local-repo libboost-program-options-dev_1.62.0.1_i386',
]
runCmd = 'aptly publish repo -keyring=${files}/aptly.pub -secret-keyring=${files}/aptly.sec local-repo gcs:test1:'
def check(self):
self.check_exists('public/dists/maverick/InRelease')
self.check_exists('public/dists/maverick/Release')
self.check_exists('public/dists/maverick/Release.gpg')
self.check_exists('public/dists/maverick/main/binary-i386/Packages')
self.check_exists('public/dists/maverick/main/binary-i386/Packages.gz')
self.check_exists('public/dists/maverick/main/binary-i386/Packages.bz2')
self.check_exists('public/dists/maverick/main/source/Sources')
self.check_exists('public/dists/maverick/main/source/Sources.gz')
self.check_exists('public/dists/maverick/main/source/Sources.bz2')
self.check_exists('public/pool/main/p/pyspi/pyspi_0.6.1-1.3.dsc')
self.check_exists('public/pool/main/p/pyspi/pyspi_0.6.1-1.3.diff.gz')
self.check_exists('public/pool/main/p/pyspi/pyspi_0.6.1.orig.tar.gz')
self.check_exists('public/pool/main/p/pyspi/pyspi-0.6.1-1.3.stripped.dsc')
self.check_exists(
'public/pool/main/b/boost-defaults/libboost-program-options-dev_1.49.0.1_i386.deb'
)
# verify contents except sums/date chunks
self.check_file_contents(
'public/dists/maverick/Release', 'release', match_prepare=strip_processor
)
self.check_file_contents(
'public/dists/maverick/main/source/Sources',
'sources',
match_prepare=lambda s: '\n'.join(sorted(s.split('\n'))),
)
self.check_file_contents(
'public/dists/maverick/main/binary-i386/Packages',
'binary',
match_prepare=lambda s: '\n'.join(sorted(s.split('\n'))),
)
class GCSPublish2Test(GCSTest):
"""
publish to GCS: update after removing package from repo
"""
fixtureCmds = [
'aptly repo create -distribution=maverick local-repo',
'aptly repo add local-repo ${files}/',
'aptly repo remove local-repo libboost-program-options-dev_1.62.0.1_i386',
'aptly publish repo -keyring=${files}/aptly.pub -secret-keyring=${files}/aptly.sec local-repo gcs:test1:',
'aptly repo remove local-repo pyspi',
]
runCmd = 'aptly publish update -keyring=${files}/aptly.pub -secret-keyring=${files}/aptly.sec maverick gcs:test1:'
def check(self):
self.check_exists('public/dists/maverick/InRelease')
self.check_exists('public/dists/maverick/Release')
self.check_exists('public/dists/maverick/Release.gpg')
self.check_not_exists('public/pool/main/p/pyspi/pyspi_0.6.1-1.3.dsc')
self.check_not_exists('public/pool/main/p/pyspi/pyspi_0.6.1-1.3.diff.gz')
self.check_not_exists('public/pool/main/p/pyspi/pyspi_0.6.1.orig.tar.gz')
self.check_not_exists('public/pool/main/p/pyspi/pyspi-0.6.1-1.3.stripped.dsc')
self.check_exists(
'public/pool/main/b/boost-defaults/libboost-program-options-dev_1.49.0.1_i386.deb'
)
class GCSPublish3Test(GCSTest):
"""
publish to GCS: publish drop performs cleanup
"""
fixtureCmds = [
'aptly repo create local1',
'aptly repo create local2',
'aptly repo add local1 ${files}/libboost-program-options-dev_1.49.0.1_i386.deb',
'aptly repo add local2 ${files}',
'aptly publish repo -keyring=${files}/aptly.pub -secret-keyring=${files}/aptly.sec -distribution=sq1 local1 gcs:test1:',
'aptly publish repo -keyring=${files}/aptly.pub -secret-keyring=${files}/aptly.sec -distribution=sq2 local2 gcs:test1:',
]
runCmd = 'aptly publish drop sq2 gcs:test1:'
def check(self):
self.check_exists('public/dists/sq1')
self.check_not_exists('public/dists/sq2')
self.check_exists('public/pool/main/')
self.check_not_exists('public/pool/main/p/pyspi/pyspi_0.6.1-1.3.dsc')
self.check_not_exists('public/pool/main/p/pyspi/pyspi_0.6.1-1.3.diff.gz')
self.check_not_exists('public/pool/main/p/pyspi/pyspi_0.6.1.orig.tar.gz')
self.check_not_exists('public/pool/main/p/pyspi/pyspi-0.6.1-1.3.stripped.dsc')
self.check_exists(
'public/pool/main/b/boost-defaults/libboost-program-options-dev_1.49.0.1_i386.deb'
)
+17
View File
@@ -62,6 +62,7 @@ type ConfigStructure struct { // nolint: maligned
// Storage
FileSystemPublishRoots map[string]FileSystemPublishRoot `json:"FileSystemPublishEndpoints" yaml:"filesystem_publish_endpoints"`
S3PublishRoots map[string]S3PublishRoot `json:"S3PublishEndpoints" yaml:"s3_publish_endpoints"`
GCSPublishRoots map[string]GCSPublishRoot `json:"GcsPublishEndpoints" yaml:"gcs_publish_endpoints"`
SwiftPublishRoots map[string]SwiftPublishRoot `json:"SwiftPublishEndpoints" yaml:"swift_publish_endpoints"`
AzurePublishRoots map[string]AzureEndpoint `json:"AzurePublishEndpoints" yaml:"azure_publish_endpoints"`
PackagePoolStorage PackagePoolStorage `json:"packagePoolStorage" yaml:"packagepool_storage"`
@@ -189,6 +190,21 @@ type S3PublishRoot struct {
Debug bool `json:"debug" yaml:"debug"`
}
// GCSPublishRoot describes single GCS publishing entry point
type GCSPublishRoot struct {
Bucket string `json:"bucket" yaml:"bucket"`
Prefix string `json:"prefix" yaml:"prefix"`
CredentialsFile string `json:"credentialsFile" yaml:"credentials_file"`
ServiceAccountJSON string `json:"serviceAccountJSON" yaml:"service_account_json"`
Project string `json:"project" yaml:"project"`
Endpoint string `json:"endpoint" yaml:"endpoint"`
ACL string `json:"acl" yaml:"acl"`
StorageClass string `json:"storageClass" yaml:"storage_class"`
EncryptionKey string `json:"encryptionKey" yaml:"encryption_key"`
DisableMultiDel bool `json:"disableMultiDel" yaml:"disable_multidel"`
Debug bool `json:"debug" yaml:"debug"`
}
// SwiftPublishRoot describes single OpenStack Swift publishing entry point
type SwiftPublishRoot struct {
Container string `json:"container" yaml:"container"`
@@ -239,6 +255,7 @@ var Config = ConfigStructure{
PpaBaseURL: "http://ppa.launchpad.net",
FileSystemPublishRoots: map[string]FileSystemPublishRoot{},
S3PublishRoots: map[string]S3PublishRoot{},
GCSPublishRoots: map[string]GCSPublishRoot{},
SwiftPublishRoots: map[string]SwiftPublishRoot{},
AzurePublishRoots: map[string]AzureEndpoint{},
AsyncAPI: false,
+32
View File
@@ -49,6 +49,9 @@ func (s *ConfigSuite) TestSaveConfig(c *C) {
Region: "us-east-1",
Bucket: "repo"}}
s.config.GCSPublishRoots = map[string]GCSPublishRoot{"test": {
Bucket: "repo"}}
s.config.SwiftPublishRoots = map[string]SwiftPublishRoot{"test": {
Container: "repo"}}
@@ -132,6 +135,21 @@ func (s *ConfigSuite) TestSaveConfig(c *C) {
" \"debug\": false\n" +
" }\n" +
" },\n" +
" \"GcsPublishEndpoints\": {\n" +
" \"test\": {\n" +
" \"bucket\": \"repo\",\n" +
" \"prefix\": \"\",\n" +
" \"credentialsFile\": \"\",\n" +
" \"serviceAccountJSON\": \"\",\n" +
" \"project\": \"\",\n" +
" \"endpoint\": \"\",\n" +
" \"acl\": \"\",\n" +
" \"storageClass\": \"\",\n" +
" \"encryptionKey\": \"\",\n" +
" \"disableMultiDel\": false,\n" +
" \"debug\": false\n" +
" }\n" +
" },\n" +
" \"SwiftPublishEndpoints\": {\n" +
" \"test\": {\n" +
" \"container\": \"repo\",\n" +
@@ -275,6 +293,7 @@ func (s *ConfigSuite) TestSaveYAML2Config(c *C) {
"skip_bz2_publishing: false\n" +
"filesystem_publish_endpoints: {}\n" +
"s3_publish_endpoints: {}\n" +
"gcs_publish_endpoints: {}\n" +
"swift_publish_endpoints: {}\n" +
"azure_publish_endpoints: {}\n" +
"packagepool_storage:\n" +
@@ -352,6 +371,19 @@ s3_publish_endpoints:
force_sigv2: true
force_virtualhosted_style: true
debug: true
gcs_publish_endpoints:
test:
bucket: gcs-bucket
prefix: pre-gcs
credentials_file: /tmp/creds.json
service_account_json: '{"type":"service_account"}'
project: test-project
endpoint: ""
acl: public-read
storage_class: STANDARD
encryption_key: "12345678901234567890123456789012"
disable_multidel: true
debug: true
swift_publish_endpoints:
test:
container: c1