diff --git a/api/api_test.go b/api/api_test.go index f9f3b7b1e..afb5e65bc 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -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"}) +} diff --git a/api/gcs.go b/api/gcs.go new file mode 100644 index 000000000..4a1eec14c --- /dev/null +++ b/api/gcs.go @@ -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) +} diff --git a/api/router.go b/api/router.go index 3cd7d4271..ab2edac2b 100644 --- a/api/router.go +++ b/api/router.go @@ -168,6 +168,7 @@ func Router(c *ctx.AptlyContext) http.Handler { { api.GET("/s3", apiS3List) + api.GET("/gcs", apiGCSList) } { diff --git a/context/context.go b/context/context.go index 0ffc3f722..fe0a4767c 100644 --- a/context/context.go +++ b/context/context.go @@ -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.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 { diff --git a/debian/aptly.conf b/debian/aptly.conf index c233118fb..216bf3c48 100644 --- a/debian/aptly.conf +++ b/debian/aptly.conf @@ -253,6 +253,54 @@ 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: "" + # # 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. diff --git a/gcs/gcs.go b/gcs/gcs.go new file mode 100644 index 000000000..7a9c7cf7a --- /dev/null +++ b/gcs/gcs.go @@ -0,0 +1,2 @@ +// Package gcs handles publishing to Google Cloud Storage. +package gcs diff --git a/gcs/gcs_test.go b/gcs/gcs_test.go new file mode 100644 index 000000000..b31ecfbe1 --- /dev/null +++ b/gcs/gcs_test.go @@ -0,0 +1,12 @@ +package gcs + +import ( + "testing" + + . "gopkg.in/check.v1" +) + +// Launch gocheck tests. +func Test(t *testing.T) { + TestingT(t) +} diff --git a/gcs/public.go b/gcs/public.go new file mode 100644 index 000000000..e1142c185 --- /dev/null +++ b/gcs/public.go @@ -0,0 +1,410 @@ +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, defaultACL, storageClass, encryptionKey string, + disableMultiDel, debug bool) (*PublishedStorage, error) { + + ctx := context.TODO() + opts := make([]option.ClientOption, 0, 2) + + if credentialsFile != "" { + opts = append(opts, option.WithCredentialsFile(credentialsFile)) + } else if serviceAccountJSON != "" { + opts = append(opts, option.WithCredentialsJSON([]byte(serviceAccountJSON))) + } + + if project != "" { + opts = append(opts, option.WithQuotaProject(project)) + } + + 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.ObjectAttrs.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 +} diff --git a/gcs/public_test.go b/gcs/public_test.go new file mode 100644 index 000000000..4cf3d83fb --- /dev/null +++ b/gcs/public_test.go @@ -0,0 +1,75 @@ +package gcs + +import ( + "path/filepath" + + "github.com/aptly-dev/aptly/utils" + . "gopkg.in/check.v1" +) + +type PublishedStorageSuite struct{} + +var _ = Suite(&PublishedStorageSuite{}) + +func (s *PublishedStorageSuite) TestString(c *C) { + storage := &PublishedStorage{bucketName: "bucket-1", prefix: "prefix/a"} + c.Check(storage.String(), Equals, "GCS: bucket-1/prefix/a") +} + +func (s *PublishedStorageSuite) TestObjectPath(c *C) { + storage := &PublishedStorage{prefix: "root"} + c.Check(storage.objectPath("dists/stable/Release"), Equals, filepath.Join("root", "dists/stable/Release")) +} + +func (s *PublishedStorageSuite) TestApplyACLNoOpModes(c *C) { + for _, acl := range []string{"", "none", "private"} { + storage := &PublishedStorage{acl: acl} + err := storage.applyACL(nil) + c.Check(err, IsNil) + } +} + +func (s *PublishedStorageSuite) TestApplyACLUnsupported(c *C) { + storage := &PublishedStorage{acl: "bucket-owner-full-control"} + err := storage.applyACL(nil) + c.Assert(err, NotNil) + c.Check(err, ErrorMatches, "unsupported GCS ACL value: bucket-owner-full-control") +} + +func (s *PublishedStorageSuite) TestLinkFromPoolMissingMD5(c *C) { + publishedPrefix := "repo" + publishedRelPath := "pool/main/a/aptly" + fileName := "pkg.deb" + relPath := filepath.Join(filepath.Join(publishedPrefix, publishedRelPath), fileName) + + storage := &PublishedStorage{pathCache: map[string]string{relPath: "0123456789abcdef0123456789abcdef"}} + + err := storage.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) + + storage := &PublishedStorage{pathCache: map[string]string{relPath: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}} + + err := storage.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) + + storage := &PublishedStorage{pathCache: map[string]string{relPath: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}} + + err := storage.LinkFromPool(publishedPrefix, publishedRelPath, fileName, nil, "", utils.ChecksumInfo{MD5: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}, false) + c.Check(err, IsNil) +} diff --git a/go.mod b/go.mod index 867fcd1ee..eefda29e7 100644 --- a/go.mod +++ b/go.mod @@ -35,14 +35,24 @@ require ( golang.org/x/crypto v0.45.0 // indirect golang.org/x/sys v0.38.0 golang.org/x/term v0.37.0 - golang.org/x/time v0.5.0 - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/time v0.8.0 + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ) require ( - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cel.dev/expr v0.16.1 // indirect + cloud.google.com/go v0.115.1 // indirect + cloud.google.com/go/auth v0.13.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/iam v1.2.1 // indirect + cloud.google.com/go/monitoring v1.21.0 // indirect + cloud.google.com/go/storage v1.45.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // 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 @@ -61,14 +71,21 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect 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.1 // indirect + github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // 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 v0.13.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.1.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-logr/logr v1.4.2 // 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 @@ -77,8 +94,12 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.0 // 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 @@ -93,6 +114,7 @@ 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/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.59.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -101,6 +123,15 @@ require ( 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/contrib/detectors/gcp v1.29.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk v1.29.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.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 @@ -108,9 +139,12 @@ require ( golang.org/x/sync v0.18.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/tools v0.38.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect - google.golang.org/grpc v1.64.1 // indirect + google.golang.org/api v0.212.0 // indirect + google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index a59e0b721..fc1a35e2b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,22 @@ +cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= +cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= +cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU= +cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g= +cloud.google.com/go/monitoring v1.21.0 h1:EMc0tB+d3lUewT2NzKC/hr8cSR9WsUieVywzIHetGro= +cloud.google.com/go/monitoring v1.21.0/go.mod h1:tuJ+KNDdJbetSsbSGTqnaBvbauS5kr3Q/koy3Up6r+4= +cloud.google.com/go/storage v1.45.0 h1:5av0QcIVj77t+44mV4gffFC/LscFRUhto6UBMB5SimM= +cloud.google.com/go/storage v1.45.0/go.mod h1:wpPblkIuMP5jCB/E48Pz9zIo2S/zD8g+ITmxKkPCITE= 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 +31,15 @@ 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.24.1 h1:pB2F2JKCj1Znmp2rwxxt1J0Fg0wezTMgWYk5Mpbi1kg= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= 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.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= @@ -70,6 +94,9 @@ 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/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= 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= @@ -80,9 +107,13 @@ 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.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= 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= @@ -91,8 +122,18 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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/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.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= 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= @@ -105,6 +146,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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -131,29 +177,47 @@ 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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +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.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= +github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= 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= @@ -242,10 +306,13 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjL 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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= @@ -309,6 +376,24 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5 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/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= +go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= +go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= 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/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= @@ -326,18 +411,27 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +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= @@ -350,9 +444,11 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= golang.org/x/oauth2 v0.33.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= @@ -361,6 +457,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.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= @@ -407,7 +504,13 @@ golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 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/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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= @@ -421,22 +524,49 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.212.0 h1:BcRj3MJfHF3FYD29rk7u9kuu1SyfGqfHcA0hSwKqkHg= +google.golang.org/api v0.212.0/go.mod h1:gICpLlpp12/E8mycRMzgy3SQ9cFh2XnVJ6vJi/kQbvI= +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-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU= +google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4= google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 h1:IfdSdTcLFy4lqUQrQJLkLt1PB+AsqVz6lwkWPzWEz10= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +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.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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= @@ -456,4 +586,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= diff --git a/man/aptly.1 b/man/aptly.1 index b777d4526..638acf1de 100644 --- a/man/aptly.1 +++ b/man/aptly.1 @@ -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, diff --git a/man/aptly.1.ronn.tmpl b/man/aptly.1.ronn.tmpl index 708fdbb64..043643109 100644 --- a/man/aptly.1.ronn.tmpl +++ b/man/aptly.1.ronn.tmpl @@ -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, diff --git a/system/gcs_lib.py b/system/gcs_lib.py new file mode 100644 index 000000000..9cc4c5c05 --- /dev/null +++ b/system/gcs_lib.py @@ -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() diff --git a/system/requirements.txt b/system/requirements.txt index 67fd5dc1d..5105e61ae 100644 --- a/system/requirements.txt +++ b/system/requirements.txt @@ -1,4 +1,5 @@ azure-storage-blob +google-cloud-storage boto requests==2.32.4 requests-unixsocket diff --git a/system/t02_config/ConfigShowTest_gold b/system/t02_config/ConfigShowTest_gold index ba3b88d65..2899d9b56 100644 --- a/system/t02_config/ConfigShowTest_gold +++ b/system/t02_config/ConfigShowTest_gold @@ -34,6 +34,7 @@ "skipBz2Publishing": false, "FileSystemPublishEndpoints": {}, "S3PublishEndpoints": {}, + "GcsPublishEndpoints": {}, "SwiftPublishEndpoints": {}, "AzurePublishEndpoints": {}, "packagePoolStorage": {} diff --git a/system/t02_config/ConfigShowYAMLTest_gold b/system/t02_config/ConfigShowYAMLTest_gold index 02efe23d3..a920927fb 100644 --- a/system/t02_config/ConfigShowYAMLTest_gold +++ b/system/t02_config/ConfigShowYAMLTest_gold @@ -32,6 +32,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: {} diff --git a/system/t02_config/CreateConfigTest_gold b/system/t02_config/CreateConfigTest_gold index c233118fb..216bf3c48 100644 --- a/system/t02_config/CreateConfigTest_gold +++ b/system/t02_config/CreateConfigTest_gold @@ -253,6 +253,54 @@ 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: "" + # # 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. diff --git a/system/t06_publish/gcs.py b/system/t06_publish/gcs.py new file mode 100644 index 000000000..d60c55536 --- /dev/null +++ b/system/t06_publish/gcs.py @@ -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' + ) diff --git a/utils/config.go b/utils/config.go index cb72105f5..d288a8482 100644 --- a/utils/config.go +++ b/utils/config.go @@ -61,6 +61,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"` @@ -188,6 +189,20 @@ 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"` + 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"` @@ -237,6 +252,7 @@ var Config = ConfigStructure{ PpaCodename: "", FileSystemPublishRoots: map[string]FileSystemPublishRoot{}, S3PublishRoots: map[string]S3PublishRoot{}, + GCSPublishRoots: map[string]GCSPublishRoot{}, SwiftPublishRoots: map[string]SwiftPublishRoot{}, AzurePublishRoots: map[string]AzureEndpoint{}, AsyncAPI: false, diff --git a/utils/config_test.go b/utils/config_test.go index 294f41675..4f2b2eba9 100644 --- a/utils/config_test.go +++ b/utils/config_test.go @@ -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"}} @@ -131,6 +134,20 @@ 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" + + " \"acl\": \"\",\n" + + " \"storageClass\": \"\",\n" + + " \"encryptionKey\": \"\",\n" + + " \"disableMultiDel\": false,\n" + + " \"debug\": false\n" + + " }\n" + + " },\n" + " \"SwiftPublishEndpoints\": {\n" + " \"test\": {\n" + " \"container\": \"repo\",\n" + @@ -273,6 +290,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" + @@ -349,6 +367,18 @@ 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 + acl: public-read + storage_class: STANDARD + encryption_key: "12345678901234567890123456789012" + disable_multidel: true + debug: true swift_publish_endpoints: test: container: c1