diff --git a/cmd/vela-worker/exec.go b/cmd/vela-worker/exec.go index 492ccbf3..b789db48 100644 --- a/cmd/vela-worker/exec.go +++ b/cmd/vela-worker/exec.go @@ -17,6 +17,7 @@ import ( "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" "github.com/go-vela/server/queue/models" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor" "github.com/go-vela/worker/runtime" "github.com/go-vela/worker/version" @@ -29,6 +30,10 @@ import ( func (w *Worker) exec(ctx context.Context, index int, config *api.Worker) error { var err error + var execStorage storage.Storage + + var _executor executor.Engine + // setup the version v := version.New() @@ -158,6 +163,14 @@ func (w *Worker) exec(ctx context.Context, index int, config *api.Worker) error execOutputCtn := *w.Config.Executor.OutputCtn execOutputCtn.ID = fmt.Sprintf("outputs_%s", p.ID) + if w.Storage != nil { + execStorage = w.Storage + + logrus.Debugf("executor storage is available, setting up storage") + } else { + logrus.Debugf("executor storage is nil, skipping storage setup") + } + // create logger with extra metadata // // https://pkg.go.dev/github.com/sirupsen/logrus#WithFields @@ -235,7 +248,7 @@ func (w *Worker) exec(ctx context.Context, index int, config *api.Worker) error // setup the executor // // https://pkg.go.dev/github.com/go-vela/worker/executor#New - _executor, err := executor.New(&executor.Setup{ + setup := &executor.Setup{ Logger: logger, Mock: w.Config.Mock, Driver: w.Config.Executor.Driver, @@ -250,8 +263,19 @@ func (w *Worker) exec(ctx context.Context, index int, config *api.Worker) error Pipeline: p.Sanitize(w.Config.Runtime.Driver), Version: v.Semantic(), OutputCtn: &execOutputCtn, - }) + } + if execStorage != nil { + fmt.Printf("setting up executor storage\n") + + setup.Storage = execStorage + } + + _executor, err = executor.New(setup) + if err != nil { + logger.Errorf("unable to setup executor: %v", err) + return err + } // add the executor to the worker w.Executors[index] = _executor diff --git a/cmd/vela-worker/flags.go b/cmd/vela-worker/flags.go index 23fde260..eb7730d9 100644 --- a/cmd/vela-worker/flags.go +++ b/cmd/vela-worker/flags.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli/v3" "github.com/go-vela/server/queue" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor" "github.com/go-vela/worker/runtime" ) @@ -120,5 +121,8 @@ func flags() []cli.Flag { f = append(f, runtime.Flags...) + // Storage Flags + f = append(f, storage.Flags...) + return f } diff --git a/cmd/vela-worker/operate.go b/cmd/vela-worker/operate.go index 588c34b1..c931bf75 100644 --- a/cmd/vela-worker/operate.go +++ b/cmd/vela-worker/operate.go @@ -12,13 +12,14 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/constants" "github.com/go-vela/server/queue" + "github.com/go-vela/server/storage" ) // operate is a helper function to initiate all // subprocesses for the operator to poll the // queue and execute Vela pipelines. // -//nolint:funlen // refactor candidate +//nolint:funlen,gocyclo // refactor candidate func (w *Worker) operate(ctx context.Context) error { var err error // create the errgroup for managing operator subprocesses @@ -77,6 +78,58 @@ func (w *Worker) operate(ctx context.Context) error { w.updateWorkerStatus(ctx, registryWorker, constants.WorkerStatusError) } + // getting storage creds + logrus.Trace("getting storage s3 creds..") + // fetching queue credentials using registration token + stCreds, _, err := w.VelaClient.Storage.GetInfo(ctx) + if err != nil { + logrus.Tracef("error getting storage creds: %v", err) + return err + } + + if stCreds.GetEnabled() { + logrus.Trace("storage enabled") + // if an address was given at start up, use that — else use what is returned from server + if len(w.Config.Storage.Endpoint) == 0 { + w.Config.Storage.Endpoint = stCreds.GetStorageAddress() + } + + // set access key in storage config + w.Config.Storage.AccessKey = stCreds.GetAccessKey() + + // set secret key in storage config + w.Config.Storage.SecretKey = stCreds.GetSecretKey() + + // set bucket name in storage config + w.Config.Storage.Bucket = stCreds.GetStorageBucket() + + // set storage enabled to true + w.Config.Storage.Enable = stCreds.GetEnabled() + + s, err := storage.New(w.Config.Storage) + if err != nil { + logrus.Error("storage setup failed") + // set to error as storage setup fails + w.updateWorkerStatus(ctx, registryWorker, constants.WorkerStatusError) + + return err + } + + w.Storage = s + logrus.WithFields(logrus.Fields{ + "driver": w.Config.Storage.Driver, + "bucket": w.Config.Storage.Bucket, + "endpoint": w.Config.Storage.Endpoint, + }).Debug("storage initialized") + } else { + logrus.Trace("storage not enabled") + // storage disabled; nothing to validate + w.Config.Storage.Enable = false + w.Storage = nil + + logrus.Debug("storage disabled: worker storage unset") + } + // spawn goroutine for phoning home executors.Go(func() error { // five second ticker for signal handling diff --git a/cmd/vela-worker/run.go b/cmd/vela-worker/run.go index 6d1e419d..38d43fab 100644 --- a/cmd/vela-worker/run.go +++ b/cmd/vela-worker/run.go @@ -17,6 +17,7 @@ import ( "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" "github.com/go-vela/server/queue" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor" "github.com/go-vela/worker/runtime" ) @@ -138,6 +139,13 @@ func run(ctx context.Context, c *cli.Command) error { Address: c.String("server.addr"), Secret: c.String("server.secret"), }, + Storage: &storage.Setup{ + Driver: c.String("storage.driver"), + Endpoint: c.String("storage.endpoint.name"), + AccessKey: c.String("storage.access.key"), + SecretKey: c.String("storage.secret.key"), + Bucket: c.String("storage.bucket.name"), + }, // Certificate configuration Certificate: &Certificate{ Cert: c.String("server.cert"), diff --git a/cmd/vela-worker/worker.go b/cmd/vela-worker/worker.go index ec6fcae4..d17acf10 100644 --- a/cmd/vela-worker/worker.go +++ b/cmd/vela-worker/worker.go @@ -10,6 +10,7 @@ import ( "github.com/go-vela/sdk-go/vela" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/queue" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor" "github.com/go-vela/worker/runtime" ) @@ -57,6 +58,7 @@ type ( Server *Server Certificate *Certificate TLSMinVersion string + Storage *storage.Setup } // Worker represents all configuration and @@ -72,5 +74,6 @@ type ( RunningBuilds []*api.Build QueueCheckedIn bool RunningBuildsMutex sync.Mutex + Storage storage.Storage } ) diff --git a/executor/executor_test.go b/executor/executor_test.go index 9540a52f..238f545c 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -16,6 +16,7 @@ import ( "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" "github.com/go-vela/server/mock/server" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor/linux" "github.com/go-vela/worker/executor/local" "github.com/go-vela/worker/runtime/docker" @@ -37,6 +38,38 @@ func TestExecutor_New(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storageT := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _sT, err := storage.New(_storageT) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + + _storageF := &storage.Setup{ + Enable: false, + Driver: "", + Endpoint: "", + AccessKey: "", + SecretKey: "", + Bucket: "", + Region: "", + Secure: false, + } + + _sF, err := storage.New(_storageF) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + _linux, err := linux.New( linux.WithBuild(_build), linux.WithHostname("localhost"), @@ -45,6 +78,7 @@ func TestExecutor_New(t *testing.T) { linux.WithRuntime(_runtime), linux.WithVelaClient(_client), linux.WithVersion("v1.0.0"), + linux.WithStorage(_sT), ) if err != nil { t.Errorf("unable to create linux engine: %v", err) @@ -57,6 +91,7 @@ func TestExecutor_New(t *testing.T) { local.WithRuntime(_runtime), local.WithVelaClient(_client), local.WithVersion("v1.0.0"), + local.WithStorage(_sT), ) if err != nil { t.Errorf("unable to create local engine: %v", err) @@ -80,6 +115,7 @@ func TestExecutor_New(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _sF, }, want: nil, equal: reflect.DeepEqual, @@ -95,6 +131,7 @@ func TestExecutor_New(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _sT, }, want: _linux, equal: linux.Equal, @@ -109,6 +146,7 @@ func TestExecutor_New(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _sT, }, want: _local, equal: local.Equal, @@ -123,6 +161,7 @@ func TestExecutor_New(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _sT, }, want: nil, equal: reflect.DeepEqual, @@ -137,6 +176,7 @@ func TestExecutor_New(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _sT, }, want: nil, equal: reflect.DeepEqual, @@ -151,6 +191,7 @@ func TestExecutor_New(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _sT, }, want: nil, equal: reflect.DeepEqual, diff --git a/executor/linux/build.go b/executor/linux/build.go index 6f00fa6e..b3f51cbe 100644 --- a/executor/linux/build.go +++ b/executor/linux/build.go @@ -450,6 +450,12 @@ func (c *client) ExecBuild(ctx context.Context) error { // output maps for dynamic environment variables captured from volume var opEnv, maskEnv map[string]string + // test report object for storing the test report information + var tr *api.TestReport + + // Flag to track if we've already created the test report record + testReportCreated := false + // fire up output container to run with the build c.Logger.Infof("creating outputs container %s", c.OutputCtn.ID) @@ -521,7 +527,7 @@ func (c *client) ExecBuild(ctx context.Context) error { // check if the step should be skipped // // https://pkg.go.dev/github.com/go-vela/worker/internal/step#Skip - skip, err := step.Skip(_step, c.build, c.build.GetStatus()) + skip, err := step.Skip(_step, c.build, c.build.GetStatus(), c.Storage) if err != nil { return fmt.Errorf("unable to plan step: %w", c.err) } @@ -530,6 +536,64 @@ func (c *client) ExecBuild(ctx context.Context) error { continue } + // Check if this step has test_report and storage is disabled + //if !_step.TestReport.Empty() && c.Storage == nil { + // c.Logger.Infof("skipping %s step: storage is disabled but test_report is defined", _step.Name) + // + // //// Load step model + // //stepData, err := step.Load(_step, &c.steps) + // //if err != nil { + // // return fmt.Errorf("unable to load step: %w", err) + // //} + // // + // //// Load or create logs for this step + // ////stepLog, err := step.LoadLogs(_step, &c.stepLogs) + // ////if err != nil { + // //// return fmt.Errorf("unable to load step logs: %w", err) + // ////} + // // + // //// Ensure timestamps + // //now := time.Now().UTC().Unix() + // //if stepData.GetStarted() == 0 { + // // stepData.SetStarted(now) + // //} + // // + // //stepData.SetStatus(constants.StatusError) + // //stepData.SetExitCode(0) + // //stepData.SetFinished(now) + // + // // send API call to update the step + // // + // // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#StepService.Update + // //_tsstep, _, err := c.Vela.Step.Update(c.build.GetRepo().GetOrg(), c.build.GetRepo().GetName(), c.build.GetNumber(), stepData) + // //if err != nil { + // // return err + // //} + // // + // //// send API call to capture the step log + // //// + // //// https://pkg.go.dev/github.com/go-vela/sdk-go/vela#LogService.GetStep + // //_log, _, err := c.Vela.Log.GetStep(c.build.GetRepo().GetOrg(), c.build.GetRepo().GetName(), c.build.GetNumber(), _tsstep.GetNumber()) + // //if err != nil { + // // return err + // //} + // //_log.AppendData([]byte("Storage is disabled, contact Vela Admins\n")) + // // + // //// add a step log to a map + // //c.stepLogs.Store(_step.ID, _log) + // //stepLog.AppendData([]byte("Storage is disabled, contact Vela Admins\n")) + // //stepLog.SetData([]byte("Storage is disabled, contact Vela Admins\n")) + // //// Upload logs so UI can display the message + // //if _, err := c.Vela.Log. + // // UpdateStep(c.build.GetRepo().GetOrg(), c.build.GetRepo().GetName(), c.build.GetNumber(), *stepData.Number, stepLog); err != nil { + // // c.Logger.Errorf("unable to upload skipped step logs: %v", err) + // //} + // // Upload step status + // //step.Upload(_step, c.build, c.Vela, c.Logger, stepData) + // + // continue + //} + // add netrc to secrets for masking in logs sec := &pipeline.StepSecret{ Target: "VELA_NETRC_PASSWORD", @@ -557,6 +621,46 @@ func (c *client) ExecBuild(ctx context.Context) error { _step.Secrets = append(_step.Secrets, sec) } + // logic for polling files only if the test-report step is present + // iterate through the steps in the build + + // TODO: API to return if storage is enabled + //if c.Storage == nil && _step.TestReport.Empty() || c.Storage == nil && !_step.TestReport.Empty() { + // c.Logger.Infof("storage disabled, skipping test report for %s step", _step.Name) + // // skip if no storage client + // // but test report is defined in step + // continue + //} else if !_step.TestReport.Empty() && c.Storage != nil { + c.Logger.Debug("creating test report record in database") + // send API call to update the test report + // + // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#TestReportService.Add + // TODO: .Add should be .Update + // TODO: handle somewhere if multiple test report keys exist in pipeline + if !testReportCreated { + tr, c.err = c.CreateTestReport(ctx) + if c.err != nil { + return fmt.Errorf("unable to create test report: %w", c.err) + } + + testReportCreated = true + } + + if len(_step.TestReport.Results) != 0 { + err := c.outputs.pollFiles(ctx, c.OutputCtn, _step.TestReport.Results, c.build, tr) + if err != nil { + c.Logger.Errorf("unable to poll files for results: %v", err) + } + } + + if len(_step.TestReport.Attachments) != 0 { + err := c.outputs.pollFiles(ctx, c.OutputCtn, _step.TestReport.Attachments, c.build, tr) + if err != nil { + c.Logger.Errorf("unable to poll files for attachments: %v", err) + } + } + //} + // perform any substitution on dynamic variables err = _step.Substitute() if err != nil { diff --git a/executor/linux/build_test.go b/executor/linux/build_test.go index 59140560..96a11f69 100644 --- a/executor/linux/build_test.go +++ b/executor/linux/build_test.go @@ -21,6 +21,7 @@ import ( "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" "github.com/go-vela/server/mock/server" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" "github.com/go-vela/worker/runtime/docker" @@ -183,6 +184,22 @@ func TestLinux_CreateBuild(t *testing.T) { } } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + _engine, err := New( WithLogger(logger), WithBuild(test.build), @@ -190,6 +207,7 @@ func TestLinux_CreateBuild(t *testing.T) { WithRuntime(_runtime), WithVelaClient(_client), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -260,6 +278,22 @@ func TestLinux_PlanBuild(t *testing.T) { t.Errorf("unable to create Vela API client: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + tests := []struct { name string failure bool @@ -381,6 +415,7 @@ func TestLinux_PlanBuild(t *testing.T) { WithPipeline(_pipeline), WithRuntime(_runtime), WithVelaClient(_client), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -460,6 +495,22 @@ func TestLinux_AssembleBuild(t *testing.T) { streamRequests, done := message.MockStreamRequestsWithCancel(context.Background()) defer done() + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + tests := []struct { name string failure bool @@ -679,6 +730,7 @@ func TestLinux_AssembleBuild(t *testing.T) { WithVelaClient(_client), WithOutputCtn(testOutputsCtn()), withStreamRequests(streamRequests), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -779,6 +831,22 @@ func TestLinux_ExecBuild(t *testing.T) { t.Errorf("unable to create Vela API client: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + tests := []struct { name string failure bool @@ -936,6 +1004,7 @@ func TestLinux_ExecBuild(t *testing.T) { WithVelaClient(_client), WithOutputCtn(testOutputsCtn()), withStreamRequests(streamRequests), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -1085,6 +1154,22 @@ func TestLinux_StreamBuild(t *testing.T) { t.Errorf("unable to create Vela API client: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + type planFuncType = func(context.Context, *pipeline.Container) error // planNothing is a planFuncType that does nothing @@ -1589,6 +1674,7 @@ func TestLinux_StreamBuild(t *testing.T) { WithLogStreamingTimeout(1*time.Second), WithVelaClient(_client), withStreamRequests(streamRequests), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -1715,6 +1801,22 @@ func TestLinux_DestroyBuild(t *testing.T) { t.Errorf("unable to create Vela API client: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + tests := []struct { name string failure bool @@ -1879,6 +1981,7 @@ func TestLinux_DestroyBuild(t *testing.T) { WithRuntime(_runtime), WithVelaClient(_client), WithOutputCtn(testOutputsCtn()), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) diff --git a/executor/linux/linux.go b/executor/linux/linux.go index 70502d85..c9e8bd48 100644 --- a/executor/linux/linux.go +++ b/executor/linux/linux.go @@ -13,6 +13,7 @@ import ( "github.com/go-vela/sdk-go/vela" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" ) @@ -29,6 +30,7 @@ type ( Hostname string Version string OutputCtn *pipeline.Container + Storage storage.Storage // clients for build actions secret *secretSvc diff --git a/executor/linux/opts.go b/executor/linux/opts.go index 5c394bae..33d982c8 100644 --- a/executor/linux/opts.go +++ b/executor/linux/opts.go @@ -11,6 +11,7 @@ import ( "github.com/go-vela/sdk-go/vela" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" ) @@ -209,3 +210,23 @@ func withStreamRequests(s chan message.StreamRequest) Opt { return nil } } + +// WithStorage sets the storage in the executor client for Linux. +func WithStorage(s storage.Storage) Opt { + return func(c *client) error { + c.Logger.Trace("configuring storage in linux executor client") + + // check if the storage provided is empty + if s == nil { + return fmt.Errorf("empty storage setup provided") + } + + c.Storage = s + + if c.Storage == nil { + return fmt.Errorf("empty storage setup in linux executor client") + } + + return nil + } +} diff --git a/executor/linux/outputs.go b/executor/linux/outputs.go index 87d999e0..36d5c895 100644 --- a/executor/linux/outputs.go +++ b/executor/linux/outputs.go @@ -8,10 +8,13 @@ import ( "encoding/base64" "fmt" "maps" + "path/filepath" + "strconv" envparse "github.com/hashicorp/go-envparse" "github.com/sirupsen/logrus" + api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" ) @@ -204,3 +207,73 @@ func (o *outputSvc) poll(ctx context.Context, ctn *pipeline.Container) (map[stri return outputMap, maskMap, nil } + +// pollFiles polls the output for files from the sidecar container. +func (o *outputSvc) pollFiles(ctx context.Context, ctn *pipeline.Container, fileList []string, b *api.Build, tr *api.TestReport) error { + // exit if outputs container has not been configured + if len(ctn.Image) == 0 { + return fmt.Errorf("no outputs container configured") + } + + // update engine logger with outputs metadata + // + // https://pkg.go.dev/github.com/sirupsen/logrus#Entry.WithField + logger := o.client.Logger.WithField("test-outputs", ctn.Name) + + // grab file paths from the container + filesPath, err := o.client.Runtime.PollFileNames(ctx, ctn, fileList) + if err != nil { + return fmt.Errorf("unable to poll file names: %w", err) + } + + if len(filesPath) == 0 { + return fmt.Errorf("no files found for file list: %v", fileList) + } + + // process each file found + for _, filePath := range filesPath { + fileName := filepath.Base(filePath) + logger.Debugf("processing file: %s (path: %s)", fileName, filePath) + + // get file content from container + reader, size, err := o.client.Runtime.PollFileContent(ctx, ctn, filePath) + if err != nil { + return fmt.Errorf("unable to poll file content for %s: %w", filePath, err) + } + + // create storage object path + objectName := fmt.Sprintf("%s/%s/%s/%s", + b.GetRepo().GetOrg(), + b.GetRepo().GetName(), + strconv.FormatInt(b.GetNumber(), 10), + fileName) + + // upload file to storage + err = o.client.Storage.UploadObject(ctx, &api.Object{ + ObjectName: objectName, + Bucket: api.Bucket{BucketName: o.client.Storage.GetBucket(ctx)}, + FilePath: filePath, + }, reader, size) + if err != nil { + return fmt.Errorf("unable to upload object %s: %w", fileName, err) + } + + presignURL, err := o.client.Storage.PresignedGetObject(ctx, &api.Object{ + ObjectName: objectName, + Bucket: api.Bucket{BucketName: o.client.Storage.GetBucket(ctx)}, + FilePath: filePath, + }) + if err != nil { + return fmt.Errorf("unable to generate presign URL for %s: %w", fileName, err) + } + + // create test attachment record in database after successful upload + err = o.client.CreateTestAttachment(ctx, fileName, presignURL, size, tr) + if err != nil { + logger.Errorf("unable to create test attachment record for %s: %v", fileName, err) + // don't return error here to avoid blocking the upload process + } + } + + return nil +} diff --git a/executor/linux/report.go b/executor/linux/report.go new file mode 100644 index 00000000..f8dea1a6 --- /dev/null +++ b/executor/linux/report.go @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 + +package linux + +import ( + "context" + "fmt" + + api "github.com/go-vela/server/api/types" +) + +// CreateTestReport creates a test report record in the database for the current build. +func (c *client) CreateTestReport(ctx context.Context) (*api.TestReport, error) { + // create empty test report for the build + testReport := &api.TestReport{} + + // update test report in database + tr, resp, err := c.Vela.TestReport.Update( + ctx, + c.build.GetRepo().GetOrg(), + c.build.GetRepo().GetName(), + c.build.GetNumber(), + testReport, + ) + if err != nil { + return nil, fmt.Errorf("failed to create test report record: build=%d, status=%d, error=%w", + c.build.GetNumber(), resp.StatusCode, err) + } + + c.Logger.Debugf("created test report record: id=%d, build=%d", tr.GetID(), c.build.GetNumber()) + + return tr, nil +} diff --git a/executor/linux/stage.go b/executor/linux/stage.go index 9db22fa4..899afcd9 100644 --- a/executor/linux/stage.go +++ b/executor/linux/stage.go @@ -132,7 +132,7 @@ func (c *client) ExecStage(ctx context.Context, s *pipeline.Stage, m *sync.Map) // check if the step should be skipped // // https://pkg.go.dev/github.com/go-vela/worker/internal/step#Skip - skip, err := step.Skip(_step, c.build, useStatus) + skip, err := step.Skip(_step, c.build, useStatus, c.Storage) if err != nil { return fmt.Errorf("unable to plan step: %w", c.err) } diff --git a/executor/linux/stage_test.go b/executor/linux/stage_test.go index 1a3e7750..00ed8a3b 100644 --- a/executor/linux/stage_test.go +++ b/executor/linux/stage_test.go @@ -16,6 +16,7 @@ import ( "github.com/go-vela/server/compiler/native" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/mock/server" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" "github.com/go-vela/worker/runtime/docker" @@ -70,6 +71,22 @@ func TestLinux_CreateStage(t *testing.T) { t.Errorf("unable to create kubernetes runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + // setup tests tests := []struct { name string @@ -175,6 +192,7 @@ func TestLinux_CreateStage(t *testing.T) { WithPipeline(_pipeline), WithRuntime(test.runtime), WithVelaClient(_client), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -228,6 +246,22 @@ func TestLinux_PlanStage(t *testing.T) { t.Errorf("unable to create kubernetes runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + dockerTestMap := new(sync.Map) dockerTestMap.Store("foo", make(chan error, 1)) @@ -402,6 +436,7 @@ func TestLinux_PlanStage(t *testing.T) { WithPipeline(new(pipeline.Build)), WithRuntime(test.runtime), WithVelaClient(_client), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -452,6 +487,22 @@ func TestLinux_ExecStage(t *testing.T) { streamRequests, done := message.MockStreamRequestsWithCancel(context.Background()) defer done() + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + // setup tests tests := []struct { name string @@ -591,6 +642,7 @@ func TestLinux_ExecStage(t *testing.T) { WithVelaClient(_client), WithOutputCtn(testOutputsCtn()), withStreamRequests(streamRequests), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) @@ -636,6 +688,22 @@ func TestLinux_DestroyStage(t *testing.T) { t.Errorf("unable to create kubernetes runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + // setup tests tests := []struct { name string @@ -691,6 +759,7 @@ func TestLinux_DestroyStage(t *testing.T) { WithPipeline(new(pipeline.Build)), WithRuntime(test.runtime), WithVelaClient(_client), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create %s executor engine: %v", test.name, err) diff --git a/executor/linux/testattachments.go b/executor/linux/testattachments.go new file mode 100644 index 00000000..3dae948d --- /dev/null +++ b/executor/linux/testattachments.go @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 + +package linux + +import ( + "context" + "fmt" + "path/filepath" + "strconv" + "time" + + api "github.com/go-vela/server/api/types" +) + +// CreateTestAttachment creates a test attachment record in the database +// after a file has been successfully uploaded to storage. +func (c *client) CreateTestAttachment(ctx context.Context, fileName, presignURL string, size int64, tr *api.TestReport) error { + // extract file extension and type information + fileExt := filepath.Ext(fileName) + + // create object path matching the storage upload format + objectPath := fmt.Sprintf("%s/%s/%s/%s", + c.build.GetRepo().GetOrg(), + c.build.GetRepo().GetName(), + strconv.FormatInt(c.build.GetNumber(), 10), + fileName) + + // create timestamp for record creation + createdAt := time.Now().Unix() + + // build test attachment record + testAttachment := &api.TestAttachment{ + TestReportID: tr.ID, // will be populated by the API based on build context + FileName: &fileName, + ObjectPath: &objectPath, + FileSize: &size, + FileType: &fileExt, + PresignedURL: &presignURL, + CreatedAt: &createdAt, + } + + // update test attachment in database + ta, resp, err := c.Vela.TestAttachment.Update( + ctx, + c.build.GetRepo().GetOrg(), + c.build.GetRepo().GetName(), + c.build.GetNumber(), + testAttachment, + ) + if err != nil { + return fmt.Errorf("failed to create test attachment record: build=%d, status=%d, error=%w", + c.build.GetNumber(), resp.StatusCode, err) + } + + c.Logger.Debugf("created test attachment record: id=%d, file=%s", ta.GetID(), fileName) + + return nil +} diff --git a/executor/local/build.go b/executor/local/build.go index d5fcdcb3..d6c65a3c 100644 --- a/executor/local/build.go +++ b/executor/local/build.go @@ -289,7 +289,7 @@ func (c *client) ExecBuild(ctx context.Context) error { // check if the step should be skipped // // https://pkg.go.dev/github.com/go-vela/worker/internal/step#Skip - skip, err := step.Skip(_step, c.build, c.build.GetStatus()) + skip, err := step.Skip(_step, c.build, c.build.GetStatus(), c.Storage) if err != nil { return fmt.Errorf("unable to plan step: %w", c.err) } diff --git a/executor/local/build_test.go b/executor/local/build_test.go index a403f769..9168c6a0 100644 --- a/executor/local/build_test.go +++ b/executor/local/build_test.go @@ -11,6 +11,7 @@ import ( "github.com/go-vela/server/compiler/native" "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime/docker" ) @@ -38,6 +39,22 @@ func TestLocal_CreateBuild(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + tests := []struct { name string failure bool @@ -79,6 +96,7 @@ func TestLocal_CreateBuild(t *testing.T) { WithPipeline(_pipeline), WithRuntime(_runtime), WithOutputCtn(testOutputsCtn()), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create executor engine: %v", err) @@ -124,6 +142,22 @@ func TestLocal_PlanBuild(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + tests := []struct { name string failure bool @@ -165,6 +199,7 @@ func TestLocal_PlanBuild(t *testing.T) { WithPipeline(_pipeline), WithRuntime(_runtime), WithOutputCtn(testOutputsCtn()), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create executor engine: %v", err) @@ -216,6 +251,22 @@ func TestLocal_AssembleBuild(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + streamRequests, done := message.MockStreamRequestsWithCancel(context.Background()) defer done() @@ -291,6 +342,7 @@ func TestLocal_AssembleBuild(t *testing.T) { WithRuntime(_runtime), WithOutputCtn(testOutputsCtn()), withStreamRequests(streamRequests), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create executor engine: %v", err) @@ -342,6 +394,22 @@ func TestLocal_ExecBuild(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + streamRequests, done := message.MockStreamRequestsWithCancel(context.Background()) defer done() @@ -402,6 +470,7 @@ func TestLocal_ExecBuild(t *testing.T) { WithRuntime(_runtime), WithOutputCtn(testOutputsCtn()), withStreamRequests(streamRequests), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create executor engine: %v", err) @@ -453,6 +522,22 @@ func TestLocal_StreamBuild(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + type planFuncType = func(context.Context, *pipeline.Container) error // planNothing is a planFuncType that does nothing @@ -605,6 +690,7 @@ func TestLocal_StreamBuild(t *testing.T) { WithRuntime(_runtime), WithOutputCtn(testOutputsCtn()), withStreamRequests(streamRequests), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create executor engine: %v", err) @@ -676,6 +762,22 @@ func TestLocal_DestroyBuild(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + tests := []struct { name string failure bool @@ -732,6 +834,7 @@ func TestLocal_DestroyBuild(t *testing.T) { WithPipeline(_pipeline), WithRuntime(_runtime), WithOutputCtn(testOutputsCtn()), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create executor engine: %v", err) diff --git a/executor/local/local.go b/executor/local/local.go index 33f88a55..ee8908c8 100644 --- a/executor/local/local.go +++ b/executor/local/local.go @@ -11,6 +11,7 @@ import ( "github.com/go-vela/sdk-go/vela" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" ) @@ -23,6 +24,7 @@ type ( Hostname string Version string OutputCtn *pipeline.Container + Storage storage.Storage // private fields init *pipeline.Container diff --git a/executor/local/opts.go b/executor/local/opts.go index 61660ed7..fb012db8 100644 --- a/executor/local/opts.go +++ b/executor/local/opts.go @@ -9,6 +9,7 @@ import ( "github.com/go-vela/sdk-go/vela" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" ) @@ -140,3 +141,22 @@ func withStreamRequests(s chan message.StreamRequest) Opt { return nil } } + +// WithStorage sets the storage in the executor client for Linux. +func WithStorage(s storage.Storage) Opt { + return func(c *client) error { + // check if the storage provided is empty + if s == nil { + return fmt.Errorf("empty storage setup provided") + } + + // set the storage in the client + c.Storage = s + + if c.Storage == nil { + return fmt.Errorf("empty storage setup: %v", s) + } + + return nil + } +} diff --git a/executor/local/stage.go b/executor/local/stage.go index 05564596..a050a4f7 100644 --- a/executor/local/stage.go +++ b/executor/local/stage.go @@ -109,7 +109,7 @@ func (c *client) ExecStage(ctx context.Context, s *pipeline.Stage, m *sync.Map) // check if the step should be skipped // // https://pkg.go.dev/github.com/go-vela/worker/internal/step#Skip - skip, err := step.Skip(_step, c.build, useStatus) + skip, err := step.Skip(_step, c.build, useStatus, c.Storage) if err != nil { return fmt.Errorf("unable to plan step: %w", c.err) } diff --git a/executor/local/stage_test.go b/executor/local/stage_test.go index 2c880b7c..d63b6c6e 100644 --- a/executor/local/stage_test.go +++ b/executor/local/stage_test.go @@ -12,6 +12,7 @@ import ( "github.com/go-vela/server/compiler/native" "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime/docker" ) @@ -51,6 +52,22 @@ func TestLocal_CreateStage(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + // setup tests tests := []struct { name string @@ -103,6 +120,7 @@ func TestLocal_CreateStage(t *testing.T) { WithPipeline(_pipeline), WithRuntime(_runtime), WithOutputCtn(testOutputsCtn()), + WithStorage(_s), ) if err != nil { t.Errorf("unable to create executor engine: %v", err) diff --git a/executor/setup.go b/executor/setup.go index bd2ada04..10f37f24 100644 --- a/executor/setup.go +++ b/executor/setup.go @@ -13,6 +13,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor/linux" "github.com/go-vela/worker/executor/local" "github.com/go-vela/worker/runtime" @@ -60,6 +61,8 @@ type Setup struct { Pipeline *pipeline.Build // id token request token for the build RequestToken string + // storage client for interacting with storage resources + Storage storage.Storage } // Darwin creates and returns a Vela engine capable of @@ -75,10 +78,8 @@ func (s *Setup) Darwin() (Engine, error) { func (s *Setup) Linux() (Engine, error) { logrus.Trace("creating linux executor client from setup") - // create new Linux executor engine - // - // https://pkg.go.dev/github.com/go-vela/worker/executor/linux#New - return linux.New( + // create options for Linux executor + opts := []linux.Opt{ linux.WithBuild(s.Build), linux.WithMaxLogSize(s.MaxLogSize), linux.WithLogStreamingTimeout(s.LogStreamingTimeout), @@ -91,7 +92,19 @@ func (s *Setup) Linux() (Engine, error) { linux.WithVersion(s.Version), linux.WithLogger(s.Logger), linux.WithOutputCtn(s.OutputCtn), - ) + } + + // Conditionally add storage option + if s.Storage != nil { + fmt.Printf("Adding storage to linux executor\n") + + opts = append(opts, linux.WithStorage(s.Storage)) + } + + // create new Linux executor engine + // + // https://pkg.go.dev/github.com/go-vela/worker/executor/linux#New + return linux.New(opts...) } // Local creates and returns a Vela engine capable of @@ -99,10 +112,7 @@ func (s *Setup) Linux() (Engine, error) { func (s *Setup) Local() (Engine, error) { logrus.Trace("creating local executor client from setup") - // create new Local executor engine - // - // https://pkg.go.dev/github.com/go-vela/worker/executor/local#New - return local.New( + opts := []local.Opt{ local.WithBuild(s.Build), local.WithHostname(s.Hostname), local.WithPipeline(s.Pipeline), @@ -111,7 +121,17 @@ func (s *Setup) Local() (Engine, error) { local.WithVersion(s.Version), local.WithMockStdout(s.Mock), local.WithOutputCtn(s.OutputCtn), - ) + } + + // Conditionally add storage option + if s.Storage != nil { + opts = append(opts, local.WithStorage(s.Storage)) + } + + // create new Local executor engine + // + // https://pkg.go.dev/github.com/go-vela/worker/executor/local#New + return local.New(opts...) } // Windows creates and returns a Vela engine capable of @@ -169,6 +189,11 @@ func (s *Setup) Validate() error { return fmt.Errorf("no Vela user provided in setup") } + // If storage is provided, ensure it's enabled + if s.Storage != nil && !s.Storage.StorageEnable() { + return fmt.Errorf("storage client provided but not enabled in setup") + } + // setup is valid return nil } diff --git a/executor/setup_test.go b/executor/setup_test.go index e89700a7..87999425 100644 --- a/executor/setup_test.go +++ b/executor/setup_test.go @@ -14,6 +14,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/constants" "github.com/go-vela/server/mock/server" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor/linux" "github.com/go-vela/worker/executor/local" "github.com/go-vela/worker/runtime/docker" @@ -69,6 +70,22 @@ func TestExecutor_Setup_Linux(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + want, err := linux.New( linux.WithBuild(_build), linux.WithMaxLogSize(2097152), @@ -78,6 +95,7 @@ func TestExecutor_Setup_Linux(t *testing.T) { linux.WithRuntime(_runtime), linux.WithVelaClient(_client), linux.WithVersion("v1.0.0"), + linux.WithStorage(_s), ) if err != nil { t.Errorf("unable to create linux engine: %v", err) @@ -92,6 +110,7 @@ func TestExecutor_Setup_Linux(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _s, } // run test @@ -123,6 +142,22 @@ func TestExecutor_Setup_Local(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + want, err := local.New( local.WithBuild(_build), local.WithHostname("localhost"), @@ -130,6 +165,7 @@ func TestExecutor_Setup_Local(t *testing.T) { local.WithRuntime(_runtime), local.WithVelaClient(_client), local.WithVersion("v1.0.0"), + local.WithStorage(_s), ) if err != nil { t.Errorf("unable to create local engine: %v", err) @@ -143,6 +179,7 @@ func TestExecutor_Setup_Local(t *testing.T) { Pipeline: _pipeline, Runtime: _runtime, Version: "v1.0.0", + Storage: _s, } // run test @@ -208,6 +245,22 @@ func TestExecutor_Setup_Validate(t *testing.T) { t.Errorf("unable to create runtime engine: %v", err) } + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + _emptyOwnerBuild := new(api.Build) _emptyOwnerBuild.SetRepo(new(api.Repo)) @@ -228,6 +281,7 @@ func TestExecutor_Setup_Validate(t *testing.T) { MaxLogSize: 2097152, Pipeline: _pipeline, Runtime: _runtime, + Storage: _s, }, failure: false, }, diff --git a/go.mod b/go.mod index c6aac5a1..a55086ae 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-vela/worker -go 1.24.4 +go 1.25.5 require ( github.com/Masterminds/semver/v3 v3.4.0 @@ -17,8 +17,8 @@ require ( github.com/opencontainers/image-spec v1.1.1 github.com/prometheus/client_golang v1.23.2 github.com/sirupsen/logrus v1.9.3 - github.com/urfave/cli/v3 v3.5.0 - golang.org/x/sync v0.17.0 + github.com/urfave/cli/v3 v3.6.1 + golang.org/x/sync v0.19.0 gotest.tools/v3 v3.5.2 k8s.io/api v0.34.1 k8s.io/apimachinery v0.34.1 @@ -32,12 +32,15 @@ require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/containerd/containerd/v2 v2.1.3 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/expr-lang/expr v1.17.6 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/goccy/go-yaml v1.19.0 // indirect github.com/google/go-github/v74 v74.0.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect @@ -48,16 +51,21 @@ require ( github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/lestrrat-go/httprc/v3 v3.0.0 // indirect - github.com/lestrrat-go/jwx/v3 v3.0.10 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/lestrrat-go/httprc/v3 v3.0.2 // indirect + github.com/lestrrat-go/jwx/v3 v3.0.12 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-sqlite3 v1.14.28 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect + github.com/minio/crc64nvme v1.0.1 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/minio-go/v7 v7.0.89 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.54.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.57.1 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect @@ -68,11 +76,8 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect go.opentelemetry.io/otel/sdk v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.7.0 // indirect - go.uber.org/mock v0.5.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.26.0 // indirect - golang.org/x/tools v0.35.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/grpc v1.73.0 // indirect @@ -91,8 +96,8 @@ require ( github.com/PuerkitoBio/purell v1.2.1 // indirect github.com/alicebob/miniredis/v2 v2.35.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.14.0 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/bytedance/sonic v1.14.2 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/containerd/log v0.1.0 // indirect @@ -104,7 +109,7 @@ require ( github.com/drone/envsubst v1.0.3 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gabriel-vasile/mimetype v1.4.11 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-logr/logr v1.4.3 // indirect @@ -114,7 +119,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/go-playground/validator/v10 v10.29.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect @@ -133,8 +138,7 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/option v1.0.1 // indirect - github.com/mailru/easyjson v0.9.0 // indirect + github.com/mailru/easyjson v0.9.1 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -151,28 +155,28 @@ require ( github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/redis/go-redis/v9 v9.12.1 // indirect - github.com/segmentio/asm v1.2.0 // indirect + github.com/segmentio/asm v1.2.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.8.0 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect go.starlark.net v0.0.0-20250826212936-2a4f36945129 // indirect - golang.org/x/arch v0.20.0 // indirect - golang.org/x/crypto v0.41.0 // indirect - golang.org/x/net v0.43.0 // indirect + golang.org/x/arch v0.23.0 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/net v0.48.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.12.0 // indirect - google.golang.org/protobuf v1.36.9 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 5a7f2d70..304844fd 100644 --- a/go.sum +++ b/go.sum @@ -34,10 +34,12 @@ 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/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -74,6 +76,8 @@ 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/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= +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/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec= @@ -86,14 +90,16 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= -github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= +github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -113,18 +119,14 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-playground/validator/v10 v10.29.0 h1:lQlF5VNJWNlRbRZNeOIkWElR+1LL/OuHcc0Kp14w1xk= +github.com/go-playground/validator/v10 v10.29.0/go.mod h1:D6QxqeMlgIPuT02L66f2ccrZ7AGgHkzKmmTMZhk/Kc4= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-vela/sdk-go v0.27.2-0.20251230154052-e1dae5f07d93 h1:YzcZG/5nNGC8OylBkQkdDk47ikfWjHx1J01Gk8R32Uk= -github.com/go-vela/sdk-go v0.27.2-0.20251230154052-e1dae5f07d93/go.mod h1:hTkiVuPlurq/r/bAO0e74oDl0YeBfeHGbRI9H9MZVFA= -github.com/go-vela/server v0.27.3-0.20251230152334-d014b0feb8db h1:vVyCKnfsJhrXS3oT6cCxv4V4/M3plFAc/Fs/eAlUX4Q= -github.com/go-vela/server v0.27.3-0.20251230152334-d014b0feb8db/go.mod h1:8nFIwIrcWsfHzKYgMlj7rkN3pYdnSOVfcM7dJ2ci3b8= 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.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE= +github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= @@ -193,6 +195,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -208,21 +211,23 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= +github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38= +github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc/v3 v3.0.0 h1:nZUx/zFg5uc2rhlu1L1DidGr5Sj02JbXvGSpnY4LMrc= -github.com/lestrrat-go/httprc/v3 v3.0.0/go.mod h1:k2U1QIiyVqAKtkffbg+cUmsyiPGQsb9aAfNQiNFuQ9Q= -github.com/lestrrat-go/jwx/v3 v3.0.10 h1:XuoCBhZBncRIjMQ32HdEc76rH0xK/Qv2wq5TBouYJDw= -github.com/lestrrat-go/jwx/v3 v3.0.10/go.mod h1:kNMedLgTpHvPJkK5EMVa1JFz+UVyY2dMmZKu3qjl/Pk= -github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= -github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/httprc/v3 v3.0.2 h1:7u4HUaD0NQbf2/n5+fyp+T10hNCsAnwKfqn4A4Baif0= +github.com/lestrrat-go/httprc/v3 v3.0.2/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= +github.com/lestrrat-go/jwx/v3 v3.0.12 h1:p25r68Y4KrbBdYjIsQweYxq794CtGCzcrc5dGzJIRjg= +github.com/lestrrat-go/jwx/v3 v3.0.12/go.mod h1:HiUSaNmMLXgZ08OmGBaPVvoZQgJVOQphSrGr5zMamS8= github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -231,6 +236,12 @@ github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEu github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= +github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY= +github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.89 h1:hx4xV5wwTUfyv8LarhJAwNecnXpoTsj9v3f3q/ZkiJU= +github.com/minio/minio-go/v7 v7.0.89/go.mod h1:2rFnGAp02p7Dddo1Fq4S2wYOfpF0MUTSeLTRC90I204= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -266,8 +277,9 @@ github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -276,16 +288,18 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= +github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= -github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -303,23 +317,24 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +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/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 h1:Jjn3zoRz13f8b1bR6LrXWglx93Sbh4kYfwgmPju3E2k= github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2/go.mod h1:wocb5pNrj/sjhWB9J5jctnC0K2eisSdz/nJJBNFHo+A= github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c= github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ= -github.com/urfave/cli/v3 v3.5.0 h1:qCuFMmdayTF3zmjG8TSsoBzrDqszNrklYg2x3g4MSgw= -github.com/urfave/cli/v3 v3.5.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= +github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo= +github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= @@ -356,52 +371,50 @@ go.starlark.net v0.0.0-20250826212936-2a4f36945129 h1:YZ5gQdgswYWAsZfzXQOzH7l8gu go.starlark.net v0.0.0-20250826212936-2a4f36945129/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= -golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= +golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -409,8 +422,8 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -421,8 +434,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/step/skip.go b/internal/step/skip.go index b6f2ded7..e1e05165 100644 --- a/internal/step/skip.go +++ b/internal/step/skip.go @@ -8,17 +8,30 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" + "github.com/go-vela/server/storage" ) // Skip creates the ruledata from the build and repository // information and returns true if the data does not match // the ruleset for the given container. -func Skip(c *pipeline.Container, b *api.Build, status string) (bool, error) { +func Skip(c *pipeline.Container, b *api.Build, status string, storage storage.Storage) (bool, error) { // check if the container provided is empty if c == nil { return true, nil } + if !c.TestReport.Empty() { + if storage == nil { + return true, nil + } + + if !storage.StorageEnable() { + return true, nil + } + + return false, nil + } + event := b.GetEvent() action := b.GetEventAction() diff --git a/internal/step/skip_test.go b/internal/step/skip_test.go index e23eca2f..0e11757c 100644 --- a/internal/step/skip_test.go +++ b/internal/step/skip_test.go @@ -9,6 +9,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" + "github.com/go-vela/server/storage" ) func TestStep_Skip(t *testing.T) { @@ -211,54 +212,76 @@ func TestStep_Skip(t *testing.T) { Name: constants.InitName, Number: 1, Pull: "always", + TestReport: pipeline.TestReport{ + Results: []string{"foo.xml", "bar.xml"}, + Attachments: []string{"foo.txt", "bar.txt"}, + }, + } + + s := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "minioadmin", + SecretKey: "minioadmin", + Bucket: "vela", } + _storage, _ := storage.New(s) tests := []struct { name string build *api.Build container *pipeline.Container + storage *storage.Storage want bool }{ { name: "build", build: _build, container: _container, + storage: &_storage, want: false, }, { name: "comment", build: _comment, container: _container, + storage: &_storage, want: false, }, { name: "deploy", build: _deploy, container: _container, + storage: &_storage, want: false, }, { name: "deployFromTag", build: _deployFromTag, container: _container, + storage: &_storage, want: false, }, { name: "schedule", build: _schedule, container: _container, + storage: &_storage, want: false, }, { name: "tag", build: _tag, container: _container, + storage: &_storage, want: false, }, { name: "skip nil", build: nil, container: nil, + storage: &_storage, want: true, }, } @@ -266,7 +289,7 @@ func TestStep_Skip(t *testing.T) { // run test for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := Skip(test.container, test.build, test.build.GetStatus()) + got, err := Skip(test.container, test.build, test.build.GetStatus(), *test.storage) if err != nil { t.Errorf("Skip returned error: %s", err) } diff --git a/router/middleware/executor/executor_test.go b/router/middleware/executor/executor_test.go index ced87338..09fbc9e1 100644 --- a/router/middleware/executor/executor_test.go +++ b/router/middleware/executor/executor_test.go @@ -14,6 +14,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/constants" + "github.com/go-vela/server/storage" "github.com/go-vela/worker/executor" "github.com/go-vela/worker/runtime/docker" ) @@ -33,6 +34,22 @@ func TestExecutor_Retrieve(t *testing.T) { _build := new(api.Build) _build.SetRepo(_repo) + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + want, err := executor.New(&executor.Setup{ Driver: constants.DriverLinux, MaxLogSize: 2097152, @@ -40,6 +57,7 @@ func TestExecutor_Retrieve(t *testing.T) { Runtime: _runtime, Build: _build, Pipeline: new(pipeline.Build), + Storage: _s, }) if err != nil { t.Errorf("unable to create executor engine: %v", err) @@ -72,6 +90,22 @@ func TestExecutor_Establish(t *testing.T) { _build := new(api.Build) _build.SetRepo(_repo) + _storage := &storage.Setup{ + Enable: true, + Driver: "minio", + Endpoint: "http://localhost:9000", + AccessKey: "ad", + SecretKey: "asd", + Bucket: "vela", + Region: "", + Secure: false, + } + + _s, err := storage.New(_storage) + if err != nil { + t.Errorf("unable to create storage engine: %v", err) + } + want, err := executor.New(&executor.Setup{ Driver: constants.DriverLinux, MaxLogSize: 2097152, @@ -79,6 +113,7 @@ func TestExecutor_Establish(t *testing.T) { Runtime: _runtime, Build: _build, Pipeline: new(pipeline.Build), + Storage: _s, }) if err != nil { t.Errorf("unable to create executor engine: %v", err) diff --git a/runtime/docker/test_report.go b/runtime/docker/test_report.go new file mode 100644 index 00000000..424b3725 --- /dev/null +++ b/runtime/docker/test_report.go @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: Apache-2.0 + +package docker + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + "io" + "path/filepath" + "strings" + + dockerContainerTypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/pkg/stdcopy" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/constants" +) + +// isAllowedExt returns true if ext (".xml", ".png", etc.) is in your allow-list. +func isAllowedExt(ext string) bool { + ext = strings.ToLower(ext) + for _, a := range constants.AllAllowedExtensions { + if ext == a { + return true + } + } + + return false +} + +// execContainerLines runs `sh -c cmd` in the named container and +// returns its stdout split by newline (error if anything on stderr). +func (c *client) execContainerLines(ctx context.Context, containerID, cmd string) ([]string, error) { + execConfig := dockerContainerTypes.ExecOptions{ + Tty: true, + Cmd: []string{"sh", "-c", cmd}, + AttachStdout: true, + AttachStderr: true, + } + + resp, err := c.Docker.ContainerExecCreate(ctx, containerID, execConfig) + if err != nil { + return nil, fmt.Errorf("create exec: %w", err) + } + + attach, err := c.Docker.ContainerExecAttach(ctx, resp.ID, dockerContainerTypes.ExecAttachOptions{}) + if err != nil { + return nil, fmt.Errorf("attach exec: %w", err) + } + + defer attach.Close() + + var outBuf, errBuf bytes.Buffer + if _, err := stdcopy.StdCopy(&outBuf, &errBuf, attach.Reader); err != nil { + return nil, fmt.Errorf("copy exec output: %w", err) + } + + if errBuf.Len() > 0 { + return nil, fmt.Errorf("exec error: %s", errBuf.String()) + } + + lines := strings.Split(strings.TrimSpace(outBuf.String()), "\n") + + return lines, nil +} + +// PollFileNames searches for files matching the provided patterns within a container. +func (c *client) PollFileNames(ctx context.Context, ctn *pipeline.Container, paths []string) ([]string, error) { + c.Logger.Tracef("gathering files from container %s", ctn.ID) + + if ctn.Image == "" { + return nil, nil + } + + var results []string + + for _, pattern := range paths { + // use find command to locate files matching the pattern + cmd := fmt.Sprintf("find / -type f -path '*%s' -print", pattern) + c.Logger.Debugf("searching for files with pattern: %s", pattern) + + lines, err := c.execContainerLines(ctx, ctn.ID, cmd) + if err != nil { + return nil, fmt.Errorf("failed to search for pattern %q: %w", pattern, err) + } + + c.Logger.Tracef("found %d candidates for pattern %s", len(lines), pattern) + + // process each found file + for _, line := range lines { + filePath := filepath.Clean(strings.TrimSpace(line)) + if filePath == "" { + continue + } + + // check if file extension is allowed + ext := strings.ToLower(filepath.Ext(filePath)) + if !isAllowedExt(ext) { + c.Logger.Debugf("skipping file %s (extension %s not allowed)", filePath, ext) + continue + } + + c.Logger.Debugf("accepted file: %s", filePath) + results = append(results, filePath) + } + } + + if len(results) == 0 { + return results, fmt.Errorf("no matching files found for patterns: %v", paths) + } + + c.Logger.Infof("found %d files matching patterns", len(results)) + + return results, nil +} + +// PollFileContent retrieves the content and size of a file inside a container. +func (c *client) PollFileContent(ctx context.Context, ctn *pipeline.Container, path string) (io.Reader, int64, error) { + c.Logger.Tracef("gathering test results and attachments from container %s", ctn.ID) + + if len(ctn.Image) == 0 { + // return an empty reader instead of nil + return bytes.NewReader(nil), 0, fmt.Errorf("empty container image") + } + + cmd := []string{"sh", "-c", fmt.Sprintf("base64 %s", path)} + execConfig := dockerContainerTypes.ExecOptions{ + Cmd: cmd, + AttachStdout: true, + AttachStderr: false, + Tty: false, + } + + c.Logger.Infof("executing command for content: %v", execConfig.Cmd) + + execID, err := c.Docker.ContainerExecCreate(ctx, ctn.ID, execConfig) + if err != nil { + c.Logger.Debugf("PollFileContent exec-create failed for %q: %v", path, err) + return nil, 0, fmt.Errorf("failed to create exec instance: %w", err) + } + + resp, err := c.Docker.ContainerExecAttach(ctx, execID.ID, dockerContainerTypes.ExecAttachOptions{}) + if err != nil { + c.Logger.Debugf("PollFileContent exec-attach failed for %q: %v", path, err) + return nil, 0, fmt.Errorf("failed to attach to exec instance: %w", err) + } + + defer func() { + if resp.Conn != nil { + resp.Close() + } + }() + + outputStdout := new(bytes.Buffer) + outputStderr := new(bytes.Buffer) + + if resp.Reader != nil { + _, err := stdcopy.StdCopy(outputStdout, outputStderr, resp.Reader) + if err != nil { + c.Logger.Errorf("unable to copy logs for container: %v", err) + } + } + + if outputStderr.Len() > 0 { + return nil, 0, fmt.Errorf("error: %s", outputStderr.String()) + } + + data := outputStdout.Bytes() + + // Add logging for empty data in PollFileContent + if len(data) == 0 { + c.Logger.Errorf("PollFileContent returned no data for path: %s", path) + return nil, 0, fmt.Errorf("no data returned from base64 command") + } + + decoded, err := base64.StdEncoding.DecodeString(string(data)) + if err != nil { + c.Logger.Errorf("unable to decode base64 data: %v", err) + return nil, 0, fmt.Errorf("failed to decode base64 data: %w", err) + } + + return bytes.NewReader(decoded), int64(len(decoded)), nil +} diff --git a/runtime/docker/test_report_test.go b/runtime/docker/test_report_test.go new file mode 100644 index 00000000..85df4ded --- /dev/null +++ b/runtime/docker/test_report_test.go @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 + +package docker + +// +//import ( +// "context" +// "github.com/go-vela/server/compiler/types/pipeline" +// "testing" +//) +// +//func TestClient_TestReport(t *testing.T) { +// // setup client +// _engine, err := NewMock() +// if err != nil { +// t.Errorf("unable to create runtime engine: %v", err) +// } +// +// // setup tests +// tests := []struct { +// name string +// failure bool +// container *pipeline.Container +// }{ +// { +// name: "valid test report", +// failure: false, +// container: &pipeline.Container{ +// ID: "test_container", +// TestReport: pipeline.TestReport{ +// Results: []string{"test_result_path"}, +// Attachments: []string{"test_attachment_path"}, +// }, +// }, +// }, +// { +// name: "no results provided", +// failure: true, +// container: &pipeline.Container{ +// ID: "test_container", +// TestReport: pipeline.TestReport{ +// Results: []string{}, +// Attachments: []string{"test_attachment_path"}, +// }, +// }, +// }, +// { +// name: "no attachments provided", +// failure: true, +// container: &pipeline.Container{ +// ID: "test_container", +// TestReport: pipeline.TestReport{ +// Results: []string{"test_result_path"}, +// Attachments: []string{}, +// }, +// }, +// }, +// } +// +// // run tests +// for _, test := range tests { +// t.Run(test.name, func(t *testing.T) { +// err := _engine.TestReport(context.Background(), test.container) +// +// if test.failure { +// if err == nil { +// t.Errorf("TestReport should have returned err") +// } +// +// return // continue to next test +// } +// +// if err != nil { +// t.Errorf("TestReport returned err: %v", err) +// } +// }) +// } +//} diff --git a/runtime/engine.go b/runtime/engine.go index e842a9c0..f615df06 100644 --- a/runtime/engine.go +++ b/runtime/engine.go @@ -94,4 +94,12 @@ type Engine interface { // RemoveVolume defines a function that // deletes the pipeline volume. RemoveVolume(context.Context, *pipeline.Build) error + // TestReport Interface functions + + // PollFileNames defines a function that + // captures the test results from the pipeline container. + PollFileNames(ctx context.Context, ctn *pipeline.Container, paths []string) ([]string, error) + // PollFileContent defines a function that + // captures the content and size of a file from the pipeline container. + PollFileContent(ctx context.Context, ctn *pipeline.Container, path string) (io.Reader, int64, error) } diff --git a/runtime/kubernetes/container.go b/runtime/kubernetes/container.go index 5321253e..65bae84e 100644 --- a/runtime/kubernetes/container.go +++ b/runtime/kubernetes/container.go @@ -77,6 +77,22 @@ func (c *client) PollOutputsContainer(_ context.Context, ctn *pipeline.Container return nil, nil } +// PollFileNames grabs test results and attachments from provided path within a container. +// This is a no-op for kubernetes. Pod environments cannot be dynamic. +func (c *client) PollFileNames(_ context.Context, ctn *pipeline.Container, paths []string) ([]string, error) { + c.Logger.Tracef("no-op: gathering test results and attachments from container %s", ctn.ID) + + return nil, nil +} + +// PollFileContent captures the content and size of a file from the pipeline container. +// This is a no-op for kubernetes. Pod environments cannot be dynamic. +func (c *client) PollFileContent(_ context.Context, ctn *pipeline.Container, path string) (io.Reader, int64, error) { + c.Logger.Tracef("no-op: gathering test results and attachments from container %s", ctn.ID) + + return nil, 0, nil +} + // RunContainer creates and starts the pipeline container. func (c *client) RunContainer(ctx context.Context, ctn *pipeline.Container, _ *pipeline.Build) error { c.Logger.Tracef("running container %s", ctn.ID)