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() +})