From 7f2ce8bbb085b8842f6c3b8bc411c61cf03a7b4a Mon Sep 17 00:00:00 2001 From: Moshe Immerman Date: Fri, 3 Apr 2026 16:45:59 +0300 Subject: [PATCH] feat(artifact): add support for artifact storage with inline and external backends Introduces a pluggable artifact storage system supporting multiple backends (S3, GCS, SMB, SFTP, local filesystem) and inline database storage. Includes compression support (gzip), filesystem abstraction interfaces, and database schema for storing artifacts either inline or in external cloud/object storage systems. External connections can be configured via connection URL properties. --- .github/workflows/test.yaml | 36 ++- Makefile | 6 +- artifact/blob_store.go | 110 +++++++ artifact/clients/aws/doc.go | 1 + artifact/clients/aws/fileinfo.go | 48 ++++ artifact/clients/azure/fileinfo.go | 21 ++ artifact/clients/gcp/fileinfo.go | 40 +++ artifact/clients/sftp/sftp.go | 35 +++ artifact/clients/smb/smb.go | 66 +++++ artifact/data.go | 69 +++++ artifact/fs.go | 20 ++ artifact/fs/azure.go | 105 +++++++ artifact/fs/gcs.go | 86 ++++++ artifact/fs/local.go | 135 +++++++++ artifact/fs/s3.go | 139 +++++++++ artifact/fs/save.go | 77 +++++ artifact/fs/smb.go | 102 +++++++ artifact/fs/ssh.go | 113 ++++++++ artifact/inline.go | 134 +++++++++ artifact/logged.go | 165 +++++++++++ connection/aws.go | 2 +- connection/blob.go | 165 +++++++++++ connection/gcp.go | 2 +- connection/gcs.go | 2 +- connection/gke.go | 2 +- context/blobs.go | 30 ++ go.mod | 48 +++- go.sum | 363 +++++------------------- logs/gcpcloudlogging/cloud_logging.go | 2 +- models/artifacts.go | 179 ++++++++++++ models/artifacts_test.go | 104 +++++++ schema/artifacts.hcl | 10 + tests/artifacts_test.go | 234 +++++++++++++++ tests/e2e-blobs/blobs_test.go | 151 ++++++++++ tests/e2e-blobs/containers.go | 203 +++++++++++++ tests/e2e-blobs/sftp-configuration.json | 1 + tests/e2e-blobs/suite_test.go | 87 ++++++ 37 files changed, 2775 insertions(+), 318 deletions(-) create mode 100644 artifact/blob_store.go create mode 100644 artifact/clients/aws/doc.go create mode 100644 artifact/clients/aws/fileinfo.go create mode 100644 artifact/clients/azure/fileinfo.go create mode 100644 artifact/clients/gcp/fileinfo.go create mode 100644 artifact/clients/sftp/sftp.go create mode 100644 artifact/clients/smb/smb.go create mode 100644 artifact/data.go create mode 100644 artifact/fs.go create mode 100644 artifact/fs/azure.go create mode 100644 artifact/fs/gcs.go create mode 100644 artifact/fs/local.go create mode 100644 artifact/fs/s3.go create mode 100644 artifact/fs/save.go create mode 100644 artifact/fs/smb.go create mode 100644 artifact/fs/ssh.go create mode 100644 artifact/inline.go create mode 100644 artifact/logged.go create mode 100644 connection/blob.go create mode 100644 context/blobs.go create mode 100644 models/artifacts_test.go create mode 100644 tests/artifacts_test.go create mode 100644 tests/e2e-blobs/blobs_test.go create mode 100644 tests/e2e-blobs/containers.go create mode 100644 tests/e2e-blobs/sftp-configuration.json create mode 100644 tests/e2e-blobs/suite_test.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1b06f78af..917ae7232 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -40,13 +40,13 @@ jobs: postgrest GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Install Go - uses: buildjet/setup-go@555ce355a95ff01018ffcf8fbbd9c44654db8374 # v5.0.2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: 1.25.x cache: false # Using custom cache action below for .bin directory - name: Checkout code uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - uses: buildjet/cache@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | ~/go/pkg/mod @@ -76,13 +76,13 @@ jobs: --health-retries 5 steps: - name: Install Go - uses: buildjet/setup-go@555ce355a95ff01018ffcf8fbbd9c44654db8374 # v5.0.2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: 1.25.x cache: false # Using custom cache action below for .bin directory - name: Checkout code uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - - uses: buildjet/cache@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | ~/go/pkg/mod @@ -99,6 +99,30 @@ jobs: DUTY_DB_DISABLE_RLS: "true" LOKI_URL: http://localhost:3100 + e2e-blobs: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: 1.25.x + cache: false + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + .bin + key: cache-${{ hashFiles('**/go.sum') }}-${{ hashFiles('.bin/*') }} + restore-keys: | + cache- + - name: E2E Blob Store Tests + run: make test-e2e-blobs + env: + DUTY_DB_DISABLE_RLS: "true" + migrate: runs-on: ubuntu-latest strategy: @@ -115,11 +139,11 @@ jobs: go build main.go && ./main --db-url 'postgres://postgres:postgres@localhost:5432/test?sslmode=disable' steps: - name: Install Go - uses: buildjet/setup-go@555ce355a95ff01018ffcf8fbbd9c44654db8374 # v5.0.2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: 1.25.x cache: false # Using custom cache action below for .bin directory - - uses: buildjet/cache@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | ~/go/pkg/mod diff --git a/Makefile b/Makefile index 98eff8460..11278699a 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ ginkgo: go install github.com/onsi/ginkgo/v2/ginkgo test: ginkgo - ginkgo -r --succinct --skip-package=tests/e2e,bench --label-filter "!e2e" + ginkgo -r --succinct --skip-package=tests/e2e,tests/e2e-blobs,bench --label-filter "!e2e" test-concurrent: ginkgo ginkgo -r -v --nodes=4 --skip-package=bench --label-filter "!e2e" @@ -30,6 +30,10 @@ e2e-services: ## Run e2e test services in foreground with automatic cleanup on e trap 'docker-compose down -v && docker-compose rm -f' EXIT INT TERM && \ docker-compose up --remove-orphans +.PHONY: test-e2e-blobs +test-e2e-blobs: ginkgo + ginkgo -v --label-filter="e2e" ./tests/e2e-blobs/ + .PHONY: bench bench: go test -run ^$$ -bench=. -benchtime=10s -timeout 30m github.com/flanksource/duty/bench diff --git a/artifact/blob_store.go b/artifact/blob_store.go new file mode 100644 index 000000000..7e2c42846 --- /dev/null +++ b/artifact/blob_store.go @@ -0,0 +1,110 @@ +package artifact + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + + "github.com/flanksource/duty/models" + "github.com/google/uuid" + "gorm.io/gorm" +) + +type BlobStore interface { + Write(data Data, artifact *models.Artifact) (*models.Artifact, error) + Read(artifactID uuid.UUID) (*Data, error) + io.Closer +} + +type blobStore struct { + fs FilesystemRW + db *gorm.DB + backend string +} + +func NewBlobStore(fs FilesystemRW, db *gorm.DB, backend string) BlobStore { + return &blobStore{fs: fs, db: db, backend: backend} +} + +func (s *blobStore) Write(data Data, a *models.Artifact) (*models.Artifact, error) { + if a == nil { + a = &models.Artifact{} + } + if data.Content == nil { + return nil, fmt.Errorf("artifact data content is nil") + } + defer func() { _ = data.Content.Close() }() + + checksum := sha256.New() + mimeReader := io.TeeReader(data.Content, checksum) + + mw := &mimeWriter{Max: maxBytesForMimeDetection} + fileReader := io.TeeReader(mimeReader, mw) + + info, err := s.fs.Write(s.db.Statement.Context, data.Filename, fileReader) + if err != nil { + return nil, fmt.Errorf("writing artifact %s: %w", data.Filename, err) + } + + if data.ContentType == "" { + data.ContentType = mw.Detect().String() + } + + // For inline store, the artifact already has content set + if inlineArt := InlineArtifact(info); inlineArt != nil { + a.Content = inlineArt.Content + a.CompressionType = inlineArt.CompressionType + } + + a.Path = data.Filename + a.Filename = info.Name() + a.Size = info.Size() + a.ContentType = data.ContentType + a.Checksum = hex.EncodeToString(checksum.Sum(nil)) + + if err := s.db.Create(a).Error; err != nil { + return nil, fmt.Errorf("saving artifact to db: %w", err) + } + + return a, nil +} + +func (s *blobStore) Read(artifactID uuid.UUID) (*Data, error) { + var a models.Artifact + if err := s.db.Where("id = ?", artifactID).First(&a).Error; err != nil { + return nil, fmt.Errorf("finding artifact %s: %w", artifactID, err) + } + + if a.IsInline() { + content, err := a.GetContent() + if err != nil { + return nil, fmt.Errorf("decompressing inline artifact %s: %w", artifactID, err) + } + return &Data{ + Content: io.NopCloser(bytes.NewReader(content)), + ContentLength: a.Size, + Checksum: a.Checksum, + ContentType: a.ContentType, + Filename: a.Filename, + }, nil + } + + r, err := s.fs.Read(s.db.Statement.Context, a.Path) + if err != nil { + return nil, fmt.Errorf("reading artifact %s from %s: %w", artifactID, a.Path, err) + } + + return &Data{ + Content: r, + ContentLength: a.Size, + Checksum: a.Checksum, + ContentType: a.ContentType, + Filename: a.Filename, + }, nil +} + +func (s *blobStore) Close() error { + return s.fs.Close() +} diff --git a/artifact/clients/aws/doc.go b/artifact/clients/aws/doc.go new file mode 100644 index 000000000..a1f9c0e3b --- /dev/null +++ b/artifact/clients/aws/doc.go @@ -0,0 +1 @@ +package aws diff --git a/artifact/clients/aws/fileinfo.go b/artifact/clients/aws/fileinfo.go new file mode 100644 index 000000000..efc0bdb76 --- /dev/null +++ b/artifact/clients/aws/fileinfo.go @@ -0,0 +1,48 @@ +//go:build !fast + +package aws + +import ( + "io/fs" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/flanksource/commons/utils" + "github.com/samber/lo" +) + +type S3FileInfo struct { + Object types.Object +} + +func (obj S3FileInfo) Name() string { + if obj.Object.Key == nil { + return "" + } + return *obj.Object.Key +} + +func (obj S3FileInfo) Size() int64 { + return utils.Deref(obj.Object.Size) +} + +func (obj S3FileInfo) Mode() fs.FileMode { + return fs.FileMode(0644) +} + +func (obj S3FileInfo) ModTime() time.Time { + return lo.FromPtr(obj.Object.LastModified) +} + +func (obj S3FileInfo) FullPath() string { + return *obj.Object.Key +} + +func (obj S3FileInfo) IsDir() bool { + return strings.HasSuffix(obj.Name(), "/") +} + +func (obj S3FileInfo) Sys() interface{} { + return obj.Object +} diff --git a/artifact/clients/azure/fileinfo.go b/artifact/clients/azure/fileinfo.go new file mode 100644 index 000000000..d656bcf8f --- /dev/null +++ b/artifact/clients/azure/fileinfo.go @@ -0,0 +1,21 @@ +package azure + +import ( + "io/fs" + "time" +) + +type BlobFileInfo struct { + BlobName string + BlobSize int64 + LastMod time.Time + ContentType string +} + +func (f BlobFileInfo) Name() string { return f.BlobName } +func (f BlobFileInfo) Size() int64 { return f.BlobSize } +func (f BlobFileInfo) Mode() fs.FileMode { return 0644 } +func (f BlobFileInfo) ModTime() time.Time { return f.LastMod } +func (f BlobFileInfo) IsDir() bool { return false } +func (f BlobFileInfo) Sys() any { return nil } +func (f BlobFileInfo) FullPath() string { return f.BlobName } diff --git a/artifact/clients/gcp/fileinfo.go b/artifact/clients/gcp/fileinfo.go new file mode 100644 index 000000000..edbea82ab --- /dev/null +++ b/artifact/clients/gcp/fileinfo.go @@ -0,0 +1,40 @@ +package gcp + +import ( + "io/fs" + "time" + + gcs "cloud.google.com/go/storage" +) + +type GCSFileInfo struct { + Object *gcs.ObjectAttrs +} + +func (GCSFileInfo) IsDir() bool { + return false +} + +func (obj GCSFileInfo) ModTime() time.Time { + return obj.Object.Updated +} + +func (obj GCSFileInfo) Mode() fs.FileMode { + return fs.FileMode(0644) +} + +func (obj GCSFileInfo) Name() string { + return obj.Object.Name +} + +func (obj GCSFileInfo) Size() int64 { + return obj.Object.Size +} + +func (obj GCSFileInfo) Sys() interface{} { + return obj.Object +} + +func (obj GCSFileInfo) FullPath() string { + return obj.Object.Name +} diff --git a/artifact/clients/sftp/sftp.go b/artifact/clients/sftp/sftp.go new file mode 100644 index 000000000..18a4ae496 --- /dev/null +++ b/artifact/clients/sftp/sftp.go @@ -0,0 +1,35 @@ +package sftp + +import ( + "time" + + "github.com/pkg/sftp" + "golang.org/x/crypto/ssh" +) + +// SSHConnect creates an SFTP client connection. +// NOTE: Uses InsecureIgnoreHostKey because artifact storage targets are +// configured by admins via trusted connection objects, not user input. +func SSHConnect(host, user, password string) (*sftp.Client, error) { + config := &ssh.ClientConfig{ + User: user, + Auth: []ssh.AuthMethod{ + ssh.Password(password), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec + Timeout: 30 * time.Second, + } + + conn, err := ssh.Dial("tcp", host, config) + if err != nil { + return nil, err + } + + client, err := sftp.NewClient(conn) + if err != nil { + conn.Close() + return nil, err + } + + return client, nil +} diff --git a/artifact/clients/smb/smb.go b/artifact/clients/smb/smb.go new file mode 100644 index 000000000..8bac0b61a --- /dev/null +++ b/artifact/clients/smb/smb.go @@ -0,0 +1,66 @@ +package smb + +import ( + "net" + + "github.com/flanksource/duty/types" + "github.com/hirochachacha/go-smb2" +) + +type SMBSession struct { + net.Conn + *smb2.Session + *smb2.Share +} + +func SMBConnect(server string, port, share string, auth types.Authentication) (*SMBSession, error) { + var err error + var smb *SMBSession + server = server + ":" + port + conn, err := net.Dial("tcp", server) + if err != nil { + return nil, err + } + smb = &SMBSession{ + Conn: conn, + } + + d := &smb2.Dialer{ + Initiator: &smb2.NTLMInitiator{ + User: auth.GetUsername(), + Password: auth.GetPassword(), + Domain: auth.GetDomain(), + }, + } + + s, err := d.Dial(conn) + if err != nil { + conn.Close() + return nil, err + } + smb.Session = s + fs, err := s.Mount(share) + if err != nil { + _ = s.Logoff() + conn.Close() + return nil, err + } + + smb.Share = fs + + return smb, err +} + +func (s *SMBSession) Close() error { + if s.Conn != nil { + _ = s.Conn.Close() + } + if s.Session != nil { + _ = s.Logoff() + } + if s.Share != nil { + _ = s.Umount() + } + + return nil +} diff --git a/artifact/data.go b/artifact/data.go new file mode 100644 index 000000000..df7ec1feb --- /dev/null +++ b/artifact/data.go @@ -0,0 +1,69 @@ +package artifact + +import ( + "fmt" + "io" + + "github.com/flanksource/clicky" + "github.com/flanksource/clicky/api" + "github.com/gabriel-vasile/mimetype" +) + +type Data struct { + Content io.ReadCloser + ContentLength int64 + Checksum string + ContentType string + Filename string +} + +func (d Data) Pretty() api.Text { + s := clicky.Text(d.Filename, "font-bold") + if d.ContentType != "" { + s = s.AddText(" "+d.ContentType, "text-gray-500") + } + if d.ContentLength > 0 { + s = s.AddText(fmt.Sprintf(" (%s)", formatBytes(d.ContentLength)), "text-gray-400") + } + if d.Checksum != "" { + short := d.Checksum + if len(short) > 8 { + short = short[:8] + } + s = s.AddText(" sha:"+short, "text-yellow-500") + } + return s +} + +func formatBytes(b int64) string { + switch { + case b >= 1<<20: + return fmt.Sprintf("%.1f MB", float64(b)/(1<<20)) + case b >= 1<<10: + return fmt.Sprintf("%.1f KB", float64(b)/(1<<10)) + default: + return fmt.Sprintf("%d B", b) + } +} + +const maxBytesForMimeDetection = 512 * 1024 + +type mimeWriter struct { + buffer []byte + Max int +} + +func (t *mimeWriter) Write(bb []byte) (n int, err error) { + if len(t.buffer) < t.Max { + rem := t.Max - len(t.buffer) + if rem > len(bb) { + rem = len(bb) + } + t.buffer = append(t.buffer, bb[:rem]...) + } + return len(bb), nil +} + +func (t *mimeWriter) Detect() *mimetype.MIME { + return mimetype.Detect(t.buffer) +} diff --git a/artifact/fs.go b/artifact/fs.go new file mode 100644 index 000000000..1263f6b55 --- /dev/null +++ b/artifact/fs.go @@ -0,0 +1,20 @@ +package artifact + +import ( + gocontext "context" + "io" + "os" +) + +type FileInfo interface { + os.FileInfo + FullPath() string +} + +type FilesystemRW interface { + io.Closer + Read(ctx gocontext.Context, path string) (io.ReadCloser, error) + Write(ctx gocontext.Context, path string, data io.Reader) (os.FileInfo, error) + ReadDir(name string) ([]FileInfo, error) + Stat(name string) (os.FileInfo, error) +} diff --git a/artifact/fs/azure.go b/artifact/fs/azure.go new file mode 100644 index 000000000..906248302 --- /dev/null +++ b/artifact/fs/azure.go @@ -0,0 +1,105 @@ +package fs + +import ( + "bytes" + gocontext "context" + "fmt" + "io" + "os" + "strings" + + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/flanksource/duty/artifact" + azureUtil "github.com/flanksource/duty/artifact/clients/azure" +) + +type azureBlobFS struct { + client *azblob.Client + container string +} + +func NewAzureBlobFS(client *azblob.Client, container string) *azureBlobFS { + return &azureBlobFS{client: client, container: container} +} + +func (t *azureBlobFS) Close() error { return nil } + +func (t *azureBlobFS) Read(ctx gocontext.Context, path string) (io.ReadCloser, error) { + resp, err := t.client.DownloadStream(ctx, t.container, path, nil) + if err != nil { + return nil, fmt.Errorf("downloading blob %s: %w", path, err) + } + return resp.Body, nil +} + +func (t *azureBlobFS) Write(ctx gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + content, err := io.ReadAll(data) + if err != nil { + return nil, fmt.Errorf("reading data for blob %s: %w", path, err) + } + + if _, err := t.client.UploadBuffer(ctx, t.container, path, content, nil); err != nil { + return nil, fmt.Errorf("uploading blob %s: %w", path, err) + } + + return t.Stat(path) +} + +func (t *azureBlobFS) ReadDir(name string) ([]artifact.FileInfo, error) { + prefix := name + if strings.Contains(prefix, "*") { + prefix = strings.SplitN(prefix, "*", 2)[0] + } + + pager := t.client.NewListBlobsFlatPager(t.container, &azblob.ListBlobsFlatOptions{ + Prefix: &prefix, + }) + + var output []artifact.FileInfo + for pager.More() { + resp, err := pager.NextPage(gocontext.TODO()) + if err != nil { + return nil, fmt.Errorf("listing blobs under %s: %w", name, err) + } + for _, blob := range resp.Segment.BlobItems { + output = append(output, azureUtil.BlobFileInfo{ + BlobName: *blob.Name, + BlobSize: *blob.Properties.ContentLength, + LastMod: *blob.Properties.LastModified, + }) + } + } + return output, nil +} + +func (t *azureBlobFS) Stat(name string) (os.FileInfo, error) { + resp, err := t.client.DownloadStream(gocontext.TODO(), t.container, name, nil) + if err != nil { + return nil, fmt.Errorf("stat blob %s: %w", name, err) + } + _ = resp.Body.Close() + + size := int64(0) + if resp.ContentLength != nil { + size = *resp.ContentLength + } + + return azureUtil.BlobFileInfo{ + BlobName: name, + BlobSize: size, + }, nil +} + +// CreateContainer creates the blob container if it doesn't exist. +func (t *azureBlobFS) CreateContainer(ctx gocontext.Context) error { + _, err := t.client.CreateContainer(ctx, t.container, nil) + if err != nil && !strings.Contains(err.Error(), "ContainerAlreadyExists") { + return err + } + return nil +} + +// SaveArtifactInline is a helper that creates inline artifacts for testing. +func SaveArtifactInline(ctx gocontext.Context, fs artifact.FilesystemRW, path string, data []byte) (os.FileInfo, error) { + return fs.Write(ctx, path, bytes.NewReader(data)) +} diff --git a/artifact/fs/gcs.go b/artifact/fs/gcs.go new file mode 100644 index 000000000..0e550a8a8 --- /dev/null +++ b/artifact/fs/gcs.go @@ -0,0 +1,86 @@ +package fs + +import ( + gocontext "context" + "errors" + "io" + "os" + "strings" + + gcs "cloud.google.com/go/storage" + "github.com/flanksource/duty/artifact" + gcpUtil "github.com/flanksource/duty/artifact/clients/gcp" + "google.golang.org/api/iterator" +) + +type gcsFS struct { + *gcs.Client + Bucket string +} + +func NewGCSFS(client *gcs.Client, bucket string) *gcsFS { + return &gcsFS{ + Bucket: strings.TrimPrefix(bucket, "gcs://"), + Client: client, + } +} + +func (t *gcsFS) Close() error { + return t.Client.Close() +} + +func (t *gcsFS) ReadDir(name string) ([]artifact.FileInfo, error) { + bucket := t.Client.Bucket(t.Bucket) + objs := bucket.Objects(gocontext.TODO(), &gcs.Query{Prefix: name}) + + var output []artifact.FileInfo + for { + obj, err := objs.Next() + if err != nil { + if errors.Is(err, iterator.Done) { + break + } + return nil, err + } + if obj == nil { + break + } + + output = append(output, gcpUtil.GCSFileInfo{Object: obj}) + } + + return output, nil +} + +func (t *gcsFS) Stat(path string) (os.FileInfo, error) { + obj := t.Client.Bucket(t.Bucket).Object(path) + attrs, err := obj.Attrs(gocontext.TODO()) + if err != nil { + return nil, err + } + + return &gcpUtil.GCSFileInfo{Object: attrs}, nil +} + +func (t *gcsFS) Read(ctx gocontext.Context, path string) (io.ReadCloser, error) { + return t.Client.Bucket(t.Bucket).Object(path).NewReader(ctx) +} + +func (t *gcsFS) Write(ctx gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + obj := t.Client.Bucket(t.Bucket).Object(path) + + content, err := io.ReadAll(data) + if err != nil { + return nil, err + } + + writer := obj.NewWriter(ctx) + if _, err := writer.Write(content); err != nil { + return nil, err + } + if err := writer.Close(); err != nil { + return nil, err + } + + return t.Stat(path) +} diff --git a/artifact/fs/local.go b/artifact/fs/local.go new file mode 100644 index 000000000..3d761ab12 --- /dev/null +++ b/artifact/fs/local.go @@ -0,0 +1,135 @@ +package fs + +import ( + gocontext "context" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/bmatcuk/doublestar/v4" + "github.com/flanksource/duty/artifact" +) + +func (t *localFS) safePath(path string) (string, error) { + full := filepath.Join(t.base, path) + abs, err := filepath.Abs(full) + if err != nil { + return "", fmt.Errorf("resolving path: %w", err) + } + baseAbs, err := filepath.Abs(t.base) + if err != nil { + return "", fmt.Errorf("resolving base: %w", err) + } + if !strings.HasPrefix(abs, baseAbs) { + return "", fmt.Errorf("path %q escapes base directory", path) + } + return full, nil +} + +type localFS struct { + base string +} + +type localFileInfo struct { + os.FileInfo + fullpath string +} + +func (t localFileInfo) FullPath() string { + return t.fullpath +} + +func NewLocalFS(base string) *localFS { + return &localFS{base: base} +} + +func (t *localFS) Close() error { + return nil +} + +func (t *localFS) ReadDir(name string) ([]artifact.FileInfo, error) { + if strings.Contains(name, "*") { + return t.ReadDirGlob(name) + } + + path := filepath.Join(t.base, name) + files, err := os.ReadDir(path) + if err != nil { + return nil, err + } + + output := make([]artifact.FileInfo, 0, len(files)) + for _, match := range files { + fullPath := filepath.Join(path, match.Name()) + info, err := os.Stat(fullPath) + if err != nil { + return nil, err + } + + output = append(output, localFileInfo{FileInfo: info, fullpath: fullPath}) + } + + return output, nil +} + +func (t *localFS) ReadDirGlob(name string) ([]artifact.FileInfo, error) { + base, pattern := doublestar.SplitPattern(filepath.Join(t.base, name)) + matches, err := doublestar.Glob(os.DirFS(base), pattern) + if err != nil { + return nil, err + } + + output := make([]artifact.FileInfo, 0, len(matches)) + for _, match := range matches { + fullPath := filepath.Join(base, match) + info, err := os.Stat(fullPath) + if err != nil { + return nil, err + } + + output = append(output, localFileInfo{FileInfo: info, fullpath: fullPath}) + } + + return output, nil +} + +func (t *localFS) Stat(name string) (os.FileInfo, error) { + p, err := t.safePath(name) + if err != nil { + return nil, err + } + return os.Stat(p) +} + +func (t *localFS) Read(_ gocontext.Context, path string) (io.ReadCloser, error) { + p, err := t.safePath(path) + if err != nil { + return nil, err + } + return os.Open(p) +} + +func (t *localFS) Write(_ gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + fullpath, err := t.safePath(path) + if err != nil { + return nil, err + } + + if err := os.MkdirAll(filepath.Dir(fullpath), os.ModePerm); err != nil { + return nil, fmt.Errorf("error creating base directory: %w", err) + } + + f, err := os.Create(fullpath) + if err != nil { + return nil, err + } + defer f.Close() + + if _, err = io.Copy(f, data); err != nil { + return nil, err + } + + return t.Stat(path) +} diff --git a/artifact/fs/s3.go b/artifact/fs/s3.go new file mode 100644 index 000000000..63bc7e138 --- /dev/null +++ b/artifact/fs/s3.go @@ -0,0 +1,139 @@ +package fs + +import ( + gocontext "context" + "io" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/s3" + s3Types "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/bmatcuk/doublestar/v4" + "github.com/flanksource/commons/utils" + "github.com/flanksource/duty/artifact" + awsUtil "github.com/flanksource/duty/artifact/clients/aws" + "github.com/samber/lo" +) + +const s3ListObjectMaxKeys = 1000 + +type s3FS struct { + maxObjects int + Client *s3.Client + Bucket string +} + +func NewS3FS(client *s3.Client, bucket string) *s3FS { + return &s3FS{ + maxObjects: 50 * 10_000, + Client: client, + Bucket: strings.TrimPrefix(bucket, "s3://"), + } +} + +func (t *s3FS) SetMaxListItems(max int) { + t.maxObjects = max +} + +func (t *s3FS) Close() error { + return nil +} + +func (t *s3FS) ReadDir(pattern string) ([]artifact.FileInfo, error) { + prefix, glob := doublestar.SplitPattern(pattern) + if prefix == "." { + prefix = "" + } + + req := &s3.ListObjectsV2Input{ + Bucket: aws.String(t.Bucket), + Prefix: aws.String(prefix), + } + + if t.maxObjects < s3ListObjectMaxKeys { + req.MaxKeys = lo.ToPtr(int32(t.maxObjects)) + } + + hasGlob := glob != "" + var output []artifact.FileInfo + var numObjectsFetched int + for { + resp, err := t.Client.ListObjectsV2(gocontext.TODO(), req) + if err != nil { + return nil, err + } + + for _, obj := range resp.Contents { + if hasGlob { + if matched, err := doublestar.Match(pattern, *obj.Key); err != nil { + return nil, err + } else if !matched { + continue + } + } + + fileInfo := &awsUtil.S3FileInfo{Object: obj} + output = append(output, fileInfo) + } + + if resp.NextContinuationToken == nil { + break + } + + numObjectsFetched += int(*resp.KeyCount) + if numObjectsFetched >= t.maxObjects { + break + } + + req.ContinuationToken = resp.NextContinuationToken + } + + return output, nil +} + +func (t *s3FS) Stat(path string) (fs.FileInfo, error) { + headObject, err := t.Client.HeadObject(gocontext.TODO(), &s3.HeadObjectInput{ + Bucket: aws.String(t.Bucket), + Key: aws.String(path), + }) + if err != nil { + return nil, err + } + + return &awsUtil.S3FileInfo{ + Object: s3Types.Object{ + Key: utils.Ptr(filepath.Base(path)), + Size: headObject.ContentLength, + LastModified: headObject.LastModified, + ETag: headObject.ETag, + }, + }, nil +} + +func (t *s3FS) Read(ctx gocontext.Context, key string) (io.ReadCloser, error) { + results, err := t.Client.GetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(t.Bucket), + Key: aws.String(key), + }) + if err != nil { + return nil, err + } + + return results.Body, nil +} + +func (t *s3FS) Write(ctx gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + _, err := t.Client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(t.Bucket), + Key: aws.String(path), + Body: data, + }) + if err != nil { + return nil, err + } + + return t.Stat(path) +} diff --git a/artifact/fs/save.go b/artifact/fs/save.go new file mode 100644 index 000000000..701de6258 --- /dev/null +++ b/artifact/fs/save.go @@ -0,0 +1,77 @@ +package fs + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + + "github.com/flanksource/duty/artifact" + "github.com/flanksource/duty/context" + "github.com/flanksource/duty/models" + "github.com/gabriel-vasile/mimetype" +) + +type MIMEWriter struct { + buffer []byte + Max int +} + +func (t *MIMEWriter) Write(bb []byte) (n int, err error) { + if len(t.buffer) < t.Max { + rem := t.Max - len(t.buffer) + if rem > len(bb) { + rem = len(bb) + } + t.buffer = append(t.buffer, bb[:rem]...) + } + return len(bb), nil +} + +func (t *MIMEWriter) Detect() *mimetype.MIME { + return mimetype.Detect(t.buffer) +} + +type Artifact struct { + ContentType string + Path string + Content io.ReadCloser +} + +const maxBytesForMimeDetection = 512 * 1024 + +func SaveArtifact(ctx context.Context, fs artifact.FilesystemRW, a *models.Artifact, data Artifact) error { + if a == nil { + return fmt.Errorf("artifact model is nil") + } + if data.Content == nil { + return fmt.Errorf("artifact data content is nil") + } + defer func() { _ = data.Content.Close() }() + + checksum := sha256.New() + mimeReader := io.TeeReader(data.Content, checksum) + + mimeWriter := &MIMEWriter{Max: maxBytesForMimeDetection} + fileReader := io.TeeReader(mimeReader, mimeWriter) + + info, err := fs.Write(ctx, data.Path, fileReader) + if err != nil { + return fmt.Errorf("error writing artifact(%s): %w", data.Path, err) + } + + if data.ContentType == "" { + data.ContentType = mimeWriter.Detect().String() + } + + a.Path = data.Path + a.Filename = info.Name() + a.Size = info.Size() + a.ContentType = data.ContentType + a.Checksum = hex.EncodeToString(checksum.Sum(nil)) + if err := ctx.DB().Create(a).Error; err != nil { + return fmt.Errorf("error saving artifact to db: %w", err) + } + + return nil +} diff --git a/artifact/fs/smb.go b/artifact/fs/smb.go new file mode 100644 index 000000000..4fa16ba42 --- /dev/null +++ b/artifact/fs/smb.go @@ -0,0 +1,102 @@ +package fs + +import ( + gocontext "context" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + + "github.com/bmatcuk/doublestar/v4" + "github.com/flanksource/duty/artifact" + "github.com/flanksource/duty/artifact/clients/smb" + "github.com/flanksource/duty/types" +) + +type smbFS struct { + *smb.SMBSession +} + +type SMBFileInfo struct { + Base string + os.FileInfo +} + +func (t *SMBFileInfo) FullPath() string { + return path.Join(t.Base, t.Name()) +} + +func NewSMBFS(server string, port, share string, auth types.Authentication) (*smbFS, error) { + if port == "" { + port = "445" + } + + session, err := smb.SMBConnect(server, port, share, auth) + if err != nil { + return nil, err + } + + return &smbFS{SMBSession: session}, nil +} + +func (s *smbFS) Close() error { + return s.SMBSession.Close() +} + +func (s *smbFS) Read(_ gocontext.Context, path string) (io.ReadCloser, error) { + return s.Open(path) +} + +func (s *smbFS) Write(_ gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + f, err := s.Create(path) + if err != nil { + return nil, err + } + defer f.Close() + + if _, err = io.Copy(f, data); err != nil { + return nil, fmt.Errorf("error writing file: %w", err) + } + + return f.Stat() +} + +func (t *smbFS) ReadDir(name string) ([]artifact.FileInfo, error) { + if strings.Contains(name, "*") { + return t.ReadDirGlob(name) + } + + fileInfos, err := t.SMBSession.ReadDir(name) + if err != nil { + return nil, err + } + + output := make([]artifact.FileInfo, 0, len(fileInfos)) + for _, fileInfo := range fileInfos { + output = append(output, &SMBFileInfo{Base: name, FileInfo: fileInfo}) + } + + return output, nil +} + +func (t *smbFS) ReadDirGlob(name string) ([]artifact.FileInfo, error) { + base, pattern := doublestar.SplitPattern(name) + matches, err := doublestar.Glob(t.DirFS(base), pattern) + if err != nil { + return nil, fmt.Errorf("error globbing pattern %q: %w", pattern, err) + } + + output := make([]artifact.FileInfo, 0, len(matches)) + for _, match := range matches { + fullPath := filepath.Join(base, match) + info, err := t.Stat(fullPath) + if err != nil { + return nil, err + } + output = append(output, &SMBFileInfo{Base: base, FileInfo: info}) + } + + return output, nil +} diff --git a/artifact/fs/ssh.go b/artifact/fs/ssh.go new file mode 100644 index 000000000..8835c0e94 --- /dev/null +++ b/artifact/fs/ssh.go @@ -0,0 +1,113 @@ +package fs + +import ( + gocontext "context" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "strings" + + sftpClient "github.com/flanksource/duty/artifact/clients/sftp" + "github.com/flanksource/duty/artifact" + "github.com/pkg/sftp" +) + +type sshFS struct { + *sftp.Client + wd string +} + +type sshFileInfo struct { + fullpath string + fs.FileInfo +} + +func (t *sshFileInfo) FullPath() string { + return t.fullpath +} + +func NewSSHFS(host, user, password string) (*sshFS, error) { + client, err := sftpClient.SSHConnect(host, user, password) + if err != nil { + return nil, err + } + + wd, err := client.Getwd() + if err != nil { + return nil, fmt.Errorf("failed to get working directory: %w", err) + } + + return &sshFS{ + wd: wd, + Client: client, + }, nil +} + +func (t *sshFS) ReadDir(name string) ([]artifact.FileInfo, error) { + if strings.Contains(name, "*") { + return t.ReadDirGlob(name) + } + + files, err := t.Client.ReadDir(name) + if err != nil { + return nil, err + } + + output := make([]artifact.FileInfo, 0, len(files)) + for _, file := range files { + base := name + if !strings.HasPrefix(name, "/") { + base = filepath.Join(t.wd, name) + } + output = append(output, &sshFileInfo{FileInfo: file, fullpath: filepath.Join(base, file.Name())}) + } + + return output, nil +} + +func (t *sshFS) ReadDirGlob(name string) ([]artifact.FileInfo, error) { + entries, err := t.Glob(name) + if err != nil { + return nil, err + } + + output := make([]artifact.FileInfo, 0, len(entries)) + for _, entry := range entries { + info, err := t.Stat(entry) + if err != nil { + return nil, err + } + output = append(output, &sshFileInfo{FileInfo: info, fullpath: entry}) + } + + return output, nil +} + +func (s *sshFS) Read(_ gocontext.Context, path string) (io.ReadCloser, error) { + return s.Open(path) +} + +func (s *sshFS) Close() error { + return s.Client.Close() +} + +func (s *sshFS) Write(_ gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + dir := filepath.Dir(path) + if err := s.MkdirAll(dir); err != nil { + return nil, fmt.Errorf("error creating directory: %w", err) + } + + f, err := s.Create(path) + if err != nil { + return nil, fmt.Errorf("error creating file: %w", err) + } + defer f.Close() + + if _, err = io.Copy(f, data); err != nil { + return nil, fmt.Errorf("error writing to file: %w", err) + } + + return f.Stat() +} diff --git a/artifact/inline.go b/artifact/inline.go new file mode 100644 index 000000000..033f61ffd --- /dev/null +++ b/artifact/inline.go @@ -0,0 +1,134 @@ +package artifact + +import ( + "bytes" + gocontext "context" + "fmt" + "io" + "os" + "strings" + "time" + + "github.com/flanksource/duty/models" + "gorm.io/gorm" +) + +type InlineStore struct { + db *gorm.DB + compression string + maxSize int +} + +func NewInlineStore(db *gorm.DB) *InlineStore { + return &InlineStore{db: db, compression: "gzip", maxSize: 1048576} +} + +func (s *InlineStore) WithCompression(compression string) *InlineStore { + s.compression = compression + return s +} + +func (s *InlineStore) WithMaxSize(maxSize int) *InlineStore { + s.maxSize = maxSize + return s +} + +func (s *InlineStore) Write(_ gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + raw, err := io.ReadAll(data) + if err != nil { + return nil, fmt.Errorf("reading artifact data: %w", err) + } + + a := models.Artifact{ + Path: path, + Filename: path, + } + if err := a.SetContent(raw, s.compression, s.maxSize); err != nil { + return nil, fmt.Errorf("setting inline content for %s: %w", path, err) + } + + return &inlineFileInfo{ + name: a.Filename, + size: a.Size, + mod: time.Now(), + path: path, + artifact: &a, + }, nil +} + +// InlineFileInfo returns the underlying artifact with inline content set. +// Used by blobStore to persist the artifact with content. +func InlineArtifact(info os.FileInfo) *models.Artifact { + if fi, ok := info.(*inlineFileInfo); ok { + return fi.artifact + } + return nil +} + +func (s *InlineStore) Read(_ gocontext.Context, path string) (io.ReadCloser, error) { + var artifact models.Artifact + if err := s.db.Where("path = ?", path).First(&artifact).Error; err != nil { + return nil, fmt.Errorf("finding inline artifact %s: %w", path, err) + } + + content, err := artifact.GetContent() + if err != nil { + return nil, fmt.Errorf("decompressing inline artifact %s: %w", path, err) + } + + return io.NopCloser(bytes.NewReader(content)), nil +} + +func (s *InlineStore) ReadDir(name string) ([]FileInfo, error) { + var artifacts []models.Artifact + pattern := strings.ReplaceAll(name, "*", "%") + if !strings.Contains(pattern, "%") { + pattern += "%" + } + if err := s.db.Where("path LIKE ?", pattern).Find(&artifacts).Error; err != nil { + return nil, fmt.Errorf("listing inline artifacts under %s: %w", name, err) + } + + infos := make([]FileInfo, len(artifacts)) + for i, a := range artifacts { + infos[i] = &inlineFileInfo{ + name: a.Filename, + size: a.Size, + mod: a.CreatedAt, + path: a.Path, + } + } + return infos, nil +} + +func (s *InlineStore) Stat(name string) (os.FileInfo, error) { + var artifact models.Artifact + if err := s.db.Where("path = ?", name).First(&artifact).Error; err != nil { + return nil, fmt.Errorf("stat inline artifact %s: %w", name, err) + } + + return &inlineFileInfo{ + name: artifact.Filename, + size: artifact.Size, + mod: artifact.CreatedAt, + path: artifact.Path, + }, nil +} + +func (s *InlineStore) Close() error { return nil } + +type inlineFileInfo struct { + name string + size int64 + mod time.Time + path string + artifact *models.Artifact +} + +func (f *inlineFileInfo) Name() string { return f.name } +func (f *inlineFileInfo) Size() int64 { return f.size } +func (f *inlineFileInfo) Mode() os.FileMode { return 0444 } +func (f *inlineFileInfo) ModTime() time.Time { return f.mod } +func (f *inlineFileInfo) IsDir() bool { return false } +func (f *inlineFileInfo) Sys() any { return nil } +func (f *inlineFileInfo) FullPath() string { return f.path } diff --git a/artifact/logged.go b/artifact/logged.go new file mode 100644 index 000000000..fba21742e --- /dev/null +++ b/artifact/logged.go @@ -0,0 +1,165 @@ +package artifact + +import ( + gocontext "context" + "crypto/sha256" + "fmt" + "hash" + "io" + "os" + "time" + + "github.com/flanksource/clicky" + "github.com/flanksource/commons/logger" + "github.com/flanksource/duty/models" + "github.com/google/uuid" +) + +// LoggedBlobStore wraps a BlobStore with structured logging. +type LoggedBlobStore struct { + inner BlobStore + logger logger.Logger + backend string +} + +func NewLoggedBlobStore(inner BlobStore, log logger.Logger, backend string) BlobStore { + return &LoggedBlobStore{inner: inner, logger: log, backend: backend} +} + +func (l *LoggedBlobStore) Write(data Data, a *models.Artifact) (*models.Artifact, error) { + l.logger.Debugf("%s", l.formatOp("Write", data.Filename)) + start := time.Now() + result, err := l.inner.Write(data, a) + if v := l.logger.V(2); err == nil && result != nil { + v.Infof("%s", l.formatResult("Write", result.Filename, result.Size, result.Checksum, time.Since(start))) + } + return result, err +} + +func (l *LoggedBlobStore) Read(artifactID uuid.UUID) (*Data, error) { + l.logger.Debugf("%s", l.formatOp("Read", artifactID.String())) + start := time.Now() + data, err := l.inner.Read(artifactID) + if v := l.logger.V(2); err == nil && data != nil { + v.Infof("%s %s %s", l.formatOp("Read", artifactID.String()), data.Pretty().String(), clicky.Text(time.Since(start).String(), "text-gray-500").String()) + } + return data, err +} + +func (l *LoggedBlobStore) Close() error { + l.logger.V(2).Infof("%s", l.formatOp("Close", "")) + return l.inner.Close() +} + +func (l *LoggedBlobStore) formatOp(op, detail string) string { + return clicky.Text(fmt.Sprintf("[%s]", l.backend), "text-blue-500"). + AddText(" "+op, "font-bold"). + AddText(" "+detail, "text-gray-300"). + String() +} + +func (l *LoggedBlobStore) formatResult(op, filename string, size int64, checksum string, duration time.Duration) string { + s := clicky.Text(fmt.Sprintf("[%s]", l.backend), "text-blue-500"). + AddText(" "+op, "font-bold"). + AddText(" "+filename, "text-gray-300") + if size >= 0 { + s = s.AddText(fmt.Sprintf(" %s", formatBytes(size)), "text-gray-400") + } + if checksum != "" { + short := checksum + if len(short) > 8 { + short = short[:8] + } + s = s.AddText(" sha:"+short, "text-yellow-500") + } + s = s.AddText(fmt.Sprintf(" %s", duration), "text-gray-500") + return s.String() +} + +// LoggedFS wraps a FilesystemRW with structured logging (used by e2e tests). +type LoggedFS struct { + inner FilesystemRW + logger logger.Logger + backend string +} + +func NewLoggedFS(inner FilesystemRW, log logger.Logger, backend string) *LoggedFS { + return &LoggedFS{inner: inner, logger: log, backend: backend} +} + +func (l *LoggedFS) Read(ctx gocontext.Context, path string) (io.ReadCloser, error) { + l.logger.Debugf("[%s] Read %s", l.backend, path) + start := time.Now() + r, err := l.inner.Read(ctx, path) + if err != nil { + return nil, err + } + if v := l.logger.V(2); v.Enabled() { + return &checksumReader{ + ReadCloser: r, + hash: sha256.New(), + onClose: func(h hash.Hash, n int64) { + v.Infof("[%s] Read %s (%s, sha:%x, %s)", l.backend, path, formatBytes(n), h.Sum(nil)[:4], time.Since(start)) + }, + }, nil + } + return r, nil +} + +func (l *LoggedFS) Write(ctx gocontext.Context, path string, data io.Reader) (os.FileInfo, error) { + l.logger.Debugf("[%s] Write %s", l.backend, path) + start := time.Now() + info, err := l.inner.Write(ctx, path, data) + if v := l.logger.V(2); err == nil && info != nil { + v.Infof("[%s] Write %s (%s, %s)", l.backend, path, formatBytes(info.Size()), time.Since(start)) + } + return info, err +} + +func (l *LoggedFS) ReadDir(name string) ([]FileInfo, error) { + l.logger.Debugf("[%s] ReadDir %s", l.backend, name) + start := time.Now() + entries, err := l.inner.ReadDir(name) + if v := l.logger.V(2); err == nil { + v.Infof("[%s] ReadDir %s (%d entries, %s)", l.backend, name, len(entries), time.Since(start)) + } + return entries, err +} + +func (l *LoggedFS) Stat(name string) (os.FileInfo, error) { + l.logger.Debugf("[%s] Stat %s", l.backend, name) + info, err := l.inner.Stat(name) + if v := l.logger.V(2); err == nil && info != nil { + v.Infof("[%s] Stat %s (%s)", l.backend, name, formatBytes(info.Size())) + } + return info, err +} + +func (l *LoggedFS) Close() error { + l.logger.V(2).Infof("[%s] Close", l.backend) + return l.inner.Close() +} + +type checksumReader struct { + io.ReadCloser + hash hash.Hash + n int64 + onClose func(hash.Hash, int64) +} + +func (r *checksumReader) Read(p []byte) (int, error) { + n, err := r.ReadCloser.Read(p) + if n > 0 { + r.hash.Write(p[:n]) + r.n += int64(n) + } + return n, err +} + +func (r *checksumReader) Close() error { + err := r.ReadCloser.Close() + r.onClose(r.hash, r.n) + return err +} + +var _ io.ReadCloser = (*checksumReader)(nil) diff --git a/connection/aws.go b/connection/aws.go index 08bb38def..346bb9380 100644 --- a/connection/aws.go +++ b/connection/aws.go @@ -153,7 +153,7 @@ func (t *AWSConnection) Client(ctx context.Context, opts ...types.ClientOption) tr = harCollector.Middleware()(tr) } - if ctx.IsTrace() { + if ctx.IsTrace() && harCollector == nil { httplogger := &httpretty.Logger{ Time: true, TLS: ctx.Logger.IsLevelEnabled(7), diff --git a/connection/blob.go b/connection/blob.go new file mode 100644 index 000000000..0b6917fe2 --- /dev/null +++ b/connection/blob.go @@ -0,0 +1,165 @@ +package connection + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/flanksource/commons/logger" + "github.com/flanksource/duty/artifact" + artifactFS "github.com/flanksource/duty/artifact/fs" + "github.com/flanksource/duty/context" + "github.com/flanksource/duty/models" + "github.com/flanksource/duty/types" + "github.com/henvic/httpretty" +) + +var blobsLogger = logger.GetLogger("blobs") + +func init() { + context.BlobStoreProvider = getBlobStore +} + +func getBlobStore(ctx context.Context, connURL string) (artifact.BlobStore, error) { + conn, err := ctx.HydrateConnectionByURL(connURL) + if err != nil { + return nil, fmt.Errorf("resolving artifact connection %q: %w", connURL, err) + } + if conn == nil { + return nil, fmt.Errorf("artifact connection %q not found", connURL) + } + + fs, backend, err := getFSForConnection(ctx, *conn) + if err != nil { + return nil, err + } + + blobsLogger.Infof("Initializing %s blob store", backend) + store := artifact.NewBlobStore(fs, ctx.DB(), backend) + return artifact.NewLoggedBlobStore(store, blobsLogger, backend), nil +} + +func debugTransport() http.RoundTripper { + httpLogger := &httpretty.Logger{ + Time: true, + TLS: true, + RequestHeader: true, + RequestBody: false, + ResponseHeader: true, + ResponseBody: false, + Colors: true, + Formatters: []httpretty.Formatter{&httpretty.JSONFormatter{}}, + } + return httpLogger.RoundTripper(http.DefaultTransport) +} + +func GetFSForConnection(ctx context.Context, c models.Connection) (artifact.FilesystemRW, error) { + fs, _, err := getFSForConnection(ctx, c) + return fs, err +} + +func getFSForConnection(ctx context.Context, c models.Connection) (artifact.FilesystemRW, string, error) { + useDebugTransport := blobsLogger.V(3).Enabled() + + switch c.Type { + case models.ConnectionTypeFolder: + return artifactFS.NewLocalFS(c.Properties["path"]), "local", nil + + case models.ConnectionTypeS3: + var conn S3Connection + conn.ConnectionName = c.ID.String() + if err := conn.Populate(ctx); err != nil { + return nil, "", err + } + + if c.Properties["bucket"] != "" { + conn.Bucket = c.Properties["bucket"] + } + if val, ok := c.Properties["usePathStyle"]; ok { + if b, err := strconv.ParseBool(val); err == nil { + conn.UsePathStyle = b + } + } + + cfg, err := conn.Client(ctx) + if err != nil { + return nil, "", err + } + + if useDebugTransport { + cfg.HTTPClient = &http.Client{Transport: debugTransport()} + } + + client := s3.NewFromConfig(cfg, func(o *s3.Options) { + o.UsePathStyle = conn.UsePathStyle + if conn.Endpoint != "" { + o.BaseEndpoint = &conn.Endpoint + } + }) + + return artifactFS.NewS3FS(client, conn.Bucket), "s3", nil + + case models.ConnectionTypeGCS: + var conn GCSConnection + conn.ConnectionName = c.ID.String() + if err := conn.HydrateConnection(ctx); err != nil { + return nil, "", err + } + + if c.Properties["bucket"] != "" { + conn.Bucket = c.Properties["bucket"] + } + + client, err := conn.Client(ctx) + if err != nil { + return nil, "", err + } + + return artifactFS.NewGCSFS(client, conn.Bucket), "gcs", nil + + case models.ConnectionTypeSFTP: + parsedURL, err := url.Parse(c.URL) + if err != nil { + return nil, "", err + } + port := c.Properties["port"] + if port == "" { + port = "22" + } + fs, err := artifactFS.NewSSHFS(fmt.Sprintf("%s:%s", parsedURL.Host, port), c.Username, c.Password) + return fs, "sftp", err + + case models.ConnectionTypeSMB: + fs, err := artifactFS.NewSMBFS(c.URL, c.Properties["port"], c.Properties["share"], types.Authentication{ + Username: types.EnvVar{ValueStatic: c.Username}, + Password: types.EnvVar{ValueStatic: c.Password}, + }) + return fs, "smb", err + + case models.ConnectionTypeAzure: + container := c.Properties["container"] + connStr := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;BlobEndpoint=%s", + c.Username, c.Password, c.URL) + + var opts *azblob.ClientOptions + if useDebugTransport { + opts = &azblob.ClientOptions{ + ClientOptions: policy.ClientOptions{ + Transport: &http.Client{Transport: debugTransport()}, + }, + } + } + + client, err := azblob.NewClientFromConnectionString(connStr, opts) + if err != nil { + return nil, "", fmt.Errorf("creating Azure Blob client: %w", err) + } + return artifactFS.NewAzureBlobFS(client, container), "azure", nil + } + + return nil, "", fmt.Errorf("unsupported connection type %q for blob store", c.Type) +} diff --git a/connection/gcp.go b/connection/gcp.go index 7ffc610a8..c1b3db5e8 100644 --- a/connection/gcp.go +++ b/connection/gcp.go @@ -45,7 +45,7 @@ func (t *GCPConnection) FromModel(connection models.Connection) { } func (g *GCPConnection) TokenSource(ctx context.Context, scopes ...string) (oauth2.TokenSource, error) { - creds, err := google.CredentialsFromJSON(ctx, []byte(g.Credentials.ValueStatic), scopes...) + creds, err := google.CredentialsFromJSON(ctx, []byte(g.Credentials.ValueStatic), scopes...) //nolint:staticcheck if err != nil { return nil, err } diff --git a/connection/gcs.go b/connection/gcs.go index 9c7bdc22b..b0c44aebd 100644 --- a/connection/gcs.go +++ b/connection/gcs.go @@ -62,7 +62,7 @@ func (g *GCSConnection) Client(ctx context.Context, opts ...types.ClientOption) if err != nil { return nil, err } - creds, err := google.CredentialsFromJSON(ctx, []byte(credential), gcs.ScopeReadWrite) + creds, err := google.CredentialsFromJSON(ctx, []byte(credential), gcs.ScopeReadWrite) //nolint:staticcheck if err != nil { return nil, err } diff --git a/connection/gke.go b/connection/gke.go index 74f62f352..fb4086534 100644 --- a/connection/gke.go +++ b/connection/gke.go @@ -70,7 +70,7 @@ func (t *GKEConnection) Client(ctx context.Context, opts ...types.ClientOption) if err != nil { return nil, err } - creds, err := google.CredentialsFromJSON(ctx, []byte(credential), container.CloudPlatformScope) + creds, err := google.CredentialsFromJSON(ctx, []byte(credential), container.CloudPlatformScope) //nolint:staticcheck if err != nil { return nil, err } diff --git a/context/blobs.go b/context/blobs.go new file mode 100644 index 000000000..85ccd5526 --- /dev/null +++ b/context/blobs.go @@ -0,0 +1,30 @@ +package context + +import ( + "fmt" + + "github.com/flanksource/commons/logger" + "github.com/flanksource/duty/artifact" +) + +var blobsLogger = logger.GetLogger("blobs") + +// BlobStoreProvider resolves a connection URL and returns a BlobStore backed by an external FS. +// It is set by the connection package during init(). +var BlobStoreProvider func(ctx Context, connURL string) (artifact.BlobStore, error) + +// Blobs returns the appropriate blob store for this context. +// If an artifacts.connection property is configured and a provider is registered, +// it returns the external backend. Otherwise it returns the inline DB-backed store. +func (k Context) Blobs() (artifact.BlobStore, error) { + connURL := k.Properties().String("artifacts.connection", "") + if connURL == "" { + blobsLogger.Infof("Initializing inline blob store") + store := artifact.NewBlobStore(artifact.NewInlineStore(k.DB()), k.DB(), "inline") + return artifact.NewLoggedBlobStore(store, blobsLogger, "inline"), nil + } + if BlobStoreProvider == nil { + return nil, fmt.Errorf("artifacts.connection is configured as %q but no blob store provider is registered", connURL) + } + return BlobStoreProvider(k, connURL) +} diff --git a/go.mod b/go.mod index f57209ba1..35cb3f5dc 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery v1.2.0 + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 github.com/Masterminds/squirrel v1.5.4 github.com/RaveNoX/go-jsonmerge v1.0.0 github.com/TomOnTime/utfutil v1.0.0 @@ -78,25 +79,26 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 + github.com/testcontainers/testcontainers-go v0.41.0 github.com/timberio/go-datemath v0.1.0 github.com/zclconf/go-cty v1.17.0 go.opentelemetry.io/otel v1.41.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 - go.opentelemetry.io/otel/sdk v1.40.0 + go.opentelemetry.io/otel/sdk v1.41.0 go.opentelemetry.io/otel/trace v1.41.0 gocloud.dev v0.44.0 gocloud.dev/pubsub/kafkapubsub v0.44.0 gocloud.dev/pubsub/natspubsub v0.44.0 golang.org/x/crypto v0.48.0 golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa - golang.org/x/oauth2 v0.34.0 + golang.org/x/oauth2 v0.35.0 golang.org/x/sync v0.19.0 gonum.org/v1/gonum v0.17.0 google.golang.org/api v0.262.0 - google.golang.org/grpc v1.78.0 + google.golang.org/grpc v1.79.1 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.31.1 @@ -111,7 +113,29 @@ require ( ) require ( + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/docker/docker v28.5.2+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.10.0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.2.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.2 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/shirou/gopsutil/v4 v4.26.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect ) @@ -160,7 +184,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.49.5 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.95.1 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.95.1 github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect github.com/aws/aws-sdk-go-v2/service/sns v1.39.11 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 // indirect @@ -170,7 +194,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar v1.3.4 // indirect - github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect + github.com/bmatcuk/doublestar/v4 v4.10.0 github.com/buger/jsonparser v1.1.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cert-manager/cert-manager v1.19.4 // indirect @@ -203,7 +227,7 @@ require ( github.com/flanksource/kubectl-neat v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 github.com/geoffgarside/ber v1.2.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect @@ -251,11 +275,11 @@ require ( github.com/gosimple/slug v1.15.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.5 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hairyhenderson/toml v0.4.2-0.20210923231440-40456b8e66cf // indirect github.com/hairyhenderson/yaml v0.0.0-20220618171115-2d35fca545ce // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hirochachacha/go-smb2 v1.1.0 // indirect + github.com/hirochachacha/go-smb2 v1.1.0 github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.7 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -315,7 +339,7 @@ require ( github.com/pierrec/lz4/v4 v4.1.25 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/sftp v1.13.10 // indirect + github.com/pkg/sftp v1.13.10 github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/playwright-community/playwright-go v0.5700.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -370,7 +394,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect go.opentelemetry.io/otel/metric v1.41.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect diff --git a/go.sum b/go.sum index 300fded01..c8e387f8d 100644 --- a/go.sum +++ b/go.sum @@ -19,26 +19,10 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 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/accessapproval v1.8.8/go.mod h1:RFwPY9JDKseP4gJrX1BlAVsP5O6kI8NdGlTmaeDefmk= -cloud.google.com/go/accesscontextmanager v1.9.7/go.mod h1:i6e0nd5CPcrh7+YwGq4bKvju5YB9sgoAip+mXU73aMM= -cloud.google.com/go/aiplatform v1.113.0/go.mod h1:B8fcWtC2vSadapIQqweJrTATJe/odNDjk2uIA5kmXog= -cloud.google.com/go/analytics v0.30.1/go.mod h1:V/FnINU5kMOsttZnKPnXfKi6clJUHTEXUKQjHxcNK8A= -cloud.google.com/go/apigateway v1.7.7/go.mod h1:j1bCmrUK1BzVHpiIyTApxB7cRyhivKzltqLmp6j6i7U= -cloud.google.com/go/apigeeconnect v1.7.7/go.mod h1:ftGK3nca0JePiVLl0A6alaMjKdOc5C+sAkFMyH2RH8U= -cloud.google.com/go/apigeeregistry v0.10.0/go.mod h1:SAlF5OhKvyLDuwWAaFAIVJjrEqKRrGTPkJs+TWNnSqg= -cloud.google.com/go/appengine v1.9.7/go.mod h1:y1XpGVeAhbsNzHida79cHbr3pFRsym0ob8xnC8yphbo= -cloud.google.com/go/area120 v0.9.7/go.mod h1:5nJ0yksmjOMfc4Zpk+okWfJ3A1004FvB82rfia+ZLaY= -cloud.google.com/go/artifactregistry v1.19.0/go.mod h1:UEAPCgHDFC1q+A8nnVxXHPEy9KCVOeavFBF1fEChQvU= -cloud.google.com/go/asset v1.22.0/go.mod h1:q80JP2TeWWzMCazYnrAfDf36aQKf1QiKzzpNLflJwf8= -cloud.google.com/go/assuredworkloads v1.13.0/go.mod h1:o/oHEOnUlribR+uJWTKQo8A5RhSl9K9FNeMOew4TJ3M= 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/automl v1.15.0/go.mod h1:U9zOtQb8zVrFNGTuW3BfxeqmLyeleLgT9B12EaXfODg= -cloud.google.com/go/baremetalsolution v1.4.0/go.mod h1:K6C6g4aS8LW95I0fEHZiBsBlh0UxwDLGf+S/vyfXbvg= -cloud.google.com/go/batch v1.14.0/go.mod h1:oeQveyG6NDS/ks2ilOP4LzKRmuIaI7GLe0CkR7WF6pk= -cloud.google.com/go/beyondcorp v1.2.0/go.mod h1:sszcgxpPPBEfLzbI0aYCTg6tT1tyt3CmKav3NZIUcvI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -47,85 +31,25 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/bigquery v1.72.0 h1:D/yLju+3Ens2IXx7ou1DJ62juBm+/coBInn4VVOg5Cw= cloud.google.com/go/bigquery v1.72.0/go.mod h1:GUbRtmeCckOE85endLherHD9RsujY+gS7i++c1CqssQ= -cloud.google.com/go/bigtable v1.41.0/go.mod h1:JlaltP06LEFXaxQdZiarGR9tKsX/II0IkNAKMDrWspI= -cloud.google.com/go/billing v1.21.0/go.mod h1:ZGairB3EVnb3i09E2SxFxo50p5unPaMTuo1jh6jW9js= -cloud.google.com/go/binaryauthorization v1.10.0/go.mod h1:WOuiaQkI4PU/okwrcREjSAr2AUtjQgVe+PlrXKOmKKw= -cloud.google.com/go/certificatemanager v1.9.6/go.mod h1:vWogV874jKZkSRDFCMM3r7wqybv8WXs3XhyNff6o/Zo= -cloud.google.com/go/channel v1.21.0/go.mod h1:8v3TwHtgLmFxTpL2U+e10CLFOQN8u/Vr9RhYcJUS3y8= -cloud.google.com/go/cloudbuild v1.25.0/go.mod h1:lCu+T6IPkobPo2Nw+vCE7wuaAl9HbXLzdPx/tcF+oWo= -cloud.google.com/go/clouddms v1.8.8/go.mod h1:QtCyw+a73dlkDb2q20aTAPvfaTZCepDDi6Gb1AKq0a4= cloud.google.com/go/cloudsqlconn v1.20.0 h1:5EBr98dktt5QStX6jacFTECTQ4rxfY6qpIUIV9YNRqo= cloud.google.com/go/cloudsqlconn v1.20.0/go.mod h1:YCoWR0SWYTDf9npeqq8ODFN1WdGMGVC5G74+A3CXXP4= -cloud.google.com/go/cloudtasks v1.13.7/go.mod h1:H0TThOUG+Ml34e2+ZtW6k6nt4i9KuH3nYAJ5mxh7OM4= -cloud.google.com/go/compute v1.53.0/go.mod h1:zdogTa7daHhEtEX92+S5IARtQmi/RNVPUfoI8Jhl8Do= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= 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/contactcenterinsights v1.17.4/go.mod h1:kZe6yOnKDfpPz2GphDHynxk/Spx+53UX/pGf+SmWAKM= -cloud.google.com/go/container v1.45.0/go.mod h1:eB6jUfJLjne9VsTDGcH7mnj6JyZK+KOUIA6KZnYE/ds= -cloud.google.com/go/containeranalysis v0.14.2/go.mod h1:FjppROiUtP9cyMegdWdY/TsBSGc6kqh1GjA2NOJXXL8= cloud.google.com/go/datacatalog v1.26.1 h1:bCRKA8uSQN8wGW3Tw0gwko4E9a64GRmbW1nCblhgC2k= cloud.google.com/go/datacatalog v1.26.1/go.mod h1:2Qcq8vsHNxMDgjgadRFmFG47Y+uuIVsyEGUrlrKEdrg= -cloud.google.com/go/dataflow v0.11.1/go.mod h1:3s6y/h5Qz7uuxTmKJKBifkYZ3zs63jS+6VGtSu8Cf7Y= -cloud.google.com/go/dataform v0.12.1/go.mod h1:atGS8ReRjfNDUQib0X/o/7Gi2bqHI2G7/J86LKiGimE= -cloud.google.com/go/datafusion v1.8.7/go.mod h1:4dkFb1la41qCEXh1AzYtFwl842bu2ikTUXyKhjvFCb0= -cloud.google.com/go/datalabeling v0.9.7/go.mod h1:EEUVn+wNn3jl19P2S13FqE1s9LsKzRsPuuMRq2CMsOk= -cloud.google.com/go/dataplex v1.28.0/go.mod h1:VB+xlYJiJ5kreonXsa2cHPj0A3CfPh/mgiHG4JFhbUA= -cloud.google.com/go/dataproc/v2 v2.15.0/go.mod h1:tSdkodShfzrrUNPDVEL6MdH9/mIEvp/Z9s9PBdbsZg8= -cloud.google.com/go/dataqna v0.9.8/go.mod h1:2lHKmGPOqzzuqCc5NI0+Xrd5om4ulxGwPpLB4AnFgpA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.21.0/go.mod h1:9l+KyAHO+YVVcdBbNQZJu8svF17Nw5sMKuFR0LYf1nY= -cloud.google.com/go/datastream v1.15.1/go.mod h1:aV1Grr9LFon0YvqryE5/gF1XAhcau2uxN2OvQJPpqRw= -cloud.google.com/go/deploy v1.27.3/go.mod h1:7LFIYYTSSdljYRqY3n+JSmIFdD4lv6aMD5xg0crB5iw= -cloud.google.com/go/dialogflow v1.73.0/go.mod h1:vFkeDO7ishnfakWVLlbgIynQGTFJ/YaVMlYmSn5M+1o= -cloud.google.com/go/dlp v1.28.0/go.mod h1:C3od1fIK8lf7Kr62aU1Uh0z4OL5Z8s3do3znAiEupAw= -cloud.google.com/go/documentai v1.39.0/go.mod h1:KmlLO93F7GRU8dENXRxvt+7V8o7eCG6Y6WDitKbcYJs= -cloud.google.com/go/domains v0.10.7/go.mod h1:T3WG/QUAO/52z4tUPooKS8AY7yXaFxPYn1V3F0/JbNQ= -cloud.google.com/go/edgecontainer v1.4.4/go.mod h1:yyNVHsCKtsX/0mqFdbljQw0Uo660q2dlMPaiqYiC2Tg= -cloud.google.com/go/errorreporting v0.4.0/go.mod h1:dZGEhqzdHZSRxxWLVjC3Ue5CVaROzvP58D9rU6zbBfw= -cloud.google.com/go/essentialcontacts v1.7.7/go.mod h1:ytycWAEn/aKUMRKQPMVgMrAtphEMgjbzL8vFwM3tqXs= -cloud.google.com/go/eventarc v1.18.0/go.mod h1:/6SDoqh5+9QNUqCX4/oQcJVK16fG/snHBSXu7lrJtO8= -cloud.google.com/go/filestore v1.10.3/go.mod h1:94ZGyLTx9j+aWKozPQ6Wbq1DuImie/L/HIdGMshtwac= -cloud.google.com/go/firestore v1.21.0/go.mod h1:1xH6HNcnkf/gGyR8udd6pFO4Z7GWJSwLKQMx/u6UrP4= -cloud.google.com/go/functions v1.19.7/go.mod h1:xbcKfS7GoIcaXr2FSwmtn9NXal1JR4TV6iYZlgXffwA= -cloud.google.com/go/gkebackup v1.8.1/go.mod h1:GAaAl+O5D9uISH5MnClUop2esQW4pDa2qe/95A4l7YQ= -cloud.google.com/go/gkeconnect v0.12.5/go.mod h1:wMD2RXcsAWlkREZWJDVeDV70PYka1iEb9stFmgpw+5o= -cloud.google.com/go/gkehub v0.16.0/go.mod h1:ADp27Ucor8v81wY+x/5pOxTorxkPj/xswH3AUpN62GU= -cloud.google.com/go/gkemulticloud v1.6.0/go.mod h1:bGpd4o/Z5Z/XFlaojkgdVisHRwb+fLJvUPzsmV0I9ok= -cloud.google.com/go/gsuiteaddons v1.7.8/go.mod h1:DBKNHH4YXAdd/rd6zVvtOGAJNGo0ekOh+nIjTUDEJ5U= 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/iap v1.11.3/go.mod h1:+gXO0ClH62k2LVlfhHzrpiHQNyINlEVmGAE3+DB4ShU= -cloud.google.com/go/ids v1.5.7/go.mod h1:N3ZQOIgIBwwOu2tzyhmh3JDT+kt8PcoKkn2BRT9Qe4A= -cloud.google.com/go/iot v1.8.7/go.mod h1:HvVcypV8LPv1yTXSLCNK+YCtqGHhq+p0F3BXETfpN+U= cloud.google.com/go/kms v1.25.0 h1:gVqvGGUmz0nYCmtoxWmdc1wli2L1apgP8U4fghPGSbQ= cloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk= -cloud.google.com/go/language v1.14.6/go.mod h1:7y3J9OexQsfkWNGCxhT+7lb64pa60e12ZCoWDOHxJ1M= -cloud.google.com/go/lifesciences v0.10.7/go.mod h1:v3AbTki9iWttEls/Wf4ag3EqeLRHofploOcpsLnu7iY= 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/managedidentities v1.7.7/go.mod h1:nwNlMxtBo2YJMvsKXRtAD1bL41qiCI9npS7cbqrsJUs= -cloud.google.com/go/maps v1.26.0/go.mod h1:+auempdONAP8emtm48aCfNo1ZC+3CJniRA1h8J4u7bY= -cloud.google.com/go/mediatranslation v0.9.7/go.mod h1:mz3v6PR7+Fd/1bYrRxNFGnd+p4wqdc/fyutqC5QHctw= -cloud.google.com/go/memcache v1.11.7/go.mod h1:AU1jYlUqCihxapcJ1GGMtlMWDVhzjbfUWBXqsXa4rBg= -cloud.google.com/go/metastore v1.14.8/go.mod h1:h1XI2LpD4ohJhQYn9TwXqKb5sVt6KSo47ft96SiFF1s= 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/networkconnectivity v1.19.1/go.mod h1:Q5v6uNNNz8BP232uuXM66XgWML9m379xhwv58Y+8Kb0= -cloud.google.com/go/networkmanagement v1.21.0/go.mod h1:clG/5Yt0wQ57qSH6Yh7oehQYlobHw3F6nb3Pn4ig5hU= -cloud.google.com/go/networksecurity v0.11.0/go.mod h1:JLgDsg4tOyJ3eMO8lypjqMftbfd60SJ+P7T+DUmWBsM= -cloud.google.com/go/notebooks v1.12.7/go.mod h1:uR9pxAkKmlNloibMr9Q1t8WhIu4P2JeqJs7c064/0Mo= -cloud.google.com/go/optimization v1.7.7/go.mod h1:OY2IAlX23o52qwMAZ0w65wibKuV12a4x6IHDTCq6kcU= -cloud.google.com/go/orchestration v1.11.10/go.mod h1:tz7m1s4wNEvhNNIM3JOMH0lYxBssu9+7si5MCPw/4/0= -cloud.google.com/go/orgpolicy v1.15.1/go.mod h1:bpvi9YIyU7wCW9WiXL/ZKT7pd2Ovegyr2xENIeRX5q0= -cloud.google.com/go/osconfig v1.15.1/go.mod h1:NegylQQl0+5m+I+4Ey/g3HGeQxKkncQ1q+Il4DZ8PME= -cloud.google.com/go/oslogin v1.14.7/go.mod h1:NB6NqBHfDMwznePdBVX+ILllc1oPCdNSGp5u/WIyndY= -cloud.google.com/go/phishingprotection v0.9.7/go.mod h1:JTI4HNGyAbWolBoNOoCyCF0e3cqPNrYnlievHU49EwE= -cloud.google.com/go/policytroubleshooter v1.11.7/go.mod h1:JP/aQ+bUkt4Gz6lQXBi/+A/6nyNRZ0Pvxui5Xl9ieyk= -cloud.google.com/go/privatecatalog v0.10.8/go.mod h1:BkLHi+rtAGYBt5DocXLytHhF0n6F03Tegxgty40Y7aA= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -134,23 +58,6 @@ cloud.google.com/go/pubsub v1.50.1 h1:fzbXpPyJnSGvWXF1jabhQeXyxdbCIkXTpjXHy7xviB cloud.google.com/go/pubsub v1.50.1/go.mod h1:6YVJv3MzWJUVdvQXG081sFvS0dWQOdnV+oTo++q/xFk= cloud.google.com/go/pubsub/v2 v2.3.0 h1:DgAN907x+sP0nScYfBzneRiIhWoXcpCD8ZAut8WX9vs= cloud.google.com/go/pubsub/v2 v2.3.0/go.mod h1:O5f0KHG9zDheZAd3z5rlCRhxt2JQtB+t/IYLKK3Bpvw= -cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= -cloud.google.com/go/recaptchaenterprise/v2 v2.21.0/go.mod h1:HxQYqZC2/zl2CvKN7jJEv71vEdDi1GMGNUiZxnpiuVI= -cloud.google.com/go/recommendationengine v0.9.7/go.mod h1:snZ/FL147u86Jqpv1j95R+CyU5NvL/UzYiyDo6UByTM= -cloud.google.com/go/recommender v1.13.6/go.mod h1:y5/5womtdOaIM3xx+76vbsiA+8EBTIVfWnxHDFHBGJM= -cloud.google.com/go/redis v1.18.3/go.mod h1:x8HtXZbvMBDNT6hMHaQ022Pos5d7SP7YsUH8fCJ2Wm4= -cloud.google.com/go/resourcemanager v1.10.7/go.mod h1:rScGkr6j2eFwxAjctvOP/8sqnEpDbQ9r5CKwKfomqjs= -cloud.google.com/go/resourcesettings v1.8.3/go.mod h1:BzgfXFHIWOOmHe6ZV9+r3OWfpHJgnqXy8jqwx4zTMLw= -cloud.google.com/go/retail v1.25.1/go.mod h1:J75G8pd+DH0SHueL9IJw7Y5d2VhTsjFsk+F1t9f8jXc= -cloud.google.com/go/run v1.14.0/go.mod h1:KStBOpjX7m47Yi1xStWSkvJcCqLr+PMUkz6p3po5/VA= -cloud.google.com/go/scheduler v1.11.8/go.mod h1:bNKU7/f04eoM6iKQpwVLvFNBgGyJNS87RiFN73mIPik= -cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q= -cloud.google.com/go/security v1.19.2/go.mod h1:KXmf64mnOsLVKe8mk/bZpU1Rsvxqc0Ej0A6tgCeN93w= -cloud.google.com/go/securitycenter v1.38.1/go.mod h1:Ge2D/SlG2lP1FrQD7wXHy8qyeloRenvKXeB4e7zO6z0= -cloud.google.com/go/servicedirectory v1.12.7/go.mod h1:gOtN+qbuCMH6tj2dqlDY3qQL7w3V0+nkWaZElnJK8Ps= -cloud.google.com/go/shell v1.8.7/go.mod h1:OTke7qc3laNEW5Jr5OV9VR3IwU5x5VqGOE6705zFex4= -cloud.google.com/go/spanner v1.87.0/go.mod h1:tcj735Y2aqphB6/l+X5MmwG4NnV+X1NJIbFSZGaHYXw= -cloud.google.com/go/speech v1.29.0/go.mod h1:wtUmIS/h0ZYU6cPA9klcyST3f6i2FdnvNDqENjrRDds= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= @@ -158,34 +65,15 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.59.1 h1:DXAZLcTimtiXdGqDSnebROVPd9QvRsFVVlptz02Wk58= cloud.google.com/go/storage v1.59.1/go.mod h1:cMWbtM+anpC74gn6qjLh+exqYcfmB9Hqe5z6adx+CLI= -cloud.google.com/go/storagetransfer v1.13.1/go.mod h1:S858w5l383ffkdqAqrAA+BC7KlhCqeNieK3sFf5Bj4Y= -cloud.google.com/go/talent v1.8.4/go.mod h1:3yukBXUTVFNyKcJpUExW/k5gqEy8qW6OCNj7WdN0MWo= -cloud.google.com/go/texttospeech v1.16.0/go.mod h1:AeSkoH3ziPvapsuyI07TWY4oGxluAjntX+pF4PJ2jy0= -cloud.google.com/go/tpu v1.8.4/go.mod h1:ul0cyWSHr6jHGZYElZe6HvQn35VY93RAlwpDiSBRnPA= cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= -cloud.google.com/go/translate v1.12.7/go.mod h1:wwJp14NZyWvcrFANhIXutXj0pOBkYciBHwSlUOykcjI= -cloud.google.com/go/video v1.27.1/go.mod h1:xzfAC77B4vtnbi/TT3UUxEjCa/+Ehy5EA8w470ytOig= -cloud.google.com/go/videointelligence v1.12.7/go.mod h1:XAk5hCMY+GihxJ55jNoMdwdXSNZnCl3wGs2+94gK7MA= -cloud.google.com/go/vision/v2 v2.9.6/go.mod h1:lJC+vP15D5znJvHQYjEoTKnpToX1L93BUlvBmzM0gyg= -cloud.google.com/go/vmmigration v1.10.0/go.mod h1:LDztCWEb+RwS1bPg4Xzt0fcJS9kVrFxa3ejhH7OW9vg= -cloud.google.com/go/vmwareengine v1.3.6/go.mod h1:ps0rb+Skgpt9ppHYC0o5DqtJ5ld2FyS8sAqtbHH8t9s= -cloud.google.com/go/vpcaccess v1.8.7/go.mod h1:9RYw5bVvk4Z51Rc8vwXT63yjEiMD/l7XyEaDyrNHgmk= -cloud.google.com/go/webrisk v1.11.2/go.mod h1:yH44GeXz5iz4HFsIlGeoVvnjwnmfbni7Lwj1SelV4f0= -cloud.google.com/go/websecurityscanner v1.7.7/go.mod h1:ng/PzARaus3Bj4Os4LpUnyYHsbtJky1HbBDmz148v1o= -cloud.google.com/go/workflows v1.14.3/go.mod h1:CC9+YdVI2Kvp0L58WajHpEfKJxhrtRh3uQ0SYWcmAk4= -codeberg.org/go-fonts/liberation v0.5.0/go.mod h1:zS/2e1354/mJ4pGzIIaEtm/59VFCFnYC7YV6YdGl5GU= -codeberg.org/go-latex/latex v0.1.0/go.mod h1:LA0q/AyWIYrqVd+A9Upkgsb+IqPcmSTKc9Dny04MHMw= -codeberg.org/go-pdf/fpdf v0.10.0/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU= -cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= @@ -206,21 +94,20 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0/go.mod h1:Pu5Zksi2KrU7LPbZbNINx6fuVrUp/ffvpxdDj+i8LeE= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= -github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.9.1/go.mod h1:NydgUaroiShkgOcb+X6OUdS3RalWBrvDNtOyFHJtsZY= github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery v1.2.0 h1:s0SaQtHigowP0n3Kx4ieV94pNZAHlHhS+xjZyLCSVCQ= github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery v1.2.0/go.mod h1:oI5SPI1vpNJYfP9MPWXthq7jDfh9xTAuQVBKPOu7DPo= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8= -github.com/Azure/go-amqp v1.4.0/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest/to v0.4.1/go.mod h1:EtaofgU4zmtvn1zT2ARsjRFdq9vXx0YWtmElwL+GZ9M= -github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= @@ -231,21 +118,16 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/GoogleCloudPlatform/cloudsql-proxy v1.37.8/go.mod h1:exon/I6I+5u/ab7AHmGh0eCXGoYZO5cjqA3wHJlYFFQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.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/exporter/trace v1.29.0/go.mod h1:rKOFVIPbNs2wZeh7ZeQ0D9p/XLgbNiTr5m7x6KuAshk= 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/GoogleCloudPlatform/opentelemetry-operations-go/propagator v0.53.0/go.mod h1:dtCRwgvytbGKWdlrjMOg9geBoRwRpCYWIOM/JhVsDIc= github.com/IBM/sarama v1.46.3 h1:njRsX6jNlnR+ClJ8XmkO+CM4unbrNr/2vB5KK6UA+IE= github.com/IBM/sarama v1.46.3/go.mod h1:GTUYiF9DMOZVe3FwyGT+dtSPceGFIgA+sPc5u6CBwko= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= @@ -256,19 +138,15 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/RaveNoX/go-jsonmerge v1.0.0 h1:2e0nqnadoGUP8rAvcA0hkQelZreVO5X3BHomT2XMrAk= github.com/RaveNoX/go-jsonmerge v1.0.0/go.mod h1:qYM/NA77LhO4h51JJM7Z+xBU3ovqrNIACZe+SkSNVFo= -github.com/Snawoot/go-http-digest-auth-client v1.1.3/go.mod h1:WiwNiPXTRGyjTGpBtSQJlM2wDPRRPpFGhMkMWpV4uqg= github.com/TomOnTime/utfutil v1.0.0 h1:/0Ivgo2OjXJxo8i7zgvs7ewSFZMLwCRGm3P5Umowb90= github.com/TomOnTime/utfutil v1.0.0/go.mod h1:l9lZmOniizVSuIliSkEf87qivMRlSNzbdBFKjuLRg1c= -github.com/Venafi/vcert/v5 v5.12.2/go.mod h1:x3l0pB0q0E6wuhPe7nzfkUEwwraK7amnBWQ4LtT1bbw= github.com/WinterYukky/gorm-extra-clause-plugin v0.4.0 h1:e4gYsN9tNzoBMYKYBaGwwZpSljJhW231+1cBlYwv8YQ= github.com/WinterYukky/gorm-extra-clause-plugin v0.4.0/go.mod h1:jNWq8AymgsVev9Kq6mke0b3o3yzY6bTSwjMDfTvZPPM= -github.com/XSAM/otelsql v0.39.0/go.mod h1:uMOXLUX+wkuAuP0AR3B45NXX7E9lJS2mERa8gqdU8R0= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= @@ -277,15 +155,12 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/akamai/AkamaiOPEN-edgegrid-golang/v12 v12.0.0/go.mod h1:Bf6hnZkloZnfL4I/gFGnMMMdMHiu/ERnSOWtFgnodDk= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/participle/v2 v2.1.0/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -294,17 +169,12 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE= github.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA= -github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= -github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= @@ -312,7 +182,6 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdK github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= @@ -324,13 +193,9 @@ github.com/aws/aws-sdk-go-v2/config v1.32.9/go.mod h1:U+fCQ+9QKsLW786BCfEjYRj34V github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= github.com/aws/aws-sdk-go-v2/credentials v1.19.9 h1:sWvTKsyrMlJGEuj/WgrwilpoJ6Xa1+KhIpGdzw7mMU8= github.com/aws/aws-sdk-go-v2/credentials v1.19.9/go.mod h1:+J44MBhmfVY/lETFiKI+klz0Vym2aCmIjqgClMmW82w= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.5/go.mod h1:VNM08cHlOsIbSHRqb6D/M2L4kKXfJv3A2/f0GNbOQSc= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.7.87/go.mod h1:ZeQC4gVarhdcWeM1c90DyBLaBCNhEeAbKUXwVI/byvw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA= -github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.5.13/go.mod h1:RxLhhGmjEidlLTRZyk1BLMigHONURhQakw2//prq+DA= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.3/go.mod h1:br7KA6edAAqDGUYJ+zVVPAyMrPhnN+zdt17yTUT6FPw= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ= @@ -344,15 +209,12 @@ github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0= github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.63.1 h1:l65dmgr7tO26EcHe6WMdseRnFLoJ2nqdkPz1nJdXfaw= github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.63.1/go.mod h1:wvnXh1w1pGS2UpEvPTKSjXYuxiXhuvob/IMaK2AWvek= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.44.0/go.mod h1:mWB0GE1bqcVSvpW7OtFA0sKuHk52+IqtnsYU2jUfYAs= -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.26.0/go.mod h1:He/RikglWUczbkV+fkdpcV/3GdL/rTRNVy7VaUiezMo= github.com/aws/aws-sdk-go-v2/service/eks v1.77.0 h1:Z5mTpmbJKU7jEM7xoXI5tO4Nm0JUZSgVSFkpYuu6Ic0= github.com/aws/aws-sdk-go-v2/service/eks v1.77.0/go.mod h1:Qg678m+87sCuJhcsZojenz8mblYG+Tq86V4m3hjVz0s= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.17/go.mod h1:mC9qMbA6e1pwEq6X3zDGtZRXMG2YaElJkbJlMVHLs5I= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU= @@ -360,17 +222,14 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g= github.com/aws/aws-sdk-go-v2/service/kms v1.49.5 h1:DKibav4XF66XSeaXcrn9GlWGHos6D/vJ4r7jsK7z5CE= github.com/aws/aws-sdk-go-v2/service/kms v1.49.5/go.mod h1:1SdcmEGUEQE1mrU2sIgeHtcMSxHuybhPvuEPANzIDfI= -github.com/aws/aws-sdk-go-v2/service/route53 v1.58.4/go.mod h1:xNLZLn4SusktBQ5moqUOgiDKGz3a7vHwF4W0KD+WBPc= github.com/aws/aws-sdk-go-v2/service/s3 v1.95.1 h1:C2dUPSnEpy4voWFIq3JNd8gN0Y5vYGDo44eUE58a/p8= github.com/aws/aws-sdk-go-v2/service/s3 v1.95.1/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7/go.mod h1:1X1NotbcGHH7PCQJ98PsExSxsJj/VWzz8MfFz43+02M= github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y= github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M= github.com/aws/aws-sdk-go-v2/service/sns v1.39.11 h1:Ke7RS0NuP9Xwk31prXYcFGA1Qfn8QmNWcxyjKPcXZdc= github.com/aws/aws-sdk-go-v2/service/sns v1.39.11/go.mod h1:hdZDKzao0PBfJJygT7T92x2uVcWc/htqlhrjFIjnHDM= github.com/aws/aws-sdk-go-v2/service/sqs v1.42.21 h1:Oa0IhwDLVrcBHDlNo1aosG4CxO4HyvzDV5xUWqWcBc0= github.com/aws/aws-sdk-go-v2/service/sqs v1.42.21/go.mod h1:t98Ssq+qtXKXl2SFtaSkuT6X42FSM//fnO6sfq5RqGM= -github.com/aws/aws-sdk-go-v2/service/ssm v1.60.1/go.mod h1:IyVabkWrs8SNdOEZLyFFcW9bUltV4G6OQS0s6H20PHg= github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 h1:+VTRawC4iVY58pS/lzpo0lnoa/SYNGF4/B/3/U5ro8Y= github.com/aws/aws-sdk-go-v2/service/sso v1.30.10/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw= @@ -389,13 +248,10 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= @@ -406,8 +262,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= github.com/casbin/casbin/v2 v2.135.0 h1:6BLkMQiGotYyS5yYeWgW19vxqugUlvHFkFiLnLR/bxk= github.com/casbin/casbin/v2 v2.135.0/go.mod h1:FmcfntdXLTcYXv/hxgNntcRPqAbwOG9xsism0yXT+18= github.com/casbin/gorm-adapter/v3 v3.39.0 h1:k15txH6vE4796MuA+LFcU8I1vMjutklyzMXfjDz7lzo= @@ -415,6 +269,7 @@ github.com/casbin/gorm-adapter/v3 v3.39.0/go.mod h1:kjXoK8MqA3E/CcqEF2l3SCkhJj1Y github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= github.com/casbin/govaluate v1.10.0 h1:ffGw51/hYH3w3rZcxO/KcaUIDOLP84w7nsidMVgaDG0= github.com/casbin/govaluate v1.10.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= @@ -426,7 +281,6 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY= github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= @@ -441,15 +295,12 @@ github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSg github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= 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/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/clarkmcc/gorm-sqlite v0.0.0-20240426202654-00ed082c0311 h1://GDWpsQ8pSg8u8SCavanukwPu5yE0Rz3uu7CuFVfFc= github.com/clarkmcc/gorm-sqlite v0.0.0-20240426202654-00ed082c0311/go.mod h1:HrR53jwmQF7sTyNxEJ3rqfx9sRVnaTUqIo1nXn0KRho= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= -github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= @@ -459,11 +310,18 @@ github.com/cncf/xds/go v0.0.0-20260121142036-a486691bba94 h1:kkHPnzHm5Ln7WA0XYjr github.com/cncf/xds/go v0.0.0-20260121142036-a486691bba94/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= -github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -479,14 +337,18 @@ github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+Zlfu github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/digitalocean/godo v1.165.1/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= @@ -495,6 +357,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4A github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/eko/gocache/lib/v4 v4.2.3 h1:s78TFqEGAH3SbzP4N40D755JYT/aaGFKEPrsUtC1chU= github.com/eko/gocache/lib/v4 v4.2.3/go.mod h1:Zus8mwmaPu1VYOzfomb+Dvx2wV7fT5jDRbHYtQM6MEY= github.com/eko/gocache/store/go_cache/v4 v4.2.4 h1:toHpoIi4HhuXYv1bFOh5FiEQhpli4sWoSAN74j3/MXw= @@ -510,8 +374,8 @@ github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT 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.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= +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= @@ -519,9 +383,6 @@ github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJP 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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/exaring/otelpgx v0.10.0 h1:NGGegdoBQM3jNZDKG8ENhigUcgBN7d7943L0YlcIpZc= github.com/exaring/otelpgx v0.10.0/go.mod h1:R5/M5LWsPPBZc1SrRE5e0DiU48bI78C1/GPTWs6I66U= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -548,7 +409,6 @@ github.com/flanksource/sandbox-runtime v1.0.1 h1:zBzNx9GoZILo1ot4qI2wd/gqny0vejv github.com/flanksource/sandbox-runtime v1.0.1/go.mod h1:HCeOqw4QQOpvzDeN3hMdQpxIZ9yrp0/5ziXjiiOw5ec= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= @@ -566,10 +426,8 @@ github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZ github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= -github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= @@ -581,7 +439,6 @@ github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiy github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw= github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= @@ -591,7 +448,6 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -601,7 +457,6 @@ 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= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -641,7 +496,6 @@ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxE github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -655,7 +509,6 @@ github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUW github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccmack/gocc v1.0.2/go.mod h1:LXX2tFVUggS/Zgx/ICPOr3MLyusuM7EcbfkPvNsjdO8= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= @@ -664,8 +517,6 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= @@ -675,9 +526,7 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -719,7 +568,6 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= -github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k= github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs= github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= @@ -741,7 +589,6 @@ 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/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs= github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw= -github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/go-replayers/grpcreplay v1.3.0 h1:1Keyy0m1sIpqstQmgz307zhiJ1pV4uIlFds5weTmxbo= @@ -749,7 +596,6 @@ github.com/google/go-replayers/grpcreplay v1.3.0/go.mod h1:v6NgKtkijC0d3e3RW8il6 github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gops v0.3.29 h1:n98J2qSOK1NJvRjdLDcjgDryjpIBGhbaqph1mXKL0rY= github.com/google/gops v0.3.29/go.mod h1:8N3jZftuPazvUwtYY/ncG4iPrjp15ysNKLfq+QQPiwc= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= @@ -769,7 +615,6 @@ github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmI github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -792,27 +637,12 @@ github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6 github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.5 h1:jP1RStw811EvUDzsUQ9oESqw2e4RqCjSAD9qIL8eMns= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.5/go.mod h1:WXNBZ64q3+ZUemCMXD9kYnr56H7CgZxDBHCVwstfl3s= -github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hairyhenderson/toml v0.4.2-0.20210923231440-40456b8e66cf h1:I1sbT4ZbIt9i+hB1zfKw2mE8C12TuGxPiW7YmtLbPa4= github.com/hairyhenderson/toml v0.4.2-0.20210923231440-40456b8e66cf/go.mod h1:jDHmWDKZY6MIIYltYYfW4Rs7hQ50oS4qf/6spSiZAxY= github.com/hairyhenderson/yaml v0.0.0-20220618171115-2d35fca545ce h1:cVkYhlWAxwuS2/Yp6qPtcl0fGpcWxuZNonywHZ6/I+s= github.com/hairyhenderson/yaml v0.0.0-20220618171115-2d35fca545ce/go.mod h1:7TyiGlHI+IO+iJbqRZ82QbFtvgj/AIcFm5qc9DLn7Kc= -github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6/go.mod h1:y+HSOcOGB48PkUxNyLAiCiY6rEENu+E+Ss4LG8QHwf4= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.1/go.mod h1:hH8rgXHh9fPSDPerG6WzABHsHF+9ZpLhRI1LPk4JZ8c= -github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -821,26 +651,19 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= -github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= -github.com/hashicorp/vault/sdk v0.20.0/go.mod h1:xEjAt/n/2lHBAkYiRPRmvf1d5B6HlisPh2pELlRCosk= github.com/henvic/httpretty v0.1.4 h1:Jo7uwIRWVFxkqOnErcoYfH90o3ddQyVrSANeS4cxYmU= github.com/henvic/httpretty v0.1.4/go.mod h1:Dn60sQTZfbt2dYsdUSNsCljyF4AfdqnuJFDLJA1I4AM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI= github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/itchyny/go-yaml v0.0.0-20251001235044-fca9a0999f15/go.mod h1:Tmbz8uw5I/I6NvVpEGuhzlElCGS5hPoXJkt7l+ul6LE= github.com/itchyny/gojq v0.12.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc= github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg= github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA= @@ -928,8 +751,6 @@ github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= @@ -945,13 +766,11 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= @@ -997,7 +816,8 @@ github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQ github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= -github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= @@ -1011,11 +831,9 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/mattn/go-sqlite3 v1.14.30/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= @@ -1023,19 +841,30 @@ github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU= github.com/microsoft/go-mssqldb v1.9.6 h1:1MNQg5UiSsokiPz3++K2KPx4moKrwIqly1wv+RyCKTw= github.com/microsoft/go-mssqldb v1.9.6/go.mod h1:yYMPDufyoF2vVuVCUGtZARr06DKFIhMrluTcgWlXpr4= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1046,8 +875,8 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -1069,7 +898,6 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg= github.com/ohler55/ojg v1.28.0 h1:8xClBgMIRRJGDUC9xNe7NprP4kD2C3mQMeon3wY4KXA= github.com/ohler55/ojg v1.28.0/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= @@ -1082,25 +910,23 @@ github.com/olekukonko/ll v0.1.7 h1:WyK1YZwOTUKHEXZz3VydBDT5t3zDqa9yI8iJg5PHon4= github.com/olekukonko/ll v0.1.7/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw= github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA= github.com/olekukonko/tablewriter v1.1.3/go.mod h1:9VU0knjhmMkXjnMKrZ3+L2JhhtsQ/L38BbL3CRNE8tM= -github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0= github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc= github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8= github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c= github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM= -github.com/orisano/pixelmatch v0.0.0-20230914042517-fa304d1dc785/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0= github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= @@ -1179,7 +1005,6 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rodaine/table v1.3.0 h1:4/3S3SVkHnVZX91EHFvAMV7K42AnJ0XuymRR2C5HlGE= github.com/rodaine/table v1.3.0/go.mod h1:47zRsHar4zw0jgxGxL9YtFfs7EGN6B/TaS+/Dmk4WxU= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= @@ -1189,7 +1014,6 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/samber/oops v1.21.0 h1:18atcO4oEigNFuGXqr3NZWZ6P0XOSEXyBSAMXdQRxTc= @@ -1201,6 +1025,8 @@ github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= +github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= +github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/shoenig/go-m1cpu v0.1.7 h1:C76Yd0ObKR82W4vhfjZiCp0HxcSZ8Nqd84v+HZ0qyI0= github.com/shoenig/go-m1cpu v0.1.7/go.mod h1:KkDOw6m3ZJQAPHbrzkZki4hnx+pDRR1Lo+ldA56wD5w= github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= @@ -1218,9 +1044,6 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -1229,7 +1052,6 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A 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/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1250,7 +1072,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl 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/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= +github.com/testcontainers/testcontainers-go v0.41.0 h1:mfpsD0D36YgkxGj2LrIyxuwQ9i2wCKAD+ESsYM1wais= +github.com/testcontainers/testcontainers-go v0.41.0/go.mod h1:pdFrEIfaPl24zmBjerWTTYaY0M6UHsqA1YSvsoU40MI= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -1274,7 +1097,6 @@ github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYI github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= -github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= @@ -1290,9 +1112,6 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= -github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -1309,8 +1128,6 @@ github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1 github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= @@ -1319,7 +1136,6 @@ github.com/xuri/excelize/v2 v2.10.1 h1:V62UlqopMqha3kOpnlHy2CcRVw1V8E63jFoWUmMzx github.com/xuri/excelize/v2 v2.10.1/go.mod h1:iG5tARpgaEeIhTqt3/fgXCGoBRt4hNXgCp3tfXKoOIc= github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE= github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1337,18 +1153,9 @@ github.com/zclconf/go-cty-yaml v1.2.0 h1:GDyL4+e/Qe/S0B7YaecMLbVvAR/Mp21CXMOSiCT github.com/zclconf/go-cty-yaml v1.2.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.einride.tech/aip v0.73.0/go.mod h1:Mj7rFbmXEgw0dq1dqJ7JGMvYCZZVxmGOR3S4ZcV5LvQ= -go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= -go.etcd.io/etcd/api/v3 v3.6.5/go.mod h1:ob0/oWA/UQQlT1BmaEkWQzI0sJ1M0Et0mMpaABxguOQ= -go.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk= -go.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo= -go.etcd.io/etcd/pkg/v3 v3.6.5/go.mod h1:uqrXrzmMIJDEy5j00bCqhVLzR5jEJIwDp5wTlLwPGOU= -go.etcd.io/etcd/server/v3 v3.6.5/go.mod h1:PLuhyVXz8WWRhzXDsl3A3zv/+aK9e4A9lpQkqawIaH0= -go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1358,33 +1165,30 @@ 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/aws/ec2 v1.37.0/go.mod h1:gs3y8jvJscW5D+FzrZvJZEsGj+xlMCF0S1x4R6ktiNo= 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.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= -go.opentelemetry.io/contrib/propagators/aws v1.37.0/go.mod h1:Cy8Hk2E2iSGEbsLnPUdeigrexaAOAGIAmBFK919EQs0= go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 h1:inYW9ZhgqiDqh6BioM7DVHHzEGVq76Db5897WLGZ5Go= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0/go.mod h1:Izur+Wt8gClgMJqO/cZ8wdeeMryJ/xxiOVgFSSfpDTY= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 h1:wm/Q0GAAykXv83wzcKzGGqAnnfLFyFe7RslekZuv+VI= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0/go.mod h1:ra3Pa40+oKjvYh+ZD3EdxFZZB0xdMfuileHAm4nNN7w= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g= go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= @@ -1400,13 +1204,10 @@ go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -1533,8 +1334,8 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -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/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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1597,6 +1398,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1696,8 +1498,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= -golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1706,11 +1506,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -gonum.org/v1/plot v0.15.2/go.mod h1:DX+x+DWso3LTha+AdkJEv5Txvi+Tql3KAGkehP0/Ubg= -gonum.org/v1/tools v0.0.0-20200318103217-c168b003ce8c/go.mod h1:fy6Otjqbk477ELp8IXTpw1cObQtLbRCBVonY+bTTfcM= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1736,7 +1533,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1770,7 +1566,6 @@ google.golang.org/genproto v0.0.0-20260126211449-d11affda4bed h1:qZW022+WR7NN5TK google.golang.org/genproto v0.0.0-20260126211449-d11affda4bed/go.mod h1:SpjiK7gGN2j/djoQMxLl3QOe/J/XxNzC5M+YLecVVWU= google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4= google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20260120174246-409b4a993575/go.mod h1:Tej9lWiwVvQJP+b43pjJIsr/3mZycXWCIyoiXmbFf40= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1786,9 +1581,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= -google.golang.org/grpc/examples v0.0.0-20250407062114-b368379ef8f6/go.mod h1:6ytKWczdvnpnO+m+JiG9NjEDzR1FJfsnmJdG7B8QVZ8= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= 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= @@ -1819,9 +1613,6 @@ gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWM gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ= gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -1841,7 +1632,6 @@ gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= -gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0= gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00= gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= @@ -1868,27 +1658,18 @@ k8s.io/apiextensions-apiserver v0.35.2 h1:iyStXHoJZsUXPh/nFAsjC29rjJWdSgUmG1XpAp k8s.io/apiextensions-apiserver v0.35.2/go.mod h1:OdyGvcO1FtMDWQ+rRh/Ei3b6X3g2+ZDHd0MSRGeS8rU= k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8= k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/apiserver v0.35.2/go.mod h1:CROJUAu0tfjZLyYgSeBsBan2T7LUJGh0ucWwTCSSk7g= k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o= k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g= -k8s.io/code-generator v0.35.2/go.mod h1:id4XLCm0yAQq5nlvyfAKibMOKnMjzlesAwGw6kM3Adc= -k8s.io/component-base v0.35.2/go.mod h1:B1iBJjooe6xIJYUucAxb26RwhAjzx0gHnqO9htWIX+0= -k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kms v0.35.2/go.mod h1:VT+4ekZAdrZDMgShK37vvlyHUVhwI9t/9tvh0AyCWmQ= -k8s.io/kube-aggregator v0.34.1/go.mod h1:RU8j+5ERfp0h+gIvWtxRPfsa5nK7rboDm8RST8BJfYQ= k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf h1:btPscg4cMql0XdYK2jLsJcNEKmACJz8l+U7geC06FiM= k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc= -lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM= modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= @@ -1916,21 +1697,15 @@ modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/gateway-api v1.5.0 h1:duoo14Ky/fJXpjpmyMISE2RTBGnfCg8zICfTYLTnBJA= sigs.k8s.io/gateway-api v1.5.0/go.mod h1:GvCETiaMAlLym5CovLxGjS0NysqFk3+Yuq3/rh6QL2o= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= -software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/logs/gcpcloudlogging/cloud_logging.go b/logs/gcpcloudlogging/cloud_logging.go index 52d06025b..19caf1900 100644 --- a/logs/gcpcloudlogging/cloud_logging.go +++ b/logs/gcpcloudlogging/cloud_logging.go @@ -42,7 +42,7 @@ func New(ctx context.Context, conn connection.GCPConnection, mappingConfig *logs } if conn.Credentials != nil && !conn.Credentials.IsEmpty() { - c, err := google.CredentialsFromJSON(ctx, []byte(conn.Credentials.ValueStatic)) + c, err := google.CredentialsFromJSON(ctx, []byte(conn.Credentials.ValueStatic)) //nolint:staticcheck if err != nil { return nil, fmt.Errorf("%w", err) } diff --git a/models/artifacts.go b/models/artifacts.go index 89e8282aa..77f0d6f3f 100644 --- a/models/artifacts.go +++ b/models/artifacts.go @@ -1,8 +1,16 @@ package models import ( + "bytes" + "compress/gzip" + "crypto/sha256" + "encoding/base64" + "fmt" + "io" "time" + "github.com/flanksource/clicky" + "github.com/flanksource/clicky/api" "github.com/google/uuid" "github.com/samber/lo" "gorm.io/gorm" @@ -22,6 +30,8 @@ type Artifact struct { Size int64 `json:"size"` // Size in bytes ContentType string `json:"content_type,omitempty"` Checksum string `json:"checksum"` + Content []byte `json:"-" gorm:"type:bytea"` + CompressionType string `json:"compression_type,omitempty"` CreatedAt time.Time `json:"created_at" yaml:"created_at" time_format:"postgres_timestamp"` UpdatedAt time.Time `json:"updated_at" yaml:"updated_at" time_format:"postgres_timestamp"` DeletedAt *time.Time `json:"deleted_at,omitempty" yaml:"deleted_at,omitempty" time_format:"postgres_timestamp"` @@ -36,6 +46,175 @@ func (a Artifact) PK() string { return a.ID.String() } +func (a Artifact) Pretty() api.Text { + s := clicky.Text(a.Filename, "font-bold") + if a.ContentType != "" { + s = s.AddText(" "+a.ContentType, "text-gray-500") + } + s = s.AddText(fmt.Sprintf(" (%s)", formatBytes(a.Size)), "text-gray-400") + if a.Checksum != "" { + short := a.Checksum + if len(short) > 12 { + short = short[:12] + } + s = s.AddText(" sha:"+short, "text-gray-400") + } + return s +} + +func (a Artifact) Columns() []api.ColumnDef { + return []api.ColumnDef{ + clicky.Column("Filename").Build(), + clicky.Column("Path").Build(), + clicky.Column("ContentType").Build(), + clicky.Column("Size").Build(), + clicky.Column("Checksum").Build(), + clicky.Column("CreatedAt").Build(), + } +} + +func (a Artifact) Row() map[string]any { + checksum := a.Checksum + if len(checksum) > 12 { + checksum = checksum[:12] + "…" + } + return map[string]any{ + "Filename": clicky.Text(a.Filename), + "Path": clicky.Text(a.Path), + "ContentType": clicky.Text(a.ContentType), + "Size": clicky.Text(formatBytes(a.Size)), + "Checksum": clicky.Text(checksum), + "CreatedAt": clicky.Text(a.CreatedAt.Format(time.RFC3339)), + } +} + +func (a Artifact) RowDetail() api.Textable { + if a.ContentType == "" || !isImageContentType(a.ContentType) { + return nil + } + url := fmt.Sprintf("/artifacts/download/%s", a.ID) + img := artifactImage{ + html: fmt.Sprintf( + `%s`, + url, a.Filename, + ), + } + if data, err := a.GetContent(); err == nil && len(data) > 0 { + b64 := base64.StdEncoding.EncodeToString(data) + img.staticHTML = fmt.Sprintf( + `%s`, + a.ContentType, b64, a.Filename, + ) + } + return img +} + +func isImageContentType(ct string) bool { + for _, prefix := range []string{"image/png", "image/jpeg", "image/gif", "image/webp", "image/svg"} { + if ct == prefix { + return true + } + } + return false +} + +type artifactImage struct { + html string + staticHTML string +} + +func (s artifactImage) String() string { return "[image]" } +func (s artifactImage) ANSI() string { return "[image]" } +func (s artifactImage) HTML() string { return s.html } +func (s artifactImage) Markdown() string { return "[image]" } +func (s artifactImage) StaticHTML() string { + if s.staticHTML != "" { + return s.staticHTML + } + return s.html +} + +func formatBytes(b int64) string { + switch { + case b >= 1<<20: + return fmt.Sprintf("%.1f MB", float64(b)/(1<<20)) + case b >= 1<<10: + return fmt.Sprintf("%.1f KB", float64(b)/(1<<10)) + default: + return fmt.Sprintf("%d B", b) + } +} + +func (a Artifact) IsInline() bool { + return a.Content != nil +} + +// SetContent compresses data using the given compression type and sets the +// Content and CompressionType fields. maxSize is checked against the +// post-compression size; returns an error if exceeded. +func (a *Artifact) SetContent(data []byte, compressionType string, maxSize int) error { + compressed, err := compress(data, compressionType) + if err != nil { + return fmt.Errorf("compressing artifact: %w", err) + } + if maxSize > 0 && len(compressed) > maxSize { + return fmt.Errorf("compressed artifact size %d exceeds max %d", len(compressed), maxSize) + } + a.Content = compressed + a.CompressionType = compressionType + a.Size = int64(len(data)) + a.Checksum = fmt.Sprintf("%x", sha256Sum(data)) + return nil +} + +// GetContent decompresses and returns the inline content. +func (a Artifact) GetContent() ([]byte, error) { + if a.Content == nil { + return nil, nil + } + return decompress(a.Content, a.CompressionType) +} + +func sha256Sum(data []byte) []byte { + h := sha256.Sum256(data) + return h[:] +} + +func compress(data []byte, compressionType string) ([]byte, error) { + switch compressionType { + case "gzip": + var buf bytes.Buffer + w := gzip.NewWriter(&buf) + if _, err := w.Write(data); err != nil { + return nil, err + } + if err := w.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil + case "", "none": + return data, nil + default: + return nil, fmt.Errorf("unsupported compression type: %s", compressionType) + } +} + +func decompress(data []byte, compressionType string) ([]byte, error) { + switch compressionType { + case "gzip": + r, err := gzip.NewReader(bytes.NewReader(data)) + if err != nil { + return nil, err + } + defer r.Close() + return io.ReadAll(r) + case "", "none": + return data, nil + default: + return nil, fmt.Errorf("unsupported compression type: %s", compressionType) + } +} + func (t Artifact) GetUnpushed(db *gorm.DB) ([]DBTable, error) { var items []Artifact err := db.Where("is_pushed IS FALSE").Find(&items).Error diff --git a/models/artifacts_test.go b/models/artifacts_test.go new file mode 100644 index 000000000..34d58afbd --- /dev/null +++ b/models/artifacts_test.go @@ -0,0 +1,104 @@ +package models + +import ( + "bytes" + "strings" + "testing" + + "github.com/onsi/gomega" +) + +func TestArtifact_IsInline(t *testing.T) { + g := gomega.NewWithT(t) + + g.Expect(Artifact{}.IsInline()).To(gomega.BeFalse()) + g.Expect(Artifact{Content: []byte("data")}.IsInline()).To(gomega.BeTrue()) +} + +func TestArtifact_SetContent_NoCompression(t *testing.T) { + g := gomega.NewWithT(t) + + a := &Artifact{} + data := []byte("hello world") + g.Expect(a.SetContent(data, "none", 0)).To(gomega.Succeed()) + g.Expect(a.Content).To(gomega.Equal(data)) + g.Expect(a.CompressionType).To(gomega.Equal("none")) + g.Expect(a.Size).To(gomega.Equal(int64(len(data)))) + g.Expect(a.IsInline()).To(gomega.BeTrue()) +} + +func TestArtifact_SetContent_Gzip(t *testing.T) { + g := gomega.NewWithT(t) + + a := &Artifact{} + data := []byte(strings.Repeat("abcdefghij", 100)) + g.Expect(a.SetContent(data, "gzip", 0)).To(gomega.Succeed()) + + g.Expect(a.CompressionType).To(gomega.Equal("gzip")) + g.Expect(a.Size).To(gomega.Equal(int64(len(data)))) + g.Expect(len(a.Content)).To(gomega.BeNumerically("<", len(data))) +} + +func TestArtifact_SetContent_RoundTrip(t *testing.T) { + g := gomega.NewWithT(t) + + original := []byte(strings.Repeat("test data for compression ", 50)) + + for _, ct := range []string{"none", "gzip"} { + a := &Artifact{} + g.Expect(a.SetContent(original, ct, 0)).To(gomega.Succeed()) + + got, err := a.GetContent() + g.Expect(err).To(gomega.Succeed()) + g.Expect(got).To(gomega.Equal(original)) + } +} + +func TestArtifact_SetContent_MaxSize_PostCompression(t *testing.T) { + g := gomega.NewWithT(t) + + // Highly compressible data: 10KB of zeros compresses to ~30 bytes + data := bytes.Repeat([]byte{0}, 10*1024) + + // With a maxSize larger than compressed output, should succeed + a := &Artifact{} + g.Expect(a.SetContent(data, "gzip", 1024)).To(gomega.Succeed()) + g.Expect(len(a.Content)).To(gomega.BeNumerically("<", 1024)) + + // With no compression, 10KB exceeds 1KB max — should fail + a2 := &Artifact{} + err := a2.SetContent(data, "none", 1024) + g.Expect(err).To(gomega.HaveOccurred()) + g.Expect(err.Error()).To(gomega.ContainSubstring("exceeds max")) +} + +func TestArtifact_SetContent_UnsupportedCompression(t *testing.T) { + g := gomega.NewWithT(t) + + a := &Artifact{} + err := a.SetContent([]byte("data"), "lz4", 0) + g.Expect(err).To(gomega.HaveOccurred()) + g.Expect(err.Error()).To(gomega.ContainSubstring("unsupported compression")) +} + +func TestArtifact_GetContent_Nil(t *testing.T) { + g := gomega.NewWithT(t) + + a := Artifact{} + got, err := a.GetContent() + g.Expect(err).To(gomega.Succeed()) + g.Expect(got).To(gomega.BeNil()) +} + +func TestArtifact_SetContent_Checksum(t *testing.T) { + g := gomega.NewWithT(t) + + a1 := &Artifact{} + a2 := &Artifact{} + g.Expect(a1.SetContent([]byte("hello"), "gzip", 0)).To(gomega.Succeed()) + g.Expect(a2.SetContent([]byte("hello"), "none", 0)).To(gomega.Succeed()) + + // Checksum is on original data, so should match regardless of compression + g.Expect(a1.Checksum).To(gomega.Equal(a2.Checksum)) + g.Expect(a1.Checksum).ToNot(gomega.BeEmpty()) +} diff --git a/schema/artifacts.hcl b/schema/artifacts.hcl index 55d6dc3d0..b461cd555 100644 --- a/schema/artifacts.hcl +++ b/schema/artifacts.hcl @@ -42,6 +42,16 @@ table "artifacts" { null = false type = text } + column "content" { + null = true + type = bytea + comment = "inline blob storage for artifact data when no external connection is configured" + } + column "compression_type" { + null = true + type = text + comment = "compression algorithm applied to content: gzip, zstd, or none" + } column "is_pushed" { null = false default = false diff --git a/tests/artifacts_test.go b/tests/artifacts_test.go new file mode 100644 index 000000000..b2d7caa20 --- /dev/null +++ b/tests/artifacts_test.go @@ -0,0 +1,234 @@ +package tests + +import ( + "bytes" + "io" + "strings" + + "github.com/flanksource/duty/artifact" + "github.com/flanksource/duty/models" + "github.com/google/uuid" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Artifacts", Ordered, func() { + var inlineArtifactID uuid.UUID + + Describe("inline blob storage", func() { + It("should store and retrieve uncompressed content", func() { + a := models.Artifact{ + Path: "/test/inline", + Filename: "uncompressed.txt", + Checksum: "placeholder", + } + data := []byte("hello world uncompressed") + Expect(a.SetContent(data, "none", 0)).To(Succeed()) + + err := DefaultContext.DB().Create(&a).Error + Expect(err).ToNot(HaveOccurred()) + Expect(a.ID).ToNot(Equal(uuid.Nil)) + + var fetched models.Artifact + err = DefaultContext.DB().Where("id = ?", a.ID).First(&fetched).Error + Expect(err).ToNot(HaveOccurred()) + + got, err := fetched.GetContent() + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(data)) + Expect(fetched.IsInline()).To(BeTrue()) + Expect(fetched.CompressionType).To(Equal("none")) + Expect(fetched.Size).To(Equal(int64(len(data)))) + }) + + It("should store and retrieve gzip compressed content", func() { + a := models.Artifact{ + Path: "/test/inline", + Filename: "compressed.txt", + Checksum: "placeholder", + } + data := []byte(strings.Repeat("compressible data ", 100)) + Expect(a.SetContent(data, "gzip", 0)).To(Succeed()) + + err := DefaultContext.DB().Create(&a).Error + Expect(err).ToNot(HaveOccurred()) + inlineArtifactID = a.ID + + var fetched models.Artifact + err = DefaultContext.DB().Where("id = ?", a.ID).First(&fetched).Error + Expect(err).ToNot(HaveOccurred()) + + Expect(fetched.CompressionType).To(Equal("gzip")) + Expect(fetched.Size).To(Equal(int64(len(data)))) + Expect(len(fetched.Content)).To(BeNumerically("<", len(data))) + + got, err := fetched.GetContent() + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(data)) + }) + + It("should store artifacts without inline content (external)", func() { + a := models.Artifact{ + Path: "/test/external", + Filename: "external.txt", + Size: 4096, + Checksum: "abc123", + ConnectionID: uuid.New(), + } + + err := DefaultContext.DB().Create(&a).Error + Expect(err).ToNot(HaveOccurred()) + + var fetched models.Artifact + err = DefaultContext.DB().Where("id = ?", a.ID).First(&fetched).Error + Expect(err).ToNot(HaveOccurred()) + + Expect(fetched.IsInline()).To(BeFalse()) + Expect(fetched.Content).To(BeNil()) + Expect(fetched.CompressionType).To(BeEmpty()) + }) + }) + + Describe("migration from inline to external", func() { + It("should simulate migrating inline content to external storage", func() { + var a models.Artifact + err := DefaultContext.DB().Where("id = ?", inlineArtifactID).First(&a).Error + Expect(err).ToNot(HaveOccurred()) + Expect(a.IsInline()).To(BeTrue()) + + original, err := a.GetContent() + Expect(err).ToNot(HaveOccurred()) + Expect(original).ToNot(BeEmpty()) + + a.ConnectionID = uuid.New() + a.Content = nil + a.CompressionType = "" + + err = DefaultContext.DB().Save(&a).Error + Expect(err).ToNot(HaveOccurred()) + + var fetched models.Artifact + err = DefaultContext.DB().Where("id = ?", inlineArtifactID).First(&fetched).Error + Expect(err).ToNot(HaveOccurred()) + Expect(fetched.IsInline()).To(BeFalse()) + Expect(fetched.Content).To(BeNil()) + Expect(fetched.ConnectionID).ToNot(Equal(uuid.Nil)) + }) + }) + + Describe("migration from external to inline", func() { + It("should simulate migrating external content to inline storage", func() { + a := models.Artifact{ + Path: "/test/ext-to-inline", + Filename: "migrate-me.txt", + Size: 100, + Checksum: "old-checksum", + ConnectionID: uuid.New(), + } + err := DefaultContext.DB().Create(&a).Error + Expect(err).ToNot(HaveOccurred()) + + data := []byte(strings.Repeat("migrated content ", 20)) + Expect(a.SetContent(data, "gzip", 0)).To(Succeed()) + a.ConnectionID = uuid.Nil + + err = DefaultContext.DB().Save(&a).Error + Expect(err).ToNot(HaveOccurred()) + + var fetched models.Artifact + err = DefaultContext.DB().Where("id = ?", a.ID).First(&fetched).Error + Expect(err).ToNot(HaveOccurred()) + Expect(fetched.IsInline()).To(BeTrue()) + Expect(fetched.ConnectionID).To(Equal(uuid.Nil)) + + got, err := fetched.GetContent() + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(data)) + }) + }) + + Describe("BlobStore", func() { + It("should write and read via BlobStore interface", func() { + store := artifact.NewBlobStore( + artifact.NewInlineStore(DefaultContext.DB()), + DefaultContext.DB(), "inline", + ) + + data := []byte(strings.Repeat("inline store test data ", 50)) + artData := artifact.Data{ + Content: io.NopCloser(bytes.NewReader(data)), + Filename: "/test/blobstore/write-read.txt", + } + + a, err := store.Write(artData, &models.Artifact{}) + Expect(err).ToNot(HaveOccurred()) + Expect(a.Filename).To(Equal("/test/blobstore/write-read.txt")) + Expect(a.Size).To(Equal(int64(len(data)))) + Expect(a.Checksum).ToNot(BeEmpty()) + + result, err := store.Read(a.ID) + Expect(err).ToNot(HaveOccurred()) + defer result.Content.Close() + + got, err := io.ReadAll(result.Content) + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(data)) + }) + + It("should respect max size post-compression", func() { + inlineFS := artifact.NewInlineStore(DefaultContext.DB()). + WithMaxSize(10). + WithCompression("none") + store := artifact.NewBlobStore(inlineFS, DefaultContext.DB(), "inline") + + data := []byte(strings.Repeat("x", 200)) + artData := artifact.Data{ + Content: io.NopCloser(bytes.NewReader(data)), + Filename: "/test/blobstore/too-big.txt", + } + _, err := store.Write(artData, &models.Artifact{}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("exceeds max")) + }) + + It("should return Pretty() on Data", func() { + d := artifact.Data{ + Filename: "test.json", + ContentType: "application/json", + ContentLength: 1024, + Checksum: "abcdef1234567890", + } + pretty := d.Pretty().String() + Expect(pretty).To(ContainSubstring("test.json")) + Expect(pretty).To(ContainSubstring("abcdef12")) + }) + }) + + Describe("context.Blobs()", func() { + It("should return inline store when no connection is configured", func() { + store, err := DefaultContext.Blobs() + Expect(err).ToNot(HaveOccurred()) + Expect(store).ToNot(BeNil()) + defer store.Close() + + data := []byte("context blobs test") + artData := artifact.Data{ + Content: io.NopCloser(bytes.NewReader(data)), + Filename: "/test/context-blobs/test.txt", + } + + a, err := store.Write(artData, &models.Artifact{}) + Expect(err).ToNot(HaveOccurred()) + Expect(a.Checksum).ToNot(BeEmpty()) + + result, err := store.Read(a.ID) + Expect(err).ToNot(HaveOccurred()) + defer result.Content.Close() + + got, err := io.ReadAll(result.Content) + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(data)) + Expect(result.Filename).To(Equal(a.Filename)) + }) + }) +}) diff --git a/tests/e2e-blobs/blobs_test.go b/tests/e2e-blobs/blobs_test.go new file mode 100644 index 000000000..f4685c338 --- /dev/null +++ b/tests/e2e-blobs/blobs_test.go @@ -0,0 +1,151 @@ +package e2e_blobs + +import ( + "bytes" + gocontext "context" + "io" + "strings" + + "github.com/flanksource/commons/logger" + "github.com/flanksource/duty/artifact" + artifactFS "github.com/flanksource/duty/artifact/fs" + "github.com/flanksource/duty/models" + "github.com/flanksource/duty/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +type testBackend struct { + name string + fs artifact.FilesystemRW +} + +var testLogger = logger.GetLogger("e2e-blobs") + +func logged(name string, fs artifact.FilesystemRW) artifact.FilesystemRW { + return artifact.NewLoggedFS(fs, testLogger, name) +} + +func getBackends() []testBackend { + backends := []testBackend{ + {"s3", logged("s3", artifactFS.NewS3FS(s3Client, "test"))}, + {"gcs", logged("gcs", artifactFS.NewGCSFS(gcsClient, "test"))}, + {"azure", logged("azure", artifactFS.NewAzureBlobFS(azureClient, "test"))}, + {"local", logged("local", artifactFS.NewLocalFS(GinkgoT().TempDir()))}, + } + + sshfs, err := artifactFS.NewSSHFS(sftpHost, "foo", "pass") + if err == nil { + backends = append(backends, testBackend{"sftp", logged("sftp", sshfs)}) + } + + smbfs, err := artifactFS.NewSMBFS(smbHost, smbPort, "users", types.Authentication{ + Username: types.EnvVar{ValueStatic: "foo"}, + Password: types.EnvVar{ValueStatic: "pass"}, + }) + if err == nil { + backends = append(backends, testBackend{"smb", logged("smb", smbfs)}) + } + + return backends +} + +var testFiles = []struct { + name string + content string +}{ + {"first.json", `{"name": "first"}`}, + {"second.json", `{"name": "second"}`}, + {"third.yaml", "third"}, + {"record-1.txt", "record-1"}, + {"record-2.txt", "record-2"}, +} + +var _ = Describe("Blob Stores", Label("e2e"), func() { + for _, backend := range []string{"s3", "gcs", "azure", "local", "sftp", "smb"} { + backend := backend + Describe(backend, Ordered, func() { + var fs artifact.FilesystemRW + + BeforeAll(func() { + for _, b := range getBackends() { + if b.name == backend { + fs = b.fs + break + } + } + if fs == nil { + Skip("backend not available: " + backend) + } + }) + + AfterAll(func() { + if fs != nil { + _ = fs.Close() + } + }) + + It("should write files", func() { + ctx := gocontext.Background() + for _, tf := range testFiles { + _, err := fs.Write(ctx, tf.name, strings.NewReader(tf.content)) + Expect(err).ToNot(HaveOccurred()) + } + }) + + It("should read files back correctly", func() { + reader, err := fs.Read(gocontext.Background(), "record-1.txt") + Expect(err).ToNot(HaveOccurred()) + defer reader.Close() + + content, err := io.ReadAll(reader) + Expect(err).ToNot(HaveOccurred()) + Expect(string(content)).To(Equal("record-1")) + }) + + It("should stat files", func() { + info, err := fs.Stat("first.json") + Expect(err).ToNot(HaveOccurred()) + Expect(info.Size()).To(Equal(int64(len(`{"name": "first"}`)))) + }) + + It("should list files", func() { + dir := "" + if backend == "sftp" || backend == "local" { + dir = "." + } + entries, err := fs.ReadDir(dir) + Expect(err).ToNot(HaveOccurred()) + Expect(len(entries)).To(BeNumerically(">=", 5)) + }) + }) + } + + Describe("ctx.Blobs()", Ordered, func() { + It("should write and read via BlobStore interface", func() { + store, err := DefaultContext.Blobs() + Expect(err).ToNot(HaveOccurred()) + defer store.Close() + + data := []byte("ctx blobs e2e test data") + artData := artifact.Data{ + Content: io.NopCloser(bytes.NewReader(data)), + Filename: "/test/ctx-blobs/e2e-test.txt", + } + + a, err := store.Write(artData, &models.Artifact{}) + Expect(err).ToNot(HaveOccurred()) + Expect(a.Checksum).ToNot(BeEmpty()) + Expect(a.Size).To(Equal(int64(len(data)))) + + result, err := store.Read(a.ID) + Expect(err).ToNot(HaveOccurred()) + defer result.Content.Close() + + got, err := io.ReadAll(result.Content) + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(data)) + Expect(result.Pretty().String()).ToNot(BeEmpty()) + }) + }) +}) diff --git a/tests/e2e-blobs/containers.go b/tests/e2e-blobs/containers.go new file mode 100644 index 000000000..e58544b97 --- /dev/null +++ b/tests/e2e-blobs/containers.go @@ -0,0 +1,203 @@ +package e2e_blobs + +import ( + gocontext "context" + "fmt" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + gcs "cloud.google.com/go/storage" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +func startMinio(ctx gocontext.Context) (*s3.Client, testcontainers.Container, error) { + req := testcontainers.ContainerRequest{ + Image: "minio/minio", + ExposedPorts: []string{"9000/tcp"}, + Env: map[string]string{ + "MINIO_ROOT_USER": "minioadmin", + "MINIO_ROOT_PASSWORD": "minioadmin", + }, + Cmd: []string{"server", "/data"}, + WaitingFor: wait.ForHTTP("/minio/health/live").WithPort("9000").WithStartupTimeout(60 * time.Second), + } + + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + return nil, nil, fmt.Errorf("starting minio: %w", err) + } + + endpoint, err := container.Endpoint(ctx, "http") + if err != nil { + return nil, container, err + } + + cfg, err := config.LoadDefaultConfig(ctx, + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("minioadmin", "minioadmin", "")), + config.WithRegion("us-east-1"), + ) + if err != nil { + return nil, container, err + } + + client := s3.NewFromConfig(cfg, func(o *s3.Options) { + o.UsePathStyle = true + o.BaseEndpoint = &endpoint + }) + + _, _ = client.CreateBucket(ctx, &s3.CreateBucketInput{Bucket: aws.String("test")}) + + return client, container, nil +} + +func startFakeGCS(ctx gocontext.Context) (*gcs.Client, testcontainers.Container, error) { + req := testcontainers.ContainerRequest{ + Image: "fsouza/fake-gcs-server:1.49.3", + ExposedPorts: []string{"8083/tcp"}, + Cmd: []string{"-scheme", "http", "-port", "8083"}, + WaitingFor: wait.ForHTTP("/storage/v1/b").WithPort("8083").WithStartupTimeout(30 * time.Second), + } + + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + return nil, nil, fmt.Errorf("starting fake-gcs-server: %w", err) + } + + host, _ := container.Host(ctx) + port, _ := container.MappedPort(ctx, "8083") + emulatorHost := fmt.Sprintf("%s:%s", host, port.Port()) + emulatorURL := fmt.Sprintf("http://%s", emulatorHost) + + // Update external URL to match the dynamically mapped port + updateReq, _ := http.NewRequestWithContext(ctx, http.MethodPut, + emulatorURL+"/_internal/config", + strings.NewReader(fmt.Sprintf(`{"externalUrl": "%s"}`, emulatorURL))) + updateReq.Header.Set("Content-Type", "application/json") + if resp, err := http.DefaultClient.Do(updateReq); err == nil { + _ = resp.Body.Close() + } + + os.Setenv("STORAGE_EMULATOR_HOST", emulatorHost) + client, err := gcs.NewClient(ctx, gcs.WithJSONReads()) + if err != nil { + return nil, container, err + } + + _ = client.Bucket("test").Create(ctx, "fake-project", nil) + + return client, container, nil +} + +func startAzurite(ctx gocontext.Context) (*azblob.Client, testcontainers.Container, error) { + req := testcontainers.ContainerRequest{ + Image: "mcr.microsoft.com/azure-storage/azurite", + ExposedPorts: []string{"10000/tcp"}, + Cmd: []string{"azurite-blob", "--blobHost", "0.0.0.0", "--skipApiVersionCheck"}, + WaitingFor: wait.ForListeningPort("10000").WithStartupTimeout(30 * time.Second), + } + + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + return nil, nil, fmt.Errorf("starting azurite: %w", err) + } + + host, err := container.Host(ctx) + if err != nil { + return nil, container, err + } + port, err := container.MappedPort(ctx, "10000") + if err != nil { + return nil, container, err + } + + connStr := fmt.Sprintf( + "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://%s:%s/devstoreaccount1", + host, port.Port(), + ) + + client, err := azblob.NewClientFromConnectionString(connStr, nil) + if err != nil { + return nil, container, err + } + + _, _ = client.CreateContainer(ctx, "test", nil) + + return client, container, nil +} + +func startSFTP(ctx gocontext.Context) (string, testcontainers.Container, error) { + _, filename, _, _ := runtime.Caller(0) + configPath := filepath.Join(filepath.Dir(filename), "sftp-configuration.json") + + req := testcontainers.ContainerRequest{ + Image: "emberstack/sftp", + ExposedPorts: []string{"22/tcp"}, + Files: []testcontainers.ContainerFile{ + {HostFilePath: configPath, ContainerFilePath: "/app/config/sftp.json", FileMode: 0644}, + }, + WaitingFor: wait.ForListeningPort("22").WithStartupTimeout(30 * time.Second), + } + + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + return "", nil, fmt.Errorf("starting sftp: %w", err) + } + + host, err := container.Endpoint(ctx, "") + if err != nil { + return "", container, err + } + + return host, container, nil +} + +func startSMB(ctx gocontext.Context) (string, string, testcontainers.Container, error) { + req := testcontainers.ContainerRequest{ + Image: "dperson/samba", + ExposedPorts: []string{"445/tcp"}, + Cmd: []string{"-p", "-u", "foo;pass", "-s", "users;/srv;no;no;no;foo"}, + WaitingFor: wait.ForListeningPort("445").WithStartupTimeout(30 * time.Second), + } + + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + return "", "", nil, fmt.Errorf("starting smb: %w", err) + } + + host, err := container.Host(ctx) + if err != nil { + return "", "", container, err + } + + port, err := container.MappedPort(ctx, "445") + if err != nil { + return "", "", container, err + } + + return host, port.Port(), container, nil +} diff --git a/tests/e2e-blobs/sftp-configuration.json b/tests/e2e-blobs/sftp-configuration.json new file mode 100644 index 000000000..180f5f933 --- /dev/null +++ b/tests/e2e-blobs/sftp-configuration.json @@ -0,0 +1 @@ +{"Global":{"Chroot":{"Directory":"%h","StartPath":"sftp"},"Directories":["sftp"]},"Users":[{"Username":"foo","Password":"pass"}]} diff --git a/tests/e2e-blobs/suite_test.go b/tests/e2e-blobs/suite_test.go new file mode 100644 index 000000000..cca50aad6 --- /dev/null +++ b/tests/e2e-blobs/suite_test.go @@ -0,0 +1,87 @@ +package e2e_blobs + +import ( + gocontext "context" + "testing" + "time" + + gcs "cloud.google.com/go/storage" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/flanksource/commons/logger" + "github.com/flanksource/duty/context" + "github.com/flanksource/duty/tests/setup" + ginkgo "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/testcontainers/testcontainers-go" +) + +var log = logger.GetLogger("e2e-blobs") + +var ( + DefaultContext context.Context + + s3Client *s3.Client + gcsClient *gcs.Client + azureClient *azblob.Client + + allContainers []testcontainers.Container + + sftpHost string + smbHost string + smbPort string +) + +func TestBlobStores(t *testing.T) { + RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Blob Stores E2E Suite") +} + +var _ = ginkgo.BeforeSuite(func() { + DefaultContext = setup.BeforeSuiteFn(setup.WithoutDummyData) + + ctx, cancel := gocontext.WithTimeout(gocontext.Background(), 2*time.Minute) + defer cancel() + + var c testcontainers.Container + var err error + + log.Infof("Starting MinIO container...") + s3Client, c, err = startMinio(ctx) + Expect(err).ToNot(HaveOccurred()) + allContainers = append(allContainers, c) + log.Infof("MinIO ready") + + log.Infof("Starting fake-gcs-server container...") + gcsClient, c, err = startFakeGCS(ctx) + Expect(err).ToNot(HaveOccurred()) + allContainers = append(allContainers, c) + log.Infof("GCS ready") + + log.Infof("Starting Azurite container...") + azureClient, c, err = startAzurite(ctx) + Expect(err).ToNot(HaveOccurred()) + allContainers = append(allContainers, c) + log.Infof("Azurite ready") + + log.Infof("Starting SFTP container...") + sftpHost, c, err = startSFTP(ctx) + Expect(err).ToNot(HaveOccurred()) + allContainers = append(allContainers, c) + log.Infof("SFTP ready at %s", sftpHost) + + log.Infof("Starting SMB container...") + smbHost, smbPort, c, err = startSMB(ctx) + Expect(err).ToNot(HaveOccurred()) + allContainers = append(allContainers, c) + log.Infof("SMB ready at %s:%s", smbHost, smbPort) +}) + +var _ = ginkgo.AfterSuite(func() { + for _, c := range allContainers { + if c != nil { + _ = c.Terminate(gocontext.Background()) + } + } + setup.AfterSuiteFn() +})