From a2042f318f47ae8ee951018a4bb9392749498006 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Thu, 12 Dec 2024 09:41:14 -0600 Subject: [PATCH 01/41] ground work --- api/types/test-report/code_coverage_file.go | 214 +++++++ api/types/test-report/code_coverage_group.go | 99 ++++ api/types/test-report/code_coverage_run.go | 57 ++ api/types/test-report/code_coverage_stats.go | 214 +++++++ api/types/test-report/performance_results.go | 130 +++++ api/types/test-report/results_metadata.go | 115 ++++ api/types/test-report/results_processing.go | 118 ++++ .../test-report/results_processing_failure.go | 169 ++++++ api/types/test-report/shedlock.go | 115 ++++ api/types/test-report/test_case.go | 419 +++++++++++++ api/types/test-report/test_failure.go | 132 +++++ api/types/test-report/test_run.go | 319 ++++++++++ api/types/test-report/test_run_attachment.go | 11 + .../test-report/test_run_system_attributes.go | 47 ++ api/types/test-report/test_suite.go | 552 ++++++++++++++++++ api/types/test-report/test_suite_group.go | 142 +++++ cmd/vela-server/main.go | 4 + cmd/vela-server/server.go | 6 + constants/driver.go | 8 + docker-compose.yml | 10 + go.mod | 33 +- go.sum | 67 ++- storage/context.go | 59 ++ storage/context_test.go | 118 ++++ storage/flags.go | 49 ++ storage/minio/bucket_exists.go | 16 + storage/minio/create_bucket.go | 25 + storage/minio/delete_bucket.go | 14 + storage/minio/doc.go | 1 + storage/minio/download.go | 16 + storage/minio/get_bucket_lifecycle.go | 20 + storage/minio/list_bucket.go | 21 + storage/minio/list_objects.go | 23 + storage/minio/minio.go | 115 ++++ storage/minio/opts.go | 54 ++ storage/minio/set_bucket_lifecycle.go | 20 + storage/minio/upload.go | 16 + storage/service.go | 28 + storage/setup.go | 73 +++ storage/storage.go | 55 ++ 40 files changed, 3689 insertions(+), 15 deletions(-) create mode 100644 api/types/test-report/code_coverage_file.go create mode 100644 api/types/test-report/code_coverage_group.go create mode 100644 api/types/test-report/code_coverage_run.go create mode 100644 api/types/test-report/code_coverage_stats.go create mode 100644 api/types/test-report/performance_results.go create mode 100644 api/types/test-report/results_metadata.go create mode 100644 api/types/test-report/results_processing.go create mode 100644 api/types/test-report/results_processing_failure.go create mode 100644 api/types/test-report/shedlock.go create mode 100644 api/types/test-report/test_case.go create mode 100644 api/types/test-report/test_failure.go create mode 100644 api/types/test-report/test_run.go create mode 100644 api/types/test-report/test_run_attachment.go create mode 100644 api/types/test-report/test_run_system_attributes.go create mode 100644 api/types/test-report/test_suite.go create mode 100644 api/types/test-report/test_suite_group.go create mode 100644 storage/context.go create mode 100644 storage/context_test.go create mode 100644 storage/flags.go create mode 100644 storage/minio/bucket_exists.go create mode 100644 storage/minio/create_bucket.go create mode 100644 storage/minio/delete_bucket.go create mode 100644 storage/minio/doc.go create mode 100644 storage/minio/download.go create mode 100644 storage/minio/get_bucket_lifecycle.go create mode 100644 storage/minio/list_bucket.go create mode 100644 storage/minio/list_objects.go create mode 100644 storage/minio/minio.go create mode 100644 storage/minio/opts.go create mode 100644 storage/minio/set_bucket_lifecycle.go create mode 100644 storage/minio/upload.go create mode 100644 storage/service.go create mode 100644 storage/setup.go create mode 100644 storage/storage.go diff --git a/api/types/test-report/code_coverage_file.go b/api/types/test-report/code_coverage_file.go new file mode 100644 index 000000000..31b513b93 --- /dev/null +++ b/api/types/test-report/code_coverage_file.go @@ -0,0 +1,214 @@ +package test_report + +// CodeCoverageFile is the API types representation of a code coverage file for a pipeline. +// +// swagger:model CodeCoverageFile +type CodeCoverageFile struct { + ID *int64 `json:"id,omitempty"` + CodeCoverageRun *CodeCoverageRun `json:"code_coverage_run,omitempty"` + CodeCoverageGroup *CodeCoverageGroup `json:"code_coverage_group,omitempty"` + CodeCoverageStats *CodeCoverageStats `json:"code_coverage_stats,omitempty"` + DirectoryName *string `json:"directory_name,omitempty"` + FileName *string `json:"file_name,omitempty"` + MissedLines *int `json:"missed_lines,omitempty"` + PartialLines *int `json:"partial_lines,omitempty"` + FilePath *string `json:"file_path,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// SetID sets the ID field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetID(v int64) { + if c == nil { + return + } + c.ID = &v +} + +// GetCodeCoverageRun returns the CodeCoverageRun field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetCodeCoverageRun() *CodeCoverageRun { + if c == nil || c.CodeCoverageRun == nil { + return new(CodeCoverageRun) + } + return c.CodeCoverageRun +} + +// SetCodeCoverageRun sets the CodeCoverageRun field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetCodeCoverageRun(v *CodeCoverageRun) { + if c == nil { + return + } + c.CodeCoverageRun = v +} + +// GetCodeCoverageGroup returns the CodeCoverageGroup field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetCodeCoverageGroup() *CodeCoverageGroup { + if c == nil || c.CodeCoverageGroup == nil { + return new(CodeCoverageGroup) + } + return c.CodeCoverageGroup +} + +// SetCodeCoverageGroup sets the CodeCoverageGroup field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetCodeCoverageGroup(v *CodeCoverageGroup) { + if c == nil { + return + } + c.CodeCoverageGroup = v +} + +// GetCodeCoverageStats returns the CodeCoverageStats field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetCodeCoverageStats() *CodeCoverageStats { + if c == nil || c.CodeCoverageStats == nil { + return new(CodeCoverageStats) + } + return c.CodeCoverageStats +} + +// SetCodeCoverageStats sets the CodeCoverageStats field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetCodeCoverageStats(v *CodeCoverageStats) { + if c == nil { + return + } + c.CodeCoverageStats = v +} + +// GetDirectoryName returns the DirectoryName field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetDirectoryName() string { + if c == nil || c.DirectoryName == nil { + return "" + } + return *c.DirectoryName +} + +// SetDirectoryName sets the DirectoryName field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetDirectoryName(v string) { + if c == nil { + return + } + c.DirectoryName = &v +} + +// GetFileName returns the FileName field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetFileName() string { + if c == nil || c.FileName == nil { + return "" + } + return *c.FileName +} + +// SetFileName sets the FileName field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetFileName(v string) { + if c == nil { + return + } + c.FileName = &v +} + +// GetMissedLines returns the MissedLines field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetMissedLines() int { + if c == nil || c.MissedLines == nil { + return 0 + } + return *c.MissedLines +} + +// SetMissedLines sets the MissedLines field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetMissedLines(v int) { + if c == nil { + return + } + c.MissedLines = &v +} + +// GetPartialLines returns the PartialLines field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetPartialLines() int { + if c == nil || c.PartialLines == nil { + return 0 + } + return *c.PartialLines +} + +// SetPartialLines sets the PartialLines field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetPartialLines(v int) { + if c == nil { + return + } + c.PartialLines = &v +} + +// GetFilePath returns the FilePath field. +// +// When the provided CodeCoverageFile type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageFile) GetFilePath() string { + if c == nil || c.FilePath == nil { + return "" + } + return *c.FilePath +} + +// SetFilePath sets the FilePath field. +// +// When the provided CodeCoverageFile type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageFile) SetFilePath(v string) { + if c == nil { + return + } + c.FilePath = &v +} diff --git a/api/types/test-report/code_coverage_group.go b/api/types/test-report/code_coverage_group.go new file mode 100644 index 000000000..88e832701 --- /dev/null +++ b/api/types/test-report/code_coverage_group.go @@ -0,0 +1,99 @@ +package test_report + +// CodeCoverageGroup is the API types representation of a group of code coverage. +// +// swagger:model CodeCoverageGroup +type CodeCoverageGroup struct { + ID *int64 `json:"id,omitempty"` + CodeCoverageRun *CodeCoverageRun `json:"code_coverage_run,omitempty"` + Name *string `json:"name,omitempty"` + CodeCoverageStats *CodeCoverageStats `json:"code_coverage_stats,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided CodeCoverageGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageGroup) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// SetID sets the ID field. +// +// When the provided CodeCoverageGroup type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageGroup) SetID(v int64) { + if c == nil { + return + } + c.ID = &v +} + +// GetCodeCoverageRun returns the CodeCoverageRun field. +// +// When the provided CodeCoverageGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageGroup) GetCodeCoverageRun() *CodeCoverageRun { + if c == nil || c.CodeCoverageRun == nil { + return new(CodeCoverageRun) + } + return c.CodeCoverageRun +} + +// SetCodeCoverageRun sets the CodeCoverageRun field. +// +// When the provided CodeCoverageGroup type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageGroup) SetCodeCoverageRun(v *CodeCoverageRun) { + if c == nil { + return + } + c.CodeCoverageRun = v +} + +// GetName returns the Name field. +// +// When the provided CodeCoverageGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageGroup) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// SetName sets the Name field. +// +// When the provided CodeCoverageGroup type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageGroup) SetName(v string) { + if c == nil { + return + } + c.Name = &v +} + +// GetCodeCoverageStats returns the CodeCoverageStats field. +// +// When the provided CodeCoverageGroup type is nil, or the field within +// the type is nil, it returns a new CodeCoverageStats instance. +func (c *CodeCoverageGroup) GetCodeCoverageStats() *CodeCoverageStats { + if c == nil || c.CodeCoverageStats == nil { + return new(CodeCoverageStats) + } + return c.CodeCoverageStats +} + +// SetCodeCoverageStats sets the CodeCoverageStats field. +// +// When the provided CodeCoverageGroup type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageGroup) SetCodeCoverageStats(v *CodeCoverageStats) { + if c == nil { + return + } + c.CodeCoverageStats = v +} diff --git a/api/types/test-report/code_coverage_run.go b/api/types/test-report/code_coverage_run.go new file mode 100644 index 000000000..23128be83 --- /dev/null +++ b/api/types/test-report/code_coverage_run.go @@ -0,0 +1,57 @@ +package test_report + +// CodeCoverageRun is the API types representation of a code coverage run for a pipeline. +// +// swagger:model CodeCoverageRun +type CodeCoverageRun struct { + ID *int64 `json:"id,omitempty"` + TestRunPublicID *string `json:"test_run_public_id,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided CodeCoverageRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageRun) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetTestRunPublicID returns the TestRunPublicID field. +// +// When the provided CodeCoverageRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageRun) GetTestRunPublicID() string { + if c == nil || c.TestRunPublicID == nil { + return "" + } + return *c.TestRunPublicID +} + +// SetID sets the ID field. +// +// When the provided CodeCoverageRun type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageRun) SetID(v int64) { + // return if CodeCoverageRun type is nil + if c == nil { + return + } + + c.ID = &v +} + +// SetTestRunPublicID sets the TestRunPublicID field. +// +// When the provided CodeCoverageRun type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageRun) SetTestRunPublicID(v string) { + // return if CodeCoverageRun type is nil + if c == nil { + return + } + + c.TestRunPublicID = &v +} diff --git a/api/types/test-report/code_coverage_stats.go b/api/types/test-report/code_coverage_stats.go new file mode 100644 index 000000000..5fce2aca8 --- /dev/null +++ b/api/types/test-report/code_coverage_stats.go @@ -0,0 +1,214 @@ +package test_report + +// CodeCoverageStats is the API types representation of code coverage stats for a pipeline. +// +// swagger:model CodeCoverageStats +type CodeCoverageStats struct { + ID *int64 `json:"id,omitempty"` + CodeCoverageRun *CodeCoverageRun `json:"code_coverage_run,omitempty"` + Scope *string `json:"scope,omitempty"` + StatementCovered *int `json:"statement_covered,omitempty"` + StatementMissed *int `json:"statement_missed,omitempty"` + LineCovered *int `json:"line_covered,omitempty"` + LineMissed *int `json:"line_missed,omitempty"` + BranchCovered *int `json:"branch_covered,omitempty"` + BranchMissed *int `json:"branch_missed,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// SetID sets the ID field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetID(v int64) { + if c == nil { + return + } + c.ID = &v +} + +// GetCodeCoverageRun returns the CodeCoverageRun field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetCodeCoverageRun() *CodeCoverageRun { + if c == nil || c.CodeCoverageRun == nil { + return new(CodeCoverageRun) + } + return c.CodeCoverageRun +} + +// SetCodeCoverageRun sets the CodeCoverageRun field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetCodeCoverageRun(v *CodeCoverageRun) { + if c == nil { + return + } + c.CodeCoverageRun = v +} + +// GetScope returns the Scope field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetScope() string { + if c == nil || c.Scope == nil { + return "" + } + return *c.Scope +} + +// SetScope sets the Scope field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetScope(v string) { + if c == nil { + return + } + c.Scope = &v +} + +// GetStatementCovered returns the StatementCovered field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetStatementCovered() int { + if c == nil || c.StatementCovered == nil { + return 0 + } + return *c.StatementCovered +} + +// SetStatementCovered sets the StatementCovered field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetStatementCovered(v int) { + if c == nil { + return + } + c.StatementCovered = &v +} + +// GetStatementMissed returns the StatementMissed field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetStatementMissed() int { + if c == nil || c.StatementMissed == nil { + return 0 + } + return *c.StatementMissed +} + +// SetStatementMissed sets the StatementMissed field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetStatementMissed(v int) { + if c == nil { + return + } + c.StatementMissed = &v +} + +// GetLineCovered returns the LineCovered field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetLineCovered() int { + if c == nil || c.LineCovered == nil { + return 0 + } + return *c.LineCovered +} + +// SetLineCovered sets the LineCovered field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetLineCovered(v int) { + if c == nil { + return + } + c.LineCovered = &v +} + +// GetLineMissed returns the LineMissed field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetLineMissed() int { + if c == nil || c.LineMissed == nil { + return 0 + } + return *c.LineMissed +} + +// SetLineMissed sets the LineMissed field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetLineMissed(v int) { + if c == nil { + return + } + c.LineMissed = &v +} + +// GetBranchCovered returns the BranchCovered field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetBranchCovered() int { + if c == nil || c.BranchCovered == nil { + return 0 + } + return *c.BranchCovered +} + +// SetBranchCovered sets the BranchCovered field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetBranchCovered(v int) { + if c == nil { + return + } + c.BranchCovered = &v +} + +// GetBranchMissed returns the BranchMissed field. +// +// When the provided CodeCoverageStats type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (c *CodeCoverageStats) GetBranchMissed() int { + if c == nil || c.BranchMissed == nil { + return 0 + } + return *c.BranchMissed +} + +// SetBranchMissed sets the BranchMissed field. +// +// When the provided CodeCoverageStats type is nil, it +// will set nothing and immediately return. +func (c *CodeCoverageStats) SetBranchMissed(v int) { + if c == nil { + return + } + c.BranchMissed = &v +} diff --git a/api/types/test-report/performance_results.go b/api/types/test-report/performance_results.go new file mode 100644 index 000000000..f29b6da7c --- /dev/null +++ b/api/types/test-report/performance_results.go @@ -0,0 +1,130 @@ +package test_report + +// PerformanceResults is the API types representation of performance results for a test run. +// +// swagger:model PerformanceResults +type PerformanceResults struct { + ID *int64 `json:"id,omitempty"` + TestRun *TestRun `json:"test_run,omitempty"` + TestRunPublicID *string `json:"test_run_public_id,omitempty"` + Name *string `json:"name,omitempty"` + RequestCount *int64 `json:"request_count,omitempty"` + RequestsPerSecond *float64 `json:"requests_per_second,omitempty"` + Average *float64 `json:"average,omitempty"` + Maximum *float64 `json:"maximum,omitempty"` + P95 *float64 `json:"p95,omitempty"` +} // GetID returns the ID field. +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetID() int64 { + // return zero value if PerformanceResults type or ID field is nil + if p == nil || p.ID == nil { + return 0 + } + + return *p.ID +} + +// GetTestRun returns the TestRun field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetTestRun() TestRun { + // return zero value if PerformanceResults type or TestRun field is nil + if p == nil || p.TestRun == nil { + return TestRun{} + } + + return *p.TestRun +} + +// GetTestRunPublicID returns the TestRunPublicID field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetTestRunPublicID() string { + // return zero value if PerformanceResults type or TestRunPublicID field is nil + if p == nil || p.TestRunPublicID == nil { + return "" + } + + return *p.TestRunPublicID +} + +// GetName returns the Name field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetName() string { + // return zero value if PerformanceResults type or Name field is nil + if p == nil || p.Name == nil { + return "" + } + + return *p.Name +} + +// GetRequestCount returns the RequestCount field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetRequestCount() int64 { + // return zero value if PerformanceResults type or RequestCount field is nil + if p == nil || p.RequestCount == nil { + return 0 + } + + return *p.RequestCount +} + +// GetRequestsPerSecond returns the RequestsPerSecond field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetRequestsPerSecond() float64 { + // return zero value if PerformanceResults type or RequestsPerSecond field is nil + if p == nil || p.RequestsPerSecond == nil { + return 0.0 + } + + return *p.RequestsPerSecond +} + +// GetAverage returns the Average field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetAverage() float64 { + // return zero value if PerformanceResults type or Average field is nil + if p == nil || p.Average == nil { + return 0.0 + } + + return *p.Average +} + +// GetMaximum returns the Maximum field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetMaximum() float64 { + // return zero value if PerformanceResults type or Maximum field is nil + if p == nil || p.Maximum == nil { + return 0.0 + } + + return *p.Maximum +} + +// GetP95 returns the P95 field. +// +// When the provided PerformanceResults type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *PerformanceResults) GetP95() float64 { + // return zero value if PerformanceResults type or P95 field is nil + if p == nil || p.P95 == nil { + return 0.0 + } + + return *p.P95 +} diff --git a/api/types/test-report/results_metadata.go b/api/types/test-report/results_metadata.go new file mode 100644 index 000000000..d9a33333c --- /dev/null +++ b/api/types/test-report/results_metadata.go @@ -0,0 +1,115 @@ +package test_report + +// ResultsMetadata is the API types representation of metadata for a test run. +// +// swagger:model ResultsMetadata +type ResultsMetadata struct { + ID *int64 `json:"id,omitempty"` + TestRun *TestRun `json:"test_run,omitempty"` + CI *bool `json:"ci,omitempty"` + Group *string `json:"group,omitempty"` +} + +// SetID sets the ID field. +// +// When the provided ResultsMetadata type is nil, it +// will set nothing and immediately return. +func (r *ResultsMetadata) SetID(v int64) { + // return if ResultsMetadata type is nil + if r == nil { + return + } + + r.ID = &v +} + +// GetID returns the ID field. +// +// When the provided ResultsMetadata type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsMetadata) GetID() int64 { + // return zero value if ResultsMetadata type or ID field is nil + if r == nil || r.ID == nil { + return 0 + } + + return *r.ID +} + +// SetTestRun sets the TestRun field. +// +// When the provided ResultsMetadata type is nil, it +// will set nothing and immediately return. +func (r *ResultsMetadata) SetTestRun(v TestRun) { + // return if ResultsMetadata type is nil + if r == nil { + return + } + + r.TestRun = &v +} + +// GetTestRun returns the TestRun field. +// +// When the provided ResultsMetadata type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsMetadata) GetTestRun() TestRun { + // return zero value if ResultsMetadata type or TestRun field is nil + if r == nil || r.TestRun == nil { + return TestRun{} + } + + return *r.TestRun +} + +// SetCI sets the CI field. +// +// When the provided ResultsMetadata type is nil, it +// will set nothing and immediately return. +func (r *ResultsMetadata) SetCI(v bool) { + // return if ResultsMetadata type is nil + if r == nil { + return + } + + r.CI = &v +} + +// GetCI returns the CI field. +// +// When the provided ResultsMetadata type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsMetadata) GetCI() bool { + // return zero value if ResultsMetadata type or CI field is nil + if r == nil || r.CI == nil { + return false + } + + return *r.CI +} + +// SetGroup sets the Group field. +// +// When the provided ResultsMetadata type is nil, it +// will set nothing and immediately return. +func (r *ResultsMetadata) SetGroup(v string) { + // return if ResultsMetadata type is nil + if r == nil { + return + } + + r.Group = &v +} + +// GetGroup returns the Group field. +// +// When the provided ResultsMetadata type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsMetadata) GetGroup() string { + // return zero value if ResultsMetadata type or Group field is nil + if r == nil || r.Group == nil { + return "" + } + + return *r.Group +} diff --git a/api/types/test-report/results_processing.go b/api/types/test-report/results_processing.go new file mode 100644 index 000000000..840aea5c4 --- /dev/null +++ b/api/types/test-report/results_processing.go @@ -0,0 +1,118 @@ +package test_report + +// ResultsProcessing is the API types representation of processing results for a test run. +// +// swagger:model ResultsProcessing +type ResultsProcessing struct { + ID *int64 `json:"id,omitempty"` + Status *string `json:"status,omitempty"` + ErrorMessage *string `json:"error_message,omitempty"` + Created *int64 `json:"created,omitempty"` + //Created replaced created_timestamp in Projektor model + // Following the same pattern as other fields in Vela project + // PublicID is replaced by ID for consistency across all models +} + +// SetID sets the PublicID field. +// +// When the provided ResultsProcessing type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessing) SetID(v int64) { + // return if ResultsProcessing type is nil + if r == nil { + return + } + + r.ID = &v +} + +// GetPublicID returns the PublicID field. +// +// When the provided ResultsProcessing type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessing) GetPublicID() int64 { + // return zero value if ResultsProcessing type or PublicID field is nil + if r == nil || r.ID == nil { + return 0 + } + + return *r.ID +} + +// SetStatus sets the Status field. +// +// When the provided ResultsProcessing type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessing) SetStatus(v string) { + // return if ResultsProcessing type is nil + if r == nil { + return + } + + r.Status = &v +} + +// GetStatus returns the Status field. +// +// When the provided ResultsProcessing type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessing) GetStatus() string { + // return zero value if ResultsProcessing type or Status field is nil + if r == nil || r.Status == nil { + return "" + } + + return *r.Status +} + +// SetErrorMessage sets the ErrorMessage field. +// +// When the provided ResultsProcessing type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessing) SetErrorMessage(v string) { + // return if ResultsProcessing type is nil + if r == nil { + return + } + + r.ErrorMessage = &v +} + +// GetErrorMessage returns the ErrorMessage field. +// +// When the provided ResultsProcessing type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessing) GetErrorMessage() string { + // return zero value if ResultsProcessing type or ErrorMessage field is nil + if r == nil || r.ErrorMessage == nil { + return "" + } + + return *r.ErrorMessage +} + +// SetCreated sets the Created field. +// +// When the provided ResultsProcessing type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessing) SetCreated(v int64) { + // return if ResultsProcessing type is nil + if r == nil { + return + } + + r.Created = &v +} + +// GetCreated returns the Created field. +// +// When the provided ResultsProcessing type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessing) GetCreated() int64 { + // return zero value if ResultsProcessing type or Created field is nil + if r == nil || r.Created == nil { + return 0 + } + + return *r.Created +} diff --git a/api/types/test-report/results_processing_failure.go b/api/types/test-report/results_processing_failure.go new file mode 100644 index 000000000..ad9ffd26a --- /dev/null +++ b/api/types/test-report/results_processing_failure.go @@ -0,0 +1,169 @@ +package test_report + +// ResultsProcessingFailure is the API types representation of a failure processing test results. +// +// swagger:model ResultsProcessingFailure +type ResultsProcessingFailure struct { + ID *int64 `json:"id,omitempty"` + Body *string `json:"body,omitempty"` + Created *int64 `json:"created,omitempty"` + FailureMessage *string `json:"failure_message,omitempty"` + FailureType *string `json:"failure_type,omitempty"` + BodyType *string `json:"body_type,omitempty"` +} + +// SetID sets the ID field. +// +// When the provided ResultsProcessingFailure type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessingFailure) SetID(v int64) { + // return if ResultsProcessingFailure type is nil + if r == nil { + return + } + + r.ID = &v +} + +// GetID returns the ID field. +// +// When the provided ResultsProcessingFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessingFailure) GetID() int64 { + // return zero value if ResultsProcessingFailure type or ID field is nil + if r == nil || r.ID == nil { + return 0 + } + + return *r.ID +} + +// SetBody sets the Body field. +// +// When the provided ResultsProcessingFailure type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessingFailure) SetBody(v string) { + // return if ResultsProcessingFailure type is nil + if r == nil { + return + } + + r.Body = &v +} + +// GetBody returns the Body field. +// +// When the provided ResultsProcessingFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessingFailure) GetBody() string { + // return zero value if ResultsProcessingFailure type or Body field is nil + if r == nil || r.Body == nil { + return "" + } + + return *r.Body +} + +// SetCreated sets the Created field. +// +// When the provided ResultsProcessingFailure type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessingFailure) SetCreated(v int64) { + // return if ResultsProcessingFailure type is nil + if r == nil { + return + } + + r.Created = &v +} + +// GetCreated returns the Created field. +// +// When the provided ResultsProcessingFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessingFailure) GetCreated() int64 { + // return zero value if ResultsProcessingFailure type or Created field is nil + if r == nil || r.Created == nil { + return 0 + } + + return *r.Created +} + +// SetFailureMessage sets the FailureMessage field. +// +// When the provided ResultsProcessingFailure type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessingFailure) SetFailureMessage(v string) { + // return if ResultsProcessingFailure type is nil + if r == nil { + return + } + + r.FailureMessage = &v +} + +// GetFailureMessage returns the FailureMessage field. +// +// When the provided ResultsProcessingFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessingFailure) GetFailureMessage() string { + // return zero value if ResultsProcessingFailure type or FailureMessage field is nil + if r == nil || r.FailureMessage == nil { + return "" + } + + return *r.FailureMessage +} + +// SetFailureType sets the FailureType field. +// +// When the provided ResultsProcessingFailure type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessingFailure) SetFailureType(v string) { + // return if ResultsProcessingFailure type is nil + if r == nil { + return + } + + r.FailureType = &v +} + +// GetFailureType returns the FailureType field. +// +// When the provided ResultsProcessingFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessingFailure) GetFailureType() string { + // return zero value if ResultsProcessingFailure type or FailureType field is nil + if r == nil || r.FailureType == nil { + return "" + } + + return *r.FailureType +} + +// SetBodyType sets the BodyType field. +// +// When the provided ResultsProcessingFailure type is nil, it +// will set nothing and immediately return. +func (r *ResultsProcessingFailure) SetBodyType(v string) { + // return if ResultsProcessingFailure type is nil + if r == nil { + return + } + + r.BodyType = &v +} + +// GetBodyType returns the BodyType field. +// +// When the provided ResultsProcessingFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *ResultsProcessingFailure) GetBodyType() string { + // return zero value if ResultsProcessingFailure type or BodyType field is nil + if r == nil || r.BodyType == nil { + return "" + } + + return *r.BodyType +} diff --git a/api/types/test-report/shedlock.go b/api/types/test-report/shedlock.go new file mode 100644 index 000000000..767b987dc --- /dev/null +++ b/api/types/test-report/shedlock.go @@ -0,0 +1,115 @@ +package test_report + +// Shedlock is the API types representation of a shedlock lock. +// +// swagger:model Shedlock +type Shedlock struct { + Name *string `json:"name,omitempty"` + LockUntil *int64 `json:"lock_until,omitempty"` + LockAt *int64 `json:"lock_at,omitempty"` + LockedBy *string `json:"locked_by,omitempty"` +} + +// SetName sets the Name field. +// +// When the provided Shedlock type is nil, it +// will set nothing and immediately return. +func (s *Shedlock) SetName(v string) { + // return if Shedlock type is nil + if s == nil { + return + } + + s.Name = &v +} + +// GetName returns the Name field. +// +// When the provided Shedlock type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (s *Shedlock) GetName() string { + // return zero value if Shedlock type or Name field is nil + if s == nil || s.Name == nil { + return "" + } + + return *s.Name +} + +// SetLockUntil sets the LockUntil field. +// +// When the provided Shedlock type is nil, it +// will set nothing and immediately return. +func (s *Shedlock) SetLockUntil(v int64) { + // return if Shedlock type is nil + if s == nil { + return + } + + s.LockUntil = &v +} + +// GetLockUntil returns the LockUntil field. +// +// When the provided Shedlock type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (s *Shedlock) GetLockUntil() int64 { + // return zero value if Shedlock type or LockUntil field is nil + if s == nil || s.LockUntil == nil { + return 0 + } + + return *s.LockUntil +} + +// SetLockAt sets the LockAt field. +// +// When the provided Shedlock type is nil, it +// will set nothing and immediately return. +func (s *Shedlock) SetLockAt(v int64) { + // return if Shedlock type is nil + if s == nil { + return + } + + s.LockAt = &v +} + +// GetLockAt returns the LockAt field. +// +// When the provided Shedlock type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (s *Shedlock) GetLockAt() int64 { + // return zero value if Shedlock type or LockAt field is nil + if s == nil || s.LockAt == nil { + return 0 + } + + return *s.LockAt +} + +// SetLockedBy sets the LockedBy field. +// +// When the provided Shedlock type is nil, it +// will set nothing and immediately return. +func (s *Shedlock) SetLockedBy(v string) { + // return if Shedlock type is nil + if s == nil { + return + } + + s.LockedBy = &v +} + +// GetLockedBy returns the LockedBy field. +// +// When the provided Shedlock type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (s *Shedlock) GetLockedBy() string { + // return zero value if Shedlock type or LockedBy field is nil + if s == nil || s.LockedBy == nil { + return "" + } + + return *s.LockedBy +} diff --git a/api/types/test-report/test_case.go b/api/types/test-report/test_case.go new file mode 100644 index 000000000..2ad1e16cc --- /dev/null +++ b/api/types/test-report/test_case.go @@ -0,0 +1,419 @@ +package test_report + +import "time" + +// TestCases is the API types representation of a test case. +// +// swagger:model TestCase + +type TestCase struct { + ID *int64 `json:"id,omitempty"` + TestSuite *TestSuite `json:"test_suite,omitempty"` + Idx *int64 `json:"idx,omitempty"` + Name *string `json:"name,omitempty"` + PackageName *string `json:"package_name,omitempty"` + ClassName *string `json:"class_name,omitempty"` + Created *int64 `json:"created,omitempty"` + Started *int64 `json:"started,omitempty"` + Finished *int64 `json:"finished,omitempty"` + Passed *bool `json:"passed,omitempty"` + Skipped *bool `json:"skipped,omitempty"` + SystemOut *string `json:"system_out,omitempty"` + SystemErr *string `json:"system_err,omitempty"` + HasSystemOut *bool `json:"has_system_out,omitempty"` + HasSystemErr *bool `json:"has_system_err,omitempty"` +} + +// Duration calculates and returns the total amount of +// time the test case ran for in a human-readable format. +func (t *TestCase) Duration() string { + // check if the test case doesn't have a started timestamp + if t.GetStarted() == 0 { + return "..." + } + + // capture started unix timestamp from the test case + started := time.Unix(t.GetStarted(), 0) + + // check if the test case doesn't have a finished timestamp + if t.GetFinished() == 0 { + // return the duration in a human-readable form by + // subtracting the test case started time from the + // current time rounded to the nearest second + return time.Since(started).Round(time.Second).String() + } + + // capture finished unix timestamp from the test case + finished := time.Unix(t.GetFinished(), 0) + + // calculate the duration by subtracting the test case + // started time from the test case finished time + duration := finished.Sub(started) + + // return the duration in a human-readable form + return duration.String() +} + +// SetID sets the ID field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetID(v int64) { + // return if TestCase type is nil + if t == nil { + return + } + + t.ID = &v +} + +// GetID returns the ID field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetID() int64 { + // return zero value if TestCase type or ID field is nil + if t == nil || t.ID == nil { + return 0 + } + + return *t.ID +} + +// SetIdx sets the Idx field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetIdx(v int64) { + // return if TestCase type is nil + if t == nil { + return + } + + t.Idx = &v +} + +// GetIdx returns the Idx field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetIdx() int64 { + // return zero value if TestCase type or Idx field is nil + if t == nil || t.Idx == nil { + return 0 + } + + return *t.Idx +} + +// SetName sets the Name field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetName(v string) { + // return if TestCase type is nil + if t == nil { + return + } + + t.Name = &v +} + +// GetName returns the Name field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetName() string { + // return zero value if TestCase type or Name field is nil + if t == nil || t.Name == nil { + return "" + } + + return *t.Name +} + +// SetPackageName sets the PackageName field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetPackageName(v string) { + // return if TestCase type is nil + if t == nil { + return + } + + t.PackageName = &v +} + +// GetPackageName returns the PackageName field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetPackageName() string { + // return zero value if TestCase type or PackageName field is nil + if t == nil || t.PackageName == nil { + return "" + } + + return *t.PackageName +} + +// SetClassName sets the ClassName field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetClassName(v string) { + // return if TestCase type is nil + if t == nil { + return + } + + t.ClassName = &v +} + +// GetClassName returns the ClassName field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetClassName() string { + // return zero value if TestCase type or ClassName field is nil + if t == nil || t.ClassName == nil { + return "" + } + + return *t.ClassName +} + +// SetCreated sets the Created field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetCreated(v int64) { + // return if TestCase type is nil + if t == nil { + return + } + + t.Created = &v +} + +// GetCreated returns the Created field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetCreated() int64 { + // return zero value if TestCase type or Created field is nil + if t == nil || t.Created == nil { + return 0 + } + + return *t.Created +} + +// SetStarted sets the Started field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetStarted(v int64) { + // return if TestCase type is nil + if t == nil { + return + } + + t.Started = &v +} + +// GetStarted returns the Started field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetStarted() int64 { + // return zero value if TestCase type or Started field is nil + if t == nil || t.Started == nil { + return 0 + } + + return *t.Started +} + +// SetFinished sets the Finished field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetFinished(v int64) { + // return if TestCase type is nil + if t == nil { + return + } + + t.Finished = &v +} + +// GetFinished returns the Finished field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetFinished() int64 { + // return zero value if TestCase type or Finished field is nil + if t == nil || t.Finished == nil { + return 0 + } + + return *t.Finished +} + +// SetPassed sets the Passed field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetPassed(v bool) { + // return if TestCase type is nil + if t == nil { + return + } + + t.Passed = &v +} + +// GetPassed returns the Passed field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetPassed() bool { + // return zero value if TestCase type or Passed field is nil + if t == nil || t.Passed == nil { + return false + } + + return *t.Passed +} + +// SetSkipped sets the Skipped field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetSkipped(v bool) { + // return if TestCase type is nil + if t == nil { + return + } + + t.Skipped = &v +} + +// GetSkipped returns the Skipped field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetSkipped() bool { + // return zero value if TestCase type or Skipped field is nil + if t == nil || t.Skipped == nil { + return false + } + + return *t.Skipped +} + +// SetSystemOut sets the SystemOut field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetSystemOut(v string) { + // return if TestCase type is nil + if t == nil { + return + } + + t.SystemOut = &v +} + +// GetSystemOut returns the SystemOut field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetSystemOut() string { + // return zero value if TestCase type or SystemOut field is nil + if t == nil || t.SystemOut == nil { + return "" + } + + return *t.SystemOut +} + +// SetSystemErr sets the SystemErr field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetSystemErr(v string) { + // return if TestCase type is nil + if t == nil { + return + } + + t.SystemErr = &v +} + +// GetSystemErr returns the SystemErr field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetSystemErr() string { + // return zero value if TestCase type or SystemErr field is nil + if t == nil || t.SystemErr == nil { + return "" + } + + return *t.SystemErr +} + +// SetHasSystemOut sets the HasSystemOut field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetHasSystemOut(v bool) { + // return if TestCase type is nil + if t == nil { + return + } + + t.HasSystemOut = &v +} + +// GetHasSystemOut returns the HasSystemOut field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetHasSystemOut() bool { + // return zero value if TestCase type or HasSystemOut field is nil + if t == nil || t.HasSystemOut == nil { + return false + } + + return *t.HasSystemOut +} + +// SetHasSystemErr sets the HasSystemErr field. +// +// When the provided TestCase type is nil, it +// will set nothing and immediately return. +func (t *TestCase) SetHasSystemErr(v bool) { + // return if TestCase type is nil + if t == nil { + return + } + + t.HasSystemErr = &v +} + +// GetHasSystemErr returns the HasSystemErr field. +// +// When the provided TestCase type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestCase) GetHasSystemErr() bool { + // return zero value if TestCase type or HasSystemErr field is nil + if t == nil || t.HasSystemErr == nil { + return false + } + + return *t.HasSystemErr +} diff --git a/api/types/test-report/test_failure.go b/api/types/test-report/test_failure.go new file mode 100644 index 000000000..62cc83dfb --- /dev/null +++ b/api/types/test-report/test_failure.go @@ -0,0 +1,132 @@ +package test_report + +// TestFailure is the API types representation of a test for a pipeline. +// +// swagger:model TestFailure +type TestFailure struct { + ID *int64 `json:"id,omitempty"` + TestCaseID *int64 `json:"test_case_id,omitempty"` + FailureMessage *string `json:"failure_message,omitempty"` + FailureType *string `json:"failure_type,omitempty"` + FailureText *string `json:"failure_text,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided TestFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestFailure) GetID() int64 { + if t == nil || t.ID == nil { + return 0 + } + return *t.ID +} + +// GetTestCaseID returns the TestCaseID field. +// +// When the provided TestFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestFailure) GetTestCaseID() int64 { + if t == nil || t.TestCaseID == nil { + return 0 + } + return *t.TestCaseID +} + +// GetFailureMessage returns the FailureMessage field. +// +// When the provided TestFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestFailure) GetFailureMessage() string { + if t == nil || t.FailureMessage == nil { + return "" + } + return *t.FailureMessage +} + +// GetFailureType returns the FailureType field. +// +// When the provided TestFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestFailure) GetFailureType() string { + if t == nil || t.FailureType == nil { + return "" + } + return *t.FailureType +} + +// GetFailureText returns the FailureText field. +// +// When the provided TestFailure type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestFailure) GetFailureText() string { + if t == nil || t.FailureText == nil { + return "" + } + return *t.FailureText +} + +// SetID sets the ID field. +// +// When the provided TestFailure type is nil, it +// will set nothing and immediately return. +func (t *TestFailure) SetID(v int64) { + // return if TestFailure type is nil + if t == nil { + return + } + + t.ID = &v +} + +// SetTestCaseID sets the TestCaseID field. +// +// When the provided TestFailure type is nil, it +// will set nothing and immediately return. +func (t *TestFailure) SetTestCaseID(v int64) { + // return if TestFailure type is nil + if t == nil { + return + } + + t.TestCaseID = &v +} + +// SetFailureMessage sets the FailureMessage field. +// +// When the provided TestFailure type is nil, it +// will set nothing and immediately return. +func (t *TestFailure) SetFailureMessage(v string) { + // return if TestFailure type is nil + if t == nil { + return + } + + t.FailureMessage = &v +} + +// SetFailureType sets the FailureType field. +// +// When the provided TestFailure type is nil, it +// will set nothing and immediately return. +func (t *TestFailure) SetFailureType(v string) { + // return if TestFailure type is nil + if t == nil { + return + } + + t.FailureType = &v +} + +// SetFailureText sets the FailureText field. +// +// When the provided TestFailure type is nil, it +// will set nothing and immediately return. +func (t *TestFailure) SetFailureText(v string) { + // return if TestFailure type is nil + if t == nil { + return + } + + t.FailureText = &v +} diff --git a/api/types/test-report/test_run.go b/api/types/test-report/test_run.go new file mode 100644 index 000000000..2e9eaee52 --- /dev/null +++ b/api/types/test-report/test_run.go @@ -0,0 +1,319 @@ +package test_report + +import "time" + +// TestRun is the API types representation of a test run for a pipeline. +// +// swagger:model TestRun +type TestRun struct { + ID *int64 `json:"id,omitempty"` + TestRunSystemAttributes *TestRunSystemAttributes `json:"test_run_system_attributes,omitempty"` + TotalTestCount *int `json:"total_test_count,omitempty"` + TotalPassingCount *int `json:"total_passing_count,omitempty"` + TotalSkipCount *int `json:"total_skip_count,omitempty"` + TotalFailureCount *int `json:"total_failure_count,omitempty"` + Passed *bool `json:"passed,omitempty"` + Created *int64 `json:"created,omitempty"` + Started *int64 `json:"started,omitempty"` + Finished *int64 `json:"finished,omitempty"` + AverageDuration *int64 `json:"average_duration,omitempty"` + SlowestTestCaseDuration *int64 `json:"slowest_test_case_duration,omitempty"` + WallClockDuration *int64 `json:"wall_clock_duration,omitempty"` +} + +// In Vela, int64 is used to stored Unix timestamps instead of float64 for time.Time +// created_timestamp is replaced by Created +// Original Projektor model has cumulative_duration field which is not present in this model +// Created, Started, Finished are Vela standard model fields +// AverageDuration, SlowestTestCaseDuration, WallClockDuration might be taken out and calculated on the fly +// like in the Duration method + +// Duration calculates and returns the total amount of +// time the build ran for in a human-readable format. +func (b *TestRun) Duration() string { + // check if the build doesn't have a started timestamp + if b.GetStarted() == 0 { + return "..." + } + + // capture started unix timestamp from the build + started := time.Unix(b.GetStarted(), 0) + + // check if the build doesn't have a finished timestamp + if b.GetFinished() == 0 { + // return the duration in a human-readable form by + // subtracting the build started time from the + // current time rounded to the nearest second + return time.Since(started).Round(time.Second).String() + } + + // capture finished unix timestamp from the build + finished := time.Unix(b.GetFinished(), 0) + + // calculate the duration by subtracting the build + // started time from the build finished time + duration := finished.Sub(started) + + // return the duration in a human-readable form + return duration.String() +} + +// GetID returns the ID field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetID() int64 { + // return zero value if TestRun type or ID field is nil + if b == nil || b.ID == nil { + return 0 + } + return *b.ID +} + +// SetID sets the ID field. +func (b *TestRun) SetID(v int64) { + if b == nil { + return + } + b.ID = &v +} + +// GetTestRunSystemAttributes returns the TestRunSystemAttributes field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetTestRunSystemAttributes() TestRunSystemAttributes { + // return zero value if TestRun type or TestRunSystemAttributes field is nil + if b == nil || b.TestRunSystemAttributes == nil { + return TestRunSystemAttributes{} + } + return *b.TestRunSystemAttributes +} + +// SetTestRunSystemAttributes sets the TestRunSystemAttributes field. +func (b *TestRun) SetTestRunSystemAttributes(v TestRunSystemAttributes) { + if b == nil { + return + } + b.TestRunSystemAttributes = &v +} + +// GetTotalTestCount returns the TotalTestCount field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetTotalTestCount() int { + // return zero value if TestRun type or TotalTestCount field is nil + if b == nil || b.TotalTestCount == nil { + return 0 + } + return *b.TotalTestCount +} + +// SetTotalTestCount sets the TotalTestCount field. +func (b *TestRun) SetTotalTestCount(v int) { + if b == nil { + return + } + b.TotalTestCount = &v +} + +// GetTotalPassingCount returns the TotalPassingCount field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetTotalPassingCount() int { + // return zero value if TestRun type or TotalPassingCount field is nil + if b == nil || b.TotalPassingCount == nil { + return 0 + } + return *b.TotalPassingCount +} + +// SetTotalPassingCount sets the TotalPassingCount field. +func (b *TestRun) SetTotalPassingCount(v int) { + if b == nil { + return + } + b.TotalPassingCount = &v +} + +// GetTotalSkipCount returns the TotalSkipCount field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetTotalSkipCount() int { + // return zero value if TestRun type or TotalSkipCount field is nil + if b == nil || b.TotalSkipCount == nil { + return 0 + } + return *b.TotalSkipCount +} + +// SetTotalSkipCount sets the TotalSkipCount field. +func (b *TestRun) SetTotalSkipCount(v int) { + if b == nil { + return + } + b.TotalSkipCount = &v +} + +// GetTotalFailureCount returns the TotalFailureCount field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetTotalFailureCount() int { + // return zero value if TestRun type or TotalFailureCount field is nil + if b == nil || b.TotalFailureCount == nil { + return 0 + } + return *b.TotalFailureCount +} + +// SetTotalFailureCount sets the TotalFailureCount field. +func (b *TestRun) SetTotalFailureCount(v int) { + if b == nil { + return + } + b.TotalFailureCount = &v +} + +// GetPassed returns the Passed field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetPassed() bool { + // return zero value if TestRun type or Passed field is nil + if b == nil || b.Passed == nil { + return false + } + return *b.Passed +} + +// SetPassed sets the Passed field. +func (b *TestRun) SetPassed(v bool) { + if b == nil { + return + } + b.Passed = &v +} + +// GetCreated returns the Created field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetCreated() int64 { + // return zero value if TestRun type or Created field is nil + if b == nil || b.Created == nil { + return 0 + } + return *b.Created +} + +// SetCreated sets the Created field. +func (b *TestRun) SetCreated(v int64) { + if b == nil { + return + } + b.Created = &v +} + +// GetStarted returns the Started field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetStarted() int64 { + // return zero value if TestRun type or Started field is nil + if b == nil || b.Started == nil { + return 0 + } + return *b.Started +} + +// SetStarted sets the Started field. +func (b *TestRun) SetStarted(v int64) { + if b == nil { + return + } + b.Started = &v +} + +// GetFinished returns the Finished field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetFinished() int64 { + // return zero value if TestRun type or Finished field is nil + if b == nil || b.Finished == nil { + return 0 + } + return *b.Finished +} + +// SetFinished sets the Finished field. +func (b *TestRun) SetFinished(v int64) { + if b == nil { + return + } + b.Finished = &v +} + +// GetAverageDuration returns the AverageDuration field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetAverageDuration() int64 { + // return zero value if TestRun type or AverageDuration field is nil + if b == nil || b.AverageDuration == nil { + return 0 + } + return *b.AverageDuration +} + +// SetAverageDuration sets the AverageDuration field. +func (b *TestRun) SetAverageDuration(v int64) { + if b == nil { + return + } + b.AverageDuration = &v +} + +// GetSlowestTestCaseDuration returns the SlowestTestCaseDuration field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetSlowestTestCaseDuration() int64 { + // return zero value if TestRun type or SlowestTestCaseDuration field is nil + if b == nil || b.SlowestTestCaseDuration == nil { + return 0 + } + return *b.SlowestTestCaseDuration +} + +// SetSlowestTestCaseDuration sets the SlowestTestCaseDuration field. +func (b *TestRun) SetSlowestTestCaseDuration(v int64) { + if b == nil { + return + } + b.SlowestTestCaseDuration = &v +} + +// GetWallClockDuration returns the WallClockDuration field. +// +// When the provided TestRun type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRun) GetWallClockDuration() int64 { + // return zero value if TestRun type or WallClockDuration field is nil + if b == nil || b.WallClockDuration == nil { + return 0 + } + return *b.WallClockDuration +} + +// SetWallClockDuration sets the WallClockDuration field. +func (b *TestRun) SetWallClockDuration(v int64) { + if b == nil { + return + } + b.WallClockDuration = &v +} diff --git a/api/types/test-report/test_run_attachment.go b/api/types/test-report/test_run_attachment.go new file mode 100644 index 000000000..c5ca02d7e --- /dev/null +++ b/api/types/test-report/test_run_attachment.go @@ -0,0 +1,11 @@ +package test_report + +// TestRunAttachment is the API types representation of an attachment for a test run. +// +// swagger:model TestRunAttachment +type TestRunAttachment struct { + ID *int64 `json:"id,omitempty"` + FileName *string `json:"file_name,omitempty"` + ObjectName *string `json:"object_name,omitempty"` + FileSize *int64 `json:"file_size,omitempty"` +} diff --git a/api/types/test-report/test_run_system_attributes.go b/api/types/test-report/test_run_system_attributes.go new file mode 100644 index 000000000..6efb5ad9a --- /dev/null +++ b/api/types/test-report/test_run_system_attributes.go @@ -0,0 +1,47 @@ +package test_report + +// TestRunSystemAttributes is the API types representation of system attributes for a test run. +// +// swagger:model TestRunSystemAttributes +type TestRunSystemAttributes struct { + ID *int64 `json:"id,omitempty"` + Pinned *bool `json:"pinned,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided TestRunSystemAttributes type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRunSystemAttributes) GetID() int64 { + if b == nil || b.ID == nil { + return 0 + } + return *b.ID +} + +// SetID sets the ID field. +func (b *TestRunSystemAttributes) SetID(v int64) { + if b == nil { + return + } + b.ID = &v +} + +// GetPinned returns the Pinned field. +// +// When the provided TestRunSystemAttributes type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *TestRunSystemAttributes) GetPinned() bool { + if b == nil || b.Pinned == nil { + return false + } + return *b.Pinned +} + +// SetPinned sets the Pinned field. +func (b *TestRunSystemAttributes) SetPinned(v bool) { + if b == nil { + return + } + b.Pinned = &v +} diff --git a/api/types/test-report/test_suite.go b/api/types/test-report/test_suite.go new file mode 100644 index 000000000..6ad451f48 --- /dev/null +++ b/api/types/test-report/test_suite.go @@ -0,0 +1,552 @@ +package test_report + +import "time" + +// TestSuite is the API types representation of a test suite for a test run. +// +// swagger:model TestSuite +type TestSuite struct { + ID *int64 `json:"id,omitempty"` + TestRun *TestRun `json:"test_run,omitempty"` + TestSuiteGroup *TestSuiteGroup `json:"test_suite_group,omitempty"` + Idx *int64 `json:"idx,omitempty"` + PackageName *string `json:"package_name,omitempty"` + ClassName *string `json:"class_name,omitempty"` + TestCount *int64 `json:"test_count,omitempty"` + PassingCount *int64 `json:"passing_count,omitempty"` + SkippedCount *int64 `json:"skipped_count,omitempty"` + FailureCount *int64 `json:"failure_count,omitempty"` + StartTs *int64 `json:"start_ts,omitempty"` + Hostname *string `json:"hostname,omitempty"` + Started *int64 `json:"started,omitempty"` + Finished *int64 `json:"finished,omitempty"` + SystemOut *string `json:"system_out,omitempty"` + SystemErr *string `json:"system_err,omitempty"` + HasSystemOut *bool `json:"has_system_out,omitempty"` + HasSystemErr *bool `json:"has_system_err,omitempty"` + FileName *string `json:"file_name,omitempty"` +} + +// Duration calculates and returns the total amount of +// time the test suite ran for in a human-readable format. +func (t *TestSuite) Duration() string { + // check if the test suite doesn't have a started timestamp + if t.GetStarted() == 0 { + return "..." + } + + // capture started unix timestamp from the test suite + started := time.Unix(t.GetStarted(), 0) + + // check if the test suite doesn't have a finished timestamp + if t.GetFinished() == 0 { + // return the duration in a human-readable form by + // subtracting the test suite started time from the + // current time rounded to the nearest second + return time.Since(started).Round(time.Second).String() + } + + // capture finished unix timestamp from the test suite + finished := time.Unix(t.GetFinished(), 0) + + // calculate the duration by subtracting the test suite + // started time from the test suite finished time + duration := finished.Sub(started) + + // return the duration in a human-readable form + return duration.String() +} + +// SetID sets the ID field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetID(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.ID = &v +} + +// GetID returns the ID field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetID() int64 { + // return zero value if TestSuite type or ID field is nil + if t == nil || t.ID == nil { + return 0 + } + + return *t.ID +} + +// SetTestRun sets the TestRun field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetTestRun(v TestRun) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.TestRun = &v +} + +// GetTestRun returns the TestRun field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetTestRun() TestRun { + // return zero value if TestSuite type or TestRun field is nil + if t == nil || t.TestRun == nil { + return TestRun{} + } + + return *t.TestRun +} + +// SetTestSuiteGroup sets the TestSuiteGroup field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetTestSuiteGroup(v TestSuiteGroup) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.TestSuiteGroup = &v +} + +// GetTestSuiteGroup returns the TestSuiteGroup field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetTestSuiteGroup() TestSuiteGroup { + // return zero value if TestSuite type or TestSuiteGroup field is nil + if t == nil || t.TestSuiteGroup == nil { + return TestSuiteGroup{} + } + + return *t.TestSuiteGroup +} + +// SetIdx sets the Idx field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetIdx(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.Idx = &v +} + +// GetIdx returns the Idx field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetIdx() int64 { + // return zero value if TestSuite type or Idx field is nil + if t == nil || t.Idx == nil { + return 0 + } + + return *t.Idx +} + +// SetPackageName sets the PackageName field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetPackageName(v string) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.PackageName = &v +} + +// GetPackageName returns the PackageName field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetPackageName() string { + // return zero value if TestSuite type or PackageName field is nil + if t == nil || t.PackageName == nil { + return "" + } + + return *t.PackageName +} + +// SetClassName sets the ClassName field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetClassName(v string) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.ClassName = &v +} + +// GetClassName returns the ClassName field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetClassName() string { + // return zero value if TestSuite type or ClassName field is nil + if t == nil || t.ClassName == nil { + return "" + } + + return *t.ClassName +} + +// SetTestCount sets the TestCount field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetTestCount(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.TestCount = &v +} + +// GetTestCount returns the TestCount field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetTestCount() int64 { + // return zero value if TestSuite type or TestCount field is nil + if t == nil || t.TestCount == nil { + return 0 + } + + return *t.TestCount +} + +// SetPassingCount sets the PassingCount field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetPassingCount(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.PassingCount = &v +} + +// GetPassingCount returns the PassingCount field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetPassingCount() int64 { + // return zero value if TestSuite type or PassingCount field is nil + if t == nil || t.PassingCount == nil { + return 0 + } + + return *t.PassingCount +} + +// SetSkippedCount sets the SkippedCount field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetSkippedCount(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.SkippedCount = &v +} + +// GetSkippedCount returns the SkippedCount field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetSkippedCount() int64 { + // return zero value if TestSuite type or SkippedCount field is nil + if t == nil || t.SkippedCount == nil { + return 0 + } + + return *t.SkippedCount +} + +// SetFailureCount sets the FailureCount field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetFailureCount(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.FailureCount = &v +} + +// GetFailureCount returns the FailureCount field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetFailureCount() int64 { + // return zero value if TestSuite type or FailureCount field is nil + if t == nil || t.FailureCount == nil { + return 0 + } + + return *t.FailureCount +} + +// SetStartTs sets the StartTs field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetStartTs(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.StartTs = &v +} + +// GetStartTs returns the StartTs field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetStartTs() int64 { + // return zero value if TestSuite type or StartTs field is nil + if t == nil || t.StartTs == nil { + return 0 + } + + return *t.StartTs +} + +// SetHostname sets the Hostname field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetHostname(v string) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.Hostname = &v +} + +// GetHostname returns the Hostname field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetHostname() string { + // return zero value if TestSuite type or Hostname field is nil + if t == nil || t.Hostname == nil { + return "" + } + + return *t.Hostname +} + +// SetStarted sets the Started field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetStarted(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.Started = &v +} + +// GetStarted returns the Started field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetStarted() int64 { + // return zero value if TestSuite type or Started field is nil + if t == nil || t.Started == nil { + return 0 + } + + return *t.Started +} + +// SetFinished sets the Finished field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetFinished(v int64) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.Finished = &v +} + +// GetFinished returns the Finished field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetFinished() int64 { + // return zero value if TestSuite type or Finished field is nil + if t == nil || t.Finished == nil { + return 0 + } + + return *t.Finished +} + +// SetSystemOut sets the SystemOut field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetSystemOut(v string) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.SystemOut = &v +} + +// GetSystemOut returns the SystemOut field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetSystemOut() string { + // return zero value if TestSuite type or SystemOut field is nil + if t == nil || t.SystemOut == nil { + return "" + } + + return *t.SystemOut +} + +// SetSystemErr sets the SystemErr field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetSystemErr(v string) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.SystemErr = &v +} + +// GetSystemErr returns the SystemErr field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetSystemErr() string { + // return zero value if TestSuite type or SystemErr field is nil + if t == nil || t.SystemErr == nil { + return "" + } + + return *t.SystemErr +} + +// SetHasSystemOut sets the HasSystemOut field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetHasSystemOut(v bool) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.HasSystemOut = &v +} + +// GetHasSystemOut returns the HasSystemOut field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetHasSystemOut() bool { + // return zero value if TestSuite type or HasSystemOut field is nil + if t == nil || t.HasSystemOut == nil { + return false + } + + return *t.HasSystemOut +} + +// SetHasSystemErr sets the HasSystemErr field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetHasSystemErr(v bool) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.HasSystemErr = &v +} + +// GetHasSystemErr returns the HasSystemErr field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetHasSystemErr() bool { + // return zero value if TestSuite type or HasSystemErr field is nil + if t == nil || t.HasSystemErr == nil { + return false + } + + return *t.HasSystemErr +} + +// SetFileName sets the FileName field. +// +// When the provided TestSuite type is nil, it +// will set nothing and immediately return. +func (t *TestSuite) SetFileName(v string) { + // return if TestSuite type is nil + if t == nil { + return + } + + t.FileName = &v +} + +// GetFileName returns the FileName field. +// +// When the provided TestSuite type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuite) GetFileName() string { + // return zero value if TestSuite type or FileName field is nil + if t == nil || t.FileName == nil { + return "" + } + + return *t.FileName +} diff --git a/api/types/test-report/test_suite_group.go b/api/types/test-report/test_suite_group.go new file mode 100644 index 000000000..78ccc1d8f --- /dev/null +++ b/api/types/test-report/test_suite_group.go @@ -0,0 +1,142 @@ +package test_report + +// TestSuiteGroup is the API types representation of a group of test suites for a test run. +// +// swagger:model TestSuiteGroup +type TestSuiteGroup struct { + ID *int64 `json:"id,omitempty"` + TestRun *TestRun `json:"test_run,omitempty"` + GroupName *string `json:"group_name,omitempty"` + GroupLabel *string `json:"group_label,omitempty"` + Directory *string `json:"directory,omitempty"` +} + +// SetID sets the ID field. +// +// When the provided TestSuiteGroup type is nil, it +// will set nothing and immediately return. +func (t *TestSuiteGroup) SetID(v int64) { + // return if TestSuiteGroup type is nil + if t == nil { + return + } + + t.ID = &v +} + +// GetID returns the ID field. +// +// When the provided TestSuiteGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuiteGroup) GetID() int64 { + // return zero value if TestSuiteGroup type or ID field is nil + if t == nil || t.ID == nil { + return 0 + } + + return *t.ID +} + +// SetTestRun sets the TestRun field. +// +// When the provided TestSuiteGroup type is nil, it +// will set nothing and immediately return. +func (t *TestSuiteGroup) SetTestRun(v TestRun) { + // return if TestSuiteGroup type is nil + if t == nil { + return + } + + t.TestRun = &v +} + +// GetTestRun returns the TestRun field. +// +// When the provided TestSuiteGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuiteGroup) GetTestRun() TestRun { + // return zero value if TestSuiteGroup type or TestRun field is nil + if t == nil || t.TestRun == nil { + return TestRun{} + } + + return *t.TestRun +} + +// SetGroupName sets the GroupName field. +// +// When the provided TestSuiteGroup type is nil, it +// will set nothing and immediately return. +func (t *TestSuiteGroup) SetGroupName(v string) { + // return if TestSuiteGroup type is nil + if t == nil { + return + } + + t.GroupName = &v +} + +// GetGroupName returns the GroupName field. +// +// When the provided TestSuiteGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuiteGroup) GetGroupName() string { + // return zero value if TestSuiteGroup type or GroupName field is nil + if t == nil || t.GroupName == nil { + return "" + } + + return *t.GroupName +} + +// SetGroupLabel sets the GroupLabel field. +// +// When the provided TestSuiteGroup type is nil, it +// will set nothing and immediately return. +func (t *TestSuiteGroup) SetGroupLabel(v string) { + // return if TestSuiteGroup type is nil + if t == nil { + return + } + + t.GroupLabel = &v +} + +// GetGroupLabel returns the GroupLabel field. +// +// When the provided TestSuiteGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuiteGroup) GetGroupLabel() string { + // return zero value if TestSuiteGroup type or GroupLabel field is nil + if t == nil || t.GroupLabel == nil { + return "" + } + + return *t.GroupLabel +} + +// SetDirectory sets the Directory field. +// +// When the provided TestSuiteGroup type is nil, it +// will set nothing and immediately return. +func (t *TestSuiteGroup) SetDirectory(v string) { + // return if TestSuiteGroup type is nil + if t == nil { + return + } + + t.Directory = &v +} + +// GetDirectory returns the Directory field. +// +// When the provided TestSuiteGroup type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestSuiteGroup) GetDirectory() string { + // return zero value if TestSuiteGroup type or Directory field is nil + if t == nil || t.Directory == nil { + return "" + } + + return *t.Directory +} diff --git a/cmd/vela-server/main.go b/cmd/vela-server/main.go index 5af8a852c..27f1a2ff1 100644 --- a/cmd/vela-server/main.go +++ b/cmd/vela-server/main.go @@ -5,6 +5,7 @@ package main import ( "encoding/json" "fmt" + "github.com/go-vela/server/ex-storage" "os" "time" @@ -283,6 +284,9 @@ func main() { // Add Tracing Flags app.Flags = append(app.Flags, tracing.Flags...) + // Add S3 Flags + app.Flags = append(app.Flags, ex_storage.Flags...) + if err = app.Run(os.Args); err != nil { logrus.Fatal(err) } diff --git a/cmd/vela-server/server.go b/cmd/vela-server/server.go index 7733b9c15..90e98d78a 100644 --- a/cmd/vela-server/server.go +++ b/cmd/vela-server/server.go @@ -6,6 +6,7 @@ import ( "context" "errors" "fmt" + "github.com/go-vela/server/ex-storage" "net/http" "net/url" "os" @@ -93,6 +94,11 @@ func server(c *cli.Context) error { }() } + st, err := ex_storage.FromCLIContext(c) + if err != nil { + return err + } + database, err := database.FromCLIContext(c, tc) if err != nil { return err diff --git a/constants/driver.go b/constants/driver.go index e42b924c5..fb716e435 100644 --- a/constants/driver.go +++ b/constants/driver.go @@ -62,3 +62,11 @@ const ( // DriverGitLab defines the driver type when integrating with a Gitlab source code system. DriverGitlab = "gitlab" ) + +// Server storage drivers. +const ( + // DriverMinio defines the driver type when integrating with a local storage system. + DriverMinio = "minio" + // DriverAws defines the driver type when integrating with an AWS S3 storage system. + DriverAws = "aws" +) diff --git a/docker-compose.yml b/docker-compose.yml index a96b4b87f..74e5a2a34 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -179,5 +179,15 @@ services: - '16686:16686' - '4318:4318' + objectstorage: + image: bitnami/minio:2020.12.29-debian-10-r17 + ports: + - "9000:9000" + volumes: + - ./minio:/data + environment: + MINIO_ACCESS_KEY: minio_access_key + MINIO_SECRET_KEY: minio_secret_key + networks: vela: diff --git a/go.mod b/go.mod index 290078d95..5b6df7662 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,9 @@ require ( github.com/adhocore/gronx v1.19.1 github.com/alicebob/miniredis/v2 v2.33.0 github.com/aws/aws-sdk-go v1.55.5 + github.com/aws/aws-sdk-go-v2/config v1.28.6 + github.com/aws/aws-sdk-go-v2/credentials v1.17.47 + github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/distribution/reference v0.6.0 github.com/drone/envsubst v1.0.3 @@ -29,6 +32,7 @@ require ( github.com/lestrrat-go/jwx/v2 v2.1.1 github.com/lib/pq v1.10.9 github.com/microcosm-cc/bluemonday v1.0.27 + github.com/minio/minio-go/v7 v7.0.81 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.4 github.com/redis/go-redis/v9 v9.6.1 @@ -45,7 +49,7 @@ require ( go.opentelemetry.io/otel/sdk v1.30.0 go.opentelemetry.io/otel/trace v1.30.0 go.starlark.net v0.0.0-20240925182052-1207426daebd - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.28.0 golang.org/x/oauth2 v0.23.0 golang.org/x/sync v0.8.0 golang.org/x/time v0.6.0 @@ -61,6 +65,21 @@ require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect + github.com/aws/aws-sdk-go-v2 v1.32.6 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect + github.com/aws/smithy-go v1.22.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.12.2 // indirect @@ -72,10 +91,12 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -104,7 +125,7 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect @@ -114,6 +135,7 @@ require ( github.com/lestrrat-go/option v1.0.1 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -126,6 +148,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/segmentio/asm v1.2.0 // indirect @@ -140,9 +163,9 @@ require ( go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/arch v0.10.0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/grpc v1.66.1 // indirect diff --git a/go.sum b/go.sum index 1957fb598..9432f2853 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,42 @@ github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= +github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= +github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -68,6 +104,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= @@ -85,6 +123,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -181,8 +221,9 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -221,6 +262,10 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o github.com/mattn/go-sqlite3 v1.14.22/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/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.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA= +github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 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= @@ -261,6 +306,8 @@ github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0 github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -336,8 +383,8 @@ golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 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/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -345,8 +392,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -362,12 +409,12 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/storage/context.go b/storage/context.go new file mode 100644 index 000000000..411ba4412 --- /dev/null +++ b/storage/context.go @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 + +package storage + +import ( + "context" + "github.com/gin-gonic/gin" +) + +// key is the key used to store minio service in context +const key = "minio" + +// FromContext retrieves minio service from the context +func FromContext(ctx context.Context) Storage { + // get minio value from context.Context + v := ctx.Value(key) + if v == nil { + return nil + } + + // cast minio value to expected Storage type + s, ok := v.(Storage) + if !ok { + return nil + } + return s +} + +// FromGinContext retrieves the S3 Service from the gin.Context. +func FromGinContext(c *gin.Context) Storage { + // get minio value from gin.Context + // + // https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc#Context.Get + v, ok := c.Get(key) + if !ok { + return nil + } + + // cast minio value to expected Service type + s, ok := v.(Storage) + if !ok { + return nil + } + + return s +} + +// WithContext adds the minio Storage to the context +func WithContext(ctx context.Context, storage Storage) context.Context { + return context.WithValue(ctx, key, storage) +} + +// WithGinContext inserts the minio Storage into the gin.Context. +func WithGinContext(c *gin.Context, s Storage) { + // set the minio Storage in the gin.Context + // + // https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc#Context.Set + c.Set(key, s) +} diff --git a/storage/context_test.go b/storage/context_test.go new file mode 100644 index 000000000..9b96d83b9 --- /dev/null +++ b/storage/context_test.go @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 + +package storage + +import ( + "context" + "reflect" + "testing" + + "github.com/gin-gonic/gin" +) + +func TestExecutor_FromContext(t *testing.T) { + // setup types + _service, _ := New(&Setup{}) + + // setup tests + tests := []struct { + context context.Context + want Storage + }{ + { + //nolint:staticcheck,revive // ignore using string with context value + context: context.WithValue(context.Background(), key, _service), + want: _service, + }, + { + context: context.Background(), + want: nil, + }, + { + //nolint:staticcheck,revive // ignore using string with context value + context: context.WithValue(context.Background(), key, "foo"), + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := FromContext(test.context) + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("FromContext is %v, want %v", got, test.want) + } + } +} + +func TestExecutor_FromGinContext(t *testing.T) { + // setup types + _service, _ := New(&Setup{}) + + // setup tests + tests := []struct { + context *gin.Context + value interface{} + want Storage + }{ + { + context: new(gin.Context), + value: _service, + want: _service, + }, + { + context: new(gin.Context), + value: nil, + want: nil, + }, + { + context: new(gin.Context), + value: "foo", + want: nil, + }, + } + + // run tests + for _, test := range tests { + if test.value != nil { + test.context.Set(key, test.value) + } + + got := FromGinContext(test.context) + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("FromGinContext is %v, want %v", got, test.want) + } + } +} + +func TestExecutor_WithContext(t *testing.T) { + // setup types + _service, _ := New(&Setup{}) + + //nolint:staticcheck,revive // ignore using string with context value + want := context.WithValue(context.Background(), key, _service) + + // run test + got := WithContext(context.Background(), _service) + + if !reflect.DeepEqual(got, want) { + t.Errorf("WithContext is %v, want %v", got, want) + } +} + +func TestExecutor_WithGinContext(t *testing.T) { + // setup types + _service, _ := New(&Setup{}) + + want := new(gin.Context) + want.Set(key, _service) + + // run test + got := new(gin.Context) + WithGinContext(got, _service) + + if !reflect.DeepEqual(got, want) { + t.Errorf("WithGinContext is %v, want %v", got, want) + } +} diff --git a/storage/flags.go b/storage/flags.go new file mode 100644 index 000000000..6c1d780f8 --- /dev/null +++ b/storage/flags.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 + +package storage + +import ( + "github.com/urfave/cli/v2" +) + +var Flags = []cli.Flag{ + // STORAGE Flags + + &cli.BoolFlag{ + EnvVars: []string{"VELA_STORAGE_ENABLE"}, + Name: "storage.enable", + Usage: "enable object storage", + }, + &cli.StringFlag{ + EnvVars: []string{"VELA_STORAGE_DRIVER"}, + Name: "storage.driver.name", + Usage: "object storage driver", + }, + &cli.StringFlag{ + EnvVars: []string{"VELA_STORAGE_ENDPOINT"}, + Name: "storage.endpoint.name", + Usage: "set the storage endpoint (ex. scheme://host:port)", + }, + + &cli.StringFlag{ + EnvVars: []string{"VELA_STORAGE_ACCESS_KEY"}, + Name: "storage.access.key", + Usage: "set storage access key", + }, + &cli.StringFlag{ + EnvVars: []string{"VELA_STORAGE_SECRET_KEY"}, + Name: "storage.secret.key", + Usage: "set storage secret key", + }, + &cli.StringFlag{ + EnvVars: []string{"VELA_STORAGE_BUCKET"}, + Name: "storage.bucket.name", + Usage: "set storage bucket name", + }, + &cli.BoolFlag{ + EnvVars: []string{"VELA_STORAGE_USE_SSL"}, + Name: "storage.use.ssl", + Usage: "enable storage to use SSL", + Value: true, + }, +} diff --git a/storage/minio/bucket_exists.go b/storage/minio/bucket_exists.go new file mode 100644 index 000000000..4a3704011 --- /dev/null +++ b/storage/minio/bucket_exists.go @@ -0,0 +1,16 @@ +package minio + +import ( + "context" +) + +// BucketExists checks if a bucket exists in MinIO. +func (c *MinioClient) BucketExists(ctx context.Context, bucketName string) (bool, error) { + c.Logger.Tracef("checking if bucket %s exists", bucketName) + + exists, err := c.client.BucketExists(ctx, bucketName) + if err != nil { + return false, err + } + return exists, nil +} diff --git a/storage/minio/create_bucket.go b/storage/minio/create_bucket.go new file mode 100644 index 000000000..4a6739986 --- /dev/null +++ b/storage/minio/create_bucket.go @@ -0,0 +1,25 @@ +package minio + +import ( + "context" + "github.com/minio/minio-go/v7" +) + +// CreateBucket creates a new bucket in MinIO. +func (c *MinioClient) CreateBucket(ctx context.Context, bucketName string, opts *minio.MakeBucketOptions) error { + c.Logger.Tracef("create new bucket: %s", bucketName) + if opts == nil { + opts = &minio.MakeBucketOptions{} + c.Logger.Trace("Using US Standard Region as location default") + } + err := c.client.MakeBucket(ctx, bucketName, *opts) + if err != nil { + exists, errBucketExists := c.BucketExists(ctx, bucketName) + if errBucketExists == nil && exists { + c.Logger.Tracef("Bucket %s already exists", bucketName) + return nil + } + return err + } + return nil +} diff --git a/storage/minio/delete_bucket.go b/storage/minio/delete_bucket.go new file mode 100644 index 000000000..cf06c2081 --- /dev/null +++ b/storage/minio/delete_bucket.go @@ -0,0 +1,14 @@ +package minio + +import "context" + +// DeleteBucket deletes a bucket in MinIO. +func (c *MinioClient) DeleteBucket(ctx context.Context, bucketName string) error { + c.Logger.Tracef("deleting bucketName: %s", bucketName) + + err := c.client.RemoveBucket(ctx, bucketName) + if err != nil { + return err + } + return nil +} diff --git a/storage/minio/doc.go b/storage/minio/doc.go new file mode 100644 index 000000000..c5576b1fd --- /dev/null +++ b/storage/minio/doc.go @@ -0,0 +1 @@ +package minio diff --git a/storage/minio/download.go b/storage/minio/download.go new file mode 100644 index 000000000..c176b34b4 --- /dev/null +++ b/storage/minio/download.go @@ -0,0 +1,16 @@ +package minio + +import ( + "context" + "github.com/minio/minio-go/v7" + "io" +) + +func (c *MinioClient) Download(ctx context.Context, bucketName, key string) ([]byte, error) { + object, err := c.client.GetObject(ctx, bucketName, key, minio.GetObjectOptions{}) + if err != nil { + return nil, err + } + defer object.Close() + return io.ReadAll(object) +} diff --git a/storage/minio/get_bucket_lifecycle.go b/storage/minio/get_bucket_lifecycle.go new file mode 100644 index 000000000..0153a3eb2 --- /dev/null +++ b/storage/minio/get_bucket_lifecycle.go @@ -0,0 +1,20 @@ +package minio + +import ( + "context" + "encoding/xml" +) + +// GetBucketLifecycle retrieves the lifecycle configuration for a bucket. +func (c *MinioClient) GetBucketLifecycle(ctx context.Context, bucketName string) (string, error) { + c.Logger.Tracef("getting lifecycle configuration for bucket %s", bucketName) + + lifecycleConfig, err := c.client.GetBucketLifecycle(ctx, bucketName) + if err != nil { + return "", err + } + + lifecycleBytes, err := xml.MarshalIndent(lifecycleConfig, "", " ") + + return string(lifecycleBytes), nil +} diff --git a/storage/minio/list_bucket.go b/storage/minio/list_bucket.go new file mode 100644 index 000000000..a619e7705 --- /dev/null +++ b/storage/minio/list_bucket.go @@ -0,0 +1,21 @@ +package minio + +import ( + "context" +) + +// ListBuckets lists all buckets in MinIO. +func (c *MinioClient) ListBuckets(ctx context.Context) ([]string, error) { + c.Logger.Trace("listing all buckets") + + buckets, err := c.client.ListBuckets(ctx) + if err != nil { + return nil, err + } + + bucketNames := make([]string, len(buckets)) + for i, bucket := range buckets { + bucketNames[i] = bucket.Name + } + return bucketNames, nil +} diff --git a/storage/minio/list_objects.go b/storage/minio/list_objects.go new file mode 100644 index 000000000..31d8b5b8f --- /dev/null +++ b/storage/minio/list_objects.go @@ -0,0 +1,23 @@ +package minio + +import ( + "context" + "github.com/minio/minio-go/v7" +) + +// ListObjects lists the objects in a bucket. +func (c *MinioClient) ListObjects(ctx context.Context, bucketName string) ([]string, error) { + c.Logger.Tracef("listing objects in bucket %s", bucketName) + + objectCh := c.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{}) + + var objects []string + for object := range objectCh { + if object.Err != nil { + return nil, object.Err + } + objects = append(objects, object.Key) + } + + return objects, nil +} diff --git a/storage/minio/minio.go b/storage/minio/minio.go new file mode 100644 index 000000000..983a548a2 --- /dev/null +++ b/storage/minio/minio.go @@ -0,0 +1,115 @@ +package minio + +import ( + "context" + "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" + "time" +) + +// config holds the configuration for the MinIO client. +type config struct { + Endpoint string + AccessKey string + SecretKey string + Secure bool +} + +// MinioClient implements the Storage interface using MinIO. +type MinioClient struct { + config *config + client *minio.Client + Options *minio.Options + // https://pkg.go.dev/github.com/sirupsen/logrus#Entry + Logger *logrus.Entry +} + +// New creates a new MinIO client. +func New(endpoint string, opts ...ClientOpt) (*MinioClient, error) { + // create new Minio client + c := new(MinioClient) + + // create new fields + c.config = new(config) + c.Options = new(minio.Options) + + // create new logger for the client + logger := logrus.StandardLogger() + c.Logger = logrus.NewEntry(logger).WithField("storage", "minio") + + // apply all provided configuration options + for _, opt := range opts { + err := opt(c) + if err != nil { + return nil, err + } + } + + // create the Minio client from the provided endpoint and options + minioClient, err := minio.New(endpoint, c.Options) + if err != nil { + return nil, err + } + + c.client = minioClient + + return c, nil + //minioClient, err := minio.New(endpoint, &minio.Options{ + // Creds: credentials.NewStaticV4(accessKey, secretKey, ""), + // Secure: useSSL, + //}) + //if err != nil { + // return nil, err + //} + //return &MinioClient{client: minioClient}, nil +} + +// pingBucket checks if the specified bucket exists. +func pingBucket(c *MinioClient, bucket string) error { + for i := 0; i < 10; i++ { + _, err := c.client.BucketExists(context.Background(), bucket) + if err != nil { + c.Logger.Debugf("unable to ping %s. Retrying in %v", bucket, time.Duration(i)*time.Second) + time.Sleep(1 * time.Second) + + continue + } + } + + return nil +} + +//// UploadArtifact uploads an artifact to storage. +//func (c *MinioClient) UploadArtifact(ctx context.Context, workflowID, artifactName string, data []byte) error { +// key := path.Join("artifacts", workflowID, artifactName) +// bucket := "vela-artifacts" +// return c.upload(ctx, bucket, key, data) +//} +// +//// DownloadArtifact downloads an artifact from storage. +//func (c *MinioClient) DownloadArtifact(ctx context.Context, workflowID, artifactName string) ([]byte, error) { +// key := path.Join("artifacts", workflowID, artifactName) +// bucket := "vela-artifacts" +// return c.download(ctx, bucket, key) +//} +// +//// UploadCache uploads cache data to storage. +//func (c *MinioClient) UploadCache(ctx context.Context, key string, data []byte) error { +// cacheKey := path.Join("cache", key) +// bucket := "vela-cache" +// return c.upload(ctx, bucket, cacheKey, data) +//} +// +//// DownloadCache downloads cache data from storage. +//func (c *MinioClient) DownloadCache(ctx context.Context, key string) ([]byte, error) { +// cacheKey := path.Join("cache", key) +// bucket := "vela-cache" +// return c.download(ctx, bucket, cacheKey) +//} +// +//// DeleteCache deletes cache data from storage. +//func (c *MinioClient) DeleteCache(ctx context.Context, key string) error { +// cacheKey := path.Join("cache", key) +// bucket := "vela-cache" +// return c.client.RemoveObject(ctx, bucket, cacheKey, minio.RemoveObjectOptions{}) +//} diff --git a/storage/minio/opts.go b/storage/minio/opts.go new file mode 100644 index 000000000..a4ab86c5c --- /dev/null +++ b/storage/minio/opts.go @@ -0,0 +1,54 @@ +package minio + +import ( + "fmt" +) + +// ClientOpt represents a configuration option to initialize the MinIO client. +type ClientOpt func(client *MinioClient) error + +// WithAccessKey sets the access key in the MinIO client. +func WithAccessKey(accessKey string) ClientOpt { + return func(c *MinioClient) error { + c.Logger.Trace("configuring access key in minio client") + + // check if the access key provided is empty + if len(accessKey) == 0 { + return fmt.Errorf("no MinIO access key provided") + } + + // set the access key in the minio client + c.config.AccessKey = accessKey + + return nil + } +} + +// WithSecretKey sets the secret key in the MinIO client. +func WithSecretKey(secretKey string) ClientOpt { + return func(c *MinioClient) error { + c.Logger.Trace("configuring secret key in minio client") + + // check if the secret key provided is empty + if len(secretKey) == 0 { + return fmt.Errorf("no MinIO secret key provided") + } + + // set the secret key in the minio client + c.config.SecretKey = secretKey + + return nil + } +} + +// WithSecure sets the secure connection mode in the MinIO client. +func WithSecure(secure bool) ClientOpt { + return func(c *MinioClient) error { + c.Logger.Trace("configuring secure connection mode in minio client") + + // set the secure connection mode in the minio client + c.config.Secure = secure + + return nil + } +} diff --git a/storage/minio/set_bucket_lifecycle.go b/storage/minio/set_bucket_lifecycle.go new file mode 100644 index 000000000..8ea7a1993 --- /dev/null +++ b/storage/minio/set_bucket_lifecycle.go @@ -0,0 +1,20 @@ +package minio + +import ( + "context" + "encoding/xml" + "github.com/minio/minio-go/v7/pkg/lifecycle" +) + +// SetBucketLifecycle sets the lifecycle configuration for a bucket. +func (c *MinioClient) SetBucketLifecycle(ctx context.Context, bucketName string, lifecycleConfig string) error { + c.Logger.Tracef("setting lifecycle configuration for bucket %s", bucketName) + + var config lifecycle.Configuration + if err := xml.Unmarshal([]byte(lifecycleConfig), &config); err != nil { + c.Logger.Errorf("failed to unmarshal lifecycle configuration: %s", err) + return err + } + + return c.client.SetBucketLifecycle(ctx, bucketName, &config) +} diff --git a/storage/minio/upload.go b/storage/minio/upload.go new file mode 100644 index 000000000..10e50aaf1 --- /dev/null +++ b/storage/minio/upload.go @@ -0,0 +1,16 @@ +package minio + +import ( + "bytes" + "context" + "github.com/minio/minio-go/v7" +) + +// Helper methods for uploading objects +func (c *MinioClient) Upload(ctx context.Context, bucketName, objectName string, data []byte, contentType string) error { + c.Logger.Tracef("uploading data to bucket %s", bucketName) + + reader := bytes.NewReader(data) + _, err := c.client.PutObject(ctx, bucketName, objectName, reader, int64(len(data)), minio.PutObjectOptions{ContentType: contentType}) + return err +} diff --git a/storage/service.go b/storage/service.go new file mode 100644 index 000000000..552e66f08 --- /dev/null +++ b/storage/service.go @@ -0,0 +1,28 @@ +package storage + +import "context" + +// Storage defines the service interface for object storage operations. +type Storage interface { + // Bucket Management + CreateBucket(ctx context.Context, bucketName string) error + DeleteBucket(ctx context.Context, bucketName string) error + BucketExists(ctx context.Context, bucketName string) (bool, error) + ListBuckets(ctx context.Context) ([]string, error) + // Object Operations + Upload(ctx context.Context, bucketName string, objectName string, data []byte, contentType string) error + Download(ctx context.Context, bucketName string, objectName string) ([]byte, error) + Delete(ctx context.Context, bucketName string, objectName string) error + ListObjects(ctx context.Context, bucketName string, prefix string) ([]string, error) + //// Presigned URLs + //GeneratePresignedURL(ctx context.Context, bucket string, key string, expiry int64) (string, error) + // Object Lifecycle + SetBucketLifecycle(ctx context.Context, bucketName string, lifecycleConfig string) error + GetBucketLifecycle(ctx context.Context, bucketName string) (string, error) + //// Workflow-Specific Operations + //UploadArtifact(ctx context.Context, workflowID, artifactName string, data []byte) error + //DownloadArtifact(ctx context.Context, workflowID, artifactName string) ([]byte, error) + //UploadCache(ctx context.Context, key string, data []byte) error + //DownloadCache(ctx context.Context, key string) ([]byte, error) + //DeleteCache(ctx context.Context, key string) error +} diff --git a/storage/setup.go b/storage/setup.go new file mode 100644 index 000000000..f89354668 --- /dev/null +++ b/storage/setup.go @@ -0,0 +1,73 @@ +package storage + +import ( + "fmt" + "github.com/go-vela/server/storage/minio" + "github.com/sirupsen/logrus" +) + +// Setup represents the configuration necessary for +// creating a Vela service capable of integrating +// with a configured S3 environment. +type Setup struct { + Enable bool + Driver string + Endpoint string + AccessKey string + SecretKey string + Bucket string + Region string + Secure bool +} + +// Minio creates and returns a Vela service capable +// of integrating with an S3 environment. +func (s *Setup) Minio() (Storage, error) { + //client, err := minio.New(s.Endpoint, &minio.Options{ + // Creds: credentials.NewStaticV4(s.AccessKey, s.SecretKey, ""), + // Secure: s.Secure, + //}) + //if err != nil { + // return nil, err + //} + return minio.New( + s.Endpoint, + minio.WithAccessKey(s.AccessKey), + minio.WithSecretKey(s.SecretKey), + minio.WithSecure(s.Secure), + ) +} + +// Validate verifies the necessary fields for the +// provided configuration are populated correctly. +func (s *Setup) Validate() error { + logrus.Trace("validating Storage setup for client") + + // verify storage is enabled + if !s.Enable { + return fmt.Errorf("Storage is not enabled") + } + + // verify an endpoint was provided + if len(s.Endpoint) == 0 { + return fmt.Errorf("no storage endpoint provided") + } + + // verify an access key was provided + if len(s.AccessKey) == 0 { + return fmt.Errorf("no storage access key provided") + } + + // verify a secret key was provided + if len(s.SecretKey) == 0 { + return fmt.Errorf("no storage secret key provided") + } + + // verify a bucket was provided + if len(s.Bucket) == 0 { + return fmt.Errorf("no storage bucket provided") + } + + // setup is valid + return nil +} diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 000000000..c2e9cf955 --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,55 @@ +package storage + +import ( + "fmt" + "github.com/go-vela/server/constants" + "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" +) + +// FromCLIContext helper function to setup Minio Client from the CLI arguments. +func FromCLIContext(c *cli.Context) (Storage, error) { + logrus.Debug("creating Minio client from CLI configuration") + + // S3 configuration + _setup := &Setup{ + Enable: c.Bool("storage.enable"), + Driver: c.String("storage.driver.name"), + Endpoint: c.String("storage.endpoint.name"), + AccessKey: c.String("storage.access.key"), + SecretKey: c.String("storage.secret.key"), + Bucket: c.String("storage.bucket.name"), + Secure: c.Bool("storage.use.ssl"), + } + + return New(_setup) + +} + +// New creates and returns a Vela service capable of +// integrating with the configured storage environment. +// Currently, the following storages are supported: +// +// * minio +// . +func New(s *Setup) (Storage, error) { + // validate the setup being provided + // + err := s.Validate() + if err != nil { + return nil, err + } + logrus.Debug("creating storage client from setup") + // process the storage driver being provided + switch s.Driver { + case constants.DriverMinio: + // handle the Kafka queue driver being provided + // + // https://pkg.go.dev/github.com/go-vela/server/queue?tab=doc#Setup.Kafka + return s.Minio() + default: + // handle an invalid queue driver being provided + return nil, fmt.Errorf("invalid storage driver provided: %s", s.Driver) + } + +} From 8fb48dc0740f59fe809a505ab17cdfe85ec2fb4c Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:56:03 -0400 Subject: [PATCH 02/41] feat: add sender rule for pipelines (#1206) * refactor(pipeline): use server API types for pipeline and migrate compiler types * gci * feat: add sender rule for pipelines --------- Co-authored-by: David May <49894298+wass3rw3rk@users.noreply.github.com> --- api/pipeline/compile.go | 28 +++++-- api/pipeline/compile_test.go | 38 +++++---- compiler/native/compile.go | 1 + compiler/types/pipeline/ruleset.go | 12 ++- compiler/types/pipeline/ruleset_test.go | 5 ++ compiler/types/yaml/ruleset.go | 5 ++ compiler/types/yaml/ruleset_test.go | 82 +++++++++++-------- .../types/yaml/testdata/ruleset_simple.yml | 3 + 8 files changed, 114 insertions(+), 60 deletions(-) diff --git a/api/pipeline/compile.go b/api/pipeline/compile.go index f21f0a811..23f997fcc 100644 --- a/api/pipeline/compile.go +++ b/api/pipeline/compile.go @@ -119,8 +119,14 @@ func prepareRuleData(c *gin.Context) *pipeline.RuleData { comment := c.Query("comment") // capture the event type parameter event := c.Query("event") + // capture the instance parameter + instance := c.Query("instance") + // capture the label parameter + labelSet := c.QueryArray("label") // capture the repo parameter ruleDataRepo := c.Query("repo") + // capture the sender parameter + sender := c.Query("sender") // capture the status type parameter status := c.Query("status") // capture the tag parameter @@ -134,20 +140,26 @@ func prepareRuleData(c *gin.Context) *pipeline.RuleData { if len(branch) > 0 || len(comment) > 0 || len(event) > 0 || + len(instance) > 0 || + len(labelSet) > 0 || len(pathSet) > 0 || len(ruleDataRepo) > 0 || + len(sender) > 0 || len(status) > 0 || len(tag) > 0 || len(target) > 0 { return &pipeline.RuleData{ - Branch: branch, - Comment: comment, - Event: event, - Path: pathSet, - Repo: ruleDataRepo, - Status: status, - Tag: tag, - Target: target, + Branch: branch, + Comment: comment, + Event: event, + Instance: instance, + Label: labelSet, + Path: pathSet, + Repo: ruleDataRepo, + Sender: sender, + Status: status, + Tag: tag, + Target: target, } } diff --git a/api/pipeline/compile_test.go b/api/pipeline/compile_test.go index 05a3de45d..877bed8c7 100644 --- a/api/pipeline/compile_test.go +++ b/api/pipeline/compile_test.go @@ -26,24 +26,30 @@ func TestPrepareRuleData(t *testing.T) { { name: "all params provided", parameters: map[string]string{ - "branch": "main", - "comment": "Test comment", - "event": "push", - "repo": "my-repo", - "status": "success", - "tag": "v1.0.0", - "target": "production", - "path": "README.md", + "branch": "main", + "comment": "Test comment", + "event": "push", + "instance": "vela-server", + "label": "bug", + "repo": "my-repo", + "sender": "octocat", + "status": "success", + "tag": "v1.0.0", + "target": "production", + "path": "README.md", }, want: &pipeline.RuleData{ - Branch: "main", - Comment: "Test comment", - Event: "push", - Repo: "my-repo", - Status: "success", - Tag: "v1.0.0", - Target: "production", - Path: []string{"README.md"}, + Branch: "main", + Comment: "Test comment", + Event: "push", + Instance: "vela-server", + Label: []string{"bug"}, + Repo: "my-repo", + Sender: "octocat", + Status: "success", + Tag: "v1.0.0", + Target: "production", + Path: []string{"README.md"}, }, }, { diff --git a/compiler/native/compile.go b/compiler/native/compile.go index e39d43718..49d071431 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -67,6 +67,7 @@ func (c *client) Compile(ctx context.Context, v interface{}) (*pipeline.Build, * Event: event, Path: c.files, Repo: c.repo.GetFullName(), + Sender: c.build.GetSender(), Tag: strings.TrimPrefix(c.build.GetRef(), "refs/tags/"), Target: c.build.GetDeploy(), Label: c.labels, diff --git a/compiler/types/pipeline/ruleset.go b/compiler/types/pipeline/ruleset.go index a6e5101f6..36d6c1302 100644 --- a/compiler/types/pipeline/ruleset.go +++ b/compiler/types/pipeline/ruleset.go @@ -34,6 +34,7 @@ type ( Event Ruletype `json:"event,omitempty" yaml:"event,omitempty"` Path Ruletype `json:"path,omitempty" yaml:"path,omitempty"` Repo Ruletype `json:"repo,omitempty" yaml:"repo,omitempty"` + Sender Ruletype `json:"sender,omitempty" yaml:"sender,omitempty"` Status Ruletype `json:"status,omitempty" yaml:"status,omitempty"` Tag Ruletype `json:"tag,omitempty" yaml:"tag,omitempty"` Target Ruletype `json:"target,omitempty" yaml:"target,omitempty"` @@ -56,6 +57,7 @@ type ( Event string `json:"event,omitempty" yaml:"event,omitempty"` Path []string `json:"path,omitempty" yaml:"path,omitempty"` Repo string `json:"repo,omitempty" yaml:"repo,omitempty"` + Sender string `json:"sender,omitempty" yaml:"sender,omitempty"` Status string `json:"status,omitempty" yaml:"status,omitempty"` Tag string `json:"tag,omitempty" yaml:"tag,omitempty"` Target string `json:"target,omitempty" yaml:"target,omitempty"` @@ -113,6 +115,7 @@ func (r *Rules) Empty() bool { len(r.Event) == 0 && len(r.Path) == 0 && len(r.Repo) == 0 && + len(r.Sender) == 0 && len(r.Status) == 0 && len(r.Tag) == 0 && len(r.Target) == 0 && @@ -168,6 +171,11 @@ func (r *Rules) Match(from *RuleData, matcher, op string) (bool, error) { return false, err } + matchSender, err := r.Sender.MatchSingle(from.Sender, matcher, op) + if err != nil { + return false, err + } + matchTag, err := r.Tag.MatchSingle(from.Tag, matcher, op) if err != nil { return false, err @@ -190,9 +198,9 @@ func (r *Rules) Match(from *RuleData, matcher, op string) (bool, error) { switch op { case constants.OperatorOr: - return (matchBranch || matchComment || matchEvent || matchPath || matchRepo || matchTag || matchTarget || matchLabel || matchInstance || status), nil + return (matchBranch || matchComment || matchEvent || matchPath || matchRepo || matchSender || matchTag || matchTarget || matchLabel || matchInstance || status), nil default: - return (matchBranch && matchComment && matchEvent && matchPath && matchRepo && matchTag && matchTarget && matchLabel && matchInstance && status), nil + return (matchBranch && matchComment && matchEvent && matchPath && matchRepo && matchSender && matchTag && matchTarget && matchLabel && matchInstance && status), nil } } diff --git a/compiler/types/pipeline/ruleset_test.go b/compiler/types/pipeline/ruleset_test.go index d6618d444..5f9bf7bc2 100644 --- a/compiler/types/pipeline/ruleset_test.go +++ b/compiler/types/pipeline/ruleset_test.go @@ -79,6 +79,11 @@ func TestPipeline_Ruleset_Match(t *testing.T) { data: &RuleData{Branch: "dev", Comment: "ok to test", Event: "push", Repo: "octocat/hello-world", Status: "failure", Tag: "refs/heads/main", Target: ""}, want: true, }, + { + ruleset: &Ruleset{If: Rules{Sender: []string{"octocat"}}, Operator: "and"}, + data: &RuleData{Branch: "dev", Comment: "ok to test", Event: "push", Repo: "octocat/hello-world", Sender: "octocat", Status: "pending", Tag: "refs/heads/main", Target: ""}, + want: true, + }, // If with or operator { ruleset: &Ruleset{If: Rules{Branch: []string{"main"}, Event: []string{"push"}}, Operator: "or"}, diff --git a/compiler/types/yaml/ruleset.go b/compiler/types/yaml/ruleset.go index 348f6315b..ee3c014e3 100644 --- a/compiler/types/yaml/ruleset.go +++ b/compiler/types/yaml/ruleset.go @@ -27,6 +27,7 @@ type ( Event []string `yaml:"event,omitempty,flow" json:"event,omitempty" jsonschema:"description=Limits the execution of a step to matching build events.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Path []string `yaml:"path,omitempty,flow" json:"path,omitempty" jsonschema:"description=Limits the execution of a step to matching files changed in a repository.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Repo []string `yaml:"repo,omitempty,flow" json:"repo,omitempty" jsonschema:"description=Limits the execution of a step to matching repos.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Sender []string `yaml:"sender,omitempty,flow" json:"sender,omitempty" jsonschema:"description=Limits the execution of a step to matching build senders.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Status []string `yaml:"status,omitempty,flow" json:"status,omitempty" jsonschema:"enum=[failure],enum=[success],description=Limits the execution of a step to matching build statuses.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Tag []string `yaml:"tag,omitempty,flow" json:"tag,omitempty" jsonschema:"description=Limits the execution of a step to matching build tag references.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Target []string `yaml:"target,omitempty,flow" json:"target,omitempty" jsonschema:"description=Limits the execution of a step to matching build deployment targets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` @@ -83,6 +84,7 @@ func (r *Ruleset) UnmarshalYAML(unmarshal func(interface{}) error) error { advanced.If.Event = append(advanced.If.Event, simple.Event...) advanced.If.Path = append(advanced.If.Path, simple.Path...) advanced.If.Repo = append(advanced.If.Repo, simple.Repo...) + advanced.If.Sender = append(advanced.If.Sender, simple.Sender...) advanced.If.Status = append(advanced.If.Status, simple.Status...) advanced.If.Tag = append(advanced.If.Tag, simple.Tag...) advanced.If.Target = append(advanced.If.Target, simple.Target...) @@ -114,6 +116,7 @@ func (r *Rules) ToPipeline() *pipeline.Rules { Event: r.Event, Path: r.Path, Repo: r.Repo, + Sender: r.Sender, Status: r.Status, Tag: r.Tag, Target: r.Target, @@ -131,6 +134,7 @@ func (r *Rules) UnmarshalYAML(unmarshal func(interface{}) error) error { Event raw.StringSlice Path raw.StringSlice Repo raw.StringSlice + Sender raw.StringSlice Status raw.StringSlice Tag raw.StringSlice Target raw.StringSlice @@ -145,6 +149,7 @@ func (r *Rules) UnmarshalYAML(unmarshal func(interface{}) error) error { r.Comment = rules.Comment r.Path = rules.Path r.Repo = rules.Repo + r.Sender = rules.Sender r.Status = rules.Status r.Tag = rules.Tag r.Target = rules.Target diff --git a/compiler/types/yaml/ruleset_test.go b/compiler/types/yaml/ruleset_test.go index d1f559daa..6275046b0 100644 --- a/compiler/types/yaml/ruleset_test.go +++ b/compiler/types/yaml/ruleset_test.go @@ -26,6 +26,7 @@ func TestYaml_Ruleset_ToPipeline(t *testing.T) { Event: []string{"push", "pull_request:labeled"}, Path: []string{"foo.txt"}, Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, Status: []string{"success"}, Tag: []string{"v0.1.0"}, Target: []string{"production"}, @@ -38,6 +39,7 @@ func TestYaml_Ruleset_ToPipeline(t *testing.T) { Event: []string{"pull_request"}, Path: []string{"bar.txt"}, Repo: []string{"github/octocat"}, + Sender: []string{"octokitty"}, Status: []string{"failure"}, Tag: []string{"v0.2.0"}, Target: []string{"production"}, @@ -54,6 +56,7 @@ func TestYaml_Ruleset_ToPipeline(t *testing.T) { Event: []string{"push", "pull_request:labeled"}, Path: []string{"foo.txt"}, Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, Status: []string{"success"}, Tag: []string{"v0.1.0"}, Target: []string{"production"}, @@ -66,6 +69,7 @@ func TestYaml_Ruleset_ToPipeline(t *testing.T) { Event: []string{"pull_request"}, Path: []string{"bar.txt"}, Repo: []string{"github/octocat"}, + Sender: []string{"octokitty"}, Status: []string{"failure"}, Tag: []string{"v0.2.0"}, Target: []string{"production"}, @@ -98,14 +102,17 @@ func TestYaml_Ruleset_UnmarshalYAML(t *testing.T) { file: "testdata/ruleset_simple.yml", want: &Ruleset{ If: Rules{ - Branch: []string{"main"}, - Comment: []string{"test comment"}, - Event: []string{"push"}, - Path: []string{"foo.txt"}, - Repo: []string{"github/octocat"}, - Status: []string{"success"}, - Tag: []string{"v0.1.0"}, - Target: []string{"production"}, + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Instance: []string{"vela-server"}, + Label: []string{"bug"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, }, Matcher: "filepath", Operator: "and", @@ -172,26 +179,30 @@ func TestYaml_Rules_ToPipeline(t *testing.T) { }{ { rules: &Rules{ - Branch: []string{"main"}, - Comment: []string{"test comment"}, - Event: []string{"push", "pull_request:labeled"}, - Path: []string{"foo.txt"}, - Repo: []string{"github/octocat"}, - Status: []string{"success"}, - Tag: []string{"v0.1.0"}, - Target: []string{"production"}, - Label: []string{"enhancement"}, + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push", "pull_request:labeled"}, + Instance: []string{"vela-server"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + Label: []string{"enhancement"}, }, want: &pipeline.Rules{ - Branch: []string{"main"}, - Comment: []string{"test comment"}, - Event: []string{"push", "pull_request:labeled"}, - Path: []string{"foo.txt"}, - Repo: []string{"github/octocat"}, - Status: []string{"success"}, - Tag: []string{"v0.1.0"}, - Target: []string{"production"}, - Label: []string{"enhancement"}, + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push", "pull_request:labeled"}, + Instance: []string{"vela-server"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + Label: []string{"enhancement"}, }, }, } @@ -223,14 +234,17 @@ func TestYaml_Rules_UnmarshalYAML(t *testing.T) { failure: false, file: "testdata/ruleset_simple.yml", want: &Rules{ - Branch: []string{"main"}, - Comment: []string{"test comment"}, - Event: []string{"push"}, - Path: []string{"foo.txt"}, - Repo: []string{"github/octocat"}, - Status: []string{"success"}, - Tag: []string{"v0.1.0"}, - Target: []string{"production"}, + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Instance: []string{"vela-server"}, + Label: []string{"bug"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, }, }, { diff --git a/compiler/types/yaml/testdata/ruleset_simple.yml b/compiler/types/yaml/testdata/ruleset_simple.yml index 7696e49b1..acbe4e198 100644 --- a/compiler/types/yaml/testdata/ruleset_simple.yml +++ b/compiler/types/yaml/testdata/ruleset_simple.yml @@ -3,8 +3,11 @@ branch: main comment: "test comment" continue: true event: push +instance: vela-server +label: bug path: foo.txt repo: github/octocat +sender: octocat status: success tag: v0.1.0 target: production From a3b1bff5ad71ac063186edab4004253c08f008aa Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:26:26 -0400 Subject: [PATCH 03/41] chore(lint): add struct tag align rule (#1216) --- .golangci.yml | 7 ++++++ api/types/secret.go | 2 +- api/types/settings/compiler.go | 4 ++-- api/types/settings/platform.go | 12 +++++------ compiler/types/pipeline/build.go | 16 +++++++------- compiler/types/pipeline/container.go | 4 ++-- compiler/types/pipeline/metadata.go | 8 +++---- compiler/types/yaml/build.go | 16 +++++++------- compiler/types/yaml/metadata.go | 12 +++++------ compiler/types/yaml/ruleset.go | 26 +++++++++++----------- compiler/types/yaml/secret.go | 20 ++++++++--------- compiler/types/yaml/service.go | 14 ++++++------ compiler/types/yaml/step.go | 32 ++++++++++++++-------------- compiler/types/yaml/template.go | 6 +++--- compiler/types/yaml/volume.go | 2 +- compiler/types/yaml/worker.go | 2 +- database/types/deployment.go | 2 +- database/types/repo.go | 2 +- database/types/secret.go | 2 +- database/types/settings.go | 6 +++--- database/types/user.go | 4 ++-- database/types/worker.go | 4 ++-- 22 files changed, 105 insertions(+), 98 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index df7592d27..23cf2547f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -47,6 +47,12 @@ linters-settings: - dot - prefix(github.com/go-vela) + # https://golangci-lint.run/usage/linters/#tagalign + # ensure struct tags are aligned + # will auto fix with `make lintfix` + tagalign: + sort: false + # https://github.com/denis-tingaikin/go-header goheader: template: |- @@ -100,6 +106,7 @@ linters: - revive # linter for go - staticcheck # applies static analysis checks, go vet on steroids - stylecheck # replacement for golint + - tagalign # finds maligned struct tags - tenv # analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 - typecheck # parses and type-checks go code, like the front-end of a go compiler - unconvert # remove unnecessary type conversions diff --git a/api/types/secret.go b/api/types/secret.go index 1cb9eb48a..0a35572f1 100644 --- a/api/types/secret.go +++ b/api/types/secret.go @@ -22,7 +22,7 @@ type Secret struct { Value *string `json:"value,omitempty"` Type *string `json:"type,omitempty"` Images *[]string `json:"images,omitempty"` - AllowEvents *Events `json:"allow_events,omitempty" yaml:"allow_events"` + AllowEvents *Events `json:"allow_events,omitempty" yaml:"allow_events"` AllowCommand *bool `json:"allow_command,omitempty"` AllowSubstitution *bool `json:"allow_substitution,omitempty"` CreatedAt *int64 `json:"created_at,omitempty"` diff --git a/api/types/settings/compiler.go b/api/types/settings/compiler.go index 147cade94..7b6db1239 100644 --- a/api/types/settings/compiler.go +++ b/api/types/settings/compiler.go @@ -5,8 +5,8 @@ package settings import "fmt" type Compiler struct { - CloneImage *string `json:"clone_image,omitempty" yaml:"clone_image,omitempty"` - TemplateDepth *int `json:"template_depth,omitempty" yaml:"template_depth,omitempty"` + CloneImage *string `json:"clone_image,omitempty" yaml:"clone_image,omitempty"` + TemplateDepth *int `json:"template_depth,omitempty" yaml:"template_depth,omitempty"` StarlarkExecLimit *uint64 `json:"starlark_exec_limit,omitempty" yaml:"starlark_exec_limit,omitempty"` } diff --git a/api/types/settings/platform.go b/api/types/settings/platform.go index ff7c58ef3..22429663f 100644 --- a/api/types/settings/platform.go +++ b/api/types/settings/platform.go @@ -13,13 +13,13 @@ import ( // swagger:model Platform type Platform struct { ID *int64 `json:"id"` - *Compiler `json:"compiler,omitempty" yaml:"compiler,omitempty"` - *Queue `json:"queue,omitempty" yaml:"queue,omitempty"` - RepoAllowlist *[]string `json:"repo_allowlist,omitempty" yaml:"repo_allowlist,omitempty"` + *Compiler `json:"compiler,omitempty" yaml:"compiler,omitempty"` + *Queue `json:"queue,omitempty" yaml:"queue,omitempty"` + RepoAllowlist *[]string `json:"repo_allowlist,omitempty" yaml:"repo_allowlist,omitempty"` ScheduleAllowlist *[]string `json:"schedule_allowlist,omitempty" yaml:"schedule_allowlist,omitempty"` - CreatedAt *int64 `json:"created_at,omitempty" yaml:"created_at,omitempty"` - UpdatedAt *int64 `json:"updated_at,omitempty" yaml:"updated_at,omitempty"` - UpdatedBy *string `json:"updated_by,omitempty" yaml:"updated_by,omitempty"` + CreatedAt *int64 `json:"created_at,omitempty" yaml:"created_at,omitempty"` + UpdatedAt *int64 `json:"updated_at,omitempty" yaml:"updated_at,omitempty"` + UpdatedBy *string `json:"updated_by,omitempty" yaml:"updated_by,omitempty"` } // FromCLIContext returns a new Platform record from a cli context. diff --git a/compiler/types/pipeline/build.go b/compiler/types/pipeline/build.go index aef5eaea8..195a353b5 100644 --- a/compiler/types/pipeline/build.go +++ b/compiler/types/pipeline/build.go @@ -15,15 +15,15 @@ import ( // // swagger:model PipelineBuild type Build struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Metadata Metadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` + ID string `json:"id,omitempty" yaml:"id,omitempty"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + Metadata Metadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` Environment raw.StringSliceMap `json:"environment,omitempty" yaml:"environment,omitempty"` - Worker Worker `json:"worker,omitempty" yaml:"worker,omitempty"` - Secrets SecretSlice `json:"secrets,omitempty" yaml:"secrets,omitempty"` - Services ContainerSlice `json:"services,omitempty" yaml:"services,omitempty"` - Stages StageSlice `json:"stages,omitempty" yaml:"stages,omitempty"` - Steps ContainerSlice `json:"steps,omitempty" yaml:"steps,omitempty"` + Worker Worker `json:"worker,omitempty" yaml:"worker,omitempty"` + Secrets SecretSlice `json:"secrets,omitempty" yaml:"secrets,omitempty"` + Services ContainerSlice `json:"services,omitempty" yaml:"services,omitempty"` + Stages StageSlice `json:"stages,omitempty" yaml:"stages,omitempty"` + Steps ContainerSlice `json:"steps,omitempty" yaml:"steps,omitempty"` } // Purge removes the steps, in every stage, that contain a ruleset diff --git a/compiler/types/pipeline/container.go b/compiler/types/pipeline/container.go index 246076bd3..5cfb2acf9 100644 --- a/compiler/types/pipeline/container.go +++ b/compiler/types/pipeline/container.go @@ -51,8 +51,8 @@ type ( Ulimits UlimitSlice `json:"ulimits,omitempty" yaml:"ulimits,omitempty"` Volumes VolumeSlice `json:"volumes,omitempty" yaml:"volumes,omitempty"` User string `json:"user,omitempty" yaml:"user,omitempty"` - ReportAs string `json:"report_as,omitempty" yaml:"report_as,omitempty"` - IDRequest string `json:"id_request,omitempty" yaml:"id_request,omitempty"` + ReportAs string `json:"report_as,omitempty" yaml:"report_as,omitempty"` + IDRequest string `json:"id_request,omitempty" yaml:"id_request,omitempty"` } ) diff --git a/compiler/types/pipeline/metadata.go b/compiler/types/pipeline/metadata.go index c4f5fdf87..56c3c7f06 100644 --- a/compiler/types/pipeline/metadata.go +++ b/compiler/types/pipeline/metadata.go @@ -6,15 +6,15 @@ package pipeline // // swagger:model PipelineMetadata type Metadata struct { - Template bool `json:"template,omitempty" yaml:"template,omitempty"` - Clone bool `json:"clone,omitempty" yaml:"clone,omitempty"` + Template bool `json:"template,omitempty" yaml:"template,omitempty"` + Clone bool `json:"clone,omitempty" yaml:"clone,omitempty"` Environment []string `json:"environment,omitempty" yaml:"environment,omitempty"` AutoCancel *CancelOptions `json:"auto_cancel,omitempty" yaml:"auto_cancel,omitempty"` } // CancelOptions is the pipeline representation of the auto_cancel block for a pipeline. type CancelOptions struct { - Running bool `yaml:"running,omitempty" json:"running,omitempty"` - Pending bool `yaml:"pending,omitempty" json:"pending,omitempty"` + Running bool `yaml:"running,omitempty" json:"running,omitempty"` + Pending bool `yaml:"pending,omitempty" json:"pending,omitempty"` DefaultBranch bool `yaml:"default_branch,omitempty" json:"default_branch,omitempty"` } diff --git a/compiler/types/yaml/build.go b/compiler/types/yaml/build.go index b2d918945..2a9f2b750 100644 --- a/compiler/types/yaml/build.go +++ b/compiler/types/yaml/build.go @@ -9,15 +9,15 @@ import ( // Build is the yaml representation of a build for a pipeline. type Build struct { - Version string `yaml:"version,omitempty" json:"version,omitempty" jsonschema:"required,minLength=1,description=Provide syntax version used to evaluate the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/version/"` - Metadata Metadata `yaml:"metadata,omitempty" json:"metadata,omitempty" jsonschema:"description=Pass extra information.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/"` + Version string `yaml:"version,omitempty" json:"version,omitempty" jsonschema:"required,minLength=1,description=Provide syntax version used to evaluate the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/version/"` + Metadata Metadata `yaml:"metadata,omitempty" json:"metadata,omitempty" jsonschema:"description=Pass extra information.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/"` Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Provide global environment variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-key"` - Worker Worker `yaml:"worker,omitempty" json:"worker,omitempty" jsonschema:"description=Limit the pipeline to certain types of workers.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/"` - Secrets SecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Provide sensitive information.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/"` - Services ServiceSlice `yaml:"services,omitempty" json:"services,omitempty" jsonschema:"description=Provide detached (headless) execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/services/"` - Stages StageSlice `yaml:"stages,omitempty" json:"stages,omitempty" jsonschema:"oneof_required=stages,description=Provide parallel execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/"` - Steps StepSlice `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"oneof_required=steps,description=Provide sequential execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/"` - Templates TemplateSlice `yaml:"templates,omitempty" json:"templates,omitempty" jsonschema:"description=Provide the name of templates to expand.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/"` + Worker Worker `yaml:"worker,omitempty" json:"worker,omitempty" jsonschema:"description=Limit the pipeline to certain types of workers.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/"` + Secrets SecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Provide sensitive information.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/"` + Services ServiceSlice `yaml:"services,omitempty" json:"services,omitempty" jsonschema:"description=Provide detached (headless) execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/services/"` + Stages StageSlice `yaml:"stages,omitempty" json:"stages,omitempty" jsonschema:"oneof_required=stages,description=Provide parallel execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/"` + Steps StepSlice `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"oneof_required=steps,description=Provide sequential execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/"` + Templates TemplateSlice `yaml:"templates,omitempty" json:"templates,omitempty" jsonschema:"description=Provide the name of templates to expand.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/"` } // ToPipelineAPI converts the Build type to an API Pipeline type. diff --git a/compiler/types/yaml/metadata.go b/compiler/types/yaml/metadata.go index 83dfe5c31..89d21c369 100644 --- a/compiler/types/yaml/metadata.go +++ b/compiler/types/yaml/metadata.go @@ -10,18 +10,18 @@ type ( // Metadata is the yaml representation of // the metadata block for a pipeline. Metadata struct { - Template bool `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"description=Enables compiling the pipeline as a template.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-template-key"` + Template bool `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"description=Enables compiling the pipeline as a template.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-template-key"` RenderInline bool `yaml:"render_inline,omitempty" json:"render_inline,omitempty" jsonschema:"description=Enables inline compiling for the pipeline templates.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-render-inline-key"` - Clone *bool `yaml:"clone,omitempty" json:"clone,omitempty" jsonschema:"default=true,description=Enables injecting the default clone process.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-clone-key"` - Environment []string `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Controls which containers processes can have global env injected.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-environment-key"` - AutoCancel *CancelOptions `yaml:"auto_cancel,omitempty" json:"auto_cancel,omitempty" jsonschema:"description=Enables auto canceling of queued or running pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` + Clone *bool `yaml:"clone,omitempty" json:"clone,omitempty" jsonschema:"default=true,description=Enables injecting the default clone process.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-clone-key"` + Environment []string `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Controls which containers processes can have global env injected.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-environment-key"` + AutoCancel *CancelOptions `yaml:"auto_cancel,omitempty" json:"auto_cancel,omitempty" jsonschema:"description=Enables auto canceling of queued or running pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` } // CancelOptions is the yaml representation of // the auto_cancel block for a pipeline. CancelOptions struct { - Running *bool `yaml:"running,omitempty" json:"running,omitempty" jsonschema:"description=Enables auto canceling of running pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` - Pending *bool `yaml:"pending,omitempty" json:"pending,omitempty" jsonschema:"description=Enables auto canceling of queued pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` + Running *bool `yaml:"running,omitempty" json:"running,omitempty" jsonschema:"description=Enables auto canceling of running pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` + Pending *bool `yaml:"pending,omitempty" json:"pending,omitempty" jsonschema:"description=Enables auto canceling of queued pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` DefaultBranch *bool `yaml:"default_branch,omitempty" json:"default_branch,omitempty" jsonschema:"description=Enables auto canceling of queued or running pipelines that become stale due to new push to default branch.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` } ) diff --git a/compiler/types/yaml/ruleset.go b/compiler/types/yaml/ruleset.go index ee3c014e3..c30e3b109 100644 --- a/compiler/types/yaml/ruleset.go +++ b/compiler/types/yaml/ruleset.go @@ -12,9 +12,9 @@ type ( // Ruleset is the yaml representation of a // ruleset block for a step in a pipeline. Ruleset struct { - If Rules `yaml:"if,omitempty" json:"if,omitempty" jsonschema:"description=Limit execution to when all rules match.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Unless Rules `yaml:"unless,omitempty" json:"unless,omitempty" jsonschema:"description=Limit execution to when all rules do not match.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Matcher string `yaml:"matcher,omitempty" json:"matcher,omitempty" jsonschema:"enum=filepath,enum=regexp,default=filepath,description=Use the defined matching method.\nReference: coming soon"` + If Rules `yaml:"if,omitempty" json:"if,omitempty" jsonschema:"description=Limit execution to when all rules match.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Unless Rules `yaml:"unless,omitempty" json:"unless,omitempty" jsonschema:"description=Limit execution to when all rules do not match.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Matcher string `yaml:"matcher,omitempty" json:"matcher,omitempty" jsonschema:"enum=filepath,enum=regexp,default=filepath,description=Use the defined matching method.\nReference: coming soon"` Operator string `yaml:"operator,omitempty" json:"operator,omitempty" jsonschema:"enum=or,enum=and,default=and,description=Whether all rule conditions must be met or just any one of them.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Continue bool `yaml:"continue,omitempty" json:"continue,omitempty" jsonschema:"default=false,description=Limits the execution of a step to continuing on any failure.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` } @@ -22,16 +22,16 @@ type ( // Rules is the yaml representation of the ruletypes // from a ruleset block for a step in a pipeline. Rules struct { - Branch []string `yaml:"branch,omitempty,flow" json:"branch,omitempty" jsonschema:"description=Limits the execution of a step to matching build branches.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Comment []string `yaml:"comment,omitempty,flow" json:"comment,omitempty" jsonschema:"description=Limits the execution of a step to matching a pull request comment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Event []string `yaml:"event,omitempty,flow" json:"event,omitempty" jsonschema:"description=Limits the execution of a step to matching build events.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Path []string `yaml:"path,omitempty,flow" json:"path,omitempty" jsonschema:"description=Limits the execution of a step to matching files changed in a repository.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Repo []string `yaml:"repo,omitempty,flow" json:"repo,omitempty" jsonschema:"description=Limits the execution of a step to matching repos.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Sender []string `yaml:"sender,omitempty,flow" json:"sender,omitempty" jsonschema:"description=Limits the execution of a step to matching build senders.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Status []string `yaml:"status,omitempty,flow" json:"status,omitempty" jsonschema:"enum=[failure],enum=[success],description=Limits the execution of a step to matching build statuses.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Tag []string `yaml:"tag,omitempty,flow" json:"tag,omitempty" jsonschema:"description=Limits the execution of a step to matching build tag references.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Target []string `yaml:"target,omitempty,flow" json:"target,omitempty" jsonschema:"description=Limits the execution of a step to matching build deployment targets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Label []string `yaml:"label,omitempty,flow" json:"label,omitempty" jsonschema:"description=Limits step execution to match on pull requests labels.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Branch []string `yaml:"branch,omitempty,flow" json:"branch,omitempty" jsonschema:"description=Limits the execution of a step to matching build branches.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Comment []string `yaml:"comment,omitempty,flow" json:"comment,omitempty" jsonschema:"description=Limits the execution of a step to matching a pull request comment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Event []string `yaml:"event,omitempty,flow" json:"event,omitempty" jsonschema:"description=Limits the execution of a step to matching build events.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Path []string `yaml:"path,omitempty,flow" json:"path,omitempty" jsonschema:"description=Limits the execution of a step to matching files changed in a repository.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Repo []string `yaml:"repo,omitempty,flow" json:"repo,omitempty" jsonschema:"description=Limits the execution of a step to matching repos.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Sender []string `yaml:"sender,omitempty,flow" json:"sender,omitempty" jsonschema:"description=Limits the execution of a step to matching build senders.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Status []string `yaml:"status,omitempty,flow" json:"status,omitempty" jsonschema:"enum=[failure],enum=[success],description=Limits the execution of a step to matching build statuses.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Tag []string `yaml:"tag,omitempty,flow" json:"tag,omitempty" jsonschema:"description=Limits the execution of a step to matching build tag references.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Target []string `yaml:"target,omitempty,flow" json:"target,omitempty" jsonschema:"description=Limits the execution of a step to matching build deployment targets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Label []string `yaml:"label,omitempty,flow" json:"label,omitempty" jsonschema:"description=Limits step execution to match on pull requests labels.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Instance []string `yaml:"instance,omitempty,flow" json:"instance,omitempty" jsonschema:"description=Limits step execution to match on certain instances.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` } ) diff --git a/compiler/types/yaml/secret.go b/compiler/types/yaml/secret.go index a99f452e8..d0779e5c6 100644 --- a/compiler/types/yaml/secret.go +++ b/compiler/types/yaml/secret.go @@ -20,24 +20,24 @@ type ( // Secret is the yaml representation of a secret // from the secrets block for a pipeline. Secret struct { - Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Name of secret to reference in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-name-key"` - Key string `yaml:"key,omitempty" json:"key,omitempty" jsonschema:"minLength=1,description=Path to secret to fetch from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-key-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Name of secret to reference in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-name-key"` + Key string `yaml:"key,omitempty" json:"key,omitempty" jsonschema:"minLength=1,description=Path to secret to fetch from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-key-key"` Engine string `yaml:"engine,omitempty" json:"engine,omitempty" jsonschema:"enum=native,enum=vault,default=native,description=Name of storage backend to fetch secret from.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-engine-key"` - Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"enum=repo,enum=org,enum=shared,default=repo,description=Type of secret to fetch from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-type-key"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"enum=repo,enum=org,enum=shared,default=repo,description=Type of secret to fetch from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-type-key"` Origin Origin `yaml:"origin,omitempty" json:"origin,omitempty" jsonschema:"description=Declaration to pull secrets from non-internal secret providers.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-origin-key"` - Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=step_start,enum=build_start,default=build_start,description=When to pull in secrets from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-pull-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=step_start,enum=build_start,default=build_start,description=When to pull in secrets from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-pull-key"` } // Origin is the yaml representation of a method // for looking up secrets with a secret plugin. Origin struct { Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Variables to inject into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-key"` - Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"required,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-key"` - Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the secret origin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-key"` - Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for the secret plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-key"` - Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Secrets to inject that are necessary to retrieve the secrets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` - Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-key"` - Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"required,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the secret origin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-key"` + Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for the secret plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-key"` + Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Secrets to inject that are necessary to retrieve the secrets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-key"` + Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` } ) diff --git a/compiler/types/yaml/service.go b/compiler/types/yaml/service.go index f5d5103cc..3d08e8cba 100644 --- a/compiler/types/yaml/service.go +++ b/compiler/types/yaml/service.go @@ -19,14 +19,14 @@ type ( // Service is the yaml representation // of a Service in a pipeline. Service struct { - Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"required,minLength=1,description=Docker image used to create ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-image-key"` - Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the container in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-name-key"` - Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Commands to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-entrypoint-key"` + Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"required,minLength=1,description=Docker image used to create ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-image-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the container in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-name-key"` + Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Commands to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-entrypoint-key"` Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Variables to inject into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-environment-key"` - Ports raw.StringSlice `yaml:"ports,omitempty" json:"ports,omitempty" jsonschema:"description=List of ports to map for the container in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-ports-key"` - Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-pul-key"` - Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-ulimits-key"` - User string `yaml:"user,omitempty" json:"user,omitempty" jsonschema:"description=Set the user for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-user-key"` + Ports raw.StringSlice `yaml:"ports,omitempty" json:"ports,omitempty" jsonschema:"description=List of ports to map for the container in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-ports-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-pul-key"` + Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-ulimits-key"` + User string `yaml:"user,omitempty" json:"user,omitempty" jsonschema:"description=Set the user for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-user-key"` } ) diff --git a/compiler/types/yaml/step.go b/compiler/types/yaml/step.go index 0b579f614..ea583fe95 100644 --- a/compiler/types/yaml/step.go +++ b/compiler/types/yaml/step.go @@ -19,23 +19,23 @@ type ( // Step is the yaml representation of a step // from the steps block for a pipeline. Step struct { - Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Commands raw.StringSlice `yaml:"commands,omitempty" json:"commands,omitempty" jsonschema:"description=Execution instructions to run inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-commands-key"` - Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Command to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-entrypoint-key"` - Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Sensitive variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` - Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"oneof_required=template,description=Name of template to expand in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-key"` - Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` - Volumes VolumeSlice `yaml:"volumes,omitempty" json:"volumes,omitempty" jsonschema:"description=Mount volumes for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` - Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"oneof_required=image,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-key"` - Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-key"` - Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-key"` + Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Commands raw.StringSlice `yaml:"commands,omitempty" json:"commands,omitempty" jsonschema:"description=Execution instructions to run inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-commands-key"` + Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Command to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-entrypoint-key"` + Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Sensitive variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` + Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"oneof_required=template,description=Name of template to expand in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-key"` + Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` + Volumes VolumeSlice `yaml:"volumes,omitempty" json:"volumes,omitempty" jsonschema:"description=Mount volumes for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` + Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"oneof_required=image,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-key"` Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Provide environment variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-key"` - Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for a plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-key"` - Detach bool `yaml:"detach,omitempty" json:"detach,omitempty" jsonschema:"description=Run the container in a detached (headless) state.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-detach-key"` - Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty" jsonschema:"description=Run the container with extra privileges.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-privileged-key"` - User string `yaml:"user,omitempty" json:"user,omitempty" jsonschema:"description=Set the user for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-user-key"` - ReportAs string `yaml:"report_as,omitempty" json:"report_as,omitempty" jsonschema:"description=Set the name of the step to report as.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-report_as-key"` - IDRequest string `yaml:"id_request,omitempty" json:"id_request,omitempty" jsonschema:"description=Request ID Request Token for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-id_request-key"` + Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for a plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-key"` + Detach bool `yaml:"detach,omitempty" json:"detach,omitempty" jsonschema:"description=Run the container in a detached (headless) state.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-detach-key"` + Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty" jsonschema:"description=Run the container with extra privileges.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-privileged-key"` + User string `yaml:"user,omitempty" json:"user,omitempty" jsonschema:"description=Set the user for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-user-key"` + ReportAs string `yaml:"report_as,omitempty" json:"report_as,omitempty" jsonschema:"description=Set the name of the step to report as.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-report_as-key"` + IDRequest string `yaml:"id_request,omitempty" json:"id_request,omitempty" jsonschema:"description=Request ID Request Token for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-id_request-key"` } ) diff --git a/compiler/types/yaml/template.go b/compiler/types/yaml/template.go index 99da1964f..ef2005540 100644 --- a/compiler/types/yaml/template.go +++ b/compiler/types/yaml/template.go @@ -14,11 +14,11 @@ type ( // Template is the yaml representation of a template // from the templates block for a pipeline. Template struct { - Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-name-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-name-key"` Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"required,minLength=1,description=Path to template in remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-source-key"` Format string `yaml:"format,omitempty" json:"format,omitempty" jsonschema:"enum=starlark,enum=golang,enum=go,default=go,minLength=1,description=language used within the template file \nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-format-key"` - Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"minLength=1,example=github,description=Type of template provided from the remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-type-key"` - Variables map[string]interface{} `yaml:"vars,omitempty" json:"vars,omitempty" jsonschema:"description=Variables injected into the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-variables-key"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"minLength=1,example=github,description=Type of template provided from the remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-type-key"` + Variables map[string]interface{} `yaml:"vars,omitempty" json:"vars,omitempty" jsonschema:"description=Variables injected into the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-variables-key"` } // StepTemplate is the yaml representation of the diff --git a/compiler/types/yaml/volume.go b/compiler/types/yaml/volume.go index cc6cd991e..7b7b87d32 100644 --- a/compiler/types/yaml/volume.go +++ b/compiler/types/yaml/volume.go @@ -18,7 +18,7 @@ type ( // Volume is the yaml representation of a volume // from a volumes block for a step in a pipeline. Volume struct { - Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"required,minLength=1,description=Set the source directory to be mounted.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` + Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"required,minLength=1,description=Set the source directory to be mounted.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` Destination string `yaml:"destination,omitempty" json:"destination,omitempty" jsonschema:"required,minLength=1,description=Set the destination directory for the mount in the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` AccessMode string `yaml:"access_mode,omitempty" json:"access_mode,omitempty" jsonschema:"default=ro,description=Set the access mode for the mounted volume.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` } diff --git a/compiler/types/yaml/worker.go b/compiler/types/yaml/worker.go index 85c2a91cd..86710f68c 100644 --- a/compiler/types/yaml/worker.go +++ b/compiler/types/yaml/worker.go @@ -7,7 +7,7 @@ import "github.com/go-vela/server/compiler/types/pipeline" // Worker is the yaml representation of a worker // from a worker block in a pipeline. type Worker struct { - Flavor string `yaml:"flavor,omitempty" json:"flavor,omitempty" jsonschema:"minLength=1,description=Flavor identifier for worker.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/#the-flavor-key,example=large"` + Flavor string `yaml:"flavor,omitempty" json:"flavor,omitempty" jsonschema:"minLength=1,description=Flavor identifier for worker.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/#the-flavor-key,example=large"` Platform string `yaml:"platform,omitempty" json:"platform,omitempty" jsonschema:"minLength=1,description=Platform identifier for the worker.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/#the-platform-key,example=kubernetes"` } diff --git a/database/types/deployment.go b/database/types/deployment.go index 965c8dbc6..f7c5f85cf 100644 --- a/database/types/deployment.go +++ b/database/types/deployment.go @@ -39,7 +39,7 @@ type Deployment struct { Payload raw.StringSliceMap `sql:"payload"` CreatedAt sql.NullInt64 `sql:"created_at"` CreatedBy sql.NullString `sql:"created_by"` - Builds pq.StringArray `sql:"builds" gorm:"type:varchar(50)"` + Builds pq.StringArray `sql:"builds" gorm:"type:varchar(50)"` Repo Repo `gorm:"foreignKey:RepoID"` } diff --git a/database/types/repo.go b/database/types/repo.go index e406c0c5c..d769f0c52 100644 --- a/database/types/repo.go +++ b/database/types/repo.go @@ -55,7 +55,7 @@ type Repo struct { Link sql.NullString `sql:"link"` Clone sql.NullString `sql:"clone"` Branch sql.NullString `sql:"branch"` - Topics pq.StringArray `sql:"topics" gorm:"type:varchar(1020)"` + Topics pq.StringArray `sql:"topics" gorm:"type:varchar(1020)"` BuildLimit sql.NullInt64 `sql:"build_limit"` Timeout sql.NullInt64 `sql:"timeout"` Counter sql.NullInt32 `sql:"counter"` diff --git a/database/types/secret.go b/database/types/secret.go index 1d4dc130e..c7b8df66d 100644 --- a/database/types/secret.go +++ b/database/types/secret.go @@ -50,7 +50,7 @@ type Secret struct { Name sql.NullString `sql:"name"` Value sql.NullString `sql:"value"` Type sql.NullString `sql:"type"` - Images pq.StringArray `sql:"images" gorm:"type:varchar(1000)"` + Images pq.StringArray `sql:"images" gorm:"type:varchar(1000)"` AllowEvents sql.NullInt64 `sql:"allow_events"` AllowCommand sql.NullBool `sql:"allow_command"` AllowSubstitution sql.NullBool `sql:"allow_substitution"` diff --git a/database/types/settings.go b/database/types/settings.go index ea0d47a2a..7bc29a6ca 100644 --- a/database/types/settings.go +++ b/database/types/settings.go @@ -28,7 +28,7 @@ type ( Compiler Queue - RepoAllowlist pq.StringArray `json:"repo_allowlist" sql:"repo_allowlist" gorm:"type:varchar(1000)"` + RepoAllowlist pq.StringArray `json:"repo_allowlist" sql:"repo_allowlist" gorm:"type:varchar(1000)"` ScheduleAllowlist pq.StringArray `json:"schedule_allowlist" sql:"schedule_allowlist" gorm:"type:varchar(1000)"` CreatedAt sql.NullInt64 `sql:"created_at"` @@ -38,8 +38,8 @@ type ( // Compiler is the database representation of compiler settings. Compiler struct { - CloneImage sql.NullString `json:"clone_image" sql:"clone_image"` - TemplateDepth sql.NullInt64 `json:"template_depth" sql:"template_depth"` + CloneImage sql.NullString `json:"clone_image" sql:"clone_image"` + TemplateDepth sql.NullInt64 `json:"template_depth" sql:"template_depth"` StarlarkExecLimit sql.NullInt64 `json:"starlark_exec_limit" sql:"starlark_exec_limit"` } diff --git a/database/types/user.go b/database/types/user.go index bde757a6b..893336664 100644 --- a/database/types/user.go +++ b/database/types/user.go @@ -51,10 +51,10 @@ type User struct { Name sql.NullString `sql:"name"` RefreshToken sql.NullString `sql:"refresh_token"` Token sql.NullString `sql:"token"` - Favorites pq.StringArray `sql:"favorites" gorm:"type:varchar(5000)"` + Favorites pq.StringArray `sql:"favorites" gorm:"type:varchar(5000)"` Active sql.NullBool `sql:"active"` Admin sql.NullBool `sql:"admin"` - Dashboards pq.StringArray `sql:"dashboards" gorm:"type:varchar(5000)"` + Dashboards pq.StringArray `sql:"dashboards" gorm:"type:varchar(5000)"` } // Decrypt will manipulate the existing user tokens by diff --git a/database/types/worker.go b/database/types/worker.go index 7dfe1ffab..6af93ae97 100644 --- a/database/types/worker.go +++ b/database/types/worker.go @@ -33,11 +33,11 @@ type Worker struct { ID sql.NullInt64 `sql:"id"` Hostname sql.NullString `sql:"hostname"` Address sql.NullString `sql:"address"` - Routes pq.StringArray `sql:"routes" gorm:"type:varchar(1000)"` + Routes pq.StringArray `sql:"routes" gorm:"type:varchar(1000)"` Active sql.NullBool `sql:"active"` Status sql.NullString `sql:"status"` LastStatusUpdateAt sql.NullInt64 `sql:"last_status_update_at"` - RunningBuildIDs pq.StringArray `sql:"running_build_ids" gorm:"type:varchar(500)"` + RunningBuildIDs pq.StringArray `sql:"running_build_ids" gorm:"type:varchar(500)"` LastBuildStartedAt sql.NullInt64 `sql:"last_build_started_at"` LastBuildFinishedAt sql.NullInt64 `sql:"last_build_finished_at"` LastCheckedIn sql.NullInt64 `sql:"last_checked_in"` From 2245376f6f15940e4227c25f08066752862dee93 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:07:09 -0500 Subject: [PATCH 04/41] chore(lint): address existing linter issues (#1218) * chore(lint): address existing linter issues * remove dupl from exclusion list --- .golangci.yml | 4 +- api/admin/service.go | 1 - api/admin/step.go | 1 - api/admin/user.go | 1 - api/build/enqueue.go | 4 +- api/log/create_service.go | 1 - api/log/create_step.go | 1 - api/log/delete_service.go | 1 - api/log/delete_step.go | 1 - api/log/get_service.go | 1 - api/log/get_step.go | 1 - api/log/update_service.go | 1 - api/log/update_step.go | 1 - api/pipeline/compile.go | 1 - api/pipeline/expand.go | 1 - api/repo/repair.go | 2 +- api/types/actions/deploy.go | 2 - api/types/actions/schedule.go | 2 - api/types/build.go | 4 +- api/types/service.go | 2 +- api/types/settings/compiler.go | 6 +-- api/types/step.go | 2 +- api/types/template.go | 1 - api/user/create_token.go | 1 - api/user/delete_token.go | 1 - api/webhook/post.go | 44 ++++++++++----------- compiler/native/compile.go | 4 ++ compiler/native/native.go | 2 +- compiler/registry/github/template.go | 4 +- compiler/template/starlark/render.go | 30 ++++++++------ compiler/template/starlark/render_test.go | 2 +- compiler/template/starlark/starlark.go | 1 + compiler/types/pipeline/ruleset.go | 40 +++++++++++++------ compiler/types/yaml/service.go | 2 - compiler/types/yaml/stage.go | 2 + compiler/types/yaml/step.go | 2 - constants/errors.go | 12 ++++++ database/build/create.go | 1 - database/build/update.go | 1 - database/log/create.go | 1 - database/log/get_service.go | 1 - database/log/get_step.go | 1 - database/log/update.go | 1 - database/repo/create.go | 1 - database/repo/update.go | 1 - database/resource.go | 30 +++++++------- database/secret/create.go | 1 - database/secret/update.go | 1 - database/types/build.go | 18 ++++----- database/types/build_test.go | 10 ++--- database/types/repo.go | 6 +-- database/types/repo_test.go | 2 +- database/types/service.go | 18 ++++----- database/types/service_test.go | 16 ++++---- database/types/settings.go | 4 +- database/types/step.go | 18 ++++----- database/types/step_test.go | 16 ++++---- database/user/create.go | 1 - database/user/update.go | 1 - internal/token/generate_rsa.go | 2 +- mock/server/authentication.go | 8 ++-- mock/server/hook.go | 1 - mock/server/secret.go | 1 - mock/server/service.go | 1 - mock/server/step.go | 1 - queue/redis/opts.go | 4 -- router/middleware/logger.go | 2 +- router/middleware/perm/perm_test.go | 48 +++++++++++------------ router/service.go | 1 - router/step.go | 1 - scm/github/authentication.go | 6 +-- scm/github/repo.go | 4 -- scm/github/webhook.go | 4 +- secret/vault/count.go | 2 +- secret/vault/create.go | 2 +- secret/vault/delete.go | 2 +- secret/vault/get.go | 5 +-- secret/vault/refresh_test.go | 16 ++++---- 78 files changed, 217 insertions(+), 233 deletions(-) create mode 100644 constants/errors.go diff --git a/.golangci.yml b/.golangci.yml index 23cf2547f..3eccaf51e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -80,10 +80,9 @@ linters: - bidichk # checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - contextcheck # check the function whether use a non-inherited context - - dupl # code clone detection + - copyloopvar # checks for loop variables - errcheck # checks for unchecked errors - errorlint # find misuses of errors - - exportloopref # check for exported loop vars - funlen # detects long functions - gci # consistent import ordering - goconst # finds repeated strings that could be replaced by a constant @@ -169,7 +168,6 @@ issues: # prevent linters from running on *_test.go files - path: _test\.go linters: - - dupl - funlen - goconst - gocyclo diff --git a/api/admin/service.go b/api/admin/service.go index 5e5e5dcba..8d4d56d1b 100644 --- a/api/admin/service.go +++ b/api/admin/service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code package admin import ( diff --git a/api/admin/step.go b/api/admin/step.go index 6e7ad4cdb..67311d091 100644 --- a/api/admin/step.go +++ b/api/admin/step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with user.go package admin import ( diff --git a/api/admin/user.go b/api/admin/user.go index 3f6d1b1ca..5f480495e 100644 --- a/api/admin/user.go +++ b/api/admin/user.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code package admin import ( diff --git a/api/build/enqueue.go b/api/build/enqueue.go index aa87f4473..695967ca0 100644 --- a/api/build/enqueue.go +++ b/api/build/enqueue.go @@ -39,11 +39,11 @@ func Enqueue(ctx context.Context, queue queue.Service, db database.Interface, it l.Debugf("pushing item for build to queue route %s", route) // push item on to the queue - err = queue.Push(context.Background(), route, byteItem) + err = queue.Push(ctx, route, byteItem) if err != nil { l.Errorf("retrying; failed to publish build: %v", err) - err = queue.Push(context.Background(), route, byteItem) + err = queue.Push(ctx, route, byteItem) if err != nil { l.Errorf("failed to publish build: %v", err) diff --git a/api/log/create_service.go b/api/log/create_service.go index fda667ae5..578663c64 100644 --- a/api/log/create_service.go +++ b/api/log/create_service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code to step package log import ( diff --git a/api/log/create_step.go b/api/log/create_step.go index 507d19798..799122d8e 100644 --- a/api/log/create_step.go +++ b/api/log/create_step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code to service package log import ( diff --git a/api/log/delete_service.go b/api/log/delete_service.go index 598874def..34e61f811 100644 --- a/api/log/delete_service.go +++ b/api/log/delete_service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with step package log import ( diff --git a/api/log/delete_step.go b/api/log/delete_step.go index 28b8b6cd5..221ccffa3 100644 --- a/api/log/delete_step.go +++ b/api/log/delete_step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with service package log import ( diff --git a/api/log/get_service.go b/api/log/get_service.go index a82f268ec..f512fbc60 100644 --- a/api/log/get_service.go +++ b/api/log/get_service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with step package log import ( diff --git a/api/log/get_step.go b/api/log/get_step.go index 2a8f0f5e0..f5d5b60b4 100644 --- a/api/log/get_step.go +++ b/api/log/get_step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with service package log import ( diff --git a/api/log/update_service.go b/api/log/update_service.go index 646a0d031..f51bf6084 100644 --- a/api/log/update_service.go +++ b/api/log/update_service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with step package log import ( diff --git a/api/log/update_step.go b/api/log/update_step.go index 7204afdb0..824544627 100644 --- a/api/log/update_step.go +++ b/api/log/update_step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with service package log import ( diff --git a/api/pipeline/compile.go b/api/pipeline/compile.go index 23f997fcc..57d66c0aa 100644 --- a/api/pipeline/compile.go +++ b/api/pipeline/compile.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with expand package pipeline import ( diff --git a/api/pipeline/expand.go b/api/pipeline/expand.go index f50bb6c14..966ef8cc2 100644 --- a/api/pipeline/expand.go +++ b/api/pipeline/expand.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with compile package pipeline import ( diff --git a/api/repo/repair.go b/api/repo/repair.go index 2fd99f9e2..f48ceea2c 100644 --- a/api/repo/repair.go +++ b/api/repo/repair.go @@ -156,7 +156,7 @@ func RepairRepo(c *gin.Context) { sourceRepo.SetPreviousName(r.GetName()) } - r, err = wh.RenameRepository(ctx, h, sourceRepo, c, m) + r, err = wh.RenameRepository(ctx, l, database.FromContext(c), h, sourceRepo, m) if err != nil { util.HandleError(c, http.StatusInternalServerError, err) return diff --git a/api/types/actions/deploy.go b/api/types/actions/deploy.go index 1664c39d3..409e24d1b 100644 --- a/api/types/actions/deploy.go +++ b/api/types/actions/deploy.go @@ -1,6 +1,4 @@ // SPDX-License-Identifier: Apache-2.0 -// -//nolint:dupl // similar code to schedule.go package actions import "github.com/go-vela/server/constants" diff --git a/api/types/actions/schedule.go b/api/types/actions/schedule.go index 296f0ba16..4d30ce384 100644 --- a/api/types/actions/schedule.go +++ b/api/types/actions/schedule.go @@ -1,6 +1,4 @@ // SPDX-License-Identifier: Apache-2.0 -// -//nolint:dupl // similar code to deploy.go package actions import "github.com/go-vela/server/constants" diff --git a/api/types/build.go b/api/types/build.go index f5af28911..70c33b8e7 100644 --- a/api/types/build.go +++ b/api/types/build.go @@ -57,7 +57,7 @@ type Build struct { func (b *Build) Duration() string { // check if the build doesn't have a started timestamp if b.GetStarted() == 0 { - return "..." + return constants.ErrorEmptyDuration } // capture started unix timestamp from the build @@ -1128,8 +1128,6 @@ func (b *Build) SetApprovedBy(v string) { } // String implements the Stringer interface for the Build type. -// -//nolint:dupl // this is duplicated in the test func (b *Build) String() string { return fmt.Sprintf(`{ ApprovedAt: %d, diff --git a/api/types/service.go b/api/types/service.go index 075beb44f..92360a662 100644 --- a/api/types/service.go +++ b/api/types/service.go @@ -37,7 +37,7 @@ type Service struct { func (s *Service) Duration() string { // check if the service doesn't have a started timestamp if s.GetStarted() == 0 { - return "..." + return constants.ErrorEmptyDuration } // capture started unix timestamp from the service diff --git a/api/types/settings/compiler.go b/api/types/settings/compiler.go index 7b6db1239..3a945c637 100644 --- a/api/types/settings/compiler.go +++ b/api/types/settings/compiler.go @@ -7,7 +7,7 @@ import "fmt" type Compiler struct { CloneImage *string `json:"clone_image,omitempty" yaml:"clone_image,omitempty"` TemplateDepth *int `json:"template_depth,omitempty" yaml:"template_depth,omitempty"` - StarlarkExecLimit *uint64 `json:"starlark_exec_limit,omitempty" yaml:"starlark_exec_limit,omitempty"` + StarlarkExecLimit *int64 `json:"starlark_exec_limit,omitempty" yaml:"starlark_exec_limit,omitempty"` } // GetCloneImage returns the CloneImage field. @@ -40,7 +40,7 @@ func (cs *Compiler) GetTemplateDepth() int { // // When the provided Compiler type is nil, or the field within // the type is nil, it returns the zero value for the field. -func (cs *Compiler) GetStarlarkExecLimit() uint64 { +func (cs *Compiler) GetStarlarkExecLimit() int64 { // return zero value if Compiler type or StarlarkExecLimit field is nil if cs == nil || cs.StarlarkExecLimit == nil { return 0 @@ -79,7 +79,7 @@ func (cs *Compiler) SetTemplateDepth(v int) { // // When the provided Compiler type is nil, it // will set nothing and immediately return. -func (cs *Compiler) SetStarlarkExecLimit(v uint64) { +func (cs *Compiler) SetStarlarkExecLimit(v int64) { // return if Compiler type is nil if cs == nil { return diff --git a/api/types/step.go b/api/types/step.go index 52d96f45c..4f1dcab06 100644 --- a/api/types/step.go +++ b/api/types/step.go @@ -39,7 +39,7 @@ type Step struct { func (s *Step) Duration() string { // check if the step doesn't have a started timestamp if s.GetStarted() == 0 { - return "..." + return constants.ErrorEmptyDuration } // capture started unix timestamp from the step diff --git a/api/types/template.go b/api/types/template.go index 2090e5ae7..9b435dad8 100644 --- a/api/types/template.go +++ b/api/types/template.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore false positive with build_queue.go package types import ( diff --git a/api/user/create_token.go b/api/user/create_token.go index bb1345193..240f2ef9a 100644 --- a/api/user/create_token.go +++ b/api/user/create_token.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with delete token package user import ( diff --git a/api/user/delete_token.go b/api/user/delete_token.go index 6a03d21e8..56e799dd1 100644 --- a/api/user/delete_token.go +++ b/api/user/delete_token.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with create token package user import ( diff --git a/api/webhook/post.go b/api/webhook/post.go index d8ca86ab7..3cb1017d2 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -145,9 +145,11 @@ func PostWebhook(c *gin.Context) { l.Debugf("hook generated from SCM: %v", h) l.Debugf("repo generated from SCM: %v", r) + db := database.FromContext(c) + // if event is repository event, handle separately and return if strings.EqualFold(h.GetEvent(), constants.EventRepository) { - r, err = handleRepositoryEvent(ctx, c, m, h, r) + r, err = handleRepositoryEvent(ctx, l, db, m, h, r) if err != nil { util.HandleError(c, http.StatusInternalServerError, err) return @@ -189,6 +191,8 @@ func PostWebhook(c *gin.Context) { defer func() { // send API call to update the webhook + // + //nolint:contextcheck // false positive _, err = database.FromContext(c).UpdateHook(ctx, h) if err != nil { l.Errorf("unable to update webhook %s/%d: %v", r.GetFullName(), h.GetNumber(), err) @@ -617,9 +621,7 @@ func PostWebhook(c *gin.Context) { // the database resources with any relevant changes resulting from the event, such as name changes, transfers, etc. // // the caller is responsible for returning errors to the client. -func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Metadata, h *types.Hook, r *types.Repo) (*types.Repo, error) { - l := c.MustGet("logger").(*logrus.Entry) - +func handleRepositoryEvent(ctx context.Context, l *logrus.Entry, db database.Interface, m *internal.Metadata, h *types.Hook, r *types.Repo) (*types.Repo, error) { l = l.WithFields(logrus.Fields{ "event_type": h.GetEvent(), }) @@ -628,7 +630,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta defer func() { // send API call to update the webhook - hr, err := database.FromContext(c).CreateHook(ctx, h) + hr, err := db.CreateHook(ctx, h) if err != nil { l.Errorf("unable to create webhook %s/%d: %v", r.GetFullName(), h.GetNumber(), err) } @@ -645,7 +647,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta switch h.GetEventAction() { // if action is renamed or transferred, go through rename routine case constants.ActionRenamed, constants.ActionTransferred: - r, err := RenameRepository(ctx, h, r, c, m) + r, err := RenameRepository(ctx, l, db, h, r, m) if err != nil { h.SetStatus(constants.StatusFailure) h.SetError(err.Error()) @@ -658,7 +660,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta case "archived", "unarchived", constants.ActionEdited: l.Debugf("repository action %s for %s", h.GetEventAction(), r.GetFullName()) // send call to get repository from database - dbRepo, err := database.FromContext(c).GetRepoForOrg(ctx, r.GetOrg(), r.GetName()) + dbRepo, err := db.GetRepoForOrg(ctx, r.GetOrg(), r.GetName()) if err != nil { retErr := fmt.Errorf("%s: failed to get repo %s: %w", baseErr, r.GetFullName(), err) @@ -669,7 +671,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta } // send API call to capture the last hook for the repo - lastHook, err := database.FromContext(c).LastHookForRepo(ctx, dbRepo) + lastHook, err := db.LastHookForRepo(ctx, dbRepo) if err != nil { retErr := fmt.Errorf("unable to get last hook for repo %s: %w", r.GetFullName(), err) @@ -702,7 +704,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta } // update repo object in the database after applying edits - dbRepo, err = database.FromContext(c).UpdateRepo(ctx, dbRepo) + dbRepo, err = db.UpdateRepo(ctx, dbRepo) if err != nil { retErr := fmt.Errorf("%s: failed to update repo %s: %w", baseErr, r.GetFullName(), err) @@ -731,9 +733,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta // associated with that repo as well as build links for the UI. // // the caller is responsible for returning errors to the client. -func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin.Context, m *internal.Metadata) (*types.Repo, error) { - l := c.MustGet("logger").(*logrus.Entry) - +func RenameRepository(ctx context.Context, l *logrus.Entry, db database.Interface, h *types.Hook, r *types.Repo, m *internal.Metadata) (*types.Repo, error) { l = l.WithFields(logrus.Fields{ "event_type": h.GetEvent(), }) @@ -741,13 +741,13 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. l.Debugf("renaming repository from %s to %s", r.GetPreviousName(), r.GetName()) // get any matching hook with the repo's unique webhook ID in the SCM - hook, err := database.FromContext(c).GetHookByWebhookID(ctx, h.GetWebhookID()) + hook, err := db.GetHookByWebhookID(ctx, h.GetWebhookID()) if err != nil { return nil, fmt.Errorf("%s: failed to get hook with webhook ID %d from database", baseErr, h.GetWebhookID()) } // get the repo from the database using repo id of matching hook - dbR, err := database.FromContext(c).GetRepo(ctx, hook.GetRepo().GetID()) + dbR, err := db.GetRepo(ctx, hook.GetRepo().GetID()) if err != nil { return nil, fmt.Errorf("%s: failed to get repo %d from database", baseErr, hook.GetRepo().GetID()) } @@ -756,7 +756,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. h.SetRepo(r) // send API call to capture the last hook for the repo - lastHook, err := database.FromContext(c).LastHookForRepo(ctx, dbR) + lastHook, err := db.LastHookForRepo(ctx, dbR) if err != nil { retErr := fmt.Errorf("unable to get last hook for repo %s: %w", r.GetFullName(), err) @@ -774,7 +774,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. } // get total number of secrets associated with repository - t, err := database.FromContext(c).CountSecretsForRepo(ctx, dbR, map[string]interface{}{}) + t, err := db.CountSecretsForRepo(ctx, dbR, map[string]interface{}{}) if err != nil { return nil, fmt.Errorf("unable to get secret count for repo %s/%s: %w", dbR.GetOrg(), dbR.GetName(), err) } @@ -783,7 +783,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. page := 1 // capture all secrets belonging to certain repo in database for repoSecrets := int64(0); repoSecrets < t; repoSecrets += 100 { - s, _, err := database.FromContext(c).ListSecretsForRepo(ctx, dbR, map[string]interface{}{}, page, 100) + s, _, err := db.ListSecretsForRepo(ctx, dbR, map[string]interface{}{}, page, 100) if err != nil { return nil, fmt.Errorf("unable to get secret list for repo %s/%s: %w", dbR.GetOrg(), dbR.GetName(), err) } @@ -798,7 +798,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. secret.SetOrg(r.GetOrg()) secret.SetRepo(r.GetName()) - _, err = database.FromContext(c).UpdateSecret(ctx, secret) + _, err = db.UpdateSecret(ctx, secret) if err != nil { return nil, fmt.Errorf("unable to update secret for repo %s/%s: %w", dbR.GetOrg(), dbR.GetName(), err) } @@ -811,7 +811,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. } // get total number of builds associated with repository - t, err = database.FromContext(c).CountBuildsForRepo(ctx, dbR, nil) + t, err = db.CountBuildsForRepo(ctx, dbR, nil) if err != nil { return nil, fmt.Errorf("unable to get build count for repo %s: %w", dbR.GetFullName(), err) } @@ -820,7 +820,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. page = 1 // capture all builds belonging to repo in database for build := int64(0); build < t; build += 100 { - b, _, err := database.FromContext(c).ListBuildsForRepo(ctx, dbR, nil, time.Now().Unix(), 0, page, 100) + b, _, err := db.ListBuildsForRepo(ctx, dbR, nil, time.Now().Unix(), 0, page, 100) if err != nil { return nil, fmt.Errorf("unable to get build list for repo %s: %w", dbR.GetFullName(), err) } @@ -836,7 +836,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. fmt.Sprintf("%s/%s/%d", m.Vela.WebAddress, r.GetFullName(), build.GetNumber()), ) - _, err = database.FromContext(c).UpdateBuild(ctx, build) + _, err = db.UpdateBuild(ctx, build) if err != nil { return nil, fmt.Errorf("unable to update build for repo %s: %w", dbR.GetFullName(), err) } @@ -859,7 +859,7 @@ func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin. dbR.SetPreviousName(r.GetPreviousName()) // update the repo in the database - dbR, err = database.FromContext(c).UpdateRepo(ctx, dbR) + dbR, err = db.UpdateRepo(ctx, dbR) if err != nil { retErr := fmt.Errorf("%s: failed to update repo %s/%s", baseErr, dbR.GetOrg(), dbR.GetName()) diff --git a/compiler/native/compile.go b/compiler/native/compile.go index 49d071431..13ad8eba7 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -330,6 +330,8 @@ func (c *client) compileSteps(ctx context.Context, p *yaml.Build, _pipeline *api if c.ModificationService.Endpoint != "" { // send config to external endpoint for modification + // + //nolint:contextcheck // modification service has its own context with a set timeout p, err = c.modifyConfig(p, c.build, c.repo) if err != nil { return nil, _pipeline, err @@ -425,6 +427,8 @@ func (c *client) compileStages(ctx context.Context, p *yaml.Build, _pipeline *ap if c.ModificationService.Endpoint != "" { // send config to external endpoint for modification + // + //nolint:contextcheck // modification service has its own context with a set timeout p, err = c.modifyConfig(p, c.build, c.repo) if err != nil { return nil, _pipeline, err diff --git a/compiler/native/native.go b/compiler/native/native.go index 0c83b0728..ec6d78366 100644 --- a/compiler/native/native.go +++ b/compiler/native/native.go @@ -88,7 +88,7 @@ func FromCLIContext(ctx *cli.Context) (*client, error) { c.SetTemplateDepth(ctx.Int("max-template-depth")) // set the starlark execution step limit for compiling starlark pipelines - c.SetStarlarkExecLimit(ctx.Uint64("compiler-starlark-exec-limit")) + c.SetStarlarkExecLimit(ctx.Int64("compiler-starlark-exec-limit")) if ctx.Bool("github-driver") { logrus.Tracef("setting up Private GitHub Client for %s", ctx.String("github-url")) diff --git a/compiler/registry/github/template.go b/compiler/registry/github/template.go index cd3dfc75f..44bc0d1e8 100644 --- a/compiler/registry/github/template.go +++ b/compiler/registry/github/template.go @@ -34,9 +34,7 @@ func (c *client) Template(ctx context.Context, u *api.User, s *registry.Source) } // send API call to capture the templated pipeline configuration - // - - data, _, resp, err := cli.Repositories.GetContents(context.Background(), s.Org, s.Repo, s.Name, opts) + data, _, resp, err := cli.Repositories.GetContents(ctx, s.Org, s.Repo, s.Name, opts) if err != nil { if resp != nil && resp.StatusCode != http.StatusNotFound { // return different error message depending on if a branch was provided diff --git a/compiler/template/starlark/render.go b/compiler/template/starlark/render.go index 325e49c53..edd822463 100644 --- a/compiler/template/starlark/render.go +++ b/compiler/template/starlark/render.go @@ -10,6 +10,7 @@ import ( yaml "github.com/buildkite/yaml" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" + "go.starlark.net/syntax" "github.com/go-vela/server/compiler/types/raw" types "github.com/go-vela/server/compiler/types/yaml" @@ -30,19 +31,20 @@ var ( ) // Render combines the template with the step in the yaml pipeline. -func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}, limit uint64) (*types.Build, error) { +func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}, limit int64) (*types.Build, error) { config := new(types.Build) thread := &starlark.Thread{Name: name} - // arbitrarily limiting the steps of the thread to 5000 to help prevent infinite loops - // may need to further investigate spawning a separate POSIX process if user input is problematic - // see https://github.com/google/starlark-go/issues/160#issuecomment-466794230 for further details - thread.SetMaxExecutionSteps(limit) - predeclared := starlark.StringDict{"struct": starlark.NewBuiltin("struct", starlarkstruct.Make)} + if limit < 0 { + return nil, fmt.Errorf("starlark exec limit must be non-negative") + } - globals, err := starlark.ExecFile(thread, tName, tmpl, predeclared) + thread.SetMaxExecutionSteps(uint64(limit)) + + predeclared := starlark.StringDict{"struct": starlark.NewBuiltin("struct", starlarkstruct.Make)} + globals, err := starlark.ExecFileOptions(syntax.LegacyFileOptions(), thread, "templated-base", tmpl, predeclared) if err != nil { return nil, err } @@ -138,18 +140,20 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM // RenderBuild renders the templated build. // //nolint:lll // ignore function length due to input args -func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}, limit uint64) (*types.Build, error) { +func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}, limit int64) (*types.Build, error) { config := new(types.Build) thread := &starlark.Thread{Name: "templated-base"} - // arbitrarily limiting the steps of the thread to 5000 to help prevent infinite loops - // may need to further investigate spawning a separate POSIX process if user input is problematic - // see https://github.com/google/starlark-go/issues/160#issuecomment-466794230 for further details - thread.SetMaxExecutionSteps(limit) + + if limit < 0 { + return nil, fmt.Errorf("starlark exec limit must be non-negative") + } + + thread.SetMaxExecutionSteps(uint64(limit)) predeclared := starlark.StringDict{"struct": starlark.NewBuiltin("struct", starlarkstruct.Make)} - globals, err := starlark.ExecFile(thread, "templated-base", b, predeclared) + globals, err := starlark.ExecFileOptions(syntax.LegacyFileOptions(), thread, "templated-base", b, predeclared) if err != nil { return nil, err } diff --git a/compiler/template/starlark/render_test.go b/compiler/template/starlark/render_test.go index 2f34e88f0..33db5a7f0 100644 --- a/compiler/template/starlark/render_test.go +++ b/compiler/template/starlark/render_test.go @@ -142,7 +142,7 @@ func TestNative_RenderBuild(t *testing.T) { args args wantFile string wantErr bool - execLimit uint64 + execLimit int64 }{ { name: "steps", diff --git a/compiler/template/starlark/starlark.go b/compiler/template/starlark/starlark.go index 98b9a2c64..954c7df96 100644 --- a/compiler/template/starlark/starlark.go +++ b/compiler/template/starlark/starlark.go @@ -127,6 +127,7 @@ func toStarlark(value interface{}) (starlark.Value, error) { } var m map[string]interface{} + err = json.Unmarshal(data, &m) if err != nil { return nil, err diff --git a/compiler/types/pipeline/ruleset.go b/compiler/types/pipeline/ruleset.go index 36d6c1302..a830ba6ff 100644 --- a/compiler/types/pipeline/ruleset.go +++ b/compiler/types/pipeline/ruleset.go @@ -135,15 +135,9 @@ func (r *Rules) Empty() bool { // both operators, when none of the ruletypes from the rules // match the provided ruledata, the function returns false. func (r *Rules) Match(from *RuleData, matcher, op string) (bool, error) { - status := true - - var err error - - if len(from.Status) != 0 { - status, err = r.Status.MatchSingle(from.Status, matcher, op) - if err != nil { - return false, err - } + status, err := r.matchStatus(from, matcher, op) + if err != nil { + return false, err } matchBranch, err := r.Branch.MatchSingle(from.Branch, matcher, op) @@ -196,11 +190,35 @@ func (r *Rules) Match(from *RuleData, matcher, op string) (bool, error) { return false, err } + return r.evaluateMatches(op, status, matchBranch, matchComment, matchEvent, matchPath, matchRepo, matchSender, matchTag, matchTarget, matchLabel, matchInstance), nil +} + +func (r *Rules) matchStatus(from *RuleData, matcher, op string) (bool, error) { + if len(from.Status) == 0 { + return true, nil + } + + return r.Status.MatchSingle(from.Status, matcher, op) +} + +func (r *Rules) evaluateMatches(op string, matches ...bool) bool { switch op { case constants.OperatorOr: - return (matchBranch || matchComment || matchEvent || matchPath || matchRepo || matchSender || matchTag || matchTarget || matchLabel || matchInstance || status), nil + for _, match := range matches { + if match { + return true + } + } + + return false default: - return (matchBranch && matchComment && matchEvent && matchPath && matchRepo && matchSender && matchTag && matchTarget && matchLabel && matchInstance && status), nil + for _, match := range matches { + if !match { + return false + } + } + + return true } } diff --git a/compiler/types/yaml/service.go b/compiler/types/yaml/service.go index 3d08e8cba..a727cc32e 100644 --- a/compiler/types/yaml/service.go +++ b/compiler/types/yaml/service.go @@ -56,8 +56,6 @@ func (s *ServiceSlice) ToPipeline() *pipeline.ContainerSlice { } // UnmarshalYAML implements the Unmarshaler interface for the ServiceSlice type. -// -//nolint:dupl // accepting duplicative code that exists in step.go as well func (s *ServiceSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { // service slice we try unmarshalling to serviceSlice := new([]*Service) diff --git a/compiler/types/yaml/stage.go b/compiler/types/yaml/stage.go index f62ce6353..93bc8c8ca 100644 --- a/compiler/types/yaml/stage.go +++ b/compiler/types/yaml/stage.go @@ -88,12 +88,14 @@ func (s *StageSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { return needs } } + return append(needs, "clone") }(stage.Needs) } // append stage to stage slice *s = append(*s, stage) } + return nil } diff --git a/compiler/types/yaml/step.go b/compiler/types/yaml/step.go index ea583fe95..405e6d598 100644 --- a/compiler/types/yaml/step.go +++ b/compiler/types/yaml/step.go @@ -71,8 +71,6 @@ func (s *StepSlice) ToPipeline() *pipeline.ContainerSlice { } // UnmarshalYAML implements the Unmarshaler interface for the StepSlice type. -// -//nolint:dupl // accepting duplicative code that exits in service.go as well func (s *StepSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { // step slice we try unmarshalling to stepSlice := new([]*Step) diff --git a/constants/errors.go b/constants/errors.go new file mode 100644 index 000000000..d0e142df7 --- /dev/null +++ b/constants/errors.go @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 + +package constants + +// Error messages for the Vela API. +const ( + // ErrorEmptyDuration defines the error message for a duration calculation without a started value. + ErrorEmptyDuration = "..." + + // ErrorMock defines the error message returned by mock API functions. + ErrorMock = "error" +) diff --git a/database/build/create.go b/database/build/create.go index d8f122470..63e5509a1 100644 --- a/database/build/create.go +++ b/database/build/create.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with update.go package build import ( diff --git a/database/build/update.go b/database/build/update.go index 2d838d9db..005855eda 100644 --- a/database/build/update.go +++ b/database/build/update.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with create.go package build import ( diff --git a/database/log/create.go b/database/log/create.go index 98ec63beb..fc42ed748 100644 --- a/database/log/create.go +++ b/database/log/create.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with create.go package log import ( diff --git a/database/log/get_service.go b/database/log/get_service.go index f760af06e..92c9ec3d7 100644 --- a/database/log/get_service.go +++ b/database/log/get_service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with get_step.go package log import ( diff --git a/database/log/get_step.go b/database/log/get_step.go index ff92bac10..a6ed23659 100644 --- a/database/log/get_step.go +++ b/database/log/get_step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with get_service.go package log import ( diff --git a/database/log/update.go b/database/log/update.go index 0854a9c2d..b2ae9ce0d 100644 --- a/database/log/update.go +++ b/database/log/update.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with update.go package log import ( diff --git a/database/repo/create.go b/database/repo/create.go index 2768a72be..2dec53bfa 100644 --- a/database/repo/create.go +++ b/database/repo/create.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with update.go package repo import ( diff --git a/database/repo/update.go b/database/repo/update.go index 8168bf404..ddcaac2bd 100644 --- a/database/repo/update.go +++ b/database/repo/update.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with create.go package repo import ( diff --git a/database/resource.go b/database/resource.go index 43e659fd9..ecdf8731b 100644 --- a/database/resource.go +++ b/database/resource.go @@ -31,7 +31,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for settings e.SettingsInterface, err = settings.New( - settings.WithContext(e.ctx), + settings.WithContext(ctx), settings.WithClient(e.client), settings.WithLogger(e.logger), settings.WithSkipCreation(e.config.SkipCreation), @@ -42,7 +42,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for builds e.BuildInterface, err = build.New( - build.WithContext(e.ctx), + build.WithContext(ctx), build.WithClient(e.client), build.WithLogger(e.logger), build.WithSkipCreation(e.config.SkipCreation), @@ -53,7 +53,7 @@ func (e *engine) NewResources(ctx context.Context) error { } e.DashboardInterface, err = dashboard.New( - dashboard.WithContext(e.ctx), + dashboard.WithContext(ctx), dashboard.WithClient(e.client), dashboard.WithLogger(e.logger), dashboard.WithSkipCreation(e.config.SkipCreation), @@ -64,7 +64,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for build_executables e.BuildExecutableInterface, err = executable.New( - executable.WithContext(e.ctx), + executable.WithContext(ctx), executable.WithClient(e.client), executable.WithLogger(e.logger), executable.WithSkipCreation(e.config.SkipCreation), @@ -77,7 +77,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for deployments e.DeploymentInterface, err = deployment.New( - deployment.WithContext(e.ctx), + deployment.WithContext(ctx), deployment.WithClient(e.client), deployment.WithLogger(e.logger), deployment.WithSkipCreation(e.config.SkipCreation), @@ -89,7 +89,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for hooks e.HookInterface, err = hook.New( - hook.WithContext(e.ctx), + hook.WithContext(ctx), hook.WithClient(e.client), hook.WithLogger(e.logger), hook.WithEncryptionKey(e.config.EncryptionKey), @@ -101,7 +101,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for JWKs e.JWKInterface, err = jwk.New( - jwk.WithContext(e.ctx), + jwk.WithContext(ctx), jwk.WithClient(e.client), jwk.WithLogger(e.logger), jwk.WithSkipCreation(e.config.SkipCreation), @@ -112,7 +112,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for logs e.LogInterface, err = log.New( - log.WithContext(e.ctx), + log.WithContext(ctx), log.WithClient(e.client), log.WithCompressionLevel(e.config.CompressionLevel), log.WithLogger(e.logger), @@ -124,7 +124,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for pipelines e.PipelineInterface, err = pipeline.New( - pipeline.WithContext(e.ctx), + pipeline.WithContext(ctx), pipeline.WithClient(e.client), pipeline.WithCompressionLevel(e.config.CompressionLevel), pipeline.WithEncryptionKey(e.config.EncryptionKey), @@ -137,7 +137,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for repos e.RepoInterface, err = repo.New( - repo.WithContext(e.ctx), + repo.WithContext(ctx), repo.WithClient(e.client), repo.WithEncryptionKey(e.config.EncryptionKey), repo.WithLogger(e.logger), @@ -149,7 +149,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for schedules e.ScheduleInterface, err = schedule.New( - schedule.WithContext(e.ctx), + schedule.WithContext(ctx), schedule.WithClient(e.client), schedule.WithEncryptionKey(e.config.EncryptionKey), schedule.WithLogger(e.logger), @@ -163,7 +163,7 @@ func (e *engine) NewResources(ctx context.Context) error { // // https://pkg.go.dev/github.com/go-vela/server/database/secret#New e.SecretInterface, err = secret.New( - secret.WithContext(e.ctx), + secret.WithContext(ctx), secret.WithClient(e.client), secret.WithEncryptionKey(e.config.EncryptionKey), secret.WithLogger(e.logger), @@ -185,7 +185,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for steps e.StepInterface, err = step.New( - step.WithContext(e.ctx), + step.WithContext(ctx), step.WithClient(e.client), step.WithLogger(e.logger), step.WithSkipCreation(e.config.SkipCreation), @@ -196,7 +196,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for users e.UserInterface, err = user.New( - user.WithContext(e.ctx), + user.WithContext(ctx), user.WithClient(e.client), user.WithEncryptionKey(e.config.EncryptionKey), user.WithLogger(e.logger), @@ -208,7 +208,7 @@ func (e *engine) NewResources(ctx context.Context) error { // create the database agnostic engine for workers e.WorkerInterface, err = worker.New( - worker.WithContext(e.ctx), + worker.WithContext(ctx), worker.WithClient(e.client), worker.WithLogger(e.logger), worker.WithSkipCreation(e.config.SkipCreation), diff --git a/database/secret/create.go b/database/secret/create.go index c0ac65dc7..74e8bae53 100644 --- a/database/secret/create.go +++ b/database/secret/create.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with update.go package secret import ( diff --git a/database/secret/update.go b/database/secret/update.go index d512e7e33..8ab516ba8 100644 --- a/database/secret/update.go +++ b/database/secret/update.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with create.go package secret import ( diff --git a/database/types/build.go b/database/types/build.go index 43471e34e..f3aa22cd3 100644 --- a/database/types/build.go +++ b/database/types/build.go @@ -35,8 +35,8 @@ type Build struct { ID sql.NullInt64 `sql:"id"` RepoID sql.NullInt64 `sql:"repo_id"` PipelineID sql.NullInt64 `sql:"pipeline_id"` - Number sql.NullInt32 `sql:"number"` - Parent sql.NullInt32 `sql:"parent"` + Number sql.NullInt64 `sql:"number"` + Parent sql.NullInt64 `sql:"parent"` Event sql.NullString `sql:"event"` EventAction sql.NullString `sql:"event_action"` Status sql.NullString `sql:"status"` @@ -121,12 +121,12 @@ func (b *Build) Nullify() *Build { } // check if the Number field should be false - if b.Number.Int32 == 0 { + if b.Number.Int64 == 0 { b.Number.Valid = false } // check if the Parent field should be false - if b.Parent.Int32 == 0 { + if b.Parent.Int64 == 0 { b.Parent.Valid = false } @@ -295,8 +295,8 @@ func (b *Build) ToAPI() *api.Build { build.SetID(b.ID.Int64) build.SetRepo(repo) build.SetPipelineID(b.PipelineID.Int64) - build.SetNumber(int(b.Number.Int32)) - build.SetParent(int(b.Parent.Int32)) + build.SetNumber(int(b.Number.Int64)) + build.SetParent(int(b.Parent.Int64)) build.SetEvent(b.Event.String) build.SetEventAction(b.EventAction.String) build.SetStatus(b.Status.String) @@ -340,7 +340,7 @@ func (b *Build) Validate() error { } // verify the Number field is populated - if b.Number.Int32 <= 0 { + if b.Number.Int64 <= 0 { return ErrEmptyBuildNumber } @@ -381,8 +381,8 @@ func BuildFromAPI(b *api.Build) *Build { ID: sql.NullInt64{Int64: b.GetID(), Valid: true}, RepoID: sql.NullInt64{Int64: b.GetRepo().GetID(), Valid: true}, PipelineID: sql.NullInt64{Int64: b.GetPipelineID(), Valid: true}, - Number: sql.NullInt32{Int32: int32(b.GetNumber()), Valid: true}, - Parent: sql.NullInt32{Int32: int32(b.GetParent()), Valid: true}, + Number: sql.NullInt64{Int64: int64(b.GetNumber()), Valid: true}, + Parent: sql.NullInt64{Int64: int64(b.GetParent()), Valid: true}, Event: sql.NullString{String: b.GetEvent(), Valid: true}, EventAction: sql.NullString{String: b.GetEventAction(), Valid: true}, Status: sql.NullString{String: b.GetStatus(), Valid: true}, diff --git a/database/types/build_test.go b/database/types/build_test.go index 84cababf8..372e09fc4 100644 --- a/database/types/build_test.go +++ b/database/types/build_test.go @@ -47,8 +47,8 @@ func TestTypes_Build_Nullify(t *testing.T) { ID: sql.NullInt64{Int64: 0, Valid: false}, RepoID: sql.NullInt64{Int64: 0, Valid: false}, PipelineID: sql.NullInt64{Int64: 0, Valid: false}, - Number: sql.NullInt32{Int32: 0, Valid: false}, - Parent: sql.NullInt32{Int32: 0, Valid: false}, + Number: sql.NullInt64{Int64: 0, Valid: false}, + Parent: sql.NullInt64{Int64: 0, Valid: false}, Event: sql.NullString{String: "", Valid: false}, EventAction: sql.NullString{String: "", Valid: false}, Status: sql.NullString{String: "", Valid: false}, @@ -170,7 +170,7 @@ func TestTypes_Build_Validate(t *testing.T) { failure: true, build: &Build{ ID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, }, }, { // no number set for build @@ -274,8 +274,8 @@ func testBuild() *Build { ID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, PipelineID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, - Parent: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + Parent: sql.NullInt64{Int64: 1, Valid: true}, Event: sql.NullString{String: "push", Valid: true}, EventAction: sql.NullString{String: "", Valid: false}, Status: sql.NullString{String: "running", Valid: true}, diff --git a/database/types/repo.go b/database/types/repo.go index d769f0c52..96f45caf9 100644 --- a/database/types/repo.go +++ b/database/types/repo.go @@ -58,7 +58,7 @@ type Repo struct { Topics pq.StringArray `sql:"topics" gorm:"type:varchar(1020)"` BuildLimit sql.NullInt64 `sql:"build_limit"` Timeout sql.NullInt64 `sql:"timeout"` - Counter sql.NullInt32 `sql:"counter"` + Counter sql.NullInt64 `sql:"counter"` Visibility sql.NullString `sql:"visibility"` Private sql.NullBool `sql:"private"` Trusted sql.NullBool `sql:"trusted"` @@ -241,7 +241,7 @@ func (r *Repo) ToAPI() *api.Repo { repo.SetTopics(r.Topics) repo.SetBuildLimit(r.BuildLimit.Int64) repo.SetTimeout(r.Timeout.Int64) - repo.SetCounter(int(r.Counter.Int32)) + repo.SetCounter(int(r.Counter.Int64)) repo.SetVisibility(r.Visibility.String) repo.SetPrivate(r.Private.Bool) repo.SetTrusted(r.Trusted.Bool) @@ -333,7 +333,7 @@ func RepoFromAPI(r *api.Repo) *Repo { Topics: pq.StringArray(r.GetTopics()), BuildLimit: sql.NullInt64{Int64: r.GetBuildLimit(), Valid: true}, Timeout: sql.NullInt64{Int64: r.GetTimeout(), Valid: true}, - Counter: sql.NullInt32{Int32: int32(r.GetCounter()), Valid: true}, + Counter: sql.NullInt64{Int64: int64(r.GetCounter()), Valid: true}, Visibility: sql.NullString{String: r.GetVisibility(), Valid: true}, Private: sql.NullBool{Bool: r.GetPrivate(), Valid: true}, Trusted: sql.NullBool{Bool: r.GetTrusted(), Valid: true}, diff --git a/database/types/repo_test.go b/database/types/repo_test.go index 949985490..c9a8b6afd 100644 --- a/database/types/repo_test.go +++ b/database/types/repo_test.go @@ -373,7 +373,7 @@ func testRepo() *Repo { Topics: []string{"cloud", "security"}, BuildLimit: sql.NullInt64{Int64: 10, Valid: true}, Timeout: sql.NullInt64{Int64: 30, Valid: true}, - Counter: sql.NullInt32{Int32: 0, Valid: true}, + Counter: sql.NullInt64{Int64: 0, Valid: true}, Visibility: sql.NullString{String: "public", Valid: true}, Private: sql.NullBool{Bool: false, Valid: true}, Trusted: sql.NullBool{Bool: false, Valid: true}, diff --git a/database/types/service.go b/database/types/service.go index f3b508ed6..4d7ea46c5 100644 --- a/database/types/service.go +++ b/database/types/service.go @@ -37,12 +37,12 @@ type Service struct { ID sql.NullInt64 `sql:"id"` BuildID sql.NullInt64 `sql:"build_id"` RepoID sql.NullInt64 `sql:"repo_id"` - Number sql.NullInt32 `sql:"number"` + Number sql.NullInt64 `sql:"number"` Name sql.NullString `sql:"name"` Image sql.NullString `sql:"image"` Status sql.NullString `sql:"status"` Error sql.NullString `sql:"error"` - ExitCode sql.NullInt32 `sql:"exit_code"` + ExitCode sql.NullInt64 `sql:"exit_code"` Created sql.NullInt64 `sql:"created"` Started sql.NullInt64 `sql:"started"` Finished sql.NullInt64 `sql:"finished"` @@ -78,7 +78,7 @@ func (s *Service) Nullify() *Service { } // check if the Number field should be false - if s.Number.Int32 == 0 { + if s.Number.Int64 == 0 { s.Number.Valid = false } @@ -103,7 +103,7 @@ func (s *Service) Nullify() *Service { } // check if the ExitCode field should be false - if s.ExitCode.Int32 == 0 { + if s.ExitCode.Int64 == 0 { s.ExitCode.Valid = false } @@ -148,12 +148,12 @@ func (s *Service) ToAPI() *api.Service { service.SetID(s.ID.Int64) service.SetBuildID(s.BuildID.Int64) service.SetRepoID(s.RepoID.Int64) - service.SetNumber(int(s.Number.Int32)) + service.SetNumber(int(s.Number.Int64)) service.SetName(s.Name.String) service.SetImage(s.Image.String) service.SetStatus(s.Status.String) service.SetError(s.Error.String) - service.SetExitCode(int(s.ExitCode.Int32)) + service.SetExitCode(int(s.ExitCode.Int64)) service.SetCreated(s.Created.Int64) service.SetStarted(s.Started.Int64) service.SetFinished(s.Finished.Int64) @@ -178,7 +178,7 @@ func (s *Service) Validate() error { } // verify the Number field is populated - if s.Number.Int32 <= 0 { + if s.Number.Int64 <= 0 { return ErrEmptyServiceNumber } @@ -213,12 +213,12 @@ func ServiceFromAPI(s *api.Service) *Service { ID: sql.NullInt64{Int64: s.GetID(), Valid: true}, BuildID: sql.NullInt64{Int64: s.GetBuildID(), Valid: true}, RepoID: sql.NullInt64{Int64: s.GetRepoID(), Valid: true}, - Number: sql.NullInt32{Int32: int32(s.GetNumber()), Valid: true}, + Number: sql.NullInt64{Int64: int64(s.GetNumber()), Valid: true}, Name: sql.NullString{String: s.GetName(), Valid: true}, Image: sql.NullString{String: s.GetImage(), Valid: true}, Status: sql.NullString{String: s.GetStatus(), Valid: true}, Error: sql.NullString{String: s.GetError(), Valid: true}, - ExitCode: sql.NullInt32{Int32: int32(s.GetExitCode()), Valid: true}, + ExitCode: sql.NullInt64{Int64: int64(s.GetExitCode()), Valid: true}, Created: sql.NullInt64{Int64: s.GetCreated(), Valid: true}, Started: sql.NullInt64{Int64: s.GetStarted(), Valid: true}, Finished: sql.NullInt64{Int64: s.GetFinished(), Valid: true}, diff --git a/database/types/service_test.go b/database/types/service_test.go index ce39d90db..518b9ddb5 100644 --- a/database/types/service_test.go +++ b/database/types/service_test.go @@ -18,12 +18,12 @@ func TestDatabase_Service_Nullify(t *testing.T) { ID: sql.NullInt64{Int64: 0, Valid: false}, BuildID: sql.NullInt64{Int64: 0, Valid: false}, RepoID: sql.NullInt64{Int64: 0, Valid: false}, - Number: sql.NullInt32{Int32: 0, Valid: false}, + Number: sql.NullInt64{Int64: 0, Valid: false}, Name: sql.NullString{String: "", Valid: false}, Image: sql.NullString{String: "", Valid: false}, Status: sql.NullString{String: "", Valid: false}, Error: sql.NullString{String: "", Valid: false}, - ExitCode: sql.NullInt32{Int32: 0, Valid: false}, + ExitCode: sql.NullInt64{Int64: 0, Valid: false}, Created: sql.NullInt64{Int64: 0, Valid: false}, Started: sql.NullInt64{Int64: 0, Valid: false}, Finished: sql.NullInt64{Int64: 0, Valid: false}, @@ -103,7 +103,7 @@ func TestDatabase_Service_Validate(t *testing.T) { service: &Service{ ID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "postgres", Valid: true}, Image: sql.NullString{String: "postgres:12-alpine", Valid: true}, }, @@ -113,7 +113,7 @@ func TestDatabase_Service_Validate(t *testing.T) { service: &Service{ ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "postgres", Valid: true}, Image: sql.NullString{String: "postgres:12-alpine", Valid: true}, }, @@ -134,7 +134,7 @@ func TestDatabase_Service_Validate(t *testing.T) { ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Image: sql.NullString{String: "postgres:12-alpine", Valid: true}, }, }, @@ -144,7 +144,7 @@ func TestDatabase_Service_Validate(t *testing.T) { ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "postgres", Valid: true}, }, }, @@ -205,12 +205,12 @@ func testService() *Service { ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "postgres", Valid: true}, Image: sql.NullString{String: "postgres:12-alpine", Valid: true}, Status: sql.NullString{String: "running", Valid: true}, Error: sql.NullString{String: "", Valid: false}, - ExitCode: sql.NullInt32{Int32: 0, Valid: false}, + ExitCode: sql.NullInt64{Int64: 0, Valid: false}, Created: sql.NullInt64{Int64: 1563474076, Valid: true}, Started: sql.NullInt64{Int64: 1563474078, Valid: true}, Finished: sql.NullInt64{Int64: 1563474079, Valid: true}, diff --git a/database/types/settings.go b/database/types/settings.go index 7bc29a6ca..637b1d17d 100644 --- a/database/types/settings.go +++ b/database/types/settings.go @@ -131,7 +131,7 @@ func (ps *Platform) ToAPI() *settings.Platform { psAPI.Compiler = &settings.Compiler{} psAPI.SetCloneImage(ps.CloneImage.String) psAPI.SetTemplateDepth(int(ps.TemplateDepth.Int64)) - psAPI.SetStarlarkExecLimit(uint64(ps.StarlarkExecLimit.Int64)) + psAPI.SetStarlarkExecLimit(ps.StarlarkExecLimit.Int64) psAPI.Queue = &settings.Queue{} psAPI.SetRoutes(ps.Routes) @@ -202,7 +202,7 @@ func SettingsFromAPI(s *settings.Platform) *Platform { Compiler: Compiler{ CloneImage: sql.NullString{String: s.GetCloneImage(), Valid: true}, TemplateDepth: sql.NullInt64{Int64: int64(s.GetTemplateDepth()), Valid: true}, - StarlarkExecLimit: sql.NullInt64{Int64: int64(s.GetStarlarkExecLimit()), Valid: true}, + StarlarkExecLimit: sql.NullInt64{Int64: s.GetStarlarkExecLimit(), Valid: true}, }, Queue: Queue{ Routes: pq.StringArray(s.GetRoutes()), diff --git a/database/types/step.go b/database/types/step.go index 4bd09c0b5..1ce8ab6d9 100644 --- a/database/types/step.go +++ b/database/types/step.go @@ -37,13 +37,13 @@ type Step struct { ID sql.NullInt64 `sql:"id"` BuildID sql.NullInt64 `sql:"build_id"` RepoID sql.NullInt64 `sql:"repo_id"` - Number sql.NullInt32 `sql:"number"` + Number sql.NullInt64 `sql:"number"` Name sql.NullString `sql:"name"` Image sql.NullString `sql:"image"` Stage sql.NullString `sql:"stage"` Status sql.NullString `sql:"status"` Error sql.NullString `sql:"error"` - ExitCode sql.NullInt32 `sql:"exit_code"` + ExitCode sql.NullInt64 `sql:"exit_code"` Created sql.NullInt64 `sql:"created"` Started sql.NullInt64 `sql:"started"` Finished sql.NullInt64 `sql:"finished"` @@ -80,7 +80,7 @@ func (s *Step) Nullify() *Step { } // check if the Number field should be false - if s.Number.Int32 == 0 { + if s.Number.Int64 == 0 { s.Number.Valid = false } @@ -110,7 +110,7 @@ func (s *Step) Nullify() *Step { } // check if the ExitCode field should be false - if s.ExitCode.Int32 == 0 { + if s.ExitCode.Int64 == 0 { s.ExitCode.Valid = false } @@ -160,13 +160,13 @@ func (s *Step) ToAPI() *api.Step { step.SetID(s.ID.Int64) step.SetBuildID(s.BuildID.Int64) step.SetRepoID(s.RepoID.Int64) - step.SetNumber(int(s.Number.Int32)) + step.SetNumber(int(s.Number.Int64)) step.SetName(s.Name.String) step.SetImage(s.Image.String) step.SetStage(s.Stage.String) step.SetStatus(s.Status.String) step.SetError(s.Error.String) - step.SetExitCode(int(s.ExitCode.Int32)) + step.SetExitCode(int(s.ExitCode.Int64)) step.SetCreated(s.Created.Int64) step.SetStarted(s.Started.Int64) step.SetFinished(s.Finished.Int64) @@ -192,7 +192,7 @@ func (s *Step) Validate() error { } // verify the Number field is populated - if s.Number.Int32 <= 0 { + if s.Number.Int64 <= 0 { return ErrEmptyStepNumber } @@ -229,13 +229,13 @@ func StepFromAPI(s *api.Step) *Step { ID: sql.NullInt64{Int64: s.GetID(), Valid: true}, BuildID: sql.NullInt64{Int64: s.GetBuildID(), Valid: true}, RepoID: sql.NullInt64{Int64: s.GetRepoID(), Valid: true}, - Number: sql.NullInt32{Int32: int32(s.GetNumber()), Valid: true}, + Number: sql.NullInt64{Int64: int64(s.GetNumber()), Valid: true}, Name: sql.NullString{String: s.GetName(), Valid: true}, Image: sql.NullString{String: s.GetImage(), Valid: true}, Stage: sql.NullString{String: s.GetStage(), Valid: true}, Status: sql.NullString{String: s.GetStatus(), Valid: true}, Error: sql.NullString{String: s.GetError(), Valid: true}, - ExitCode: sql.NullInt32{Int32: int32(s.GetExitCode()), Valid: true}, + ExitCode: sql.NullInt64{Int64: int64(s.GetExitCode()), Valid: true}, Created: sql.NullInt64{Int64: s.GetCreated(), Valid: true}, Started: sql.NullInt64{Int64: s.GetStarted(), Valid: true}, Finished: sql.NullInt64{Int64: s.GetFinished(), Valid: true}, diff --git a/database/types/step_test.go b/database/types/step_test.go index d2614effe..cc27030ca 100644 --- a/database/types/step_test.go +++ b/database/types/step_test.go @@ -18,13 +18,13 @@ func TestDatabase_Step_Nullify(t *testing.T) { ID: sql.NullInt64{Int64: 0, Valid: false}, BuildID: sql.NullInt64{Int64: 0, Valid: false}, RepoID: sql.NullInt64{Int64: 0, Valid: false}, - Number: sql.NullInt32{Int32: 0, Valid: false}, + Number: sql.NullInt64{Int64: 0, Valid: false}, Name: sql.NullString{String: "", Valid: false}, Image: sql.NullString{String: "", Valid: false}, Stage: sql.NullString{String: "", Valid: false}, Status: sql.NullString{String: "", Valid: false}, Error: sql.NullString{String: "", Valid: false}, - ExitCode: sql.NullInt32{Int32: 0, Valid: false}, + ExitCode: sql.NullInt64{Int64: 0, Valid: false}, Created: sql.NullInt64{Int64: 0, Valid: false}, Started: sql.NullInt64{Int64: 0, Valid: false}, Finished: sql.NullInt64{Int64: 0, Valid: false}, @@ -108,7 +108,7 @@ func TestDatabase_Step_Validate(t *testing.T) { step: &Step{ ID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "clone", Valid: true}, Image: sql.NullString{String: "target/vela-git:v0.3.0", Valid: true}, }, @@ -118,7 +118,7 @@ func TestDatabase_Step_Validate(t *testing.T) { step: &Step{ ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "clone", Valid: true}, Image: sql.NullString{String: "target/vela-git:v0.3.0", Valid: true}, }, @@ -129,7 +129,7 @@ func TestDatabase_Step_Validate(t *testing.T) { ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Image: sql.NullString{String: "target/vela-git:v0.3.0", Valid: true}, }, }, @@ -149,7 +149,7 @@ func TestDatabase_Step_Validate(t *testing.T) { ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "clone", Valid: true}, }, }, @@ -212,13 +212,13 @@ func testStep() *Step { ID: sql.NullInt64{Int64: 1, Valid: true}, BuildID: sql.NullInt64{Int64: 1, Valid: true}, RepoID: sql.NullInt64{Int64: 1, Valid: true}, - Number: sql.NullInt32{Int32: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, Name: sql.NullString{String: "clone", Valid: true}, Image: sql.NullString{String: "target/vela-git:v0.3.0", Valid: true}, Stage: sql.NullString{String: "", Valid: false}, Status: sql.NullString{String: "running", Valid: true}, Error: sql.NullString{String: "", Valid: false}, - ExitCode: sql.NullInt32{Int32: 0, Valid: false}, + ExitCode: sql.NullInt64{Int64: 0, Valid: false}, Created: sql.NullInt64{Int64: 1563474076, Valid: true}, Started: sql.NullInt64{Int64: 1563474078, Valid: true}, Finished: sql.NullInt64{Int64: 1563474079, Valid: true}, diff --git a/database/user/create.go b/database/user/create.go index ce2162d96..924a6f5f2 100644 --- a/database/user/create.go +++ b/database/user/create.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code in update.go package user import ( diff --git a/database/user/update.go b/database/user/update.go index bd8850502..10c3cc02f 100644 --- a/database/user/update.go +++ b/database/user/update.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code in create.go package user import ( diff --git a/internal/token/generate_rsa.go b/internal/token/generate_rsa.go index 483f0074f..3bb28b8f8 100644 --- a/internal/token/generate_rsa.go +++ b/internal/token/generate_rsa.go @@ -42,7 +42,7 @@ func (tm *Manager) GenerateRSA(ctx context.Context, db database.Interface) error } // create the JWK in the database - err = db.CreateJWK(context.TODO(), j) + err = db.CreateJWK(ctx, j) if err != nil { return err } diff --git a/mock/server/authentication.go b/mock/server/authentication.go index 8d940934e..2d9b87fbb 100644 --- a/mock/server/authentication.go +++ b/mock/server/authentication.go @@ -79,7 +79,7 @@ func getAuthenticate(c *gin.Context) { state := c.Request.FormValue("state") code := c.Request.FormValue("code") - err := "error" + err := constants.ErrorMock if len(state) == 0 && len(code) == 0 { c.AbortWithStatusJSON(http.StatusUnauthorized, api.Error{Message: &err}) @@ -100,7 +100,7 @@ func getAuthenticate(c *gin.Context) { // Don't pass "Token" in header to receive an error message. func getAuthenticateFromToken(c *gin.Context) { data := []byte(TokenRefreshResp) - err := "error" + err := constants.ErrorMock token := c.Request.Header.Get("Token") if len(token) == 0 { @@ -117,7 +117,7 @@ func getAuthenticateFromToken(c *gin.Context) { // // Don't pass "Authorization" in header to receive an unauthorized error message. func validateToken(c *gin.Context) { - err := "error" + err := constants.ErrorMock token := c.Request.Header.Get("Authorization") if len(token) == 0 { @@ -131,7 +131,7 @@ func validateToken(c *gin.Context) { // // Don't pass "Authorization" in header to receive an unauthorized error message. func validateOAuthToken(c *gin.Context) { - err := "error" + err := constants.ErrorMock token := c.Request.Header.Get("Authorization") if len(token) == 0 { diff --git a/mock/server/hook.go b/mock/server/hook.go index 0721a8a8f..c239bf471 100644 --- a/mock/server/hook.go +++ b/mock/server/hook.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore duplicate with user code package server import ( diff --git a/mock/server/secret.go b/mock/server/secret.go index 5410a4a75..72082cef1 100644 --- a/mock/server/secret.go +++ b/mock/server/secret.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore duplicate with user code package server import ( diff --git a/mock/server/service.go b/mock/server/service.go index bd241bf73..24b685aec 100644 --- a/mock/server/service.go +++ b/mock/server/service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore duplicate with user code package server import ( diff --git a/mock/server/step.go b/mock/server/step.go index 9b4a67e3f..c2c26e88b 100644 --- a/mock/server/step.go +++ b/mock/server/step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore duplicate with user code package server import ( diff --git a/queue/redis/opts.go b/queue/redis/opts.go index f58dab06a..a417de06c 100644 --- a/queue/redis/opts.go +++ b/queue/redis/opts.go @@ -71,8 +71,6 @@ func WithTimeout(timeout time.Duration) ClientOpt { } // WithPrivateKey sets the private key in the queue client for Redis. -// -//nolint:dupl // ignore similar code func WithPrivateKey(key string) ClientOpt { return func(c *client) error { c.Logger.Trace("configuring private key in redis queue client") @@ -111,8 +109,6 @@ func WithPrivateKey(key string) ClientOpt { } // WithPublicKey sets the public key in the queue client for Redis. -// -//nolint:dupl // ignore similar code func WithPublicKey(key string) ClientOpt { return func(c *client) error { c.Logger.Tracef("configuring public key in redis queue client") diff --git a/router/middleware/logger.go b/router/middleware/logger.go index 2e6a518f8..d549b87b4 100644 --- a/router/middleware/logger.go +++ b/router/middleware/logger.go @@ -45,7 +45,7 @@ type ECSFormatter struct { // // It receives: // 1. A time package format string (e.g. time.RFC3339). -func Logger(logger *logrus.Logger, timeFormat string) gin.HandlerFunc { +func Logger(logger *logrus.Logger, _ string) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() // some evil middlewares modify this values diff --git a/router/middleware/perm/perm_test.go b/router/middleware/perm/perm_test.go index a39c3a1b2..dad92845f 100644 --- a/router/middleware/perm/perm_test.go +++ b/router/middleware/perm/perm_test.go @@ -444,11 +444,11 @@ func TestPerm_MustBuildAccess(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) @@ -537,14 +537,14 @@ func TestPerm_MustBuildAccess_PlatAdmin(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) - _ = db.DeleteUser(_context.TODO(), u) + _ = db.DeleteRepo(ctx, r) + _ = db.DeleteUser(ctx, u) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) - _, _ = db.CreateUser(_context.TODO(), u) + _, _ = db.CreateUser(ctx, u) context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tok)) @@ -631,11 +631,11 @@ func TestPerm_MustBuildToken_WrongBuild(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) @@ -725,11 +725,11 @@ func TestPerm_MustIDRequestToken(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) @@ -822,14 +822,14 @@ func TestPerm_MustIDRequestToken_NotRunning(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) - _ = db.DeleteUser(_context.TODO(), u) + _ = db.DeleteRepo(ctx, r) + _ = db.DeleteUser(ctx, u) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) - _, _ = db.CreateUser(_context.TODO(), u) + _, _ = db.CreateUser(ctx, u) context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tok)) @@ -916,11 +916,11 @@ func TestPerm_MustIDRequestToken_WrongBuild(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) @@ -1005,11 +1005,11 @@ func TestPerm_MustSecretAdmin_BuildToken_Repo(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) context.Request, _ = http.NewRequest(http.MethodGet, "/test/native/repo/foo/bar/baz", nil) @@ -1091,11 +1091,11 @@ func TestPerm_MustSecretAdmin_BuildToken_Org(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) context.Request, _ = http.NewRequest(http.MethodGet, "/test/native/org/foo/*/baz", nil) @@ -1177,11 +1177,11 @@ func TestPerm_MustSecretAdmin_BuildToken_Shared(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) _, _ = db.CreateBuild(ctx, b) context.Request, _ = http.NewRequest(http.MethodGet, "/test/native/shared/foo/*/*", nil) @@ -2163,12 +2163,12 @@ func TestPerm_MustRead_WorkerBuildToken(t *testing.T) { defer func() { _ = db.DeleteBuild(ctx, b) - _ = db.DeleteRepo(_context.TODO(), r) + _ = db.DeleteRepo(ctx, r) db.Close() }() _, _ = db.CreateBuild(ctx, b) - _, _ = db.CreateRepo(_context.TODO(), r) + _, _ = db.CreateRepo(ctx, r) context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tok)) diff --git a/router/service.go b/router/service.go index 48787d19d..8aa09889c 100644 --- a/router/service.go +++ b/router/service.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with step package router import ( diff --git a/router/step.go b/router/step.go index 934e61d81..70bfad3dc 100644 --- a/router/step.go +++ b/router/step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code with service package router import ( diff --git a/scm/github/authentication.go b/scm/github/authentication.go index f7e86bb89..30376991f 100644 --- a/scm/github/authentication.go +++ b/scm/github/authentication.go @@ -33,7 +33,7 @@ func (c *client) Authorize(ctx context.Context, token string) (string, error) { } // Login begins the authentication workflow for the session. -func (c *client) Login(ctx context.Context, w http.ResponseWriter, r *http.Request) (string, error) { +func (c *client) Login(_ context.Context, w http.ResponseWriter, r *http.Request) (string, error) { c.Logger.Trace("processing login request") // generate a random string for creating the OAuth state @@ -78,7 +78,7 @@ func (c *client) Authenticate(ctx context.Context, _ http.ResponseWriter, r *htt } // exchange OAuth code for token - token, err := c.OAuth.Exchange(context.Background(), code) + token, err := c.OAuth.Exchange(ctx, code) if err != nil { return nil, err } @@ -155,7 +155,7 @@ func (c *client) ValidateOAuthToken(ctx context.Context, token string) (bool, er client.UploadURL = enterpriseURL } // check if the provided token was created by Vela - _, resp, err := client.Authorizations.Check(context.Background(), c.config.ClientID, token) + _, resp, err := client.Authorizations.Check(ctx, c.config.ClientID, token) // check if the error is of type ErrorResponse var gerr *github.ErrorResponse if errors.As(err, &gerr) { diff --git a/scm/github/repo.go b/scm/github/repo.go index 31fa2b9e8..3cf9dc760 100644 --- a/scm/github/repo.go +++ b/scm/github/repo.go @@ -636,10 +636,6 @@ func (c *client) GetHTMLURL(ctx context.Context, u *api.User, org, repo, name, r if data != nil { URL := data.GetHTMLURL() - if err != nil { - return "", err - } - return URL, nil } diff --git a/scm/github/webhook.go b/scm/github/webhook.go index b6f6556fe..632a42e1b 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -83,7 +83,7 @@ func (c *client) ProcessWebhook(ctx context.Context, request *http.Request) (*in } // VerifyWebhook verifies the webhook from a repo. -func (c *client) VerifyWebhook(ctx context.Context, request *http.Request, r *api.Repo) error { +func (c *client) VerifyWebhook(_ context.Context, request *http.Request, r *api.Repo) error { c.Logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -131,7 +131,7 @@ func (c *client) RedeliverWebhook(ctx context.Context, u *api.User, h *api.Hook) } // processPushEvent is a helper function to process the push event. -func (c *client) processPushEvent(ctx context.Context, h *api.Hook, payload *github.PushEvent) (*internal.Webhook, error) { +func (c *client) processPushEvent(_ context.Context, h *api.Hook, payload *github.PushEvent) (*internal.Webhook, error) { c.Logger.WithFields(logrus.Fields{ "org": payload.GetRepo().GetOwner().GetLogin(), "repo": payload.GetRepo().GetName(), diff --git a/secret/vault/count.go b/secret/vault/count.go index 927a1da85..4947b3848 100644 --- a/secret/vault/count.go +++ b/secret/vault/count.go @@ -14,7 +14,7 @@ import ( ) // Count counts a list of secrets. -func (c *client) Count(ctx context.Context, sType, org, name string, _ []string) (i int64, err error) { +func (c *client) Count(_ context.Context, sType, org, name string, _ []string) (i int64, err error) { // create log fields from secret metadata fields := logrus.Fields{ "org": org, diff --git a/secret/vault/create.go b/secret/vault/create.go index 2a3e94a2b..0fcede885 100644 --- a/secret/vault/create.go +++ b/secret/vault/create.go @@ -15,7 +15,7 @@ import ( ) // Create creates a new secret. -func (c *client) Create(ctx context.Context, sType, org, name string, s *api.Secret) (*api.Secret, error) { +func (c *client) Create(_ context.Context, sType, org, name string, s *api.Secret) (*api.Secret, error) { // create log fields from secret metadata fields := logrus.Fields{ "org": org, diff --git a/secret/vault/delete.go b/secret/vault/delete.go index 246393764..6f1cd8546 100644 --- a/secret/vault/delete.go +++ b/secret/vault/delete.go @@ -13,7 +13,7 @@ import ( ) // Delete deletes a secret. -func (c *client) Delete(ctx context.Context, sType, org, name, path string) error { +func (c *client) Delete(_ context.Context, sType, org, name, path string) error { // create log fields from secret metadata fields := logrus.Fields{ "org": org, diff --git a/secret/vault/get.go b/secret/vault/get.go index dd3b08591..129cf6ec2 100644 --- a/secret/vault/get.go +++ b/secret/vault/get.go @@ -15,7 +15,7 @@ import ( ) // Get captures a secret. -func (c *client) Get(ctx context.Context, sType, org, name, path string) (s *velaAPI.Secret, err error) { +func (c *client) Get(_ context.Context, sType, org, name, path string) (s *velaAPI.Secret, err error) { // create log fields from secret metadata fields := logrus.Fields{ "org": org, @@ -37,8 +37,7 @@ func (c *client) Get(ctx context.Context, sType, org, name, path string) (s *vel c.Logger.WithFields(fields).Tracef("getting vault %s secret %s for %s/%s", sType, path, org, name) - //nolint:ineffassign,staticcheck // ignore false positive - vault := new(api.Secret) + var vault *api.Secret // capture the secret from the Vault service switch sType { diff --git a/secret/vault/refresh_test.go b/secret/vault/refresh_test.go index 9fa3f62d1..176d1f06c 100644 --- a/secret/vault/refresh_test.go +++ b/secret/vault/refresh_test.go @@ -42,7 +42,7 @@ func Test_client_initialize(t *testing.T) { // run tests for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { data, err := os.ReadFile(fmt.Sprintf("testdata/refresh/%s", tt.responseFile)) if err != nil { t.Error(err) @@ -68,7 +68,7 @@ func Test_client_initialize(t *testing.T) { c.config.AuthMethod = tt.vaultAuthMethod c.AWS.StsClient = &mockSTSClient{ - mockGetCallerIdentityRequest: func(in *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { + mockGetCallerIdentityRequest: func(_ *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { return &request.Request{ HTTPRequest: &http.Request{ Host: "sts.amazonaws.com", @@ -105,7 +105,7 @@ func Test_client_getAwsToken(t *testing.T) { responseFile: "auth-response-success.json", responseCode: 200, stsClient: &mockSTSClient{ - mockGetCallerIdentityRequest: func(in *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { + mockGetCallerIdentityRequest: func(_ *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { return &request.Request{ HTTPRequest: &http.Request{ Host: "sts.amazonaws.com", @@ -125,7 +125,7 @@ func Test_client_getAwsToken(t *testing.T) { responseFile: "auth-response-error-role-not-found.json", responseCode: 400, stsClient: &mockSTSClient{ - mockGetCallerIdentityRequest: func(in *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { + mockGetCallerIdentityRequest: func(_ *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { return &request.Request{ HTTPRequest: &http.Request{ Host: "sts.amazonaws.com", @@ -143,7 +143,7 @@ func Test_client_getAwsToken(t *testing.T) { responseFile: "auth-response-error-no-auth-values.json", responseCode: 400, stsClient: &mockSTSClient{ - mockGetCallerIdentityRequest: func(in *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { + mockGetCallerIdentityRequest: func(_ *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { return &request.Request{ HTTPRequest: &http.Request{ Host: "sts.amazonaws.com", @@ -161,7 +161,7 @@ func Test_client_getAwsToken(t *testing.T) { responseFile: "auth-response-error-nil-secret.json", responseCode: 200, stsClient: &mockSTSClient{ - mockGetCallerIdentityRequest: func(in *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { + mockGetCallerIdentityRequest: func(_ *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { return &request.Request{ HTTPRequest: &http.Request{ Host: "sts.amazonaws.com", @@ -179,7 +179,7 @@ func Test_client_getAwsToken(t *testing.T) { responseFile: "testdata/auth-response-error-no-auth-values.json", responseCode: 400, stsClient: &mockSTSClient{ - mockGetCallerIdentityRequest: func(in *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { + mockGetCallerIdentityRequest: func(_ *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { return &request.Request{ HTTPRequest: &http.Request{ Host: "sts.amazonaws.com", @@ -198,7 +198,7 @@ func Test_client_getAwsToken(t *testing.T) { // run tests for _, tt := range tests { t.Run(tt.name, func(*testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { data, err := os.ReadFile(fmt.Sprintf("testdata/refresh/%s", tt.responseFile)) if err != nil { t.Error(err) From 5a1f4035c14ff36f616136471cf126bb4c72be3d Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:50:38 -0600 Subject: [PATCH 05/41] enhance(build)!: add fork field for OIDC (#1221) * enhance(build): add fork field for OIDC * fix test * integration test update --- api/oi_config.go | 1 + api/types/build.go | 30 +++++++++++++++++++++++ api/types/build_test.go | 18 ++++++++++++++ api/types/oidc.go | 1 + api/webhook/post.go | 2 +- compiler/native/environment_test.go | 10 ++++---- database/build/clean.go | 15 ++++-------- database/build/clean_test.go | 4 ++-- database/build/create_test.go | 6 ++--- database/build/table.go | 2 ++ database/build/update_test.go | 6 ++--- database/integration_test.go | 2 ++ database/testutils/api_resources.go | 1 + database/types/build.go | 3 +++ database/types/build_test.go | 4 ++++ internal/token/mint.go | 3 +++ internal/webhook.go | 7 +++--- mock/server/build.go | 1 + router/middleware/build/build_test.go | 1 + scm/github/webhook.go | 10 ++++---- scm/github/webhook_test.go | 34 ++++++++++++++------------- 21 files changed, 112 insertions(+), 49 deletions(-) diff --git a/api/oi_config.go b/api/oi_config.go index acd082951..b2984b38b 100644 --- a/api/oi_config.go +++ b/api/oi_config.go @@ -48,6 +48,7 @@ func GetOpenIDConfig(c *gin.Context) { "build_number", "build_id", "repo", + "pull_fork", "token_type", "actor", "actor_scm_id", diff --git a/api/types/build.go b/api/types/build.go index 70c33b8e7..2d6fa7041 100644 --- a/api/types/build.go +++ b/api/types/build.go @@ -38,6 +38,7 @@ type Build struct { Commit *string `json:"commit,omitempty"` Sender *string `json:"sender,omitempty"` SenderSCMID *string `json:"sender_scm_id,omitempty"` + Fork *bool `json:"fork,omitempty"` Author *string `json:"author,omitempty"` Email *string `json:"email,omitempty"` Link *string `json:"link,omitempty"` @@ -191,6 +192,7 @@ func (b *Build) Environment(workspace, channel string) map[string]string { envs["VELA_PULL_REQUEST"] = number envs["VELA_PULL_REQUEST_SOURCE"] = b.GetHeadRef() envs["VELA_PULL_REQUEST_TARGET"] = b.GetBaseRef() + envs["VELA_PULL_REQUEST_FORK"] = ToString(b.GetFork()) } // check if the Build event is tag @@ -516,6 +518,19 @@ func (b *Build) GetSenderSCMID() string { return *b.SenderSCMID } +// GetFork returns the Fork field. +// +// When the provided Build type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *Build) GetFork() bool { + // return zero value if Build type or Fork field is nil + if b == nil || b.Fork == nil { + return false + } + + return *b.Fork +} + // GetAuthor returns the Author field. // // When the provided Build type is nil, or the field within @@ -971,6 +986,19 @@ func (b *Build) SetSenderSCMID(v string) { b.SenderSCMID = &v } +// SetFork sets the Fork field. +// +// When the provided Build type is nil, it +// will set nothing and immediately return. +func (b *Build) SetFork(v bool) { + // return if Build type is nil + if b == nil { + return + } + + b.Fork = &v +} + // SetAuthor sets the Author field. // // When the provided Build type is nil, it @@ -1148,6 +1176,7 @@ func (b *Build) String() string { Event: %s, EventAction: %s, Finished: %d, + Fork: %t, HeadRef: %s, Host: %s, ID: %d, @@ -1184,6 +1213,7 @@ func (b *Build) String() string { b.GetEvent(), b.GetEventAction(), b.GetFinished(), + b.GetFork(), b.GetHeadRef(), b.GetHost(), b.GetID(), diff --git a/api/types/build_test.go b/api/types/build_test.go index ffb0656f1..9733475a2 100644 --- a/api/types/build_test.go +++ b/api/types/build_test.go @@ -79,6 +79,7 @@ func TestTypes_Build_Environment(t *testing.T) { _pull.SetEvent("pull_request") _pull.SetEventAction("opened") _pull.SetRef("refs/pulls/1/head") + _pull.SetFork(false) _tag := testBuild() _tag.SetEvent("tag") @@ -363,6 +364,7 @@ func TestTypes_Build_Environment(t *testing.T) { "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_SOURCE": "changes", "VELA_PULL_REQUEST_TARGET": "", + "VELA_PULL_REQUEST_FORK": "false", "BUILD_AUTHOR": "OctoKitty", "BUILD_AUTHOR_EMAIL": "OctoKitty@github.com", "BUILD_BASE_REF": "", @@ -563,6 +565,14 @@ func TestTypes_Build_Getters(t *testing.T) { t.Errorf("GetSender is %v, want %v", test.build.GetSender(), test.want.GetSender()) } + if test.build.GetSenderSCMID() != test.want.GetSenderSCMID() { + t.Errorf("GetSenderSCMID is %v, want %v", test.build.GetSenderSCMID(), test.want.GetSenderSCMID()) + } + + if test.build.GetFork() != test.want.GetFork() { + t.Errorf("GetFork is %v, want %v", test.build.GetFork(), test.want.GetFork()) + } + if test.build.GetAuthor() != test.want.GetAuthor() { t.Errorf("GetAuthor is %v, want %v", test.build.GetAuthor(), test.want.GetAuthor()) } @@ -657,6 +667,7 @@ func TestTypes_Build_Setters(t *testing.T) { test.build.SetCommit(test.want.GetCommit()) test.build.SetSender(test.want.GetSender()) test.build.SetSenderSCMID(test.want.GetSenderSCMID()) + test.build.SetFork(test.want.GetFork()) test.build.SetAuthor(test.want.GetAuthor()) test.build.SetEmail(test.want.GetEmail()) test.build.SetLink(test.want.GetLink()) @@ -762,6 +773,10 @@ func TestTypes_Build_Setters(t *testing.T) { t.Errorf("SetSenderSCMID is %v, want %v", test.build.GetSenderSCMID(), test.want.GetSenderSCMID()) } + if test.build.GetFork() != test.want.GetFork() { + t.Errorf("SetFork is %v, want %v", test.build.GetFork(), test.want.GetFork()) + } + if test.build.GetAuthor() != test.want.GetAuthor() { t.Errorf("SetAuthor is %v, want %v", test.build.GetAuthor(), test.want.GetAuthor()) } @@ -835,6 +850,7 @@ func TestTypes_Build_String(t *testing.T) { Event: %s, EventAction: %s, Finished: %d, + Fork: %t, HeadRef: %s, Host: %s, ID: %d, @@ -871,6 +887,7 @@ func TestTypes_Build_String(t *testing.T) { b.GetEvent(), b.GetEventAction(), b.GetFinished(), + b.GetFork(), b.GetHeadRef(), b.GetHost(), b.GetID(), @@ -925,6 +942,7 @@ func testBuild() *Build { b.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") b.SetSender("OctoKitty") b.SetSenderSCMID("123") + b.SetFork(false) b.SetAuthor("OctoKitty") b.SetEmail("OctoKitty@github.com") b.SetLink("https://example.company.com/github/octocat/1") diff --git a/api/types/oidc.go b/api/types/oidc.go index c6ba560e7..44ec2ee52 100644 --- a/api/types/oidc.go +++ b/api/types/oidc.go @@ -31,6 +31,7 @@ type OpenIDClaims struct { Image string `json:"image,omitempty"` ImageName string `json:"image_name,omitempty"` ImageTag string `json:"image_tag,omitempty"` + PullFork bool `json:"pull_fork,omitempty"` Ref string `json:"ref,omitempty"` Repo string `json:"repo,omitempty"` Request string `json:"request,omitempty"` diff --git a/api/webhook/post.go b/api/webhook/post.go index 3cb1017d2..6cbc17a72 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -537,7 +537,7 @@ func PostWebhook(c *gin.Context) { responded := false // if the webhook was from a Pull event from a forked repository, verify it is allowed to run - if webhook.PullRequest.IsFromFork { + if b.GetFork() { l.Tracef("inside %s workflow for fork PR build %s/%d", repo.GetApproveBuild(), repo.GetFullName(), b.GetNumber()) switch repo.GetApproveBuild() { diff --git a/compiler/native/environment_test.go b/compiler/native/environment_test.go index fae1f4552..b9ddcdec0 100644 --- a/compiler/native/environment_test.go +++ b/compiler/native/environment_test.go @@ -610,11 +610,11 @@ func TestNative_environment(t *testing.T) { // pull_request { w: workspace, - b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &pull, EventAction: &pullact, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, + b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &pull, EventAction: &pullact, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Fork: &booL, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // deployment { @@ -631,7 +631,7 @@ func TestNative_environment(t *testing.T) { for _, test := range tests { got := environment(test.b, test.m, test.r, test.u) - if diff := cmp.Diff(got, test.want); diff != "" { + if diff := cmp.Diff(test.want, got); diff != "" { t.Errorf("environment mismatch (-want +got):\n%s", diff) } } @@ -721,11 +721,11 @@ func Test_client_EnvironmentBuild(t *testing.T) { }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, {"pull_request", fields{ - build: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &pull, EventAction: &pullact, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, + build: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &pull, EventAction: &pullact, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Fork: &booL, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, metadata: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, {"deployment", fields{ build: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &deploy, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &target, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, diff --git a/database/build/clean.go b/database/build/clean.go index 521e24121..879e87f90 100644 --- a/database/build/clean.go +++ b/database/build/clean.go @@ -8,29 +8,24 @@ import ( "github.com/sirupsen/logrus" - api "github.com/go-vela/server/api/types" "github.com/go-vela/server/constants" - "github.com/go-vela/server/database/types" ) // CleanBuilds updates builds to an error with a provided message with a created timestamp prior to a defined moment. func (e *engine) CleanBuilds(ctx context.Context, msg string, before int64) (int64, error) { logrus.Tracef("cleaning pending or running builds created prior to %d", before) - b := new(api.Build) - b.SetStatus(constants.StatusError) - b.SetError(msg) - b.SetFinished(time.Now().UTC().Unix()) - - build := types.BuildFromAPI(b) - // send query to the database result := e.client. WithContext(ctx). Table(constants.TableBuild). Where("created < ?", before). Where("status = 'running' OR status = 'pending'"). - Updates(build) + Updates(map[string]interface{}{ + "status": constants.StatusError, + "error": msg, + "finished": time.Now().UTC().Unix(), + }) return result.RowsAffected, result.Error } diff --git a/database/build/clean_test.go b/database/build/clean_test.go index 179dd2fb8..bbaba4b39 100644 --- a/database/build/clean_test.go +++ b/database/build/clean_test.go @@ -66,8 +66,8 @@ func TestBuild_Engine_CleanBuilds(t *testing.T) { defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // ensure the mock expects the name query - _mock.ExpectExec(`UPDATE "builds" SET "status"=$1,"error"=$2,"finished"=$3,"deploy_payload"=$4 WHERE created < $5 AND (status = 'running' OR status = 'pending')`). - WithArgs("error", "msg", NowTimestamp{}, AnyArgument{}, 3). + _mock.ExpectExec(`UPDATE "builds" SET "error"=$1,"finished"=$2,"status"=$3 WHERE created < $4 AND (status = 'running' OR status = 'pending')`). + WithArgs("msg", NowTimestamp{}, "error", 3). WillReturnResult(sqlmock.NewResult(1, 2)) _sqlite := testSqlite(t) diff --git a/database/build/create_test.go b/database/build/create_test.go index 9f9d72399..219cd8209 100644 --- a/database/build/create_test.go +++ b/database/build/create_test.go @@ -42,9 +42,9 @@ func TestBuild_Engine_CreateBuild(t *testing.T) { // ensure the mock expects the query _mock.ExpectQuery(`INSERT INTO "builds" -("repo_id","pipeline_id","number","parent","event","event_action","status","error","enqueued","created","started","finished","deploy","deploy_number","deploy_payload","clone","source","title","message","commit","sender","sender_scm_id","author","email","link","branch","ref","base_ref","head_ref","host","runtime","distribution","approved_at","approved_by","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35) RETURNING "id"`). - WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). +("repo_id","pipeline_id","number","parent","event","event_action","status","error","enqueued","created","started","finished","deploy","deploy_number","deploy_payload","clone","source","title","message","commit","sender","sender_scm_id","fork","author","email","link","branch","ref","base_ref","head_ref","host","runtime","distribution","approved_at","approved_by","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35,$36) RETURNING "id"`). + WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). WillReturnRows(_rows) _sqlite := testSqlite(t) diff --git a/database/build/table.go b/database/build/table.go index 756a621cb..c250df1db 100644 --- a/database/build/table.go +++ b/database/build/table.go @@ -37,6 +37,7 @@ builds ( commit VARCHAR(500), sender VARCHAR(250), sender_scm_id VARCHAR(250), + fork BOOLEAN, author VARCHAR(250), email VARCHAR(500), link VARCHAR(1000), @@ -82,6 +83,7 @@ builds ( 'commit' TEXT, sender TEXT, sender_scm_id TEXT, + fork BOOLEAN, author TEXT, email TEXT, link TEXT, diff --git a/database/build/update_test.go b/database/build/update_test.go index 4c0c1137e..fe6a23596 100644 --- a/database/build/update_test.go +++ b/database/build/update_test.go @@ -41,9 +41,9 @@ func TestBuild_Engine_UpdateBuild(t *testing.T) { // ensure the mock expects the query _mock.ExpectExec(`UPDATE "builds" -SET "repo_id"=$1,"pipeline_id"=$2,"number"=$3,"parent"=$4,"event"=$5,"event_action"=$6,"status"=$7,"error"=$8,"enqueued"=$9,"created"=$10,"started"=$11,"finished"=$12,"deploy"=$13,"deploy_number"=$14,"deploy_payload"=$15,"clone"=$16,"source"=$17,"title"=$18,"message"=$19,"commit"=$20,"sender"=$21,"sender_scm_id"=$22,"author"=$23,"email"=$24,"link"=$25,"branch"=$26,"ref"=$27,"base_ref"=$28,"head_ref"=$29,"host"=$30,"runtime"=$31,"distribution"=$32,"approved_at"=$33,"approved_by"=$34 -WHERE "id" = $35`). - WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). +SET "repo_id"=$1,"pipeline_id"=$2,"number"=$3,"parent"=$4,"event"=$5,"event_action"=$6,"status"=$7,"error"=$8,"enqueued"=$9,"created"=$10,"started"=$11,"finished"=$12,"deploy"=$13,"deploy_number"=$14,"deploy_payload"=$15,"clone"=$16,"source"=$17,"title"=$18,"message"=$19,"commit"=$20,"sender"=$21,"sender_scm_id"=$22,"fork"=$23,"author"=$24,"email"=$25,"link"=$26,"branch"=$27,"ref"=$28,"base_ref"=$29,"head_ref"=$30,"host"=$31,"runtime"=$32,"distribution"=$33,"approved_at"=$34,"approved_by"=$35 +WHERE "id" = $36`). + WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). WillReturnResult(sqlmock.NewResult(1, 1)) _sqlite := testSqlite(t) diff --git a/database/integration_test.go b/database/integration_test.go index 89eda666a..ce9a3ccfe 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -2539,6 +2539,7 @@ func newResources() *Resources { buildOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") buildOne.SetSender("OctoKitty") buildOne.SetSenderSCMID("123") + buildOne.SetFork(false) buildOne.SetAuthor("OctoKitty") buildOne.SetEmail("OctoKitty@github.com") buildOne.SetLink("https://example.company.com/github/octocat/1") @@ -2576,6 +2577,7 @@ func newResources() *Resources { buildTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") buildTwo.SetSender("OctoKitty") buildTwo.SetSenderSCMID("123") + buildTwo.SetFork(false) buildTwo.SetAuthor("OctoKitty") buildTwo.SetEmail("OctoKitty@github.com") buildTwo.SetLink("https://example.company.com/github/octocat/2") diff --git a/database/testutils/api_resources.go b/database/testutils/api_resources.go index 18f619afd..70cc45d65 100644 --- a/database/testutils/api_resources.go +++ b/database/testutils/api_resources.go @@ -42,6 +42,7 @@ func APIBuild() *api.Build { Commit: new(string), Sender: new(string), SenderSCMID: new(string), + Fork: new(bool), Author: new(string), Email: new(string), Link: new(string), diff --git a/database/types/build.go b/database/types/build.go index f3aa22cd3..0ea840b9b 100644 --- a/database/types/build.go +++ b/database/types/build.go @@ -55,6 +55,7 @@ type Build struct { Commit sql.NullString `sql:"commit"` Sender sql.NullString `sql:"sender"` SenderSCMID sql.NullString `sql:"sender_scm_id"` + Fork sql.NullBool `sql:"fork"` Author sql.NullString `sql:"author"` Email sql.NullString `sql:"email"` Link sql.NullString `sql:"link"` @@ -315,6 +316,7 @@ func (b *Build) ToAPI() *api.Build { build.SetCommit(b.Commit.String) build.SetSender(b.Sender.String) build.SetSenderSCMID(b.SenderSCMID.String) + build.SetFork(b.Fork.Bool) build.SetAuthor(b.Author.String) build.SetEmail(b.Email.String) build.SetLink(b.Link.String) @@ -401,6 +403,7 @@ func BuildFromAPI(b *api.Build) *Build { Commit: sql.NullString{String: b.GetCommit(), Valid: true}, Sender: sql.NullString{String: b.GetSender(), Valid: true}, SenderSCMID: sql.NullString{String: b.GetSenderSCMID(), Valid: true}, + Fork: sql.NullBool{Bool: b.GetFork(), Valid: true}, Author: sql.NullString{String: b.GetAuthor(), Valid: true}, Email: sql.NullString{String: b.GetEmail(), Valid: true}, Link: sql.NullString{String: b.GetLink(), Valid: true}, diff --git a/database/types/build_test.go b/database/types/build_test.go index 372e09fc4..3bba2b634 100644 --- a/database/types/build_test.go +++ b/database/types/build_test.go @@ -66,6 +66,7 @@ func TestTypes_Build_Nullify(t *testing.T) { Message: sql.NullString{String: "", Valid: false}, Commit: sql.NullString{String: "", Valid: false}, Sender: sql.NullString{String: "", Valid: false}, + Fork: sql.NullBool{Bool: false, Valid: false}, Author: sql.NullString{String: "", Valid: false}, Email: sql.NullString{String: "", Valid: false}, Link: sql.NullString{String: "", Valid: false}, @@ -134,6 +135,7 @@ func TestTypes_Build_ToAPI(t *testing.T) { want.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") want.SetSender("OctoKitty") want.SetSenderSCMID("123") + want.SetFork(false) want.SetAuthor("OctoKitty") want.SetEmail("OctoKitty@github.com") want.SetLink("https://example.company.com/github/octocat/1") @@ -230,6 +232,7 @@ func TestTypes_Build_BuildFromAPI(t *testing.T) { b.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") b.SetSender("OctoKitty") b.SetSenderSCMID("123") + b.SetFork(false) b.SetAuthor("OctoKitty") b.SetEmail("OctoKitty@github.com") b.SetLink("https://example.company.com/github/octocat/1") @@ -294,6 +297,7 @@ func testBuild() *Build { Commit: sql.NullString{String: "48afb5bdc41ad69bf22588491333f7cf71135163", Valid: true}, Sender: sql.NullString{String: "OctoKitty", Valid: true}, SenderSCMID: sql.NullString{String: "123", Valid: true}, + Fork: sql.NullBool{Bool: false, Valid: true}, Author: sql.NullString{String: "OctoKitty", Valid: true}, Email: sql.NullString{String: "OctoKitty@github.com", Valid: true}, Link: sql.NullString{String: "https://example.company.com/github/octocat/1", Valid: true}, diff --git a/internal/token/mint.go b/internal/token/mint.go index 8fe074aab..75c8c4487 100644 --- a/internal/token/mint.go +++ b/internal/token/mint.go @@ -27,6 +27,7 @@ type Claims struct { IsActive bool `json:"is_active,omitempty"` IsAdmin bool `json:"is_admin,omitempty"` Repo string `json:"repo,omitempty"` + PullFork bool `json:"pull_fork,omitempty"` TokenType string `json:"token_type,omitempty"` Image string `json:"image,omitempty"` Request string `json:"request,omitempty"` @@ -103,6 +104,7 @@ func (tm *Manager) MintToken(mto *MintTokenOpts) (string, error) { } claims.Repo = mto.Repo + claims.PullFork = mto.Build.GetFork() claims.Subject = fmt.Sprintf("repo:%s:ref:%s:event:%s", mto.Repo, mto.Build.GetRef(), mto.Build.GetEvent()) claims.BuildID = mto.Build.GetID() claims.BuildNumber = mto.Build.GetNumber() @@ -162,6 +164,7 @@ func (tm *Manager) MintIDToken(ctx context.Context, mto *MintTokenOpts, db datab claims.BuildID = mto.Build.GetID() claims.Repo = mto.Repo claims.Event = fmt.Sprintf("%s:%s", mto.Build.GetEvent(), mto.Build.GetEventAction()) + claims.PullFork = mto.Build.GetFork() claims.SHA = mto.Build.GetCommit() claims.Ref = mto.Build.GetRef() claims.Subject = fmt.Sprintf("repo:%s:ref:%s:event:%s", mto.Repo, mto.Build.GetRef(), mto.Build.GetEvent()) diff --git a/internal/webhook.go b/internal/webhook.go index 7edb365c2..6020d2ab1 100644 --- a/internal/webhook.go +++ b/internal/webhook.go @@ -16,10 +16,9 @@ var ( // PullRequest defines the data pulled from PRs while // processing a webhook. type PullRequest struct { - Comment string - Number int - IsFromFork bool - Labels []string + Comment string + Number int + Labels []string } // Webhook defines a struct that is used to return diff --git a/mock/server/build.go b/mock/server/build.go index 786f26cb0..3a9905d16 100644 --- a/mock/server/build.go +++ b/mock/server/build.go @@ -84,6 +84,7 @@ const ( "commit": "48afb5bdc41ad69bf22588491333f7cf71135163", "sender": "OctoKitty", "sender_scm_id": "0", + "fork": false, "author": "OctoKitty", "email": "octokitty@github.com", "link": "https://vela.example.company.com/github/octocat/1", diff --git a/router/middleware/build/build_test.go b/router/middleware/build/build_test.go index aacf14949..8db7c8000 100644 --- a/router/middleware/build/build_test.go +++ b/router/middleware/build/build_test.go @@ -77,6 +77,7 @@ func TestBuild_Establish(t *testing.T) { want.SetCommit("") want.SetSender("") want.SetSenderSCMID("") + want.SetFork(false) want.SetAuthor("") want.SetEmail("") want.SetLink("") diff --git a/scm/github/webhook.go b/scm/github/webhook.go index 632a42e1b..91a3854f2 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -330,14 +330,14 @@ func (c *client) processPREvent(h *api.Hook, payload *github.PullRequestEvent) ( } // determine if pull request head is a fork and does not match the repo name of base - fromFork := payload.GetPullRequest().GetHead().GetRepo().GetFork() && - !strings.EqualFold(payload.GetPullRequest().GetBase().GetRepo().GetFullName(), payload.GetPullRequest().GetHead().GetRepo().GetFullName()) + + b.SetFork(payload.GetPullRequest().GetHead().GetRepo().GetFork() && + !strings.EqualFold(payload.GetPullRequest().GetBase().GetRepo().GetFullName(), payload.GetPullRequest().GetHead().GetRepo().GetFullName())) return &internal.Webhook{ PullRequest: internal.PullRequest{ - Number: payload.GetNumber(), - IsFromFork: fromFork, - Labels: prLabels, + Number: payload.GetNumber(), + Labels: prLabels, }, Hook: h, Repo: r, diff --git a/scm/github/webhook_test.go b/scm/github/webhook_test.go index 1ef1685fe..0d342ccdb 100644 --- a/scm/github/webhook_test.go +++ b/scm/github/webhook_test.go @@ -370,6 +370,7 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { wantBuild.SetCommit("34c5c7793cb3b279e22454cb6750c80560547b3a") wantBuild.SetSender("Codertocat") wantBuild.SetSenderSCMID("21031067") + wantBuild.SetFork(false) wantBuild.SetAuthor("Codertocat") wantBuild.SetEmail("") wantBuild.SetBranch("main") @@ -377,6 +378,10 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { wantBuild.SetBaseRef("main") wantBuild.SetHeadRef("changes") + wantBuildFork := *wantBuild + tBool := true + wantBuildFork.Fork = &tBool + wantBuild2 := new(api.Build) wantBuild2.SetEvent("pull_request") wantBuild2.SetEventAction("labeled") @@ -387,6 +392,7 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { wantBuild2.SetCommit("34c5c7793cb3b279e22454cb6750c80560547b3a") wantBuild2.SetSender("Codertocat") wantBuild2.SetSenderSCMID("21031067") + wantBuild2.SetFork(false) wantBuild2.SetAuthor("Codertocat") wantBuild2.SetEmail("") wantBuild2.SetBranch("main") @@ -404,6 +410,7 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { wantBuild3.SetCommit("34c5c7793cb3b279e22454cb6750c80560547b3a") wantBuild3.SetSender("Codertocat") wantBuild3.SetSenderSCMID("21031067") + wantBuild3.SetFork(false) wantBuild3.SetAuthor("Codertocat") wantBuild3.SetEmail("") wantBuild3.SetBranch("main") @@ -421,6 +428,7 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { wantBuild4.SetCommit("34c5c7793cb3b279e22454cb6750c80560547b3a") wantBuild4.SetSender("Codertocat") wantBuild4.SetSenderSCMID("21031067") + wantBuild4.SetFork(false) wantBuild4.SetAuthor("Codertocat") wantBuild4.SetEmail("") wantBuild4.SetBranch("main") @@ -439,8 +447,7 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { testData: "testdata/hooks/pull_request.json", want: &internal.Webhook{ PullRequest: internal.PullRequest{ - Number: wantHook.GetNumber(), - IsFromFork: false, + Number: wantHook.GetNumber(), }, Hook: wantHook, Repo: wantRepo, @@ -452,12 +459,11 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { testData: "testdata/hooks/pull_request_fork.json", want: &internal.Webhook{ PullRequest: internal.PullRequest{ - Number: wantHook.GetNumber(), - IsFromFork: true, + Number: wantHook.GetNumber(), }, Hook: wantHook, Repo: wantRepo, - Build: wantBuild, + Build: &wantBuildFork, }, }, { @@ -465,8 +471,7 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { testData: "testdata/hooks/pull_request_fork_same-repo.json", want: &internal.Webhook{ PullRequest: internal.PullRequest{ - Number: wantHook.GetNumber(), - IsFromFork: false, + Number: wantHook.GetNumber(), }, Hook: wantHook, Repo: wantRepo, @@ -496,9 +501,8 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { testData: "testdata/hooks/pull_request_labeled.json", want: &internal.Webhook{ PullRequest: internal.PullRequest{ - Number: wantHook.GetNumber(), - IsFromFork: false, - Labels: []string{"documentation"}, + Number: wantHook.GetNumber(), + Labels: []string{"documentation"}, }, Hook: wantHook, Repo: wantRepo, @@ -510,9 +514,8 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { testData: "testdata/hooks/pull_request_unlabeled.json", want: &internal.Webhook{ PullRequest: internal.PullRequest{ - Number: wantHook.GetNumber(), - IsFromFork: false, - Labels: []string{"documentation"}, + Number: wantHook.GetNumber(), + Labels: []string{"documentation"}, }, Hook: wantHook, Repo: wantRepo, @@ -524,9 +527,8 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { testData: "testdata/hooks/pull_request_edited_while_labeled.json", want: &internal.Webhook{ PullRequest: internal.PullRequest{ - Number: wantHook.GetNumber(), - IsFromFork: false, - Labels: []string{"documentation", "enhancement"}, + Number: wantHook.GetNumber(), + Labels: []string{"documentation", "enhancement"}, }, Hook: wantHook, Repo: wantRepo, From 9a5bf0018167360ef77db8dbbcc3eb5a27d69f4e Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Tue, 3 Dec 2024 08:47:44 -0600 Subject: [PATCH 06/41] enhance(compiler): cache templates per compilation (#1224) --- compiler/native/compile.go | 14 +- compiler/native/expand.go | 15 ++- compiler/native/expand_test.go | 231 +++++++++++++++++++++++++++++++++ compiler/native/native.go | 4 + compiler/native/native_test.go | 7 +- 5 files changed, 263 insertions(+), 8 deletions(-) diff --git a/compiler/native/compile.go b/compiler/native/compile.go index 13ad8eba7..9e7c638df 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -220,9 +220,17 @@ func (c *client) compileInline(ctx context.Context, p *yaml.Build, depth int) (* } for _, template := range p.Templates { - bytes, err := c.getTemplate(ctx, template, template.Name) - if err != nil { - return nil, err + var ( + bytes []byte + found bool + err error + ) + + if bytes, found = c.TemplateCache[template.Source]; !found { + bytes, err = c.getTemplate(ctx, template, template.Name) + if err != nil { + return nil, err + } } format := template.Format diff --git a/compiler/native/expand.go b/compiler/native/expand.go index 0420338c0..aae868693 100644 --- a/compiler/native/expand.go +++ b/compiler/native/expand.go @@ -118,9 +118,16 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin return s, err } - bytes, err := c.getTemplate(ctx, tmpl, step.Template.Name) - if err != nil { - return s, err + var ( + bytes []byte + found bool + ) + + if bytes, found = c.TemplateCache[tmpl.Source]; !found { + bytes, err = c.getTemplate(ctx, tmpl, step.Template.Name) + if err != nil { + return s, err + } } // initialize variable map if not parsed from config @@ -341,6 +348,8 @@ func (c *client) getTemplate(ctx context.Context, tmpl *yaml.Template, name stri return bytes, fmt.Errorf("unsupported template type: %v", tmpl.Type) } + c.TemplateCache[tmpl.Source] = bytes + return bytes, nil } diff --git a/compiler/native/expand_test.go b/compiler/native/expand_test.go index 8702f9c8b..8c56d77f8 100644 --- a/compiler/native/expand_test.go +++ b/compiler/native/expand_test.go @@ -954,6 +954,237 @@ func TestNative_ExpandSteps_TemplateCallTemplate(t *testing.T) { } } +func TestNative_ExpandStepsDuplicateCalls(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + testCallsMap := make(map[string]bool) + + // setup mock server + engine.GET("/api/v3/repos/:org/:repo/contents/:path", func(c *gin.Context) { + testCallKey := c.Param("path") + + if refQuery, exists := c.GetQuery("ref"); exists { + testCallKey += refQuery + } + + // this is the real test + if testCallsMap[testCallKey] { + t.Errorf("ExpandSteps() called the same template %s twice", c.Param("path")) + } + + testCallsMap[c.Param("path")] = true + body, err := convertFileToGithubResponse(c.Param("path")) + if err != nil { + t.Error(err) + } + c.JSON(http.StatusOK, body) + }) + + s := httptest.NewServer(engine) + defer s.Close() + + // setup types + set := flag.NewFlagSet("test", 0) + set.Bool("github-driver", true, "doc") + set.String("github-url", s.URL, "doc") + set.String("github-token", "", "doc") + set.Int("max-template-depth", 5, "doc") + set.String("clone-image", defaultCloneImage, "doc") + c := cli.NewContext(nil, set, nil) + + testRepo := new(api.Repo) + + testRepo.SetID(1) + testRepo.SetOrg("foo") + testRepo.SetName("bar") + + tests := []struct { + name string + tmpls map[string]*yaml.Template + }{ + { + name: "GitHub", + tmpls: map[string]*yaml.Template{ + "gradle": { + Name: "gradle", + Source: "github.example.com/foo/bar/long_template.yml", + Type: "github", + }, + }, + }, + } + + steps := yaml.StepSlice{ + &yaml.Step{ + Name: "sample", + Template: yaml.StepTemplate{ + Name: "gradle", + Variables: map[string]interface{}{ + "image": "openjdk:latest", + "environment": "{ GRADLE_USER_HOME: .gradle, GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false }", + "pull_policy": "pull: true", + }, + }, + }, + &yaml.Step{ + Name: "sample-dup", + Template: yaml.StepTemplate{ + Name: "gradle", + Variables: map[string]interface{}{ + "image": "openjdk:latest", + "environment": "{ GRADLE_USER_HOME: .gradle, GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false }", + "pull_policy": "pull: true", + }, + }, + }, + } + + globalEnvironment := raw.StringSliceMap{ + "foo": "test1", + "bar": "test2", + } + + wantSteps := yaml.StepSlice{ + &yaml.Step{ + Commands: []string{"./gradlew downloadDependencies"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "sample_install", + Pull: "always", + }, + &yaml.Step{ + Commands: []string{"./gradlew check"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "sample_test", + Pull: "always", + }, + &yaml.Step{ + Commands: []string{"./gradlew build", "echo gradle"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "sample_build", + Pull: "always", + }, + &yaml.Step{ + Commands: []string{"./gradlew downloadDependencies"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "sample-dup_install", + Pull: "always", + }, + &yaml.Step{ + Commands: []string{"./gradlew check"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "sample-dup_test", + Pull: "always", + }, + &yaml.Step{ + Commands: []string{"./gradlew build", "echo gradle"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "sample-dup_build", + Pull: "always", + }, + } + + wantSecrets := yaml.SecretSlice{ + &yaml.Secret{ + Name: "docker_username", + Key: "org/repo/foo/bar", + Engine: "native", + Type: "repo", + Origin: yaml.Origin{}, + Pull: "build_start", + }, + &yaml.Secret{ + Name: "foo_password", + Key: "org/repo/foo/password", + Engine: "vault", + Type: "repo", + Origin: yaml.Origin{}, + Pull: "build_start", + }, + } + + wantServices := yaml.ServiceSlice{ + &yaml.Service{ + Image: "postgres:12", + Name: "postgres", + Pull: "not_present", + }, + } + + wantEnvironment := raw.StringSliceMap{ + "foo": "test1", + "bar": "test2", + "star": "test3", + } + + // run test + compiler, err := FromCLIContext(c) + if err != nil { + t.Errorf("Creating new compiler returned err: %v", err) + } + + compiler.WithCommit("123abc456def").WithRepo(testRepo) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + build, err := compiler.ExpandSteps( + context.Background(), + &yaml.Build{ + Steps: steps, + Services: yaml.ServiceSlice{}, + Environment: globalEnvironment, + }, + test.tmpls, new(pipeline.RuleData), compiler.GetTemplateDepth()) + if err != nil { + t.Errorf("ExpandSteps_Type%s returned err: %v", test.name, err) + } + + if diff := cmp.Diff(build.Steps, wantSteps); diff != "" { + t.Errorf("ExpandSteps()_Type%s mismatch (-want +got):\n%s", test.name, diff) + } + + if diff := cmp.Diff(build.Secrets, wantSecrets); diff != "" { + t.Errorf("ExpandSteps()_Type%s mismatch (-want +got):\n%s", test.name, diff) + } + + if diff := cmp.Diff(build.Services, wantServices); diff != "" { + t.Errorf("ExpandSteps()_Type%s mismatch (-want +got):\n%s", test.name, diff) + } + + if diff := cmp.Diff(build.Environment, wantEnvironment); diff != "" { + t.Errorf("ExpandSteps()_Type%s mismatch (-want +got):\n%s", test.name, diff) + } + }) + } +} + func TestNative_ExpandSteps_TemplateCallTemplate_CircularFail(t *testing.T) { // setup context gin.SetMode(gin.TestMode) diff --git a/compiler/native/native.go b/compiler/native/native.go index ec6d78366..1b36e39fb 100644 --- a/compiler/native/native.go +++ b/compiler/native/native.go @@ -31,6 +31,7 @@ type client struct { PrivateGithub registry.Service UsePrivateGithub bool ModificationService ModificationConfig + TemplateCache map[string][]byte settings.Compiler @@ -102,6 +103,8 @@ func FromCLIContext(ctx *cli.Context) (*client, error) { c.UsePrivateGithub = true } + c.TemplateCache = make(map[string][]byte) + return c, nil } @@ -131,6 +134,7 @@ func (c *client) Duplicate() compiler.Engine { cc.CloneImage = c.CloneImage cc.TemplateDepth = c.TemplateDepth cc.StarlarkExecLimit = c.StarlarkExecLimit + cc.TemplateCache = c.TemplateCache return cc } diff --git a/compiler/native/native_test.go b/compiler/native/native_test.go index 86b143dbe..ef3ddb202 100644 --- a/compiler/native/native_test.go +++ b/compiler/native/native_test.go @@ -23,8 +23,9 @@ func TestNative_New(t *testing.T) { c := cli.NewContext(nil, set, nil) public, _ := github.New(context.Background(), "", "") want := &client{ - Github: public, - Compiler: settings.CompilerMockEmpty(), + Github: public, + Compiler: settings.CompilerMockEmpty(), + TemplateCache: make(map[string][]byte), } want.SetCloneImage(defaultCloneImage) @@ -56,6 +57,7 @@ func TestNative_New_PrivateGithub(t *testing.T) { Github: public, PrivateGithub: private, UsePrivateGithub: true, + TemplateCache: make(map[string][]byte), Compiler: settings.CompilerMockEmpty(), } want.SetCloneImage(defaultCloneImage) @@ -88,6 +90,7 @@ func TestNative_DuplicateRetainSettings(t *testing.T) { Github: public, PrivateGithub: private, UsePrivateGithub: true, + TemplateCache: make(map[string][]byte), Compiler: settings.CompilerMockEmpty(), } want.SetCloneImage(defaultCloneImage) From 58718a14273abee46c784c7d05887c30e38150c1 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Thu, 12 Dec 2024 10:07:18 -0600 Subject: [PATCH 07/41] ground work --- cmd/vela-server/main.go | 4 ++-- cmd/vela-server/server.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/vela-server/main.go b/cmd/vela-server/main.go index 27f1a2ff1..de8ccfd49 100644 --- a/cmd/vela-server/main.go +++ b/cmd/vela-server/main.go @@ -5,7 +5,7 @@ package main import ( "encoding/json" "fmt" - "github.com/go-vela/server/ex-storage" + "github.com/go-vela/server/storage" "os" "time" @@ -285,7 +285,7 @@ func main() { app.Flags = append(app.Flags, tracing.Flags...) // Add S3 Flags - app.Flags = append(app.Flags, ex_storage.Flags...) + app.Flags = append(app.Flags, storage.Flags...) if err = app.Run(os.Args); err != nil { logrus.Fatal(err) diff --git a/cmd/vela-server/server.go b/cmd/vela-server/server.go index 90e98d78a..fc3e55d2c 100644 --- a/cmd/vela-server/server.go +++ b/cmd/vela-server/server.go @@ -6,7 +6,7 @@ import ( "context" "errors" "fmt" - "github.com/go-vela/server/ex-storage" + "github.com/go-vela/server/storage" "net/http" "net/url" "os" @@ -94,7 +94,7 @@ func server(c *cli.Context) error { }() } - st, err := ex_storage.FromCLIContext(c) + st, err := storage.FromCLIContext(c) if err != nil { return err } From 8e9cd8e884268a62fd0d5bd68b1689f872726c6a Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Mon, 16 Dec 2024 22:18:58 -0600 Subject: [PATCH 08/41] working admin endpoints for bucket management --- api/admin/storage.go | 196 ++++++++++++++++++++++++++ api/types/storage.go | 17 +++ cmd/vela-server/server.go | 12 +- docker-compose.yml | 23 ++- go.mod | 21 +-- go.sum | 36 ----- router/admin.go | 6 + router/middleware/storage.go | 19 +++ router/middleware/storage_test.go | 44 ++++++ storage/context.go | 11 ++ storage/flags.go | 2 +- storage/minio/bucket_exists.go | 7 +- storage/minio/create_bucket.go | 21 ++- storage/minio/delete.go | 17 +++ storage/minio/delete_bucket.go | 11 +- storage/minio/get_bucket_lifecycle.go | 16 +-- storage/minio/minio.go | 35 ++++- storage/minio/set_bucket_lifecycle.go | 15 +- storage/service.go | 17 ++- storage/setup_test.go | 99 +++++++++++++ storage/storage.go | 3 +- storage/storage_test.go | 53 +++++++ 22 files changed, 574 insertions(+), 107 deletions(-) create mode 100644 api/admin/storage.go create mode 100644 api/types/storage.go create mode 100644 router/middleware/storage.go create mode 100644 router/middleware/storage_test.go create mode 100644 storage/minio/delete.go create mode 100644 storage/setup_test.go create mode 100644 storage/storage_test.go diff --git a/api/admin/storage.go b/api/admin/storage.go new file mode 100644 index 000000000..a45dc9615 --- /dev/null +++ b/api/admin/storage.go @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: Apache-2.0 + +package admin + +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/go-vela/server/api/types" + "github.com/go-vela/server/storage" + "github.com/go-vela/server/util" + "github.com/sirupsen/logrus" + "net/http" +) + +// swagger:operation POST /api/v1/admin/storage/bucket admin CreateBucket + +// +// Create a new bucket +// +// --- +// produces: +// - application/json +// parameters: +// - in: body +// name: body +// description: The bucket name to be created +// required: true +// schema: +// type: object +// properties: +// bucketName: +// type: string +// security: +// - ApiKeyAuth: [] +// responses: +// '201': +// description: Successfully created the bucket +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" + +// CreateBucket represents the API handler to create a new bucket. +func CreateBucket(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: creating bucket") + + // capture body from API request + input := new(types.Bucket) + + err := c.Bind(input) + if err != nil { + retErr := fmt.Errorf("unable to decode JSON for bucket %s: %w", input.BucketName, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + err = storage.FromGinContext(c).CreateBucket(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to create bucket: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.Status(http.StatusCreated) +} + +// swagger:operation DELETE /api/v1/admin/storage/bucket admin DeleteBucket +// +// Delete a bucket +// +// --- +// produces: +// - application/json +// parameters: +// - in: body +// name: body +// description: The bucket name to be deleted +// required: true +// schema: +// type: object +// properties: +// bucketName: +// type: string +// security: +// - ApiKeyAuth: [] +// responses: +// '200': +// description: Successfully deleted the bucket +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" + +// DeleteBucket represents the API handler to delete a bucket. +func DeleteBucket(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: deleting bucket") + + // capture body from API request + input := new(types.Bucket) + + err := c.Bind(input) + if err != nil { + retErr := fmt.Errorf("unable to decode JSON for bucket %s: %w", input.BucketName, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + err = storage.FromGinContext(c).DeleteBucket(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to delete bucket: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.Status(http.StatusOK) +} + +// swagger:operation PUT /api/v1/admin/storage/bucket/lifecycle admin AdminSetBucketLifecycle +// +// Set bucket lifecycle configuration +// +// --- +// produces: +// - application/json +// parameters: +// - in: body +// name: body +// description: The bucket lifecycle configuration +// required: true +// schema: +// type: object +// properties: +// bucketName: +// type: string +// lifecycleConfig: +// type: string +// security: +// - ApiKeyAuth: [] +// responses: +// '200': +// description: Successfully set the bucket lifecycle configuration +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" + +// SetBucketLifecycle represents the API handler to set bucket lifecycle configuration. +func SetBucketLifecycle(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: setting bucket lifecycle configuration") + + // capture body from API request + input := new(types.Bucket) + + err := c.Bind(input) + if err != nil { + retErr := fmt.Errorf("unable to decode JSON for bucket %s: %w", input.BucketName, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + err = storage.FromGinContext(c).SetBucketLifecycle(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to set bucket lifecycle configuration: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.Status(http.StatusOK) +} diff --git a/api/types/storage.go b/api/types/storage.go new file mode 100644 index 000000000..afe841f6e --- /dev/null +++ b/api/types/storage.go @@ -0,0 +1,17 @@ +package types + +import "github.com/minio/minio-go/v7/pkg/lifecycle" + +// Bucket is the API types representation of an object storage. +// +// swagger:model CreateBucket +type Bucket struct { + BucketName string `json:"bucket_name,omitempty"` + Options BucketOptions `json:"options,omitempty"` + LifecycleConfig lifecycle.Configuration `json:"life_cycle_config,omitempty"` +} + +type BucketOptions struct { + Region string `json:"region,omitempty"` + ObjectLocking bool `json:"object_locking,omitempty"` +} diff --git a/cmd/vela-server/server.go b/cmd/vela-server/server.go index fc3e55d2c..707c08210 100644 --- a/cmd/vela-server/server.go +++ b/cmd/vela-server/server.go @@ -94,11 +94,6 @@ func server(c *cli.Context) error { }() } - st, err := storage.FromCLIContext(c) - if err != nil { - return err - } - database, err := database.FromCLIContext(c, tc) if err != nil { return err @@ -119,6 +114,11 @@ func server(c *cli.Context) error { return err } + st, err := storage.FromCLIContext(c) + if err != nil { + return err + } + metadata, err := setupMetadata(c) if err != nil { return err @@ -193,10 +193,12 @@ func server(c *cli.Context) error { middleware.Metadata(metadata), middleware.TokenManager(tm), middleware.Queue(queue), + middleware.Storage(st), middleware.RequestVersion, middleware.Secret(c.String("vela-secret")), middleware.Secrets(secrets), middleware.Scm(scm), + middleware.Storage(st), middleware.QueueSigningPrivateKey(c.String("queue.private-key")), middleware.QueueSigningPublicKey(c.String("queue.public-key")), middleware.QueueAddress(c.String("queue.addr")), diff --git a/docker-compose.yml b/docker-compose.yml index 74e5a2a34..7a289976a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,6 +47,13 @@ services: VELA_OTEL_TRACING_ENABLE: true VELA_OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 VELA_OTEL_TRACING_SAMPLER_RATELIMIT_PER_SECOND: 100 + VELA_STORAGE_ENABLE: 'true' + VELA_STORAGE_DRIVER: minio + VELA_STORAGE_ENDPOINT: objectstorage:9000 + VELA_STORAGE_ACCESS_KEY: minio_access_user + VELA_STORAGE_SECRET_KEY: minio_secret_key + VELA_STORAGE_USE_SSL: 'false' + VELA_STORAGE_BUCKET: vela env_file: - .env restart: always @@ -180,14 +187,20 @@ services: - '4318:4318' objectstorage: - image: bitnami/minio:2020.12.29-debian-10-r17 + container_name: objectstorage + image: bitnami/minio:latest ports: - "9000:9000" - volumes: - - ./minio:/data + - "9001:9001" + networks: + - vela +# volumes: +# - ~./minio:/data environment: - MINIO_ACCESS_KEY: minio_access_key - MINIO_SECRET_KEY: minio_secret_key + - MINIO_ROOT_USER=minio_access_user + - MINIO_ROOT_PASSWORD=minio_secret_key +# command: minio server --address ":9000" --console-address ":9001" /data + networks: vela: diff --git a/go.mod b/go.mod index 5b6df7662..b2c848f11 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,6 @@ require ( github.com/adhocore/gronx v1.19.1 github.com/alicebob/miniredis/v2 v2.33.0 github.com/aws/aws-sdk-go v1.55.5 - github.com/aws/aws-sdk-go-v2/config v1.28.6 - github.com/aws/aws-sdk-go-v2/credentials v1.17.47 - github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/distribution/reference v0.6.0 github.com/drone/envsubst v1.0.3 @@ -38,6 +35,7 @@ require ( github.com/redis/go-redis/v9 v9.6.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.11.0 + github.com/stretchr/testify v1.9.0 github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 github.com/urfave/cli/v2 v2.27.4 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0 @@ -65,21 +63,6 @@ require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect - github.com/aws/aws-sdk-go-v2 v1.32.6 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect - github.com/aws/smithy-go v1.22.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.12.2 // indirect @@ -89,6 +72,7 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -145,6 +129,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect diff --git a/go.sum b/go.sum index 9432f2853..efa5846f7 100644 --- a/go.sum +++ b/go.sum @@ -27,42 +27,6 @@ github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= -github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= -github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= -github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= -github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= -github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= -github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= -github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/router/admin.go b/router/admin.go index d2eb667ac..0de1ef756 100644 --- a/router/admin.go +++ b/router/admin.go @@ -60,6 +60,12 @@ func AdminHandlers(base *gin.RouterGroup) { // Admin step endpoint _admin.PUT("/step", admin.UpdateStep) + // Admin storage endpoints + //_admin.GET("/storage/bucket", admin.) + _admin.POST("/storage/bucket", admin.CreateBucket) + _admin.PUT("/storage/bucket", admin.SetBucketLifecycle) + _admin.DELETE("/storage/bucket", admin.DeleteBucket) + // Admin user endpoint _admin.PUT("/user", admin.UpdateUser) diff --git a/router/middleware/storage.go b/router/middleware/storage.go new file mode 100644 index 000000000..11bc1dc2c --- /dev/null +++ b/router/middleware/storage.go @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "github.com/gin-gonic/gin" + "github.com/go-vela/server/storage" +) + +// Storage is a middleware function that initializes the object storage and +// attaches to the context of every http.Request. +func Storage(q storage.Storage) gin.HandlerFunc { + return func(c *gin.Context) { + // attach the object storage to the context + storage.ToContext(c, q) + + c.Next() + } +} diff --git a/router/middleware/storage_test.go b/router/middleware/storage_test.go new file mode 100644 index 000000000..eb0b74e37 --- /dev/null +++ b/router/middleware/storage_test.go @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "github.com/gin-gonic/gin" + "github.com/go-vela/server/storage" + "github.com/go-vela/server/storage/minio" + "net/http" + "net/http/httptest" + "reflect" + "testing" +) + +func TestMiddleware_Storage(t *testing.T) { + // setup types + var got storage.Storage + want, _ := minio.NewTest("", "", "") + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + context, engine := gin.CreateTestContext(resp) + context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) + + // setup mock server + engine.Use(Storage(want)) + engine.GET("/health", func(c *gin.Context) { + got = storage.FromGinContext(c) + + c.Status(http.StatusOK) + }) + + // run test + engine.ServeHTTP(context.Writer, context.Request) + + if resp.Code != http.StatusOK { + t.Errorf("Storage returned %v, want %v", resp.Code, http.StatusOK) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Storage is %v, want %v", got, want) + } +} diff --git a/storage/context.go b/storage/context.go index 411ba4412..82bad6627 100644 --- a/storage/context.go +++ b/storage/context.go @@ -10,6 +10,11 @@ import ( // key is the key used to store minio service in context const key = "minio" +// Setter defines a context that enables setting values. +type Setter interface { + Set(string, interface{}) +} + // FromContext retrieves minio service from the context func FromContext(ctx context.Context) Storage { // get minio value from context.Context @@ -45,6 +50,12 @@ func FromGinContext(c *gin.Context) Storage { return s } +// ToContext adds the secret Service to this +// context if it supports the Setter interface. +func ToContext(c Setter, s Storage) { + c.Set(key, s) +} + // WithContext adds the minio Storage to the context func WithContext(ctx context.Context, storage Storage) context.Context { return context.WithValue(ctx, key, storage) diff --git a/storage/flags.go b/storage/flags.go index 6c1d780f8..7eb89fd84 100644 --- a/storage/flags.go +++ b/storage/flags.go @@ -44,6 +44,6 @@ var Flags = []cli.Flag{ EnvVars: []string{"VELA_STORAGE_USE_SSL"}, Name: "storage.use.ssl", Usage: "enable storage to use SSL", - Value: true, + Value: false, }, } diff --git a/storage/minio/bucket_exists.go b/storage/minio/bucket_exists.go index 4a3704011..488f34191 100644 --- a/storage/minio/bucket_exists.go +++ b/storage/minio/bucket_exists.go @@ -2,13 +2,14 @@ package minio import ( "context" + api "github.com/go-vela/server/api/types" ) // BucketExists checks if a bucket exists in MinIO. -func (c *MinioClient) BucketExists(ctx context.Context, bucketName string) (bool, error) { - c.Logger.Tracef("checking if bucket %s exists", bucketName) +func (c *MinioClient) BucketExists(ctx context.Context, bucket *api.Bucket) (bool, error) { + c.Logger.Tracef("checking if bucket %s exists", bucket.BucketName) - exists, err := c.client.BucketExists(ctx, bucketName) + exists, err := c.client.BucketExists(ctx, bucket.BucketName) if err != nil { return false, err } diff --git a/storage/minio/create_bucket.go b/storage/minio/create_bucket.go index 4a6739986..d96cef081 100644 --- a/storage/minio/create_bucket.go +++ b/storage/minio/create_bucket.go @@ -2,21 +2,28 @@ package minio import ( "context" + api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" ) // CreateBucket creates a new bucket in MinIO. -func (c *MinioClient) CreateBucket(ctx context.Context, bucketName string, opts *minio.MakeBucketOptions) error { - c.Logger.Tracef("create new bucket: %s", bucketName) - if opts == nil { - opts = &minio.MakeBucketOptions{} +func (c *MinioClient) CreateBucket(ctx context.Context, bucket *api.Bucket) error { + c.Logger.Tracef("create new bucket: %s", bucket.BucketName) + var opts minio.MakeBucketOptions + if &bucket.Options == nil { c.Logger.Trace("Using US Standard Region as location default") + opts = minio.MakeBucketOptions{} + } else { + opts = minio.MakeBucketOptions{ + Region: bucket.Options.Region, + ObjectLocking: bucket.Options.ObjectLocking, + } } - err := c.client.MakeBucket(ctx, bucketName, *opts) + err := c.client.MakeBucket(ctx, bucket.BucketName, opts) if err != nil { - exists, errBucketExists := c.BucketExists(ctx, bucketName) + exists, errBucketExists := c.BucketExists(ctx, bucket) if errBucketExists == nil && exists { - c.Logger.Tracef("Bucket %s already exists", bucketName) + c.Logger.Tracef("Bucket %s already exists", bucket.BucketName) return nil } return err diff --git a/storage/minio/delete.go b/storage/minio/delete.go new file mode 100644 index 000000000..d07b38d78 --- /dev/null +++ b/storage/minio/delete.go @@ -0,0 +1,17 @@ +package minio + +import ( + "context" + "github.com/minio/minio-go/v7" +) + +// Delete deletes an object in a bucket in MinIO. +func (c *MinioClient) Delete(ctx context.Context, bucketName string, objectName string) error { + c.Logger.Tracef("deleting objectName: %s from bucketName: %s", objectName, bucketName) + + err := c.client.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{}) + if err != nil { + return err + } + return nil +} diff --git a/storage/minio/delete_bucket.go b/storage/minio/delete_bucket.go index cf06c2081..01aa02a66 100644 --- a/storage/minio/delete_bucket.go +++ b/storage/minio/delete_bucket.go @@ -1,12 +1,15 @@ package minio -import "context" +import ( + "context" + api "github.com/go-vela/server/api/types" +) // DeleteBucket deletes a bucket in MinIO. -func (c *MinioClient) DeleteBucket(ctx context.Context, bucketName string) error { - c.Logger.Tracef("deleting bucketName: %s", bucketName) +func (c *MinioClient) DeleteBucket(ctx context.Context, bucket *api.Bucket) error { + c.Logger.Tracef("deleting bucketName: %s", bucket.BucketName) - err := c.client.RemoveBucket(ctx, bucketName) + err := c.client.RemoveBucket(ctx, bucket.BucketName) if err != nil { return err } diff --git a/storage/minio/get_bucket_lifecycle.go b/storage/minio/get_bucket_lifecycle.go index 0153a3eb2..7fdb56ffa 100644 --- a/storage/minio/get_bucket_lifecycle.go +++ b/storage/minio/get_bucket_lifecycle.go @@ -2,19 +2,19 @@ package minio import ( "context" - "encoding/xml" + api "github.com/go-vela/server/api/types" ) // GetBucketLifecycle retrieves the lifecycle configuration for a bucket. -func (c *MinioClient) GetBucketLifecycle(ctx context.Context, bucketName string) (string, error) { - c.Logger.Tracef("getting lifecycle configuration for bucket %s", bucketName) - - lifecycleConfig, err := c.client.GetBucketLifecycle(ctx, bucketName) +func (c *MinioClient) GetBucketLifecycle(ctx context.Context, bucket *api.Bucket) (*api.Bucket, error) { + c.Logger.Tracef("getting lifecycle configuration for bucket %s", bucket.BucketName) + var lifecycleConfig *api.Bucket + lifeCycle, err := c.client.GetBucketLifecycle(ctx, bucket.BucketName) if err != nil { - return "", err + return lifecycleConfig, err } - lifecycleBytes, err := xml.MarshalIndent(lifecycleConfig, "", " ") + lifecycleConfig = &api.Bucket{BucketName: bucket.BucketName, LifecycleConfig: *lifeCycle} - return string(lifecycleBytes), nil + return lifecycleConfig, nil } diff --git a/storage/minio/minio.go b/storage/minio/minio.go index 983a548a2..7483ba83d 100644 --- a/storage/minio/minio.go +++ b/storage/minio/minio.go @@ -3,6 +3,8 @@ package minio import ( "context" "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/sirupsen/logrus" "time" ) @@ -44,7 +46,9 @@ func New(endpoint string, opts ...ClientOpt) (*MinioClient, error) { return nil, err } } - + c.Options.Creds = credentials.NewStaticV4(c.config.AccessKey, c.config.SecretKey, "") + c.Options.Secure = c.config.Secure + logrus.Debugf("secure: %v", c.config.Secure) // create the Minio client from the provided endpoint and options minioClient, err := minio.New(endpoint, c.Options) if err != nil { @@ -79,6 +83,35 @@ func pingBucket(c *MinioClient, bucket string) error { return nil } +// NewTest returns a Storage implementation that +// integrates with a local MinIO instance. +// +// This function is intended for running tests only. +// +//nolint:revive // ignore returning unexported client +func NewTest(endpoint, accessKey, secretKey string) (*MinioClient, error) { + // create a local fake MinIO instance + // + // https://pkg.go.dev/github.com/minio/minio-go/v7#New + minioClient, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKey, secretKey, ""), + Secure: false, + }) + if err != nil { + return nil, err + } + + return &MinioClient{ + client: minioClient, + config: &config{ + Endpoint: endpoint, + AccessKey: accessKey, + SecretKey: secretKey, + Secure: false, + }, + }, nil +} + //// UploadArtifact uploads an artifact to storage. //func (c *MinioClient) UploadArtifact(ctx context.Context, workflowID, artifactName string, data []byte) error { // key := path.Join("artifacts", workflowID, artifactName) diff --git a/storage/minio/set_bucket_lifecycle.go b/storage/minio/set_bucket_lifecycle.go index 8ea7a1993..1af96fb11 100644 --- a/storage/minio/set_bucket_lifecycle.go +++ b/storage/minio/set_bucket_lifecycle.go @@ -2,19 +2,12 @@ package minio import ( "context" - "encoding/xml" - "github.com/minio/minio-go/v7/pkg/lifecycle" + api "github.com/go-vela/server/api/types" ) // SetBucketLifecycle sets the lifecycle configuration for a bucket. -func (c *MinioClient) SetBucketLifecycle(ctx context.Context, bucketName string, lifecycleConfig string) error { - c.Logger.Tracef("setting lifecycle configuration for bucket %s", bucketName) +func (c *MinioClient) SetBucketLifecycle(ctx context.Context, bucket *api.Bucket) error { + c.Logger.Tracef("setting lifecycle configuration for bucket %s", bucket.BucketName) - var config lifecycle.Configuration - if err := xml.Unmarshal([]byte(lifecycleConfig), &config); err != nil { - c.Logger.Errorf("failed to unmarshal lifecycle configuration: %s", err) - return err - } - - return c.client.SetBucketLifecycle(ctx, bucketName, &config) + return c.client.SetBucketLifecycle(ctx, bucket.BucketName, &bucket.LifecycleConfig) } diff --git a/storage/service.go b/storage/service.go index 552e66f08..0439b36d1 100644 --- a/storage/service.go +++ b/storage/service.go @@ -1,24 +1,27 @@ package storage -import "context" +import ( + "context" + api "github.com/go-vela/server/api/types" +) // Storage defines the service interface for object storage operations. type Storage interface { // Bucket Management - CreateBucket(ctx context.Context, bucketName string) error - DeleteBucket(ctx context.Context, bucketName string) error - BucketExists(ctx context.Context, bucketName string) (bool, error) + CreateBucket(ctx context.Context, bucket *api.Bucket) error + DeleteBucket(ctx context.Context, bucket *api.Bucket) error + BucketExists(ctx context.Context, bucket *api.Bucket) (bool, error) ListBuckets(ctx context.Context) ([]string, error) // Object Operations Upload(ctx context.Context, bucketName string, objectName string, data []byte, contentType string) error Download(ctx context.Context, bucketName string, objectName string) ([]byte, error) Delete(ctx context.Context, bucketName string, objectName string) error - ListObjects(ctx context.Context, bucketName string, prefix string) ([]string, error) + ListObjects(ctx context.Context, bucketName string) ([]string, error) //// Presigned URLs //GeneratePresignedURL(ctx context.Context, bucket string, key string, expiry int64) (string, error) // Object Lifecycle - SetBucketLifecycle(ctx context.Context, bucketName string, lifecycleConfig string) error - GetBucketLifecycle(ctx context.Context, bucketName string) (string, error) + SetBucketLifecycle(ctx context.Context, bucketName *api.Bucket) error + GetBucketLifecycle(ctx context.Context, bucket *api.Bucket) (*api.Bucket, error) //// Workflow-Specific Operations //UploadArtifact(ctx context.Context, workflowID, artifactName string, data []byte) error //DownloadArtifact(ctx context.Context, workflowID, artifactName string) ([]byte, error) diff --git a/storage/setup_test.go b/storage/setup_test.go new file mode 100644 index 000000000..8e9eeb269 --- /dev/null +++ b/storage/setup_test.go @@ -0,0 +1,99 @@ +package storage + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetup_Minio(t *testing.T) { + setup := &Setup{ + Enable: true, + Driver: "minio", + Endpoint: "minio.example.com", + AccessKey: "access-key", + SecretKey: "secret-key", + Bucket: "bucket-name", + Secure: true, + } + + storage, err := setup.Minio() + assert.NoError(t, err) + assert.NotNil(t, storage) +} + +func TestSetup_Validate(t *testing.T) { + tests := []struct { + failure bool + setup *Setup + }{ + { + failure: false, + setup: &Setup{ + Enable: true, + Endpoint: "example.com", + AccessKey: "access-key", + SecretKey: "secret-key", + Bucket: "bucket-name", + }, + }, + { + failure: true, + setup: &Setup{ + Enable: false, + }, + }, + { + failure: true, + setup: &Setup{ + Enable: true, + AccessKey: "access-key", + SecretKey: "secret-key", + Bucket: "bucket-name", + }, + }, + { + failure: true, + setup: &Setup{ + Enable: true, + Endpoint: "example.com", + SecretKey: "secret-key", + Bucket: "bucket-name", + }, + }, + { + failure: true, + setup: &Setup{ + Enable: true, + Endpoint: "example.com", + AccessKey: "access-key", + Bucket: "bucket-name", + }, + }, + { + failure: true, + setup: &Setup{ + Enable: true, + Endpoint: "example.com", + AccessKey: "access-key", + SecretKey: "secret-key", + }, + }, + } + + for _, test := range tests { + err := test.setup.Validate() + + if test.failure { + if err == nil { + t.Errorf("Validate should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("Validate returned err: %v", err) + } + } +} diff --git a/storage/storage.go b/storage/storage.go index c2e9cf955..44469e6b3 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -10,7 +10,8 @@ import ( // FromCLIContext helper function to setup Minio Client from the CLI arguments. func FromCLIContext(c *cli.Context) (Storage, error) { logrus.Debug("creating Minio client from CLI configuration") - + logrus.Debugf("STORAGE Key: %s", c.String("storage.access.key")) + logrus.Debugf("STORAGE Secret: %s", c.String("storage.secret.key")) // S3 configuration _setup := &Setup{ Enable: c.Bool("storage.enable"), diff --git a/storage/storage_test.go b/storage/storage_test.go new file mode 100644 index 000000000..e481cac6e --- /dev/null +++ b/storage/storage_test.go @@ -0,0 +1,53 @@ +package storage + +import ( + "github.com/go-vela/server/constants" + "testing" +) + +func TestStorage_New(t *testing.T) { + tests := []struct { + failure bool + setup *Setup + }{ + { + failure: false, + setup: &Setup{ + Driver: constants.DriverMinio, + Enable: true, + Endpoint: "minio.example.com", + AccessKey: "access-key", + SecretKey: "secret-key", + Bucket: "bucket-name", + Secure: true, + }, + }, + { + failure: true, + setup: &Setup{ + Driver: "invalid-driver", + Enable: false, + Endpoint: "invalid.example.com", + AccessKey: "access-key", + SecretKey: "secret-key", + Bucket: "bucket-name", + Secure: true, + }, + }, + } + + for _, test := range tests { + _, err := New(test.setup) + + if test.failure { + if err == nil { + t.Errorf("New should have returned err") + } + continue + } + + if err != nil { + t.Errorf("New returned err: %v", err) + } + } +} From 3239b5372dab47a8e317f757e8951c0b7ff8fcd8 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Wed, 18 Dec 2024 14:50:17 -0600 Subject: [PATCH 09/41] working admin endpoints for object operations --- api/admin/storage.go | 216 +++++++++++++++++++++++++- api/types/storage.go | 6 + docker-compose.yml | 9 +- router/admin.go | 15 +- storage/minio/delete.go | 7 +- storage/minio/download.go | 26 +++- storage/minio/presigned_get_object.go | 19 +++ storage/minio/upload.go | 11 +- storage/service.go | 9 +- 9 files changed, 293 insertions(+), 25 deletions(-) create mode 100644 storage/minio/presigned_get_object.go diff --git a/api/admin/storage.go b/api/admin/storage.go index a45dc9615..4d4c804a6 100644 --- a/api/admin/storage.go +++ b/api/admin/storage.go @@ -62,7 +62,7 @@ func CreateBucket(c *gin.Context) { return } - + l.Debugf("bucket name: %s", input.BucketName) err = storage.FromGinContext(c).CreateBucket(ctx, input) if err != nil { retErr := fmt.Errorf("unable to create bucket: %w", err) @@ -194,3 +194,217 @@ func SetBucketLifecycle(c *gin.Context) { c.Status(http.StatusOK) } + +// swagger:operation POST /api/v1/admin/storage/bucket/upload admin UploadObject +// +// # Upload an object to a bucket +// +// --- +// produces: +// - application/json +// parameters: +// - in: body +// name: body +// description: The object to be uploaded +// required: true +// schema: +// type: object +// properties: +// bucketName: +// type: string +// objectName: +// type: string +// objectData: +// type: string +// +// security: +// - ApiKeyAuth: [] +// +// responses: +// +// '201': +// description: Successfully uploaded the object +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" +// +// UploadObject represents the API handler to upload an object to a bucket. +func UploadObject(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: uploading object") + + // capture body from API request + input := new(types.Object) + + err := c.Bind(input) + if err != nil { + retErr := fmt.Errorf("unable to decode JSON for object %s: %w", input.ObjectName, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + if input.BucketName == "" || input.ObjectName == "" { + retErr := fmt.Errorf("bucketName and objectName are required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + if input.FilePath == "" { + retErr := fmt.Errorf("file path is required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + err = storage.FromGinContext(c).Upload(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to upload object: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.Status(http.StatusCreated) +} + +// swagger:operation GET /api/v1/admin/storage/bucket/download admin DownloadObject +// +// # Download an object from a bucket +// +// --- +// produces: +// - application/json +// parameters: +// - in: query +// name: bucketName +// description: The name of the bucket +// required: true +// type: string +// - in: query +// name: objectName +// description: The name of the object +// required: true +// type: string +// +// security: +// - ApiKeyAuth: [] +// +// responses: +// +// '200': +// description: Successfully downloaded the object +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" +// +// DownloadObject represents the API handler to download an object from a bucket. +func DownloadObject(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: downloading object") + + // capture body from API request + input := new(types.Object) + + err := c.Bind(input) + if err != nil { + retErr := fmt.Errorf("unable to decode JSON for object %s: %w", input.ObjectName, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + if input.BucketName == "" || input.ObjectName == "" { + retErr := fmt.Errorf("bucketName and objectName are required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + if input.FilePath == "" { + retErr := fmt.Errorf("file path is required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + err = storage.FromGinContext(c).Download(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to download object: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("File has been downloaded to %s", input.FilePath)}) +} + +// swagger:operation GET /api/v1/admin/storage/presign admin GetPresignedURL +// +// # Generate a presigned URL for an object +// +// --- +// produces: +// - application/json +// parameters: +// - in: query +// name: bucketName +// description: The name of the bucket +// required: true +// type: string +// - in: query +// name: objectName +// description: The name of the object +// required: true +// type: string +// +// security: +// - ApiKeyAuth: [] +// +// responses: +// +// '200': +// description: Successfully generated the presigned URL +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" +func GetPresignedURL(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: generating presigned URL") + + // capture query parameters from API request + bucketName := c.Query("bucketName") + objectName := c.Query("objectName") + + if bucketName == "" || objectName == "" { + retErr := fmt.Errorf("bucketName and objectName are required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + + input := &types.Object{ + BucketName: bucketName, + ObjectName: objectName, + } + + url, err := storage.FromGinContext(c).PresignedGetObject(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to generate presigned URL: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.JSON(http.StatusOK, url) +} diff --git a/api/types/storage.go b/api/types/storage.go index afe841f6e..008388466 100644 --- a/api/types/storage.go +++ b/api/types/storage.go @@ -15,3 +15,9 @@ type BucketOptions struct { Region string `json:"region,omitempty"` ObjectLocking bool `json:"object_locking,omitempty"` } + +type Object struct { + ObjectName string `json:"object_name,omitempty"` + BucketName string `json:"bucket_name,omitempty"` + FilePath string `json:"file_path,omitempty"` +} diff --git a/docker-compose.yml b/docker-compose.yml index 7a289976a..6b359d052 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,9 +49,12 @@ services: VELA_OTEL_TRACING_SAMPLER_RATELIMIT_PER_SECOND: 100 VELA_STORAGE_ENABLE: 'true' VELA_STORAGE_DRIVER: minio - VELA_STORAGE_ENDPOINT: objectstorage:9000 - VELA_STORAGE_ACCESS_KEY: minio_access_user - VELA_STORAGE_SECRET_KEY: minio_secret_key +# VELA_STORAGE_ENDPOINT: objectstorage:9000 +# VELA_STORAGE_ACCESS_KEY: minio_access_user +# VELA_STORAGE_SECRET_KEY: minio_secret_key + VELA_STORAGE_ENDPOINT: stage.ttc.toss.target.com + VELA_STORAGE_ACCESS_KEY: + VELA_STORAGE_SECRET_KEY: VELA_STORAGE_USE_SSL: 'false' VELA_STORAGE_BUCKET: vela env_file: diff --git a/router/admin.go b/router/admin.go index 0de1ef756..eb1d1fdd9 100644 --- a/router/admin.go +++ b/router/admin.go @@ -60,12 +60,23 @@ func AdminHandlers(base *gin.RouterGroup) { // Admin step endpoint _admin.PUT("/step", admin.UpdateStep) - // Admin storage endpoints - //_admin.GET("/storage/bucket", admin.) + // Admin storage bucket endpoints + //_admin.GET("/storage/bucket", admin.ListBuckets) _admin.POST("/storage/bucket", admin.CreateBucket) _admin.PUT("/storage/bucket", admin.SetBucketLifecycle) _admin.DELETE("/storage/bucket", admin.DeleteBucket) + // Admin storage bucket lifecycle endpoint + //_admin.GET("/storage/bucket/lifecycle", admin.) + //_admin.POST("/storage/bucket/lifecycle", admin.) + + // Admin storage object endpoints + _admin.POST("/storage/object/download", admin.DownloadObject) + _admin.POST("/storage/object", admin.UploadObject) + + // Admin storage presign endpoints + _admin.GET("/storage/presign", admin.GetPresignedURL) + // Admin user endpoint _admin.PUT("/user", admin.UpdateUser) diff --git a/storage/minio/delete.go b/storage/minio/delete.go index d07b38d78..781b49c89 100644 --- a/storage/minio/delete.go +++ b/storage/minio/delete.go @@ -2,14 +2,15 @@ package minio import ( "context" + api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" ) // Delete deletes an object in a bucket in MinIO. -func (c *MinioClient) Delete(ctx context.Context, bucketName string, objectName string) error { - c.Logger.Tracef("deleting objectName: %s from bucketName: %s", objectName, bucketName) +func (c *MinioClient) Delete(ctx context.Context, object *api.Object) error { + c.Logger.Tracef("deleting objectName: %s from bucketName: %s", object.ObjectName, object.BucketName) - err := c.client.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{}) + err := c.client.RemoveObject(ctx, object.BucketName, object.ObjectName, minio.RemoveObjectOptions{}) if err != nil { return err } diff --git a/storage/minio/download.go b/storage/minio/download.go index c176b34b4..ac0d1deaf 100644 --- a/storage/minio/download.go +++ b/storage/minio/download.go @@ -2,15 +2,29 @@ package minio import ( "context" + api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" - "io" ) -func (c *MinioClient) Download(ctx context.Context, bucketName, key string) ([]byte, error) { - object, err := c.client.GetObject(ctx, bucketName, key, minio.GetObjectOptions{}) +func (c *MinioClient) Download(ctx context.Context, object *api.Object) error { + + // Check if the directory exists + //_, err := os.Stat(object.FilePath) + //if os.IsNotExist(err) { + // // Create the directory if it does not exist + // err = os.MkdirAll(object.FilePath, 0755) + // if err != nil { + // return fmt.Errorf("failed to create directory: %w", err) + // } + //} else if err != nil { + // return fmt.Errorf("failed to check directory: %w", err) + //} + err := c.client.FGetObject(ctx, object.BucketName, object.ObjectName, object.FilePath, minio.GetObjectOptions{}) if err != nil { - return nil, err + c.Logger.Errorf("unable to retrive object %s", object.ObjectName) + return err } - defer object.Close() - return io.ReadAll(object) + + c.Logger.Tracef("successfully downloaded object %s to %s", object.ObjectName, object.FilePath) + return nil } diff --git a/storage/minio/presigned_get_object.go b/storage/minio/presigned_get_object.go new file mode 100644 index 000000000..2f4777416 --- /dev/null +++ b/storage/minio/presigned_get_object.go @@ -0,0 +1,19 @@ +package minio + +import ( + "context" + api "github.com/go-vela/server/api/types" + "time" +) + +// PresignedGetObject generates a presigned URL for downloading an object. +func (c *MinioClient) PresignedGetObject(ctx context.Context, object *api.Object) (string, error) { + // Generate presigned URL for downloading the object. + // The URL is valid for 7 days. + presignedURL, err := c.client.PresignedGetObject(ctx, object.BucketName, object.ObjectName, 7*24*time.Hour, nil) + if err != nil { + return "", err + } + + return presignedURL.String(), nil +} diff --git a/storage/minio/upload.go b/storage/minio/upload.go index 10e50aaf1..6d6cd9c64 100644 --- a/storage/minio/upload.go +++ b/storage/minio/upload.go @@ -1,16 +1,15 @@ package minio import ( - "bytes" "context" + api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" ) -// Helper methods for uploading objects -func (c *MinioClient) Upload(ctx context.Context, bucketName, objectName string, data []byte, contentType string) error { - c.Logger.Tracef("uploading data to bucket %s", bucketName) +// Upload uploads an object to a bucket in MinIO.ts +func (c *MinioClient) Upload(ctx context.Context, object *api.Object) error { + c.Logger.Tracef("uploading data to bucket %s", object.ObjectName) - reader := bytes.NewReader(data) - _, err := c.client.PutObject(ctx, bucketName, objectName, reader, int64(len(data)), minio.PutObjectOptions{ContentType: contentType}) + _, err := c.client.FPutObject(ctx, object.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) return err } diff --git a/storage/service.go b/storage/service.go index 0439b36d1..bc133a619 100644 --- a/storage/service.go +++ b/storage/service.go @@ -13,12 +13,13 @@ type Storage interface { BucketExists(ctx context.Context, bucket *api.Bucket) (bool, error) ListBuckets(ctx context.Context) ([]string, error) // Object Operations - Upload(ctx context.Context, bucketName string, objectName string, data []byte, contentType string) error - Download(ctx context.Context, bucketName string, objectName string) ([]byte, error) - Delete(ctx context.Context, bucketName string, objectName string) error + Upload(ctx context.Context, object *api.Object) error + Download(ctx context.Context, object *api.Object) error + Delete(ctx context.Context, object *api.Object) error ListObjects(ctx context.Context, bucketName string) ([]string, error) - //// Presigned URLs + // Presigned URLs //GeneratePresignedURL(ctx context.Context, bucket string, key string, expiry int64) (string, error) + PresignedGetObject(ctx context.Context, object *api.Object) (string, error) // Object Lifecycle SetBucketLifecycle(ctx context.Context, bucketName *api.Bucket) error GetBucketLifecycle(ctx context.Context, bucket *api.Bucket) (*api.Bucket, error) From 8a957755642f44e323c3747cb0d77c25240f7c85 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Thu, 2 Jan 2025 14:47:56 -0600 Subject: [PATCH 10/41] all endpoints for storage --- api/admin/storage.go | 62 ++++++++++++++++++++++- docker-compose.yml | 4 +- go.mod | 11 +++- go.sum | 38 ++++++++++++++ router/admin.go | 4 +- storage/minio/bucket_exists.go | 5 +- storage/minio/create_bucket.go | 13 +++-- storage/minio/delete.go | 6 ++- storage/minio/delete_bucket.go | 5 +- storage/minio/download.go | 73 +++++++++++++++++++++++++-- storage/minio/get_bucket_lifecycle.go | 6 ++- storage/minio/list_objects.go | 5 +- storage/minio/presigned_get_object.go | 22 +++++++- storage/minio/set_bucket_lifecycle.go | 6 ++- storage/minio/stat_object.go | 28 ++++++++++ storage/minio/upload.go | 7 ++- storage/service.go | 1 + 17 files changed, 272 insertions(+), 24 deletions(-) create mode 100644 storage/minio/stat_object.go diff --git a/api/admin/storage.go b/api/admin/storage.go index 4d4c804a6..487cf1896 100644 --- a/api/admin/storage.go +++ b/api/admin/storage.go @@ -133,6 +133,64 @@ func DeleteBucket(c *gin.Context) { c.Status(http.StatusOK) } +// swagger:operation GET /api/v1/admin/storage/bucket/lifecycle admin GetBucketLifecycle +// +// # Get bucket lifecycle configuration +// +// --- +// produces: +// - application/json +// parameters: +// - in: query +// name: bucketName +// description: The name of the bucket +// required: true +// type: string +// +// security: +// - ApiKeyAuth: [] +// +// responses: +// +// '200': +// description: Successfully retrieved the bucket lifecycle configuration +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" +func GetBucketLifecycle(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: getting bucket lifecycle configuration") + + // capture query parameters from API request + bucketName := c.Query("bucketName") + + if bucketName == "" { + retErr := fmt.Errorf("bucketName is required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + + input := &types.Bucket{ + BucketName: bucketName, + } + + lifecycleConfig, err := storage.FromGinContext(c).GetBucketLifecycle(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to get bucket lifecycle configuration: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.JSON(http.StatusOK, lifecycleConfig) +} + // swagger:operation PUT /api/v1/admin/storage/bucket/lifecycle admin AdminSetBucketLifecycle // // Set bucket lifecycle configuration @@ -400,9 +458,9 @@ func GetPresignedURL(c *gin.Context) { } url, err := storage.FromGinContext(c).PresignedGetObject(ctx, input) - if err != nil { + if err != nil || url == "" { retErr := fmt.Errorf("unable to generate presigned URL: %w", err) - util.HandleError(c, http.StatusInternalServerError, retErr) + util.HandleError(c, http.StatusBadRequest, retErr) return } diff --git a/docker-compose.yml b/docker-compose.yml index 6b359d052..b42e760ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,8 +53,8 @@ services: # VELA_STORAGE_ACCESS_KEY: minio_access_user # VELA_STORAGE_SECRET_KEY: minio_secret_key VELA_STORAGE_ENDPOINT: stage.ttc.toss.target.com - VELA_STORAGE_ACCESS_KEY: - VELA_STORAGE_SECRET_KEY: + VELA_STORAGE_ACCESS_KEY: CTEGXGCIAFJS5EOV0YXY + VELA_STORAGE_SECRET_KEY: 7Qbay0a2OrgHnQPWmuMiEzmungbn87B5Exjgx2hw VELA_STORAGE_USE_SSL: 'false' VELA_STORAGE_BUCKET: vela env_file: diff --git a/go.mod b/go.mod index b2c848f11..9596bb396 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,11 @@ require ( github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/distribution/reference v0.6.0 github.com/drone/envsubst v1.0.3 + github.com/dustin/go-humanize v1.0.1 github.com/ghodss/yaml v1.0.0 github.com/gin-gonic/gin v1.10.0 github.com/go-playground/assert/v2 v2.2.0 + github.com/go-vela/archiver/v3 v3.4.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v65 v65.0.0 @@ -63,6 +65,7 @@ require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect + github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.12.2 // indirect @@ -75,7 +78,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect @@ -89,6 +92,7 @@ require ( github.com/go-playground/validator/v10 v10.22.1 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.2 // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -111,6 +115,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect @@ -127,8 +132,10 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nwaples/rardecode v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pierrec/lz4/v4 v4.1.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect @@ -141,8 +148,10 @@ require ( github.com/spf13/cast v1.7.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/ulikunitz/xz v0.5.9 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect go.opentelemetry.io/otel/metric v1.30.0 // indirect diff --git a/go.sum b/go.sum index efa5846f7..2fb5a2ec6 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGn github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE= github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= +github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= @@ -56,6 +58,7 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -68,6 +71,9 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -75,6 +81,7 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -106,17 +113,25 @@ github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27 github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-vela/archiver v3.1.1+incompatible h1:xTTMMwKyHwDgNFn+c1XsKHrHFg6UyWmK4oSceSduH7A= +github.com/go-vela/archiver v3.1.1+incompatible/go.mod h1:pYn/vJ47tuVltHdnA8YrDWOZ6yix4k0ZSKUBbWmkAFM= +github.com/go-vela/archiver/v3 v3.4.0 h1:c6GQRNTzr4Pn8HaxjzslIEiN89+kgZB4hLkXuBCOczI= +github.com/go-vela/archiver/v3 v3.4.0/go.mod h1:1HbXVOHBXsfzwSog3x7T6ZU9BUv+VEWnuaPLZS/v0/8= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v65 v65.0.0 h1:pQ7BmO3DZivvFk92geC0jB0q2m3gyn8vnYPgV7GSLhQ= @@ -185,15 +200,24 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -248,10 +272,16 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= +github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 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/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= +github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -268,6 +298,7 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= @@ -302,6 +333,9 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= +github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 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= @@ -310,6 +344,8 @@ github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +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/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -398,8 +434,10 @@ google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/router/admin.go b/router/admin.go index eb1d1fdd9..0890fe4d4 100644 --- a/router/admin.go +++ b/router/admin.go @@ -67,8 +67,8 @@ func AdminHandlers(base *gin.RouterGroup) { _admin.DELETE("/storage/bucket", admin.DeleteBucket) // Admin storage bucket lifecycle endpoint - //_admin.GET("/storage/bucket/lifecycle", admin.) - //_admin.POST("/storage/bucket/lifecycle", admin.) + _admin.GET("/storage/bucket/lifecycle", admin.GetBucketLifecycle) + _admin.PUT("/storage/bucket/lifecycle", admin.SetBucketLifecycle) // Admin storage object endpoints _admin.POST("/storage/object/download", admin.DownloadObject) diff --git a/storage/minio/bucket_exists.go b/storage/minio/bucket_exists.go index 488f34191..21b05d6e9 100644 --- a/storage/minio/bucket_exists.go +++ b/storage/minio/bucket_exists.go @@ -3,11 +3,14 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" + "github.com/sirupsen/logrus" ) // BucketExists checks if a bucket exists in MinIO. func (c *MinioClient) BucketExists(ctx context.Context, bucket *api.Bucket) (bool, error) { - c.Logger.Tracef("checking if bucket %s exists", bucket.BucketName) + c.Logger.WithFields(logrus.Fields{ + "bucket": bucket.BucketName, + }).Tracef("checking if bucket %s exists", bucket.BucketName) exists, err := c.client.BucketExists(ctx, bucket.BucketName) if err != nil { diff --git a/storage/minio/create_bucket.go b/storage/minio/create_bucket.go index d96cef081..8bee870ab 100644 --- a/storage/minio/create_bucket.go +++ b/storage/minio/create_bucket.go @@ -4,14 +4,19 @@ import ( "context" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" ) // CreateBucket creates a new bucket in MinIO. func (c *MinioClient) CreateBucket(ctx context.Context, bucket *api.Bucket) error { - c.Logger.Tracef("create new bucket: %s", bucket.BucketName) + c.Logger.WithFields(logrus.Fields{ + "bucket": bucket.BucketName, + }).Tracef("create new bucket: %s", bucket.BucketName) var opts minio.MakeBucketOptions if &bucket.Options == nil { - c.Logger.Trace("Using US Standard Region as location default") + c.Logger.WithFields(logrus.Fields{ + "bucket": bucket.BucketName, + }).Trace("Using US Standard Region as location default") opts = minio.MakeBucketOptions{} } else { opts = minio.MakeBucketOptions{ @@ -23,7 +28,9 @@ func (c *MinioClient) CreateBucket(ctx context.Context, bucket *api.Bucket) erro if err != nil { exists, errBucketExists := c.BucketExists(ctx, bucket) if errBucketExists == nil && exists { - c.Logger.Tracef("Bucket %s already exists", bucket.BucketName) + c.Logger.WithFields(logrus.Fields{ + "bucket": bucket.BucketName, + }).Tracef("Bucket %s already exists", bucket.BucketName) return nil } return err diff --git a/storage/minio/delete.go b/storage/minio/delete.go index 781b49c89..4d9794bbc 100644 --- a/storage/minio/delete.go +++ b/storage/minio/delete.go @@ -4,11 +4,15 @@ import ( "context" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" ) // Delete deletes an object in a bucket in MinIO. func (c *MinioClient) Delete(ctx context.Context, object *api.Object) error { - c.Logger.Tracef("deleting objectName: %s from bucketName: %s", object.ObjectName, object.BucketName) + c.Logger.WithFields(logrus.Fields{ + "bucket": object.BucketName, + "object": object.ObjectName, + }).Tracef("deleting objectName: %s from bucketName: %s", object.ObjectName, object.BucketName) err := c.client.RemoveObject(ctx, object.BucketName, object.ObjectName, minio.RemoveObjectOptions{}) if err != nil { diff --git a/storage/minio/delete_bucket.go b/storage/minio/delete_bucket.go index 01aa02a66..b2a69ea4d 100644 --- a/storage/minio/delete_bucket.go +++ b/storage/minio/delete_bucket.go @@ -3,11 +3,14 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" + "github.com/sirupsen/logrus" ) // DeleteBucket deletes a bucket in MinIO. func (c *MinioClient) DeleteBucket(ctx context.Context, bucket *api.Bucket) error { - c.Logger.Tracef("deleting bucketName: %s", bucket.BucketName) + c.Logger.WithFields(logrus.Fields{ + "bucket": bucket.BucketName, + }).Tracef("deleting bucketName: %s", bucket.BucketName) err := c.client.RemoveBucket(ctx, bucket.BucketName) if err != nil { diff --git a/storage/minio/download.go b/storage/minio/download.go index ac0d1deaf..ee71b68e4 100644 --- a/storage/minio/download.go +++ b/storage/minio/download.go @@ -2,12 +2,17 @@ package minio import ( "context" + "github.com/dustin/go-humanize" + "github.com/go-vela/archiver/v3" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" + "os" ) func (c *MinioClient) Download(ctx context.Context, object *api.Object) error { - + // Temporary file to store the object + filename := "/" // Check if the directory exists //_, err := os.Stat(object.FilePath) //if os.IsNotExist(err) { @@ -19,12 +24,72 @@ func (c *MinioClient) Download(ctx context.Context, object *api.Object) error { //} else if err != nil { // return fmt.Errorf("failed to check directory: %w", err) //} - err := c.client.FGetObject(ctx, object.BucketName, object.ObjectName, object.FilePath, minio.GetObjectOptions{}) + //err := c.client.FGetObject(ctx, object.BucketName, object.ObjectName, object.FilePath, minio.GetObjectOptions{}) + //if err != nil { + // c.Logger.Errorf("unable to retrive object %s", object.ObjectName) + // return err + //} + // + //c.Logger.Tracef("successfully downloaded object %s to %s", object.ObjectName, object.FilePath) + //return nil + logrus.Debugf("getting object info on bucket %s from path: %s", object.BucketName, object.ObjectName) + + // set a timeout on the request to the cache provider + //ctx, cancel := context.WithTimeout(context.Background(), r.Timeout) + //defer cancel() + + // collect metadata on the object + objInfo, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) + if objInfo.Key == "" { + logrus.Error(err) + return nil + } + + logrus.Debugf("getting object in bucket %s from path: %s", object.BucketName, object.ObjectName) + + logrus.Infof("%s to download", humanize.Bytes(uint64(objInfo.Size))) + + // retrieve the object in specified path of the bucket + err = c.client.FGetObject(ctx, object.BucketName, object.ObjectName, filename, minio.GetObjectOptions{}) if err != nil { - c.Logger.Errorf("unable to retrive object %s", object.ObjectName) return err } - c.Logger.Tracef("successfully downloaded object %s to %s", object.ObjectName, object.FilePath) + stat, err := os.Stat(object.FilePath) + if err != nil { + return err + } + + logrus.Infof("downloaded %s to %s on local filesystem", humanize.Bytes(uint64(stat.Size())), filename) + + logrus.Debug("getting current working directory") + + // grab the current working directory for unpacking the object + pwd, err := os.Getwd() + if err != nil { + return err + } + + logrus.Debugf("unarchiving file %s into directory %s", filename, pwd) + + // expand the object back onto the filesystem + err = archiver.Unarchive(object.FilePath, pwd) + if err != nil { + return err + } + + logrus.Infof("successfully unpacked file %s", object.FilePath) + + // delete the temporary archive file + err = os.Remove(filename) + if err != nil { + logrus.Infof("delete of file %s unsuccessful", filename) + } else { + logrus.Infof("file archive %s successfully deleted", filename) + } + + logrus.Infof("object downloaded successfully") + return nil + } diff --git a/storage/minio/get_bucket_lifecycle.go b/storage/minio/get_bucket_lifecycle.go index 7fdb56ffa..29e508b91 100644 --- a/storage/minio/get_bucket_lifecycle.go +++ b/storage/minio/get_bucket_lifecycle.go @@ -3,11 +3,15 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" + "github.com/sirupsen/logrus" ) // GetBucketLifecycle retrieves the lifecycle configuration for a bucket. func (c *MinioClient) GetBucketLifecycle(ctx context.Context, bucket *api.Bucket) (*api.Bucket, error) { - c.Logger.Tracef("getting lifecycle configuration for bucket %s", bucket.BucketName) + c.Logger.WithFields(logrus.Fields{ + "bucket": bucket.BucketName, + }).Tracef("getting lifecycle configuration for bucket %s", bucket.BucketName) + var lifecycleConfig *api.Bucket lifeCycle, err := c.client.GetBucketLifecycle(ctx, bucket.BucketName) if err != nil { diff --git a/storage/minio/list_objects.go b/storage/minio/list_objects.go index 31d8b5b8f..4cd2941a3 100644 --- a/storage/minio/list_objects.go +++ b/storage/minio/list_objects.go @@ -3,11 +3,14 @@ package minio import ( "context" "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" ) // ListObjects lists the objects in a bucket. func (c *MinioClient) ListObjects(ctx context.Context, bucketName string) ([]string, error) { - c.Logger.Tracef("listing objects in bucket %s", bucketName) + c.Logger.WithFields(logrus.Fields{ + "bucket": bucketName, + }).Tracef("listing objects in bucket %s", bucketName) objectCh := c.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{}) diff --git a/storage/minio/presigned_get_object.go b/storage/minio/presigned_get_object.go index 2f4777416..ac2f3a9ef 100644 --- a/storage/minio/presigned_get_object.go +++ b/storage/minio/presigned_get_object.go @@ -3,17 +3,37 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" + "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" "time" ) +// TODO hide URL behind a different name // PresignedGetObject generates a presigned URL for downloading an object. func (c *MinioClient) PresignedGetObject(ctx context.Context, object *api.Object) (string, error) { + c.Logger.WithFields(logrus.Fields{ + "bucket": object.BucketName, + "object": object.ObjectName, + }).Tracef("generating presigned URL for object %s in bucket %s", object.ObjectName, object.BucketName) + + // collect metadata on the object + objInfo, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) + if objInfo.Key == "" { + logrus.Errorf("unable to get object info %s from bucket %s: %v", object.ObjectName, object.BucketName, err) + return "", err + } + + _, err = c.client.BucketExists(ctx, object.BucketName) + if err != nil { + logrus.Errorf("unable to check if bucket %s exists: %v", object.BucketName, err) + return "", err + } // Generate presigned URL for downloading the object. // The URL is valid for 7 days. presignedURL, err := c.client.PresignedGetObject(ctx, object.BucketName, object.ObjectName, 7*24*time.Hour, nil) if err != nil { return "", err } - + //presignedURL.RequestURI() return presignedURL.String(), nil } diff --git a/storage/minio/set_bucket_lifecycle.go b/storage/minio/set_bucket_lifecycle.go index 1af96fb11..6b411c78d 100644 --- a/storage/minio/set_bucket_lifecycle.go +++ b/storage/minio/set_bucket_lifecycle.go @@ -3,11 +3,13 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" + "github.com/sirupsen/logrus" ) // SetBucketLifecycle sets the lifecycle configuration for a bucket. func (c *MinioClient) SetBucketLifecycle(ctx context.Context, bucket *api.Bucket) error { - c.Logger.Tracef("setting lifecycle configuration for bucket %s", bucket.BucketName) - + c.Logger.WithFields(logrus.Fields{ + "bucket": bucket.BucketName, + }).Tracef("setting lifecycle configuration for bucket %s", bucket.BucketName) return c.client.SetBucketLifecycle(ctx, bucket.BucketName, &bucket.LifecycleConfig) } diff --git a/storage/minio/stat_object.go b/storage/minio/stat_object.go new file mode 100644 index 000000000..84c8cb8e2 --- /dev/null +++ b/storage/minio/stat_object.go @@ -0,0 +1,28 @@ +package minio + +import ( + "context" + "fmt" + "github.com/go-vela/server/api/types" + "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" +) + +// StatObject retrieves the metadata of an object from the MinIO storage. +func (c *MinioClient) StatObject(ctx context.Context, object *types.Object) (*types.Object, error) { + c.Logger.WithFields(logrus.Fields{ + "bucket": object.BucketName, + "object": object.ObjectName, + }).Tracef("retrieving metadata for object %s from bucket %s", object.ObjectName, object.BucketName) + + // Get object info + info, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) + if err != nil { + return nil, fmt.Errorf("unable to get object info %s from bucket %s: %v", object.ObjectName, object.BucketName, err) + } + + // Map MinIO object info to API object + return &types.Object{ + ObjectName: info.Key, + }, nil +} diff --git a/storage/minio/upload.go b/storage/minio/upload.go index 6d6cd9c64..7c65fc7ae 100644 --- a/storage/minio/upload.go +++ b/storage/minio/upload.go @@ -4,12 +4,15 @@ import ( "context" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" + "github.com/sirupsen/logrus" ) // Upload uploads an object to a bucket in MinIO.ts func (c *MinioClient) Upload(ctx context.Context, object *api.Object) error { - c.Logger.Tracef("uploading data to bucket %s", object.ObjectName) - + c.Logger.WithFields(logrus.Fields{ + "bucket": object.BucketName, + "object": object.ObjectName, + }).Tracef("uploading data to bucket %s", object.BucketName) _, err := c.client.FPutObject(ctx, object.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) return err } diff --git a/storage/service.go b/storage/service.go index bc133a619..f45af8e75 100644 --- a/storage/service.go +++ b/storage/service.go @@ -13,6 +13,7 @@ type Storage interface { BucketExists(ctx context.Context, bucket *api.Bucket) (bool, error) ListBuckets(ctx context.Context) ([]string, error) // Object Operations + StatObject(ctx context.Context, object *api.Object) (*api.Object, error) Upload(ctx context.Context, object *api.Object) error Download(ctx context.Context, object *api.Object) error Delete(ctx context.Context, object *api.Object) error From a4f29d029be9823fe8b016823a376bbee8997097 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Tue, 7 Jan 2025 15:08:06 -0600 Subject: [PATCH 11/41] some working test --- docker-compose.yml | 17 +- go.mod | 95 +++ go.sum | 683 +++++++++++++++++++++ router/admin.go | 3 +- router/middleware/storage_test.go | 2 +- storage/minio/bucket_exists.go | 5 +- storage/minio/bucket_exists_test.go | 120 ++++ storage/minio/create_bucket.go | 15 +- storage/minio/create_bucket_test.go | 42 ++ storage/minio/delete.go | 6 +- storage/minio/delete_bucket.go | 5 +- storage/minio/download_test.go | 1 + storage/minio/get_bucket_lifecycle.go | 5 +- storage/minio/list_objects.go | 5 +- storage/minio/minio.go | 66 +- storage/minio/minio_test.go | 55 ++ storage/minio/presigned_get_object.go | 5 +- storage/minio/set_bucket_lifecycle.go | 5 +- storage/minio/stat_object.go | 6 +- storage/minio/test_data/create_bucket.json | 3 + storage/minio/upload.go | 6 +- 21 files changed, 1067 insertions(+), 83 deletions(-) create mode 100644 storage/minio/bucket_exists_test.go create mode 100644 storage/minio/create_bucket_test.go create mode 100644 storage/minio/download_test.go create mode 100644 storage/minio/minio_test.go create mode 100644 storage/minio/test_data/create_bucket.json diff --git a/docker-compose.yml b/docker-compose.yml index b42e760ce..9ed814c7e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,12 +49,9 @@ services: VELA_OTEL_TRACING_SAMPLER_RATELIMIT_PER_SECOND: 100 VELA_STORAGE_ENABLE: 'true' VELA_STORAGE_DRIVER: minio -# VELA_STORAGE_ENDPOINT: objectstorage:9000 -# VELA_STORAGE_ACCESS_KEY: minio_access_user -# VELA_STORAGE_SECRET_KEY: minio_secret_key - VELA_STORAGE_ENDPOINT: stage.ttc.toss.target.com - VELA_STORAGE_ACCESS_KEY: CTEGXGCIAFJS5EOV0YXY - VELA_STORAGE_SECRET_KEY: 7Qbay0a2OrgHnQPWmuMiEzmungbn87B5Exjgx2hw + VELA_STORAGE_ENDPOINT: "http://minio:9000" + VELA_STORAGE_ACCESS_KEY: minio_access_user + VELA_STORAGE_SECRET_KEY: minio_secret_key VELA_STORAGE_USE_SSL: 'false' VELA_STORAGE_BUCKET: vela env_file: @@ -189,9 +186,9 @@ services: - '16686:16686' - '4318:4318' - objectstorage: - container_name: objectstorage - image: bitnami/minio:latest + minio: + container_name: minio + image: minio/minio:latest ports: - "9000:9000" - "9001:9001" @@ -202,7 +199,7 @@ services: environment: - MINIO_ROOT_USER=minio_access_user - MINIO_ROOT_PASSWORD=minio_secret_key -# command: minio server --address ":9000" --console-address ":9001" /data + command: minio server --address ":9000" --console-address ":9001" /data networks: diff --git a/go.mod b/go.mod index 9596bb396..47d0cd856 100644 --- a/go.mod +++ b/go.mod @@ -61,24 +61,50 @@ require ( require ( dario.cat/mergo v1.0.1 // indirect + git.apache.org/thrift.git v0.13.0 // indirect + github.com/Azure/azure-pipeline-go v0.2.2 // indirect + github.com/Azure/azure-storage-blob-go v0.10.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/Shopify/sarama v1.27.2 // indirect + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect + github.com/alecthomas/participle v0.2.1 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/bcicen/jstream v1.0.1 // indirect + github.com/beevik/ntp v0.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.12.2 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cheggaaa/pb v1.0.29 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect + github.com/coredns/coredns v1.4.0 // indirect + github.com/coreos/go-semver v0.2.0 // indirect + github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 // indirect + github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dchest/siphash v1.2.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/djherbis/atime v1.0.0 // indirect + github.com/draganm/miniotest v0.1.0 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect + github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d // indirect + github.com/eapache/go-resiliency v1.2.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect + github.com/eapache/queue v1.1.0 // indirect + github.com/eclipse/paho.mqtt.golang v1.3.0 // indirect + github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect @@ -87,85 +113,154 @@ require ( github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.4 // 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.22.1 // indirect + github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.2 // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gorilla/css v1.0.1 // indirect + github.com/gorilla/handlers v1.5.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jcmturner/gofork v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid v1.3.1 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/klauspost/pgzip v1.2.5 // indirect + github.com/klauspost/readahead v1.3.1 // indirect + github.com/klauspost/reedsolomon v1.9.9 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/miekg/dns v1.1.35 // indirect + github.com/minio/cli v1.22.0 // indirect + github.com/minio/highwayhash v1.0.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/minio v0.0.0-20201229095728-cc457f179873 // indirect + github.com/minio/selfupdate v0.3.1 // indirect + github.com/minio/sha256-simd v0.1.1 // indirect + github.com/minio/simdjson-go v0.1.5 // indirect + github.com/minio/sio v0.2.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.5.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nats-io/jwt v1.1.0 // indirect + github.com/nats-io/nats.go v1.10.0 // indirect + github.com/nats-io/nkeys v0.2.0 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/nats-io/stan.go v0.7.0 // indirect + github.com/ncw/directio v1.0.5 // indirect + github.com/nsqio/go-nsq v1.0.8 // indirect github.com/nwaples/rardecode v1.1.0 // indirect + github.com/olivere/elastic/v7 v7.0.22 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/philhofer/fwd v1.1.1 // indirect + github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pierrec/lz4/v4 v4.1.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect + github.com/rjeczalik/notify v0.9.2 // indirect + github.com/rs/cors v1.7.0 // indirect github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/secure-io/sio-go v0.3.0 // indirect github.com/segmentio/asm v1.2.0 // indirect + github.com/shirou/gopsutil v3.20.11+incompatible // indirect github.com/shopspring/decimal v1.4.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/cast v1.7.0 // indirect + github.com/streadway/amqp v1.0.0 // indirect + github.com/tidwall/gjson v1.3.5 // indirect + github.com/tidwall/match v1.0.1 // indirect + github.com/tidwall/pretty v1.0.0 // indirect + github.com/tidwall/sjson v1.0.4 // indirect + github.com/tinylib/msgp v1.1.3 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/ulikunitz/xz v0.5.9 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect + github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a // indirect + github.com/willf/bitset v1.1.11 // indirect + github.com/willf/bloom v2.0.3+incompatible // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect + github.com/xdg/stringprep v1.0.0 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect + go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b // indirect go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/atomic v1.5.0 // indirect + go.uber.org/multierr v1.3.0 // indirect + go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee // indirect + go.uber.org/zap v1.13.0 // indirect golang.org/x/arch v0.10.0 // indirect + golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/api v0.152.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/grpc v1.66.1 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect + gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect + gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect + gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect + gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect + gopkg.in/ldap.v3 v3.0.3 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + honnef.co/go/tools v0.0.1-2019.2.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect ) diff --git a/go.sum b/go.sum index 2fb5a2ec6..ba6ddf701 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,40 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +git.apache.org/thrift.git v0.13.0 h1:/3bz5WZ+sqYArk7MBBBbDufMxKKOA56/6JO6psDpUDY= +git.apache.org/thrift.git v0.13.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= +github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs= +github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.9.1/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 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/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc= github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 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.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= @@ -16,8 +45,23 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.27.2 h1:1EyY1dsxNDUQEv0O/4TsjosHI2CgB1uo9H/v56xzTxc= +github.com/Shopify/sarama v1.27.2/go.mod h1:g5s5osgELxgM+Md9Qni9rzo7Rbt+vvFQI4bt/Mc93II= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adhocore/gronx v1.19.1 h1:S4c3uVp5jPjnk00De0lslyTenGJ4nA3Ydbkj1SbdPVc= github.com/adhocore/gronx v1.19.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/participle v0.2.1 h1:4AVLj1viSGa4LG5HDXKXrm5xRx19SB/rS/skPQB1Grw= +github.com/alecthomas/participle v0.2.1/go.mod h1:SW6HZGeZgSIpcUWX3fXpfZhuaWHnmoD5KCVaqSaNTkk= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +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/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= @@ -26,14 +70,31 @@ github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUi github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.35.20/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck= +github.com/bcicen/jstream v1.0.1/go.mod h1:9ielPxqFry7Y4Tg3j4BfjPocfJ3TbsRtXOAYXYmRuAQ= +github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw= +github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -45,45 +106,105 @@ github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKz github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/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/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= +github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/colinmarc/hdfs/v2 v2.1.1/go.mod h1:M3x+k8UKKmxtFu++uAZ0OtDU8jR3jnaZIAc6yK4Ue0c= +github.com/coredns/coredns v1.4.0 h1:RubBkYmkByUqZWWkjRHvNLnUHgkRVqAWgSMmRFvpE1A= +github.com/coredns/coredns v1.4.0/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= +github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= +github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 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/djherbis/atime v1.0.0 h1:ySLvBAM0EvOGaX7TI4dAM5lWj+RdJUCKtGSEHN8SGBg= +github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8= +github.com/draganm/miniotest v0.1.0 h1:Za9dqQYV5NV6UNxQkrR55wKc79LxZ5VHq4qcRLx5fZ0= +github.com/draganm/miniotest v0.1.0/go.mod h1:GxoGGOLfw0s0z6H7Be2MfKLoMXOrM9Yg78lu7FDdIho= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= +github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d h1:QK8IYltsNy+5QZcDFbVkyInrs98/wHy1tfUTGG91sps= +github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d/go.mod h1:apXo4PA/BgBPrt66j0N45O2stlBTRowdip2igwcUWVc= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/eclipse/paho.mqtt.golang v1.3.0 h1:MU79lqr3FKNKbSrGN7d7bNYqh8MwWW7Zcx0iG+VIw9I= +github.com/eclipse/paho.mqtt.golang v1.3.0/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= @@ -98,11 +219,20 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= +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= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -111,6 +241,11 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-vela/archiver v3.1.1+incompatible h1:xTTMMwKyHwDgNFn+c1XsKHrHFg6UyWmK4oSceSduH7A= @@ -119,16 +254,47 @@ github.com/go-vela/archiver/v3 v3.4.0 h1:c6GQRNTzr4Pn8HaxjzslIEiN89+kgZB4hLkXuBC github.com/go-vela/archiver/v3 v3.4.0/go.mod h1:1HbXVOHBXsfzwSog3x7T6ZU9BUv+VEWnuaPLZS/v0/8= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -141,27 +307,64 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/goware/urlx v0.3.2 h1:gdoo4kBHlkqZNaf6XlQ12LGtQOmpKJrR04Rc3RnpJEo= github.com/goware/urlx v0.3.2/go.mod h1:h8uwbJy68o+tQXCGZNa9D73WN8n0r9OBae5bUnLcgjw= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= @@ -169,14 +372,40 @@ github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3 github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= +github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -185,34 +414,69 @@ github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/gofork v0.0.0-20180107083740-2aebee971930/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= +github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/klauspost/readahead v1.3.1 h1:QqXNYvm+VvqYcbrRT4LojUciM0XrznFRIDrbHiJtu/0= +github.com/klauspost/readahead v1.3.1/go.mod h1:AH9juHzNH7xqdqFHrMRSHeH2Ps+vFf+kblDqzPFiLJg= +github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54= +github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -236,92 +500,277 @@ github.com/lestrrat-go/jwx/v2 v2.1.1 h1:Y2ltVl8J6izLYFs54BVcpXLv5msSW4o8eXwnzZLI github.com/lestrrat-go/jwx/v2 v2.1.1/go.mod h1:4LvZg7oxu6Q5VJwn7Mk/UwooNRnTHUpXBj2C4j3HNx0= 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/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 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-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= +github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= +github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/minio/cli v1.22.0 h1:VTQm7lmXm3quxO917X3p+el1l0Ca5X3S4PM2ruUYO68= +github.com/minio/cli v1.22.0/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY= +github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA= +github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= +github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= 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 v0.0.0-20201229095728-cc457f179873 h1:zGS+Yw+0GivMJ0ij70aMNWnjaHU43Kk4kc/N/xfVxfk= +github.com/minio/minio v0.0.0-20201229095728-cc457f179873/go.mod h1:Y2vPTyVMgM/hdmWY4z1FAh0tAiPVAMLz4eeQ5FNcJmk= +github.com/minio/minio-go/v7 v7.0.7-0.20201217170524-3baf9ea06f7c/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc= github.com/minio/minio-go/v7 v7.0.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA= github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= +github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs= +github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM= +github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/simdjson-go v0.1.5 h1:6T5mHh7r3kUvgwhmFWQAjoPV5Yt5oD/VPjAI9ViH1kM= +github.com/minio/simdjson-go v0.1.5/go.mod h1:oKURrZZEBtqObgJrSjN1Ln2n9MJj2icuBTkeJzZnvSI= +github.com/minio/sio v0.2.1 h1:NjzKiIMSMcHediVQR0AFVx2tp7Wxh9tKPfDI3kH7aHQ= +github.com/minio/sio v0.2.1/go.mod h1:8b0yPp2avGThviy/+OCJBI6OMpvxoUuiLvE6F1lebhw= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA= +github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= 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= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLgzmk= +github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM= +github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats-server/v2 v2.1.9/go.mod h1:9qVyoewoYXzG1ME9ox0HwkkzyYvnlBDugfR4Gg/8uHU= +github.com/nats-io/nats-streaming-server v0.19.0/go.mod h1:oqrRqpMg84aiPDyroTornjVWNYJKh+6ozh2Mgt8dslE= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY= +github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.2.0 h1:WXKF7diOaPU9cJdLD7nuzwasQy9vT1tBqzXZZf3AMJM= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +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/nats-io/stan.go v0.7.0 h1:sMVHD9RkxPOl6PJfDVBQd+gbxWkApeYl6GrH+10msO4= +github.com/nats-io/stan.go v0.7.0/go.mod h1:Ci6mUIpGQTjl++MqK2XzkWI/0vF+Bl72uScx7ejSYmU= +github.com/ncw/directio v1.0.5 h1:JSUBhdjEvVaJvOoyPAbcW0fnd0tvRXD76wEfZ1KcQz4= +github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t6ROk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nsqio/go-nsq v1.0.8 h1:3L2F8tNLlwXXlp2slDUrUWSBn2O3nMh8R1/KEDFTHPk= +github.com/nsqio/go-nsq v1.0.8/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olivere/elastic/v7 v7.0.22 h1:esBA6JJwvYgfms0EVlH7Z+9J4oQ/WUADF2y/nCNDw7s= +github.com/olivere/elastic/v7 v7.0.22/go.mod h1:VDexNy9NjmtAkrjNoI7tImv7FR4tf5zUA3ickqu5Pc8= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 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/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/quasilyte/go-ruleguard/dsl/fluent v0.0.0-20201222093424-5d7e62a465d3/go.mod h1:P7JlQWFT7jDcFZMtUPQbtGzzzxva3rBn6oIF+LPwFcM= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= +github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/secure-io/sio-go v0.3.0 h1:QKGb6rGJeiExac9wSWxnWPYo8O8OFN7lxXQvHshX6vo= +github.com/secure-io/sio-go v0.3.0/go.mod h1:D3KmXgKETffyYxBdFRN+Hpd2WzhzqS0EQwT3XWsAcBU= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto= +github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= @@ -329,6 +778,18 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/gjson v1.3.5 h1:2oW9FBNu8qt9jy5URgrzsVx/T/KSn3qn/smJQ0crlDQ= +github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.0.4 h1:UcdIRXff12Lpnu3OLtZvnc03g4vH2suXDXhBwBqmzYg= +github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= +github.com/tinylib/msgp v1.1.3 h1:3giwAkmtaEDLSV0MdO1lDLuPgklgPzmk8H9+So2BVfA= +github.com/tinylib/msgp v1.1.3/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -340,12 +801,25 @@ github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 h1:Jjn3zoRz13f8b1bR6Lr 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 v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX8Wdm2/JPA= +github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 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-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -354,6 +828,16 @@ github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBU github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b h1:5makfKENOTVu2bNoHzSqwwz+g70ivWLSnExzd33/2bI= +go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0 h1:n4Dd8YaDFeTd2uw+uCHJzOKeqfLgAOlePZpQ5f9cAoE= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0/go.mod h1:8aCCTMjP225r98yevEMM5NYDb3ianWLoeIzZ1rPyxHU= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0 h1:sqmsIQ75l6lfZjjpnXXT9DFVtYEDg6CH0/Cn4/3A1Wg= @@ -378,70 +862,260 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.starlark.net v0.0.0-20240925182052-1207426daebd h1:S+EMisJOHklQxnS3kqsY8jl2y5aF0FDEdcLnOw3q22E= go.starlark.net v0.0.0-20240925182052-1207426daebd/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/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-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= +google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM= google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= +gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= +gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg= +gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= +gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= +gopkg.in/ldap.v3 v3.0.3 h1:YKRHW/2sIl05JsCtx/5ZuUueFuJyoj/6+DGXe3wp6ro= +gopkg.in/ldap.v3 v3.0.3/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= @@ -450,6 +1124,12 @@ gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= @@ -457,5 +1137,8 @@ k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/router/admin.go b/router/admin.go index 0890fe4d4..cff6dd869 100644 --- a/router/admin.go +++ b/router/admin.go @@ -62,8 +62,7 @@ func AdminHandlers(base *gin.RouterGroup) { // Admin storage bucket endpoints //_admin.GET("/storage/bucket", admin.ListBuckets) - _admin.POST("/storage/bucket", admin.CreateBucket) - _admin.PUT("/storage/bucket", admin.SetBucketLifecycle) + _admin.PUT("/storage/bucket", admin.CreateBucket) _admin.DELETE("/storage/bucket", admin.DeleteBucket) // Admin storage bucket lifecycle endpoint diff --git a/router/middleware/storage_test.go b/router/middleware/storage_test.go index eb0b74e37..0652ec589 100644 --- a/router/middleware/storage_test.go +++ b/router/middleware/storage_test.go @@ -15,7 +15,7 @@ import ( func TestMiddleware_Storage(t *testing.T) { // setup types var got storage.Storage - want, _ := minio.NewTest("", "", "") + want, _ := minio.NewTest("", "", "", false) // setup context gin.SetMode(gin.TestMode) diff --git a/storage/minio/bucket_exists.go b/storage/minio/bucket_exists.go index 21b05d6e9..488f34191 100644 --- a/storage/minio/bucket_exists.go +++ b/storage/minio/bucket_exists.go @@ -3,14 +3,11 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" - "github.com/sirupsen/logrus" ) // BucketExists checks if a bucket exists in MinIO. func (c *MinioClient) BucketExists(ctx context.Context, bucket *api.Bucket) (bool, error) { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucket.BucketName, - }).Tracef("checking if bucket %s exists", bucket.BucketName) + c.Logger.Tracef("checking if bucket %s exists", bucket.BucketName) exists, err := c.client.BucketExists(ctx, bucket.BucketName) if err != nil { diff --git a/storage/minio/bucket_exists_test.go b/storage/minio/bucket_exists_test.go new file mode 100644 index 000000000..b52768fa3 --- /dev/null +++ b/storage/minio/bucket_exists_test.go @@ -0,0 +1,120 @@ +//package minio +// +//import ( +// "context" +// "net/http" +// "net/http/httptest" +// "testing" +// +// api "github.com/go-vela/server/api/types" +//) +// + +package minio + +import ( + "context" + "github.com/gin-gonic/gin" + api "github.com/go-vela/server/api/types" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func TestMinioClient_BucketExists(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.HEAD("/foo/", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + fake := httptest.NewServer(engine) + defer fake.Close() + ctx := context.TODO() + b := new(api.Bucket) + b.BucketName = "foo" + + client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + + // run test + exists, err := client.BucketExists(ctx, b) + if resp.Code != http.StatusOK { + t.Errorf("BucketExists returned %v, want %v", resp.Code, http.StatusOK) + } + + if err != nil { + t.Errorf("BucketExists returned err: %v", err) + } + + if !exists { + t.Errorf("BucketExists returned %v, want %v", exists, true) + } +} + +func TestMinioClient_BucketExists_Success(t *testing.T) { + // setup context + //gin.SetMode(gin.TestMode) + // + //resp := httptest.NewRecorder() + //_, engine := gin.CreateTestContext(resp) + // + //// setup mock server + //engine.GET("/api/v3/orgs/:org", func(c *gin.Context) { + // c.Header("Content-Type", "application/json") + // c.Status(http.StatusOK) + // c.File("testdata/get_org.json") + //}) + // + //s := httptest.NewServer(engine) + //defer s.Close() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Last-Modified", time.DateTime) + w.Header().Set("Content-Length", "5") + + // Write less bytes than the content length. + w.Write([]byte("12345")) + })) + defer srv.Close() + + //// New - instantiate minio client with options + //clnt, err := New(srv.Listener.Addr().String(), &Options{ + // Region: "us-east-1", + //}) + //if err != nil { + // t.Fatal(err) + //} + // setup types + u := new(api.Bucket) + u.BucketName = "foo" + + //want := "minio" + + client, err := New(srv.URL, WithAccessKey("accessKey"), WithSecretKey("secretKey"), WithSecure(false)) + if err != nil { + t.Fatal(err) + } + // run test + got, err := client.BucketExists(context.TODO(), u) + t.Logf("got: %v", got) + // We expect an error when reading back. + if got { + t.Errorf("BucketExists returned %v, want %v", got, false) + t.Errorf("BucketExists returned err: %v", err) + } + + // + //if err != nil { + // t.Errorf("GetOrgName returned err: %v", err) + //} + // + //if !reflect.DeepEqual(got, want) { + // t.Errorf("GetOrgName is %v, want %v", got, want) + //} + +} diff --git a/storage/minio/create_bucket.go b/storage/minio/create_bucket.go index 8bee870ab..0a53554d6 100644 --- a/storage/minio/create_bucket.go +++ b/storage/minio/create_bucket.go @@ -4,19 +4,14 @@ import ( "context" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" - "github.com/sirupsen/logrus" ) // CreateBucket creates a new bucket in MinIO. func (c *MinioClient) CreateBucket(ctx context.Context, bucket *api.Bucket) error { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucket.BucketName, - }).Tracef("create new bucket: %s", bucket.BucketName) + c.Logger.Tracef("create new bucket: %s", bucket.BucketName) var opts minio.MakeBucketOptions if &bucket.Options == nil { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucket.BucketName, - }).Trace("Using US Standard Region as location default") + c.Logger.Trace("Using US Standard Region as location default") opts = minio.MakeBucketOptions{} } else { opts = minio.MakeBucketOptions{ @@ -28,10 +23,8 @@ func (c *MinioClient) CreateBucket(ctx context.Context, bucket *api.Bucket) erro if err != nil { exists, errBucketExists := c.BucketExists(ctx, bucket) if errBucketExists == nil && exists { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucket.BucketName, - }).Tracef("Bucket %s already exists", bucket.BucketName) - return nil + c.Logger.Tracef("Bucket %s already exists", bucket.BucketName) + return err } return err } diff --git a/storage/minio/create_bucket_test.go b/storage/minio/create_bucket_test.go new file mode 100644 index 000000000..4e9d8a6aa --- /dev/null +++ b/storage/minio/create_bucket_test.go @@ -0,0 +1,42 @@ +package minio + +import ( + "context" + "github.com/gin-gonic/gin" + api "github.com/go-vela/server/api/types" + "net/http" + "net/http/httptest" + "testing" +) + +func TestMinioClient_CreateBucket(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.PUT("/foo/", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + }) + + fake := httptest.NewServer(engine) + defer fake.Close() + + b := new(api.Bucket) + b.BucketName = "foo" + + client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + + // run test + err := client.CreateBucket(context.TODO(), b) + if resp.Code != http.StatusOK { + t.Errorf("CreateBucket returned %v, want %v", resp.Code, http.StatusOK) + } + + if err != nil { + t.Errorf("CreateBucket returned err: %v", err) + } +} diff --git a/storage/minio/delete.go b/storage/minio/delete.go index 4d9794bbc..781b49c89 100644 --- a/storage/minio/delete.go +++ b/storage/minio/delete.go @@ -4,15 +4,11 @@ import ( "context" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" - "github.com/sirupsen/logrus" ) // Delete deletes an object in a bucket in MinIO. func (c *MinioClient) Delete(ctx context.Context, object *api.Object) error { - c.Logger.WithFields(logrus.Fields{ - "bucket": object.BucketName, - "object": object.ObjectName, - }).Tracef("deleting objectName: %s from bucketName: %s", object.ObjectName, object.BucketName) + c.Logger.Tracef("deleting objectName: %s from bucketName: %s", object.ObjectName, object.BucketName) err := c.client.RemoveObject(ctx, object.BucketName, object.ObjectName, minio.RemoveObjectOptions{}) if err != nil { diff --git a/storage/minio/delete_bucket.go b/storage/minio/delete_bucket.go index b2a69ea4d..01aa02a66 100644 --- a/storage/minio/delete_bucket.go +++ b/storage/minio/delete_bucket.go @@ -3,14 +3,11 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" - "github.com/sirupsen/logrus" ) // DeleteBucket deletes a bucket in MinIO. func (c *MinioClient) DeleteBucket(ctx context.Context, bucket *api.Bucket) error { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucket.BucketName, - }).Tracef("deleting bucketName: %s", bucket.BucketName) + c.Logger.Tracef("deleting bucketName: %s", bucket.BucketName) err := c.client.RemoveBucket(ctx, bucket.BucketName) if err != nil { diff --git a/storage/minio/download_test.go b/storage/minio/download_test.go new file mode 100644 index 000000000..c5576b1fd --- /dev/null +++ b/storage/minio/download_test.go @@ -0,0 +1 @@ +package minio diff --git a/storage/minio/get_bucket_lifecycle.go b/storage/minio/get_bucket_lifecycle.go index 29e508b91..8c9027cc8 100644 --- a/storage/minio/get_bucket_lifecycle.go +++ b/storage/minio/get_bucket_lifecycle.go @@ -3,14 +3,11 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" - "github.com/sirupsen/logrus" ) // GetBucketLifecycle retrieves the lifecycle configuration for a bucket. func (c *MinioClient) GetBucketLifecycle(ctx context.Context, bucket *api.Bucket) (*api.Bucket, error) { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucket.BucketName, - }).Tracef("getting lifecycle configuration for bucket %s", bucket.BucketName) + c.Logger.Tracef("getting lifecycle configuration for bucket %s", bucket.BucketName) var lifecycleConfig *api.Bucket lifeCycle, err := c.client.GetBucketLifecycle(ctx, bucket.BucketName) diff --git a/storage/minio/list_objects.go b/storage/minio/list_objects.go index 4cd2941a3..31d8b5b8f 100644 --- a/storage/minio/list_objects.go +++ b/storage/minio/list_objects.go @@ -3,14 +3,11 @@ package minio import ( "context" "github.com/minio/minio-go/v7" - "github.com/sirupsen/logrus" ) // ListObjects lists the objects in a bucket. func (c *MinioClient) ListObjects(ctx context.Context, bucketName string) ([]string, error) { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucketName, - }).Tracef("listing objects in bucket %s", bucketName) + c.Logger.Tracef("listing objects in bucket %s", bucketName) objectCh := c.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{}) diff --git a/storage/minio/minio.go b/storage/minio/minio.go index 7483ba83d..5a5c883a9 100644 --- a/storage/minio/minio.go +++ b/storage/minio/minio.go @@ -2,10 +2,11 @@ package minio import ( "context" + "fmt" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/sirupsen/logrus" + "strings" "time" ) @@ -31,13 +32,17 @@ func New(endpoint string, opts ...ClientOpt) (*MinioClient, error) { // create new Minio client c := new(MinioClient) + // default to secure connection + urlEndpoint := "s3.amazonaws.com" + useSSL := true + // create new fields c.config = new(config) c.Options = new(minio.Options) // create new logger for the client logger := logrus.StandardLogger() - c.Logger = logrus.NewEntry(logger).WithField("storage", "minio") + c.Logger = logrus.NewEntry(logger).WithField("minio", "minio") // apply all provided configuration options for _, opt := range opts { @@ -49,8 +54,23 @@ func New(endpoint string, opts ...ClientOpt) (*MinioClient, error) { c.Options.Creds = credentials.NewStaticV4(c.config.AccessKey, c.config.SecretKey, "") c.Options.Secure = c.config.Secure logrus.Debugf("secure: %v", c.config.Secure) + + if len(endpoint) > 0 { + useSSL = strings.HasPrefix(endpoint, "https://") + + if !useSSL { + if !strings.HasPrefix(endpoint, "http://") { + return nil, fmt.Errorf("invalid server %s: must to be a HTTP URI", endpoint) + } + + urlEndpoint = endpoint[7:] + } else { + urlEndpoint = endpoint[8:] + } + } + // create the Minio client from the provided endpoint and options - minioClient, err := minio.New(endpoint, c.Options) + minioClient, err := minio.New(urlEndpoint, c.Options) if err != nil { return nil, err } @@ -89,27 +109,33 @@ func pingBucket(c *MinioClient, bucket string) error { // This function is intended for running tests only. // //nolint:revive // ignore returning unexported client -func NewTest(endpoint, accessKey, secretKey string) (*MinioClient, error) { +func NewTest(endpoint, accessKey, secretKey string, secure bool) (*MinioClient, error) { + //var cleanup func() error + //var err error + //endpoint, cleanup, err = miniotest.StartEmbedded() + // + //if err != nil { + // fmt.Fprintf(os.Stderr, "while starting embedded server: %s", err) + // os.Exit(1) + //} + // + //err = cleanup() + //if err != nil { + // fmt.Fprintf(os.Stderr, "while stopping embedded server: %s", err) + //} + // create a local fake MinIO instance // // https://pkg.go.dev/github.com/minio/minio-go/v7#New - minioClient, err := minio.New(endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKey, secretKey, ""), - Secure: false, - }) - if err != nil { - return nil, err - } + //minioClient, err := minio.New(endpoint, &minio.Options{ + // Creds: credentials.NewStaticV4(accessKey, secretKey, ""), + // Secure: false, + //}) + //if err != nil { + // return nil, err + //} - return &MinioClient{ - client: minioClient, - config: &config{ - Endpoint: endpoint, - AccessKey: accessKey, - SecretKey: secretKey, - Secure: false, - }, - }, nil + return New(endpoint, WithAccessKey(accessKey), WithSecretKey(secretKey), WithSecure(secure)) } //// UploadArtifact uploads an artifact to storage. diff --git a/storage/minio/minio_test.go b/storage/minio/minio_test.go new file mode 100644 index 000000000..d4846858a --- /dev/null +++ b/storage/minio/minio_test.go @@ -0,0 +1,55 @@ +package minio + +import ( + "testing" +) + +var ( + endpoint = "localhost:9000" + _accessKey = "minio_access_user" + _secretKey = "minio_secret_key" + _useSSL = false +) + +func TestMinio_New(t *testing.T) { + // setup types + // create a local fake MinIO instance + // + // https://pkg.go.dev/github.com/minio/minio-go/v7#New + // setup tests + tests := []struct { + failure bool + endpoint string + }{ + { + failure: false, + endpoint: endpoint, + }, + { + failure: true, + endpoint: "", + }, + } + + // run tests + for _, test := range tests { + _, err := New( + test.endpoint, + WithAccessKey(_accessKey), + WithSecretKey(_secretKey), + WithSecure(_useSSL), + ) + + if test.failure { + if err == nil { + t.Errorf("New should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("New returned err: %v", err) + } + } +} diff --git a/storage/minio/presigned_get_object.go b/storage/minio/presigned_get_object.go index ac2f3a9ef..5ca04dbf2 100644 --- a/storage/minio/presigned_get_object.go +++ b/storage/minio/presigned_get_object.go @@ -11,10 +11,7 @@ import ( // TODO hide URL behind a different name // PresignedGetObject generates a presigned URL for downloading an object. func (c *MinioClient) PresignedGetObject(ctx context.Context, object *api.Object) (string, error) { - c.Logger.WithFields(logrus.Fields{ - "bucket": object.BucketName, - "object": object.ObjectName, - }).Tracef("generating presigned URL for object %s in bucket %s", object.ObjectName, object.BucketName) + c.Logger.Tracef("generating presigned URL for object %s in bucket %s", object.ObjectName, object.BucketName) // collect metadata on the object objInfo, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) diff --git a/storage/minio/set_bucket_lifecycle.go b/storage/minio/set_bucket_lifecycle.go index 6b411c78d..1b98c36da 100644 --- a/storage/minio/set_bucket_lifecycle.go +++ b/storage/minio/set_bucket_lifecycle.go @@ -3,13 +3,10 @@ package minio import ( "context" api "github.com/go-vela/server/api/types" - "github.com/sirupsen/logrus" ) // SetBucketLifecycle sets the lifecycle configuration for a bucket. func (c *MinioClient) SetBucketLifecycle(ctx context.Context, bucket *api.Bucket) error { - c.Logger.WithFields(logrus.Fields{ - "bucket": bucket.BucketName, - }).Tracef("setting lifecycle configuration for bucket %s", bucket.BucketName) + c.Logger.Tracef("setting lifecycle configuration for bucket %s", bucket.BucketName) return c.client.SetBucketLifecycle(ctx, bucket.BucketName, &bucket.LifecycleConfig) } diff --git a/storage/minio/stat_object.go b/storage/minio/stat_object.go index 84c8cb8e2..a2bcc3eec 100644 --- a/storage/minio/stat_object.go +++ b/storage/minio/stat_object.go @@ -5,15 +5,11 @@ import ( "fmt" "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" - "github.com/sirupsen/logrus" ) // StatObject retrieves the metadata of an object from the MinIO storage. func (c *MinioClient) StatObject(ctx context.Context, object *types.Object) (*types.Object, error) { - c.Logger.WithFields(logrus.Fields{ - "bucket": object.BucketName, - "object": object.ObjectName, - }).Tracef("retrieving metadata for object %s from bucket %s", object.ObjectName, object.BucketName) + c.Logger.Tracef("retrieving metadata for object %s from bucket %s", object.ObjectName, object.BucketName) // Get object info info, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) diff --git a/storage/minio/test_data/create_bucket.json b/storage/minio/test_data/create_bucket.json new file mode 100644 index 000000000..b1e77da2f --- /dev/null +++ b/storage/minio/test_data/create_bucket.json @@ -0,0 +1,3 @@ +{ + "bucket_name": "foo" +} \ No newline at end of file diff --git a/storage/minio/upload.go b/storage/minio/upload.go index 7c65fc7ae..ff0c9138a 100644 --- a/storage/minio/upload.go +++ b/storage/minio/upload.go @@ -4,15 +4,11 @@ import ( "context" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" - "github.com/sirupsen/logrus" ) // Upload uploads an object to a bucket in MinIO.ts func (c *MinioClient) Upload(ctx context.Context, object *api.Object) error { - c.Logger.WithFields(logrus.Fields{ - "bucket": object.BucketName, - "object": object.ObjectName, - }).Tracef("uploading data to bucket %s", object.BucketName) + c.Logger.Tracef("uploading data to bucket %s", object.BucketName) _, err := c.client.FPutObject(ctx, object.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) return err } From 41df509e4cde01ca464af5534b1463eb799062eb Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Wed, 8 Jan 2025 11:48:17 -0600 Subject: [PATCH 12/41] working tests --- api/types/storage.go | 2 +- storage/minio/bucket_exists_test.go | 110 +++++++++----------------- storage/minio/delete.go | 4 +- storage/minio/delete_bucket_test.go | 86 ++++++++++++++++++++ storage/minio/download.go | 8 +- storage/minio/presigned_get_object.go | 12 +-- storage/minio/stat_object.go | 6 +- storage/minio/upload.go | 4 +- storage/minio/upload_test.go | 102 ++++++++++++++++++++++++ 9 files changed, 245 insertions(+), 89 deletions(-) create mode 100644 storage/minio/delete_bucket_test.go create mode 100644 storage/minio/upload_test.go diff --git a/api/types/storage.go b/api/types/storage.go index 008388466..70bc7ee9a 100644 --- a/api/types/storage.go +++ b/api/types/storage.go @@ -18,6 +18,6 @@ type BucketOptions struct { type Object struct { ObjectName string `json:"object_name,omitempty"` - BucketName string `json:"bucket_name,omitempty"` + Bucket Bucket `json:"bucket,omitempty"` FilePath string `json:"file_path,omitempty"` } diff --git a/storage/minio/bucket_exists_test.go b/storage/minio/bucket_exists_test.go index b52768fa3..cb1b3e9a5 100644 --- a/storage/minio/bucket_exists_test.go +++ b/storage/minio/bucket_exists_test.go @@ -1,15 +1,3 @@ -//package minio -// -//import ( -// "context" -// "net/http" -// "net/http/httptest" -// "testing" -// -// api "github.com/go-vela/server/api/types" -//) -// - package minio import ( @@ -19,7 +7,6 @@ import ( "net/http" "net/http/httptest" "testing" - "time" ) func TestMinioClient_BucketExists(t *testing.T) { @@ -30,6 +17,12 @@ func TestMinioClient_BucketExists(t *testing.T) { _, engine := gin.CreateTestContext(resp) // setup mock server + // mock create bucket call + engine.PUT("/foo/", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + }) + // mock bucket exists call engine.HEAD("/foo/", func(c *gin.Context) { c.Status(http.StatusOK) }) @@ -37,13 +30,17 @@ func TestMinioClient_BucketExists(t *testing.T) { fake := httptest.NewServer(engine) defer fake.Close() ctx := context.TODO() - b := new(api.Bucket) - b.BucketName = "foo" client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + // create bucket + err := client.CreateBucket(ctx, &api.Bucket{BucketName: "foo"}) + if err != nil { + t.Errorf("CreateBucket returned err: %v", err) + } + // run test - exists, err := client.BucketExists(ctx, b) + exists, err := client.BucketExists(ctx, &api.Bucket{BucketName: "foo"}) if resp.Code != http.StatusOK { t.Errorf("BucketExists returned %v, want %v", resp.Code, http.StatusOK) } @@ -57,64 +54,35 @@ func TestMinioClient_BucketExists(t *testing.T) { } } -func TestMinioClient_BucketExists_Success(t *testing.T) { +func TestMinioClient_BucketExists_Failure(t *testing.T) { // setup context - //gin.SetMode(gin.TestMode) - // - //resp := httptest.NewRecorder() - //_, engine := gin.CreateTestContext(resp) - // - //// setup mock server - //engine.GET("/api/v3/orgs/:org", func(c *gin.Context) { - // c.Header("Content-Type", "application/json") - // c.Status(http.StatusOK) - // c.File("testdata/get_org.json") - //}) - // - //s := httptest.NewServer(engine) - //defer s.Close() - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Last-Modified", time.DateTime) - w.Header().Set("Content-Length", "5") - - // Write less bytes than the content length. - w.Write([]byte("12345")) - })) - defer srv.Close() - - //// New - instantiate minio client with options - //clnt, err := New(srv.Listener.Addr().String(), &Options{ - // Region: "us-east-1", - //}) - //if err != nil { - // t.Fatal(err) - //} - // setup types - u := new(api.Bucket) - u.BucketName = "foo" - - //want := "minio" - - client, err := New(srv.URL, WithAccessKey("accessKey"), WithSecretKey("secretKey"), WithSecure(false)) - if err != nil { - t.Fatal(err) - } + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.HEAD("/foo/", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + fake := httptest.NewServer(engine) + defer fake.Close() + ctx := context.TODO() + + client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + // run test - got, err := client.BucketExists(context.TODO(), u) - t.Logf("got: %v", got) - // We expect an error when reading back. - if got { - t.Errorf("BucketExists returned %v, want %v", got, false) - t.Errorf("BucketExists returned err: %v", err) + exists, err := client.BucketExists(ctx, &api.Bucket{BucketName: "bar"}) + if resp.Code != http.StatusOK { + t.Errorf("BucketExists returned %v, want %v", resp.Code, http.StatusOK) } - // - //if err != nil { - // t.Errorf("GetOrgName returned err: %v", err) - //} - // - //if !reflect.DeepEqual(got, want) { - // t.Errorf("GetOrgName is %v, want %v", got, want) - //} + if err != nil { + t.Errorf("BucketExists returned err: %v", err) + } + if exists { + t.Errorf("BucketExists returned %v, want %v", exists, false) + } } diff --git a/storage/minio/delete.go b/storage/minio/delete.go index 781b49c89..d6a62441f 100644 --- a/storage/minio/delete.go +++ b/storage/minio/delete.go @@ -8,9 +8,9 @@ import ( // Delete deletes an object in a bucket in MinIO. func (c *MinioClient) Delete(ctx context.Context, object *api.Object) error { - c.Logger.Tracef("deleting objectName: %s from bucketName: %s", object.ObjectName, object.BucketName) + c.Logger.Tracef("deleting objectName: %s from bucketName: %s", object.ObjectName, object.Bucket.BucketName) - err := c.client.RemoveObject(ctx, object.BucketName, object.ObjectName, minio.RemoveObjectOptions{}) + err := c.client.RemoveObject(ctx, object.Bucket.BucketName, object.ObjectName, minio.RemoveObjectOptions{}) if err != nil { return err } diff --git a/storage/minio/delete_bucket_test.go b/storage/minio/delete_bucket_test.go new file mode 100644 index 000000000..eb08ec591 --- /dev/null +++ b/storage/minio/delete_bucket_test.go @@ -0,0 +1,86 @@ +package minio + +import ( + "context" + "github.com/gin-gonic/gin" + api "github.com/go-vela/server/api/types" + "net/http" + "net/http/httptest" + "testing" +) + +func TestMinioClient_Bucket_Delete_Success(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + // mock create bucket call + engine.PUT("/foo/", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + }) + + // mock delete bucket call + engine.DELETE("/foo/", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + fake := httptest.NewServer(engine) + defer fake.Close() + ctx := context.TODO() + b := new(api.Bucket) + b.BucketName = "foo" + client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + + // create bucket + err := client.CreateBucket(ctx, b) + if err != nil { + t.Errorf("CreateBucket returned err: %v", err) + } + + // run test + err = client.DeleteBucket(ctx, b) + if resp.Code != http.StatusOK { + t.Errorf("DeleteBucket returned %v, want %v", resp.Code, http.StatusOK) + } + + // in Minio SDK, removeBucket returns status code 200 OK as error if a bucket is deleted successfully + if err != nil && err.Error() != "200 OK" { + t.Errorf("DeleteBucket returned err: %v", err) + } + +} + +func TestMinioClient_Bucket_Delete_BucketNotFound(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // mock delete bucket call + engine.DELETE("/foo/", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"message": "The specified bucket does not exist"}) + }) + + fake := httptest.NewServer(engine) + defer fake.Close() + ctx := context.TODO() + b := new(api.Bucket) + b.BucketName = "foo" + client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + + // run test + err := client.DeleteBucket(ctx, b) + if resp.Code != http.StatusOK { + t.Errorf("DeleteBucket returned %v, want %v", resp.Code, http.StatusOK) + } + + if err == nil { + t.Errorf("DeleteBucket expected error, got nil") + } + +} diff --git a/storage/minio/download.go b/storage/minio/download.go index ee71b68e4..db167ada5 100644 --- a/storage/minio/download.go +++ b/storage/minio/download.go @@ -32,25 +32,25 @@ func (c *MinioClient) Download(ctx context.Context, object *api.Object) error { // //c.Logger.Tracef("successfully downloaded object %s to %s", object.ObjectName, object.FilePath) //return nil - logrus.Debugf("getting object info on bucket %s from path: %s", object.BucketName, object.ObjectName) + logrus.Debugf("getting object info on bucket %s from path: %s", object.Bucket.BucketName, object.ObjectName) // set a timeout on the request to the cache provider //ctx, cancel := context.WithTimeout(context.Background(), r.Timeout) //defer cancel() // collect metadata on the object - objInfo, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) + objInfo, err := c.client.StatObject(ctx, object.Bucket.BucketName, object.ObjectName, minio.StatObjectOptions{}) if objInfo.Key == "" { logrus.Error(err) return nil } - logrus.Debugf("getting object in bucket %s from path: %s", object.BucketName, object.ObjectName) + logrus.Debugf("getting object in bucket %s from path: %s", object.Bucket.BucketName, object.ObjectName) logrus.Infof("%s to download", humanize.Bytes(uint64(objInfo.Size))) // retrieve the object in specified path of the bucket - err = c.client.FGetObject(ctx, object.BucketName, object.ObjectName, filename, minio.GetObjectOptions{}) + err = c.client.FGetObject(ctx, object.Bucket.BucketName, object.ObjectName, filename, minio.GetObjectOptions{}) if err != nil { return err } diff --git a/storage/minio/presigned_get_object.go b/storage/minio/presigned_get_object.go index 5ca04dbf2..f2add5ef8 100644 --- a/storage/minio/presigned_get_object.go +++ b/storage/minio/presigned_get_object.go @@ -11,23 +11,23 @@ import ( // TODO hide URL behind a different name // PresignedGetObject generates a presigned URL for downloading an object. func (c *MinioClient) PresignedGetObject(ctx context.Context, object *api.Object) (string, error) { - c.Logger.Tracef("generating presigned URL for object %s in bucket %s", object.ObjectName, object.BucketName) + c.Logger.Tracef("generating presigned URL for object %s in bucket %s", object.ObjectName, object.Bucket.BucketName) // collect metadata on the object - objInfo, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) + objInfo, err := c.client.StatObject(ctx, object.Bucket.BucketName, object.ObjectName, minio.StatObjectOptions{}) if objInfo.Key == "" { - logrus.Errorf("unable to get object info %s from bucket %s: %v", object.ObjectName, object.BucketName, err) + logrus.Errorf("unable to get object info %s from bucket %s: %v", object.ObjectName, object.Bucket.BucketName, err) return "", err } - _, err = c.client.BucketExists(ctx, object.BucketName) + _, err = c.client.BucketExists(ctx, object.Bucket.BucketName) if err != nil { - logrus.Errorf("unable to check if bucket %s exists: %v", object.BucketName, err) + logrus.Errorf("unable to check if bucket %s exists: %v", object.Bucket.BucketName, err) return "", err } // Generate presigned URL for downloading the object. // The URL is valid for 7 days. - presignedURL, err := c.client.PresignedGetObject(ctx, object.BucketName, object.ObjectName, 7*24*time.Hour, nil) + presignedURL, err := c.client.PresignedGetObject(ctx, object.Bucket.BucketName, object.ObjectName, 7*24*time.Hour, nil) if err != nil { return "", err } diff --git a/storage/minio/stat_object.go b/storage/minio/stat_object.go index a2bcc3eec..4e4d8d2a9 100644 --- a/storage/minio/stat_object.go +++ b/storage/minio/stat_object.go @@ -9,12 +9,12 @@ import ( // StatObject retrieves the metadata of an object from the MinIO storage. func (c *MinioClient) StatObject(ctx context.Context, object *types.Object) (*types.Object, error) { - c.Logger.Tracef("retrieving metadata for object %s from bucket %s", object.ObjectName, object.BucketName) + c.Logger.Tracef("retrieving metadata for object %s from bucket %s", object.ObjectName, object.Bucket.BucketName) // Get object info - info, err := c.client.StatObject(ctx, object.BucketName, object.ObjectName, minio.StatObjectOptions{}) + info, err := c.client.StatObject(ctx, object.Bucket.BucketName, object.ObjectName, minio.StatObjectOptions{}) if err != nil { - return nil, fmt.Errorf("unable to get object info %s from bucket %s: %v", object.ObjectName, object.BucketName, err) + return nil, fmt.Errorf("unable to get object info %s from bucket %s: %v", object.ObjectName, object.Bucket.BucketName, err) } // Map MinIO object info to API object diff --git a/storage/minio/upload.go b/storage/minio/upload.go index ff0c9138a..ca12ffc12 100644 --- a/storage/minio/upload.go +++ b/storage/minio/upload.go @@ -8,7 +8,7 @@ import ( // Upload uploads an object to a bucket in MinIO.ts func (c *MinioClient) Upload(ctx context.Context, object *api.Object) error { - c.Logger.Tracef("uploading data to bucket %s", object.BucketName) - _, err := c.client.FPutObject(ctx, object.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) + c.Logger.Tracef("uploading data to bucket %s", object.Bucket.BucketName) + _, err := c.client.FPutObject(ctx, object.Bucket.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) return err } diff --git a/storage/minio/upload_test.go b/storage/minio/upload_test.go new file mode 100644 index 000000000..fa9492e7e --- /dev/null +++ b/storage/minio/upload_test.go @@ -0,0 +1,102 @@ +package minio + +import ( + "context" + "github.com/gin-gonic/gin" + api "github.com/go-vela/server/api/types" + "net/http" + "net/http/httptest" + "os" + "testing" +) + +func TestMinioClient_Upload_Success(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + // mock create bucket call + engine.PUT("/foo/", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + }) + // mock bucket exists call + engine.PUT("/foo/test.xml", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("test_data/test.xml") + }) + fake := httptest.NewServer(engine) + defer fake.Close() + ctx := context.TODO() + obj := new(api.Object) + obj.Bucket.BucketName = "foo" + obj.ObjectName = "test.xml" + obj.FilePath = "test_data/test.xml" + client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + + // create bucket + err := client.CreateBucket(ctx, &api.Bucket{BucketName: "foo"}) + if err != nil { + t.Errorf("CreateBucket returned err: %v", err) + } + + // run test + err = client.Upload(ctx, obj) + if resp.Code != http.StatusOK { + t.Errorf("Upload returned %v, want %v", resp.Code, http.StatusOK) + } + + if err != nil { + t.Errorf("Upload returned err: %v", err) + } + +} + +func TestMinioClient_Upload_Failure(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + // mock create bucket call + engine.PUT("/foo/", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + }) + // mock bucket exists call + engine.PUT("/foo/test.xml", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("test_data/test.xml") + }) + fake := httptest.NewServer(engine) + defer fake.Close() + ctx := context.TODO() + obj := new(api.Object) + obj.Bucket.BucketName = "foo" + obj.ObjectName = "test.xml" + obj.FilePath = "nonexist/test.xml" + client, _ := NewTest(fake.URL, "miniokey", "miniosecret", false) + + // create bucket + err := client.CreateBucket(ctx, &api.Bucket{BucketName: "foo"}) + if err != nil { + t.Errorf("CreateBucket returned err: %v", err) + } + + // run test + err = client.Upload(ctx, obj) + if resp.Code != http.StatusOK { + t.Errorf("Upload returned %v, want %v", resp.Code, http.StatusOK) + } + + if !os.IsNotExist(err) { + t.Errorf("Upload returned err: %v", err) + } +} From 86d822df505962a6c15ceb44be8e0c0ed4f7ee8f Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Fri, 17 Jan 2025 12:05:20 -0600 Subject: [PATCH 13/41] keys for test report step --- api/admin/storage.go | 6 +- api/types/test_report.go | 62 +++++++++++++++++++++ compiler/native/validate_test.go | 35 ++++++++++++ compiler/types/pipeline/container.go | 4 +- compiler/types/pipeline/test_report.go | 54 ++++++++++++++++++ compiler/types/pipeline/test_report_test.go | 29 ++++++++++ compiler/types/yaml/secret.go | 2 +- compiler/types/yaml/step.go | 2 + compiler/types/yaml/step_test.go | 17 ++++++ compiler/types/yaml/test_report.go | 40 +++++++++++++ compiler/types/yaml/testdata/step.yml | 7 +++ docker-compose.yml | 6 +- storage/minio/minio.go | 2 +- storage/minio/minio_test.go | 2 +- storage/setup_test.go | 2 +- storage/storage_test.go | 4 +- 16 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 api/types/test_report.go create mode 100644 compiler/types/pipeline/test_report.go create mode 100644 compiler/types/pipeline/test_report_test.go create mode 100644 compiler/types/yaml/test_report.go diff --git a/api/admin/storage.go b/api/admin/storage.go index 487cf1896..3e74e0fdb 100644 --- a/api/admin/storage.go +++ b/api/admin/storage.go @@ -309,7 +309,7 @@ func UploadObject(c *gin.Context) { return } - if input.BucketName == "" || input.ObjectName == "" { + if input.Bucket.BucketName == "" || input.ObjectName == "" { retErr := fmt.Errorf("bucketName and objectName are required") util.HandleError(c, http.StatusBadRequest, retErr) return @@ -382,7 +382,7 @@ func DownloadObject(c *gin.Context) { return } - if input.BucketName == "" || input.ObjectName == "" { + if input.Bucket.BucketName == "" || input.ObjectName == "" { retErr := fmt.Errorf("bucketName and objectName are required") util.HandleError(c, http.StatusBadRequest, retErr) return @@ -453,7 +453,7 @@ func GetPresignedURL(c *gin.Context) { } input := &types.Object{ - BucketName: bucketName, + Bucket: types.Bucket{BucketName: bucketName}, ObjectName: objectName, } diff --git a/api/types/test_report.go b/api/types/test_report.go new file mode 100644 index 000000000..a6cb7c7da --- /dev/null +++ b/api/types/test_report.go @@ -0,0 +1,62 @@ +package types + +import "fmt" + +// TestReport is the API representation of a test report for a pipeline. +// +// swagger:model TestReport +type TestReport struct { + Results *string `json:"results,omitempty"` + Attachments *string `json:"attachments,omitempty"` +} + +// GetResults returns the Results field. +// +// When the provided TestReport type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestReport) GetResults() string { + // return zero value if TestReport type or Results field is nil + if t == nil || t.Results == nil { + return "" + } + + return *t.Results +} + +// GetAttachments returns the Attachments field. +// +// When the provided TestReport type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (t *TestReport) GetAttachments() string { + // return zero value if TestReport type or Attachments field is nil + if t == nil || t.Attachments == nil { + return "" + } + + return *t.Attachments +} + +// SetResults sets the Results field. +func (t *TestReport) SetResults(v string) { + // return if TestReport type is nil + if t == nil { + return + } + // set the Results field + t.Results = &v +} + +// SetAttachments sets the Attachments field. +func (t *TestReport) SetAttachments(v string) { + // return if TestReport type is nil + if t == nil { + return + } + // set the Attachments field + t.Attachments = &v +} + +// String implements the Stringer interface for the TestReport type. +func (t *TestReport) String() string { + return fmt.Sprintf("Results: %s, Attachments: %s", t.GetResults(), t.GetAttachments()) +} diff --git a/compiler/native/validate_test.go b/compiler/native/validate_test.go index 4d3876f0a..fce45c216 100644 --- a/compiler/native/validate_test.go +++ b/compiler/native/validate_test.go @@ -624,3 +624,38 @@ func TestNative_Validate_MultiReportAs(t *testing.T) { t.Errorf("Validate should have returned err") } } + +func TestNative_Validate_TestReport(t *testing.T) { + // setup types + set := flag.NewFlagSet("test", 0) + set.String("clone-image", defaultCloneImage, "doc") + c := cli.NewContext(nil, set, nil) + + str := "foo" + p := &yaml.Build{ + Version: "v1", + Steps: yaml.StepSlice{ + &yaml.Step{ + Commands: raw.StringSlice{"echo hello"}, + Image: "alpine", + Name: str, + Pull: "always", + TestReport: yaml.TestReport{ + Results: []string{"results.xml"}, + Attachments: []string{"attachments"}, + }, + }, + }, + } + + // run test + compiler, err := FromCLIContext(c) + if err != nil { + t.Errorf("Unable to create new compiler: %v", err) + } + + err = compiler.Validate(p) + if err != nil { + t.Errorf("Validate returned err: %v", err) + } +} diff --git a/compiler/types/pipeline/container.go b/compiler/types/pipeline/container.go index 5cfb2acf9..0c06fb691 100644 --- a/compiler/types/pipeline/container.go +++ b/compiler/types/pipeline/container.go @@ -48,6 +48,7 @@ type ( Pull string `json:"pull,omitempty" yaml:"pull,omitempty"` Ruleset Ruleset `json:"ruleset,omitempty" yaml:"ruleset,omitempty"` Secrets StepSecretSlice `json:"secrets,omitempty" yaml:"secrets,omitempty"` + TestReport TestReport `json:"test_report,omitempty" yaml:"test_report,omitempty"` Ulimits UlimitSlice `json:"ulimits,omitempty" yaml:"ulimits,omitempty"` Volumes VolumeSlice `json:"volumes,omitempty" yaml:"volumes,omitempty"` User string `json:"user,omitempty" yaml:"user,omitempty"` @@ -137,7 +138,8 @@ func (c *Container) Empty() bool { len(c.Volumes) == 0 && len(c.User) == 0 && len(c.ReportAs) == 0 && - len(c.IDRequest) == 0 { + len(c.IDRequest) == 0 && + reflect.DeepEqual(c.TestReport, TestReport{}) { return true } diff --git a/compiler/types/pipeline/test_report.go b/compiler/types/pipeline/test_report.go new file mode 100644 index 000000000..bd4f81fd4 --- /dev/null +++ b/compiler/types/pipeline/test_report.go @@ -0,0 +1,54 @@ +package pipeline + +// TestReport represents the structure for test report configuration. +type ( + // TestReportSlice is the pipleine representation + //of a slice of TestReport. + // + // swagger:model PipelineTestReportSlice + TestReportSlice []*TestReport + + // TestReport is the pipeline representation + // of a test report for a pipeline. + // + // swagger:model PipelineTestReport + TestReport struct { + Results []string `yaml:"results,omitempty" json:"results,omitempty"` + Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` + } +) + +// Purge removes the test report configuration from the pipeline +// if it does not match the provided ruledata. If both results +// and attachments are provided, then an empty test report is returned. +//func (t *TestReport) Purge(r *RuleData) (*TestReport, error) { +// // return an empty test report if both results and attachments are provided +// if len(t.Results) > 0 && len(t.Attachments) > 0 { +// return nil, fmt.Errorf("cannot have both results and attachments in the test report") +// } +// +// // purge results if provided +// if len(t.Results) > 0 { +// t.Results = "" +// } +// +// // purge attachments if provided +// if len(t.Attachments) > 0 { +// t.Attachments = "" +// } +// +// // return the purged test report +// return t, nil +//} + +// Empty returns true if the provided test report is empty. +func (t *TestReport) Empty() bool { + // return true if every test report field is empty + if len(t.Results) == 0 && + len(t.Attachments) == 0 { + return true + } + + // return false if any of the test report fields are provided + return false +} diff --git a/compiler/types/pipeline/test_report_test.go b/compiler/types/pipeline/test_report_test.go new file mode 100644 index 000000000..0e3db067c --- /dev/null +++ b/compiler/types/pipeline/test_report_test.go @@ -0,0 +1,29 @@ +package pipeline + +import "testing" + +func TestPipeline_TestReport_Empty(t *testing.T) { + // setup tests + tests := []struct { + report *TestReport + want bool + }{ + { + report: &TestReport{Results: []string{"foo"}}, + want: false, + }, + { + report: new(TestReport), + want: true, + }, + } + + // run tests + for _, test := range tests { + got := test.report.Empty() + + if got != test.want { + t.Errorf("Empty is %v, want %t", got, test.want) + } + } +} diff --git a/compiler/types/yaml/secret.go b/compiler/types/yaml/secret.go index d0779e5c6..0a6c0dbf9 100644 --- a/compiler/types/yaml/secret.go +++ b/compiler/types/yaml/secret.go @@ -155,7 +155,7 @@ func (o *Origin) Empty() bool { // MergeEnv takes a list of environment variables and attempts // to set them in the secret environment. If the environment -// variable already exists in the secret, than this will +// variable already exists in the secret, then this will // overwrite the existing environment variable. func (o *Origin) MergeEnv(environment map[string]string) error { // check if the secret container is empty diff --git a/compiler/types/yaml/step.go b/compiler/types/yaml/step.go index 405e6d598..45e8bb800 100644 --- a/compiler/types/yaml/step.go +++ b/compiler/types/yaml/step.go @@ -24,6 +24,7 @@ type ( Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Command to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-entrypoint-key"` Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Sensitive variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"oneof_required=template,description=Name of template to expand in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-key"` + TestReport TestReport `yaml:"test_report,omitempty" json:"test_report,omitempty" jsonschema:"description=Test report configuration for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-test_report-key"` Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` Volumes VolumeSlice `yaml:"volumes,omitempty" json:"volumes,omitempty" jsonschema:"description=Mount volumes for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"oneof_required=image,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-key"` @@ -64,6 +65,7 @@ func (s *StepSlice) ToPipeline() *pipeline.ContainerSlice { User: step.User, ReportAs: step.ReportAs, IDRequest: step.IDRequest, + TestReport: *step.TestReport.ToPipeline(), }) } diff --git a/compiler/types/yaml/step_test.go b/compiler/types/yaml/step_test.go index 2f5336be4..8501c66a1 100644 --- a/compiler/types/yaml/step_test.go +++ b/compiler/types/yaml/step_test.go @@ -76,6 +76,10 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { AccessMode: "ro", }, }, + TestReport: TestReport{ + Results: []string{"test.txt"}, + Attachments: []string{"test.log"}, + }, }, }, want: &pipeline.ContainerSlice{ @@ -134,6 +138,10 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { AccessMode: "ro", }, }, + TestReport: pipeline.TestReport{ + Results: []string{"test.txt"}, + Attachments: []string{"test.log"}, + }, }, }, }, @@ -213,6 +221,15 @@ func TestYaml_StepSlice_UnmarshalYAML(t *testing.T) { }, }, }, + { + Name: "test-reports", + Pull: "always", + Image: "golang:1.20", + TestReport: TestReport{ + Results: []string{"test-results/*.xml"}, + Attachments: []string{"screenshots/**/*.png", " video/*.mp4"}, + }, + }, }, }, { diff --git a/compiler/types/yaml/test_report.go b/compiler/types/yaml/test_report.go new file mode 100644 index 000000000..038db367c --- /dev/null +++ b/compiler/types/yaml/test_report.go @@ -0,0 +1,40 @@ +package yaml + +import "github.com/go-vela/server/compiler/types/pipeline" + +// TestReport represents the structure for test report configuration. +type TestReport struct { + Results []string `yaml:"results,omitempty" json:"results,omitempty"` + Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` +} + +// ToPipeline converts the TestReport type +// to a pipeline TestReport type. +func (t *TestReport) ToPipeline() *pipeline.TestReport { + return &pipeline.TestReport{ + Results: t.Results, + Attachments: t.Attachments, + } +} + +// UnmarshalYAML implements the Unmarshaler interface for the TestReport type. +func (t *TestReport) UnmarshalYAML(unmarshal func(interface{}) error) error { + // test report we try unmarshalling to + testReport := new(struct { + Results []string `yaml:"results,omitempty" json:"results,omitempty"` + Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` + }) + + // attempt to unmarshal test report type + err := unmarshal(testReport) + if err != nil { + return err + } + + // set the results field + t.Results = testReport.Results + // set the attachments field + t.Attachments = testReport.Attachments + + return nil +} diff --git a/compiler/types/yaml/testdata/step.yml b/compiler/types/yaml/testdata/step.yml index 1d6d9cc93..c0091f398 100644 --- a/compiler/types/yaml/testdata/step.yml +++ b/compiler/types/yaml/testdata/step.yml @@ -44,3 +44,10 @@ registry: index.docker.io repo: github/octocat tags: [ latest, dev ] + +- name: test-reports + image: golang:1.20 + pull: true + test_report: + results: ["test-results/*.xml"] + attachments: ["screenshots/**/*.png", " video/*.mp4"] diff --git a/docker-compose.yml b/docker-compose.yml index 9ed814c7e..8a869433a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -74,8 +74,12 @@ services: # # https://go-vela.github.io/docs/administration/worker/ worker: +# container_name: worker +# image: target/vela-worker:latest + build: + context: ../. + dockerfile: Dockerfile container_name: worker - image: target/vela-worker:latest networks: - vela environment: diff --git a/storage/minio/minio.go b/storage/minio/minio.go index 5a5c883a9..49c3f2164 100644 --- a/storage/minio/minio.go +++ b/storage/minio/minio.go @@ -33,7 +33,7 @@ func New(endpoint string, opts ...ClientOpt) (*MinioClient, error) { c := new(MinioClient) // default to secure connection - urlEndpoint := "s3.amazonaws.com" + var urlEndpoint string useSSL := true // create new fields diff --git a/storage/minio/minio_test.go b/storage/minio/minio_test.go index d4846858a..201410e20 100644 --- a/storage/minio/minio_test.go +++ b/storage/minio/minio_test.go @@ -5,7 +5,7 @@ import ( ) var ( - endpoint = "localhost:9000" + endpoint = "http://localhost:9000" _accessKey = "minio_access_user" _secretKey = "minio_secret_key" _useSSL = false diff --git a/storage/setup_test.go b/storage/setup_test.go index 8e9eeb269..21ea256c8 100644 --- a/storage/setup_test.go +++ b/storage/setup_test.go @@ -10,7 +10,7 @@ func TestSetup_Minio(t *testing.T) { setup := &Setup{ Enable: true, Driver: "minio", - Endpoint: "minio.example.com", + Endpoint: "http://minio.example.com", AccessKey: "access-key", SecretKey: "secret-key", Bucket: "bucket-name", diff --git a/storage/storage_test.go b/storage/storage_test.go index e481cac6e..a5ad55254 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -15,7 +15,7 @@ func TestStorage_New(t *testing.T) { setup: &Setup{ Driver: constants.DriverMinio, Enable: true, - Endpoint: "minio.example.com", + Endpoint: "http://minio.example.com", AccessKey: "access-key", SecretKey: "secret-key", Bucket: "bucket-name", @@ -27,7 +27,7 @@ func TestStorage_New(t *testing.T) { setup: &Setup{ Driver: "invalid-driver", Enable: false, - Endpoint: "invalid.example.com", + Endpoint: "http://invalid.example.com", AccessKey: "access-key", SecretKey: "secret-key", Bucket: "bucket-name", From 83afd1507d92872ef97dd6fe656bd85824de2aa8 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:34:56 -0600 Subject: [PATCH 14/41] feat: deployment config for expected params and targets (#1214) --- api/build/compile_publish.go | 24 ++- api/deployment/get.go | 2 +- api/deployment/get_config.go | 128 ++++++++++++ api/webhook/post.go | 15 +- compiler/native/compile.go | 12 ++ compiler/native/compile_test.go | 2 +- compiler/native/expand.go | 51 +++++ compiler/native/expand_test.go | 105 ++++++++++ compiler/native/testdata/deploy_template.yml | 25 +++ compiler/native/transform.go | 13 +- compiler/template/native/render.go | 2 +- compiler/types/pipeline/build.go | 1 + compiler/types/pipeline/deployment.go | 117 +++++++++++ compiler/types/pipeline/deployment_test.go | 188 ++++++++++++++++++ compiler/types/yaml/build.go | 3 + compiler/types/yaml/build_test.go | 51 ++++- compiler/types/yaml/deployment.go | 68 +++++++ compiler/types/yaml/deployment_test.go | 77 +++++++ .../testdata/build_with_deploy_config.yml | 36 ++++ .../types/yaml/testdata/deploy_parameter.yml | 11 + router/deployment.go | 4 +- scm/github/webhook.go | 2 + scm/github/webhook_test.go | 1 + 23 files changed, 912 insertions(+), 26 deletions(-) create mode 100644 api/deployment/get_config.go create mode 100644 compiler/native/testdata/deploy_template.yml create mode 100644 compiler/types/pipeline/deployment.go create mode 100644 compiler/types/pipeline/deployment_test.go create mode 100644 compiler/types/yaml/deployment.go create mode 100644 compiler/types/yaml/deployment_test.go create mode 100644 compiler/types/yaml/testdata/build_with_deploy_config.yml create mode 100644 compiler/types/yaml/testdata/deploy_parameter.yml diff --git a/api/build/compile_publish.go b/api/build/compile_publish.go index cc99e8579..d355488d0 100644 --- a/api/build/compile_publish.go +++ b/api/build/compile_publish.go @@ -26,13 +26,14 @@ import ( // CompileAndPublishConfig is a struct that contains information for the CompileAndPublish function. type CompileAndPublishConfig struct { - Build *types.Build - Metadata *internal.Metadata - BaseErr string - Source string - Comment string - Labels []string - Retries int + Build *types.Build + Deployment *types.Deployment + Metadata *internal.Metadata + BaseErr string + Source string + Comment string + Labels []string + Retries int } // CompileAndPublish is a helper function to generate the queue items for a build. It takes a form @@ -307,6 +308,15 @@ func CompileAndPublish( errors.New(skip) } + // validate deployment config + if (b.GetEvent() == constants.EventDeploy) && cfg.Deployment != nil { + if err := p.Deployment.Validate(cfg.Deployment.GetTarget(), cfg.Deployment.GetPayload()); err != nil { + retErr := fmt.Errorf("%s: failed to validate deployment for %s: %w", baseErr, repo.GetFullName(), err) + + return nil, nil, http.StatusBadRequest, retErr + } + } + // check if the pipeline did not already exist in the database if pipeline == nil { pipeline = compiled diff --git a/api/deployment/get.go b/api/deployment/get.go index 5c978cf64..966456027 100644 --- a/api/deployment/get.go +++ b/api/deployment/get.go @@ -87,7 +87,7 @@ func GetDeployment(c *gin.Context) { } // send API call to database to capture the deployment - d, err := database.FromContext(c).GetDeployment(ctx, int64(number)) + d, err := database.FromContext(c).GetDeploymentForRepo(ctx, r, int64(number)) if err != nil { // send API call to SCM to capture the deployment d, err = scm.FromContext(c).GetDeployment(ctx, u, r, int64(number)) diff --git a/api/deployment/get_config.go b/api/deployment/get_config.go new file mode 100644 index 000000000..bf8323e33 --- /dev/null +++ b/api/deployment/get_config.go @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "errors" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "gorm.io/gorm" + + "github.com/go-vela/server/compiler" + "github.com/go-vela/server/database" + "github.com/go-vela/server/router/middleware/repo" + "github.com/go-vela/server/router/middleware/user" + "github.com/go-vela/server/scm" + "github.com/go-vela/server/util" +) + +// swagger:operation GET /api/v1/deployments/{org}/{repo}/config deployments GetDeploymentConfig +// +// Get a deployment config +// +// --- +// produces: +// - application/json +// parameters: +// - in: path +// name: org +// description: Name of the organization +// required: true +// type: string +// - in: path +// name: repo +// description: Name of the repository +// required: true +// type: string +// - in: query +// name: ref +// description: Ref to target for the deployment config +// type: string +// security: +// - ApiKeyAuth: [] +// responses: +// '200': +// description: Successfully retrieved the deployment config +// schema: +// "$ref": "#/definitions/Deployment" +// '400': +// description: Invalid request payload or path +// schema: +// "$ref": "#/definitions/Error" +// '401': +// description: Unauthorized +// schema: +// "$ref": "#/definitions/Error" +// '404': +// description: Not found +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" + +// GetDeploymentConfig represents the API handler to get a deployment config at a given ref. +func GetDeploymentConfig(c *gin.Context) { + // capture middleware values + l := c.MustGet("logger").(*logrus.Entry) + r := repo.Retrieve(c) + u := user.Retrieve(c) + + ctx := c.Request.Context() + + // capture ref from parameters - use default branch if not provided + ref := util.QueryParameter(c, "ref", r.GetBranch()) + + entry := fmt.Sprintf("%s@%s", r.GetFullName(), ref) + + l.Debugf("reading deployment config %s", entry) + + var config []byte + + // check if the pipeline exists in the database + p, err := database.FromContext(c).GetPipelineForRepo(ctx, ref, r) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + l.Debugf("pipeline %s not found in database, fetching from scm", entry) + + config, err = scm.FromContext(c).ConfigBackoff(ctx, u, r, ref) + if err != nil { + retErr := fmt.Errorf("unable to get pipeline configuration for %s: %w", entry, err) + + util.HandleError(c, http.StatusNotFound, retErr) + + return + } + } else { + // some other error + retErr := fmt.Errorf("unable to get pipeline for %s: %w", entry, err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + } else { + l.Debugf("pipeline %s found in database", entry) + + config = p.GetData() + } + + // set up compiler + compiler := compiler.FromContext(c).Duplicate().WithCommit(ref).WithRepo(r).WithUser(u) + + // compile the pipeline + pipeline, _, err := compiler.CompileLite(ctx, config, nil, true) + if err != nil { + retErr := fmt.Errorf("unable to compile pipeline %s: %w", entry, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + c.JSON(http.StatusOK, pipeline.Deployment) +} diff --git a/api/webhook/post.go b/api/webhook/post.go index 6cbc17a72..52f0d90dd 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -378,13 +378,14 @@ func PostWebhook(c *gin.Context) { // construct CompileAndPublishConfig config := build.CompileAndPublishConfig{ - Build: b, - Metadata: m, - BaseErr: baseErr, - Source: "webhook", - Comment: prComment, - Labels: prLabels, - Retries: 3, + Build: b, + Deployment: webhook.Deployment, + Metadata: m, + BaseErr: baseErr, + Source: "webhook", + Comment: prComment, + Labels: prLabels, + Retries: 3, } // generate the queue item diff --git a/compiler/native/compile.go b/compiler/native/compile.go index 9e7c638df..313e24db3 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -131,6 +131,12 @@ func (c *client) CompileLite(ctx context.Context, v interface{}, ruleData *pipel // create map of templates for easy lookup templates := mapFromTemplates(p.Templates) + // expand deployment config + p, err = c.ExpandDeployment(ctx, p, templates) + if err != nil { + return nil, _pipeline, err + } + switch { case len(p.Stages) > 0: // inject the templates into the steps @@ -330,6 +336,12 @@ func (c *client) compileSteps(ctx context.Context, p *yaml.Build, _pipeline *api return nil, _pipeline, err } + // inject the template for deploy config if exists + p, err = c.ExpandDeployment(ctx, p, tmpls) + if err != nil { + return nil, _pipeline, err + } + // inject the templates into the steps p, err = c.ExpandSteps(ctx, p, tmpls, r, c.GetTemplateDepth()) if err != nil { diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index e98b13e2b..4b0214d6f 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -637,7 +637,7 @@ func TestNative_Compile_StepsPipeline(t *testing.T) { t.Errorf("Compile returned err: %v", err) } - if diff := cmp.Diff(got, want); diff != "" { + if diff := cmp.Diff(want, got); diff != "" { t.Errorf("Compile mismatch (-want +got):\n%s", diff) } } diff --git a/compiler/native/expand.go b/compiler/native/expand.go index aae868693..a19abcd98 100644 --- a/compiler/native/expand.go +++ b/compiler/native/expand.go @@ -220,6 +220,43 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin return s, nil } +// ExpandDeployment injects the template for a +// templated deployment config in a yaml configuration. +func (c *client) ExpandDeployment(ctx context.Context, b *yaml.Build, tmpls map[string]*yaml.Template) (*yaml.Build, error) { + if len(tmpls) == 0 { + return b, nil + } + + if len(b.Deployment.Template.Name) == 0 { + return b, nil + } + + // lookup step template name + tmpl, ok := tmpls[b.Deployment.Template.Name] + if !ok { + return b, fmt.Errorf("missing template source for template %s in pipeline for deployment config", b.Deployment.Template.Name) + } + + bytes, err := c.getTemplate(ctx, tmpl, b.Deployment.Template.Name) + if err != nil { + return b, err + } + + // initialize variable map if not parsed from config + if len(b.Deployment.Template.Variables) == 0 { + b.Deployment.Template.Variables = make(map[string]interface{}) + } + + tmplBuild, err := c.mergeDeployTemplate(bytes, tmpl, &b.Deployment) + if err != nil { + return b, err + } + + b.Deployment = tmplBuild.Deployment + + return b, nil +} + func (c *client) getTemplate(ctx context.Context, tmpl *yaml.Template, name string) ([]byte, error) { var ( bytes []byte @@ -368,6 +405,20 @@ func (c *client) mergeTemplate(bytes []byte, tmpl *yaml.Template, step *yaml.Ste } } +func (c *client) mergeDeployTemplate(bytes []byte, tmpl *yaml.Template, d *yaml.Deployment) (*yaml.Build, error) { + switch tmpl.Format { + case constants.PipelineTypeGo, "golang", "": + //nolint:lll // ignore long line length due to return + return native.Render(string(bytes), "", d.Template.Name, make(raw.StringSliceMap), d.Template.Variables) + case constants.PipelineTypeStarlark: + //nolint:lll // ignore long line length due to return + return starlark.Render(string(bytes), "", d.Template.Name, make(raw.StringSliceMap), d.Template.Variables, c.GetStarlarkExecLimit()) + default: + //nolint:lll // ignore long line length due to return + return &yaml.Build{}, fmt.Errorf("format of %s is unsupported", tmpl.Format) + } +} + // helper function that creates a map of templates from a yaml configuration. func mapFromTemplates(templates []*yaml.Template) map[string]*yaml.Template { m := make(map[string]*yaml.Template) diff --git a/compiler/native/expand_test.go b/compiler/native/expand_test.go index 8c56d77f8..412877966 100644 --- a/compiler/native/expand_test.go +++ b/compiler/native/expand_test.go @@ -391,6 +391,111 @@ func TestNative_ExpandSteps(t *testing.T) { } } +func TestNative_ExpandDeployment(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.GET("/api/v3/repos/:org/:repo/contents/:path", func(c *gin.Context) { + body, err := convertFileToGithubResponse(c.Param("path")) + if err != nil { + t.Error(err) + } + c.JSON(http.StatusOK, body) + }) + + s := httptest.NewServer(engine) + defer s.Close() + + // setup types + set := flag.NewFlagSet("test", 0) + set.Bool("github-driver", true, "doc") + set.String("github-url", s.URL, "doc") + set.String("github-token", "", "doc") + set.Int("max-template-depth", 5, "doc") + set.String("clone-image", defaultCloneImage, "doc") + c := cli.NewContext(nil, set, nil) + + testRepo := new(api.Repo) + + testRepo.SetID(1) + testRepo.SetOrg("foo") + testRepo.SetName("bar") + + tests := []struct { + name string + tmpls map[string]*yaml.Template + }{ + { + name: "GitHub", + tmpls: map[string]*yaml.Template{ + "deploy": { + Name: "deploy", + Source: "github.example.com/foo/bar/deploy_template.yml", + Type: "github", + }, + }, + }, + } + + deployCfg := yaml.Deployment{ + Template: yaml.StepTemplate{ + Name: "deploy", + Variables: map[string]interface{}{ + "regions": []string{"us-east-1", "us-west-1"}, + }, + }, + } + + wantDeployCfg := yaml.Deployment{ + Targets: []string{"dev", "prod", "stage"}, + Parameters: yaml.ParameterMap{ + "region": { + Description: "cluster region to deploy", + Type: "string", + Required: true, + Options: []string{"us-east-1", "us-west-1"}, + }, + "cluster_count": { + Description: "number of clusters to deploy to", + Type: "integer", + Required: false, + Min: 1, + Max: 10, + }, + }, + } + + // run test + compiler, err := FromCLIContext(c) + if err != nil { + t.Errorf("Creating new compiler returned err: %v", err) + } + + compiler.WithCommit("123abc456def").WithRepo(testRepo) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + build, err := compiler.ExpandDeployment( + context.Background(), + &yaml.Build{ + Deployment: deployCfg, + }, + test.tmpls) + if err != nil { + t.Errorf("ExpandDeployment_Type%s returned err: %v", test.name, err) + } + + if diff := cmp.Diff(wantDeployCfg, build.Deployment); diff != "" { + t.Errorf("ExpandDeployment()_Type%s mismatch (-want +got):\n%s", test.name, diff) + } + }) + } +} + func TestNative_ExpandStepsMulti(t *testing.T) { // setup context gin.SetMode(gin.TestMode) diff --git a/compiler/native/testdata/deploy_template.yml b/compiler/native/testdata/deploy_template.yml new file mode 100644 index 000000000..084c61482 --- /dev/null +++ b/compiler/native/testdata/deploy_template.yml @@ -0,0 +1,25 @@ +metadata: + template: true + +deployment: + targets: + - dev + - prod + - stage + + parameters: + region: + description: cluster region to deploy + required: true + type: string + options: + {{- range .regions }} + - {{ . }} + {{- end }} + + cluster_count: + description: number of clusters to deploy to + required: false + type: integer + min: 1 + max: 10 \ No newline at end of file diff --git a/compiler/native/transform.go b/compiler/native/transform.go index 2f54c41a3..ebdb6777e 100644 --- a/compiler/native/transform.go +++ b/compiler/native/transform.go @@ -151,12 +151,13 @@ func (c *client) TransformSteps(r *pipeline.RuleData, p *yaml.Build) (*pipeline. // create new executable pipeline pipeline := &pipeline.Build{ - Version: p.Version, - Metadata: *p.Metadata.ToPipeline(), - Steps: *p.Steps.ToPipeline(), - Secrets: *p.Secrets.ToPipeline(), - Services: *p.Services.ToPipeline(), - Worker: *p.Worker.ToPipeline(), + Version: p.Version, + Metadata: *p.Metadata.ToPipeline(), + Deployment: *p.Deployment.ToPipeline(), + Steps: *p.Steps.ToPipeline(), + Secrets: *p.Secrets.ToPipeline(), + Services: *p.Services.ToPipeline(), + Worker: *p.Worker.ToPipeline(), } // set the unique ID for the executable pipeline diff --git a/compiler/template/native/render.go b/compiler/template/native/render.go index 528188e0e..afc3bea51 100644 --- a/compiler/template/native/render.go +++ b/compiler/template/native/render.go @@ -57,7 +57,7 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM config.Steps[index].Name = fmt.Sprintf("%s_%s", name, newStep.Name) } - return &types.Build{Metadata: config.Metadata, Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment, Templates: config.Templates}, nil + return &types.Build{Metadata: config.Metadata, Deployment: config.Deployment, Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment, Templates: config.Templates}, nil } // RenderBuild renders the templated build. diff --git a/compiler/types/pipeline/build.go b/compiler/types/pipeline/build.go index 195a353b5..bb47c4497 100644 --- a/compiler/types/pipeline/build.go +++ b/compiler/types/pipeline/build.go @@ -20,6 +20,7 @@ type Build struct { Metadata Metadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` Environment raw.StringSliceMap `json:"environment,omitempty" yaml:"environment,omitempty"` Worker Worker `json:"worker,omitempty" yaml:"worker,omitempty"` + Deployment Deployment `json:"deployment,omitempty" yaml:"deployment,omitempty"` Secrets SecretSlice `json:"secrets,omitempty" yaml:"secrets,omitempty"` Services ContainerSlice `json:"services,omitempty" yaml:"services,omitempty"` Stages StageSlice `json:"stages,omitempty" yaml:"stages,omitempty"` diff --git a/compiler/types/pipeline/deployment.go b/compiler/types/pipeline/deployment.go new file mode 100644 index 000000000..475946dea --- /dev/null +++ b/compiler/types/pipeline/deployment.go @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 + +package pipeline + +import ( + "fmt" + "slices" + "strconv" +) + +type ( + // Deployment is the pipeline representation of the deployment block for a pipeline. + // + // swagger:model PipelineDeployment + Deployment struct { + Targets []string `json:"targets,omitempty" yaml:"targets,omitempty"` + Parameters ParameterMap `json:"parameters,omitempty" yaml:"parameters,omitempty"` + } + + ParameterMap map[string]*Parameter + + // Parameters is the pipeline representation of the deploy parameters + // from a deployment block in a pipeline. + // + // swagger:model PipelineParameters + Parameter struct { + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Required bool `json:"required,omitempty" yaml:"required,omitempty"` + Options []string `json:"options,omitempty" yaml:"options,omitempty"` + Min int `json:"min,omitempty" yaml:"min,omitempty"` + Max int `json:"max,omitempty" yaml:"max,omitempty"` + } +) + +// Empty returns true if the provided deployment is empty. +func (d *Deployment) Empty() bool { + // return true if deployment is nil + if d == nil { + return true + } + + // return true if every deployment field is empty + if len(d.Targets) == 0 && + len(d.Parameters) == 0 { + return true + } + + // return false if any of the deployment fields are provided + return false +} + +// Validate checks the build ruledata and parameters against the deployment configuration. +func (d *Deployment) Validate(target string, inputParams map[string]string) error { + if d.Empty() { + return nil + } + + // validate targets + if len(d.Targets) > 0 && !slices.Contains(d.Targets, target) { + return fmt.Errorf("deployment target %s not found in deployment config targets", target) + } + + // validate params + for kConfig, vConfig := range d.Parameters { + var ( + inputStr string + ok bool + ) + // check if the parameter is required + if vConfig.Required { + // check if the parameter is provided + if inputStr, ok = inputParams[kConfig]; !ok { + return fmt.Errorf("deployment parameter %s is required", kConfig) + } + } else { + // check if the parameter is provided + if inputStr, ok = inputParams[kConfig]; !ok { + continue + } + } + + // check if the parameter is an option + if len(vConfig.Options) > 0 && len(inputStr) > 0 { + if !slices.Contains(vConfig.Options, inputStr) { + return fmt.Errorf("deployment parameter %s is not a valid option", kConfig) + } + } + + // check if the parameter is the correct type + if len(vConfig.Type) > 0 && len(inputStr) > 0 { + switch vConfig.Type { + case "integer", "int", "number": + val, err := strconv.Atoi(inputStr) + if err != nil { + return fmt.Errorf("deployment parameter %s is not an integer", kConfig) + } + + if vConfig.Max != 0 && val < vConfig.Min { + return fmt.Errorf("deployment parameter %s is less than the minimum value", kConfig) + } + + if vConfig.Max != 0 && val > vConfig.Max { + return fmt.Errorf("deployment parameter %s is greater than the maximum value", kConfig) + } + case "boolean", "bool": + if _, err := strconv.ParseBool(inputStr); err != nil { + return fmt.Errorf("deployment parameter %s is not a boolean", kConfig) + } + default: + continue + } + } + } + + return nil +} diff --git a/compiler/types/pipeline/deployment_test.go b/compiler/types/pipeline/deployment_test.go new file mode 100644 index 000000000..7f57c263c --- /dev/null +++ b/compiler/types/pipeline/deployment_test.go @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: Apache-2.0 + +package pipeline + +import ( + "testing" +) + +func TestPipeline_Deployment_Empty(t *testing.T) { + // setup tests + tests := []struct { + deployment *Deployment + want bool + }{ + { + deployment: &Deployment{Targets: []string{"foo"}}, + want: false, + }, + { + deployment: &Deployment{Parameters: ParameterMap{"foo": new(Parameter)}}, + want: false, + }, + { + deployment: new(Deployment), + want: true, + }, + { + want: true, + }, + } + + // run tests + for _, test := range tests { + got := test.deployment.Empty() + + if got != test.want { + t.Errorf("Empty is %v, want %t", got, test.want) + } + } +} + +func TestPipeline_Deployment_Validate(t *testing.T) { + // setup types + fullDeployConfig := &Deployment{ + Targets: []string{"east", "west", "north", "south"}, + Parameters: ParameterMap{ + "alpha": { + Description: "foo", + Type: "string", + Required: true, + Options: []string{"bar", "baz"}, + }, + "beta": { + Description: "bar", + Type: "string", + Required: false, + }, + "gamma": { + Description: "baz", + Type: "integer", + Required: true, + Min: -2, + Max: 2, + }, + "delta": { + Description: "qux", + Type: "boolean", + Required: false, + }, + "epsilon": { + Description: "quux", + Type: "number", + }, + }, + } + + // setup tests + tests := []struct { + inputTarget string + inputParams map[string]string + deployConfig *Deployment + wantErr bool + }{ + { // nil deployment config + inputTarget: "north", + inputParams: map[string]string{ + "alpha": "foo", + "beta": "bar", + }, + wantErr: false, + }, + { // empty deployment config + inputTarget: "north", + inputParams: map[string]string{ + "alpha": "foo", + "beta": "bar", + }, + deployConfig: new(Deployment), + wantErr: false, + }, + { // correct target and required params + inputTarget: "west", + inputParams: map[string]string{ + "alpha": "bar", + "gamma": "1", + }, + deployConfig: fullDeployConfig, + wantErr: false, + }, + { // correct target and wrong integer type for param gamma + inputTarget: "east", + inputParams: map[string]string{ + "alpha": "bar", + "beta": "test1", + "gamma": "string", + }, + deployConfig: fullDeployConfig, + wantErr: true, + }, + { // correct target and wrong boolean type for param delta + inputTarget: "south", + inputParams: map[string]string{ + "alpha": "bar", + "beta": "test2", + "gamma": "2", + "delta": "not-bool", + }, + deployConfig: fullDeployConfig, + wantErr: true, + }, + { // correct target and wrong option for param alpha + inputTarget: "south", + inputParams: map[string]string{ + "alpha": "bazzy", + "beta": "test2", + "gamma": "2", + "delta": "true", + }, + deployConfig: fullDeployConfig, + wantErr: true, + }, + { // correct target and gamma value over max + inputTarget: "north", + inputParams: map[string]string{ + "alpha": "bar", + "beta": "bar", + "gamma": "3", + }, + deployConfig: fullDeployConfig, + wantErr: true, + }, + { // correct target and gamma value under min + inputTarget: "north", + inputParams: map[string]string{ + "alpha": "baz", + "beta": "bar", + "gamma": "-3", + }, + deployConfig: fullDeployConfig, + wantErr: true, + }, + { // correct target and some number provided for epsilon param + inputTarget: "north", + inputParams: map[string]string{ + "alpha": "bar", + "beta": "bar", + "gamma": "1", + "delta": "true", + "epsilon": "42", + }, + deployConfig: fullDeployConfig, + wantErr: false, + }, + } + + // run tests + for _, test := range tests { + err := test.deployConfig.Validate(test.inputTarget, test.inputParams) + + if err != nil && !test.wantErr { + t.Errorf("Deployment.Validate returned err: %v", err) + } + + if err == nil && test.wantErr { + t.Errorf("Deployment.Validate did not return err") + } + } +} diff --git a/compiler/types/yaml/build.go b/compiler/types/yaml/build.go index 2a9f2b750..c0b719c9e 100644 --- a/compiler/types/yaml/build.go +++ b/compiler/types/yaml/build.go @@ -18,6 +18,7 @@ type Build struct { Stages StageSlice `yaml:"stages,omitempty" json:"stages,omitempty" jsonschema:"oneof_required=stages,description=Provide parallel execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/"` Steps StepSlice `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"oneof_required=steps,description=Provide sequential execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/"` Templates TemplateSlice `yaml:"templates,omitempty" json:"templates,omitempty" jsonschema:"description=Provide the name of templates to expand.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/"` + Deployment Deployment `yaml:"deployment,omitempty" json:"deployment,omitempty" jsonschema:"description=Provide deployment configuration.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/"` } // ToPipelineAPI converts the Build type to an API Pipeline type. @@ -73,6 +74,7 @@ func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error { Stages StageSlice Steps StepSlice Templates TemplateSlice + Deployment Deployment }) // attempt to unmarshal as a build type @@ -96,6 +98,7 @@ func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error { b.Stages = build.Stages b.Steps = build.Steps b.Templates = build.Templates + b.Deployment = build.Deployment return nil } diff --git a/compiler/types/yaml/build_test.go b/compiler/types/yaml/build_test.go index fc72abc85..bf2d341f6 100644 --- a/compiler/types/yaml/build_test.go +++ b/compiler/types/yaml/build_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/buildkite/yaml" + "github.com/google/go-cmp/cmp" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/raw" @@ -596,6 +597,52 @@ func TestYaml_Build_UnmarshalYAML(t *testing.T) { }, }, }, + { + file: "testdata/build_with_deploy_config.yml", + want: &Build{ + Version: "1", + Metadata: Metadata{ + Template: false, + Clone: nil, + Environment: []string{"steps", "services", "secrets"}, + }, + Deployment: Deployment{ + Targets: []string{"dev", "stage", "production"}, + Parameters: ParameterMap{ + "alpha": { + Description: "primary node name", + Required: true, + Type: "string", + Options: []string{"north", "south"}, + }, + "beta": { + Description: "secondary node name", + Required: false, + Type: "string", + Options: []string{"east", "west"}, + }, + "cluster_count": { + Description: "number of clusters to deploy", + Required: false, + Type: "integer", + }, + "canary": { + Description: "deploy with canary strategy", + Required: true, + Type: "boolean", + }, + }, + }, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"./deploy.sh"}, + Name: "deploy plugin", + Pull: "not_present", + Image: "awesome-plugin:latest", + }, + }, + }, + }, { file: "testdata/merge_anchor.yml", want: &Build{ @@ -679,8 +726,8 @@ func TestYaml_Build_UnmarshalYAML(t *testing.T) { t.Errorf("UnmarshalYAML returned err: %v", err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("UnmarshalYAML mismatch (-want +got):\n%s", diff) } } } diff --git a/compiler/types/yaml/deployment.go b/compiler/types/yaml/deployment.go new file mode 100644 index 000000000..6cd3db4e6 --- /dev/null +++ b/compiler/types/yaml/deployment.go @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" +) + +type ( + // Deployment is the yaml representation of a + // deployment block in a pipeline. + Deployment struct { + Targets raw.StringSlice `yaml:"targets,omitempty" json:"targets,omitempty" jsonschema:"description=List of deployment targets for the deployment block.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-targets-key"` + Parameters ParameterMap `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=List of parameters for the deployment block.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"description=Name of template to expand in the deployment block.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-template-key"` + } + + // ParameterMap is the yaml representation + // of the parameters block for a deployment block of a pipeline. + ParameterMap map[string]*Parameter + + // Parameters is the yaml representation of the deploy parameters + // from a deployment block in a pipeline. + Parameter struct { + Description string `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"description=Description of the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"description=Type of the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Required bool `yaml:"required,omitempty" json:"required,omitempty" jsonschema:"description=Flag indicating if the parameter is required.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Options raw.StringSlice `yaml:"options,omitempty" json:"options,omitempty" jsonschema:"description=List of options for the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Min int `yaml:"min,omitempty" json:"min,omitempty" jsonschema:"description=Minimum value for the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Max int `yaml:"max,omitempty" json:"max,omitempty" jsonschema:"description=Maximum value for the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + } +) + +// ToPipeline converts the Deployment type +// to a pipeline Deployment type. +func (d *Deployment) ToPipeline() *pipeline.Deployment { + return &pipeline.Deployment{ + Targets: d.Targets, + Parameters: d.Parameters.ToPipeline(), + } +} + +// ToPipeline converts the Parameters type +// to a pipeline Parameters type. +func (p *ParameterMap) ToPipeline() pipeline.ParameterMap { + if len(*p) == 0 { + return nil + } + + // parameter map we want to return + parameterMap := make(pipeline.ParameterMap) + + // iterate through each element in the parameter map + for k, v := range *p { + // add the element to the pipeline parameter map + parameterMap[k] = &pipeline.Parameter{ + Description: v.Description, + Type: v.Type, + Required: v.Required, + Options: v.Options, + Min: v.Min, + Max: v.Max, + } + } + + return parameterMap +} diff --git a/compiler/types/yaml/deployment_test.go b/compiler/types/yaml/deployment_test.go new file mode 100644 index 000000000..da7fecd42 --- /dev/null +++ b/compiler/types/yaml/deployment_test.go @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Deployment_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + name string + deployment *Deployment + want *pipeline.Deployment + }{ + { + name: "deployment with template", + deployment: &Deployment{ + Template: StepTemplate{Name: "foo"}, + }, + want: &pipeline.Deployment{}, + }, + { + name: "deployment with targets and parameters", + deployment: &Deployment{ + Targets: []string{"foo"}, + Parameters: ParameterMap{ + "foo": { + Description: "bar", + Type: "string", + Required: true, + Options: []string{"baz"}, + }, + "bar": { + Description: "baz", + Type: "string", + Required: false, + }, + }, + }, + want: &pipeline.Deployment{ + Targets: []string{"foo"}, + Parameters: pipeline.ParameterMap{ + "foo": { + Description: "bar", + Type: "string", + Required: true, + Options: []string{"baz"}, + }, + "bar": { + Description: "baz", + Type: "string", + Required: false, + }, + }, + }, + }, + { + name: "empty deployment config", + deployment: &Deployment{}, + want: &pipeline.Deployment{}, + }, + } + + // run tests + for _, test := range tests { + got := test.deployment.ToPipeline() + + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("ToPipeline for %s does not match: -want +got):\n%s", test.name, diff) + } + } +} diff --git a/compiler/types/yaml/testdata/build_with_deploy_config.yml b/compiler/types/yaml/testdata/build_with_deploy_config.yml new file mode 100644 index 000000000..61aea8b48 --- /dev/null +++ b/compiler/types/yaml/testdata/build_with_deploy_config.yml @@ -0,0 +1,36 @@ +version: "1" + +deployment: + targets: [ dev, stage, production ] + parameters: + alpha: + description: primary node name + required: true + type: string + options: + - north + - south + + beta: + description: secondary node name + required: false + type: string + options: + - east + - west + + cluster_count: + description: number of clusters to deploy + required: false + type: integer + + canary: + description: deploy with canary strategy + required: true + type: boolean + +steps: + - name: deploy plugin + image: awesome-plugin:latest + commands: + - ./deploy.sh diff --git a/compiler/types/yaml/testdata/deploy_parameter.yml b/compiler/types/yaml/testdata/deploy_parameter.yml new file mode 100644 index 000000000..89a9e0ae6 --- /dev/null +++ b/compiler/types/yaml/testdata/deploy_parameter.yml @@ -0,0 +1,11 @@ +--- +foo: + description: bar + required: true + type: string + options: + - baz +hello: + description: baz + required: false + type: string \ No newline at end of file diff --git a/router/deployment.go b/router/deployment.go index 0f7640561..0665bf051 100644 --- a/router/deployment.go +++ b/router/deployment.go @@ -16,7 +16,8 @@ import ( // // POST /api/v1/deployments/:org/:repo // GET /api/v1/deployments/:org/:repo -// GET /api/v1/deployments/:org/:repo/:deployment . +// GET /api/v1/deployments/:org/:repo/:deployment +// GET /api/v1/deployments/:org/:repo/config . func DeploymentHandlers(base *gin.RouterGroup) { // Deployments endpoints deployments := base.Group("/deployments/:org/:repo", org.Establish(), repo.Establish()) @@ -24,5 +25,6 @@ func DeploymentHandlers(base *gin.RouterGroup) { deployments.POST("", perm.MustWrite(), deployment.CreateDeployment) deployments.GET("", perm.MustRead(), deployment.ListDeployments) deployments.GET("/:deployment", perm.MustRead(), deployment.GetDeployment) + deployments.GET("/config", perm.MustRead(), deployment.GetDeploymentConfig) } // end of deployments endpoints } diff --git a/scm/github/webhook.go b/scm/github/webhook.go index 91a3854f2..61bdd2135 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -416,6 +416,8 @@ func (c *client) processDeploymentEvent(h *api.Hook, payload *github.DeploymentE if len(deployPayload) != 0 { // set the payload info on the build b.SetDeployPayload(deployPayload) + // set payload info on the deployment + d.SetPayload(deployPayload) } } diff --git a/scm/github/webhook_test.go b/scm/github/webhook_test.go index 0d342ccdb..c736a94d2 100644 --- a/scm/github/webhook_test.go +++ b/scm/github/webhook_test.go @@ -622,6 +622,7 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { wantDeployment.SetTask("deploy") wantDeployment.SetTarget("production") wantDeployment.SetDescription("") + wantDeployment.SetPayload(raw.StringSliceMap{"foo": "test1", "bar": "test2"}) wantDeployment.SetCreatedAt(time.Now().UTC().Unix()) wantDeployment.SetCreatedBy("Codertocat") From 8b5b79f6078195c173315f9b622666b92e00fc4d Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Fri, 17 Jan 2025 12:15:07 -0600 Subject: [PATCH 15/41] resolve conflicts with buildkite --- .github/workflows/jsonschema.yml | 35 + .github/workflows/test.yml | 46 +- .gitignore | 4 +- Makefile | 44 ++ cmd/jsonschema-gen/main.go | 29 + compiler/types/raw/map.go | 26 + compiler/types/raw/slice.go | 23 + compiler/types/yaml/ruleset.go | 104 ++- compiler/types/yaml/secret.go | 25 +- compiler/types/yaml/stage.go | 17 + compiler/types/yaml/template.go | 2 +- compiler/types/yaml/ulimit.go | 31 + compiler/types/yaml/volume.go | 31 + go.mod | 122 +-- go.sum | 693 +----------------- schema/pipeline.go | 91 +++ schema/pipeline_test.go | 40 + .../pipeline/fail/image_and_template.yml | 7 + .../testdata/pipeline/fail/invalid_prop.yml | 11 + .../testdata/pipeline/fail/invalid_pull.yml | 8 + .../testdata/pipeline/fail/missing_name.yml | 8 + .../pipeline/fail/ruleset_invalid_matcher.yml | 8 + .../pipeline/fail/ruleset_invalid_status.yml | 10 + .../pipeline/fail/ruleset_invalid_values.yml | 9 + .../pipeline/fail/stages_and_steps.yml | 15 + schema/testdata/pipeline/pass/basic.yml | 7 + schema/testdata/pipeline/pass/complex.yml | 59 ++ schema/testdata/pipeline/pass/ruleset_if.yml | 10 + .../testdata/pipeline/pass/ruleset_path.yml | 10 + .../testdata/pipeline/pass/ruleset_status.yml | 9 + schema/testdata/pipeline/pass/stages.yml | 20 + 31 files changed, 726 insertions(+), 828 deletions(-) create mode 100644 .github/workflows/jsonschema.yml create mode 100644 cmd/jsonschema-gen/main.go create mode 100644 schema/pipeline.go create mode 100644 schema/pipeline_test.go create mode 100644 schema/testdata/pipeline/fail/image_and_template.yml create mode 100644 schema/testdata/pipeline/fail/invalid_prop.yml create mode 100644 schema/testdata/pipeline/fail/invalid_pull.yml create mode 100644 schema/testdata/pipeline/fail/missing_name.yml create mode 100644 schema/testdata/pipeline/fail/ruleset_invalid_matcher.yml create mode 100644 schema/testdata/pipeline/fail/ruleset_invalid_status.yml create mode 100644 schema/testdata/pipeline/fail/ruleset_invalid_values.yml create mode 100644 schema/testdata/pipeline/fail/stages_and_steps.yml create mode 100644 schema/testdata/pipeline/pass/basic.yml create mode 100644 schema/testdata/pipeline/pass/complex.yml create mode 100644 schema/testdata/pipeline/pass/ruleset_if.yml create mode 100644 schema/testdata/pipeline/pass/ruleset_path.yml create mode 100644 schema/testdata/pipeline/pass/ruleset_status.yml create mode 100644 schema/testdata/pipeline/pass/stages.yml diff --git a/.github/workflows/jsonschema.yml b/.github/workflows/jsonschema.yml new file mode 100644 index 000000000..8c16bc3ec --- /dev/null +++ b/.github/workflows/jsonschema.yml @@ -0,0 +1,35 @@ +# name of the action +name: jsonschema + +# trigger on release events +on: + release: + types: [created] + +# pipeline to execute +jobs: + schema: + runs-on: ubuntu-latest + + steps: + - name: clone + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + + - name: install go + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + # use version from go.mod file + go-version-file: "go.mod" + cache: true + check-latest: true + + - name: build + run: | + make jsonschema + + - name: upload + uses: skx/github-action-publish-binaries@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + args: "schema.json" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 272bbf582..4987db435 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,23 +12,29 @@ jobs: runs-on: ubuntu-latest steps: - - name: clone - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - - - name: install go - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 - with: - # use version from go.mod file - go-version-file: 'go.mod' - cache: true - check-latest: true - - - name: test - run: | - make test - - - name: coverage - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: coverage.out \ No newline at end of file + - name: clone + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + + - name: install go + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + # use version from go.mod file + go-version-file: "go.mod" + cache: true + check-latest: true + + - name: test + run: | + make test + + - name: test jsonschema + run: | + go install github.com/santhosh-tekuri/jsonschema/cmd/jv@v0.7.0 + make test-jsonschema + + - name: coverage + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage.out + diff --git a/.gitignore b/.gitignore index c557bc18b..a0b951aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -67,4 +67,6 @@ __debug_bin .history .ionide -# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode + +schema.json diff --git a/Makefile b/Makefile index 7c7a3ca31..227c4480a 100644 --- a/Makefile +++ b/Makefile @@ -326,6 +326,50 @@ spec-version-update: .PHONY: spec spec: spec-gen spec-version-update spec-validate +# The `jsonschema` target is intended to create +# a jsonschema for a Vela pipeline. +# +# Usage: `make jsonschema` +.PHONY: jsonschema +jsonschema: + @echo + @echo "### Generating JSON schema" + @go run cmd/jsonschema-gen/main.go > schema.json + +# The `test-jsonschema` target is intended to test +# the created jsonschema against a set of failing +# and passing example vela templates located in +# schema/testdata/pipeline. +# +# The test relies on the `jv` command line tool, +# which can be installed via: +# +# go install github.com/santhosh-tekuri/jsonschema/cmd/jv@latest +# +# Usage: `make test-jsonschema` +.PHONY: test-jsonschema +test-jsonschema: jsonschema + @echo + @echo "### Testing Pipelines against JSON Schema" + @echo + @echo "=== Expected Failing Tests" + @for file in schema/testdata/pipeline/fail/*.yml; do \ + echo "› Test: $$file"; \ + if jv schema.json $$file >/dev/null 2>&1; then \ + echo "Unexpected success for $$file"; \ + exit 1; \ + fi; \ + done + @echo + @echo "=== Expected Passing Tests" + @for file in schema/testdata/pipeline/pass/*.yml; do \ + echo "› Test: $$file"; \ + if ! jv schema.json $$file >/dev/null 2>&1; then \ + echo "Unexpected failure for $$file"; \ + exit 1; \ + fi; \ + done + # The `lint` target is intended to lint the # Go source code with golangci-lint. # diff --git a/cmd/jsonschema-gen/main.go b/cmd/jsonschema-gen/main.go new file mode 100644 index 000000000..ce6b49b37 --- /dev/null +++ b/cmd/jsonschema-gen/main.go @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 + +//go:build ignore + +package main + +import ( + "encoding/json" + "fmt" + + "github.com/sirupsen/logrus" + + "github.com/go-vela/server/schema" +) + +func main() { + js, err := schema.NewPipelineSchema() + if err != nil { + logrus.Fatal("schema generation failed:", err) + } + + // output json + j, err := json.MarshalIndent(js, "", " ") + if err != nil { + logrus.Fatal(err) + } + + fmt.Printf("%s\n", j) +} diff --git a/compiler/types/raw/map.go b/compiler/types/raw/map.go index ed8fff427..ed569949a 100644 --- a/compiler/types/raw/map.go +++ b/compiler/types/raw/map.go @@ -7,6 +7,8 @@ import ( "encoding/json" "errors" "strings" + + "github.com/invopop/jsonschema" ) // StringSliceMap represents an array of strings or a map of strings. @@ -138,3 +140,27 @@ func (s *StringSliceMap) UnmarshalYAML(unmarshal func(interface{}) error) error return errors.New("unable to unmarshal into StringSliceMap") } + +// JSONSchema handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Without these changes it would only allow a map of string, +// but we do some special handling to support array of strings. +func (StringSliceMap) JSONSchema() *jsonschema.Schema { + return &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + { + Type: "array", + Items: &jsonschema.Schema{ + Type: "string", + }, + }, + { + Type: "object", + AdditionalProperties: &jsonschema.Schema{ + Type: "string", + }, + }, + }, + } +} diff --git a/compiler/types/raw/slice.go b/compiler/types/raw/slice.go index 11746363e..fb6ac59b2 100644 --- a/compiler/types/raw/slice.go +++ b/compiler/types/raw/slice.go @@ -5,6 +5,8 @@ package raw import ( "encoding/json" "errors" + + "github.com/invopop/jsonschema" ) // StringSlice represents a string or an array of strings. @@ -71,3 +73,24 @@ func (s *StringSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { return errors.New("unable to unmarshal into StringSlice") } + +// JSONSchema handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Without these changes it would only allow an array of strings, +// but we do some special handling to support plain string also. +func (StringSlice) JSONSchema() *jsonschema.Schema { + return &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + { + Type: "string", + }, + { + Type: "array", + Items: &jsonschema.Schema{ + Type: "string", + }, + }, + }, + } +} diff --git a/compiler/types/yaml/ruleset.go b/compiler/types/yaml/ruleset.go index c30e3b109..a75433311 100644 --- a/compiler/types/yaml/ruleset.go +++ b/compiler/types/yaml/ruleset.go @@ -3,6 +3,8 @@ package yaml import ( + "github.com/invopop/jsonschema" + "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" "github.com/go-vela/server/constants" @@ -22,13 +24,15 @@ type ( // Rules is the yaml representation of the ruletypes // from a ruleset block for a step in a pipeline. Rules struct { - Branch []string `yaml:"branch,omitempty,flow" json:"branch,omitempty" jsonschema:"description=Limits the execution of a step to matching build branches.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Comment []string `yaml:"comment,omitempty,flow" json:"comment,omitempty" jsonschema:"description=Limits the execution of a step to matching a pull request comment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Event []string `yaml:"event,omitempty,flow" json:"event,omitempty" jsonschema:"description=Limits the execution of a step to matching build events.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Path []string `yaml:"path,omitempty,flow" json:"path,omitempty" jsonschema:"description=Limits the execution of a step to matching files changed in a repository.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Repo []string `yaml:"repo,omitempty,flow" json:"repo,omitempty" jsonschema:"description=Limits the execution of a step to matching repos.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Sender []string `yaml:"sender,omitempty,flow" json:"sender,omitempty" jsonschema:"description=Limits the execution of a step to matching build senders.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` - Status []string `yaml:"status,omitempty,flow" json:"status,omitempty" jsonschema:"enum=[failure],enum=[success],description=Limits the execution of a step to matching build statuses.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Branch []string `yaml:"branch,omitempty,flow" json:"branch,omitempty" jsonschema:"description=Limits the execution of a step to matching build branches.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Comment []string `yaml:"comment,omitempty,flow" json:"comment,omitempty" jsonschema:"description=Limits the execution of a step to matching a pull request comment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + // enums for 'event' jsonschema are set in JSONSchemaExtend() method below + Event []string `yaml:"event,omitempty,flow" json:"event,omitempty" jsonschema:"description=Limits the execution of a step to matching build events.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Path []string `yaml:"path,omitempty,flow" json:"path,omitempty" jsonschema:"description=Limits the execution of a step to matching files changed in a repository.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Repo []string `yaml:"repo,omitempty,flow" json:"repo,omitempty" jsonschema:"description=Limits the execution of a step to matching repos.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Sender []string `yaml:"sender,omitempty,flow" json:"sender,omitempty" jsonschema:"description=Limits the execution of a step to matching build senders.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + // enums for 'status' jsonschema are set in JSONSchemaExtend() method below + Status []string `yaml:"status,omitempty,flow" json:"status,omitempty" jsonschema:"description=Limits the execution of a step to matching build statuses.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Tag []string `yaml:"tag,omitempty,flow" json:"tag,omitempty" jsonschema:"description=Limits the execution of a step to matching build tag references.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Target []string `yaml:"target,omitempty,flow" json:"target,omitempty" jsonschema:"description=Limits the execution of a step to matching build deployment targets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` Label []string `yaml:"label,omitempty,flow" json:"label,omitempty" jsonschema:"description=Limits step execution to match on pull requests labels.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` @@ -186,3 +190,89 @@ func (r *Rules) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } + +// JSONSchemaExtend handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Mainly it handles the fact that all Rules fields are raw.StringSlice +// but also handles adding enums to select fields as they would be too +// cumbersome to maintain in the jsonschema struct tag. +func (Rules) JSONSchemaExtend(schema *jsonschema.Schema) { + for item := schema.Properties.Newest(); item != nil; item = item.Prev() { + currSchema := *item.Value + + // store the current description so we can lift it to top level + currDescription := currSchema.Description + currSchema.Description = "" + + // handle each field as needed + switch item.Key { + case "status": + // possible values for 'status' + enums := []string{ + "success", + "failure", + } + + for _, str := range enums { + currSchema.Items.Enum = append(currSchema.Items.Enum, str) + } + + schema.Properties.Set(item.Key, &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + &currSchema, + { + Type: "string", + Enum: currSchema.Items.Enum, + }, + }, + Description: currDescription, + }) + case "event": + // possible values for 'event' + enums := []string{ + "comment", + "comment:created", + "comment:edited", + "delete:branch", + "delete:tag", + "deployment", + "pull_request", + "pull_request*", + "pull_request:edited", + "pull_request:labeled", + "pull_request:opened", + "pull_request:reopened", + "pull_request:synchronize", + "pull_request:unlabeled", + "push", + "schedule", + "tag", + } + + for _, str := range enums { + currSchema.Items.Enum = append(currSchema.Items.Enum, str) + } + + schema.Properties.Set(item.Key, &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + &currSchema, + { + Type: "string", + Enum: currSchema.Items.Enum, + }, + }, + Description: currDescription, + }) + default: + // all other fields are raw.StringSlice + schema.Properties.Set(item.Key, &jsonschema.Schema{ + OneOf: []*jsonschema.Schema{ + &currSchema, + {Type: "string"}, + }, + Description: currDescription, + }) + } + } +} diff --git a/compiler/types/yaml/secret.go b/compiler/types/yaml/secret.go index 0a6c0dbf9..efb0b86a9 100644 --- a/compiler/types/yaml/secret.go +++ b/compiler/types/yaml/secret.go @@ -7,6 +7,8 @@ import ( "fmt" "strings" + "github.com/invopop/jsonschema" + "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" "github.com/go-vela/server/constants" @@ -203,8 +205,8 @@ type ( // StepSecret is the yaml representation of a secret // from a secrets block for a step in a pipeline. StepSecret struct { - Source string `yaml:"source,omitempty"` - Target string `yaml:"target,omitempty"` + Source string `yaml:"source,omitempty" json:"source"` + Target string `yaml:"target,omitempty" json:"target"` } ) @@ -269,3 +271,22 @@ func (s *StepSecretSlice) UnmarshalYAML(unmarshal func(interface{}) error) error return errors.New("failed to unmarshal StepSecretSlice") } + +// JSONSchemaExtend handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Allows using simple strings or objects. +func (StepSecret) JSONSchemaExtend(schema *jsonschema.Schema) { + old := *schema + schema.OneOf = []*jsonschema.Schema{ + { + Type: "string", + AdditionalProperties: jsonschema.FalseSchema, + }, + &old, + } + schema.AdditionalProperties = nil + schema.Properties = nil + schema.Required = nil + schema.Type = "" +} diff --git a/compiler/types/yaml/stage.go b/compiler/types/yaml/stage.go index 93bc8c8ca..79b635b05 100644 --- a/compiler/types/yaml/stage.go +++ b/compiler/types/yaml/stage.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/buildkite/yaml" + "github.com/invopop/jsonschema" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" @@ -125,6 +126,22 @@ func (s StageSlice) MarshalYAML() (interface{}, error) { return output, nil } +// JSONSchemaExtend handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Stages are not really a slice of stages to the user. This change +// supports the map they really are. +func (StageSlice) JSONSchemaExtend(schema *jsonschema.Schema) { + schema.AdditionalProperties = jsonschema.FalseSchema + schema.Items = nil + schema.PatternProperties = map[string]*jsonschema.Schema{ + ".*": { + Ref: "#/$defs/Stage", + }, + } + schema.Type = "object" +} + // MergeEnv takes a list of environment variables and attempts // to set them in the stage environment. If the environment // variable already exists in the stage, than this will diff --git a/compiler/types/yaml/template.go b/compiler/types/yaml/template.go index ef2005540..4055fc9dc 100644 --- a/compiler/types/yaml/template.go +++ b/compiler/types/yaml/template.go @@ -17,7 +17,7 @@ type ( Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-name-key"` Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"required,minLength=1,description=Path to template in remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-source-key"` Format string `yaml:"format,omitempty" json:"format,omitempty" jsonschema:"enum=starlark,enum=golang,enum=go,default=go,minLength=1,description=language used within the template file \nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-format-key"` - Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"minLength=1,example=github,description=Type of template provided from the remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-type-key"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"minLength=1,enum=github,enum=file,example=github,description=Type of template provided from the remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-type-key"` Variables map[string]interface{} `yaml:"vars,omitempty" json:"vars,omitempty" jsonschema:"description=Variables injected into the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-variables-key"` } diff --git a/compiler/types/yaml/ulimit.go b/compiler/types/yaml/ulimit.go index 14d96c287..af0fa544b 100644 --- a/compiler/types/yaml/ulimit.go +++ b/compiler/types/yaml/ulimit.go @@ -7,6 +7,8 @@ import ( "strconv" "strings" + "github.com/invopop/jsonschema" + "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" ) @@ -130,3 +132,32 @@ func (u *UlimitSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } + +// JSONSchemaExtend handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Without these changes it would only allow an object per the struct, +// but we do some special handling to allow specially formatted strings. +func (Ulimit) JSONSchemaExtend(schema *jsonschema.Schema) { + oldAddProps := schema.AdditionalProperties + oldProps := schema.Properties + oldReq := schema.Required + + schema.AdditionalProperties = nil + schema.OneOf = []*jsonschema.Schema{ + { + Type: "string", + Pattern: "[a-z]+=[0-9]+:[0-9]+", + AdditionalProperties: oldAddProps, + }, + { + Type: "object", + Properties: oldProps, + Required: oldReq, + AdditionalProperties: oldAddProps, + }, + } + schema.Properties = nil + schema.Required = nil + schema.Type = "" +} diff --git a/compiler/types/yaml/volume.go b/compiler/types/yaml/volume.go index 7b7b87d32..58740f652 100644 --- a/compiler/types/yaml/volume.go +++ b/compiler/types/yaml/volume.go @@ -6,6 +6,8 @@ import ( "fmt" "strings" + "github.com/invopop/jsonschema" + "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" ) @@ -119,3 +121,32 @@ func (v *VolumeSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } + +// JSONSchemaExtend handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Without these changes it would only allow an object per the struct, +// but we do some special handling to allow specially formatted strings. +func (Volume) JSONSchemaExtend(schema *jsonschema.Schema) { + oldAddProps := schema.AdditionalProperties + oldProps := schema.Properties + oldReq := schema.Required + + schema.AdditionalProperties = nil + schema.OneOf = []*jsonschema.Schema{ + { + Type: "string", + Pattern: "[a-z\\/]+:[a-z\\/]+:[row]+", + AdditionalProperties: oldAddProps, + }, + { + Type: "object", + Properties: oldProps, + Required: oldReq, + AdditionalProperties: oldAddProps, + }, + } + schema.Properties = nil + schema.Required = nil + schema.Type = "" +} diff --git a/go.mod b/go.mod index 47d0cd856..290078d95 100644 --- a/go.mod +++ b/go.mod @@ -13,11 +13,9 @@ require ( github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/distribution/reference v0.6.0 github.com/drone/envsubst v1.0.3 - github.com/dustin/go-humanize v1.0.1 github.com/ghodss/yaml v1.0.0 github.com/gin-gonic/gin v1.10.0 github.com/go-playground/assert/v2 v2.2.0 - github.com/go-vela/archiver/v3 v3.4.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v65 v65.0.0 @@ -31,13 +29,11 @@ require ( github.com/lestrrat-go/jwx/v2 v2.1.1 github.com/lib/pq v1.10.9 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.0.81 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.4 github.com/redis/go-redis/v9 v9.6.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.11.0 - github.com/stretchr/testify v1.9.0 github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 github.com/urfave/cli/v2 v2.27.4 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0 @@ -49,7 +45,7 @@ require ( go.opentelemetry.io/otel/sdk v1.30.0 go.opentelemetry.io/otel/trace v1.30.0 go.starlark.net v0.0.0-20240925182052-1207426daebd - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.27.0 golang.org/x/oauth2 v0.23.0 golang.org/x/sync v0.8.0 golang.org/x/time v0.6.0 @@ -61,206 +57,98 @@ require ( require ( dario.cat/mergo v1.0.1 // indirect - git.apache.org/thrift.git v0.13.0 // indirect - github.com/Azure/azure-pipeline-go v0.2.2 // indirect - github.com/Azure/azure-storage-blob-go v0.10.0 // indirect - github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/Shopify/sarama v1.27.2 // indirect - github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect - github.com/alecthomas/participle v0.2.1 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect - github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/bcicen/jstream v1.0.1 // indirect - github.com/beevik/ntp v0.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.12.2 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cheggaaa/pb v1.0.29 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect - github.com/coredns/coredns v1.4.0 // indirect - github.com/coreos/go-semver v0.2.0 // indirect - github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 // indirect - github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dchest/siphash v1.2.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/djherbis/atime v1.0.0 // indirect - github.com/draganm/miniotest v0.1.0 // indirect - github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect - github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d // indirect - github.com/eapache/go-resiliency v1.2.0 // indirect - github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect - github.com/eapache/queue v1.1.0 // indirect - github.com/eclipse/paho.mqtt.golang v1.3.0 // indirect - github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect - github.com/fatih/color v1.16.0 // indirect - github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.4 // 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.22.1 // indirect - github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/golang/snappy v0.0.2 // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gorilla/css v1.0.1 // indirect - github.com/gorilla/handlers v1.5.1 // indirect - github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/jcmturner/gofork v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid v1.3.1 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect - github.com/klauspost/pgzip v1.2.5 // indirect - github.com/klauspost/readahead v1.3.1 // indirect - github.com/klauspost/reedsolomon v1.9.9 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect - github.com/miekg/dns v1.1.35 // indirect - github.com/minio/cli v1.22.0 // indirect - github.com/minio/highwayhash v1.0.0 // indirect - github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/minio v0.0.0-20201229095728-cc457f179873 // indirect - github.com/minio/selfupdate v0.3.1 // indirect - github.com/minio/sha256-simd v0.1.1 // indirect - github.com/minio/simdjson-go v0.1.5 // indirect - github.com/minio/sio v0.2.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/montanaflynn/stats v0.5.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nats-io/jwt v1.1.0 // indirect - github.com/nats-io/nats.go v1.10.0 // indirect - github.com/nats-io/nkeys v0.2.0 // indirect - github.com/nats-io/nuid v1.0.1 // indirect - github.com/nats-io/stan.go v0.7.0 // indirect - github.com/ncw/directio v1.0.5 // indirect - github.com/nsqio/go-nsq v1.0.8 // indirect - github.com/nwaples/rardecode v1.1.0 // indirect - github.com/olivere/elastic/v7 v7.0.22 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/philhofer/fwd v1.1.1 // indirect - github.com/pierrec/lz4 v2.6.1+incompatible // indirect - github.com/pierrec/lz4/v4 v4.1.2 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect - github.com/rjeczalik/notify v0.9.2 // indirect - github.com/rs/cors v1.7.0 // indirect - github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/secure-io/sio-go v0.3.0 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/shirou/gopsutil v3.20.11+incompatible // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/cast v1.7.0 // indirect - github.com/streadway/amqp v1.0.0 // indirect - github.com/tidwall/gjson v1.3.5 // indirect - github.com/tidwall/match v1.0.1 // indirect - github.com/tidwall/pretty v1.0.0 // indirect - github.com/tidwall/sjson v1.0.4 // indirect - github.com/tinylib/msgp v1.1.3 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect - github.com/ulikunitz/xz v0.5.9 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect - github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a // indirect - github.com/willf/bitset v1.1.11 // indirect - github.com/willf/bloom v2.0.3+incompatible // indirect github.com/x448/float16 v0.8.4 // indirect - github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect - github.com/xdg/stringprep v1.0.0 // indirect - github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect - go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b // indirect go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect - go.uber.org/atomic v1.5.0 // indirect - go.uber.org/multierr v1.3.0 // indirect - go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee // indirect - go.uber.org/zap v1.13.0 // indirect golang.org/x/arch v0.10.0 // indirect - golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/api v0.152.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/grpc v1.66.1 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect - gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect - gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect - gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect - gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect - gopkg.in/ldap.v3 v3.0.3 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - honnef.co/go/tools v0.0.1-2019.2.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect ) diff --git a/go.sum b/go.sum index ba6ddf701..18a403659 100644 --- a/go.sum +++ b/go.sum @@ -1,40 +1,11 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -git.apache.org/thrift.git v0.13.0 h1:/3bz5WZ+sqYArk7MBBBbDufMxKKOA56/6JO6psDpUDY= -git.apache.org/thrift.git v0.13.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs= -github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.9.1/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 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/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc= github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 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.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= @@ -45,23 +16,8 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.27.2 h1:1EyY1dsxNDUQEv0O/4TsjosHI2CgB1uo9H/v56xzTxc= -github.com/Shopify/sarama v1.27.2/go.mod h1:g5s5osgELxgM+Md9Qni9rzo7Rbt+vvFQI4bt/Mc93II= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adhocore/gronx v1.19.1 h1:S4c3uVp5jPjnk00De0lslyTenGJ4nA3Ydbkj1SbdPVc= github.com/adhocore/gronx v1.19.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/alecthomas/participle v0.2.1 h1:4AVLj1viSGa4LG5HDXKXrm5xRx19SB/rS/skPQB1Grw= -github.com/alecthomas/participle v0.2.1/go.mod h1:SW6HZGeZgSIpcUWX3fXpfZhuaWHnmoD5KCVaqSaNTkk= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -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/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= @@ -70,35 +26,22 @@ github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUi github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.35.20/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck= -github.com/bcicen/jstream v1.0.1/go.mod h1:9ielPxqFry7Y4Tg3j4BfjPocfJ3TbsRtXOAYXYmRuAQ= -github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw= -github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= -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/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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 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/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 h1:q+sMKdA6L8LyGVudTkpGoC73h6ak2iWSPFiFo/pFOU8= github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3/go.mod h1:5hCug3EZaHXU3FdCA3gJm0YTNi+V+ooA2qNTiVpky4A= github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= @@ -106,105 +49,45 @@ github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKz github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/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/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= -github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/colinmarc/hdfs/v2 v2.1.1/go.mod h1:M3x+k8UKKmxtFu++uAZ0OtDU8jR3jnaZIAc6yK4Ue0c= -github.com/coredns/coredns v1.4.0 h1:RubBkYmkByUqZWWkjRHvNLnUHgkRVqAWgSMmRFvpE1A= -github.com/coredns/coredns v1.4.0/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= -github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 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/djherbis/atime v1.0.0 h1:ySLvBAM0EvOGaX7TI4dAM5lWj+RdJUCKtGSEHN8SGBg= -github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8= -github.com/draganm/miniotest v0.1.0 h1:Za9dqQYV5NV6UNxQkrR55wKc79LxZ5VHq4qcRLx5fZ0= -github.com/draganm/miniotest v0.1.0/go.mod h1:GxoGGOLfw0s0z6H7Be2MfKLoMXOrM9Yg78lu7FDdIho= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d h1:QK8IYltsNy+5QZcDFbVkyInrs98/wHy1tfUTGG91sps= -github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d/go.mod h1:apXo4PA/BgBPrt66j0N45O2stlBTRowdip2igwcUWVc= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.3.0 h1:MU79lqr3FKNKbSrGN7d7bNYqh8MwWW7Zcx0iG+VIw9I= -github.com/eclipse/paho.mqtt.golang v1.3.0/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= -github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= @@ -219,20 +102,11 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= -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= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= -github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -241,11 +115,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-vela/archiver v3.1.1+incompatible h1:xTTMMwKyHwDgNFn+c1XsKHrHFg6UyWmK4oSceSduH7A= @@ -254,47 +123,16 @@ github.com/go-vela/archiver/v3 v3.4.0 h1:c6GQRNTzr4Pn8HaxjzslIEiN89+kgZB4hLkXuBC github.com/go-vela/archiver/v3 v3.4.0/go.mod h1:1HbXVOHBXsfzwSog3x7T6ZU9BUv+VEWnuaPLZS/v0/8= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -307,64 +145,27 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/goware/urlx v0.3.2 h1:gdoo4kBHlkqZNaf6XlQ12LGtQOmpKJrR04Rc3RnpJEo= github.com/goware/urlx v0.3.2/go.mod h1:h8uwbJy68o+tQXCGZNa9D73WN8n0r9OBae5bUnLcgjw= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= @@ -372,40 +173,16 @@ github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3 github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= -github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= -github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= +github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -414,69 +191,36 @@ github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jcmturner/gofork v0.0.0-20180107083740-2aebee971930/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= -github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/klauspost/readahead v1.3.1 h1:QqXNYvm+VvqYcbrRT4LojUciM0XrznFRIDrbHiJtu/0= -github.com/klauspost/readahead v1.3.1/go.mod h1:AH9juHzNH7xqdqFHrMRSHeH2Ps+vFf+kblDqzPFiLJg= -github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54= -github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -500,277 +244,94 @@ github.com/lestrrat-go/jwx/v2 v2.1.1 h1:Y2ltVl8J6izLYFs54BVcpXLv5msSW4o8eXwnzZLI github.com/lestrrat-go/jwx/v2 v2.1.1/go.mod h1:4LvZg7oxu6Q5VJwn7Mk/UwooNRnTHUpXBj2C4j3HNx0= 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/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 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-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= -github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/minio/cli v1.22.0 h1:VTQm7lmXm3quxO917X3p+el1l0Ca5X3S4PM2ruUYO68= -github.com/minio/cli v1.22.0/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY= -github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA= -github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= -github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= 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 v0.0.0-20201229095728-cc457f179873 h1:zGS+Yw+0GivMJ0ij70aMNWnjaHU43Kk4kc/N/xfVxfk= -github.com/minio/minio v0.0.0-20201229095728-cc457f179873/go.mod h1:Y2vPTyVMgM/hdmWY4z1FAh0tAiPVAMLz4eeQ5FNcJmk= -github.com/minio/minio-go/v7 v7.0.7-0.20201217170524-3baf9ea06f7c/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc= github.com/minio/minio-go/v7 v7.0.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA= github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= -github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs= -github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/simdjson-go v0.1.5 h1:6T5mHh7r3kUvgwhmFWQAjoPV5Yt5oD/VPjAI9ViH1kM= -github.com/minio/simdjson-go v0.1.5/go.mod h1:oKURrZZEBtqObgJrSjN1Ln2n9MJj2icuBTkeJzZnvSI= -github.com/minio/sio v0.2.1 h1:NjzKiIMSMcHediVQR0AFVx2tp7Wxh9tKPfDI3kH7aHQ= -github.com/minio/sio v0.2.1/go.mod h1:8b0yPp2avGThviy/+OCJBI6OMpvxoUuiLvE6F1lebhw= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA= -github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= 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= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLgzmk= -github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM= -github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats-server/v2 v2.1.9/go.mod h1:9qVyoewoYXzG1ME9ox0HwkkzyYvnlBDugfR4Gg/8uHU= -github.com/nats-io/nats-streaming-server v0.19.0/go.mod h1:oqrRqpMg84aiPDyroTornjVWNYJKh+6ozh2Mgt8dslE= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY= -github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nkeys v0.2.0 h1:WXKF7diOaPU9cJdLD7nuzwasQy9vT1tBqzXZZf3AMJM= -github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -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/nats-io/stan.go v0.7.0 h1:sMVHD9RkxPOl6PJfDVBQd+gbxWkApeYl6GrH+10msO4= -github.com/nats-io/stan.go v0.7.0/go.mod h1:Ci6mUIpGQTjl++MqK2XzkWI/0vF+Bl72uScx7ejSYmU= -github.com/ncw/directio v1.0.5 h1:JSUBhdjEvVaJvOoyPAbcW0fnd0tvRXD76wEfZ1KcQz4= -github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t6ROk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nsqio/go-nsq v1.0.8 h1:3L2F8tNLlwXXlp2slDUrUWSBn2O3nMh8R1/KEDFTHPk= -github.com/nsqio/go-nsq v1.0.8/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olivere/elastic/v7 v7.0.22 h1:esBA6JJwvYgfms0EVlH7Z+9J4oQ/WUADF2y/nCNDw7s= -github.com/olivere/elastic/v7 v7.0.22/go.mod h1:VDexNy9NjmtAkrjNoI7tImv7FR4tf5zUA3ickqu5Pc8= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 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/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= -github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/quasilyte/go-ruleguard/dsl/fluent v0.0.0-20201222093424-5d7e62a465d3/go.mod h1:P7JlQWFT7jDcFZMtUPQbtGzzzxva3rBn6oIF+LPwFcM= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= -github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= -github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/secure-io/sio-go v0.3.0 h1:QKGb6rGJeiExac9wSWxnWPYo8O8OFN7lxXQvHshX6vo= -github.com/secure-io/sio-go v0.3.0/go.mod h1:D3KmXgKETffyYxBdFRN+Hpd2WzhzqS0EQwT3XWsAcBU= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto= -github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= -github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= @@ -778,18 +339,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tidwall/gjson v1.3.5 h1:2oW9FBNu8qt9jy5URgrzsVx/T/KSn3qn/smJQ0crlDQ= -github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= -github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= -github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/sjson v1.0.4 h1:UcdIRXff12Lpnu3OLtZvnc03g4vH2suXDXhBwBqmzYg= -github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= -github.com/tinylib/msgp v1.1.3 h1:3giwAkmtaEDLSV0MdO1lDLuPgklgPzmk8H9+So2BVfA= -github.com/tinylib/msgp v1.1.3/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -801,25 +350,14 @@ github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 h1:Jjn3zoRz13f8b1bR6Lr 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 v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX8Wdm2/JPA= -github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= +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= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 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-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -828,16 +366,6 @@ github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBU github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b h1:5makfKENOTVu2bNoHzSqwwz+g70ivWLSnExzd33/2bI= -go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0 h1:n4Dd8YaDFeTd2uw+uCHJzOKeqfLgAOlePZpQ5f9cAoE= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0/go.mod h1:8aCCTMjP225r98yevEMM5NYDb3ianWLoeIzZ1rPyxHU= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0 h1:sqmsIQ75l6lfZjjpnXXT9DFVtYEDg6CH0/Cn4/3A1Wg= @@ -862,260 +390,74 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.starlark.net v0.0.0-20240925182052-1207426daebd h1:S+EMisJOHklQxnS3kqsY8jl2y5aF0FDEdcLnOw3q22E= go.starlark.net v0.0.0-20240925182052-1207426daebd/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/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-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -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= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= -google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM= google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= -gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= -gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= -gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= -gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= -gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= -gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg= -gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= -gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= -gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= -gopkg.in/ldap.v3 v3.0.3 h1:YKRHW/2sIl05JsCtx/5ZuUueFuJyoj/6+DGXe3wp6ro= -gopkg.in/ldap.v3 v3.0.3/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= @@ -1124,12 +466,6 @@ gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= @@ -1137,8 +473,5 @@ k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/schema/pipeline.go b/schema/pipeline.go new file mode 100644 index 000000000..0daf45115 --- /dev/null +++ b/schema/pipeline.go @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 + +// This program utilizes the json/jsonschema tags on structs in compiler/types/yaml +// to generate the majority of the final jsonschema for a Vela pipeline. +// +// Some manual intervention is needed for custom types and/or custom unmarshaling +// that is in place. For reference, we use the mechanisms provided by the schema lib: +// https://github.com/invopop/jsonschema?tab=readme-ov-file#custom-type-definitions +// for hooking into the schema generation process. Some types will have a JSONSchema +// or JSONSchemaExtend method attached to handle these overrides. + +package schema + +import ( + "fmt" + + "github.com/invopop/jsonschema" + + types "github.com/go-vela/server/compiler/types/yaml" +) + +// NewPipelineSchema generates the JSON schema object for a Vela pipeline configuration. +// +// The returned value can be marshaled into actual JSON. +func NewPipelineSchema() (*jsonschema.Schema, error) { + ref := jsonschema.Reflector{ + ExpandedStruct: true, + } + s := ref.Reflect(types.Build{}) + + // very unlikely scenario + if s == nil { + return nil, fmt.Errorf("schema generation failed") + } + + s.Title = "Vela Pipeline Configuration" + + // allows folks to have other top level arbitrary + // keys without validation errors + s.AdditionalProperties = nil + + // apply Ruleset modification + // + // note: we have to do the modification here, + // because the custom type hooks can't provide + // access to the top level definitions, even if + // they were already processed, so we have to + // do it at this top level. + modRulesetSchema(s) + + return s, nil +} + +// modRulesetSchema applies modifications to the Ruleset definition. +// +// rules can currently live at ruleset level or nested within +// 'if' (default) or 'unless'. without changes the struct would +// only allow the nested version. +func modRulesetSchema(schema *jsonschema.Schema) { + if schema.Definitions == nil { + return + } + + rules, hasRules := schema.Definitions["Rules"] + ruleSet, hasRuleset := schema.Definitions["Ruleset"] + + // exit early if we don't have what we need + if !hasRules || !hasRuleset { + return + } + + // create copies + _rulesWithRuleset := *rules + _ruleSet := *ruleSet + + // copy every property from Ruleset, other than `if` and `unless` + for item := _ruleSet.Properties.Newest(); item != nil; item = item.Prev() { + if item.Key != "if" && item.Key != "unless" { + _rulesWithRuleset.Properties.Set(item.Key, item.Value) + } + } + + // create a new definition for Ruleset + schema.Definitions["Ruleset"].AnyOf = []*jsonschema.Schema{ + &_ruleSet, + &_rulesWithRuleset, + } + schema.Definitions["Ruleset"].Properties = nil + schema.Definitions["Ruleset"].Type = "" + schema.Definitions["Ruleset"].AdditionalProperties = nil +} diff --git a/schema/pipeline_test.go b/schema/pipeline_test.go new file mode 100644 index 000000000..d1a1a2d95 --- /dev/null +++ b/schema/pipeline_test.go @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 + +package schema + +import ( + "testing" +) + +func TestSchema_NewPipelineSchema(t *testing.T) { + tests := []struct { + name string + wantErr bool + }{ + { + name: "basic schema generation", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewPipelineSchema() + if (err != nil) != tt.wantErr { + t.Errorf("NewPipelineSchema() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr && got == nil { + t.Error("NewPipelineSchema() returned nil schema without error") + } + if !tt.wantErr { + if got.Title != "Vela Pipeline Configuration" { + t.Errorf("NewPipelineSchema() title = %v, want %v", got.Title, "Vela Pipeline Configuration") + } + if got.AdditionalProperties != nil { + t.Error("NewPipelineSchema() AdditionalProperties should be nil") + } + } + }) + } +} diff --git a/schema/testdata/pipeline/fail/image_and_template.yml b/schema/testdata/pipeline/fail/image_and_template.yml new file mode 100644 index 000000000..4770aacfb --- /dev/null +++ b/schema/testdata/pipeline/fail/image_and_template.yml @@ -0,0 +1,7 @@ +version: "1" + +steps: + - name: deploy + image: alpine + template: + name: deploy-template \ No newline at end of file diff --git a/schema/testdata/pipeline/fail/invalid_prop.yml b/schema/testdata/pipeline/fail/invalid_prop.yml new file mode 100644 index 000000000..41e7407a8 --- /dev/null +++ b/schema/testdata/pipeline/fail/invalid_prop.yml @@ -0,0 +1,11 @@ +version: "1" + +metadata: + auto_cancel: + invalid_key: true + +steps: + - name: test + image: alpine + commands: + - echo "Hello World" diff --git a/schema/testdata/pipeline/fail/invalid_pull.yml b/schema/testdata/pipeline/fail/invalid_pull.yml new file mode 100644 index 000000000..caa54cfd9 --- /dev/null +++ b/schema/testdata/pipeline/fail/invalid_pull.yml @@ -0,0 +1,8 @@ +version: "1" + +steps: + - name: build + pull: sometimes + image: alpine + commands: + - echo "Hello World" diff --git a/schema/testdata/pipeline/fail/missing_name.yml b/schema/testdata/pipeline/fail/missing_name.yml new file mode 100644 index 000000000..893237c0d --- /dev/null +++ b/schema/testdata/pipeline/fail/missing_name.yml @@ -0,0 +1,8 @@ +version: "1" + +stages: + test: + steps: + - image: alpine + commands: + - npm test \ No newline at end of file diff --git a/schema/testdata/pipeline/fail/ruleset_invalid_matcher.yml b/schema/testdata/pipeline/fail/ruleset_invalid_matcher.yml new file mode 100644 index 000000000..ebefd382a --- /dev/null +++ b/schema/testdata/pipeline/fail/ruleset_invalid_matcher.yml @@ -0,0 +1,8 @@ +version: "1" + +steps: + - name: test + image: python + ruleset: + matcher: invalid_matcher + branch: [main] \ No newline at end of file diff --git a/schema/testdata/pipeline/fail/ruleset_invalid_status.yml b/schema/testdata/pipeline/fail/ruleset_invalid_status.yml new file mode 100644 index 000000000..c82f15ef4 --- /dev/null +++ b/schema/testdata/pipeline/fail/ruleset_invalid_status.yml @@ -0,0 +1,10 @@ +version: "1" + +steps: + - name: deploy + image: alpine + ruleset: + if: + status: [pending] + unless: + branch: main \ No newline at end of file diff --git a/schema/testdata/pipeline/fail/ruleset_invalid_values.yml b/schema/testdata/pipeline/fail/ruleset_invalid_values.yml new file mode 100644 index 000000000..ac14ac7e4 --- /dev/null +++ b/schema/testdata/pipeline/fail/ruleset_invalid_values.yml @@ -0,0 +1,9 @@ +version: "1" + +steps: + - name: build + image: node + ruleset: + if: + event: invalid_event + operator: invalid \ No newline at end of file diff --git a/schema/testdata/pipeline/fail/stages_and_steps.yml b/schema/testdata/pipeline/fail/stages_and_steps.yml new file mode 100644 index 000000000..1418f4daf --- /dev/null +++ b/schema/testdata/pipeline/fail/stages_and_steps.yml @@ -0,0 +1,15 @@ +version: "1" + +stages: + test: + steps: + - name: test + image: alpine:latest + commands: + - echo "hello world" + +steps: + - name: test + image: alpine:latest + commands: + - echo "hello world" diff --git a/schema/testdata/pipeline/pass/basic.yml b/schema/testdata/pipeline/pass/basic.yml new file mode 100644 index 000000000..6787adfe1 --- /dev/null +++ b/schema/testdata/pipeline/pass/basic.yml @@ -0,0 +1,7 @@ +version: "1" + +steps: + - name: test + image: alpine:latest + commands: + - echo "hello world" diff --git a/schema/testdata/pipeline/pass/complex.yml b/schema/testdata/pipeline/pass/complex.yml new file mode 100644 index 000000000..c36e71f48 --- /dev/null +++ b/schema/testdata/pipeline/pass/complex.yml @@ -0,0 +1,59 @@ +version: "1" + +step_image: &step_image + image: something + +templates: + - name: go + source: github.com/octocat/hello-world/.vela/build.yml + format: go + type: github + +metadata: + clone: false + +worker: + flavor: large + +stages: + greeting: + steps: + - name: Greeting + secrets: [ docker_username ] + image: alpine + commands: + - echo "Hello, World" + - name: Template + template: + name: go + vars: + image: golang:latest + + welcome: + steps: + - name: Welcome + <<: *step_image + ruleset: + unless: + event: push + branch: main + if: + event: pull_request + continue: true + commands: | + echo "Welcome to the Vela docs" + go build something + + goodbye: + # will wait for greeting and welcome to finish + needs: [greeting, welcome] + steps: + - name: Goodbye + image: alpine + commands: + - echo "Goodbye, World" +secrets: + - name: docker_username + key: go-vela/docs/username + engine: native + type: repo \ No newline at end of file diff --git a/schema/testdata/pipeline/pass/ruleset_if.yml b/schema/testdata/pipeline/pass/ruleset_if.yml new file mode 100644 index 000000000..506eb4a1a --- /dev/null +++ b/schema/testdata/pipeline/pass/ruleset_if.yml @@ -0,0 +1,10 @@ +version: "1" + +steps: + - name: deploy + image: alpine + ruleset: + if: + event: [deployment, push] + branch: main + operator: and \ No newline at end of file diff --git a/schema/testdata/pipeline/pass/ruleset_path.yml b/schema/testdata/pipeline/pass/ruleset_path.yml new file mode 100644 index 000000000..59276f0f9 --- /dev/null +++ b/schema/testdata/pipeline/pass/ruleset_path.yml @@ -0,0 +1,10 @@ +version: "1" + +steps: + - name: test + image: golang + ruleset: + branch: feature/* + event: pull_request + path: ["src/*/*.go"] + matcher: filepath \ No newline at end of file diff --git a/schema/testdata/pipeline/pass/ruleset_status.yml b/schema/testdata/pipeline/pass/ruleset_status.yml new file mode 100644 index 000000000..3c7d7ff42 --- /dev/null +++ b/schema/testdata/pipeline/pass/ruleset_status.yml @@ -0,0 +1,9 @@ +version: "1" + +steps: + - name: notify + image: slack + ruleset: + unless: + status: success + continue: true \ No newline at end of file diff --git a/schema/testdata/pipeline/pass/stages.yml b/schema/testdata/pipeline/pass/stages.yml new file mode 100644 index 000000000..8fe7820c6 --- /dev/null +++ b/schema/testdata/pipeline/pass/stages.yml @@ -0,0 +1,20 @@ +version: "1" + +stages: + test: + steps: + - name: test + image: alpine:latest + commands: + - echo "hello world" + - name: test2 + image: alpine:latest + commands: + - echo "hello world" + + test2: + steps: + - name: test + image: alpine:latest + commands: + - echo "hello world" \ No newline at end of file From 9328ecde763db4c1bfdaeddfd103166c725ff7a9 Mon Sep 17 00:00:00 2001 From: dave vader <48764154+plyr4@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:05:55 -0600 Subject: [PATCH 16/41] feat: opt-in gh app integration (#1217) --- api/auth/get_token.go | 40 +++ api/build/compile_publish.go | 2 + api/repo/create.go | 14 + api/repo/repair.go | 35 +- api/scm/sync.go | 26 ++ api/types/repo.go | 31 +- api/types/repo_test.go | 4 +- api/webhook/post.go | 17 +- cmd/vela-server/scm.go | 8 +- compiler/engine.go | 8 + compiler/native/compile.go | 13 + compiler/native/compile_test.go | 106 +++--- compiler/native/environment.go | 25 +- compiler/native/environment_test.go | 95 +++-- compiler/native/native.go | 33 +- compiler/native/script_test.go | 4 +- compiler/native/transform_test.go | 16 +- compiler/registry/github/github.go | 14 +- compiler/registry/github/template.go | 2 +- compiler/types/pipeline/git.go | 35 ++ compiler/types/pipeline/git_test.go | 31 ++ compiler/types/yaml/build.go | 3 + compiler/types/yaml/git.go | 28 ++ compiler/types/yaml/git_test.go | 60 ++++ constants/app_install.go | 20 ++ constants/event.go | 6 + database/integration_test.go | 2 + database/repo/create_test.go | 6 +- database/repo/table.go | 2 + database/repo/update_test.go | 6 +- database/testutils/api_resources.go | 1 + database/types/repo.go | 3 + database/types/repo_test.go | 3 + database/types/schedule_test.go | 5 +- internal/webhook.go | 22 +- mock/server/repo.go | 11 +- router/middleware/build/build_test.go | 1 + router/middleware/repo/repo_test.go | 1 + scm/flags.go | 25 ++ scm/github/access.go | 10 +- scm/github/app_install.go | 156 +++++++++ scm/github/app_permissions.go | 104 ++++++ scm/github/app_permissions_test.go | 195 +++++++++++ scm/github/app_transport.go | 331 ++++++++++++++++++ scm/github/app_transport_test.go | 169 +++++++++ scm/github/authentication.go | 2 +- scm/github/changeset.go | 4 +- scm/github/deployment.go | 8 +- scm/github/driver_test.go | 2 + scm/github/github.go | 183 +++++++--- scm/github/github_client.go | 159 +++++++++ scm/github/github_client_test.go | 128 +++++++ scm/github/github_test.go | 6 +- scm/github/opts.go | 53 ++- scm/github/opts_test.go | 76 +++- scm/github/org.go | 2 +- scm/github/repo.go | 175 ++++++++- scm/github/repo_test.go | 287 +++++++++++++++ .../testdata/hooks/installation_created.json | 100 ++++++ .../testdata/hooks/installation_deleted.json | 100 ++++++ .../installation_repositories_added.json | 103 ++++++ .../installation_repositories_removed.json | 103 ++++++ .../testdata/installation_repositories.json | 123 +++++++ scm/github/testdata/installations.json | 52 +++ .../testdata/installations_access_tokens.json | 134 +++++++ scm/github/user.go | 2 +- scm/github/webhook.go | 61 +++- scm/github/webhook_test.go | 199 ++++++++++- scm/scm.go | 7 +- scm/scm_test.go | 11 +- scm/service.go | 17 + scm/setup.go | 24 +- scm/setup_test.go | 29 +- 73 files changed, 3594 insertions(+), 285 deletions(-) create mode 100644 compiler/types/pipeline/git.go create mode 100644 compiler/types/pipeline/git_test.go create mode 100644 compiler/types/yaml/git.go create mode 100644 compiler/types/yaml/git_test.go create mode 100644 constants/app_install.go create mode 100644 scm/github/app_install.go create mode 100644 scm/github/app_permissions.go create mode 100644 scm/github/app_permissions_test.go create mode 100644 scm/github/app_transport.go create mode 100644 scm/github/app_transport_test.go create mode 100644 scm/github/github_client.go create mode 100644 scm/github/github_client_test.go create mode 100644 scm/github/testdata/hooks/installation_created.json create mode 100644 scm/github/testdata/hooks/installation_deleted.json create mode 100644 scm/github/testdata/hooks/installation_repositories_added.json create mode 100644 scm/github/testdata/hooks/installation_repositories_removed.json create mode 100644 scm/github/testdata/installation_repositories.json create mode 100644 scm/github/testdata/installations.json create mode 100644 scm/github/testdata/installations_access_tokens.json diff --git a/api/auth/get_token.go b/api/auth/get_token.go index 1379ccea3..6b4b4f26b 100644 --- a/api/auth/get_token.go +++ b/api/auth/get_token.go @@ -5,11 +5,13 @@ package auth import ( "fmt" "net/http" + "strconv" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database" "github.com/go-vela/server/internal/token" "github.com/go-vela/server/scm" @@ -36,6 +38,14 @@ import ( // name: redirect_uri // description: The URL where the user will be sent after authorization // type: string +// - in: query +// name: setup_action +// description: The specific setup action callback identifier +// type: string +// - in: query +// name: installation_id +// description: The specific installation identifier for a GitHub App integration +// type: integer // responses: // '200': // description: Successfully authenticated @@ -46,6 +56,10 @@ import ( // "$ref": "#/definitions/Token" // '307': // description: Redirected for authentication +// '400': +// description: Bad Request +// schema: +// "$ref": "#/definitions/Error" // '401': // description: Unauthorized // schema: @@ -69,6 +83,32 @@ func GetAuthToken(c *gin.Context) { // capture the OAuth state if present oAuthState := c.Request.FormValue("state") + // handle scm setup events + // setup_action==install represents the GitHub App installation callback redirect + if c.Request.FormValue("setup_action") == constants.AppInstallSetupActionInstall { + installID, err := strconv.ParseInt(c.Request.FormValue("installation_id"), 10, 0) + if err != nil { + retErr := fmt.Errorf("unable to parse installation_id: %w", err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + r, err := scm.FromContext(c).FinishInstallation(ctx, c.Request, installID) + if err != nil { + retErr := fmt.Errorf("unable to finish installation: %w", err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + + c.Redirect(http.StatusTemporaryRedirect, r) + + return + } + // capture the OAuth code if present code := c.Request.FormValue("code") if len(code) == 0 { diff --git a/api/build/compile_publish.go b/api/build/compile_publish.go index d355488d0..7167d1357 100644 --- a/api/build/compile_publish.go +++ b/api/build/compile_publish.go @@ -269,6 +269,8 @@ func CompileAndPublish( WithRepo(repo). WithUser(u). WithLabels(cfg.Labels). + WithSCM(scm). + WithDatabase(database). Compile(ctx, pipelineFile) if err != nil { // format the error message with extra information diff --git a/api/repo/create.go b/api/repo/create.go index 3e35be3f0..ae766c4c5 100644 --- a/api/repo/create.go +++ b/api/repo/create.go @@ -273,6 +273,18 @@ func CreateRepo(c *gin.Context) { } } + // map this repo to an installation if possible + if r.GetInstallID() == 0 { + r, err = scm.FromContext(c).SyncRepoWithInstallation(ctx, r) + if err != nil { + retErr := fmt.Errorf("unable to sync repo %s with installation: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + } + // if the repo exists but is inactive if len(dbRepo.GetOrg()) > 0 && !dbRepo.GetActive() { // update the repo owner @@ -281,6 +293,8 @@ func CreateRepo(c *gin.Context) { dbRepo.SetBranch(r.GetBranch()) // activate the repo dbRepo.SetActive(true) + // update the install_id + dbRepo.SetInstallID(r.GetInstallID()) // send API call to update the repo // NOTE: not logging modification out separately diff --git a/api/repo/repair.go b/api/repo/repair.go index f48ceea2c..bb15e2734 100644 --- a/api/repo/repair.go +++ b/api/repo/repair.go @@ -62,6 +62,8 @@ import ( // RepairRepo represents the API handler to remove // and then create a webhook for a repo. +// +//nolint:funlen // ignore statement count func RepairRepo(c *gin.Context) { // capture middleware values m := c.MustGet("metadata").(*internal.Metadata) @@ -163,21 +165,48 @@ func RepairRepo(c *gin.Context) { } } + dirty := false + // if the repo was previously inactive, mark it as active if !r.GetActive() { r.SetActive(true) - // send API call to update the repo + dirty = true + + l.Tracef("repo %s repaired - set to active", r.GetFullName()) + } + + // map this repo to an installation, if possible + if r.GetInstallID() == 0 { + r, err = scm.FromContext(c).SyncRepoWithInstallation(ctx, r) + if err != nil { + retErr := fmt.Errorf("unable to sync repo %s with installation: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + + // install_id was synced + if r.GetInstallID() != 0 { + dirty = true + + l.Tracef("repo %s repaired - set install_id to %d", r.GetFullName(), r.GetInstallID()) + } + } + + // update the repo in the database, if necessary + if dirty { _, err := database.FromContext(c).UpdateRepo(ctx, r) if err != nil { - retErr := fmt.Errorf("unable to set repo %s to active: %w", r.GetFullName(), err) + retErr := fmt.Errorf("unable to update repo %s during repair: %w", r.GetFullName(), err) util.HandleError(c, http.StatusInternalServerError, retErr) return } - l.Infof("repo %s updated - set to active", r.GetFullName()) + l.Infof("repo %s repaired - database updated", r.GetFullName()) } c.JSON(http.StatusOK, fmt.Sprintf("repo %s repaired", r.GetFullName())) diff --git a/api/scm/sync.go b/api/scm/sync.go index 66522510a..1f06ab370 100644 --- a/api/scm/sync.go +++ b/api/scm/sync.go @@ -174,5 +174,31 @@ func SyncRepo(c *gin.Context) { } } + // map this repo to an installation, if necessary + installID := r.GetInstallID() + + r, err = scm.FromContext(c).SyncRepoWithInstallation(ctx, r) + if err != nil { + retErr := fmt.Errorf("unable to sync repo %s with installation: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + + // install_id was synced + if r.GetInstallID() != installID { + _, err := database.FromContext(c).UpdateRepo(ctx, r) + if err != nil { + retErr := fmt.Errorf("unable to update repo %s during repair: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + + l.Tracef("repo %s install_id synced to %d", r.GetFullName(), r.GetInstallID()) + } + c.Status(http.StatusNoContent) } diff --git a/api/types/repo.go b/api/types/repo.go index bfad93623..b81a061e9 100644 --- a/api/types/repo.go +++ b/api/types/repo.go @@ -32,6 +32,7 @@ type Repo struct { PipelineType *string `json:"pipeline_type,omitempty"` PreviousName *string `json:"previous_name,omitempty"` ApproveBuild *string `json:"approve_build,omitempty"` + InstallID *int64 `json:"install_id,omitempty"` } // Environment returns a list of environment variables @@ -345,6 +346,19 @@ func (r *Repo) GetApproveBuild() string { return *r.ApproveBuild } +// GetInstallID returns the InstallID field. +// +// When the provided Repo type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *Repo) GetInstallID() int64 { + // return zero value if Repo type or InstallID field is nil + if r == nil || r.InstallID == nil { + return 0 + } + + return *r.InstallID +} + // SetID sets the ID field. // // When the provided Repo type is nil, it @@ -618,6 +632,19 @@ func (r *Repo) SetApproveBuild(v string) { r.ApproveBuild = &v } +// SetInstallID sets the InstallID field. +// +// When the provided Repo type is nil, it +// will set nothing and immediately return. +func (r *Repo) SetInstallID(v int64) { + // return if Repo type is nil + if r == nil { + return + } + + r.InstallID = &v +} + // String implements the Stringer interface for the Repo type. func (r *Repo) String() string { return fmt.Sprintf(`{ @@ -640,7 +667,8 @@ func (r *Repo) String() string { Timeout: %d, Topics: %s, Trusted: %t, - Visibility: %s + Visibility: %s, + InstallID: %d }`, r.GetActive(), r.GetAllowEvents().List(), @@ -662,5 +690,6 @@ func (r *Repo) String() string { r.GetTopics(), r.GetTrusted(), r.GetVisibility(), + r.GetInstallID(), ) } diff --git a/api/types/repo_test.go b/api/types/repo_test.go index c7e20407d..fa8fb2970 100644 --- a/api/types/repo_test.go +++ b/api/types/repo_test.go @@ -303,7 +303,8 @@ func TestTypes_Repo_String(t *testing.T) { Timeout: %d, Topics: %s, Trusted: %t, - Visibility: %s + Visibility: %s, + InstallID: %d }`, r.GetActive(), r.GetAllowEvents().List(), @@ -325,6 +326,7 @@ func TestTypes_Repo_String(t *testing.T) { r.GetTopics(), r.GetTrusted(), r.GetVisibility(), + r.GetInstallID(), ) // run test diff --git a/api/webhook/post.go b/api/webhook/post.go index 52f0d90dd..8ff5b4cc2 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -84,6 +84,7 @@ func PostWebhook(c *gin.Context) { // capture middleware values m := c.MustGet("metadata").(*internal.Metadata) l := c.MustGet("logger").(*logrus.Entry) + db := database.FromContext(c) ctx := c.Request.Context() l.Debug("webhook received") @@ -133,6 +134,20 @@ func PostWebhook(c *gin.Context) { return } + if webhook.Installation != nil { + err = scm.FromContext(c).ProcessInstallation(ctx, c.Request, webhook, db) + if err != nil { + retErr := fmt.Errorf("unable to process installation: %w", err) + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + c.JSON(http.StatusOK, "installation processed successfully") + + return + } + // check if the hook should be skipped if skip, skipReason := webhook.ShouldSkip(); skip { c.JSON(http.StatusOK, fmt.Sprintf("skipping build: %s", skipReason)) @@ -145,8 +160,6 @@ func PostWebhook(c *gin.Context) { l.Debugf("hook generated from SCM: %v", h) l.Debugf("repo generated from SCM: %v", r) - db := database.FromContext(c) - // if event is repository event, handle separately and return if strings.EqualFold(h.GetEvent(), constants.EventRepository) { r, err = handleRepositoryEvent(ctx, l, db, m, h, r) diff --git a/cmd/vela-server/scm.go b/cmd/vela-server/scm.go index 7124761f0..2efa53bb5 100644 --- a/cmd/vela-server/scm.go +++ b/cmd/vela-server/scm.go @@ -20,16 +20,20 @@ func setupSCM(c *cli.Context, tc *tracing.Client) (scm.Service, error) { Address: c.String("scm.addr"), ClientID: c.String("scm.client"), ClientSecret: c.String("scm.secret"), + AppID: c.Int64("scm.app.id"), + AppPrivateKey: c.String("scm.app.private-key"), + AppPrivateKeyPath: c.String("scm.app.private-key.path"), + AppPermissions: c.StringSlice("scm.app.permissions"), ServerAddress: c.String("server-addr"), ServerWebhookAddress: c.String("scm.webhook.addr"), StatusContext: c.String("scm.context"), WebUIAddress: c.String("webui-addr"), - Scopes: c.StringSlice("scm.scopes"), + OAuthScopes: c.StringSlice("scm.scopes"), Tracing: tc, } // setup the scm // // https://pkg.go.dev/github.com/go-vela/server/scm?tab=doc#New - return scm.New(_setup) + return scm.New(c.Context, _setup) } diff --git a/compiler/engine.go b/compiler/engine.go index 31f85852e..ab4555296 100644 --- a/compiler/engine.go +++ b/compiler/engine.go @@ -10,7 +10,9 @@ import ( "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/database" "github.com/go-vela/server/internal" + "github.com/go-vela/server/scm" ) // Engine represents an interface for converting a yaml @@ -146,6 +148,12 @@ type Engine interface { // WithLabel defines a function that sets // the label(s) in the Engine. WithLabels([]string) Engine + // WithSCM defines a function that sets + // the scm in the Engine. + WithSCM(scm.Service) Engine + // WithDatabase defines a function that sets + // the database in the Engine. + WithDatabase(database.Interface) Engine // WithPrivateGitHub defines a function that sets // the private github client in the Engine. WithPrivateGitHub(context.Context, string, string) Engine diff --git a/compiler/native/compile.go b/compiler/native/compile.go index 313e24db3..04740f412 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -44,6 +44,19 @@ func (c *client) Compile(ctx context.Context, v interface{}) (*pipeline.Build, * return nil, nil, err } + // create the netrc using the scm + // this has to occur after Parse because the scm configurations might be set in yaml + // netrc can be provided directly using WithNetrc for situations like local exec + if c.netrc == nil && c.scm != nil { + // get the netrc password from the scm + netrc, err := c.scm.GetNetrcPassword(ctx, c.db, c.repo, c.user, p.Git) + if err != nil { + return nil, nil, err + } + + c.WithNetrc(netrc) + } + // create the API pipeline object from the yaml configuration _pipeline := p.ToPipelineAPI() _pipeline.SetData(data) diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index 4b0214d6f..e887176d7 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -54,21 +54,21 @@ func TestNative_Compile_StagesPipeline(t *testing.T) { }, } - initEnv := environment(nil, m, nil, nil) + initEnv := environment(nil, m, nil, nil, nil) initEnv["HELLO"] = "Hello, Global Environment" - stageEnvInstall := environment(nil, m, nil, nil) + stageEnvInstall := environment(nil, m, nil, nil, nil) stageEnvInstall["HELLO"] = "Hello, Global Environment" stageEnvInstall["GRADLE_USER_HOME"] = ".gradle" - stageEnvTest := environment(nil, m, nil, nil) + stageEnvTest := environment(nil, m, nil, nil, nil) stageEnvTest["HELLO"] = "Hello, Global Environment" stageEnvTest["GRADLE_USER_HOME"] = "willBeOverwrittenInStep" - cloneEnv := environment(nil, m, nil, nil) + cloneEnv := environment(nil, m, nil, nil, nil) cloneEnv["HELLO"] = "Hello, Global Environment" - installEnv := environment(nil, m, nil, nil) + installEnv := environment(nil, m, nil, nil, nil) installEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" installEnv["GRADLE_USER_HOME"] = ".gradle" installEnv["HOME"] = "/root" @@ -76,7 +76,7 @@ func TestNative_Compile_StagesPipeline(t *testing.T) { installEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew downloadDependencies"}) installEnv["HELLO"] = "Hello, Global Environment" - testEnv := environment(nil, m, nil, nil) + testEnv := environment(nil, m, nil, nil, nil) testEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" testEnv["GRADLE_USER_HOME"] = ".gradle" testEnv["HOME"] = "/root" @@ -84,7 +84,7 @@ func TestNative_Compile_StagesPipeline(t *testing.T) { testEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew check"}) testEnv["HELLO"] = "Hello, Global Environment" - buildEnv := environment(nil, m, nil, nil) + buildEnv := environment(nil, m, nil, nil, nil) buildEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" buildEnv["GRADLE_USER_HOME"] = ".gradle" buildEnv["HOME"] = "/root" @@ -92,7 +92,7 @@ func TestNative_Compile_StagesPipeline(t *testing.T) { buildEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew build"}) buildEnv["HELLO"] = "Hello, Global Environment" - dockerEnv := environment(nil, m, nil, nil) + dockerEnv := environment(nil, m, nil, nil, nil) dockerEnv["PARAMETER_REGISTRY"] = "index.docker.io" dockerEnv["PARAMETER_REPO"] = "github/octocat" dockerEnv["PARAMETER_TAGS"] = "latest,dev" @@ -479,13 +479,13 @@ func TestNative_Compile_StepsPipeline(t *testing.T) { }, } - initEnv := environment(nil, m, nil, nil) + initEnv := environment(nil, m, nil, nil, nil) initEnv["HELLO"] = "Hello, Global Environment" - cloneEnv := environment(nil, m, nil, nil) + cloneEnv := environment(nil, m, nil, nil, nil) cloneEnv["HELLO"] = "Hello, Global Environment" - installEnv := environment(nil, m, nil, nil) + installEnv := environment(nil, m, nil, nil, nil) installEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" installEnv["GRADLE_USER_HOME"] = ".gradle" installEnv["HOME"] = "/root" @@ -493,7 +493,7 @@ func TestNative_Compile_StepsPipeline(t *testing.T) { installEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew downloadDependencies"}) installEnv["HELLO"] = "Hello, Global Environment" - testEnv := environment(nil, m, nil, nil) + testEnv := environment(nil, m, nil, nil, nil) testEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" testEnv["GRADLE_USER_HOME"] = ".gradle" testEnv["HOME"] = "/root" @@ -501,7 +501,7 @@ func TestNative_Compile_StepsPipeline(t *testing.T) { testEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew check"}) testEnv["HELLO"] = "Hello, Global Environment" - buildEnv := environment(nil, m, nil, nil) + buildEnv := environment(nil, m, nil, nil, nil) buildEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" buildEnv["GRADLE_USER_HOME"] = ".gradle" buildEnv["HOME"] = "/root" @@ -509,7 +509,7 @@ func TestNative_Compile_StepsPipeline(t *testing.T) { buildEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"./gradlew build"}) buildEnv["HELLO"] = "Hello, Global Environment" - dockerEnv := environment(nil, m, nil, nil) + dockerEnv := environment(nil, m, nil, nil, nil) dockerEnv["PARAMETER_REGISTRY"] = "index.docker.io" dockerEnv["PARAMETER_REPO"] = "github/octocat" dockerEnv["PARAMETER_TAGS"] = "latest,dev" @@ -690,11 +690,11 @@ func TestNative_Compile_StagesPipelineTemplate(t *testing.T) { }, } - setupEnv := environment(nil, m, nil, nil) + setupEnv := environment(nil, m, nil, nil, nil) setupEnv["bar"] = "test4" setupEnv["star"] = "test3" - installEnv := environment(nil, m, nil, nil) + installEnv := environment(nil, m, nil, nil, nil) installEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" installEnv["GRADLE_USER_HOME"] = ".gradle" installEnv["HOME"] = "/root" @@ -703,7 +703,7 @@ func TestNative_Compile_StagesPipelineTemplate(t *testing.T) { installEnv["bar"] = "test4" installEnv["star"] = "test3" - testEnv := environment(nil, m, nil, nil) + testEnv := environment(nil, m, nil, nil, nil) testEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" testEnv["GRADLE_USER_HOME"] = ".gradle" testEnv["HOME"] = "/root" @@ -712,7 +712,7 @@ func TestNative_Compile_StagesPipelineTemplate(t *testing.T) { testEnv["bar"] = "test4" testEnv["star"] = "test3" - buildEnv := environment(nil, m, nil, nil) + buildEnv := environment(nil, m, nil, nil, nil) buildEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" buildEnv["GRADLE_USER_HOME"] = ".gradle" buildEnv["HOME"] = "/root" @@ -721,14 +721,14 @@ func TestNative_Compile_StagesPipelineTemplate(t *testing.T) { buildEnv["bar"] = "test4" buildEnv["star"] = "test3" - dockerEnv := environment(nil, m, nil, nil) + dockerEnv := environment(nil, m, nil, nil, nil) dockerEnv["PARAMETER_REGISTRY"] = "index.docker.io" dockerEnv["PARAMETER_REPO"] = "github/octocat" dockerEnv["PARAMETER_TAGS"] = "latest,dev" dockerEnv["bar"] = "test4" dockerEnv["star"] = "test3" - serviceEnv := environment(nil, m, nil, nil) + serviceEnv := environment(nil, m, nil, nil, nil) serviceEnv["bar"] = "test4" serviceEnv["star"] = "test3" @@ -961,11 +961,11 @@ func TestNative_Compile_StepsPipelineTemplate(t *testing.T) { }, } - setupEnv := environment(nil, m, nil, nil) + setupEnv := environment(nil, m, nil, nil, nil) setupEnv["bar"] = "test4" setupEnv["star"] = "test3" - installEnv := environment(nil, m, nil, nil) + installEnv := environment(nil, m, nil, nil, nil) installEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" installEnv["GRADLE_USER_HOME"] = ".gradle" installEnv["HOME"] = "/root" @@ -974,7 +974,7 @@ func TestNative_Compile_StepsPipelineTemplate(t *testing.T) { installEnv["bar"] = "test4" installEnv["star"] = "test3" - testEnv := environment(nil, m, nil, nil) + testEnv := environment(nil, m, nil, nil, nil) testEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" testEnv["GRADLE_USER_HOME"] = ".gradle" testEnv["HOME"] = "/root" @@ -983,7 +983,7 @@ func TestNative_Compile_StepsPipelineTemplate(t *testing.T) { testEnv["bar"] = "test4" testEnv["star"] = "test3" - buildEnv := environment(nil, m, nil, nil) + buildEnv := environment(nil, m, nil, nil, nil) buildEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" buildEnv["GRADLE_USER_HOME"] = ".gradle" buildEnv["HOME"] = "/root" @@ -992,14 +992,14 @@ func TestNative_Compile_StepsPipelineTemplate(t *testing.T) { buildEnv["bar"] = "test4" buildEnv["star"] = "test3" - dockerEnv := environment(nil, m, nil, nil) + dockerEnv := environment(nil, m, nil, nil, nil) dockerEnv["PARAMETER_REGISTRY"] = "index.docker.io" dockerEnv["PARAMETER_REPO"] = "github/octocat" dockerEnv["PARAMETER_TAGS"] = "latest,dev" dockerEnv["bar"] = "test4" dockerEnv["star"] = "test3" - serviceEnv := environment(nil, m, nil, nil) + serviceEnv := environment(nil, m, nil, nil, nil) serviceEnv["bar"] = "test4" serviceEnv["star"] = "test3" @@ -1195,9 +1195,9 @@ func TestNative_Compile_StepsPipelineTemplate_VelaFunction_TemplateName(t *testi }, } - setupEnv := environment(nil, m, nil, nil) + setupEnv := environment(nil, m, nil, nil, nil) - helloEnv := environment(nil, m, nil, nil) + helloEnv := environment(nil, m, nil, nil, nil) helloEnv["HOME"] = "/root" helloEnv["SHELL"] = "/bin/sh" helloEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"echo sample"}) @@ -1316,9 +1316,9 @@ func TestNative_Compile_StepsPipelineTemplate_VelaFunction_TemplateName_Inline(t }, } - setupEnv := environment(nil, m, nil, nil) + setupEnv := environment(nil, m, nil, nil, nil) - helloEnv := environment(nil, m, nil, nil) + helloEnv := environment(nil, m, nil, nil, nil) helloEnv["HOME"] = "/root" helloEnv["SHELL"] = "/bin/sh" helloEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"echo inline_templatename"}) @@ -1436,11 +1436,11 @@ func TestNative_Compile_InvalidType(t *testing.T) { }, } - gradleEnv := environment(nil, m, nil, nil) + gradleEnv := environment(nil, m, nil, nil, nil) gradleEnv["GRADLE_OPTS"] = "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false" gradleEnv["GRADLE_USER_HOME"] = ".gradle" - dockerEnv := environment(nil, m, nil, nil) + dockerEnv := environment(nil, m, nil, nil, nil) dockerEnv["PARAMETER_REGISTRY"] = "index.docker.io" dockerEnv["PARAMETER_REPO"] = "github/octocat" dockerEnv["PARAMETER_TAGS"] = "latest,dev" @@ -1493,10 +1493,10 @@ func TestNative_Compile_Clone(t *testing.T) { }, } - fooEnv := environment(nil, m, nil, nil) + fooEnv := environment(nil, m, nil, nil, nil) fooEnv["PARAMETER_REGISTRY"] = "foo" - cloneEnv := environment(nil, m, nil, nil) + cloneEnv := environment(nil, m, nil, nil, nil) cloneEnv["PARAMETER_DEPTH"] = "5" wantFalse := &pipeline.Build{ @@ -1512,7 +1512,7 @@ func TestNative_Compile_Clone(t *testing.T) { &pipeline.Container{ ID: "step___0_init", Directory: "/vela/src/foo//", - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: "#init", Name: "init", Number: 1, @@ -1543,7 +1543,7 @@ func TestNative_Compile_Clone(t *testing.T) { &pipeline.Container{ ID: "step___0_init", Directory: "/vela/src/foo//", - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: "#init", Name: "init", Number: 1, @@ -1552,7 +1552,7 @@ func TestNative_Compile_Clone(t *testing.T) { &pipeline.Container{ ID: "step___0_clone", Directory: "/vela/src/foo//", - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: defaultCloneImage, Name: "clone", Number: 2, @@ -1583,7 +1583,7 @@ func TestNative_Compile_Clone(t *testing.T) { &pipeline.Container{ ID: "step___0_init", Directory: "/vela/src/foo//", - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: "#init", Name: "init", Number: 1, @@ -1687,10 +1687,10 @@ func TestNative_Compile_Pipeline_Type(t *testing.T) { }, } - defaultFooEnv := environment(nil, m, nil, nil) + defaultFooEnv := environment(nil, m, nil, nil, nil) defaultFooEnv["PARAMETER_REGISTRY"] = "foo" - defaultEnv := environment(nil, m, nil, nil) + defaultEnv := environment(nil, m, nil, nil, nil) wantDefault := &pipeline.Build{ Version: "1", ID: "__0", @@ -1733,10 +1733,10 @@ func TestNative_Compile_Pipeline_Type(t *testing.T) { goPipelineType := "go" - goFooEnv := environment(nil, m, &api.Repo{PipelineType: &goPipelineType}, nil) + goFooEnv := environment(nil, m, &api.Repo{PipelineType: &goPipelineType}, nil, nil) goFooEnv["PARAMETER_REGISTRY"] = "foo" - defaultGoEnv := environment(nil, m, &api.Repo{PipelineType: &goPipelineType}, nil) + defaultGoEnv := environment(nil, m, &api.Repo{PipelineType: &goPipelineType}, nil, nil) wantGo := &pipeline.Build{ Version: "1", ID: "__0", @@ -1779,10 +1779,10 @@ func TestNative_Compile_Pipeline_Type(t *testing.T) { starPipelineType := "starlark" - starlarkFooEnv := environment(nil, m, &api.Repo{PipelineType: &starPipelineType}, nil) + starlarkFooEnv := environment(nil, m, &api.Repo{PipelineType: &starPipelineType}, nil, nil) starlarkFooEnv["PARAMETER_REGISTRY"] = "foo" - defaultStarlarkEnv := environment(nil, m, &api.Repo{PipelineType: &starPipelineType}, nil) + defaultStarlarkEnv := environment(nil, m, &api.Repo{PipelineType: &starPipelineType}, nil, nil) wantStarlark := &pipeline.Build{ Version: "1", ID: "__0", @@ -2039,13 +2039,13 @@ func Test_client_modifyConfig(t *testing.T) { }, Steps: yaml.StepSlice{ &yaml.Step{ - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: "#init", Name: "init", Pull: "not_present", }, &yaml.Step{ - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: defaultCloneImage, Name: "clone", Pull: "not_present", @@ -2072,13 +2072,13 @@ func Test_client_modifyConfig(t *testing.T) { }, Steps: yaml.StepSlice{ &yaml.Step{ - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: "#init", Name: "init", Pull: "not_present", }, &yaml.Step{ - Environment: environment(nil, m, nil, nil), + Environment: environment(nil, m, nil, nil, nil), Image: defaultCloneImage, Name: "clone", Pull: "not_present", @@ -2255,7 +2255,7 @@ func convertFileToGithubResponse(file string) (github.RepositoryContent, error) } func generateTestEnv(command string, m *internal.Metadata, pipelineType string) map[string]string { - output := environment(nil, m, nil, nil) + output := environment(nil, m, nil, nil, nil) output["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{command}) output["HOME"] = "/root" output["SHELL"] = "/bin/sh" @@ -2312,15 +2312,15 @@ func Test_Compile_Inline(t *testing.T) { }, } - initEnv := environment(nil, m, nil, nil) - testEnv := environment(nil, m, nil, nil) + initEnv := environment(nil, m, nil, nil, nil) + testEnv := environment(nil, m, nil, nil, nil) testEnv["FOO"] = "Hello, foo!" testEnv["HELLO"] = "Hello, Vela!" - stepEnv := environment(nil, m, nil, nil) + stepEnv := environment(nil, m, nil, nil, nil) stepEnv["FOO"] = "Hello, foo!" stepEnv["HELLO"] = "Hello, Vela!" stepEnv["PARAMETER_FIRST"] = "foo" - golangEnv := environment(nil, m, nil, nil) + golangEnv := environment(nil, m, nil, nil, nil) golangEnv["VELA_REPO_PIPELINE_TYPE"] = "go" type args struct { diff --git a/compiler/native/environment.go b/compiler/native/environment.go index fae7557d1..3f3162137 100644 --- a/compiler/native/environment.go +++ b/compiler/native/environment.go @@ -33,8 +33,9 @@ func (c *client) EnvironmentStages(s yaml.StageSlice, globalEnv raw.StringSliceM func (c *client) EnvironmentStage(s *yaml.Stage, globalEnv raw.StringSliceMap) (*yaml.Stage, error) { // make empty map of environment variables env := make(map[string]string) + // gather set of default environment variables - defaultEnv := environment(c.build, c.metadata, c.repo, c.user) + defaultEnv := environment(c.build, c.metadata, c.repo, c.user, c.netrc) // inject the declared global environment // WARNING: local env can override global @@ -87,8 +88,9 @@ func (c *client) EnvironmentSteps(s yaml.StepSlice, stageEnv raw.StringSliceMap) func (c *client) EnvironmentStep(s *yaml.Step, stageEnv raw.StringSliceMap) (*yaml.Step, error) { // make empty map of environment variables env := make(map[string]string) + // gather set of default environment variables - defaultEnv := environment(c.build, c.metadata, c.repo, c.user) + defaultEnv := environment(c.build, c.metadata, c.repo, c.user, c.netrc) // inject the declared stage environment // WARNING: local env can override global + stage @@ -148,8 +150,9 @@ func (c *client) EnvironmentServices(s yaml.ServiceSlice, globalEnv raw.StringSl for _, service := range s { // make empty map of environment variables env := make(map[string]string) + // gather set of default environment variables - defaultEnv := environment(c.build, c.metadata, c.repo, c.user) + defaultEnv := environment(c.build, c.metadata, c.repo, c.user, c.netrc) // inject the declared global environment // WARNING: local env can override global @@ -188,8 +191,9 @@ func (c *client) EnvironmentSecrets(s yaml.SecretSlice, globalEnv raw.StringSlic // make empty map of environment variables env := make(map[string]string) + // gather set of default environment variables - defaultEnv := environment(c.build, c.metadata, c.repo, c.user) + defaultEnv := environment(c.build, c.metadata, c.repo, c.user, c.netrc) // inject the declared global environment // WARNING: local env can override global @@ -243,11 +247,14 @@ func (c *client) EnvironmentSecrets(s yaml.SecretSlice, globalEnv raw.StringSlic return s, nil } +// EnvironmentBuild injects environment variables +// for the build in a yaml configuration. func (c *client) EnvironmentBuild() map[string]string { // make empty map of environment variables env := make(map[string]string) + // gather set of default environment variables - defaultEnv := environment(c.build, c.metadata, c.repo, c.user) + defaultEnv := environment(c.build, c.metadata, c.repo, c.user, c.netrc) // inject the default environment // variables to the build @@ -281,7 +288,7 @@ func appendMap(originalMap, otherMap map[string]string) map[string]string { } // helper function that creates the standard set of environment variables for a pipeline. -func environment(b *api.Build, m *internal.Metadata, r *api.Repo, u *api.User) map[string]string { +func environment(b *api.Build, m *internal.Metadata, r *api.Repo, u *api.User, netrc *string) map[string]string { // set default workspace workspace := constants.WorkspaceDefault notImplemented := "TODO" @@ -297,7 +304,7 @@ func environment(b *api.Build, m *internal.Metadata, r *api.Repo, u *api.User) m env["VELA_DISTRIBUTION"] = notImplemented env["VELA_HOST"] = notImplemented env["VELA_NETRC_MACHINE"] = notImplemented - env["VELA_NETRC_PASSWORD"] = u.GetToken() + env["VELA_NETRC_PASSWORD"] = notImplemented env["VELA_NETRC_USERNAME"] = "x-oauth-basic" env["VELA_QUEUE"] = notImplemented env["VELA_RUNTIME"] = notImplemented @@ -321,6 +328,10 @@ func environment(b *api.Build, m *internal.Metadata, r *api.Repo, u *api.User) m workspace = fmt.Sprintf("%s/%s/%s/%s", workspace, m.Source.Host, r.GetOrg(), r.GetName()) } + if netrc != nil { + env["VELA_NETRC_PASSWORD"] = *netrc + } + env["VELA_WORKSPACE"] = workspace // populate environment variables from repo api diff --git a/compiler/native/environment_test.go b/compiler/native/environment_test.go index b9ddcdec0..17dce3942 100644 --- a/compiler/native/environment_test.go +++ b/compiler/native/environment_test.go @@ -42,7 +42,7 @@ func TestNative_EnvironmentStages(t *testing.T) { }, } - env := environment(nil, nil, nil, nil) + env := environment(nil, nil, nil, nil, nil) env["HELLO"] = "Hello, Global Message" want := yaml.StageSlice{ @@ -174,7 +174,7 @@ func TestNative_EnvironmentSteps(t *testing.T) { "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "TODO", "VELA_NETRC_MACHINE": "TODO", - "VELA_NETRC_PASSWORD": "", + "VELA_NETRC_PASSWORD": "TODO", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "TODO", "VELA_REPO_ACTIVE": "false", @@ -351,7 +351,7 @@ func TestNative_EnvironmentServices(t *testing.T) { "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "TODO", "VELA_NETRC_MACHINE": "TODO", - "VELA_NETRC_PASSWORD": "", + "VELA_NETRC_PASSWORD": "TODO", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "TODO", "VELA_REPO_ACTIVE": "false", @@ -510,7 +510,7 @@ func TestNative_EnvironmentSecrets(t *testing.T) { "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "TODO", "VELA_NETRC_MACHINE": "TODO", - "VELA_NETRC_PASSWORD": "", + "VELA_NETRC_PASSWORD": "TODO", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "TODO", "VELA_REPO_ACTIVE": "false", @@ -580,56 +580,73 @@ func TestNative_environment(t *testing.T) { // deployment deploy := "deployment" target := "production" + // netrc + netrc := "foo" tests := []struct { - w string - b *api.Build - m *internal.Metadata - r *api.Repo - u *api.User - want map[string]string + w string + b *api.Build + m *internal.Metadata + r *api.Repo + u *api.User + netrc *string + want map[string]string }{ // push { - w: workspace, - b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &push, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &str, BaseRef: &str}, - m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, - r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, - u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "push", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "foo", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "push", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "foo", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + w: workspace, + b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &push, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &str, BaseRef: &str}, + m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, + r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, + u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "push", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "foo", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "push", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "foo", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // tag { - w: workspace, - b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &tag, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &tagref, BaseRef: &str}, - m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, - r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, - u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + w: workspace, + b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &tag, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &tagref, BaseRef: &str}, + m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, + r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, + u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // pull_request { - w: workspace, - b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &pull, EventAction: &pullact, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Fork: &booL, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, - m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, - r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, - u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + w: workspace, + b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &pull, EventAction: &pullact, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, + m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, + r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, + u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // deployment { - w: workspace, - b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &deploy, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &target, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, - m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, - r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, - u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + w: workspace, + b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &deploy, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &target, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, + m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, + r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, + u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + }, + // netrc + { + w: workspace, + b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &deploy, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &target, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, + m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, + r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, + u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: nil, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "TODO", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, } // run test for _, test := range tests { - got := environment(test.b, test.m, test.r, test.u) + got := environment(test.b, test.m, test.r, test.u, test.netrc) if diff := cmp.Diff(test.want, got); diff != "" { t.Errorf("environment mismatch (-want +got):\n%s", diff) @@ -694,12 +711,15 @@ func Test_client_EnvironmentBuild(t *testing.T) { // deployment deploy := "deployment" target := "production" + // netrc + netrc := "foo" type fields struct { build *api.Build metadata *internal.Metadata repo *api.Repo user *api.User + netrc *string } tests := []struct { @@ -712,12 +732,14 @@ func Test_client_EnvironmentBuild(t *testing.T) { metadata: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "push", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "foo", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "push", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "foo", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}}, {"tag", fields{ build: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &tag, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &tagref, BaseRef: &str}, metadata: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, {"pull_request", fields{ @@ -725,6 +747,7 @@ func Test_client_EnvironmentBuild(t *testing.T) { metadata: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, {"deployment", fields{ @@ -732,6 +755,7 @@ func Test_client_EnvironmentBuild(t *testing.T) { metadata: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, + netrc: &netrc, }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, } @@ -742,6 +766,7 @@ func Test_client_EnvironmentBuild(t *testing.T) { metadata: tt.fields.metadata, repo: tt.fields.repo, user: tt.fields.user, + netrc: tt.fields.netrc, } got := c.EnvironmentBuild() if diff := cmp.Diff(got, tt.want); diff != "" { diff --git a/compiler/native/native.go b/compiler/native/native.go index 1b36e39fb..4a10aae2f 100644 --- a/compiler/native/native.go +++ b/compiler/native/native.go @@ -15,8 +15,10 @@ import ( "github.com/go-vela/server/compiler" "github.com/go-vela/server/compiler/registry" "github.com/go-vela/server/compiler/registry/github" + "github.com/go-vela/server/database" "github.com/go-vela/server/internal" "github.com/go-vela/server/internal/image" + "github.com/go-vela/server/scm" ) type ModificationConfig struct { @@ -27,9 +29,10 @@ type ModificationConfig struct { } type client struct { - Github registry.Service - PrivateGithub registry.Service - UsePrivateGithub bool + Github registry.Service + PrivateGithub registry.Service + UsePrivateGithub bool + ModificationService ModificationConfig TemplateCache map[string][]byte @@ -45,6 +48,9 @@ type client struct { repo *api.Repo user *api.User labels []string + db database.Interface + scm scm.Service + netrc *string } // FromCLIContext returns a Pipeline implementation that integrates with the supported registries. @@ -235,3 +241,24 @@ func (c *client) WithLabels(labels []string) compiler.Engine { return c } + +// WithNetrc sets the netrc in the Engine. +func (c *client) WithNetrc(n string) compiler.Engine { + c.netrc = &n + + return c +} + +// WithSCM sets the scm in the Engine. +func (c *client) WithSCM(_scm scm.Service) compiler.Engine { + c.scm = _scm + + return c +} + +// WithDatabase sets the database in the Engine. +func (c *client) WithDatabase(db database.Interface) compiler.Engine { + c.db = db + + return c +} diff --git a/compiler/native/script_test.go b/compiler/native/script_test.go index e12e6a4b8..410c297c0 100644 --- a/compiler/native/script_test.go +++ b/compiler/native/script_test.go @@ -19,7 +19,7 @@ func TestNative_ScriptStages(t *testing.T) { set.String("clone-image", defaultCloneImage, "doc") c := cli.NewContext(nil, set, nil) - baseEnv := environment(nil, nil, nil, nil) + baseEnv := environment(nil, nil, nil, nil, nil) s := yaml.StageSlice{ &yaml.Stage{ @@ -109,7 +109,7 @@ func TestNative_ScriptSteps(t *testing.T) { set.String("clone-image", defaultCloneImage, "doc") c := cli.NewContext(nil, set, nil) - emptyEnv := environment(nil, nil, nil, nil) + emptyEnv := environment(nil, nil, nil, nil, nil) baseEnv := emptyEnv baseEnv["HOME"] = "/root" diff --git a/compiler/native/transform_test.go b/compiler/native/transform_test.go index 14da2b332..d2906bc8b 100644 --- a/compiler/native/transform_test.go +++ b/compiler/native/transform_test.go @@ -59,7 +59,7 @@ func TestNative_TransformStages(t *testing.T) { Steps: yaml.StepSlice{ &yaml.Step{ Commands: []string{"./gradlew downloadDependencies"}, - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "install", Pull: "always", @@ -72,7 +72,7 @@ func TestNative_TransformStages(t *testing.T) { Steps: yaml.StepSlice{ &yaml.Step{ Commands: []string{"./gradlew check"}, - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "test", Pull: "always", @@ -138,7 +138,7 @@ func TestNative_TransformStages(t *testing.T) { ID: "__0_install deps_install", Commands: []string{"./gradlew downloadDependencies"}, Directory: "/vela/src", - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "install", Number: 1, @@ -194,7 +194,7 @@ func TestNative_TransformStages(t *testing.T) { ID: "localOrg_localRepo_1_install deps_install", Commands: []string{"./gradlew downloadDependencies"}, Directory: "/vela/src", - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "install", Number: 1, @@ -297,14 +297,14 @@ func TestNative_TransformSteps(t *testing.T) { Steps: yaml.StepSlice{ &yaml.Step{ Commands: []string{"./gradlew downloadDependencies"}, - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "install deps", Pull: "always", }, &yaml.Step{ Commands: []string{"./gradlew check"}, - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "test", Pull: "always", @@ -365,7 +365,7 @@ func TestNative_TransformSteps(t *testing.T) { ID: "step___0_install deps", Commands: []string{"./gradlew downloadDependencies"}, Directory: "/vela/src", - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "install deps", Number: 1, @@ -416,7 +416,7 @@ func TestNative_TransformSteps(t *testing.T) { ID: "step_localOrg_localRepo_1_install deps", Commands: []string{"./gradlew downloadDependencies"}, Directory: "/vela/src", - Environment: environment(nil, nil, nil, nil), + Environment: environment(nil, nil, nil, nil, nil), Image: "openjdk:latest", Name: "install deps", Number: 1, diff --git a/compiler/registry/github/github.go b/compiler/registry/github/github.go index 09924393c..f5027a5b5 100644 --- a/compiler/registry/github/github.go +++ b/compiler/registry/github/github.go @@ -50,7 +50,7 @@ func New(ctx context.Context, address, token string) (*client, error) { if len(token) > 0 { // create GitHub OAuth client with user's token - gitClient = c.newClientToken(ctx, token) + gitClient = c.newOAuthTokenClient(ctx, token) } // overwrite the github client @@ -59,8 +59,8 @@ func New(ctx context.Context, address, token string) (*client, error) { return c, nil } -// newClientToken is a helper function to return the GitHub oauth2 client. -func (c *client) newClientToken(ctx context.Context, token string) *github.Client { +// newOAuthTokenClient is a helper function to return the GitHub oauth2 client. +func (c *client) newOAuthTokenClient(ctx context.Context, token string) *github.Client { // create the token object for the client ts := oauth2.StaticTokenSource( &oauth2.Token{AccessToken: token}, @@ -68,14 +68,6 @@ func (c *client) newClientToken(ctx context.Context, token string) *github.Clien // create the OAuth client tc := oauth2.NewClient(ctx, ts) - // if c.SkipVerify { - // tc.Transport.(*oauth2.Transport).Base = &http.Transport{ - // Proxy: http.ProxyFromEnvironment, - // TLSClientConfig: &tls.Config{ - // InsecureSkipVerify: true, - // }, - // } - // } // create the GitHub client from the OAuth client github := github.NewClient(tc) diff --git a/compiler/registry/github/template.go b/compiler/registry/github/template.go index 44bc0d1e8..5e1cd927b 100644 --- a/compiler/registry/github/template.go +++ b/compiler/registry/github/template.go @@ -19,7 +19,7 @@ func (c *client) Template(ctx context.Context, u *api.User, s *registry.Source) cli := c.Github if u != nil { // create GitHub OAuth client with user's token - cli = c.newClientToken(ctx, u.GetToken()) + cli = c.newOAuthTokenClient(ctx, u.GetToken()) } // create the options to pass diff --git a/compiler/types/pipeline/git.go b/compiler/types/pipeline/git.go new file mode 100644 index 000000000..a7628abf9 --- /dev/null +++ b/compiler/types/pipeline/git.go @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 + +package pipeline + +// Git is the pipeline representation of git configurations for a pipeline. +// +// swagger:model PipelineGit +type Git struct { + Token *Token `json:"token,omitempty" yaml:"token,omitempty"` +} + +// Token is the pipeline representation of git token access configurations for a pipeline. +// +// swagger:model PipelineGitToken +type Token struct { + Repositories []string `json:"repositories,omitempty" yaml:"repositories,omitempty"` + Permissions map[string]string `json:"permissions,omitempty" yaml:"permissions,omitempty"` +} + +// Empty returns true if the provided struct is empty. +func (g *Git) Empty() bool { + // return false if any of the fields are provided + if g.Token != nil { + if g.Token.Repositories != nil { + return false + } + + if g.Token.Permissions != nil { + return false + } + } + + // return true if all fields are empty + return true +} diff --git a/compiler/types/pipeline/git_test.go b/compiler/types/pipeline/git_test.go new file mode 100644 index 000000000..aa7b328e5 --- /dev/null +++ b/compiler/types/pipeline/git_test.go @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 + +package pipeline + +import "testing" + +func TestPipeline_Git_Empty(t *testing.T) { + // setup tests + tests := []struct { + git *Git + want bool + }{ + { + git: &Git{&Token{Repositories: []string{}}}, + want: false, + }, + { + git: new(Git), + want: true, + }, + } + + // run tests + for _, test := range tests { + got := test.git.Empty() + + if got != test.want { + t.Errorf("Empty is %v, want %t", got, test.want) + } + } +} diff --git a/compiler/types/yaml/build.go b/compiler/types/yaml/build.go index c0b719c9e..8b9a25a44 100644 --- a/compiler/types/yaml/build.go +++ b/compiler/types/yaml/build.go @@ -19,6 +19,7 @@ type Build struct { Steps StepSlice `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"oneof_required=steps,description=Provide sequential execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/"` Templates TemplateSlice `yaml:"templates,omitempty" json:"templates,omitempty" jsonschema:"description=Provide the name of templates to expand.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/"` Deployment Deployment `yaml:"deployment,omitempty" json:"deployment,omitempty" jsonschema:"description=Provide deployment configuration.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/"` + Git Git `yaml:"git,omitempty" json:"git,omitempty" jsonschema:"description=Provide the git access specifications.\nReference: https://go-vela.github.io/docs/reference/yaml/git/"` } // ToPipelineAPI converts the Build type to an API Pipeline type. @@ -75,6 +76,7 @@ func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error { Steps StepSlice Templates TemplateSlice Deployment Deployment + Git Git }) // attempt to unmarshal as a build type @@ -89,6 +91,7 @@ func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error { } // override the values + b.Git = build.Git b.Version = build.Version b.Metadata = build.Metadata b.Environment = build.Environment diff --git a/compiler/types/yaml/git.go b/compiler/types/yaml/git.go new file mode 100644 index 000000000..7391714e5 --- /dev/null +++ b/compiler/types/yaml/git.go @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import "github.com/go-vela/server/compiler/types/pipeline" + +// Git is the yaml representation of git configurations for a pipeline. +type Git struct { + Token `yaml:"token,omitempty" json:"token,omitempty" jsonschema:"description=Provide the git token specifications, primarily used for cloning.\nReference: https://go-vela.github.io/docs/reference/yaml/git/#token"` +} + +// Token is the yaml representation of the git token. +// Only applies when using GitHub App installations. +type Token struct { + Repositories []string `yaml:"repositories,omitempty" json:"repositories,omitempty" jsonschema:"description=Provide a list of repositories to clone.\nReference: https://go-vela.github.io/docs/reference/yaml/git/#repositories"` + Permissions map[string]string `yaml:"permissions,omitempty" json:"permissions,omitempty" jsonschema:"description=Provide a list of repository permissions to apply to the git token.\nReference: https://go-vela.github.io/docs/reference/yaml/git/#permissions"` +} + +// ToPipeline converts the Git type +// to a pipeline Git type. +func (g *Git) ToPipeline() *pipeline.Git { + return &pipeline.Git{ + Token: &pipeline.Token{ + Repositories: g.Repositories, + Permissions: g.Permissions, + }, + } +} diff --git a/compiler/types/yaml/git_test.go b/compiler/types/yaml/git_test.go new file mode 100644 index 000000000..d13d6be48 --- /dev/null +++ b/compiler/types/yaml/git_test.go @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "reflect" + "testing" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Git_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + git *Git + want *pipeline.Git + }{ + { + git: &Git{ + Token: Token{ + Repositories: []string{"foo", "bar"}, + }, + }, + want: &pipeline.Git{ + Token: &pipeline.Token{ + Repositories: []string{"foo", "bar"}, + }, + }, + }, + { + git: &Git{ + Token: Token{ + Permissions: map[string]string{"foo": "bar"}, + }, + }, + want: &pipeline.Git{ + Token: &pipeline.Token{ + Permissions: map[string]string{"foo": "bar"}, + }, + }, + }, + { + git: &Git{ + Token: Token{}, + }, + want: &pipeline.Git{ + Token: &pipeline.Token{}, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.git.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} diff --git a/constants/app_install.go b/constants/app_install.go new file mode 100644 index 000000000..4e97cb067 --- /dev/null +++ b/constants/app_install.go @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 + +// App Install vars. +package constants + +const ( + // GitHub App install repositories selection when "all" repositories are selected. + AppInstallRepositoriesSelectionAll = "all" + // GitHub App install repositories selection when a subset of repositories are selected. + AppInstallRepositoriesSelectionSelected = "selected" +) + +const ( + // GitHub App install setup_action type 'install'. + AppInstallSetupActionInstall = "install" + // GitHub App install event type 'created'. + AppInstallCreated = "created" + // GitHub App install event type 'deleted'. + AppInstallDeleted = "deleted" +) diff --git a/constants/event.go b/constants/event.go index c2ec26aad..a2ab76e3a 100644 --- a/constants/event.go +++ b/constants/event.go @@ -28,6 +28,12 @@ const ( // EventTag defines the event type for build and repo tag events. EventTag = "tag" + // EventInstallation defines the event type for scm installation events. + EventInstallation = "installation" + + // EventInstallationRepositories defines the event type for scm installation_repositories events. + EventInstallationRepositories = "installation_repositories" + // Alternates for common user inputs that do not match our set constants. // EventPullAlternate defines the alternate event type for build and repo pull_request events. diff --git a/database/integration_test.go b/database/integration_test.go index ce9a3ccfe..9d6258fce 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -2491,6 +2491,7 @@ func newResources() *Resources { repoOne.SetPreviousName("") repoOne.SetApproveBuild(constants.ApproveNever) repoOne.SetAllowEvents(api.NewEventsFromMask(1)) + repoOne.SetInstallID(0) repoTwo := new(api.Repo) repoTwo.SetID(2) @@ -2514,6 +2515,7 @@ func newResources() *Resources { repoTwo.SetPreviousName("") repoTwo.SetApproveBuild(constants.ApproveForkAlways) repoTwo.SetAllowEvents(api.NewEventsFromMask(1)) + repoTwo.SetInstallID(0) buildOne := new(api.Build) buildOne.SetID(1) diff --git a/database/repo/create_test.go b/database/repo/create_test.go index 07b8204ab..939fc186b 100644 --- a/database/repo/create_test.go +++ b/database/repo/create_test.go @@ -34,9 +34,9 @@ func TestRepo_Engine_CreateRepo(t *testing.T) { // ensure the mock expects the query _mock.ExpectQuery(`INSERT INTO "repos" -("user_id","hash","org","name","full_name","link","clone","branch","topics","build_limit","timeout","counter","visibility","private","trusted","active","allow_events","pipeline_type","previous_name","approve_build","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21) RETURNING "id"`). - WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, nil, "yaml", "oldName", nil, 1). +("user_id","hash","org","name","full_name","link","clone","branch","topics","build_limit","timeout","counter","visibility","private","trusted","active","allow_events","pipeline_type","previous_name","approve_build","install_id","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22) RETURNING "id"`). + WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, nil, "yaml", "oldName", nil, 0, 1). WillReturnRows(_rows) _sqlite := testSqlite(t) diff --git a/database/repo/table.go b/database/repo/table.go index f49c26933..daba7a367 100644 --- a/database/repo/table.go +++ b/database/repo/table.go @@ -35,6 +35,7 @@ repos ( pipeline_type TEXT, previous_name VARCHAR(100), approve_build VARCHAR(20), + install_id INTEGER, UNIQUE(full_name) ); ` @@ -65,6 +66,7 @@ repos ( pipeline_type TEXT, previous_name TEXT, approve_build TEXT, + install_id INTEGER, UNIQUE(full_name) ); ` diff --git a/database/repo/update_test.go b/database/repo/update_test.go index a447d6973..dab936f9b 100644 --- a/database/repo/update_test.go +++ b/database/repo/update_test.go @@ -35,9 +35,9 @@ func TestRepo_Engine_UpdateRepo(t *testing.T) { // ensure the mock expects the query _mock.ExpectExec(`UPDATE "repos" -SET "user_id"=$1,"hash"=$2,"org"=$3,"name"=$4,"full_name"=$5,"link"=$6,"clone"=$7,"branch"=$8,"topics"=$9,"build_limit"=$10,"timeout"=$11,"counter"=$12,"visibility"=$13,"private"=$14,"trusted"=$15,"active"=$16,"allow_events"=$17,"pipeline_type"=$18,"previous_name"=$19,"approve_build"=$20 -WHERE "id" = $21`). - WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, 1, "yaml", "oldName", constants.ApproveForkAlways, 1). +SET "user_id"=$1,"hash"=$2,"org"=$3,"name"=$4,"full_name"=$5,"link"=$6,"clone"=$7,"branch"=$8,"topics"=$9,"build_limit"=$10,"timeout"=$11,"counter"=$12,"visibility"=$13,"private"=$14,"trusted"=$15,"active"=$16,"allow_events"=$17,"pipeline_type"=$18,"previous_name"=$19,"approve_build"=$20,"install_id"=$21 +WHERE "id" = $22`). + WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, 1, "yaml", "oldName", constants.ApproveForkAlways, 0, 1). WillReturnResult(sqlmock.NewResult(1, 1)) _sqlite := testSqlite(t) diff --git a/database/testutils/api_resources.go b/database/testutils/api_resources.go index 70cc45d65..738602534 100644 --- a/database/testutils/api_resources.go +++ b/database/testutils/api_resources.go @@ -127,6 +127,7 @@ func APIRepo() *api.Repo { AllowEvents: APIEvents(), Topics: new([]string), ApproveBuild: new(string), + InstallID: new(int64), } } diff --git a/database/types/repo.go b/database/types/repo.go index 96f45caf9..f02949f2e 100644 --- a/database/types/repo.go +++ b/database/types/repo.go @@ -67,6 +67,7 @@ type Repo struct { PipelineType sql.NullString `sql:"pipeline_type"` PreviousName sql.NullString `sql:"previous_name"` ApproveBuild sql.NullString `sql:"approve_build"` + InstallID sql.NullInt64 `sql:"install_id"` Owner User `gorm:"foreignKey:UserID"` } @@ -250,6 +251,7 @@ func (r *Repo) ToAPI() *api.Repo { repo.SetPipelineType(r.PipelineType.String) repo.SetPreviousName(r.PreviousName.String) repo.SetApproveBuild(r.ApproveBuild.String) + repo.SetInstallID(r.InstallID.Int64) return repo } @@ -342,6 +344,7 @@ func RepoFromAPI(r *api.Repo) *Repo { PipelineType: sql.NullString{String: r.GetPipelineType(), Valid: true}, PreviousName: sql.NullString{String: r.GetPreviousName(), Valid: true}, ApproveBuild: sql.NullString{String: r.GetApproveBuild(), Valid: true}, + InstallID: sql.NullInt64{Int64: r.GetInstallID(), Valid: true}, } return repo.Nullify() diff --git a/database/types/repo_test.go b/database/types/repo_test.go index c9a8b6afd..1027e5fc2 100644 --- a/database/types/repo_test.go +++ b/database/types/repo_test.go @@ -193,6 +193,7 @@ func TestTypes_Repo_ToAPI(t *testing.T) { want.SetPipelineType("yaml") want.SetPreviousName("oldName") want.SetApproveBuild(constants.ApproveNever) + want.SetInstallID(0) // run test got := testRepo().ToAPI() @@ -345,6 +346,7 @@ func TestTypes_RepoFromAPI(t *testing.T) { r.SetPipelineType("yaml") r.SetPreviousName("oldName") r.SetApproveBuild(constants.ApproveNever) + r.SetInstallID(0) want := testRepo() want.Owner = User{} @@ -382,6 +384,7 @@ func testRepo() *Repo { PipelineType: sql.NullString{String: "yaml", Valid: true}, PreviousName: sql.NullString{String: "oldName", Valid: true}, ApproveBuild: sql.NullString{String: constants.ApproveNever, Valid: true}, + InstallID: sql.NullInt64{Int64: 0, Valid: true}, Owner: *testUser(), } diff --git a/database/types/schedule_test.go b/database/types/schedule_test.go index 10b4da233..2937c6c0a 100644 --- a/database/types/schedule_test.go +++ b/database/types/schedule_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/adhocore/gronx" + "github.com/google/go-cmp/cmp" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/constants" @@ -116,8 +117,8 @@ func TestTypes_Schedule_ToAPI(t *testing.T) { // run test got := testSchedule().ToAPI() - if !reflect.DeepEqual(got, want) { - t.Errorf("ToAPI is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ScheduleToAPI() mismatch (-want +got):\n%s", diff) } } diff --git a/internal/webhook.go b/internal/webhook.go index 6020d2ab1..7baab3701 100644 --- a/internal/webhook.go +++ b/internal/webhook.go @@ -21,15 +21,27 @@ type PullRequest struct { Labels []string } +// Installation defines the data pulled from an installation +// while processing a webhook. +// Only applies to GitHub Apps. +type Installation struct { + Action string + ID int64 + Org string + RepositoriesAdded []string + RepositoriesRemoved []string +} + // Webhook defines a struct that is used to return // the required data when processing webhook event // a for a source provider event. type Webhook struct { - Hook *api.Hook - Repo *api.Repo - Build *api.Build - PullRequest PullRequest - Deployment *api.Deployment + Hook *api.Hook + Repo *api.Repo + Build *api.Build + PullRequest PullRequest + Deployment *api.Deployment + Installation *Installation } // ShouldSkip uses the build information diff --git a/mock/server/repo.go b/mock/server/repo.go index 9b156669e..1db1fc733 100644 --- a/mock/server/repo.go +++ b/mock/server/repo.go @@ -59,8 +59,9 @@ const ( } }, "approve_build": "fork-always", - "previous_name": "" -}` + "previous_name": "", + "install_id": 0 + }` // ReposResp represents a JSON return for one to many repos. ReposResp = `[ @@ -78,7 +79,8 @@ const ( "visibility": "public", "private": false, "trusted": true, - "active": true + "active": true, + "install_id": 0 }, { "id": 2, @@ -94,7 +96,8 @@ const ( "visibility": "public", "private": false, "trusted": true, - "active": true + "active": true, + "install_id": 0 } ]` ) diff --git a/router/middleware/build/build_test.go b/router/middleware/build/build_test.go index 8db7c8000..42288f017 100644 --- a/router/middleware/build/build_test.go +++ b/router/middleware/build/build_test.go @@ -53,6 +53,7 @@ func TestBuild_Establish(t *testing.T) { r.SetName("bar") r.SetFullName("foo/bar") r.SetVisibility("public") + r.SetInstallID(0) want := new(api.Build) want.SetID(1) diff --git a/router/middleware/repo/repo_test.go b/router/middleware/repo/repo_test.go index c20067c62..a331b5ae8 100644 --- a/router/middleware/repo/repo_test.go +++ b/router/middleware/repo/repo_test.go @@ -66,6 +66,7 @@ func TestRepo_Establish(t *testing.T) { want.SetPipelineType("yaml") want.SetPreviousName("") want.SetApproveBuild("") + want.SetInstallID(0) got := new(api.Repo) diff --git a/scm/flags.go b/scm/flags.go index 0d95ebcb5..ff83d24f6 100644 --- a/scm/flags.go +++ b/scm/flags.go @@ -67,4 +67,29 @@ var Flags = []cli.Flag{ "is behind a Firewall or NAT, or when using something like ngrok to forward webhooks. " + "(defaults to VELA_ADDR).", }, + &cli.Int64Flag{ + EnvVars: []string{"VELA_SCM_APP_ID", "SCM_APP_ID"}, + FilePath: "/vela/scm/app_id", + Name: "scm.app.id", + Usage: "set ID for the SCM App integration (GitHub App)", + }, + &cli.StringFlag{ + EnvVars: []string{"VELA_SCM_APP_PRIVATE_KEY", "SCM_APP_PRIVATE_KEY"}, + FilePath: "/vela/scm/app_private_key", + Name: "scm.app.private-key", + Usage: "set value of base64 encoded SCM App integration (GitHub App) private key", + }, + &cli.StringFlag{ + EnvVars: []string{"VELA_SCM_APP_PRIVATE_KEY_PATH", "SCM_APP_PRIVATE_KEY_PATH"}, + FilePath: "/vela/scm/app_private_key_path", + Name: "scm.app.private-key.path", + Usage: "set filepath to the SCM App integration (GitHub App) private key", + }, + &cli.StringSliceFlag{ + EnvVars: []string{"VELA_SCM_APP_PERMISSIONS", "SCM_APP_PERMISSIONS", "VELA_SOURCE_APP_PERMISSIONS", "SOURCE_APP_PERMISSIONS"}, + FilePath: "/vela/scm/app/permissions", + Name: "scm.app.permissions", + Usage: "SCM App integration (GitHub App) permissions to be used as the allowed set of possible installation token permissions", + Value: cli.NewStringSlice("contents:read", "checks:write"), + }, } diff --git a/scm/github/access.go b/scm/github/access.go index a1e7f5d4d..1bd4dd2e3 100644 --- a/scm/github/access.go +++ b/scm/github/access.go @@ -31,7 +31,7 @@ func (c *client) OrgAccess(ctx context.Context, u *api.User, org string) (string } // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // send API call to capture org access level for user membership, _, err := client.Organizations.GetOrgMembership(ctx, *u.Name, org) @@ -67,7 +67,7 @@ func (c *client) RepoAccess(ctx context.Context, name, token, org, repo string) } // create github oauth client with the given token - client := c.newClientToken(ctx, token) + client := c.newOAuthTokenClient(ctx, token) // send API call to capture repo access level for user perm, _, err := client.Repositories.GetPermissionLevel(ctx, org, repo, name) @@ -98,7 +98,7 @@ func (c *client) TeamAccess(ctx context.Context, u *api.User, org, team string) } // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, u.GetToken()) + client := c.newOAuthTokenClient(ctx, u.GetToken()) teams := []*github.Team{} // set the max per page for the options to capture the list of repos @@ -148,7 +148,7 @@ func (c *client) ListUsersTeamsForOrg(ctx context.Context, u *api.User, org stri }).Tracef("capturing %s team membership for org %s", u.GetName(), org) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, u.GetToken()) + client := c.newOAuthTokenClient(ctx, u.GetToken()) teams := []*github.Team{} // set the max per page for the options to capture the list of repos @@ -193,7 +193,7 @@ func (c *client) RepoContributor(ctx context.Context, owner *api.User, sender, o }).Tracef("capturing %s contributor status for repo %s/%s", sender, org, repo) // create GitHub OAuth client with repo owner's token - client := c.newClientToken(ctx, owner.GetToken()) + client := c.newOAuthTokenClient(ctx, owner.GetToken()) // set the max per page for the options to capture the list of repos opts := github.ListContributorsOptions{ diff --git a/scm/github/app_install.go b/scm/github/app_install.go new file mode 100644 index 000000000..16c03924c --- /dev/null +++ b/scm/github/app_install.go @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: Apache-2.0 + +package github + +import ( + "context" + "errors" + "fmt" + "net/http" + "strings" + "time" + + "github.com/sirupsen/logrus" + "gorm.io/gorm" + + "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database" + "github.com/go-vela/server/internal" +) + +// ProcessInstallation takes a GitHub installation and processes the changes. +func (c *client) ProcessInstallation(ctx context.Context, _ *http.Request, webhook *internal.Webhook, db database.Interface) error { + c.Logger.Tracef("processing GitHub App installation") + + errs := []string{} + + // set install_id for repos added to the installation + for _, repo := range webhook.Installation.RepositoriesAdded { + r, err := db.GetRepoForOrg(ctx, webhook.Installation.Org, repo) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + errs = append(errs, fmt.Sprintf("%s:%s", repo, err.Error())) + } + + // skip repos that dont exist in vela + continue + } + + err = updateRepoInstallationID(ctx, webhook, r, db, webhook.Installation.ID) + if err != nil { + errs = append(errs, fmt.Sprintf("%s:%s", repo, err.Error())) + } + } + + // set install_id for repos removed from the installation + for _, repo := range webhook.Installation.RepositoriesRemoved { + r, err := db.GetRepoForOrg(ctx, webhook.Installation.Org, repo) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + errs = append(errs, fmt.Sprintf("%s:%s", repo, err.Error())) + } + + // skip repos that dont exist in vela + continue + } + + err = updateRepoInstallationID(ctx, webhook, r, db, 0) + if err != nil { + errs = append(errs, fmt.Sprintf("%s:%s", repo, err.Error())) + } + } + + // combine all errors + if len(errs) > 0 { + return errors.New(strings.Join(errs, ", ")) + } + + return nil +} + +// updateRepoInstallationID updates the installation ID for a repo. +func updateRepoInstallationID(ctx context.Context, webhook *internal.Webhook, r *types.Repo, db database.Interface, installID int64) error { + r.SetInstallID(installID) + + h := new(types.Hook) + h.SetNumber(webhook.Hook.GetNumber()) + h.SetSourceID(webhook.Hook.GetSourceID()) + h.SetWebhookID(webhook.Hook.GetWebhookID()) + h.SetCreated(webhook.Hook.GetCreated()) + h.SetHost(webhook.Hook.GetHost()) + h.SetEvent(constants.EventInstallation) + h.SetStatus(webhook.Hook.GetStatus()) + + r, err := db.UpdateRepo(ctx, r) + if err != nil { + h.SetStatus(constants.StatusFailure) + h.SetError(err.Error()) + } + + h.Repo = r + + // number of times to retry + retryLimit := 3 + // implement a loop to process asynchronous operations with a retry limit + // + // Some operations taken during the webhook workflow can lead to race conditions + // failing to successfully process the request. This logic ensures we attempt our + // best efforts to handle these cases gracefully. + for i := 0; i < retryLimit; i++ { + // check if we're on the first iteration of the loop + if i > 0 { + // incrementally sleep in between retries + time.Sleep(time.Duration(i) * time.Second) + } + + // send API call to capture the last hook for the repo + lastHook, err := db.LastHookForRepo(ctx, r) + if err != nil { + // log the error for traceability + logrus.Error(err.Error()) + + // check if the retry limit has been exceeded + if i < retryLimit { + // continue to the next iteration of the loop + continue + } + + return err + } + + // set the Number field + if lastHook != nil { + h.SetNumber( + lastHook.GetNumber() + 1, + ) + } + + // send hook update to db + _, err = db.CreateHook(ctx, h) + if err != nil { + return err + } + + break + } + + return nil +} + +// FinishInstallation completes the web flow for a GitHub App installation, returning a redirect to the app installation page. +func (c *client) FinishInstallation(ctx context.Context, _ *http.Request, installID int64) (string, error) { + c.Logger.Tracef("finishing GitHub App installation for ID %d", installID) + + client, err := c.newGithubAppClient() + if err != nil { + return "", err + } + + install, _, err := client.Apps.GetInstallation(ctx, installID) + if err != nil { + return "", err + } + + return install.GetHTMLURL(), nil +} diff --git a/scm/github/app_permissions.go b/scm/github/app_permissions.go new file mode 100644 index 000000000..411ee3d4a --- /dev/null +++ b/scm/github/app_permissions.go @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 + +package github + +import ( + "fmt" + "strings" + + "github.com/google/go-github/v65/github" +) + +// see: https://docs.github.com/en/rest/authentication/permissions-required-for-github-apps?apiVersion=2022-11-28 +const ( + // GitHub App install permission 'none'. + AppInstallPermissionNone = "none" + // GitHub App install permission 'read'. + AppInstallPermissionRead = "read" + // GitHub App install permission 'write'. + AppInstallPermissionWrite = "write" +) + +const ( + // GitHub App install contents resource. + AppInstallResourceContents = "contents" + // GitHub App install checks resource. + AppInstallResourceChecks = "checks" + // GitHub App install packages resource. + AppInstallResourcePackages = "packages" + // add more supported resources as needed. +) + +// GetInstallationPermission takes permissions and returns the permission level if valid. +func GetInstallationPermission(resource string, appPermissions *github.InstallationPermissions) (string, error) { + switch resource { + case AppInstallResourceContents: + return appPermissions.GetContents(), nil + case AppInstallResourceChecks: + return appPermissions.GetChecks(), nil + case AppInstallResourcePackages: + return appPermissions.GetPackages(), nil + // add more supported resources as needed. + default: + return "", fmt.Errorf("given permission resource not supported: %s", resource) + } +} + +// ApplyInstallationPermissions takes permissions and applies a new permission if valid. +func ApplyInstallationPermissions(resource, perm string, perms *github.InstallationPermissions) (*github.InstallationPermissions, error) { + // convert permissions from string + switch strings.ToLower(perm) { + case AppInstallPermissionNone: + case AppInstallPermissionRead: + case AppInstallPermissionWrite: + break + default: + return perms, fmt.Errorf("invalid permission level given for : in %s:%s", resource, perm) + } + + // convert resource from string + switch strings.ToLower(resource) { + case AppInstallResourceContents: + perms.Contents = github.String(perm) + case AppInstallResourceChecks: + perms.Checks = github.String(perm) + case AppInstallResourcePackages: + perms.Packages = github.String(perm) + // add more supported resources as needed. + default: + return perms, fmt.Errorf("invalid permission resource given for : in %s:%s", resource, perm) + } + + return perms, nil +} + +// InstallationHasPermission takes a resource:perm pair and checks if the actual permission matches the expected permission or is supersceded by a higher permission. +func InstallationHasPermission(resource, requiredPerm, actualPerm string) error { + if len(actualPerm) == 0 { + return fmt.Errorf("github app missing permission %s:%s", resource, requiredPerm) + } + + permitted := false + + switch requiredPerm { + case AppInstallPermissionNone: + permitted = true + case AppInstallPermissionRead: + if actualPerm == AppInstallPermissionRead || + actualPerm == AppInstallPermissionWrite { + permitted = true + } + case AppInstallPermissionWrite: + if actualPerm == AppInstallPermissionWrite { + permitted = true + } + default: + return fmt.Errorf("invalid required permission type: %s", requiredPerm) + } + + if !permitted { + return fmt.Errorf("github app requires permission %s:%s, found: %s", AppInstallResourceContents, AppInstallPermissionRead, actualPerm) + } + + return nil +} diff --git a/scm/github/app_permissions_test.go b/scm/github/app_permissions_test.go new file mode 100644 index 000000000..74b94a071 --- /dev/null +++ b/scm/github/app_permissions_test.go @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: Apache-2.0 + +package github + +import ( + "testing" + + "github.com/google/go-github/v65/github" +) + +func TestGetInstallationPermission(t *testing.T) { + tests := []struct { + name string + resource string + permissions *github.InstallationPermissions + expectedPerm string + expectedError bool + }{ + { + name: "valid contents permission", + resource: AppInstallResourceContents, + permissions: &github.InstallationPermissions{Contents: github.String(AppInstallPermissionRead)}, + expectedPerm: AppInstallPermissionRead, + }, + { + name: "valid checks permission", + resource: AppInstallResourceChecks, + permissions: &github.InstallationPermissions{Checks: github.String(AppInstallPermissionWrite)}, + expectedPerm: AppInstallPermissionWrite, + }, + { + name: "valid packages permission", + resource: AppInstallResourcePackages, + permissions: &github.InstallationPermissions{Packages: github.String(AppInstallPermissionNone)}, + expectedPerm: AppInstallPermissionNone, + }, + { + name: "invalid resource", + resource: "invalid_resource", + permissions: &github.InstallationPermissions{}, + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + perm, err := GetInstallationPermission(tt.resource, tt.permissions) + if (err != nil) != tt.expectedError { + t.Errorf("GetInstallationPermission() error = %v, expectedError %v", err, tt.expectedError) + return + } + if perm != tt.expectedPerm { + t.Errorf("GetInstallationPermission() = %v, expected %v", perm, tt.expectedPerm) + } + }) + } +} + +func TestApplyInstallationPermissions(t *testing.T) { + tests := []struct { + name string + resource string + perm string + initialPerms *github.InstallationPermissions + expectedPerms *github.InstallationPermissions + expectedError bool + }{ + { + name: "apply read permission to contents", + resource: AppInstallResourceContents, + perm: AppInstallPermissionRead, + initialPerms: &github.InstallationPermissions{}, + expectedPerms: &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionRead), + }, + }, + { + name: "apply write permission to checks", + resource: AppInstallResourceChecks, + perm: AppInstallPermissionWrite, + initialPerms: &github.InstallationPermissions{}, + expectedPerms: &github.InstallationPermissions{ + Checks: github.String(AppInstallPermissionWrite), + }, + }, + { + name: "apply none permission to packages", + resource: AppInstallResourcePackages, + perm: AppInstallPermissionNone, + initialPerms: &github.InstallationPermissions{}, + expectedPerms: &github.InstallationPermissions{ + Packages: github.String(AppInstallPermissionNone), + }, + }, + { + name: "invalid permission level", + resource: AppInstallResourceContents, + perm: "invalid_perm", + initialPerms: &github.InstallationPermissions{}, + expectedError: true, + }, + { + name: "invalid resource", + resource: "invalid_resource", + perm: AppInstallPermissionRead, + initialPerms: &github.InstallationPermissions{}, + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + perms, err := ApplyInstallationPermissions(tt.resource, tt.perm, tt.initialPerms) + if (err != nil) != tt.expectedError { + t.Errorf("ApplyInstallationPermissions() error = %v, expectedError %v", err, tt.expectedError) + return + } + if !tt.expectedError && !comparePermissions(perms, tt.expectedPerms) { + t.Errorf("ApplyInstallationPermissions() = %v, expected %v", perms, tt.expectedPerms) + } + }) + } +} + +func TestInstallationHasPermission(t *testing.T) { + tests := []struct { + name string + resource string + requiredPerm string + actualPerm string + expectedError bool + }{ + { + name: "valid read permission", + resource: AppInstallResourceContents, + requiredPerm: AppInstallPermissionRead, + actualPerm: AppInstallPermissionRead, + }, + { + name: "valid write permission", + resource: AppInstallResourceChecks, + requiredPerm: AppInstallPermissionWrite, + actualPerm: AppInstallPermissionWrite, + }, + { + name: "valid none permission", + resource: AppInstallResourcePackages, + requiredPerm: AppInstallPermissionNone, + actualPerm: AppInstallPermissionNone, + }, + { + name: "read permission superseded by write", + resource: AppInstallResourceContents, + requiredPerm: AppInstallPermissionRead, + actualPerm: AppInstallPermissionWrite, + }, + { + name: "missing permission", + resource: AppInstallResourceChecks, + requiredPerm: AppInstallPermissionWrite, + actualPerm: "", + expectedError: true, + }, + { + name: "invalid required permission", + resource: AppInstallResourcePackages, + requiredPerm: "invalid_perm", + actualPerm: AppInstallPermissionRead, + expectedError: true, + }, + { + name: "insufficient permission", + resource: AppInstallResourceContents, + requiredPerm: AppInstallPermissionWrite, + actualPerm: AppInstallPermissionRead, + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := InstallationHasPermission(tt.resource, tt.requiredPerm, tt.actualPerm) + if (err != nil) != tt.expectedError { + t.Errorf("InstallationHasPermission() error = %v, expectedError %v", err, tt.expectedError) + } + }) + } +} + +func comparePermissions(a, b *github.InstallationPermissions) bool { + if a == nil || b == nil { + return a == b + } + return github.Stringify(a) == github.Stringify(b) +} diff --git a/scm/github/app_transport.go b/scm/github/app_transport.go new file mode 100644 index 000000000..b0db1f5f8 --- /dev/null +++ b/scm/github/app_transport.go @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: Apache-2.0 + +package github + +import ( + "bytes" + "context" + "crypto/rand" + "crypto/rsa" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptrace" + "strconv" + "strings" + "sync" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/google/go-github/v65/github" + "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" +) + +const ( + acceptHeader = "application/vnd.github.v3+json" +) + +// AppsTransport provides a http.RoundTripper by wrapping an existing +// http.RoundTripper and provides GitHub Apps authentication as a GitHub App. +// +// Client can also be overwritten, and is useful to change to one which +// provides retry logic if you do experience retryable errors. +// +// See https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/ +type AppsTransport struct { + BaseURL string // BaseURL is the scheme and host for GitHub API, defaults to https://api.github.com + Client Client // Client to use to refresh tokens, defaults to http.Client with provided transport + tr http.RoundTripper // tr is the underlying roundtripper being wrapped + signer Signer // signer signs JWT tokens. + appID int64 // appID is the GitHub App's ID +} + +// newGitHubAppTransport creates a new GitHub App transport for authenticating as the GitHub App. +func (c *client) newGitHubAppTransport(appID int64, baseURL string, privateKey *rsa.PrivateKey) *AppsTransport { + transport := c.newAppsTransportFromPrivateKey(http.DefaultTransport, appID, privateKey) + transport.BaseURL = baseURL + + // apply tracing to the transport + if c.Tracing.Config.EnableTracing { + transport.tr = otelhttp.NewTransport( + transport.tr, + otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace { + return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans()) + }), + ) + } + + return transport +} + +// newAppsTransportFromPrivateKey returns an AppsTransport using a crypto/rsa.(*PrivateKey). +func (c *client) newAppsTransportFromPrivateKey(tr http.RoundTripper, appID int64, key *rsa.PrivateKey) *AppsTransport { + return &AppsTransport{ + BaseURL: defaultAPI, + Client: &http.Client{Transport: tr}, + tr: tr, + signer: NewRSASigner(jwt.SigningMethodRS256, key), + appID: appID, + } +} + +// RoundTrip implements http.RoundTripper interface. +func (t *AppsTransport) RoundTrip(req *http.Request) (*http.Response, error) { + // GitHub rejects expiry and issue timestamps that are not an integer, + // while the jwt-go library serializes to fractional timestamps + // then truncate them before passing to jwt-go. + iss := time.Now().Add(-30 * time.Second).Truncate(time.Second) + exp := iss.Add(2 * time.Minute) + claims := &jwt.RegisteredClaims{ + IssuedAt: jwt.NewNumericDate(iss), + ExpiresAt: jwt.NewNumericDate(exp), + Issuer: strconv.FormatInt(t.appID, 10), + } + + ss, err := t.signer.Sign(claims) + if err != nil { + return nil, fmt.Errorf("could not sign jwt: %w", err) + } + + req.Header.Set("Authorization", "Bearer "+ss) + req.Header.Add("Accept", acceptHeader) + + return t.tr.RoundTrip(req) +} + +// Transport provides a http.RoundTripper by wrapping an existing +// http.RoundTripper and provides GitHub Apps authentication as an installation. +// +// Client can also be overwritten, and is useful to change to one which +// provides retry logic if you do experience retryable errors. +// +// See https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/ +type Transport struct { + BaseURL string // BaseURL is the scheme and host for GitHub API, defaults to https://api.github.com + Client Client // Client to use to refresh tokens, defaults to http.Client with provided transport + tr http.RoundTripper // tr is the underlying roundtripper being wrapped + installationID int64 // installationID is the GitHub App Installation ID + InstallationTokenOptions *github.InstallationTokenOptions // parameters restrict a token's access + appsTransport *AppsTransport + + mu *sync.Mutex + token *accessToken // the installation's access token +} + +// accessToken is an installation access token response from GitHub. +type accessToken struct { + Token string `json:"token"` + ExpiresAt time.Time `json:"expires_at"` + Permissions github.InstallationPermissions `json:"permissions,omitempty"` + Repositories []github.Repository `json:"repositories,omitempty"` +} + +var _ http.RoundTripper = &Transport{} + +// Client is a HTTP client which sends a http.Request and returns a http.Response +// or an error. +type Client interface { + Do(*http.Request) (*http.Response, error) +} + +// RoundTrip implements http.RoundTripper interface. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + reqBodyClosed := false + + if req.Body != nil { + defer func() { + if !reqBodyClosed { + req.Body.Close() + } + }() + } + + token, err := t.Token(req.Context()) + if err != nil { + return nil, err + } + + creq := cloneRequest(req) + creq.Header.Set("Authorization", "token "+token) + + if creq.Header.Get("Accept") == "" { + creq.Header.Add("Accept", acceptHeader) + } + + reqBodyClosed = true + + return t.tr.RoundTrip(creq) +} + +// getRefreshTime returns the time when the token should be refreshed. +func (at *accessToken) getRefreshTime() time.Time { + return at.ExpiresAt.Add(-time.Minute) +} + +// isExpired checks if the access token is expired. +func (at *accessToken) isExpired() bool { + return at == nil || at.getRefreshTime().Before(time.Now()) +} + +// Token checks the active token expiration and renews if necessary. Token returns +// a valid access token. If renewal fails an error is returned. +func (t *Transport) Token(ctx context.Context) (string, error) { + t.mu.Lock() + + defer t.mu.Unlock() + + if t.token.isExpired() { + // token is not set or expired/nearly expired, so refresh + if err := t.refreshToken(ctx); err != nil { + return "", fmt.Errorf("could not refresh installation id %v's token: %w", t.installationID, err) + } + } + + return t.token.Token, nil +} + +// Expiry returns a transport token's expiration time and refresh time. There is a small grace period +// built in where a token will be refreshed before it expires. expiresAt is the actual token expiry, +// and refreshAt is when a call to Token() will cause it to be refreshed. +func (t *Transport) Expiry() (expiresAt time.Time, refreshAt time.Time, err error) { + if t.token == nil { + return time.Time{}, time.Time{}, errors.New("Expiry() = unknown, err: nil token") + } + + return t.token.ExpiresAt, t.token.getRefreshTime(), nil +} + +func (t *Transport) refreshToken(ctx context.Context) error { + // convert InstallationTokenOptions into a ReadWriter to pass as an argument to http.NewRequest + body, err := GetReadWriter(t.InstallationTokenOptions) + if err != nil { + return fmt.Errorf("could not convert installation token parameters into json: %w", err) + } + + requestURL := fmt.Sprintf("%s/app/installations/%v/access_tokens", strings.TrimRight(t.BaseURL, "/"), t.installationID) + + req, err := http.NewRequest("POST", requestURL, body) + if err != nil { + return fmt.Errorf("could not create request: %w", err) + } + + // set Content and Accept headers + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + + req.Header.Set("Accept", acceptHeader) + + if ctx != nil { + req = req.WithContext(ctx) + } + + t.appsTransport.BaseURL = t.BaseURL + t.appsTransport.Client = t.Client + + resp, err := t.appsTransport.RoundTrip(req) + if err != nil { + return fmt.Errorf("could not get access_tokens from GitHub API for installation ID %v: %w", t.installationID, err) + } + + if resp.StatusCode/100 != 2 { + return fmt.Errorf("received non 2xx response status %q when fetching %v", resp.Status, req.URL) + } + + // closing body late, to provide caller a chance to inspect body in an error / non-200 response status situation + defer resp.Body.Close() + + return json.NewDecoder(resp.Body).Decode(&t.token) +} + +// GetReadWriter converts a body interface into an io.ReadWriter object. +func GetReadWriter(i interface{}) (io.ReadWriter, error) { + var buf io.ReadWriter + + if i != nil { + buf = new(bytes.Buffer) + + enc := json.NewEncoder(buf) + + err := enc.Encode(i) + if err != nil { + return nil, err + } + } + + return buf, nil +} + +// cloneRequest returns a clone of the provided *http.Request. +// The clone is a shallow copy of the struct and its Header map. +func cloneRequest(r *http.Request) *http.Request { + // shallow copy of the struct + _r := new(http.Request) + + *_r = *r + + // deep copy of the Header + _r.Header = make(http.Header, len(r.Header)) + + for k, s := range r.Header { + _r.Header[k] = append([]string(nil), s...) + } + + return _r +} + +// Signer is a JWT token signer. This is a wrapper around [jwt.SigningMethod] with predetermined +// key material. +type Signer interface { + // sign the given claims and returns a JWT token string, as specified + // by [jwt.Token.SignedString] + Sign(claims jwt.Claims) (string, error) +} + +// RSASigner signs JWT tokens using RSA keys. +type RSASigner struct { + method *jwt.SigningMethodRSA + key *rsa.PrivateKey +} + +// NewRSASigner creates a new RSASigner with the given RSA key. +func NewRSASigner(method *jwt.SigningMethodRSA, key *rsa.PrivateKey) *RSASigner { + return &RSASigner{ + method: method, + key: key, + } +} + +// Sign signs the JWT claims with the RSA key. +func (s *RSASigner) Sign(claims jwt.Claims) (string, error) { + return jwt.NewWithClaims(s.method, claims).SignedString(s.key) +} + +// AppsTransportOption is a func option for configuring an AppsTransport. +type AppsTransportOption func(*AppsTransport) + +// WithSigner configures the AppsTransport to use the given Signer for generating JWT tokens. +func WithSigner(signer Signer) AppsTransportOption { + return func(at *AppsTransport) { + at.signer = signer + } +} + +// NewTestAppsTransport creates a new AppsTransport for testing purposes. +func NewTestAppsTransport(baseURL string) *AppsTransport { + pk, _ := rsa.GenerateKey(rand.Reader, 2048) + + return &AppsTransport{ + BaseURL: baseURL, + Client: &http.Client{Transport: http.DefaultTransport}, + tr: http.DefaultTransport, + signer: &RSASigner{ + method: jwt.SigningMethodRS256, + key: pk, + }, + appID: 1, + } +} diff --git a/scm/github/app_transport_test.go b/scm/github/app_transport_test.go new file mode 100644 index 000000000..3bf27443c --- /dev/null +++ b/scm/github/app_transport_test.go @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: Apache-2.0 + +package github + +import ( + "io" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/gin-gonic/gin" + "github.com/google/go-cmp/cmp" +) + +func TestGitHub_cloneRequest(t *testing.T) { + tests := []struct { + name string + request *http.Request + }{ + { + name: "basic request", + request: &http.Request{ + Method: "GET", + URL: &url.URL{ + Scheme: "https", + Path: "/", + }, + Header: http.Header{ + "Accept": []string{"application/json"}, + }, + }, + }, + { + name: "request with body", + request: &http.Request{ + Method: "POST", + URL: &url.URL{ + Scheme: "https", + Path: "/", + }, + Header: http.Header{ + "Content-Type": []string{"application/json"}, + }, + Body: io.NopCloser(strings.NewReader(`{"key":"value"}`)), + }, + }, + { + name: "request with multiple headers", + request: &http.Request{ + Method: "GET", + URL: &url.URL{ + Scheme: "https", + Path: "/", + }, + Header: http.Header{ + "Accept": []string{"application/json"}, + "Authorization": []string{"Bearer token"}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + clonedReq := cloneRequest(tt.request) + + if clonedReq == tt.request { + t.Errorf("cloneRequest() = %v, want different instance", clonedReq) + } + + if diff := cmp.Diff(clonedReq.Header, tt.request.Header); diff != "" { + t.Errorf("cloneRequest() headers mismatch (-want +got):\n%s", diff) + } + + if clonedReq.Method != tt.request.Method { + t.Errorf("cloneRequest() method = %v, want %v", clonedReq.Method, tt.request.Method) + } + + if clonedReq.URL.String() != tt.request.URL.String() { + t.Errorf("cloneRequest() URL = %v, want %v", clonedReq.URL, tt.request.URL) + } + }) + } +} + +func TestAppsTransport_RoundTrip(t *testing.T) { + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.GET("/", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + }) + + s := httptest.NewServer(engine) + defer s.Close() + + _url, _ := url.Parse(s.URL) + + tests := []struct { + name string + transport *AppsTransport + request *http.Request + wantHeader string + wantErr bool + }{ + { + name: "valid GET request", + transport: NewTestAppsTransport(s.URL), + request: &http.Request{ + Method: "GET", + URL: _url, + Header: http.Header{ + "Accept": []string{"application/json"}, + }, + }, + wantHeader: "Bearer ", + wantErr: false, + }, + { + name: "valid POST request", + transport: NewTestAppsTransport(s.URL), + request: &http.Request{ + Method: "POST", + URL: _url, + Header: http.Header{ + "Content-Type": []string{"application/json"}, + }, + Body: io.NopCloser(strings.NewReader(`{"key":"value"}`)), + }, + wantHeader: "Bearer ", + wantErr: false, + }, + { + name: "request with invalid URL", + transport: NewTestAppsTransport(s.URL), + request: &http.Request{ + Method: "GET", + URL: &url.URL{Path: "://invalid-url"}, + Header: http.Header{}, + }, + wantHeader: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resp, err := tt.transport.RoundTrip(tt.request) + if (err != nil) != tt.wantErr { + t.Errorf("RoundTrip() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if got := tt.request.Header.Get("Authorization"); !strings.HasPrefix(got, tt.wantHeader) { + t.Errorf("RoundTrip() Authorization header = %v, want prefix %v", got, tt.wantHeader) + } + } + if resp != nil { + resp.Body.Close() + } + }) + } +} diff --git a/scm/github/authentication.go b/scm/github/authentication.go index 30376991f..bd1f4d62c 100644 --- a/scm/github/authentication.go +++ b/scm/github/authentication.go @@ -21,7 +21,7 @@ func (c *client) Authorize(ctx context.Context, token string) (string, error) { c.Logger.Trace("authorizing user with token") // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, token) + client := c.newOAuthTokenClient(ctx, token) // send API call to capture the current user making the call u, _, err := client.Users.Get(ctx, "") diff --git a/scm/github/changeset.go b/scm/github/changeset.go index 2aa07a445..7a9732fc4 100644 --- a/scm/github/changeset.go +++ b/scm/github/changeset.go @@ -21,7 +21,7 @@ func (c *client) Changeset(ctx context.Context, r *api.Repo, sha string) ([]stri }).Tracef("capturing commit changeset for %s/commit/%s", r.GetFullName(), sha) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, r.GetOwner().GetToken()) + client := c.newOAuthTokenClient(ctx, r.GetOwner().GetToken()) s := []string{} // set the max per page for the options to capture the commit @@ -50,7 +50,7 @@ func (c *client) ChangesetPR(ctx context.Context, r *api.Repo, number int) ([]st }).Tracef("capturing pull request changeset for %s/pull/%d", r.GetFullName(), number) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, r.GetOwner().GetToken()) + client := c.newOAuthTokenClient(ctx, r.GetOwner().GetToken()) s := []string{} f := []*github.CommitFile{} diff --git a/scm/github/deployment.go b/scm/github/deployment.go index e7f8ed0b4..f1c32db88 100644 --- a/scm/github/deployment.go +++ b/scm/github/deployment.go @@ -22,7 +22,7 @@ func (c *client) GetDeployment(ctx context.Context, u *api.User, r *api.Repo, id }).Tracef("capturing deployment %d for repo %s", id, r.GetFullName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // send API call to capture the deployment deployment, _, err := client.Repositories.GetDeployment(ctx, r.GetOrg(), r.GetName(), id) @@ -63,7 +63,7 @@ func (c *client) GetDeploymentCount(ctx context.Context, u *api.User, r *api.Rep }).Tracef("counting deployments for repo %s", r.GetFullName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // create variable to track the deployments deployments := []*github.Deployment{} @@ -105,7 +105,7 @@ func (c *client) GetDeploymentList(ctx context.Context, u *api.User, r *api.Repo }).Tracef("listing deployments for repo %s", r.GetFullName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // set pagination options for listing deployments opts := &github.DeploymentsListOptions{ @@ -164,7 +164,7 @@ func (c *client) CreateDeployment(ctx context.Context, u *api.User, r *api.Repo, }).Tracef("creating deployment for repo %s", r.GetFullName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) var payload interface{} if d.Payload == nil { diff --git a/scm/github/driver_test.go b/scm/github/driver_test.go index 7ea6656d8..9315cb626 100644 --- a/scm/github/driver_test.go +++ b/scm/github/driver_test.go @@ -3,6 +3,7 @@ package github import ( + "context" "reflect" "testing" @@ -14,6 +15,7 @@ func TestGitHub_Driver(t *testing.T) { want := constants.DriverGithub _service, err := New( + context.Background(), WithAddress("https://github.com/"), WithClientID("foo"), WithClientSecret("bar"), diff --git a/scm/github/github.go b/scm/github/github.go index dfbffe53a..b8a3082df 100644 --- a/scm/github/github.go +++ b/scm/github/github.go @@ -4,14 +4,16 @@ package github import ( "context" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" "fmt" - "net/http/httptrace" - "net/url" + "os" + "strings" "github.com/google/go-github/v65/github" "github.com/sirupsen/logrus" - "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" - "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "golang.org/x/oauth2" "github.com/go-vela/server/tracing" @@ -39,6 +41,14 @@ type config struct { ClientID string // specifies the OAuth client secret from GitHub to use for the GitHub client ClientSecret string + // specifies the ID for the Vela GitHub App + AppID int64 + // specifies the App private key to use for the GitHub client when interacting with App resources + AppPrivateKey string + // specifies the App private key to use for the GitHub client when interacting with App resources + AppPrivateKeyPath string + // specifics the App permissions set + AppPermissions []string // specifies the Vela server address to use for the GitHub client ServerAddress string // specifies the Vela server address that the scm provider should use to send Vela webhooks @@ -48,14 +58,15 @@ type config struct { // specifies the Vela web UI address to use for the GitHub client WebUIAddress string // specifies the OAuth scopes to use for the GitHub client - Scopes []string + OAuthScopes []string } type client struct { - config *config - OAuth *oauth2.Config - AuthReq *github.AuthorizationRequest - Tracing *tracing.Client + config *config + OAuth *oauth2.Config + AuthReq *github.AuthorizationRequest + Tracing *tracing.Client + AppsTransport *AppsTransport // https://pkg.go.dev/github.com/sirupsen/logrus#Entry Logger *logrus.Entry } @@ -64,7 +75,7 @@ type client struct { // a GitHub or a GitHub Enterprise instance. // //nolint:revive // ignore returning unexported client -func New(opts ...ClientOpt) (*client, error) { +func New(ctx context.Context, opts ...ClientOpt) (*client, error) { // create new GitHub client c := new(client) @@ -95,28 +106,133 @@ func New(opts ...ClientOpt) (*client, error) { c.OAuth = &oauth2.Config{ ClientID: c.config.ClientID, ClientSecret: c.config.ClientSecret, - Scopes: c.config.Scopes, + Scopes: c.config.OAuthScopes, Endpoint: oauth2.Endpoint{ AuthURL: fmt.Sprintf("%s/login/oauth/authorize", c.config.Address), TokenURL: fmt.Sprintf("%s/login/oauth/access_token", c.config.Address), }, } - var githubScopes []github.Scope - for _, scope := range c.config.Scopes { - githubScopes = append(githubScopes, github.Scope(scope)) + var oauthScopes []github.Scope + for _, scope := range c.config.OAuthScopes { + oauthScopes = append(oauthScopes, github.Scope(scope)) } // create the GitHub authorization object c.AuthReq = &github.AuthorizationRequest{ ClientID: &c.config.ClientID, ClientSecret: &c.config.ClientSecret, - Scopes: githubScopes, + Scopes: oauthScopes, + } + + var err error + + if c.config.AppID != 0 { + c.Logger.Infof("configurating github app integration for app_id %d", c.config.AppID) + + var privateKeyPEM []byte + + if len(c.config.AppPrivateKey) == 0 && len(c.config.AppPrivateKeyPath) == 0 { + return nil, errors.New("GitHub App ID provided but no valid private key was provided in either VELA_SCM_APP_PRIVATE_KEY or VELA_SCM_APP_PRIVATE_KEY_PATH") + } + + if len(c.config.AppPrivateKey) > 0 { + privateKeyPEM, err = base64.StdEncoding.DecodeString(c.config.AppPrivateKey) + if err != nil { + return nil, fmt.Errorf("error decoding base64: %w", err) + } + } else { + // try reading from path if necessary + c.Logger.Infof("no VELA_SCM_APP_PRIVATE_KEY provided, reading github app private key from path %s", c.config.AppPrivateKeyPath) + + privateKeyPEM, err = os.ReadFile(c.config.AppPrivateKeyPath) + if err != nil { + return nil, err + } + } + + if len(privateKeyPEM) == 0 { + return nil, errors.New("GitHub App ID provided but no valid private key was provided in either VELA_SCM_APP_PRIVATE_KEY or VELA_SCM_APP_PRIVATE_KEY_PATH") + } + + block, _ := pem.Decode(privateKeyPEM) + if block == nil { + return nil, fmt.Errorf("failed to parse GitHub App private key PEM block containing the key") + } + + parsedPrivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse GitHub App RSA private key: %w", err) + } + + c.AppsTransport = c.newGitHubAppTransport(c.config.AppID, c.config.API, parsedPrivateKey) + + err = c.ValidateGitHubApp(ctx) + if err != nil { + return nil, err + } } return c, nil } +// ValidateGitHubApp ensures the GitHub App configuration is valid. +func (c *client) ValidateGitHubApp(ctx context.Context) error { + client, err := c.newGithubAppClient() + if err != nil { + return fmt.Errorf("error creating github app client: %w", err) + } + + app, _, err := client.Apps.Get(ctx, "") + if err != nil { + return fmt.Errorf("error getting github app: %w", err) + } + + appPermissions := app.GetPermissions() + + type perm struct { + resource string + requiredPermission string + actualPermission string + } + + // the GitHub App installation requires the same permissions as provided at runtime + requiredPermissions := []perm{} + + // retrieve the required permissions for checking + for _, permission := range c.config.AppPermissions { + splitPerm := strings.Split(permission, ":") + if len(splitPerm) != 2 { + return fmt.Errorf("invalid app permission format %s, expected resource:permission", permission) + } + + resource := splitPerm[0] + requiredPermission := splitPerm[1] + + actual, err := GetInstallationPermission(resource, appPermissions) + if err != nil { + return err + } + + perm := perm{ + resource: resource, + requiredPermission: requiredPermission, + actualPermission: actual, + } + requiredPermissions = append(requiredPermissions, perm) + } + + // verify the app permissions + for _, p := range requiredPermissions { + err := InstallationHasPermission(p.resource, p.requiredPermission, p.actualPermission) + if err != nil { + return err + } + } + + return nil +} + // NewTest returns a SCM implementation that integrates with the provided // mock server. Only the url from the mock server is required. // @@ -133,6 +249,7 @@ func NewTest(urls ...string) (*client, error) { } return New( + context.Background(), WithAddress(address), WithClientID("foo"), WithClientSecret("bar"), @@ -143,39 +260,3 @@ func NewTest(urls ...string) (*client, error) { WithTracing(&tracing.Client{Config: tracing.Config{EnableTracing: false}}), ) } - -// helper function to return the GitHub OAuth client. -func (c *client) newClientToken(ctx context.Context, token string) *github.Client { - // create the token object for the client - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - - // create the OAuth client - tc := oauth2.NewClient(ctx, ts) - // if c.SkipVerify { - // tc.Transport.(*oauth2.Transport).Base = &http.Transport{ - // Proxy: http.ProxyFromEnvironment, - // TLSClientConfig: &tls.Config{ - // InsecureSkipVerify: true, - // }, - // } - // } - - if c.Tracing.Config.EnableTracing { - tc.Transport = otelhttp.NewTransport( - tc.Transport, - otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace { - return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans()) - }), - ) - } - - // create the GitHub client from the OAuth client - github := github.NewClient(tc) - - // ensure the proper URL is set in the GitHub client - github.BaseURL, _ = url.Parse(c.config.API) - - return github -} diff --git a/scm/github/github_client.go b/scm/github/github_client.go new file mode 100644 index 000000000..ac79436c8 --- /dev/null +++ b/scm/github/github_client.go @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: Apache-2.0 + +package github + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/http/httptrace" + "net/url" + "strings" + + "github.com/google/go-github/v65/github" + "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "golang.org/x/oauth2" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" +) + +// newOAuthTokenClient returns the GitHub OAuth client. +func (c *client) newOAuthTokenClient(ctx context.Context, token string) *github.Client { + // create the token object for the client + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + + // create the OAuth client + tc := oauth2.NewClient(ctx, ts) + + if c.Tracing.Config.EnableTracing { + tc.Transport = otelhttp.NewTransport( + tc.Transport, + otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace { + return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans()) + }), + ) + } + + // create the GitHub client from the OAuth client + github := github.NewClient(tc) + + // ensure the proper URL is set in the GitHub client + github.BaseURL, _ = url.Parse(c.config.API) + + return github +} + +// newGithubAppClient returns the GitHub App client for authenticating as the GitHub App itself using the RoundTripper. +func (c *client) newGithubAppClient() (*github.Client, error) { + if c.AppsTransport == nil { + return nil, errors.New("unable to create github app client: no AppsTransport configured") + } + + // create a github client based off the existing GitHub App configuration + client, err := github.NewClient( + &http.Client{ + Transport: c.AppsTransport, + }). + WithEnterpriseURLs(c.config.API, c.config.API) + if err != nil { + return nil, err + } + + return client, nil +} + +// newGithubAppInstallationRepoToken returns the GitHub App installation token for a particular repo with granular permissions. +func (c *client) newGithubAppInstallationRepoToken(ctx context.Context, r *api.Repo, repos []string, permissions *github.InstallationPermissions) (*github.InstallationToken, int64, error) { + // create a github client based off the existing GitHub App configuration + client, err := c.newGithubAppClient() + if err != nil { + return nil, 0, err + } + + opts := &github.InstallationTokenOptions{ + Repositories: repos, + Permissions: permissions, + } + + id := r.GetInstallID() + + // if the source scm repo has an install ID but the Vela db record does not + // then use the source repo to create an installation token + if id == 0 { + // list all installations (a.k.a. orgs) where the GitHub App is installed + installations, _, err := client.Apps.ListInstallations(ctx, &github.ListOptions{}) + if err != nil { + return nil, 0, err + } + + // iterate through the list of installations + for _, install := range installations { + // find the installation that matches the org for the repo + if strings.EqualFold(install.GetAccount().GetLogin(), r.GetOrg()) { + if install.GetRepositorySelection() == constants.AppInstallRepositoriesSelectionSelected { + installationCanReadRepo, err := c.installationCanReadRepo(ctx, r, install) + if err != nil { + return nil, 0, fmt.Errorf("installation for org %s exists but unable to check if it can read repo %s: %w", install.GetAccount().GetLogin(), r.GetFullName(), err) + } + + if !installationCanReadRepo { + return nil, 0, fmt.Errorf("installation for org %s exists but does not have access to repo %s", install.GetAccount().GetLogin(), r.GetFullName()) + } + } + + id = install.GetID() + } + } + } + + // failsafe in case the repo does not belong to an org where the GitHub App is installed + if id == 0 { + return nil, 0, errors.New("unable to find installation ID for repo") + } + + // create installation token for the repo + t, _, err := client.Apps.CreateInstallationToken(ctx, id, opts) + if err != nil { + return nil, 0, err + } + + return t, id, nil +} + +// installationCanReadRepo checks if the installation can read the repo. +func (c *client) installationCanReadRepo(ctx context.Context, r *api.Repo, installation *github.Installation) (bool, error) { + installationCanReadRepo := false + + if installation.GetRepositorySelection() == constants.AppInstallRepositoriesSelectionSelected { + client, err := c.newGithubAppClient() + if err != nil { + return false, err + } + + t, _, err := client.Apps.CreateInstallationToken(ctx, installation.GetID(), &github.InstallationTokenOptions{}) + if err != nil { + return false, err + } + + client = c.newOAuthTokenClient(ctx, t.GetToken()) + + repos, _, err := client.Apps.ListRepos(ctx, &github.ListOptions{}) + if err != nil { + return false, err + } + + for _, repo := range repos.Repositories { + if strings.EqualFold(repo.GetFullName(), r.GetFullName()) { + installationCanReadRepo = true + break + } + } + } + + return installationCanReadRepo, nil +} diff --git a/scm/github/github_client_test.go b/scm/github/github_client_test.go new file mode 100644 index 000000000..57de6f7d7 --- /dev/null +++ b/scm/github/github_client_test.go @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: Apache-2.0 + +package github + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/google/go-github/v65/github" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" +) + +func TestClient_installationCanReadRepo(t *testing.T) { + // setup types + accessibleRepo := new(api.Repo) + accessibleRepo.SetOrg("octocat") + accessibleRepo.SetName("Hello-World") + accessibleRepo.SetFullName("octocat/Hello-World") + accessibleRepo.SetInstallID(0) + + inaccessibleRepo := new(api.Repo) + inaccessibleRepo.SetOrg("octocat") + inaccessibleRepo.SetName("Hello-World") + inaccessibleRepo.SetFullName("octocat/Hello-World2") + inaccessibleRepo.SetInstallID(4) + + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.POST("/api/v3/app/installations/:id/access_tokens", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("testdata/installations_access_tokens.json") + }) + engine.GET("/api/v3/installation/repositories", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("testdata/installation_repositories.json") + }) + + s := httptest.NewServer(engine) + defer s.Close() + + oauthClient, _ := NewTest(s.URL) + + appsClient, err := NewTest(s.URL) + if err != nil { + t.Errorf("unable to create GitHub App client: %v", err) + } + + appsClient.AppsTransport = NewTestAppsTransport("") + + // setup tests + tests := []struct { + name string + client *client + repo *api.Repo + installation *github.Installation + appsTransport bool + want bool + wantErr bool + }{ + { + name: "installation can read repo", + client: appsClient, + repo: accessibleRepo, + installation: &github.Installation{ + ID: github.Int64(1), + Account: &github.User{ + Login: github.String("github"), + }, + RepositorySelection: github.String(constants.AppInstallRepositoriesSelectionSelected), + }, + want: true, + wantErr: false, + }, + { + name: "installation cannot read repo", + client: appsClient, + repo: inaccessibleRepo, + installation: &github.Installation{ + ID: github.Int64(2), + Account: &github.User{ + Login: github.String("github"), + }, + RepositorySelection: github.String(constants.AppInstallRepositoriesSelectionSelected), + }, + want: false, + wantErr: false, + }, + { + name: "no GitHub App client", + client: oauthClient, + repo: accessibleRepo, + installation: &github.Installation{ + ID: github.Int64(1), + Account: &github.User{ + Login: github.String("github"), + }, + RepositorySelection: github.String(constants.AppInstallRepositoriesSelectionSelected), + }, + want: false, + wantErr: true, + }, + } + + // run tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.client.installationCanReadRepo(context.Background(), tt.repo, tt.installation) + if (err != nil) != tt.wantErr { + t.Errorf("installationCanReadRepo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("installationCanReadRepo() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/scm/github/github_test.go b/scm/github/github_test.go index 963aa8076..3ce90df3a 100644 --- a/scm/github/github_test.go +++ b/scm/github/github_test.go @@ -32,14 +32,14 @@ func TestGithub_New(t *testing.T) { // run tests for _, test := range tests { - _, err := New( + _, err := New(context.Background(), WithAddress("https://github.com/"), WithClientID(test.id), WithClientSecret("bar"), WithServerAddress("https://vela-server.example.com"), WithStatusContext("continuous-integration/vela"), WithWebUIAddress("https://vela.example.com"), - WithScopes([]string{"repo", "repo:status", "user:email", "read:user", "read:org"}), + WithOAuthScopes([]string{"repo", "repo:status", "user:email", "read:user", "read:org"}), ) if test.failure { @@ -72,7 +72,7 @@ func TestGithub_newClientToken(t *testing.T) { client, _ := NewTest(s.URL) // run test - got := client.newClientToken(context.Background(), "foobar") + got := client.newOAuthTokenClient(context.Background(), "foobar") //nolint:staticcheck // ignore false positive if got == nil { diff --git a/scm/github/opts.go b/scm/github/opts.go index bb7385827..ad64cae0b 100644 --- a/scm/github/opts.go +++ b/scm/github/opts.go @@ -135,8 +135,8 @@ func WithWebUIAddress(address string) ClientOpt { } } -// WithScopes sets the OAuth scopes in the scm client for GitHub. -func WithScopes(scopes []string) ClientOpt { +// WithOAuthScopes sets the OAuth scopes in the scm client for GitHub. +func WithOAuthScopes(scopes []string) ClientOpt { return func(c *client) error { c.Logger.Trace("configuring oauth scopes in github scm client") @@ -146,7 +146,7 @@ func WithScopes(scopes []string) ClientOpt { } // set the scopes in the github client - c.config.Scopes = scopes + c.config.OAuthScopes = scopes return nil } @@ -160,3 +160,50 @@ func WithTracing(tracing *tracing.Client) ClientOpt { return nil } } + +// WithGithubAppID sets the ID for the GitHub App in the scm client. +func WithGithubAppID(id int64) ClientOpt { + return func(c *client) error { + c.Logger.Trace("configuring ID for GitHub App in github scm client") + + // set the ID for the GitHub App in the github client + c.config.AppID = id + + return nil + } +} + +// WithGithubPrivateKey sets the private key for the GitHub App in the scm client. +func WithGithubPrivateKey(key string) ClientOpt { + return func(c *client) error { + c.Logger.Trace("configuring private key for GitHub App in github scm client") + + // set the private key for the GitHub App in the github client + c.config.AppPrivateKey = key + + return nil + } +} + +// WithGithubPrivateKeyPath sets the private key path for the GitHub App in the scm client. +func WithGithubPrivateKeyPath(path string) ClientOpt { + return func(c *client) error { + c.Logger.Trace("configuring private key path for GitHub App in github scm client") + + // set the private key for the GitHub App in the github client + c.config.AppPrivateKeyPath = path + + return nil + } +} + +// WithGitHubAppPermissions sets the App permissions in the scm client for GitHub. +func WithGitHubAppPermissions(permissions []string) ClientOpt { + return func(c *client) error { + c.Logger.Trace("configuring app permissions in github scm client") + + c.config.AppPermissions = permissions + + return nil + } +} diff --git a/scm/github/opts_test.go b/scm/github/opts_test.go index 8a6a2617e..a2aac20bc 100644 --- a/scm/github/opts_test.go +++ b/scm/github/opts_test.go @@ -3,9 +3,12 @@ package github import ( + "context" "reflect" "testing" + "github.com/google/go-cmp/cmp" + "github.com/go-vela/server/tracing" ) @@ -33,7 +36,7 @@ func TestGithub_ClientOpt_WithAddress(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithAddress(test.address), ) @@ -72,7 +75,7 @@ func TestGithub_ClientOpt_WithClientID(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithClientID(test.id), ) @@ -115,7 +118,7 @@ func TestGithub_ClientOpt_WithClientSecret(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithClientSecret(test.secret), ) @@ -158,7 +161,7 @@ func TestGithub_ClientOpt_WithServerAddress(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithServerAddress(test.address), ) @@ -210,7 +213,7 @@ func TestGithub_ClientOpt_WithServerWebhookAddress(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithServerAddress(test.address), WithServerWebhookAddress(test.webhookAddress), ) @@ -254,7 +257,7 @@ func TestGithub_ClientOpt_WithStatusContext(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithStatusContext(test.context), ) @@ -294,7 +297,7 @@ func TestGithub_ClientOpt_WithWebUIAddress(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithWebUIAddress(test.address), ) @@ -308,7 +311,7 @@ func TestGithub_ClientOpt_WithWebUIAddress(t *testing.T) { } } -func TestGithub_ClientOpt_WithScopes(t *testing.T) { +func TestGithub_ClientOpt_WithOAuthScopes(t *testing.T) { // setup tests tests := []struct { failure bool @@ -329,24 +332,24 @@ func TestGithub_ClientOpt_WithScopes(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( - WithScopes(test.scopes), + _service, err := New(context.Background(), + WithOAuthScopes(test.scopes), ) if test.failure { if err == nil { - t.Errorf("WithScopes should have returned err") + t.Errorf("WithOAuthScopes should have returned err") } continue } if err != nil { - t.Errorf("WithScopes returned err: %v", err) + t.Errorf("WithOAuthScopes returned err: %v", err) } - if !reflect.DeepEqual(_service.config.Scopes, test.want) { - t.Errorf("WithScopes is %v, want %v", _service.config.Scopes, test.want) + if !reflect.DeepEqual(_service.config.OAuthScopes, test.want) { + t.Errorf("WithOAuthScopes is %v, want %v", _service.config.OAuthScopes, test.want) } } } @@ -367,7 +370,7 @@ func TestGithub_ClientOpt_WithTracing(t *testing.T) { // run tests for _, test := range tests { - _service, err := New( + _service, err := New(context.Background(), WithTracing(test.tracing), ) @@ -388,3 +391,46 @@ func TestGithub_ClientOpt_WithTracing(t *testing.T) { } } } + +func TestGithub_ClientOpt_WithGitHubAppPermissions(t *testing.T) { + // setup tests + tests := []struct { + failure bool + permissions []string + want []string + }{ + { + failure: false, + permissions: []string{"contents:read"}, + want: []string{"contents:read"}, + }, + { + failure: false, + permissions: []string{}, + want: []string{}, + }, + } + + // run tests + for _, test := range tests { + _service, err := New(context.Background(), + WithGitHubAppPermissions(test.permissions), + ) + + if test.failure { + if err == nil { + t.Errorf("WithGitHubAppPermissions should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("WithGitHubAppPermissions returned err: %v", err) + } + + if diff := cmp.Diff(test.want, _service.config.AppPermissions); diff != "" { + t.Errorf("WithGitHubAppPermissions mismatch (-want +got):\n%s", diff) + } + } +} diff --git a/scm/github/org.go b/scm/github/org.go index 8c9e95986..ec1f8e314 100644 --- a/scm/github/org.go +++ b/scm/github/org.go @@ -19,7 +19,7 @@ func (c *client) GetOrgName(ctx context.Context, u *api.User, o string) (string, }).Tracef("retrieving org information for %s", o) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, u.GetToken()) + client := c.newOAuthTokenClient(ctx, u.GetToken()) // send an API call to get the org info orgInfo, resp, err := client.Organizations.Get(ctx, o) diff --git a/scm/github/repo.go b/scm/github/repo.go index 3cf9dc760..d1648caa6 100644 --- a/scm/github/repo.go +++ b/scm/github/repo.go @@ -14,7 +14,9 @@ import ( "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/compiler/types/yaml" "github.com/go-vela/server/constants" + "github.com/go-vela/server/database" ) // ConfigBackoff is a wrapper for Config that will retry five times if the function @@ -55,7 +57,7 @@ func (c *client) Config(ctx context.Context, u *api.User, r *api.Repo, ref strin }).Tracef("capturing configuration file for %s/commit/%s", r.GetFullName(), ref) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // default pipeline file names files := []string{".vela.yml", ".vela.yaml"} @@ -95,6 +97,11 @@ func (c *client) Config(ctx context.Context, u *api.User, r *api.Repo, ref strin // Disable deactivates a repo by deleting the webhook. func (c *client) Disable(ctx context.Context, u *api.User, org, name string) error { + return c.DestroyWebhook(ctx, u, org, name) +} + +// DestroyWebhook deletes a repo's webhook. +func (c *client) DestroyWebhook(ctx context.Context, u *api.User, org, name string) error { c.Logger.WithFields(logrus.Fields{ "org": org, "repo": name, @@ -102,7 +109,7 @@ func (c *client) Disable(ctx context.Context, u *api.User, org, name string) err }).Tracef("deleting repository webhooks for %s/%s", org, name) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // send API call to capture the hooks for the repo hooks, _, err := client.Repositories.ListHooks(ctx, org, name, nil) @@ -150,6 +157,11 @@ func (c *client) Disable(ctx context.Context, u *api.User, org, name string) err // Enable activates a repo by creating the webhook. func (c *client) Enable(ctx context.Context, u *api.User, r *api.Repo, h *api.Hook) (*api.Hook, string, error) { + return c.CreateWebhook(ctx, u, r, h) +} + +// CreateWebhook creates a repo's webhook. +func (c *client) CreateWebhook(ctx context.Context, u *api.User, r *api.Repo, h *api.Hook) (*api.Hook, string, error) { c.Logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -157,7 +169,7 @@ func (c *client) Enable(ctx context.Context, u *api.User, r *api.Repo, h *api.Ho }).Tracef("creating repository webhook for %s/%s", r.GetOrg(), r.GetName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // always listen to repository events in case of repo name change events := []string{eventRepository} @@ -231,7 +243,7 @@ func (c *client) Update(ctx context.Context, u *api.User, r *api.Repo, hookID in }).Tracef("updating repository webhook for %s/%s", r.GetOrg(), r.GetName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // always listen to repository events in case of repo name change events := []string{eventRepository} @@ -295,7 +307,7 @@ func (c *client) Status(ctx context.Context, u *api.User, b *api.Build, org, nam } // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) context := fmt.Sprintf("%s/%s", c.config.StatusContext, b.GetEvent()) url := fmt.Sprintf("%s/%s/%s/%d", c.config.WebUIAddress, org, name, b.GetNumber()) @@ -414,7 +426,7 @@ func (c *client) StepStatus(ctx context.Context, u *api.User, b *api.Build, s *a } // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) context := fmt.Sprintf("%s/%s/%s", c.config.StatusContext, b.GetEvent(), s.GetReportAs()) url := fmt.Sprintf("%s/%s/%s/%d#%d", c.config.WebUIAddress, org, name, b.GetNumber(), s.GetNumber()) @@ -477,7 +489,7 @@ func (c *client) GetRepo(ctx context.Context, u *api.User, r *api.Repo) (*api.Re }).Tracef("retrieving repository information for %s", r.GetFullName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, u.GetToken()) + client := c.newOAuthTokenClient(ctx, u.GetToken()) // send an API call to get the repo info repo, resp, err := client.Repositories.Get(ctx, r.GetOrg(), r.GetName()) @@ -497,7 +509,7 @@ func (c *client) GetOrgAndRepoName(ctx context.Context, u *api.User, o string, r }).Tracef("retrieving repository information for %s/%s", o, r) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, u.GetToken()) + client := c.newOAuthTokenClient(ctx, u.GetToken()) // send an API call to get the repo info repo, _, err := client.Repositories.Get(ctx, o, r) @@ -515,7 +527,7 @@ func (c *client) ListUserRepos(ctx context.Context, u *api.User) ([]*api.Repo, e }).Tracef("listing source repositories for %s", u.GetName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, u.GetToken()) + client := c.newOAuthTokenClient(ctx, u.GetToken()) r := []*github.Repository{} f := []*api.Repo{} @@ -595,7 +607,7 @@ func (c *client) GetPullRequest(ctx context.Context, r *api.Repo, number int) (s }).Tracef("retrieving pull request %d for repo %s", number, r.GetFullName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, r.GetOwner().GetToken()) + client := c.newOAuthTokenClient(ctx, r.GetOwner().GetToken()) pull, _, err := client.PullRequests.Get(ctx, r.GetOrg(), r.GetName(), number) if err != nil { @@ -619,7 +631,7 @@ func (c *client) GetHTMLURL(ctx context.Context, u *api.User, org, repo, name, r }).Tracef("capturing html_url for %s/%s/%s@%s", org, repo, name, ref) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newOAuthTokenClient(ctx, *u.Token) // set the reference for the options to capture the repository contents opts := &github.RepositoryContentGetOptions{ @@ -651,7 +663,7 @@ func (c *client) GetBranch(ctx context.Context, r *api.Repo, branch string) (str }).Tracef("retrieving branch %s for repo %s", branch, r.GetFullName()) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, r.GetOwner().GetToken()) + client := c.newOAuthTokenClient(ctx, r.GetOwner().GetToken()) maxRedirects := 3 @@ -662,3 +674,142 @@ func (c *client) GetBranch(ctx context.Context, r *api.Repo, branch string) (str return data.GetName(), data.GetCommit().GetSHA(), nil } + +// GetNetrcPassword returns a clone token using the repo's github app installation if it exists. +// If not, it defaults to the user OAuth token. +func (c *client) GetNetrcPassword(ctx context.Context, db database.Interface, r *api.Repo, u *api.User, g yaml.Git) (string, error) { + l := c.Logger.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + }) + + l.Tracef("getting netrc password for %s/%s", r.GetOrg(), r.GetName()) + + // no GitHub App configured, use legacy oauth token + if c.AppsTransport == nil { + return u.GetToken(), nil + } + + var err error + + // repos that the token has access to + // providing no repos, nil, or empty slice will default the token permissions to the list + // of repos added to the installation + repos := g.Repositories + + // use triggering repo as a restrictive default + if repos == nil { + repos = []string{r.GetName()} + } + + // convert repo fullname org/name to just name for usability + for i, repo := range repos { + split := strings.Split(repo, "/") + if len(split) == 2 { + repos[i] = split[1] + } + } + + // permissions that are applied to the token for every repo provided + // providing no permissions, nil, or empty map will default to the permissions + // of the GitHub App installation + // + // the Vela compiler follows a least-privileged-defaults model where + // the list contains only the triggering repo, unless provided in the git yaml block + // + // the default is contents:read and checks:write + ghPermissions := &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionRead), + Checks: github.String(AppInstallPermissionWrite), + } + + permissions := g.Permissions + if permissions == nil { + permissions = map[string]string{} + } + + for resource, perm := range permissions { + ghPermissions, err = ApplyInstallationPermissions(resource, perm, ghPermissions) + if err != nil { + return u.GetToken(), err + } + } + + // the app might not be installed therefore we retain backwards compatibility via the user oauth token + // https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation + // the optional list of repos and permissions are driven by yaml + installToken, installID, err := c.newGithubAppInstallationRepoToken(ctx, r, repos, ghPermissions) + if err != nil { + // return the legacy token along with no error for backwards compatibility + // todo: return an error based based on app installation requirements + l.Tracef("unable to create github app installation token for repos %v with permissions %v: %v", repos, permissions, err) + + return u.GetToken(), nil + } + + if installToken != nil && len(installToken.GetToken()) != 0 { + l.Tracef("using github app installation token for %s/%s", r.GetOrg(), r.GetName()) + + // (optional) sync the install ID with the repo + if db != nil && r.GetInstallID() != installID { + r.SetInstallID(installID) + + _, err = db.UpdateRepo(ctx, r) + if err != nil { + c.Logger.Tracef("unable to update repo with install ID %d: %v", installID, err) + } + } + + return installToken.GetToken(), nil + } + + l.Tracef("using user oauth token for %s/%s", r.GetOrg(), r.GetName()) + + return u.GetToken(), nil +} + +// SyncRepoWithInstallation ensures the repo is synchronized with the scm installation, if it exists. +func (c *client) SyncRepoWithInstallation(ctx context.Context, r *api.Repo) (*api.Repo, error) { + c.Logger.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("syncing app installation for repo %s/%s", r.GetOrg(), r.GetName()) + + // no GitHub App configured, skip + if c.AppsTransport == nil { + return r, nil + } + + client, err := c.newGithubAppClient() + if err != nil { + return r, err + } + + installations, _, err := client.Apps.ListInstallations(ctx, &github.ListOptions{}) + if err != nil { + return r, err + } + + var installation *github.Installation + + for _, install := range installations { + if strings.EqualFold(install.GetAccount().GetLogin(), r.GetOrg()) { + installation = install + } + } + + if installation == nil { + return r, nil + } + + installationCanReadRepo, err := c.installationCanReadRepo(ctx, r, installation) + if err != nil { + return r, err + } + + if installationCanReadRepo { + r.SetInstallID(installation.GetID()) + } + + return r, nil +} diff --git a/scm/github/repo_test.go b/scm/github/repo_test.go index 502c1cc7c..036639674 100644 --- a/scm/github/repo_test.go +++ b/scm/github/repo_test.go @@ -13,8 +13,11 @@ import ( "testing" "github.com/gin-gonic/gin" + "github.com/google/go-cmp/cmp" + "github.com/google/go-github/v65/github" api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/compiler/types/yaml" "github.com/go-vela/server/constants" ) @@ -1621,3 +1624,287 @@ func TestGithub_GetBranch(t *testing.T) { t.Errorf("Commit is %v, want %v", gotCommit, wantCommit) } } + +func TestGithub_GetNetrcPassword(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.GET("/api/v3/app/installations", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("testdata/installations.json") + }) + engine.POST("/api/v3/app/installations/:id/access_tokens", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("testdata/installations_access_tokens.json") + }) + + s := httptest.NewServer(engine) + defer s.Close() + + installedRepo := new(api.Repo) + installedRepo.SetOrg("octocat") + installedRepo.SetName("Hello-World") + installedRepo.SetInstallID(1) + + oauthRepo := new(api.Repo) + oauthRepo.SetOrg("octocat") + oauthRepo.SetName("Hello-World2") + oauthRepo.SetInstallID(0) + + u := new(api.User) + u.SetName("foo") + u.SetToken("bar") + + tests := []struct { + name string + repo *api.Repo + user *api.User + git yaml.Git + appsTransport bool + wantToken string + wantErr bool + }{ + { + name: "installation token", + repo: installedRepo, + user: u, + git: yaml.Git{ + Token: yaml.Token{ + Repositories: []string{"Hello-World"}, + Permissions: map[string]string{"contents": "read"}, + }, + }, + appsTransport: true, + wantToken: "ghs_16C7e42F292c6912E7710c838347Ae178B4a", + wantErr: false, + }, + { + name: "no app configured returns user oauth token", + repo: installedRepo, + user: u, + git: yaml.Git{ + Token: yaml.Token{ + Repositories: []string{"Hello-World"}, + Permissions: map[string]string{"contents": "read"}, + }, + }, + appsTransport: false, + wantToken: "bar", + wantErr: false, + }, + { + name: "repo not installed returns user oauth token", + repo: oauthRepo, + user: u, + git: yaml.Git{ + Token: yaml.Token{ + Repositories: []string{"Hello-World"}, + Permissions: map[string]string{"contents": "read"}, + }, + }, + appsTransport: true, + wantToken: "bar", + wantErr: false, + }, + { + name: "invalid permission resource", + repo: installedRepo, + user: u, + git: yaml.Git{ + Token: yaml.Token{ + Repositories: []string{"Hello-World"}, + Permissions: map[string]string{"invalid": "read"}, + }, + }, + appsTransport: true, + wantToken: "bar", + wantErr: true, + }, + { + name: "invalid permission level", + repo: installedRepo, + user: u, + git: yaml.Git{ + Token: yaml.Token{ + Repositories: []string{"Hello-World"}, + Permissions: map[string]string{"contents": "invalid"}, + }, + }, + appsTransport: true, + wantToken: "bar", + wantErr: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + client, _ := NewTest(s.URL) + if test.appsTransport { + client.AppsTransport = NewTestAppsTransport(s.URL) + } + + got, err := client.GetNetrcPassword(context.TODO(), nil, test.repo, test.user, test.git) + if (err != nil) != test.wantErr { + t.Errorf("GetNetrcPassword() error = %v, wantErr %v", err, test.wantErr) + return + } + if got != test.wantToken { + t.Errorf("GetNetrcPassword() = %v, want %v", got, test.wantToken) + } + }) + } +} + +func TestGithub_SyncRepoWithInstallation(t *testing.T) { + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + _, engine := gin.CreateTestContext(resp) + + // setup mock server + engine.GET("/api/v3/app/installations", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("testdata/installations.json") + }) + engine.POST("/api/v3/app/installations/:id/access_tokens", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("testdata/installations_access_tokens.json") + }) + engine.GET("/api/v3/installation/repositories", func(c *gin.Context) { + c.Header("Content-Type", "application/json") + c.Status(http.StatusOK) + c.File("testdata/installation_repositories.json") + }) + + s := httptest.NewServer(engine) + defer s.Close() + + tests := []struct { + name string + org string + repo string + wantInstallID int64 + wantStatusCode int + }{ + { + name: "match", + org: "octocat", + repo: "Hello-World", + wantInstallID: 1, + wantStatusCode: http.StatusOK, + }, + { + name: "no match", + repo: "octocat/Hello-World2", + wantInstallID: 0, + wantStatusCode: http.StatusOK, + }, + } + for _, test := range tests { + // setup types + r := new(api.Repo) + r.SetOrg(test.org) + r.SetName(test.repo) + r.SetFullName(fmt.Sprintf("%s/%s", test.org, test.repo)) + + client, _ := NewTest(s.URL) + client.AppsTransport = NewTestAppsTransport(s.URL) + + // run test + got, err := client.SyncRepoWithInstallation(context.TODO(), r) + + if resp.Code != test.wantStatusCode { + t.Errorf("SyncRepoWithInstallation %s returned %v, want %v", test.name, resp.Code, http.StatusOK) + } + + if err != nil { + t.Errorf("SyncRepoWithInstallation %s returned err: %v", test.name, err) + } + + if got.GetInstallID() != test.wantInstallID { + t.Errorf("SyncRepoWithInstallation %s returned %v, want %v", test.name, got.GetInstallID(), test.wantInstallID) + } + } +} + +func TestGithub_applyGitHubInstallationPermission(t *testing.T) { + tests := []struct { + name string + perms *github.InstallationPermissions + resource string + perm string + wantPerms *github.InstallationPermissions + wantErr bool + }{ + { + name: "valid read permission for contents", + perms: &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionNone), + }, + resource: AppInstallResourceContents, + perm: AppInstallPermissionRead, + wantPerms: &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionRead), + }, + wantErr: false, + }, + { + name: "valid write permission for checks", + perms: &github.InstallationPermissions{ + Checks: github.String(AppInstallPermissionNone), + }, + resource: AppInstallResourceChecks, + perm: AppInstallPermissionWrite, + wantPerms: &github.InstallationPermissions{ + Checks: github.String(AppInstallPermissionWrite), + }, + wantErr: false, + }, + { + name: "invalid permission value", + perms: &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionNone), + }, + resource: AppInstallResourceContents, + perm: "invalid", + wantPerms: &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionNone), + }, + wantErr: true, + }, + { + name: "invalid permission key", + perms: &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionNone), + }, + resource: "invalid", + perm: AppInstallPermissionRead, + wantPerms: &github.InstallationPermissions{ + Contents: github.String(AppInstallPermissionNone), + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ApplyInstallationPermissions(tt.resource, tt.perm, tt.perms) + if (err != nil) != tt.wantErr { + t.Errorf("ToGitHubAppInstallationPermissions() error = %v, wantErr %v", err, tt.wantErr) + return + } + if diff := cmp.Diff(tt.wantPerms, got); diff != "" { + t.Errorf("ToGitHubAppInstallationPermissions() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/scm/github/testdata/hooks/installation_created.json b/scm/github/testdata/hooks/installation_created.json new file mode 100644 index 000000000..c3904b286 --- /dev/null +++ b/scm/github/testdata/hooks/installation_created.json @@ -0,0 +1,100 @@ +{ + "action": "created", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/scm/github/testdata/hooks/installation_deleted.json b/scm/github/testdata/hooks/installation_deleted.json new file mode 100644 index 000000000..9972e0cf9 --- /dev/null +++ b/scm/github/testdata/hooks/installation_deleted.json @@ -0,0 +1,100 @@ +{ + "action": "deleted", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/scm/github/testdata/hooks/installation_repositories_added.json b/scm/github/testdata/hooks/installation_repositories_added.json new file mode 100644 index 000000000..f75fedcd1 --- /dev/null +++ b/scm/github/testdata/hooks/installation_repositories_added.json @@ -0,0 +1,103 @@ +{ + "action": "added", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories_added": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "repositories_removed": [ + + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } + } \ No newline at end of file diff --git a/scm/github/testdata/hooks/installation_repositories_removed.json b/scm/github/testdata/hooks/installation_repositories_removed.json new file mode 100644 index 000000000..b7a82ec5b --- /dev/null +++ b/scm/github/testdata/hooks/installation_repositories_removed.json @@ -0,0 +1,103 @@ +{ + "action": "removed", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories_added": [ + + ], + "repositories_removed": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } + } \ No newline at end of file diff --git a/scm/github/testdata/installation_repositories.json b/scm/github/testdata/installation_repositories.json new file mode 100644 index 000000000..9eb501cb5 --- /dev/null +++ b/scm/github/testdata/installation_repositories.json @@ -0,0 +1,123 @@ +{ + "total_count": 1, + "repositories": [ + { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "allow_rebase_merge": true, + "template_repository": null, + "temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://github.com/licenses/mit" + }, + "forks": 1, + "open_issues": 1, + "watchers": 1 + } + ] + } \ No newline at end of file diff --git a/scm/github/testdata/installations.json b/scm/github/testdata/installations.json new file mode 100644 index 000000000..736849702 --- /dev/null +++ b/scm/github/testdata/installations.json @@ -0,0 +1,52 @@ +[ + { + "id": 1, + "account": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "access_tokens_url": "https://api.github.com/app/installations/1/access_tokens", + "repositories_url": "https://api.github.com/installation/repositories", + "html_url": "https://github.com/organizations/github/settings/installations/1", + "app_id": 1, + "target_id": 1, + "target_type": "Organization", + "permissions": { + "checks": "write", + "metadata": "read", + "contents": "read" + }, + "events": [ + "push", + "pull_request" + ], + "single_file_name": "config.yaml", + "has_multiple_single_files": true, + "single_file_paths": [ + "config.yml", + ".github/issue_TEMPLATE.md" + ], + "repository_selection": "selected", + "created_at": "2017-07-08T16:18:44-04:00", + "updated_at": "2017-07-08T16:18:44-04:00", + "app_slug": "github-actions", + "suspended_at": null, + "suspended_by": null + } +] \ No newline at end of file diff --git a/scm/github/testdata/installations_access_tokens.json b/scm/github/testdata/installations_access_tokens.json new file mode 100644 index 000000000..86b705880 --- /dev/null +++ b/scm/github/testdata/installations_access_tokens.json @@ -0,0 +1,134 @@ +{ + "token": "ghs_16C7e42F292c6912E7710c838347Ae178B4a", + "expires_at": "2016-07-11T22:14:10Z", + "permissions": { + "issues": "write", + "contents": "read" + }, + "repository_selection": "selected", + "repositories": [ + { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "allow_rebase_merge": true, + "template_repository": null, + "temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O", + "allow_squash_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_merge_commit": true, + "subscribers_count": 42, + "network_count": 0, + "license": { + "key": "mit", + "name": "MIT License", + "url": "https://api.github.com/licenses/mit", + "spdx_id": "MIT", + "node_id": "MDc6TGljZW5zZW1pdA==", + "html_url": "https://github.com/licenses/mit" + }, + "forks": 1, + "open_issues": 1, + "watchers": 1 + } + ] + } \ No newline at end of file diff --git a/scm/github/user.go b/scm/github/user.go index d014256e4..3ceaf75ac 100644 --- a/scm/github/user.go +++ b/scm/github/user.go @@ -16,7 +16,7 @@ func (c *client) GetUserID(ctx context.Context, name string, token string) (stri }).Tracef("capturing SCM user id for %s", name) // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, token) + client := c.newOAuthTokenClient(ctx, token) // send API call to capture user user, _, err := client.Users.Get(ctx, name) diff --git a/scm/github/webhook.go b/scm/github/webhook.go index 61bdd2135..a23c6be1b 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -60,7 +60,6 @@ func (c *client) ProcessWebhook(ctx context.Context, request *http.Request) (*in // parse the payload from the webhook event, err := github.ParseWebHook(github.WebHookType(request), payload) - if err != nil { return &internal.Webhook{Hook: h}, nil } @@ -77,6 +76,10 @@ func (c *client) ProcessWebhook(ctx context.Context, request *http.Request) (*in return c.processIssueCommentEvent(h, event) case *github.RepositoryEvent: return c.processRepositoryEvent(h, event) + case *github.InstallationEvent: + return c.processInstallationEvent(ctx, h, event) + case *github.InstallationRepositoriesEvent: + return c.processInstallationRepositoriesEvent(ctx, h, event) } return &internal.Webhook{Hook: h}, nil @@ -100,7 +103,7 @@ func (c *client) VerifyWebhook(_ context.Context, request *http.Request, r *api. // RedeliverWebhook redelivers webhooks from GitHub. func (c *client) RedeliverWebhook(ctx context.Context, u *api.User, h *api.Hook) error { // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, u.GetToken()) + client := c.newOAuthTokenClient(ctx, u.GetToken()) // capture the delivery ID of the hook using GitHub API deliveryID, err := c.getDeliveryID(ctx, client, h) @@ -512,7 +515,6 @@ func (c *client) processIssueCommentEvent(h *api.Hook, payload *github.IssueComm } // processRepositoryEvent is a helper function to process the repository event. - func (c *client) processRepositoryEvent(h *api.Hook, payload *github.RepositoryEvent) (*internal.Webhook, error) { logrus.Tracef("processing repository event GitHub webhook for %s", payload.GetRepo().GetFullName()) @@ -543,6 +545,59 @@ func (c *client) processRepositoryEvent(h *api.Hook, payload *github.RepositoryE }, nil } +// processInstallationEvent is a helper function to process the installation event. +func (c *client) processInstallationEvent(_ context.Context, h *api.Hook, payload *github.InstallationEvent) (*internal.Webhook, error) { + h.SetEvent(constants.EventInstallation) + h.SetEventAction(payload.GetAction()) + + install := new(internal.Installation) + + install.Action = payload.GetAction() + install.ID = payload.GetInstallation().GetID() + install.Org = payload.GetInstallation().GetAccount().GetLogin() + + switch payload.GetAction() { + case constants.AppInstallCreated: + for _, repo := range payload.Repositories { + install.RepositoriesAdded = append(install.RepositoriesAdded, repo.GetName()) + } + case constants.AppInstallDeleted: + for _, repo := range payload.Repositories { + install.RepositoriesRemoved = append(install.RepositoriesRemoved, repo.GetName()) + } + } + + return &internal.Webhook{ + Hook: h, + Installation: install, + }, nil +} + +// processInstallationRepositoriesEvent is a helper function to process the installation repositories event. +func (c *client) processInstallationRepositoriesEvent(_ context.Context, h *api.Hook, payload *github.InstallationRepositoriesEvent) (*internal.Webhook, error) { + h.SetEvent(constants.EventInstallationRepositories) + h.SetEventAction(payload.GetAction()) + + install := new(internal.Installation) + + install.Action = payload.GetAction() + install.ID = payload.GetInstallation().GetID() + install.Org = payload.GetInstallation().GetAccount().GetLogin() + + for _, repo := range payload.RepositoriesAdded { + install.RepositoriesAdded = append(install.RepositoriesAdded, repo.GetName()) + } + + for _, repo := range payload.RepositoriesRemoved { + install.RepositoriesRemoved = append(install.RepositoriesRemoved, repo.GetName()) + } + + return &internal.Webhook{ + Hook: h, + Installation: install, + }, nil +} + // getDeliveryID gets the last 100 webhook deliveries for a repo and // finds the matching delivery id with the source id in the hook. func (c *client) getDeliveryID(ctx context.Context, ghClient *github.Client, h *api.Hook) (int64, error) { diff --git a/scm/github/webhook_test.go b/scm/github/webhook_test.go index c736a94d2..b9a118baa 100644 --- a/scm/github/webhook_test.go +++ b/scm/github/webhook_test.go @@ -1200,8 +1200,8 @@ func TestGitHub_ProcessWebhook_RepositoryRename(t *testing.T) { t.Errorf("ProcessWebhook returned err: %v", err) } - if !reflect.DeepEqual(got, want) { - t.Errorf("ProcessWebhook is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) } } @@ -1263,8 +1263,8 @@ func TestGitHub_ProcessWebhook_RepositoryTransfer(t *testing.T) { t.Errorf("ProcessWebhook returned err: %v", err) } - if !reflect.DeepEqual(got, want) { - t.Errorf("ProcessWebhook is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) } } @@ -1326,8 +1326,8 @@ func TestGitHub_ProcessWebhook_RepositoryArchived(t *testing.T) { t.Errorf("ProcessWebhook returned err: %v", err) } - if !reflect.DeepEqual(got, want) { - t.Errorf("ProcessWebhook is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) } } @@ -1389,8 +1389,8 @@ func TestGitHub_ProcessWebhook_RepositoryEdited(t *testing.T) { t.Errorf("ProcessWebhook returned err: %v", err) } - if !reflect.DeepEqual(got, want) { - t.Errorf("ProcessWebhook is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) } } @@ -1452,8 +1452,8 @@ func TestGitHub_ProcessWebhook_Repository(t *testing.T) { t.Errorf("ProcessWebhook returned err: %v", err) } - if !reflect.DeepEqual(got, want) { - t.Errorf("ProcessWebhook is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) } } @@ -1545,7 +1545,7 @@ func TestGithub_GetDeliveryID(t *testing.T) { client, _ := NewTest(s.URL, "https://foo.bar.com") - ghClient := client.newClientToken(context.Background(), *u.Token) + ghClient := client.newOAuthTokenClient(context.Background(), *u.Token) // run test got, err := client.getDeliveryID(context.TODO(), ghClient, _hook) @@ -1558,3 +1558,180 @@ func TestGithub_GetDeliveryID(t *testing.T) { t.Errorf("getDeliveryID returned: %v; want: %v", got, want) } } + +func TestGitHub_ProcessWebhook_Installation(t *testing.T) { + // setup tests + var createdHook api.Hook + createdHook.SetNumber(1) + createdHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") + createdHook.SetWebhookID(123456) + createdHook.SetCreated(time.Now().UTC().Unix()) + createdHook.SetHost("github.com") + createdHook.SetEvent(constants.EventInstallation) + createdHook.SetEventAction(constants.AppInstallCreated) + createdHook.SetStatus(constants.StatusSuccess) + + deletedHook := createdHook + deletedHook.SetEventAction(constants.AppInstallDeleted) + + tests := []struct { + name string + file string + wantHook *api.Hook + wantInstall *internal.Installation + }{ + { + name: "installation created", + file: "testdata/hooks/installation_created.json", + wantHook: &createdHook, + wantInstall: &internal.Installation{ + Action: constants.AppInstallCreated, + ID: 1, + RepositoriesAdded: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + { + name: "installation deleted", + file: "testdata/hooks/installation_deleted.json", + wantHook: &deletedHook, + wantInstall: &internal.Installation{ + Action: constants.AppInstallDeleted, + ID: 1, + RepositoriesRemoved: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + } + + // setup router + s := httptest.NewServer(http.NotFoundHandler()) + defer s.Close() + + for _, tt := range tests { + // setup request + body, err := os.Open(tt.file) + if err != nil { + t.Errorf("unable to open file: %v", err) + } + + defer body.Close() + + request, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/test", body) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("User-Agent", "GitHub-Hookshot/a22606a") + request.Header.Set("X-GitHub-Delivery", "7bd477e4-4415-11e9-9359-0d41fdf9567e") + request.Header.Set("X-GitHub-Hook-ID", "123456") + request.Header.Set("X-GitHub-Event", "installation") + + // setup client + client, _ := NewTest(s.URL) + + want := &internal.Webhook{ + Hook: tt.wantHook, + Installation: tt.wantInstall, + } + + got, err := client.ProcessWebhook(context.TODO(), request) + + if err != nil { + t.Errorf("ProcessWebhook returned err: %v", err) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) + } + } +} + +const ( + // GitHub App install event type 'added'. + AppInstallRepositoriesAdded = "added" + // GitHub App install event type 'removed'. + AppInstallRepositoriesRemoved = "removed" +) + +func TestGitHub_ProcessWebhook_InstallationRepositories(t *testing.T) { + // setup tests + var reposAddedHook api.Hook + reposAddedHook.SetNumber(1) + reposAddedHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") + reposAddedHook.SetWebhookID(123456) + reposAddedHook.SetCreated(time.Now().UTC().Unix()) + reposAddedHook.SetHost("github.com") + reposAddedHook.SetEvent(constants.EventInstallationRepositories) + reposAddedHook.SetEventAction(AppInstallRepositoriesAdded) + reposAddedHook.SetStatus(constants.StatusSuccess) + + reposRemovedHook := reposAddedHook + reposRemovedHook.SetEventAction(AppInstallRepositoriesRemoved) + + tests := []struct { + name string + file string + wantHook *api.Hook + wantInstall *internal.Installation + }{ + { + name: "installation_repositories repos added", + file: "testdata/hooks/installation_repositories_added.json", + wantHook: &reposAddedHook, + wantInstall: &internal.Installation{ + Action: AppInstallRepositoriesAdded, + ID: 1, + RepositoriesAdded: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + { + name: "installation_repositories repos removed", + file: "testdata/hooks/installation_repositories_removed.json", + wantHook: &reposRemovedHook, + wantInstall: &internal.Installation{ + Action: AppInstallRepositoriesRemoved, + ID: 1, + RepositoriesRemoved: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + } + + // setup router + s := httptest.NewServer(http.NotFoundHandler()) + defer s.Close() + + for _, tt := range tests { + // setup request + body, err := os.Open(tt.file) + if err != nil { + t.Errorf("unable to open file: %v", err) + } + + defer body.Close() + + request, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/test", body) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("User-Agent", "GitHub-Hookshot/a22606a") + request.Header.Set("X-GitHub-Delivery", "7bd477e4-4415-11e9-9359-0d41fdf9567e") + request.Header.Set("X-GitHub-Hook-ID", "123456") + request.Header.Set("X-GitHub-Event", "installation_repositories") + + // setup client + client, _ := NewTest(s.URL) + + want := &internal.Webhook{ + Hook: tt.wantHook, + Installation: tt.wantInstall, + } + + got, err := client.ProcessWebhook(context.TODO(), request) + + if err != nil { + t.Errorf("ProcessWebhook returned err: %v", err) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) + } + } +} diff --git a/scm/scm.go b/scm/scm.go index b93bde906..037a5d835 100644 --- a/scm/scm.go +++ b/scm/scm.go @@ -3,6 +3,7 @@ package scm import ( + "context" "fmt" "github.com/sirupsen/logrus" @@ -17,7 +18,7 @@ import ( // // * Github // . -func New(s *Setup) (Service, error) { +func New(ctx context.Context, s *Setup) (Service, error) { // validate the setup being provided // // https://pkg.go.dev/github.com/go-vela/server/scm?tab=doc#Setup.Validate @@ -33,12 +34,12 @@ func New(s *Setup) (Service, error) { // handle the Github scm driver being provided // // https://pkg.go.dev/github.com/go-vela/server/scm?tab=doc#Setup.Github - return s.Github() + return s.Github(ctx) case constants.DriverGitlab: // handle the Gitlab scm driver being provided // // https://pkg.go.dev/github.com/go-vela/server/scm?tab=doc#Setup.Gitlab - return s.Gitlab() + return s.Gitlab(ctx) default: // handle an invalid scm driver being provided return nil, fmt.Errorf("invalid scm driver provided: %s", s.Driver) diff --git a/scm/scm_test.go b/scm/scm_test.go index 233108758..974c85b45 100644 --- a/scm/scm_test.go +++ b/scm/scm_test.go @@ -3,6 +3,7 @@ package scm import ( + "context" "testing" ) @@ -23,7 +24,7 @@ func TestSCM_New(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -37,7 +38,7 @@ func TestSCM_New(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -51,7 +52,7 @@ func TestSCM_New(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -65,14 +66,14 @@ func TestSCM_New(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, } // run tests for _, test := range tests { - _, err := New(test.setup) + _, err := New(context.Background(), test.setup) if test.failure { if err == nil { diff --git a/scm/service.go b/scm/service.go index bc917ce9a..7d1d12ec6 100644 --- a/scm/service.go +++ b/scm/service.go @@ -7,6 +7,8 @@ import ( "net/http" api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/database" "github.com/go-vela/server/internal" ) @@ -140,6 +142,12 @@ type Service interface { // GetHTMLURL defines a function that retrieves // a repository file's html_url. GetHTMLURL(context.Context, *api.User, string, string, string, string) (string, error) + // GetNetrcPassword defines a function that returns the netrc + // password injected into build steps. + GetNetrcPassword(context.Context, database.Interface, *api.Repo, *api.User, yaml.Git) (string, error) + // SyncRepoWithInstallation defines a function that syncs + // a repo with the installation, if it exists. + SyncRepoWithInstallation(context.Context, *api.Repo) (*api.Repo, error) // Webhook SCM Interface Functions @@ -153,5 +161,14 @@ type Service interface { // redelivers the webhook from the SCM. RedeliverWebhook(context.Context, *api.User, *api.Hook) error + // App Integration SCM Interface Functions + + // ProcessInstallation defines a function that + // processes an installation event. + ProcessInstallation(context.Context, *http.Request, *internal.Webhook, database.Interface) error + // FinishInstallation defines a function that + // finishes an installation event and returns a web redirect. + FinishInstallation(context.Context, *http.Request, int64) (string, error) + // TODO: Add convert functions to interface? } diff --git a/scm/setup.go b/scm/setup.go index dc3a3e698..44704fffd 100644 --- a/scm/setup.go +++ b/scm/setup.go @@ -3,6 +3,7 @@ package scm import ( + "context" "fmt" "strings" @@ -27,6 +28,14 @@ type Setup struct { ClientID string // specifies the OAuth client secret from the scm system to use for the scm client ClientSecret string + // specifies App integration id + AppID int64 + // specifies App integration private key + AppPrivateKey string + // specifies App integration path to private key + AppPrivateKeyPath string + // specifies App integration permissions set + AppPermissions []string // specifies the Vela server address to use for the scm client ServerAddress string // specifies the Vela server address that the scm provider should use to send Vela webhooks @@ -36,20 +45,21 @@ type Setup struct { // specifies the Vela web UI address to use for the scm client WebUIAddress string // specifies the OAuth scopes to use for the scm client - Scopes []string + OAuthScopes []string // specifies OTel tracing configurations Tracing *tracing.Client } // Github creates and returns a Vela service capable of // integrating with a Github scm system. -func (s *Setup) Github() (Service, error) { +func (s *Setup) Github(ctx context.Context) (Service, error) { logrus.Trace("creating github scm client from setup") // create new Github scm service // // https://pkg.go.dev/github.com/go-vela/server/scm/github?tab=doc#New return github.New( + ctx, github.WithAddress(s.Address), github.WithClientID(s.ClientID), github.WithClientSecret(s.ClientSecret), @@ -57,14 +67,18 @@ func (s *Setup) Github() (Service, error) { github.WithServerWebhookAddress(s.ServerWebhookAddress), github.WithStatusContext(s.StatusContext), github.WithWebUIAddress(s.WebUIAddress), - github.WithScopes(s.Scopes), + github.WithOAuthScopes(s.OAuthScopes), github.WithTracing(s.Tracing), + github.WithGithubAppID(s.AppID), + github.WithGithubPrivateKey(s.AppPrivateKey), + github.WithGithubPrivateKeyPath(s.AppPrivateKeyPath), + github.WithGitHubAppPermissions(s.AppPermissions), ) } // Gitlab creates and returns a Vela service capable of // integrating with a Gitlab scm system. -func (s *Setup) Gitlab() (Service, error) { +func (s *Setup) Gitlab(_ context.Context) (Service, error) { logrus.Trace("creating gitlab scm client from setup") return nil, fmt.Errorf("unsupported scm driver: %s", constants.DriverGitlab) @@ -110,7 +124,7 @@ func (s *Setup) Validate() error { return fmt.Errorf("no scm status context provided") } - if len(s.Scopes) == 0 { + if len(s.OAuthScopes) == 0 { return fmt.Errorf("no scm scopes provided") } diff --git a/scm/setup_test.go b/scm/setup_test.go index f3ad759a4..3cb381598 100644 --- a/scm/setup_test.go +++ b/scm/setup_test.go @@ -3,6 +3,7 @@ package scm import ( + "context" "reflect" "testing" ) @@ -18,10 +19,10 @@ func TestSCM_Setup_Github(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, } - _github, err := _setup.Github() + _github, err := _setup.Github(context.Background()) if err != nil { t.Errorf("unable to setup scm: %v", err) } @@ -46,7 +47,7 @@ func TestSCM_Setup_Github(t *testing.T) { // run tests for _, test := range tests { - got, err := test.setup.Github() + got, err := test.setup.Github(context.Background()) if test.failure { if err == nil { @@ -80,7 +81,7 @@ func TestSCM_Setup_Gitlab(t *testing.T) { } // run test - got, err := _setup.Gitlab() + got, err := _setup.Gitlab(context.Background()) if err == nil { t.Errorf("Gitlab should have returned err") } @@ -107,7 +108,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -121,7 +122,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -135,7 +136,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -149,7 +150,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -163,7 +164,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -177,7 +178,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -191,7 +192,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -205,7 +206,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -219,7 +220,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "", WebUIAddress: "https://vela.example.com", - Scopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, + OAuthScopes: []string{"repo", "repo:status", "user:email", "read:user", "read:org"}, }, }, { @@ -233,7 +234,7 @@ func TestSCM_Setup_Validate(t *testing.T) { ServerWebhookAddress: "", StatusContext: "continuous-integration/vela", WebUIAddress: "https://vela.example.com", - Scopes: []string{}, + OAuthScopes: []string{}, }, }, } From b16622f196085818a0bdf788e0bd83dcb00054e7 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:38:34 -0600 Subject: [PATCH 17/41] chore(yaml): add go-yaml types package (#1225) * enhance(yaml): allow for users to parse pipelines using old library * testing file for internal yaml * chore(compiler): convert unmarshaled buildkite to go-yaml * remove tests used in later PRs * lintfix * fix schema * gci --- api/pipeline/template.go | 2 +- api/types/string.go | 2 +- compiler/engine.go | 2 +- compiler/native/clone.go | 2 +- compiler/native/clone_test.go | 2 +- compiler/native/compile.go | 4 +- compiler/native/compile_test.go | 8 +- compiler/native/environment.go | 2 +- compiler/native/environment_test.go | 2 +- compiler/native/expand.go | 2 +- compiler/native/expand_test.go | 2 +- compiler/native/initialize.go | 2 +- compiler/native/initialize_test.go | 2 +- compiler/native/parse.go | 25 +- compiler/native/parse_test.go | 2 +- compiler/native/script.go | 2 +- compiler/native/script_test.go | 2 +- compiler/native/substitute.go | 4 +- compiler/native/substitute_test.go | 2 +- compiler/native/transform.go | 15 +- compiler/native/transform_test.go | 2 +- compiler/native/validate.go | 2 +- compiler/native/validate_test.go | 2 +- compiler/template/native/convert.go | 2 +- compiler/template/native/render.go | 11 +- compiler/template/native/render_test.go | 4 +- compiler/template/starlark/render.go | 13 +- compiler/template/starlark/render_test.go | 4 +- compiler/template/template.go | 2 +- compiler/types/raw/map_test.go | 2 +- compiler/types/raw/slice_test.go | 2 +- compiler/types/yaml/buildkite/build.go | 122 ++++ .../types/yaml/{ => buildkite}/build_test.go | 2 +- compiler/types/yaml/buildkite/deployment.go | 112 +++ .../types/yaml/buildkite/deployment_test.go | 77 ++ compiler/types/yaml/buildkite/doc.go | 8 + compiler/types/yaml/buildkite/git.go | 44 ++ compiler/types/yaml/buildkite/git_test.go | 60 ++ compiler/types/yaml/buildkite/metadata.go | 108 +++ .../types/yaml/buildkite/metadata_test.go | 130 ++++ compiler/types/yaml/buildkite/ruleset.go | 223 ++++++ .../yaml/{ => buildkite}/ruleset_test.go | 2 +- compiler/types/yaml/buildkite/secret.go | 328 +++++++++ .../types/yaml/{ => buildkite}/secret_test.go | 2 +- compiler/types/yaml/buildkite/service.go | 164 +++++ .../yaml/{ => buildkite}/service_test.go | 2 +- compiler/types/yaml/{ => buildkite}/stage.go | 42 +- .../types/yaml/{ => buildkite}/stage_test.go | 2 +- compiler/types/yaml/buildkite/step.go | 188 +++++ .../types/yaml/{ => buildkite}/step_test.go | 2 +- compiler/types/yaml/buildkite/template.go | 121 +++ .../yaml/{ => buildkite}/template_test.go | 2 +- .../yaml/{ => buildkite}/testdata/build.yml | 0 .../testdata/build/validate/bad_pipeline0.yml | 0 .../testdata/build/validate/bad_pipeline1.yml | 0 .../testdata/build/validate/bad_version.yml | 0 .../testdata/build/validate/step.yml | 0 .../testdata/build_anchor_stage.yml | 0 .../testdata/build_anchor_step.yml | 0 .../testdata/build_empty_env.yml | 0 .../testdata/build_with_deploy_config.yml | 0 .../testdata/deploy_parameter.yml | 0 .../yaml/{ => buildkite}/testdata/invalid.yml | 0 .../{ => buildkite}/testdata/merge_anchor.yml | 0 .../{ => buildkite}/testdata/metadata.yml | 0 .../{ => buildkite}/testdata/metadata_env.yml | 0 .../testdata/ruleset_advanced.yml | 0 .../testdata/ruleset_regex.yml | 0 .../testdata/ruleset_simple.yml | 0 .../yaml/{ => buildkite}/testdata/secret.yml | 0 .../testdata/secret/validate/no_name.yml | 0 .../testdata/secret/validate/org.yml | 0 .../secret/validate/org_bad_engine.yml | 0 .../testdata/secret/validate/org_bad_key.yml | 0 .../testdata/secret/validate/plugin.yml | 0 .../secret/validate/plugin_bad_image.yml | 0 .../secret/validate/plugin_bad_name.yml | 0 .../testdata/secret/validate/repo.yml | 0 .../secret/validate/repo_bad_engine.yml | 0 .../testdata/secret/validate/repo_bad_key.yml | 0 .../testdata/secret/validate/shared.yml | 0 .../secret/validate/shared_bad_engine.yml | 0 .../secret/validate/shared_bad_key.yml | 0 .../yaml/{ => buildkite}/testdata/service.yml | 0 .../testdata/service/validate/bad_image.yml | 0 .../testdata/service/validate/minimal.yml | 0 .../service/validate/missing_image.yml | 0 .../service/validate/missing_name.yml | 0 .../{ => buildkite}/testdata/service_nil.yml | 0 .../yaml/{ => buildkite}/testdata/stage.yml | 0 .../testdata/stage/validate/bad_image.yml | 0 .../testdata/stage/validate/minimal.yml | 0 .../testdata/stage/validate/missing.yml | 0 .../testdata/stage/validate/missing_image.yml | 0 .../testdata/stage/validate/missing_name.yml | 0 .../types/yaml/buildkite/testdata/step.yml | 46 ++ .../testdata/step/validate/bad_image.yml | 0 .../testdata/step/validate/minimal.yml | 0 .../testdata/step/validate/missing.yml | 0 .../testdata/step/validate/missing_image.yml | 0 .../testdata/step/validate/missing_name.yml | 0 .../testdata/step_malformed.yml | 0 .../{ => buildkite}/testdata/step_nil.yml | 0 .../testdata/step_secret_slice.yml | 0 .../step_secret_slice_invalid_no_source.yml | 0 .../step_secret_slice_invalid_no_target.yml | 0 .../testdata/step_secret_string.yml | 0 .../{ => buildkite}/testdata/template.yml | 0 .../testdata/ulimit_colon_error.yml | 0 .../testdata/ulimit_equal_error.yml | 0 .../testdata/ulimit_hardlimit1_error.yml | 0 .../testdata/ulimit_hardlimit2_error.yml | 0 .../{ => buildkite}/testdata/ulimit_slice.yml | 0 .../testdata/ulimit_softlimit_error.yml | 0 .../testdata/ulimit_string.yml | 0 .../{ => buildkite}/testdata/volume_error.yml | 0 .../{ => buildkite}/testdata/volume_slice.yml | 0 .../testdata/volume_string.yml | 0 compiler/types/yaml/buildkite/ulimit.go | 158 ++++ .../types/yaml/{ => buildkite}/ulimit_test.go | 2 +- compiler/types/yaml/buildkite/volume.go | 147 ++++ compiler/types/yaml/buildkite/volume_test.go | 137 ++++ compiler/types/yaml/buildkite/worker.go | 31 + compiler/types/yaml/buildkite/worker_test.go | 38 + compiler/types/yaml/doc.go | 8 - compiler/types/yaml/{ => yaml}/build.go | 0 compiler/types/yaml/yaml/build_test.go | 686 ++++++++++++++++++ compiler/types/yaml/{ => yaml}/deployment.go | 0 .../types/yaml/{ => yaml}/deployment_test.go | 0 compiler/types/yaml/yaml/doc.go | 8 + compiler/types/yaml/{ => yaml}/git.go | 0 compiler/types/yaml/{ => yaml}/git_test.go | 0 compiler/types/yaml/{ => yaml}/metadata.go | 0 .../types/yaml/{ => yaml}/metadata_test.go | 0 compiler/types/yaml/{ => yaml}/ruleset.go | 0 compiler/types/yaml/yaml/ruleset_test.go | 288 ++++++++ compiler/types/yaml/{ => yaml}/secret.go | 0 compiler/types/yaml/yaml/secret_test.go | 460 ++++++++++++ compiler/types/yaml/{ => yaml}/service.go | 0 compiler/types/yaml/yaml/service_test.go | 186 +++++ compiler/types/yaml/yaml/stage.go | 169 +++++ compiler/types/yaml/yaml/stage_test.go | 474 ++++++++++++ compiler/types/yaml/{ => yaml}/step.go | 0 compiler/types/yaml/yaml/step_test.go | 327 +++++++++ compiler/types/yaml/{ => yaml}/template.go | 0 compiler/types/yaml/yaml/template_test.go | 121 +++ compiler/types/yaml/yaml/testdata/build.yml | 144 ++++ .../testdata/build/validate/bad_pipeline0.yml | 1 + .../testdata/build/validate/bad_pipeline1.yml | 3 + .../testdata/build/validate/bad_version.yml | 2 + .../yaml/testdata/build/validate/step.yml | 47 ++ .../yaml/yaml/testdata/build_anchor_stage.yml | 57 ++ .../yaml/yaml/testdata/build_anchor_step.yml | 48 ++ .../yaml/yaml/testdata/build_empty_env.yml | 27 + .../testdata/build_with_deploy_config.yml | 36 + .../yaml/yaml/testdata/deploy_parameter.yml | 11 + compiler/types/yaml/yaml/testdata/invalid.yml | 2 + .../types/yaml/yaml/testdata/merge_anchor.yml | 45 ++ .../types/yaml/yaml/testdata/metadata.yml | 2 + .../types/yaml/yaml/testdata/metadata_env.yml | 3 + .../yaml/yaml/testdata/ruleset_advanced.yml | 15 + .../yaml/yaml/testdata/ruleset_regex.yml | 7 + .../yaml/yaml/testdata/ruleset_simple.yml | 13 + compiler/types/yaml/yaml/testdata/secret.yml | 39 + .../yaml/testdata/secret/validate/no_name.yml | 11 + .../yaml/testdata/secret/validate/org.yml | 5 + .../secret/validate/org_bad_engine.yml | 9 + .../testdata/secret/validate/org_bad_key.yml | 9 + .../yaml/testdata/secret/validate/plugin.yml | 8 + .../secret/validate/plugin_bad_image.yml | 15 + .../secret/validate/plugin_bad_name.yml | 7 + .../yaml/testdata/secret/validate/repo.yml | 13 + .../secret/validate/repo_bad_engine.yml | 5 + .../testdata/secret/validate/repo_bad_key.yml | 14 + .../yaml/testdata/secret/validate/shared.yml | 5 + .../secret/validate/shared_bad_engine.yml | 9 + .../secret/validate/shared_bad_key.yml | 9 + compiler/types/yaml/yaml/testdata/service.yml | 14 + .../testdata/service/validate/bad_image.yml | 3 + .../testdata/service/validate/minimal.yml | 3 + .../service/validate/missing_image.yml | 2 + .../service/validate/missing_name.yml | 2 + .../types/yaml/yaml/testdata/service_nil.yml | 2 + compiler/types/yaml/yaml/testdata/stage.yml | 44 ++ .../testdata/stage/validate/bad_image.yml | 7 + .../yaml/testdata/stage/validate/minimal.yml | 7 + .../yaml/testdata/stage/validate/missing.yml | 5 + .../testdata/stage/validate/missing_image.yml | 6 + .../testdata/stage/validate/missing_name.yml | 6 + .../types/yaml/{ => yaml}/testdata/step.yml | 0 .../yaml/testdata/step/validate/bad_image.yml | 5 + .../yaml/testdata/step/validate/minimal.yml | 5 + .../yaml/testdata/step/validate/missing.yml | 3 + .../testdata/step/validate/missing_image.yml | 4 + .../testdata/step/validate/missing_name.yml | 4 + .../yaml/yaml/testdata/step_malformed.yml | 4 + .../types/yaml/yaml/testdata/step_nil.yml | 2 + .../yaml/yaml/testdata/step_secret_slice.yml | 5 + .../step_secret_slice_invalid_no_source.yml | 2 + .../step_secret_slice_invalid_no_target.yml | 2 + .../yaml/yaml/testdata/step_secret_string.yml | 2 + .../types/yaml/yaml/testdata/template.yml | 12 + .../yaml/yaml/testdata/ulimit_colon_error.yml | 2 + .../yaml/yaml/testdata/ulimit_equal_error.yml | 2 + .../yaml/testdata/ulimit_hardlimit1_error.yml | 2 + .../yaml/testdata/ulimit_hardlimit2_error.yml | 2 + .../types/yaml/yaml/testdata/ulimit_slice.yml | 6 + .../yaml/testdata/ulimit_softlimit_error.yml | 2 + .../yaml/yaml/testdata/ulimit_string.yml | 2 + .../types/yaml/yaml/testdata/volume_error.yml | 2 + .../types/yaml/yaml/testdata/volume_slice.yml | 7 + .../yaml/yaml/testdata/volume_string.yml | 2 + compiler/types/yaml/{ => yaml}/ulimit.go | 0 compiler/types/yaml/yaml/ulimit_test.go | 147 ++++ compiler/types/yaml/{ => yaml}/volume.go | 0 compiler/types/yaml/{ => yaml}/volume_test.go | 0 compiler/types/yaml/{ => yaml}/worker.go | 0 compiler/types/yaml/{ => yaml}/worker_test.go | 0 go.mod | 2 +- internal/testdata/buildkite.yml | 18 + internal/testdata/go-yaml.yml | 19 + internal/testdata/invalid.yml | 2 + internal/testdata/no_version.yml | 17 + mock/server/pipeline.go | 4 +- schema/pipeline.go | 2 +- scm/github/repo.go | 2 +- scm/github/repo_test.go | 2 +- scm/service.go | 2 +- 228 files changed, 6083 insertions(+), 94 deletions(-) create mode 100644 compiler/types/yaml/buildkite/build.go rename compiler/types/yaml/{ => buildkite}/build_test.go (99%) create mode 100644 compiler/types/yaml/buildkite/deployment.go create mode 100644 compiler/types/yaml/buildkite/deployment_test.go create mode 100644 compiler/types/yaml/buildkite/doc.go create mode 100644 compiler/types/yaml/buildkite/git.go create mode 100644 compiler/types/yaml/buildkite/git_test.go create mode 100644 compiler/types/yaml/buildkite/metadata.go create mode 100644 compiler/types/yaml/buildkite/metadata_test.go create mode 100644 compiler/types/yaml/buildkite/ruleset.go rename compiler/types/yaml/{ => buildkite}/ruleset_test.go (99%) create mode 100644 compiler/types/yaml/buildkite/secret.go rename compiler/types/yaml/{ => buildkite}/secret_test.go (99%) create mode 100644 compiler/types/yaml/buildkite/service.go rename compiler/types/yaml/{ => buildkite}/service_test.go (99%) rename compiler/types/yaml/{ => buildkite}/stage.go (85%) rename compiler/types/yaml/{ => buildkite}/stage_test.go (99%) create mode 100644 compiler/types/yaml/buildkite/step.go rename compiler/types/yaml/{ => buildkite}/step_test.go (99%) create mode 100644 compiler/types/yaml/buildkite/template.go rename compiler/types/yaml/{ => buildkite}/template_test.go (99%) rename compiler/types/yaml/{ => buildkite}/testdata/build.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build/validate/bad_pipeline0.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build/validate/bad_pipeline1.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build/validate/bad_version.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build/validate/step.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build_anchor_stage.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build_anchor_step.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build_empty_env.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/build_with_deploy_config.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/deploy_parameter.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/invalid.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/merge_anchor.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/metadata.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/metadata_env.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ruleset_advanced.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ruleset_regex.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ruleset_simple.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/no_name.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/org.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/org_bad_engine.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/org_bad_key.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/plugin.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/plugin_bad_image.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/plugin_bad_name.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/repo.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/repo_bad_engine.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/repo_bad_key.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/shared.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/shared_bad_engine.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/secret/validate/shared_bad_key.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/service.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/service/validate/bad_image.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/service/validate/minimal.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/service/validate/missing_image.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/service/validate/missing_name.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/service_nil.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/stage.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/stage/validate/bad_image.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/stage/validate/minimal.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/stage/validate/missing.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/stage/validate/missing_image.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/stage/validate/missing_name.yml (100%) create mode 100644 compiler/types/yaml/buildkite/testdata/step.yml rename compiler/types/yaml/{ => buildkite}/testdata/step/validate/bad_image.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step/validate/minimal.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step/validate/missing.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step/validate/missing_image.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step/validate/missing_name.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step_malformed.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step_nil.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step_secret_slice.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step_secret_slice_invalid_no_source.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step_secret_slice_invalid_no_target.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/step_secret_string.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/template.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ulimit_colon_error.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ulimit_equal_error.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ulimit_hardlimit1_error.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ulimit_hardlimit2_error.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ulimit_slice.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ulimit_softlimit_error.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/ulimit_string.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/volume_error.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/volume_slice.yml (100%) rename compiler/types/yaml/{ => buildkite}/testdata/volume_string.yml (100%) create mode 100644 compiler/types/yaml/buildkite/ulimit.go rename compiler/types/yaml/{ => buildkite}/ulimit_test.go (99%) create mode 100644 compiler/types/yaml/buildkite/volume.go create mode 100644 compiler/types/yaml/buildkite/volume_test.go create mode 100644 compiler/types/yaml/buildkite/worker.go create mode 100644 compiler/types/yaml/buildkite/worker_test.go delete mode 100644 compiler/types/yaml/doc.go rename compiler/types/yaml/{ => yaml}/build.go (100%) create mode 100644 compiler/types/yaml/yaml/build_test.go rename compiler/types/yaml/{ => yaml}/deployment.go (100%) rename compiler/types/yaml/{ => yaml}/deployment_test.go (100%) create mode 100644 compiler/types/yaml/yaml/doc.go rename compiler/types/yaml/{ => yaml}/git.go (100%) rename compiler/types/yaml/{ => yaml}/git_test.go (100%) rename compiler/types/yaml/{ => yaml}/metadata.go (100%) rename compiler/types/yaml/{ => yaml}/metadata_test.go (100%) rename compiler/types/yaml/{ => yaml}/ruleset.go (100%) create mode 100644 compiler/types/yaml/yaml/ruleset_test.go rename compiler/types/yaml/{ => yaml}/secret.go (100%) create mode 100644 compiler/types/yaml/yaml/secret_test.go rename compiler/types/yaml/{ => yaml}/service.go (100%) create mode 100644 compiler/types/yaml/yaml/service_test.go create mode 100644 compiler/types/yaml/yaml/stage.go create mode 100644 compiler/types/yaml/yaml/stage_test.go rename compiler/types/yaml/{ => yaml}/step.go (100%) create mode 100644 compiler/types/yaml/yaml/step_test.go rename compiler/types/yaml/{ => yaml}/template.go (100%) create mode 100644 compiler/types/yaml/yaml/template_test.go create mode 100644 compiler/types/yaml/yaml/testdata/build.yml create mode 100644 compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline0.yml create mode 100644 compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline1.yml create mode 100644 compiler/types/yaml/yaml/testdata/build/validate/bad_version.yml create mode 100644 compiler/types/yaml/yaml/testdata/build/validate/step.yml create mode 100644 compiler/types/yaml/yaml/testdata/build_anchor_stage.yml create mode 100644 compiler/types/yaml/yaml/testdata/build_anchor_step.yml create mode 100644 compiler/types/yaml/yaml/testdata/build_empty_env.yml create mode 100644 compiler/types/yaml/yaml/testdata/build_with_deploy_config.yml create mode 100644 compiler/types/yaml/yaml/testdata/deploy_parameter.yml create mode 100644 compiler/types/yaml/yaml/testdata/invalid.yml create mode 100644 compiler/types/yaml/yaml/testdata/merge_anchor.yml create mode 100644 compiler/types/yaml/yaml/testdata/metadata.yml create mode 100644 compiler/types/yaml/yaml/testdata/metadata_env.yml create mode 100644 compiler/types/yaml/yaml/testdata/ruleset_advanced.yml create mode 100644 compiler/types/yaml/yaml/testdata/ruleset_regex.yml create mode 100644 compiler/types/yaml/yaml/testdata/ruleset_simple.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/no_name.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/org.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/org_bad_engine.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/org_bad_key.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/plugin.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_image.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_name.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/repo.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_engine.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_key.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/shared.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_engine.yml create mode 100644 compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_key.yml create mode 100644 compiler/types/yaml/yaml/testdata/service.yml create mode 100644 compiler/types/yaml/yaml/testdata/service/validate/bad_image.yml create mode 100644 compiler/types/yaml/yaml/testdata/service/validate/minimal.yml create mode 100644 compiler/types/yaml/yaml/testdata/service/validate/missing_image.yml create mode 100644 compiler/types/yaml/yaml/testdata/service/validate/missing_name.yml create mode 100644 compiler/types/yaml/yaml/testdata/service_nil.yml create mode 100644 compiler/types/yaml/yaml/testdata/stage.yml create mode 100644 compiler/types/yaml/yaml/testdata/stage/validate/bad_image.yml create mode 100644 compiler/types/yaml/yaml/testdata/stage/validate/minimal.yml create mode 100644 compiler/types/yaml/yaml/testdata/stage/validate/missing.yml create mode 100644 compiler/types/yaml/yaml/testdata/stage/validate/missing_image.yml create mode 100644 compiler/types/yaml/yaml/testdata/stage/validate/missing_name.yml rename compiler/types/yaml/{ => yaml}/testdata/step.yml (100%) create mode 100644 compiler/types/yaml/yaml/testdata/step/validate/bad_image.yml create mode 100644 compiler/types/yaml/yaml/testdata/step/validate/minimal.yml create mode 100644 compiler/types/yaml/yaml/testdata/step/validate/missing.yml create mode 100644 compiler/types/yaml/yaml/testdata/step/validate/missing_image.yml create mode 100644 compiler/types/yaml/yaml/testdata/step/validate/missing_name.yml create mode 100644 compiler/types/yaml/yaml/testdata/step_malformed.yml create mode 100644 compiler/types/yaml/yaml/testdata/step_nil.yml create mode 100644 compiler/types/yaml/yaml/testdata/step_secret_slice.yml create mode 100644 compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_source.yml create mode 100644 compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_target.yml create mode 100644 compiler/types/yaml/yaml/testdata/step_secret_string.yml create mode 100644 compiler/types/yaml/yaml/testdata/template.yml create mode 100644 compiler/types/yaml/yaml/testdata/ulimit_colon_error.yml create mode 100644 compiler/types/yaml/yaml/testdata/ulimit_equal_error.yml create mode 100644 compiler/types/yaml/yaml/testdata/ulimit_hardlimit1_error.yml create mode 100644 compiler/types/yaml/yaml/testdata/ulimit_hardlimit2_error.yml create mode 100644 compiler/types/yaml/yaml/testdata/ulimit_slice.yml create mode 100644 compiler/types/yaml/yaml/testdata/ulimit_softlimit_error.yml create mode 100644 compiler/types/yaml/yaml/testdata/ulimit_string.yml create mode 100644 compiler/types/yaml/yaml/testdata/volume_error.yml create mode 100644 compiler/types/yaml/yaml/testdata/volume_slice.yml create mode 100644 compiler/types/yaml/yaml/testdata/volume_string.yml rename compiler/types/yaml/{ => yaml}/ulimit.go (100%) create mode 100644 compiler/types/yaml/yaml/ulimit_test.go rename compiler/types/yaml/{ => yaml}/volume.go (100%) rename compiler/types/yaml/{ => yaml}/volume_test.go (100%) rename compiler/types/yaml/{ => yaml}/worker.go (100%) rename compiler/types/yaml/{ => yaml}/worker_test.go (100%) create mode 100644 internal/testdata/buildkite.yml create mode 100644 internal/testdata/go-yaml.yml create mode 100644 internal/testdata/invalid.yml create mode 100644 internal/testdata/no_version.yml diff --git a/api/pipeline/template.go b/api/pipeline/template.go index aef79d165..0ff2a54bc 100644 --- a/api/pipeline/template.go +++ b/api/pipeline/template.go @@ -13,7 +13,7 @@ import ( "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler" "github.com/go-vela/server/compiler/registry/github" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/internal" "github.com/go-vela/server/router/middleware/org" "github.com/go-vela/server/router/middleware/pipeline" diff --git a/api/types/string.go b/api/types/string.go index 33ddacb59..a7637d714 100644 --- a/api/types/string.go +++ b/api/types/string.go @@ -7,8 +7,8 @@ import ( "strconv" "strings" - "github.com/buildkite/yaml" json "github.com/ghodss/yaml" + "gopkg.in/yaml.v3" ) // ToString is a helper function to convert diff --git a/compiler/engine.go b/compiler/engine.go index ab4555296..b3503b77a 100644 --- a/compiler/engine.go +++ b/compiler/engine.go @@ -9,7 +9,7 @@ import ( "github.com/go-vela/server/api/types/settings" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/database" "github.com/go-vela/server/internal" "github.com/go-vela/server/scm" diff --git a/compiler/native/clone.go b/compiler/native/clone.go index f6f4ffdbc..179e80db5 100644 --- a/compiler/native/clone.go +++ b/compiler/native/clone.go @@ -3,7 +3,7 @@ package native import ( - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) diff --git a/compiler/native/clone_test.go b/compiler/native/clone_test.go index 6bd92697c..ffee2ceaf 100644 --- a/compiler/native/clone_test.go +++ b/compiler/native/clone_test.go @@ -9,7 +9,7 @@ import ( "github.com/urfave/cli/v2" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) const defaultCloneImage = "target/vela-git-slim:latest" diff --git a/compiler/native/compile.go b/compiler/native/compile.go index 04740f412..7f0a71f41 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -12,14 +12,14 @@ import ( "strings" "time" - yml "github.com/buildkite/yaml" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-retryablehttp" + yml "gopkg.in/yaml.v3" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index e887176d7..e2134c9db 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -13,16 +13,16 @@ import ( "testing" "time" - yml "github.com/buildkite/yaml" "github.com/gin-gonic/gin" "github.com/google/go-cmp/cmp" "github.com/google/go-github/v65/github" "github.com/urfave/cli/v2" + yml "gopkg.in/yaml.v3" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" "github.com/go-vela/server/internal" ) @@ -2056,7 +2056,7 @@ func Test_client_modifyConfig(t *testing.T) { Name: "docker", Pull: "always", Parameters: map[string]interface{}{ - "init_options": map[interface{}]interface{}{ + "init_options": map[string]interface{}{ "get_plugins": "true", }, }, @@ -2089,7 +2089,7 @@ func Test_client_modifyConfig(t *testing.T) { Name: "docker", Pull: "always", Parameters: map[string]interface{}{ - "init_options": map[interface{}]interface{}{ + "init_options": map[string]interface{}{ "get_plugins": "true", }, }, diff --git a/compiler/native/environment.go b/compiler/native/environment.go index 3f3162137..34ae5b3a7 100644 --- a/compiler/native/environment.go +++ b/compiler/native/environment.go @@ -9,7 +9,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" "github.com/go-vela/server/internal" ) diff --git a/compiler/native/environment_test.go b/compiler/native/environment_test.go index 17dce3942..24f319a08 100644 --- a/compiler/native/environment_test.go +++ b/compiler/native/environment_test.go @@ -13,7 +13,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/internal" ) diff --git a/compiler/native/expand.go b/compiler/native/expand.go index a19abcd98..ac86d09ce 100644 --- a/compiler/native/expand.go +++ b/compiler/native/expand.go @@ -15,7 +15,7 @@ import ( "github.com/go-vela/server/compiler/template/starlark" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) diff --git a/compiler/native/expand_test.go b/compiler/native/expand_test.go index 412877966..0ca596338 100644 --- a/compiler/native/expand_test.go +++ b/compiler/native/expand_test.go @@ -17,7 +17,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) func TestNative_ExpandStages(t *testing.T) { diff --git a/compiler/native/initialize.go b/compiler/native/initialize.go index bbfdb071c..6380278e4 100644 --- a/compiler/native/initialize.go +++ b/compiler/native/initialize.go @@ -3,7 +3,7 @@ package native import ( - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) diff --git a/compiler/native/initialize_test.go b/compiler/native/initialize_test.go index 26c170581..738d388d7 100644 --- a/compiler/native/initialize_test.go +++ b/compiler/native/initialize_test.go @@ -9,7 +9,7 @@ import ( "github.com/urfave/cli/v2" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) func TestNative_InitStage(t *testing.T) { diff --git a/compiler/native/parse.go b/compiler/native/parse.go index 7afeefef3..804b1e015 100644 --- a/compiler/native/parse.go +++ b/compiler/native/parse.go @@ -7,12 +7,13 @@ import ( "io" "os" - "github.com/buildkite/yaml" + bkYaml "github.com/buildkite/yaml" "github.com/go-vela/server/compiler/template/native" "github.com/go-vela/server/compiler/template/starlark" typesRaw "github.com/go-vela/server/compiler/types/raw" - types "github.com/go-vela/server/compiler/types/yaml" + bkYamlTypes "github.com/go-vela/server/compiler/types/yaml/buildkite" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) @@ -41,9 +42,9 @@ func (c *client) ParseRaw(v interface{}) (string, error) { } // Parse converts an object to a yaml configuration. -func (c *client) Parse(v interface{}, pipelineType string, template *types.Template) (*types.Build, []byte, error) { +func (c *client) Parse(v interface{}, pipelineType string, template *yaml.Template) (*yaml.Build, []byte, error) { var ( - p *types.Build + p *yaml.Build raw []byte ) @@ -112,11 +113,11 @@ func (c *client) Parse(v interface{}, pipelineType string, template *types.Templ } // ParseBytes converts a byte slice to a yaml configuration. -func ParseBytes(data []byte) (*types.Build, []byte, error) { - config := new(types.Build) +func ParseBytes(data []byte) (*yaml.Build, []byte, error) { + config := new(bkYamlTypes.Build) // unmarshal the bytes into the yaml configuration - err := yaml.Unmarshal(data, config) + err := bkYaml.Unmarshal(data, config) if err != nil { return nil, data, fmt.Errorf("unable to unmarshal yaml: %w", err) } @@ -128,11 +129,11 @@ func ParseBytes(data []byte) (*types.Build, []byte, error) { config.Environment = typesRaw.StringSliceMap{} } - return config, data, nil + return config.ToYAML(), data, nil } // ParseFile converts an os.File into a yaml configuration. -func ParseFile(f *os.File) (*types.Build, []byte, error) { +func ParseFile(f *os.File) (*yaml.Build, []byte, error) { return ParseReader(f) } @@ -142,7 +143,7 @@ func ParseFileRaw(f *os.File) (string, error) { } // ParsePath converts a file path into a yaml configuration. -func ParsePath(p string) (*types.Build, []byte, error) { +func ParsePath(p string) (*yaml.Build, []byte, error) { // open the file for reading f, err := os.Open(p) if err != nil { @@ -168,7 +169,7 @@ func ParsePathRaw(p string) (string, error) { } // ParseReader converts an io.Reader into a yaml configuration. -func ParseReader(r io.Reader) (*types.Build, []byte, error) { +func ParseReader(r io.Reader) (*yaml.Build, []byte, error) { // read all the bytes from the reader data, err := io.ReadAll(r) if err != nil { @@ -190,6 +191,6 @@ func ParseReaderRaw(r io.Reader) (string, error) { } // ParseString converts a string into a yaml configuration. -func ParseString(s string) (*types.Build, []byte, error) { +func ParseString(s string) (*yaml.Build, []byte, error) { return ParseBytes([]byte(s)) } diff --git a/compiler/native/parse_test.go b/compiler/native/parse_test.go index fa2635a48..ae5b263b7 100644 --- a/compiler/native/parse_test.go +++ b/compiler/native/parse_test.go @@ -15,7 +15,7 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) diff --git a/compiler/native/script.go b/compiler/native/script.go index 03aa093bb..049b01f91 100644 --- a/compiler/native/script.go +++ b/compiler/native/script.go @@ -8,7 +8,7 @@ import ( "fmt" "strings" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) // ScriptStages injects the script for each step in every stage in a yaml configuration. diff --git a/compiler/native/script_test.go b/compiler/native/script_test.go index 410c297c0..bbcb95a73 100644 --- a/compiler/native/script_test.go +++ b/compiler/native/script_test.go @@ -10,7 +10,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/urfave/cli/v2" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) func TestNative_ScriptStages(t *testing.T) { diff --git a/compiler/native/substitute.go b/compiler/native/substitute.go index a675e92d5..8e3f44b53 100644 --- a/compiler/native/substitute.go +++ b/compiler/native/substitute.go @@ -6,10 +6,10 @@ import ( "fmt" "strings" - "github.com/buildkite/yaml" "github.com/drone/envsubst" + "gopkg.in/yaml.v3" - types "github.com/go-vela/server/compiler/types/yaml" + types "github.com/go-vela/server/compiler/types/yaml/yaml" ) // SubstituteStages replaces every declared environment diff --git a/compiler/native/substitute_test.go b/compiler/native/substitute_test.go index e8db1565c..df7a2230c 100644 --- a/compiler/native/substitute_test.go +++ b/compiler/native/substitute_test.go @@ -9,7 +9,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/urfave/cli/v2" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) func Test_client_SubstituteStages(t *testing.T) { diff --git a/compiler/native/transform.go b/compiler/native/transform.go index ebdb6777e..3bad97b89 100644 --- a/compiler/native/transform.go +++ b/compiler/native/transform.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/go-vela/server/compiler/types/pipeline" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) const ( @@ -65,12 +65,13 @@ func (c *client) TransformStages(r *pipeline.RuleData, p *yaml.Build) (*pipeline // create new executable pipeline pipeline := &pipeline.Build{ - Version: p.Version, - Metadata: *p.Metadata.ToPipeline(), - Stages: *p.Stages.ToPipeline(), - Secrets: *p.Secrets.ToPipeline(), - Services: *p.Services.ToPipeline(), - Worker: *p.Worker.ToPipeline(), + Version: p.Version, + Metadata: *p.Metadata.ToPipeline(), + Stages: *p.Stages.ToPipeline(), + Secrets: *p.Secrets.ToPipeline(), + Services: *p.Services.ToPipeline(), + Worker: *p.Worker.ToPipeline(), + Deployment: *p.Deployment.ToPipeline(), } // set the unique ID for the executable pipeline diff --git a/compiler/native/transform_test.go b/compiler/native/transform_test.go index d2906bc8b..317e9e5dd 100644 --- a/compiler/native/transform_test.go +++ b/compiler/native/transform_test.go @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli/v2" "github.com/go-vela/server/compiler/types/pipeline" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/internal" ) diff --git a/compiler/native/validate.go b/compiler/native/validate.go index 0c0775019..77a8578f2 100644 --- a/compiler/native/validate.go +++ b/compiler/native/validate.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/go-multierror" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) diff --git a/compiler/native/validate_test.go b/compiler/native/validate_test.go index fce45c216..9c27e82e7 100644 --- a/compiler/native/validate_test.go +++ b/compiler/native/validate_test.go @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli/v2" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) func TestNative_Validate_NoVersion(t *testing.T) { diff --git a/compiler/template/native/convert.go b/compiler/template/native/convert.go index 4a75a5793..5715cecd5 100644 --- a/compiler/template/native/convert.go +++ b/compiler/template/native/convert.go @@ -5,7 +5,7 @@ package native import ( "strings" - "github.com/buildkite/yaml" + "gopkg.in/yaml.v3" "github.com/go-vela/server/compiler/types/raw" ) diff --git a/compiler/template/native/render.go b/compiler/template/native/render.go index afc3bea51..9fec0fd50 100644 --- a/compiler/template/native/render.go +++ b/compiler/template/native/render.go @@ -11,13 +11,14 @@ import ( "github.com/buildkite/yaml" "github.com/go-vela/server/compiler/types/raw" - types "github.com/go-vela/server/compiler/types/yaml" + bkTypes "github.com/go-vela/server/compiler/types/yaml/buildkite" + types "github.com/go-vela/server/compiler/types/yaml/yaml" ) // Render combines the template with the step in the yaml pipeline. func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}) (*types.Build, error) { buffer := new(bytes.Buffer) - config := new(types.Build) + config := new(bkTypes.Build) velaFuncs := funcHandler{envs: convertPlatformVars(environment, name)} templateFuncMap := map[string]interface{}{ @@ -57,13 +58,13 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM config.Steps[index].Name = fmt.Sprintf("%s_%s", name, newStep.Name) } - return &types.Build{Metadata: config.Metadata, Deployment: config.Deployment, Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment, Templates: config.Templates}, nil + return &types.Build{Metadata: *config.Metadata.ToYAML(), Steps: *config.Steps.ToYAML(), Secrets: *config.Secrets.ToYAML(), Services: *config.Services.ToYAML(), Environment: config.Environment, Templates: *config.Templates.ToYAML(), Deployment: *config.Deployment.ToYAML()}, nil } // RenderBuild renders the templated build. func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}) (*types.Build, error) { buffer := new(bytes.Buffer) - config := new(types.Build) + config := new(bkTypes.Build) velaFuncs := funcHandler{envs: convertPlatformVars(envs, tmpl)} templateFuncMap := map[string]interface{}{ @@ -98,5 +99,5 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } - return config, nil + return config.ToYAML(), nil } diff --git a/compiler/template/native/render_test.go b/compiler/template/native/render_test.go index 3b4daacb8..d204f7572 100644 --- a/compiler/template/native/render_test.go +++ b/compiler/template/native/render_test.go @@ -6,11 +6,11 @@ import ( "os" "testing" - goyaml "github.com/buildkite/yaml" "github.com/google/go-cmp/cmp" + goyaml "gopkg.in/yaml.v3" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) func TestNative_Render(t *testing.T) { diff --git a/compiler/template/starlark/render.go b/compiler/template/starlark/render.go index edd822463..a89d63f99 100644 --- a/compiler/template/starlark/render.go +++ b/compiler/template/starlark/render.go @@ -7,13 +7,14 @@ import ( "errors" "fmt" - yaml "github.com/buildkite/yaml" + "github.com/buildkite/yaml" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" "go.starlark.net/syntax" "github.com/go-vela/server/compiler/types/raw" - types "github.com/go-vela/server/compiler/types/yaml" + bkTypes "github.com/go-vela/server/compiler/types/yaml/buildkite" + types "github.com/go-vela/server/compiler/types/yaml/yaml" ) var ( @@ -32,7 +33,7 @@ var ( // Render combines the template with the step in the yaml pipeline. func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}, limit int64) (*types.Build, error) { - config := new(types.Build) + config := new(bkTypes.Build) thread := &starlark.Thread{Name: name} @@ -134,14 +135,14 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM config.Steps[index].Name = fmt.Sprintf("%s_%s", name, newStep.Name) } - return &types.Build{Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment}, nil + return &types.Build{Steps: *config.Steps.ToYAML(), Secrets: *config.Secrets.ToYAML(), Services: *config.Services.ToYAML(), Environment: config.Environment}, nil } // RenderBuild renders the templated build. // //nolint:lll // ignore function length due to input args func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}, limit int64) (*types.Build, error) { - config := new(types.Build) + config := new(bkTypes.Build) thread := &starlark.Thread{Name: "templated-base"} @@ -238,5 +239,5 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } - return config, nil + return config.ToYAML(), nil } diff --git a/compiler/template/starlark/render_test.go b/compiler/template/starlark/render_test.go index 33db5a7f0..b9d184dae 100644 --- a/compiler/template/starlark/render_test.go +++ b/compiler/template/starlark/render_test.go @@ -6,11 +6,11 @@ import ( "os" "testing" - goyaml "github.com/buildkite/yaml" "github.com/google/go-cmp/cmp" + goyaml "gopkg.in/yaml.v3" "github.com/go-vela/server/compiler/types/raw" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) func TestStarlark_Render(t *testing.T) { diff --git a/compiler/template/template.go b/compiler/template/template.go index a16cc4057..ee503e980 100644 --- a/compiler/template/template.go +++ b/compiler/template/template.go @@ -2,7 +2,7 @@ package template -import "github.com/go-vela/server/compiler/types/yaml" +import "github.com/go-vela/server/compiler/types/yaml/yaml" // Engine represents the interface for Vela integrating // with the different supported template engines. diff --git a/compiler/types/raw/map_test.go b/compiler/types/raw/map_test.go index 0494c8ed4..918664a65 100644 --- a/compiler/types/raw/map_test.go +++ b/compiler/types/raw/map_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/buildkite/yaml" + "gopkg.in/yaml.v3" ) func TestRaw_StringSliceMap_UnmarshalJSON(t *testing.T) { diff --git a/compiler/types/raw/slice_test.go b/compiler/types/raw/slice_test.go index 7a32bd3d7..0ae76f9dc 100644 --- a/compiler/types/raw/slice_test.go +++ b/compiler/types/raw/slice_test.go @@ -7,7 +7,7 @@ import ( "reflect" "testing" - "github.com/buildkite/yaml" + "gopkg.in/yaml.v3" ) func TestRaw_StringSlice_UnmarshalJSON(t *testing.T) { diff --git a/compiler/types/yaml/buildkite/build.go b/compiler/types/yaml/buildkite/build.go new file mode 100644 index 000000000..8bc8dcf60 --- /dev/null +++ b/compiler/types/yaml/buildkite/build.go @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +// Build is the yaml representation of a build for a pipeline. +type Build struct { + Version string `yaml:"version,omitempty" json:"version,omitempty" jsonschema:"required,minLength=1,description=Provide syntax version used to evaluate the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/version/"` + Metadata Metadata `yaml:"metadata,omitempty" json:"metadata,omitempty" jsonschema:"description=Pass extra information.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/"` + Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Provide global environment variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-key"` + Worker Worker `yaml:"worker,omitempty" json:"worker,omitempty" jsonschema:"description=Limit the pipeline to certain types of workers.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/"` + Secrets SecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Provide sensitive information.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/"` + Services ServiceSlice `yaml:"services,omitempty" json:"services,omitempty" jsonschema:"description=Provide detached (headless) execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/services/"` + Stages StageSlice `yaml:"stages,omitempty" json:"stages,omitempty" jsonschema:"oneof_required=stages,description=Provide parallel execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/"` + Steps StepSlice `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"oneof_required=steps,description=Provide sequential execution instructions.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/"` + Templates TemplateSlice `yaml:"templates,omitempty" json:"templates,omitempty" jsonschema:"description=Provide the name of templates to expand.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/"` + Deployment Deployment `yaml:"deployment,omitempty" json:"deployment,omitempty" jsonschema:"description=Provide deployment configuration.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/"` + Git Git `yaml:"git,omitempty" json:"git,omitempty" jsonschema:"description=Provide the git access specifications.\nReference: https://go-vela.github.io/docs/reference/yaml/git/"` +} + +// ToPipelineAPI converts the Build type to an API Pipeline type. +func (b *Build) ToPipelineAPI() *api.Pipeline { + pipeline := new(api.Pipeline) + + pipeline.SetFlavor(b.Worker.Flavor) + pipeline.SetPlatform(b.Worker.Platform) + pipeline.SetVersion(b.Version) + pipeline.SetServices(len(b.Services) > 0) + pipeline.SetStages(len(b.Stages) > 0) + pipeline.SetSteps(len(b.Steps) > 0) + pipeline.SetTemplates(len(b.Templates) > 0) + + // set default for external and internal secrets + external := false + internal := false + + // iterate through all secrets in the build + for _, secret := range b.Secrets { + // check if external and internal secrets have been found + if external && internal { + // exit the loop since both secrets have been found + break + } + + // check if the secret origin is empty + if secret.Origin.Empty() { + // origin was empty so an internal secret was found + internal = true + } else { + // origin was not empty so an external secret was found + external = true + } + } + + pipeline.SetExternalSecrets(external) + pipeline.SetInternalSecrets(internal) + + return pipeline +} + +// UnmarshalYAML implements the Unmarshaler interface for the Build type. +func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error { + // build we try unmarshalling to + build := new(struct { + Version string + Metadata Metadata + Environment raw.StringSliceMap + Worker Worker + Secrets SecretSlice + Services ServiceSlice + Stages StageSlice + Steps StepSlice + Templates TemplateSlice + Deployment Deployment + }) + + // attempt to unmarshal as a build type + err := unmarshal(build) + if err != nil { + return err + } + + // give the documented default value to metadata environment + if build.Metadata.Environment == nil { + build.Metadata.Environment = []string{"steps", "services", "secrets"} + } + + // override the values + b.Version = build.Version + b.Metadata = build.Metadata + b.Environment = build.Environment + b.Worker = build.Worker + b.Secrets = build.Secrets + b.Services = build.Services + b.Stages = build.Stages + b.Steps = build.Steps + b.Templates = build.Templates + b.Deployment = build.Deployment + + return nil +} + +func (b *Build) ToYAML() *yaml.Build { + return &yaml.Build{ + Version: b.Version, + Metadata: *b.Metadata.ToYAML(), + Git: *b.Git.ToYAML(), + Environment: b.Environment, + Worker: *b.Worker.ToYAML(), + Secrets: *b.Secrets.ToYAML(), + Services: *b.Services.ToYAML(), + Stages: *b.Stages.ToYAML(), + Steps: *b.Steps.ToYAML(), + Templates: *b.Templates.ToYAML(), + Deployment: *b.Deployment.ToYAML(), + } +} diff --git a/compiler/types/yaml/build_test.go b/compiler/types/yaml/buildkite/build_test.go similarity index 99% rename from compiler/types/yaml/build_test.go rename to compiler/types/yaml/buildkite/build_test.go index bf2d341f6..ebd0fa267 100644 --- a/compiler/types/yaml/build_test.go +++ b/compiler/types/yaml/buildkite/build_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/buildkite/deployment.go b/compiler/types/yaml/buildkite/deployment.go new file mode 100644 index 000000000..305371a5b --- /dev/null +++ b/compiler/types/yaml/buildkite/deployment.go @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +type ( + // Deployment is the yaml representation of a + // deployment block in a pipeline. + Deployment struct { + Targets raw.StringSlice `yaml:"targets,omitempty" json:"targets,omitempty" jsonschema:"description=List of deployment targets for the deployment block.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-targets-key"` + Parameters ParameterMap `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=List of parameters for the deployment block.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"description=Name of template to expand in the deployment block.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-template-key"` + } + + // ParameterMap is the yaml representation + // of the parameters block for a deployment block of a pipeline. + ParameterMap map[string]*Parameter + + // Parameters is the yaml representation of the deploy parameters + // from a deployment block in a pipeline. + Parameter struct { + Description string `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"description=Description of the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"description=Type of the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Required bool `yaml:"required,omitempty" json:"required,omitempty" jsonschema:"description=Flag indicating if the parameter is required.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Options raw.StringSlice `yaml:"options,omitempty" json:"options,omitempty" jsonschema:"description=List of options for the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Min int `yaml:"min,omitempty" json:"min,omitempty" jsonschema:"description=Minimum value for the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + Max int `yaml:"max,omitempty" json:"max,omitempty" jsonschema:"description=Maximum value for the parameter.\nReference: https://go-vela.github.io/docs/reference/yaml/deployments/#the-parameters-key"` + } +) + +// ToPipeline converts the Deployment type +// to a pipeline Deployment type. +func (d *Deployment) ToPipeline() *pipeline.Deployment { + return &pipeline.Deployment{ + Targets: d.Targets, + Parameters: d.Parameters.ToPipeline(), + } +} + +// ToPipeline converts the Parameters type +// to a pipeline Parameters type. +func (p *ParameterMap) ToPipeline() pipeline.ParameterMap { + if len(*p) == 0 { + return nil + } + + // parameter map we want to return + parameterMap := make(pipeline.ParameterMap) + + // iterate through each element in the parameter map + for k, v := range *p { + // add the element to the pipeline parameter map + parameterMap[k] = &pipeline.Parameter{ + Description: v.Description, + Type: v.Type, + Required: v.Required, + Options: v.Options, + Min: v.Min, + Max: v.Max, + } + } + + return parameterMap +} + +func (p *Parameter) ToYAML() *yaml.Parameter { + if p == nil { + return nil + } + + return &yaml.Parameter{ + Description: p.Description, + Type: p.Type, + Required: p.Required, + Options: p.Options, + Min: p.Min, + Max: p.Max, + } +} + +func (p *ParameterMap) ToYAML() yaml.ParameterMap { + if len(*p) == 0 { + return nil + } + + parameterMap := make(yaml.ParameterMap) + + // iterate through each element in the volume slice + for param := range *p { + // append the element to the yaml volume slice + parameterMap[param] = (*p)[param].ToYAML() + } + + return parameterMap +} + +func (d *Deployment) ToYAML() *yaml.Deployment { + if d == nil { + return nil + } + + return &yaml.Deployment{ + Targets: d.Targets, + Parameters: d.Parameters.ToYAML(), + Template: d.Template.ToYAML(), + } +} diff --git a/compiler/types/yaml/buildkite/deployment_test.go b/compiler/types/yaml/buildkite/deployment_test.go new file mode 100644 index 000000000..410920428 --- /dev/null +++ b/compiler/types/yaml/buildkite/deployment_test.go @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Deployment_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + name string + deployment *Deployment + want *pipeline.Deployment + }{ + { + name: "deployment with template", + deployment: &Deployment{ + Template: StepTemplate{Name: "foo"}, + }, + want: &pipeline.Deployment{}, + }, + { + name: "deployment with targets and parameters", + deployment: &Deployment{ + Targets: []string{"foo"}, + Parameters: ParameterMap{ + "foo": { + Description: "bar", + Type: "string", + Required: true, + Options: []string{"baz"}, + }, + "bar": { + Description: "baz", + Type: "string", + Required: false, + }, + }, + }, + want: &pipeline.Deployment{ + Targets: []string{"foo"}, + Parameters: pipeline.ParameterMap{ + "foo": { + Description: "bar", + Type: "string", + Required: true, + Options: []string{"baz"}, + }, + "bar": { + Description: "baz", + Type: "string", + Required: false, + }, + }, + }, + }, + { + name: "empty deployment config", + deployment: &Deployment{}, + want: &pipeline.Deployment{}, + }, + } + + // run tests + for _, test := range tests { + got := test.deployment.ToPipeline() + + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("ToPipeline for %s does not match: -want +got):\n%s", test.name, diff) + } + } +} diff --git a/compiler/types/yaml/buildkite/doc.go b/compiler/types/yaml/buildkite/doc.go new file mode 100644 index 000000000..07e33c5a4 --- /dev/null +++ b/compiler/types/yaml/buildkite/doc.go @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 + +// package buildkite provides the defined yaml types for Vela. +// +// Usage: +// +// import "github.com/go-vela/server/compiler/types/yaml/yaml" +package buildkite diff --git a/compiler/types/yaml/buildkite/git.go b/compiler/types/yaml/buildkite/git.go new file mode 100644 index 000000000..53f8e6bdf --- /dev/null +++ b/compiler/types/yaml/buildkite/git.go @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +// Git is the yaml representation of git configurations for a pipeline. +type Git struct { + Token `yaml:"token,omitempty" json:"token,omitempty" jsonschema:"description=Provide the git token specifications, primarily used for cloning.\nReference: https://go-vela.github.io/docs/reference/yaml/git/#token"` +} + +// Token is the yaml representation of the git token. +// Only applies when using GitHub App installations. +type Token struct { + Repositories []string `yaml:"repositories,omitempty" json:"repositories,omitempty" jsonschema:"description=Provide a list of repositories to clone.\nReference: https://go-vela.github.io/docs/reference/yaml/git/#repositories"` + Permissions map[string]string `yaml:"permissions,omitempty" json:"permissions,omitempty" jsonschema:"description=Provide a list of repository permissions to apply to the git token.\nReference: https://go-vela.github.io/docs/reference/yaml/git/#permissions"` +} + +// ToPipeline converts the Git type +// to a pipeline Git type. +func (g *Git) ToPipeline() *pipeline.Git { + return &pipeline.Git{ + Token: &pipeline.Token{ + Repositories: g.Repositories, + Permissions: g.Permissions, + }, + } +} + +func (g *Git) ToYAML() *yaml.Git { + if g == nil { + return nil + } + + return &yaml.Git{ + Token: yaml.Token{ + Repositories: g.Repositories, + Permissions: g.Permissions, + }, + } +} diff --git a/compiler/types/yaml/buildkite/git_test.go b/compiler/types/yaml/buildkite/git_test.go new file mode 100644 index 000000000..6d23f231d --- /dev/null +++ b/compiler/types/yaml/buildkite/git_test.go @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "reflect" + "testing" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Git_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + git *Git + want *pipeline.Git + }{ + { + git: &Git{ + Token: Token{ + Repositories: []string{"foo", "bar"}, + }, + }, + want: &pipeline.Git{ + Token: &pipeline.Token{ + Repositories: []string{"foo", "bar"}, + }, + }, + }, + { + git: &Git{ + Token: Token{ + Permissions: map[string]string{"foo": "bar"}, + }, + }, + want: &pipeline.Git{ + Token: &pipeline.Token{ + Permissions: map[string]string{"foo": "bar"}, + }, + }, + }, + { + git: &Git{ + Token: Token{}, + }, + want: &pipeline.Git{ + Token: &pipeline.Token{}, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.git.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/buildkite/metadata.go b/compiler/types/yaml/buildkite/metadata.go new file mode 100644 index 000000000..a57d83273 --- /dev/null +++ b/compiler/types/yaml/buildkite/metadata.go @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +type ( + // Metadata is the yaml representation of + // the metadata block for a pipeline. + Metadata struct { + Template bool `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"description=Enables compiling the pipeline as a template.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-template-key"` + RenderInline bool `yaml:"render_inline,omitempty" json:"render_inline,omitempty" jsonschema:"description=Enables inline compiling for the pipeline templates.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-render-inline-key"` + Clone *bool `yaml:"clone,omitempty" json:"clone,omitempty" jsonschema:"default=true,description=Enables injecting the default clone process.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-clone-key"` + Environment []string `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Controls which containers processes can have global env injected.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-environment-key"` + AutoCancel *CancelOptions `yaml:"auto_cancel,omitempty" json:"auto_cancel,omitempty" jsonschema:"description=Enables auto canceling of queued or running pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` + } + + // CancelOptions is the yaml representation of + // the auto_cancel block for a pipeline. + CancelOptions struct { + Running *bool `yaml:"running,omitempty" json:"running,omitempty" jsonschema:"description=Enables auto canceling of running pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` + Pending *bool `yaml:"pending,omitempty" json:"pending,omitempty" jsonschema:"description=Enables auto canceling of queued pipelines that become stale due to new push.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` + DefaultBranch *bool `yaml:"default_branch,omitempty" json:"default_branch,omitempty" jsonschema:"description=Enables auto canceling of queued or running pipelines that become stale due to new push to default branch.\nReference: https://go-vela.github.io/docs/reference/yaml/metadata/#the-auto-cancel-key"` + } +) + +// ToPipeline converts the Metadata type +// to a pipeline Metadata type. +func (m *Metadata) ToPipeline() *pipeline.Metadata { + var clone bool + if m.Clone == nil { + clone = true + } else { + clone = *m.Clone + } + + autoCancel := new(pipeline.CancelOptions) + + // default to false for all fields if block isn't found + if m.AutoCancel == nil { + autoCancel.Pending = false + autoCancel.Running = false + autoCancel.DefaultBranch = false + } else { + // if block is found but pending field isn't, default to true + if m.AutoCancel.Pending != nil { + autoCancel.Pending = *m.AutoCancel.Pending + } else { + autoCancel.Pending = true + } + + if m.AutoCancel.Running != nil { + autoCancel.Running = *m.AutoCancel.Running + } + + if m.AutoCancel.DefaultBranch != nil { + autoCancel.DefaultBranch = *m.AutoCancel.DefaultBranch + } + } + + return &pipeline.Metadata{ + Template: m.Template, + Clone: clone, + Environment: m.Environment, + AutoCancel: autoCancel, + } +} + +// HasEnvironment checks if the container type +// is contained within the environment list. +func (m *Metadata) HasEnvironment(container string) bool { + for _, e := range m.Environment { + if e == container { + return true + } + } + + return false +} + +func (m *Metadata) ToYAML() *yaml.Metadata { + if m == nil { + return nil + } + + return &yaml.Metadata{ + Template: m.Template, + RenderInline: m.RenderInline, + Clone: m.Clone, + Environment: m.Environment, + AutoCancel: m.AutoCancel.ToYAML(), + } +} + +func (ac *CancelOptions) ToYAML() *yaml.CancelOptions { + if ac == nil { + return nil + } + + return &yaml.CancelOptions{ + Pending: ac.Pending, + Running: ac.Running, + DefaultBranch: ac.DefaultBranch, + } +} diff --git a/compiler/types/yaml/buildkite/metadata_test.go b/compiler/types/yaml/buildkite/metadata_test.go new file mode 100644 index 000000000..a3c92bec8 --- /dev/null +++ b/compiler/types/yaml/buildkite/metadata_test.go @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "reflect" + "testing" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Metadata_ToPipeline(t *testing.T) { + tBool := true + fBool := false + // setup tests + tests := []struct { + metadata *Metadata + want *pipeline.Metadata + }{ + { + metadata: &Metadata{ + Template: false, + Clone: &fBool, + Environment: []string{"steps", "services", "secrets"}, + AutoCancel: &CancelOptions{ + Pending: &tBool, + Running: &tBool, + DefaultBranch: &fBool, + }, + }, + want: &pipeline.Metadata{ + Template: false, + Clone: false, + Environment: []string{"steps", "services", "secrets"}, + AutoCancel: &pipeline.CancelOptions{ + Pending: true, + Running: true, + DefaultBranch: false, + }, + }, + }, + { + metadata: &Metadata{ + Template: false, + Clone: &tBool, + Environment: []string{"steps", "services"}, + }, + want: &pipeline.Metadata{ + Template: false, + Clone: true, + Environment: []string{"steps", "services"}, + AutoCancel: &pipeline.CancelOptions{ + Pending: false, + Running: false, + DefaultBranch: false, + }, + }, + }, + { + metadata: &Metadata{ + Template: false, + Clone: nil, + Environment: []string{"steps"}, + AutoCancel: &CancelOptions{ + Running: &tBool, + DefaultBranch: &tBool, + }, + }, + want: &pipeline.Metadata{ + Template: false, + Clone: true, + Environment: []string{"steps"}, + AutoCancel: &pipeline.CancelOptions{ + Pending: true, + Running: true, + DefaultBranch: true, + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.metadata.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_Metadata_HasEnvironment(t *testing.T) { + // setup tests + tests := []struct { + metadata *Metadata + container string + want bool + }{ + { + metadata: &Metadata{ + Environment: []string{"steps", "services", "secrets"}, + }, + container: "steps", + want: true, + }, + { + metadata: &Metadata{ + Environment: []string{"services", "secrets"}, + }, + container: "services", + want: true, + }, + { + metadata: &Metadata{ + Environment: []string{"steps", "services", "secrets"}, + }, + container: "notacontainer", + want: false, + }, + } + + // run tests + for _, test := range tests { + got := test.metadata.HasEnvironment(test.container) + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/buildkite/ruleset.go b/compiler/types/yaml/buildkite/ruleset.go new file mode 100644 index 000000000..1b56e3062 --- /dev/null +++ b/compiler/types/yaml/buildkite/ruleset.go @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" + "github.com/go-vela/server/constants" +) + +type ( + // Ruleset is the yaml representation of a + // ruleset block for a step in a pipeline. + Ruleset struct { + If Rules `yaml:"if,omitempty" json:"if,omitempty" jsonschema:"description=Limit execution to when all rules match.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Unless Rules `yaml:"unless,omitempty" json:"unless,omitempty" jsonschema:"description=Limit execution to when all rules do not match.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Matcher string `yaml:"matcher,omitempty" json:"matcher,omitempty" jsonschema:"enum=filepath,enum=regexp,default=filepath,description=Use the defined matching method.\nReference: coming soon"` + Operator string `yaml:"operator,omitempty" json:"operator,omitempty" jsonschema:"enum=or,enum=and,default=and,description=Whether all rule conditions must be met or just any one of them.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Continue bool `yaml:"continue,omitempty" json:"continue,omitempty" jsonschema:"default=false,description=Limits the execution of a step to continuing on any failure.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + } + + // Rules is the yaml representation of the ruletypes + // from a ruleset block for a step in a pipeline. + Rules struct { + Branch []string `yaml:"branch,omitempty,flow" json:"branch,omitempty" jsonschema:"description=Limits the execution of a step to matching build branches.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Comment []string `yaml:"comment,omitempty,flow" json:"comment,omitempty" jsonschema:"description=Limits the execution of a step to matching a pull request comment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Event []string `yaml:"event,omitempty,flow" json:"event,omitempty" jsonschema:"description=Limits the execution of a step to matching build events.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Path []string `yaml:"path,omitempty,flow" json:"path,omitempty" jsonschema:"description=Limits the execution of a step to matching files changed in a repository.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Repo []string `yaml:"repo,omitempty,flow" json:"repo,omitempty" jsonschema:"description=Limits the execution of a step to matching repos.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Sender []string `yaml:"sender,omitempty,flow" json:"sender,omitempty" jsonschema:"description=Limits the execution of a step to matching build senders.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Status []string `yaml:"status,omitempty,flow" json:"status,omitempty" jsonschema:"enum=[failure],enum=[success],description=Limits the execution of a step to matching build statuses.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Tag []string `yaml:"tag,omitempty,flow" json:"tag,omitempty" jsonschema:"description=Limits the execution of a step to matching build tag references.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Target []string `yaml:"target,omitempty,flow" json:"target,omitempty" jsonschema:"description=Limits the execution of a step to matching build deployment targets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Label []string `yaml:"label,omitempty,flow" json:"label,omitempty" jsonschema:"description=Limits step execution to match on pull requests labels.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Instance []string `yaml:"instance,omitempty,flow" json:"instance,omitempty" jsonschema:"description=Limits step execution to match on certain instances.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + } +) + +// ToPipeline converts the Ruleset type +// to a pipeline Ruleset type. +func (r *Ruleset) ToPipeline() *pipeline.Ruleset { + return &pipeline.Ruleset{ + If: *r.If.ToPipeline(), + Unless: *r.Unless.ToPipeline(), + Matcher: r.Matcher, + Operator: r.Operator, + Continue: r.Continue, + } +} + +// UnmarshalYAML implements the Unmarshaler interface for the Ruleset type. +func (r *Ruleset) UnmarshalYAML(unmarshal func(interface{}) error) error { + // simple struct we try unmarshalling to + simple := new(Rules) + + // advanced struct we try unmarshalling to + advanced := new(struct { + If Rules + Unless Rules + Matcher string + Operator string + Continue bool + }) + + // attempt to unmarshal simple ruleset + //nolint:errcheck // intentionally not handling error + unmarshal(simple) + // attempt to unmarshal advanced ruleset + //nolint:errcheck // intentionally not handling error + unmarshal(advanced) + + // set ruleset `unless` to advanced `unless` rules + r.Unless = advanced.Unless + // set ruleset `matcher` to advanced `matcher` + r.Matcher = advanced.Matcher + // set ruleset `operator` to advanced `operator` + r.Operator = advanced.Operator + // set ruleset `continue` to advanced `continue` + r.Continue = advanced.Continue + + // implicitly add simple ruleset to the advanced ruleset for each rule type + advanced.If.Branch = append(advanced.If.Branch, simple.Branch...) + advanced.If.Comment = append(advanced.If.Comment, simple.Comment...) + advanced.If.Event = append(advanced.If.Event, simple.Event...) + advanced.If.Path = append(advanced.If.Path, simple.Path...) + advanced.If.Repo = append(advanced.If.Repo, simple.Repo...) + advanced.If.Sender = append(advanced.If.Sender, simple.Sender...) + advanced.If.Status = append(advanced.If.Status, simple.Status...) + advanced.If.Tag = append(advanced.If.Tag, simple.Tag...) + advanced.If.Target = append(advanced.If.Target, simple.Target...) + advanced.If.Label = append(advanced.If.Label, simple.Label...) + advanced.If.Instance = append(advanced.If.Instance, simple.Instance...) + + // set ruleset `if` to advanced `if` rules + r.If = advanced.If + + // implicitly set `matcher` field if empty for ruleset + if len(r.Matcher) == 0 { + r.Matcher = constants.MatcherFilepath + } + + // implicitly set `operator` field if empty for ruleset + if len(r.Operator) == 0 { + r.Operator = constants.OperatorAnd + } + + return nil +} + +// ToPipeline converts the Rules +// type to a pipeline Rules type. +func (r *Rules) ToPipeline() *pipeline.Rules { + return &pipeline.Rules{ + Branch: r.Branch, + Comment: r.Comment, + Event: r.Event, + Path: r.Path, + Repo: r.Repo, + Sender: r.Sender, + Status: r.Status, + Tag: r.Tag, + Target: r.Target, + Label: r.Label, + Instance: r.Instance, + } +} + +// UnmarshalYAML implements the Unmarshaler interface for the Rules type. +func (r *Rules) UnmarshalYAML(unmarshal func(interface{}) error) error { + // rules struct we try unmarshalling to + rules := new(struct { + Branch raw.StringSlice + Comment raw.StringSlice + Event raw.StringSlice + Path raw.StringSlice + Repo raw.StringSlice + Sender raw.StringSlice + Status raw.StringSlice + Tag raw.StringSlice + Target raw.StringSlice + Label raw.StringSlice + Instance raw.StringSlice + }) + + // attempt to unmarshal rules + err := unmarshal(rules) + if err == nil { + r.Branch = rules.Branch + r.Comment = rules.Comment + r.Path = rules.Path + r.Repo = rules.Repo + r.Sender = rules.Sender + r.Status = rules.Status + r.Tag = rules.Tag + r.Target = rules.Target + r.Label = rules.Label + r.Instance = rules.Instance + + // account for users who use non-scoped pull_request event + events := []string{} + + for _, e := range rules.Event { + switch e { + // backwards compatibility + // pull_request = pull_request:opened + pull_request:synchronize + pull_request:reopened + // comment = comment:created + comment:edited + case constants.EventPull: + events = append(events, + constants.EventPull+":"+constants.ActionOpened, + constants.EventPull+":"+constants.ActionSynchronize, + constants.EventPull+":"+constants.ActionReopened) + case constants.EventDeploy: + events = append(events, + constants.EventDeploy+":"+constants.ActionCreated) + case constants.EventComment: + events = append(events, + constants.EventComment+":"+constants.ActionCreated, + constants.EventComment+":"+constants.ActionEdited) + default: + events = append(events, e) + } + } + + r.Event = events + } + + return err +} + +func (r *Ruleset) ToYAML() *yaml.Ruleset { + if r == nil { + return nil + } + + return &yaml.Ruleset{ + If: *r.If.ToYAML(), + Unless: *r.Unless.ToYAML(), + Matcher: r.Matcher, + Operator: r.Operator, + Continue: r.Continue, + } +} + +func (r *Rules) ToYAML() *yaml.Rules { + if r == nil { + return nil + } + + return &yaml.Rules{ + Branch: r.Branch, + Comment: r.Comment, + Event: r.Event, + Path: r.Path, + Repo: r.Repo, + Sender: r.Sender, + Status: r.Status, + Tag: r.Tag, + Target: r.Target, + Label: r.Label, + Instance: r.Instance, + } +} diff --git a/compiler/types/yaml/ruleset_test.go b/compiler/types/yaml/buildkite/ruleset_test.go similarity index 99% rename from compiler/types/yaml/ruleset_test.go rename to compiler/types/yaml/buildkite/ruleset_test.go index 6275046b0..adc926c9a 100644 --- a/compiler/types/yaml/ruleset_test.go +++ b/compiler/types/yaml/buildkite/ruleset_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/buildkite/secret.go b/compiler/types/yaml/buildkite/secret.go new file mode 100644 index 000000000..b6ea8d71a --- /dev/null +++ b/compiler/types/yaml/buildkite/secret.go @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "errors" + "fmt" + "strings" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" + "github.com/go-vela/server/constants" +) + +type ( + // SecretSlice is the yaml representation + // of the secrets block for a pipeline. + SecretSlice []*Secret + + // Secret is the yaml representation of a secret + // from the secrets block for a pipeline. + Secret struct { + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Name of secret to reference in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-name-key"` + Key string `yaml:"key,omitempty" json:"key,omitempty" jsonschema:"minLength=1,description=Path to secret to fetch from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-key-key"` + Engine string `yaml:"engine,omitempty" json:"engine,omitempty" jsonschema:"enum=native,enum=vault,default=native,description=Name of storage backend to fetch secret from.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-engine-key"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"enum=repo,enum=org,enum=shared,default=repo,description=Type of secret to fetch from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-type-key"` + Origin Origin `yaml:"origin,omitempty" json:"origin,omitempty" jsonschema:"description=Declaration to pull secrets from non-internal secret providers.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-origin-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=step_start,enum=build_start,default=build_start,description=When to pull in secrets from storage backend.\nReference: https://go-vela.github.io/docs/reference/yaml/secrets/#the-pull-key"` + } + + // Origin is the yaml representation of a method + // for looking up secrets with a secret plugin. + Origin struct { + Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Variables to inject into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-key"` + Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"required,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the secret origin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-key"` + Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for the secret plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-key"` + Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Secrets to inject that are necessary to retrieve the secrets.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-key"` + Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + } +) + +// ToPipeline converts the SecretSlice type +// to a pipeline SecretSlice type. +func (s *SecretSlice) ToPipeline() *pipeline.SecretSlice { + // secret slice we want to return + secretSlice := new(pipeline.SecretSlice) + + // iterate through each element in the secret slice + for _, secret := range *s { + // append the element to the pipeline secret slice + *secretSlice = append(*secretSlice, &pipeline.Secret{ + Name: secret.Name, + Key: secret.Key, + Engine: secret.Engine, + Type: secret.Type, + Origin: secret.Origin.ToPipeline(), + Pull: secret.Pull, + }) + } + + return secretSlice +} + +// UnmarshalYAML implements the Unmarshaler interface for the SecretSlice type. +func (s *SecretSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { + // secret slice we try unmarshalling to + secretSlice := new([]*Secret) + + // attempt to unmarshal as a secret slice type + err := unmarshal(secretSlice) + if err != nil { + return err + } + + tmp := SecretSlice{} + + // iterate through each element in the secret slice + for _, secret := range *secretSlice { + if secret.Origin.Empty() && len(secret.Name) == 0 { + continue + } + + if secret.Origin.Empty() && len(secret.Key) == 0 { + secret.Key = secret.Name + } + + // implicitly set `engine` field if empty + if secret.Origin.Empty() && len(secret.Engine) == 0 { + secret.Engine = constants.DriverNative + } + + // implicitly set `type` field if empty + if secret.Origin.Empty() && len(secret.Type) == 0 { + secret.Type = constants.SecretRepo + } + + // implicitly set `type` field if empty + if secret.Origin.Empty() && len(secret.Pull) == 0 { + secret.Pull = constants.SecretPullBuild + } + + // implicitly set `pull` field if empty + if !secret.Origin.Empty() && len(secret.Origin.Pull) == 0 { + secret.Origin.Pull = constants.PullNotPresent + } + + // TODO: remove this in a future release + // + // handle true deprecated pull policy + // + // a `true` pull policy equates to `always` + if !secret.Origin.Empty() && strings.EqualFold(secret.Origin.Pull, "true") { + secret.Origin.Pull = constants.PullAlways + } + + // TODO: remove this in a future release + // + // handle false deprecated pull policy + // + // a `false` pull policy equates to `not_present` + if !secret.Origin.Empty() && strings.EqualFold(secret.Origin.Pull, "false") { + secret.Origin.Pull = constants.PullNotPresent + } + + tmp = append(tmp, secret) + } + + // overwrite existing SecretSlice + *s = tmp + + return nil +} + +// Empty returns true if the provided origin is empty. +func (o *Origin) Empty() bool { + // return true if the origin is nil + if o == nil { + return true + } + + // return true if every origin field is empty + if o.Environment == nil && + len(o.Image) == 0 && + len(o.Name) == 0 && + o.Parameters == nil && + len(o.Secrets) == 0 && + len(o.Pull) == 0 { + return true + } + + return false +} + +// MergeEnv takes a list of environment variables and attempts +// to set them in the secret environment. If the environment +// variable already exists in the secret, than this will +// overwrite the existing environment variable. +func (o *Origin) MergeEnv(environment map[string]string) error { + // check if the secret container is empty + if o.Empty() { + // TODO: evaluate if we should error here + // + // immediately return and do nothing + // + // treated as a no-op + return nil + } + + // check if the environment provided is empty + if environment == nil { + return fmt.Errorf("empty environment provided for secret %s", o.Name) + } + + // iterate through all environment variables provided + for key, value := range environment { + // set or update the secret environment variable + o.Environment[key] = value + } + + return nil +} + +// ToPipeline converts the Origin type +// to a pipeline Container type. +func (o *Origin) ToPipeline() *pipeline.Container { + return &pipeline.Container{ + Environment: o.Environment, + Image: o.Image, + Name: o.Name, + Pull: o.Pull, + Ruleset: *o.Ruleset.ToPipeline(), + Secrets: *o.Secrets.ToPipeline(), + } +} + +type ( + // StepSecretSlice is the yaml representation of + // the secrets block for a step in a pipeline. + StepSecretSlice []*StepSecret + + // StepSecret is the yaml representation of a secret + // from a secrets block for a step in a pipeline. + StepSecret struct { + Source string `yaml:"source,omitempty"` + Target string `yaml:"target,omitempty"` + } +) + +// ToPipeline converts the StepSecretSlice type +// to a pipeline StepSecretSlice type. +func (s *StepSecretSlice) ToPipeline() *pipeline.StepSecretSlice { + // step secret slice we want to return + secretSlice := new(pipeline.StepSecretSlice) + + // iterate through each element in the step secret slice + for _, secret := range *s { + // append the element to the pipeline step secret slice + *secretSlice = append(*secretSlice, &pipeline.StepSecret{ + Source: secret.Source, + Target: secret.Target, + }) + } + + return secretSlice +} + +// UnmarshalYAML implements the Unmarshaler interface for the StepSecretSlice type. +func (s *StepSecretSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { + // string slice we try unmarshalling to + stringSlice := new(raw.StringSlice) + + // attempt to unmarshal as a string slice type + err := unmarshal(stringSlice) + if err == nil { + // iterate through each element in the string slice + for _, secret := range *stringSlice { + // append the element to the step secret slice + *s = append(*s, &StepSecret{ + Source: secret, + Target: strings.ToUpper(secret), + }) + } + + return nil + } + + // step secret slice we try unmarshalling to + secrets := new([]*StepSecret) + + // attempt to unmarshal as a step secret slice type + err = unmarshal(secrets) + if err == nil { + // check for secret source and target + for _, secret := range *secrets { + if len(secret.Source) == 0 || len(secret.Target) == 0 { + return fmt.Errorf("no secret source or target found") + } + + secret.Target = strings.ToUpper(secret.Target) + } + + // overwrite existing StepSecretSlice + *s = StepSecretSlice(*secrets) + + return nil + } + + return errors.New("failed to unmarshal StepSecretSlice") +} + +func (s *StepSecretSlice) ToYAML() *yaml.StepSecretSlice { + // step secret slice we want to return + secretSlice := new(yaml.StepSecretSlice) + + // iterate through each element in the step secret slice + for _, secret := range *s { + // append the element to the yaml step secret slice + *secretSlice = append(*secretSlice, &yaml.StepSecret{ + Source: secret.Source, + Target: secret.Target, + }) + } + + return secretSlice +} + +func (o *Origin) ToYAML() yaml.Origin { + return yaml.Origin{ + Environment: o.Environment, + Image: o.Image, + Name: o.Name, + Parameters: o.Parameters, + Secrets: *o.Secrets.ToYAML(), + Pull: o.Pull, + Ruleset: *o.Ruleset.ToYAML(), + } +} + +func (s *Secret) ToYAML() *yaml.Secret { + if s == nil { + return nil + } + + return &yaml.Secret{ + Name: s.Name, + Key: s.Key, + Engine: s.Engine, + Type: s.Type, + Origin: s.Origin.ToYAML(), + Pull: s.Pull, + } +} + +func (s *SecretSlice) ToYAML() *yaml.SecretSlice { + // secret slice we want to return + secretSlice := new(yaml.SecretSlice) + + // iterate through each element in the secret slice + for _, secret := range *s { + // append the element to the yaml secret slice + *secretSlice = append(*secretSlice, secret.ToYAML()) + } + + return secretSlice +} diff --git a/compiler/types/yaml/secret_test.go b/compiler/types/yaml/buildkite/secret_test.go similarity index 99% rename from compiler/types/yaml/secret_test.go rename to compiler/types/yaml/buildkite/secret_test.go index 68a9ed4d6..1d731e477 100644 --- a/compiler/types/yaml/secret_test.go +++ b/compiler/types/yaml/buildkite/secret_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/buildkite/service.go b/compiler/types/yaml/buildkite/service.go new file mode 100644 index 000000000..9fb9a0ed1 --- /dev/null +++ b/compiler/types/yaml/buildkite/service.go @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "fmt" + "strings" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" + "github.com/go-vela/server/constants" +) + +type ( + // ServiceSlice is the yaml representation + // of the Services block for a pipeline. + ServiceSlice []*Service + + // Service is the yaml representation + // of a Service in a pipeline. + Service struct { + Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"required,minLength=1,description=Docker image used to create ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-image-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the container in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-name-key"` + Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Commands to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-entrypoint-key"` + Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Variables to inject into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-environment-key"` + Ports raw.StringSlice `yaml:"ports,omitempty" json:"ports,omitempty" jsonschema:"description=List of ports to map for the container in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-ports-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-pul-key"` + Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/services/#the-ulimits-key"` + User string `yaml:"user,omitempty" json:"user,omitempty" jsonschema:"description=Set the user for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-user-key"` + } +) + +// ToPipeline converts the ServiceSlice type +// to a pipeline ContainerSlice type. +func (s *ServiceSlice) ToPipeline() *pipeline.ContainerSlice { + // service slice we want to return + serviceSlice := new(pipeline.ContainerSlice) + + // iterate through each element in the service slice + for _, service := range *s { + // append the element to the pipeline container slice + *serviceSlice = append(*serviceSlice, &pipeline.Container{ + Detach: true, + Image: service.Image, + Name: service.Name, + Entrypoint: service.Entrypoint, + Environment: service.Environment, + Ports: service.Ports, + Pull: service.Pull, + Ulimits: *service.Ulimits.ToPipeline(), + User: service.User, + }) + } + + return serviceSlice +} + +// UnmarshalYAML implements the Unmarshaler interface for the ServiceSlice type. +func (s *ServiceSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { + // service slice we try unmarshalling to + serviceSlice := new([]*Service) + + // attempt to unmarshal as a service slice type + err := unmarshal(serviceSlice) + if err != nil { + return err + } + + // iterate through each element in the service slice + for _, service := range *serviceSlice { + // handle nil service to avoid panic + if service == nil { + return fmt.Errorf("invalid service with nil content found") + } + + // implicitly set `pull` field if empty + if len(service.Pull) == 0 { + service.Pull = constants.PullNotPresent + } + + // TODO: remove this in a future release + // + // handle true deprecated pull policy + // + // a `true` pull policy equates to `always` + if strings.EqualFold(service.Pull, "true") { + service.Pull = constants.PullAlways + } + + // TODO: remove this in a future release + // + // handle false deprecated pull policy + // + // a `false` pull policy equates to `not_present` + if strings.EqualFold(service.Pull, "false") { + service.Pull = constants.PullNotPresent + } + } + + // overwrite existing ServiceSlice + *s = ServiceSlice(*serviceSlice) + + return nil +} + +// MergeEnv takes a list of environment variables and attempts +// to set them in the service environment. If the environment +// variable already exists in the service, than this will +// overwrite the existing environment variable. +func (s *Service) MergeEnv(environment map[string]string) error { + // check if the service container is empty + if s == nil || s.Environment == nil { + // TODO: evaluate if we should error here + // + // immediately return and do nothing + // + // treated as a no-op + return nil + } + + // check if the environment provided is empty + if environment == nil { + return fmt.Errorf("empty environment provided for service %s", s.Name) + } + + // iterate through all environment variables provided + for key, value := range environment { + // set or update the service environment variable + s.Environment[key] = value + } + + return nil +} + +func (s *Service) ToYAML() *yaml.Service { + if s == nil { + return nil + } + + return &yaml.Service{ + Image: s.Image, + Name: s.Name, + Entrypoint: s.Entrypoint, + Environment: s.Environment, + Ports: s.Ports, + Pull: s.Pull, + Ulimits: *s.Ulimits.ToYAML(), + User: s.User, + } +} + +func (s *ServiceSlice) ToYAML() *yaml.ServiceSlice { + // service slice we want to return + serviceSlice := new(yaml.ServiceSlice) + + // iterate through each element in the service slice + for _, service := range *s { + // append the element to the yaml service slice + *serviceSlice = append(*serviceSlice, service.ToYAML()) + } + + return serviceSlice +} diff --git a/compiler/types/yaml/service_test.go b/compiler/types/yaml/buildkite/service_test.go similarity index 99% rename from compiler/types/yaml/service_test.go rename to compiler/types/yaml/buildkite/service_test.go index 09f4fbc16..bc97c258e 100644 --- a/compiler/types/yaml/service_test.go +++ b/compiler/types/yaml/buildkite/service_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/stage.go b/compiler/types/yaml/buildkite/stage.go similarity index 85% rename from compiler/types/yaml/stage.go rename to compiler/types/yaml/buildkite/stage.go index 79b635b05..c7909d2f7 100644 --- a/compiler/types/yaml/stage.go +++ b/compiler/types/yaml/buildkite/stage.go @@ -1,15 +1,16 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "fmt" - "github.com/buildkite/yaml" + bkYaml "github.com/buildkite/yaml" "github.com/invopop/jsonschema" "github.com/go-vela/server/compiler/types/pipeline" "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) type ( @@ -53,7 +54,7 @@ func (s *StageSlice) ToPipeline() *pipeline.StageSlice { // UnmarshalYAML implements the Unmarshaler interface for the StageSlice type. func (s *StageSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { // map slice we try unmarshalling to - mapSlice := new(yaml.MapSlice) + mapSlice := new(bkYaml.MapSlice) // attempt to unmarshal as a map slice type err := unmarshal(mapSlice) @@ -67,10 +68,10 @@ func (s *StageSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { stage := new(Stage) // marshal interface value from ordered map - out, _ := yaml.Marshal(v.Value) + out, _ := bkYaml.Marshal(v.Value) // unmarshal interface value as stage - err = yaml.Unmarshal(out, stage) + err = bkYaml.Unmarshal(out, stage) if err != nil { return err } @@ -103,7 +104,7 @@ func (s *StageSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { // MarshalYAML implements the marshaler interface for the StageSlice type. func (s StageSlice) MarshalYAML() (interface{}, error) { // map slice to return as marshaled output - var output yaml.MapSlice + var output bkYaml.MapSlice // loop over the input stages for _, inputStage := range s { @@ -120,7 +121,7 @@ func (s StageSlice) MarshalYAML() (interface{}, error) { outputStage.Steps = inputStage.Steps // append stage to MapSlice - output = append(output, yaml.MapItem{Key: inputStage.Name, Value: outputStage}) + output = append(output, bkYaml.MapItem{Key: inputStage.Name, Value: outputStage}) } return output, nil @@ -170,3 +171,30 @@ func (s *Stage) MergeEnv(environment map[string]string) error { return nil } + +func (s *Stage) ToYAML() *yaml.Stage { + if s == nil { + return nil + } + + return &yaml.Stage{ + Environment: s.Environment, + Name: s.Name, + Needs: s.Needs, + Independent: s.Independent, + Steps: *s.Steps.ToYAML(), + } +} + +func (s *StageSlice) ToYAML() *yaml.StageSlice { + // stage slice we want to return + stageSlice := new(yaml.StageSlice) + + // iterate through each element in the stage slice + for _, stage := range *s { + // append the element to the yaml stage slice + *stageSlice = append(*stageSlice, stage.ToYAML()) + } + + return stageSlice +} diff --git a/compiler/types/yaml/stage_test.go b/compiler/types/yaml/buildkite/stage_test.go similarity index 99% rename from compiler/types/yaml/stage_test.go rename to compiler/types/yaml/buildkite/stage_test.go index 7c63253b8..3c87a3859 100644 --- a/compiler/types/yaml/stage_test.go +++ b/compiler/types/yaml/buildkite/stage_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/buildkite/step.go b/compiler/types/yaml/buildkite/step.go new file mode 100644 index 000000000..4b36b93fd --- /dev/null +++ b/compiler/types/yaml/buildkite/step.go @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "fmt" + "strings" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" + "github.com/go-vela/server/constants" +) + +type ( + // StepSlice is the yaml representation + // of the steps block for a pipeline. + StepSlice []*Step + + // Step is the yaml representation of a step + // from the steps block for a pipeline. + Step struct { + Ruleset Ruleset `yaml:"ruleset,omitempty" json:"ruleset,omitempty" jsonschema:"description=Conditions to limit the execution of the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ruleset-key"` + Commands raw.StringSlice `yaml:"commands,omitempty" json:"commands,omitempty" jsonschema:"description=Execution instructions to run inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-commands-key"` + Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Command to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-entrypoint-key"` + Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Sensitive variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` + Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"oneof_required=template,description=Name of template to expand in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-key"` + Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` + Volumes VolumeSlice `yaml:"volumes,omitempty" json:"volumes,omitempty" jsonschema:"description=Mount volumes for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` + Image string `yaml:"image,omitempty" json:"image,omitempty" jsonschema:"oneof_required=image,minLength=1,description=Docker image to use to create the ephemeral container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-image-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-name-key"` + Pull string `yaml:"pull,omitempty" json:"pull,omitempty" jsonschema:"enum=always,enum=not_present,enum=on_start,enum=never,default=not_present,description=Declaration to configure if and when the Docker image is pulled.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-pull-key"` + Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Provide environment variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-environment-key"` + Parameters map[string]interface{} `yaml:"parameters,omitempty" json:"parameters,omitempty" jsonschema:"description=Extra configuration variables for a plugin.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-parameters-key"` + Detach bool `yaml:"detach,omitempty" json:"detach,omitempty" jsonschema:"description=Run the container in a detached (headless) state.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-detach-key"` + Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty" jsonschema:"description=Run the container with extra privileges.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-privileged-key"` + User string `yaml:"user,omitempty" json:"user,omitempty" jsonschema:"description=Set the user for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-user-key"` + ReportAs string `yaml:"report_as,omitempty" json:"report_as,omitempty" jsonschema:"description=Set the name of the step to report as.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-report_as-key"` + IDRequest string `yaml:"id_request,omitempty" json:"id_request,omitempty" jsonschema:"description=Request ID Request Token for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-id_request-key"` + } +) + +// ToPipeline converts the StepSlice type +// to a pipeline ContainerSlice type. +func (s *StepSlice) ToPipeline() *pipeline.ContainerSlice { + // step slice we want to return + stepSlice := new(pipeline.ContainerSlice) + + // iterate through each element in the step slice + for _, step := range *s { + // append the element to the pipeline container slice + *stepSlice = append(*stepSlice, &pipeline.Container{ + Commands: step.Commands, + Detach: step.Detach, + Entrypoint: step.Entrypoint, + Environment: step.Environment, + Image: step.Image, + Name: step.Name, + Privileged: step.Privileged, + Pull: step.Pull, + Ruleset: *step.Ruleset.ToPipeline(), + Secrets: *step.Secrets.ToPipeline(), + Ulimits: *step.Ulimits.ToPipeline(), + Volumes: *step.Volumes.ToPipeline(), + User: step.User, + ReportAs: step.ReportAs, + IDRequest: step.IDRequest, + }) + } + + return stepSlice +} + +// UnmarshalYAML implements the Unmarshaler interface for the StepSlice type. +func (s *StepSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { + // step slice we try unmarshalling to + stepSlice := new([]*Step) + + // attempt to unmarshal as a step slice type + err := unmarshal(stepSlice) + if err != nil { + return err + } + + // iterate through each element in the step slice + for _, step := range *stepSlice { + // handle nil step to avoid panic + if step == nil { + return fmt.Errorf("invalid step with nil content found") + } + + // implicitly set `pull` field if empty + if len(step.Pull) == 0 { + step.Pull = constants.PullNotPresent + } + + // TODO: remove this in a future release + // + // handle true deprecated pull policy + // + // a `true` pull policy equates to `always` + if strings.EqualFold(step.Pull, "true") { + step.Pull = constants.PullAlways + } + + // TODO: remove this in a future release + // + // handle false deprecated pull policy + // + // a `false` pull policy equates to `not_present` + if strings.EqualFold(step.Pull, "false") { + step.Pull = constants.PullNotPresent + } + } + + // overwrite existing StepSlice + *s = StepSlice(*stepSlice) + + return nil +} + +// MergeEnv takes a list of environment variables and attempts +// to set them in the step environment. If the environment +// variable already exists in the step, than this will +// overwrite the existing environment variable. +func (s *Step) MergeEnv(environment map[string]string) error { + // check if the step container is empty + if s == nil || s.Environment == nil { + // TODO: evaluate if we should error here + // + // immediately return and do nothing + // + // treated as a no-op + return nil + } + + // check if the environment provided is empty + if environment == nil { + return fmt.Errorf("empty environment provided for step %s", s.Name) + } + + // iterate through all environment variables provided + for key, value := range environment { + // set or update the step environment variable + s.Environment[key] = value + } + + return nil +} + +func (s *Step) ToYAML() *yaml.Step { + if s == nil { + return nil + } + + return &yaml.Step{ + Commands: s.Commands, + Detach: s.Detach, + Entrypoint: s.Entrypoint, + Environment: s.Environment, + Image: s.Image, + Name: s.Name, + Privileged: s.Privileged, + Pull: s.Pull, + Ruleset: *s.Ruleset.ToYAML(), + Secrets: *s.Secrets.ToYAML(), + Template: s.Template.ToYAML(), + Ulimits: *s.Ulimits.ToYAML(), + Volumes: *s.Volumes.ToYAML(), + Parameters: s.Parameters, + User: s.User, + ReportAs: s.ReportAs, + IDRequest: s.IDRequest, + } +} + +func (s *StepSlice) ToYAML() *yaml.StepSlice { + // step slice we want to return + stepSlice := new(yaml.StepSlice) + + // iterate through each element in the step slice + for _, step := range *s { + // append the element to the yaml step slice + *stepSlice = append(*stepSlice, step.ToYAML()) + } + + return stepSlice +} diff --git a/compiler/types/yaml/step_test.go b/compiler/types/yaml/buildkite/step_test.go similarity index 99% rename from compiler/types/yaml/step_test.go rename to compiler/types/yaml/buildkite/step_test.go index 8501c66a1..cb3329dc0 100644 --- a/compiler/types/yaml/step_test.go +++ b/compiler/types/yaml/buildkite/step_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/buildkite/template.go b/compiler/types/yaml/buildkite/template.go new file mode 100644 index 000000000..f9380dab3 --- /dev/null +++ b/compiler/types/yaml/buildkite/template.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +type ( + // TemplateSlice is the yaml representation + // of the templates block for a pipeline. + TemplateSlice []*Template + + // Template is the yaml representation of a template + // from the templates block for a pipeline. + Template struct { + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-name-key"` + Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"required,minLength=1,description=Path to template in remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-source-key"` + Format string `yaml:"format,omitempty" json:"format,omitempty" jsonschema:"enum=starlark,enum=golang,enum=go,default=go,minLength=1,description=language used within the template file \nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-format-key"` + Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"minLength=1,example=github,description=Type of template provided from the remote system.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-type-key"` + Variables map[string]interface{} `yaml:"vars,omitempty" json:"vars,omitempty" jsonschema:"description=Variables injected into the template.\nReference: https://go-vela.github.io/docs/reference/yaml/templates/#the-variables-key"` + } + + // StepTemplate is the yaml representation of the + // template block for a step in a pipeline. + StepTemplate struct { + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique identifier for the template.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-key"` + Variables map[string]interface{} `yaml:"vars,omitempty" json:"vars,omitempty" jsonschema:"description=Variables injected into the template.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-key"` + } +) + +// UnmarshalYAML implements the Unmarshaler interface for the TemplateSlice type. +func (t *TemplateSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { + // template slice we try unmarshalling to + templateSlice := new([]*Template) + + // attempt to unmarshal as a template slice type + err := unmarshal(templateSlice) + if err != nil { + return err + } + + // overwrite existing TemplateSlice + *t = TemplateSlice(*templateSlice) + + return nil +} + +// ToAPI converts the Template type +// to an API Template type. +func (t *Template) ToAPI() *api.Template { + template := new(api.Template) + + template.SetName(t.Name) + template.SetSource(t.Source) + template.SetType(t.Type) + + return template +} + +// TemplateFromAPI converts the API Template type +// to a yaml Template type. +func TemplateFromAPI(t *api.Template) *Template { + template := &Template{ + Name: t.GetName(), + Source: t.GetSource(), + Type: t.GetType(), + } + + return template +} + +// Map helper function that creates a map of templates from a slice of templates. +func (t *TemplateSlice) Map() map[string]*Template { + m := make(map[string]*Template) + + if t == nil { + return m + } + + for _, tmpl := range *t { + m[tmpl.Name] = tmpl + } + + return m +} + +func (t *Template) ToYAML() *yaml.Template { + if t == nil { + return nil + } + + return &yaml.Template{ + Name: t.Name, + Source: t.Source, + Format: t.Format, + Type: t.Type, + Variables: t.Variables, + } +} + +func (t *TemplateSlice) ToYAML() *yaml.TemplateSlice { + // template slice we want to return + templateSlice := new(yaml.TemplateSlice) + + // iterate through each element in the template slice + for _, template := range *t { + // append the element to the yaml template slice + *templateSlice = append(*templateSlice, template.ToYAML()) + } + + return templateSlice +} + +func (t *StepTemplate) ToYAML() yaml.StepTemplate { + return yaml.StepTemplate{ + Name: t.Name, + Variables: t.Variables, + } +} diff --git a/compiler/types/yaml/template_test.go b/compiler/types/yaml/buildkite/template_test.go similarity index 99% rename from compiler/types/yaml/template_test.go rename to compiler/types/yaml/buildkite/template_test.go index b000cd70b..f81481005 100644 --- a/compiler/types/yaml/template_test.go +++ b/compiler/types/yaml/buildkite/template_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/testdata/build.yml b/compiler/types/yaml/buildkite/testdata/build.yml similarity index 100% rename from compiler/types/yaml/testdata/build.yml rename to compiler/types/yaml/buildkite/testdata/build.yml diff --git a/compiler/types/yaml/testdata/build/validate/bad_pipeline0.yml b/compiler/types/yaml/buildkite/testdata/build/validate/bad_pipeline0.yml similarity index 100% rename from compiler/types/yaml/testdata/build/validate/bad_pipeline0.yml rename to compiler/types/yaml/buildkite/testdata/build/validate/bad_pipeline0.yml diff --git a/compiler/types/yaml/testdata/build/validate/bad_pipeline1.yml b/compiler/types/yaml/buildkite/testdata/build/validate/bad_pipeline1.yml similarity index 100% rename from compiler/types/yaml/testdata/build/validate/bad_pipeline1.yml rename to compiler/types/yaml/buildkite/testdata/build/validate/bad_pipeline1.yml diff --git a/compiler/types/yaml/testdata/build/validate/bad_version.yml b/compiler/types/yaml/buildkite/testdata/build/validate/bad_version.yml similarity index 100% rename from compiler/types/yaml/testdata/build/validate/bad_version.yml rename to compiler/types/yaml/buildkite/testdata/build/validate/bad_version.yml diff --git a/compiler/types/yaml/testdata/build/validate/step.yml b/compiler/types/yaml/buildkite/testdata/build/validate/step.yml similarity index 100% rename from compiler/types/yaml/testdata/build/validate/step.yml rename to compiler/types/yaml/buildkite/testdata/build/validate/step.yml diff --git a/compiler/types/yaml/testdata/build_anchor_stage.yml b/compiler/types/yaml/buildkite/testdata/build_anchor_stage.yml similarity index 100% rename from compiler/types/yaml/testdata/build_anchor_stage.yml rename to compiler/types/yaml/buildkite/testdata/build_anchor_stage.yml diff --git a/compiler/types/yaml/testdata/build_anchor_step.yml b/compiler/types/yaml/buildkite/testdata/build_anchor_step.yml similarity index 100% rename from compiler/types/yaml/testdata/build_anchor_step.yml rename to compiler/types/yaml/buildkite/testdata/build_anchor_step.yml diff --git a/compiler/types/yaml/testdata/build_empty_env.yml b/compiler/types/yaml/buildkite/testdata/build_empty_env.yml similarity index 100% rename from compiler/types/yaml/testdata/build_empty_env.yml rename to compiler/types/yaml/buildkite/testdata/build_empty_env.yml diff --git a/compiler/types/yaml/testdata/build_with_deploy_config.yml b/compiler/types/yaml/buildkite/testdata/build_with_deploy_config.yml similarity index 100% rename from compiler/types/yaml/testdata/build_with_deploy_config.yml rename to compiler/types/yaml/buildkite/testdata/build_with_deploy_config.yml diff --git a/compiler/types/yaml/testdata/deploy_parameter.yml b/compiler/types/yaml/buildkite/testdata/deploy_parameter.yml similarity index 100% rename from compiler/types/yaml/testdata/deploy_parameter.yml rename to compiler/types/yaml/buildkite/testdata/deploy_parameter.yml diff --git a/compiler/types/yaml/testdata/invalid.yml b/compiler/types/yaml/buildkite/testdata/invalid.yml similarity index 100% rename from compiler/types/yaml/testdata/invalid.yml rename to compiler/types/yaml/buildkite/testdata/invalid.yml diff --git a/compiler/types/yaml/testdata/merge_anchor.yml b/compiler/types/yaml/buildkite/testdata/merge_anchor.yml similarity index 100% rename from compiler/types/yaml/testdata/merge_anchor.yml rename to compiler/types/yaml/buildkite/testdata/merge_anchor.yml diff --git a/compiler/types/yaml/testdata/metadata.yml b/compiler/types/yaml/buildkite/testdata/metadata.yml similarity index 100% rename from compiler/types/yaml/testdata/metadata.yml rename to compiler/types/yaml/buildkite/testdata/metadata.yml diff --git a/compiler/types/yaml/testdata/metadata_env.yml b/compiler/types/yaml/buildkite/testdata/metadata_env.yml similarity index 100% rename from compiler/types/yaml/testdata/metadata_env.yml rename to compiler/types/yaml/buildkite/testdata/metadata_env.yml diff --git a/compiler/types/yaml/testdata/ruleset_advanced.yml b/compiler/types/yaml/buildkite/testdata/ruleset_advanced.yml similarity index 100% rename from compiler/types/yaml/testdata/ruleset_advanced.yml rename to compiler/types/yaml/buildkite/testdata/ruleset_advanced.yml diff --git a/compiler/types/yaml/testdata/ruleset_regex.yml b/compiler/types/yaml/buildkite/testdata/ruleset_regex.yml similarity index 100% rename from compiler/types/yaml/testdata/ruleset_regex.yml rename to compiler/types/yaml/buildkite/testdata/ruleset_regex.yml diff --git a/compiler/types/yaml/testdata/ruleset_simple.yml b/compiler/types/yaml/buildkite/testdata/ruleset_simple.yml similarity index 100% rename from compiler/types/yaml/testdata/ruleset_simple.yml rename to compiler/types/yaml/buildkite/testdata/ruleset_simple.yml diff --git a/compiler/types/yaml/testdata/secret.yml b/compiler/types/yaml/buildkite/testdata/secret.yml similarity index 100% rename from compiler/types/yaml/testdata/secret.yml rename to compiler/types/yaml/buildkite/testdata/secret.yml diff --git a/compiler/types/yaml/testdata/secret/validate/no_name.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/no_name.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/no_name.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/no_name.yml diff --git a/compiler/types/yaml/testdata/secret/validate/org.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/org.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/org.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/org.yml diff --git a/compiler/types/yaml/testdata/secret/validate/org_bad_engine.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/org_bad_engine.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/org_bad_engine.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/org_bad_engine.yml diff --git a/compiler/types/yaml/testdata/secret/validate/org_bad_key.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/org_bad_key.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/org_bad_key.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/org_bad_key.yml diff --git a/compiler/types/yaml/testdata/secret/validate/plugin.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/plugin.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/plugin.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/plugin.yml diff --git a/compiler/types/yaml/testdata/secret/validate/plugin_bad_image.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/plugin_bad_image.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/plugin_bad_image.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/plugin_bad_image.yml diff --git a/compiler/types/yaml/testdata/secret/validate/plugin_bad_name.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/plugin_bad_name.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/plugin_bad_name.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/plugin_bad_name.yml diff --git a/compiler/types/yaml/testdata/secret/validate/repo.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/repo.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/repo.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/repo.yml diff --git a/compiler/types/yaml/testdata/secret/validate/repo_bad_engine.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/repo_bad_engine.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/repo_bad_engine.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/repo_bad_engine.yml diff --git a/compiler/types/yaml/testdata/secret/validate/repo_bad_key.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/repo_bad_key.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/repo_bad_key.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/repo_bad_key.yml diff --git a/compiler/types/yaml/testdata/secret/validate/shared.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/shared.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/shared.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/shared.yml diff --git a/compiler/types/yaml/testdata/secret/validate/shared_bad_engine.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/shared_bad_engine.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/shared_bad_engine.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/shared_bad_engine.yml diff --git a/compiler/types/yaml/testdata/secret/validate/shared_bad_key.yml b/compiler/types/yaml/buildkite/testdata/secret/validate/shared_bad_key.yml similarity index 100% rename from compiler/types/yaml/testdata/secret/validate/shared_bad_key.yml rename to compiler/types/yaml/buildkite/testdata/secret/validate/shared_bad_key.yml diff --git a/compiler/types/yaml/testdata/service.yml b/compiler/types/yaml/buildkite/testdata/service.yml similarity index 100% rename from compiler/types/yaml/testdata/service.yml rename to compiler/types/yaml/buildkite/testdata/service.yml diff --git a/compiler/types/yaml/testdata/service/validate/bad_image.yml b/compiler/types/yaml/buildkite/testdata/service/validate/bad_image.yml similarity index 100% rename from compiler/types/yaml/testdata/service/validate/bad_image.yml rename to compiler/types/yaml/buildkite/testdata/service/validate/bad_image.yml diff --git a/compiler/types/yaml/testdata/service/validate/minimal.yml b/compiler/types/yaml/buildkite/testdata/service/validate/minimal.yml similarity index 100% rename from compiler/types/yaml/testdata/service/validate/minimal.yml rename to compiler/types/yaml/buildkite/testdata/service/validate/minimal.yml diff --git a/compiler/types/yaml/testdata/service/validate/missing_image.yml b/compiler/types/yaml/buildkite/testdata/service/validate/missing_image.yml similarity index 100% rename from compiler/types/yaml/testdata/service/validate/missing_image.yml rename to compiler/types/yaml/buildkite/testdata/service/validate/missing_image.yml diff --git a/compiler/types/yaml/testdata/service/validate/missing_name.yml b/compiler/types/yaml/buildkite/testdata/service/validate/missing_name.yml similarity index 100% rename from compiler/types/yaml/testdata/service/validate/missing_name.yml rename to compiler/types/yaml/buildkite/testdata/service/validate/missing_name.yml diff --git a/compiler/types/yaml/testdata/service_nil.yml b/compiler/types/yaml/buildkite/testdata/service_nil.yml similarity index 100% rename from compiler/types/yaml/testdata/service_nil.yml rename to compiler/types/yaml/buildkite/testdata/service_nil.yml diff --git a/compiler/types/yaml/testdata/stage.yml b/compiler/types/yaml/buildkite/testdata/stage.yml similarity index 100% rename from compiler/types/yaml/testdata/stage.yml rename to compiler/types/yaml/buildkite/testdata/stage.yml diff --git a/compiler/types/yaml/testdata/stage/validate/bad_image.yml b/compiler/types/yaml/buildkite/testdata/stage/validate/bad_image.yml similarity index 100% rename from compiler/types/yaml/testdata/stage/validate/bad_image.yml rename to compiler/types/yaml/buildkite/testdata/stage/validate/bad_image.yml diff --git a/compiler/types/yaml/testdata/stage/validate/minimal.yml b/compiler/types/yaml/buildkite/testdata/stage/validate/minimal.yml similarity index 100% rename from compiler/types/yaml/testdata/stage/validate/minimal.yml rename to compiler/types/yaml/buildkite/testdata/stage/validate/minimal.yml diff --git a/compiler/types/yaml/testdata/stage/validate/missing.yml b/compiler/types/yaml/buildkite/testdata/stage/validate/missing.yml similarity index 100% rename from compiler/types/yaml/testdata/stage/validate/missing.yml rename to compiler/types/yaml/buildkite/testdata/stage/validate/missing.yml diff --git a/compiler/types/yaml/testdata/stage/validate/missing_image.yml b/compiler/types/yaml/buildkite/testdata/stage/validate/missing_image.yml similarity index 100% rename from compiler/types/yaml/testdata/stage/validate/missing_image.yml rename to compiler/types/yaml/buildkite/testdata/stage/validate/missing_image.yml diff --git a/compiler/types/yaml/testdata/stage/validate/missing_name.yml b/compiler/types/yaml/buildkite/testdata/stage/validate/missing_name.yml similarity index 100% rename from compiler/types/yaml/testdata/stage/validate/missing_name.yml rename to compiler/types/yaml/buildkite/testdata/stage/validate/missing_name.yml diff --git a/compiler/types/yaml/buildkite/testdata/step.yml b/compiler/types/yaml/buildkite/testdata/step.yml new file mode 100644 index 000000000..1d6d9cc93 --- /dev/null +++ b/compiler/types/yaml/buildkite/testdata/step.yml @@ -0,0 +1,46 @@ +--- +- name: install + commands: + - ./gradlew downloadDependencies + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + +- name: test + commands: + - ./gradlew check + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + +- name: build + commands: + - ./gradlew build + environment: + - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + - GRADLE_USER_HOME=.gradle + image: openjdk:latest + pull: true + +- name: docker_build + image: plugins/docker:18.09 + report_as: docker + parameters: + registry: index.docker.io + repo: github/octocat + tags: + - latest + - dev + pull: true + +- name: templated_publish + template: + name: docker_publish + vars: + registry: index.docker.io + repo: github/octocat + tags: [ latest, dev ] diff --git a/compiler/types/yaml/testdata/step/validate/bad_image.yml b/compiler/types/yaml/buildkite/testdata/step/validate/bad_image.yml similarity index 100% rename from compiler/types/yaml/testdata/step/validate/bad_image.yml rename to compiler/types/yaml/buildkite/testdata/step/validate/bad_image.yml diff --git a/compiler/types/yaml/testdata/step/validate/minimal.yml b/compiler/types/yaml/buildkite/testdata/step/validate/minimal.yml similarity index 100% rename from compiler/types/yaml/testdata/step/validate/minimal.yml rename to compiler/types/yaml/buildkite/testdata/step/validate/minimal.yml diff --git a/compiler/types/yaml/testdata/step/validate/missing.yml b/compiler/types/yaml/buildkite/testdata/step/validate/missing.yml similarity index 100% rename from compiler/types/yaml/testdata/step/validate/missing.yml rename to compiler/types/yaml/buildkite/testdata/step/validate/missing.yml diff --git a/compiler/types/yaml/testdata/step/validate/missing_image.yml b/compiler/types/yaml/buildkite/testdata/step/validate/missing_image.yml similarity index 100% rename from compiler/types/yaml/testdata/step/validate/missing_image.yml rename to compiler/types/yaml/buildkite/testdata/step/validate/missing_image.yml diff --git a/compiler/types/yaml/testdata/step/validate/missing_name.yml b/compiler/types/yaml/buildkite/testdata/step/validate/missing_name.yml similarity index 100% rename from compiler/types/yaml/testdata/step/validate/missing_name.yml rename to compiler/types/yaml/buildkite/testdata/step/validate/missing_name.yml diff --git a/compiler/types/yaml/testdata/step_malformed.yml b/compiler/types/yaml/buildkite/testdata/step_malformed.yml similarity index 100% rename from compiler/types/yaml/testdata/step_malformed.yml rename to compiler/types/yaml/buildkite/testdata/step_malformed.yml diff --git a/compiler/types/yaml/testdata/step_nil.yml b/compiler/types/yaml/buildkite/testdata/step_nil.yml similarity index 100% rename from compiler/types/yaml/testdata/step_nil.yml rename to compiler/types/yaml/buildkite/testdata/step_nil.yml diff --git a/compiler/types/yaml/testdata/step_secret_slice.yml b/compiler/types/yaml/buildkite/testdata/step_secret_slice.yml similarity index 100% rename from compiler/types/yaml/testdata/step_secret_slice.yml rename to compiler/types/yaml/buildkite/testdata/step_secret_slice.yml diff --git a/compiler/types/yaml/testdata/step_secret_slice_invalid_no_source.yml b/compiler/types/yaml/buildkite/testdata/step_secret_slice_invalid_no_source.yml similarity index 100% rename from compiler/types/yaml/testdata/step_secret_slice_invalid_no_source.yml rename to compiler/types/yaml/buildkite/testdata/step_secret_slice_invalid_no_source.yml diff --git a/compiler/types/yaml/testdata/step_secret_slice_invalid_no_target.yml b/compiler/types/yaml/buildkite/testdata/step_secret_slice_invalid_no_target.yml similarity index 100% rename from compiler/types/yaml/testdata/step_secret_slice_invalid_no_target.yml rename to compiler/types/yaml/buildkite/testdata/step_secret_slice_invalid_no_target.yml diff --git a/compiler/types/yaml/testdata/step_secret_string.yml b/compiler/types/yaml/buildkite/testdata/step_secret_string.yml similarity index 100% rename from compiler/types/yaml/testdata/step_secret_string.yml rename to compiler/types/yaml/buildkite/testdata/step_secret_string.yml diff --git a/compiler/types/yaml/testdata/template.yml b/compiler/types/yaml/buildkite/testdata/template.yml similarity index 100% rename from compiler/types/yaml/testdata/template.yml rename to compiler/types/yaml/buildkite/testdata/template.yml diff --git a/compiler/types/yaml/testdata/ulimit_colon_error.yml b/compiler/types/yaml/buildkite/testdata/ulimit_colon_error.yml similarity index 100% rename from compiler/types/yaml/testdata/ulimit_colon_error.yml rename to compiler/types/yaml/buildkite/testdata/ulimit_colon_error.yml diff --git a/compiler/types/yaml/testdata/ulimit_equal_error.yml b/compiler/types/yaml/buildkite/testdata/ulimit_equal_error.yml similarity index 100% rename from compiler/types/yaml/testdata/ulimit_equal_error.yml rename to compiler/types/yaml/buildkite/testdata/ulimit_equal_error.yml diff --git a/compiler/types/yaml/testdata/ulimit_hardlimit1_error.yml b/compiler/types/yaml/buildkite/testdata/ulimit_hardlimit1_error.yml similarity index 100% rename from compiler/types/yaml/testdata/ulimit_hardlimit1_error.yml rename to compiler/types/yaml/buildkite/testdata/ulimit_hardlimit1_error.yml diff --git a/compiler/types/yaml/testdata/ulimit_hardlimit2_error.yml b/compiler/types/yaml/buildkite/testdata/ulimit_hardlimit2_error.yml similarity index 100% rename from compiler/types/yaml/testdata/ulimit_hardlimit2_error.yml rename to compiler/types/yaml/buildkite/testdata/ulimit_hardlimit2_error.yml diff --git a/compiler/types/yaml/testdata/ulimit_slice.yml b/compiler/types/yaml/buildkite/testdata/ulimit_slice.yml similarity index 100% rename from compiler/types/yaml/testdata/ulimit_slice.yml rename to compiler/types/yaml/buildkite/testdata/ulimit_slice.yml diff --git a/compiler/types/yaml/testdata/ulimit_softlimit_error.yml b/compiler/types/yaml/buildkite/testdata/ulimit_softlimit_error.yml similarity index 100% rename from compiler/types/yaml/testdata/ulimit_softlimit_error.yml rename to compiler/types/yaml/buildkite/testdata/ulimit_softlimit_error.yml diff --git a/compiler/types/yaml/testdata/ulimit_string.yml b/compiler/types/yaml/buildkite/testdata/ulimit_string.yml similarity index 100% rename from compiler/types/yaml/testdata/ulimit_string.yml rename to compiler/types/yaml/buildkite/testdata/ulimit_string.yml diff --git a/compiler/types/yaml/testdata/volume_error.yml b/compiler/types/yaml/buildkite/testdata/volume_error.yml similarity index 100% rename from compiler/types/yaml/testdata/volume_error.yml rename to compiler/types/yaml/buildkite/testdata/volume_error.yml diff --git a/compiler/types/yaml/testdata/volume_slice.yml b/compiler/types/yaml/buildkite/testdata/volume_slice.yml similarity index 100% rename from compiler/types/yaml/testdata/volume_slice.yml rename to compiler/types/yaml/buildkite/testdata/volume_slice.yml diff --git a/compiler/types/yaml/testdata/volume_string.yml b/compiler/types/yaml/buildkite/testdata/volume_string.yml similarity index 100% rename from compiler/types/yaml/testdata/volume_string.yml rename to compiler/types/yaml/buildkite/testdata/volume_string.yml diff --git a/compiler/types/yaml/buildkite/ulimit.go b/compiler/types/yaml/buildkite/ulimit.go new file mode 100644 index 000000000..75271f293 --- /dev/null +++ b/compiler/types/yaml/buildkite/ulimit.go @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "fmt" + "strconv" + "strings" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +type ( + // UlimitSlice is the yaml representation of + // the ulimits block for a step in a pipeline. + UlimitSlice []*Ulimit + + // Ulimit is the yaml representation of a ulimit + // from the ulimits block for a step in a pipeline. + Ulimit struct { + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"required,minLength=1,description=Unique name of the user limit.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` + Soft int64 `yaml:"soft,omitempty" json:"soft,omitempty" jsonschema:"description=Set the soft limit.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` + Hard int64 `yaml:"hard,omitempty" json:"hard,omitempty" jsonschema:"description=Set the hard limit.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` + } +) + +// ToPipeline converts the UlimitSlice type +// to a pipeline UlimitSlice type. +func (u *UlimitSlice) ToPipeline() *pipeline.UlimitSlice { + // ulimit slice we want to return + ulimitSlice := new(pipeline.UlimitSlice) + + // iterate through each element in the ulimit slice + for _, ulimit := range *u { + // append the element to the pipeline ulimit slice + *ulimitSlice = append(*ulimitSlice, &pipeline.Ulimit{ + Name: ulimit.Name, + Soft: ulimit.Soft, + Hard: ulimit.Hard, + }) + } + + return ulimitSlice +} + +// UnmarshalYAML implements the Unmarshaler interface for the UlimitSlice type. +func (u *UlimitSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { + // string slice we try unmarshalling to + stringSlice := new(raw.StringSlice) + + // attempt to unmarshal as a string slice type + err := unmarshal(stringSlice) + if err == nil { + // iterate through each element in the string slice + for _, ulimit := range *stringSlice { + // split each slice element into key/value pairs + parts := strings.Split(ulimit, "=") + if len(parts) != 2 { + return fmt.Errorf("ulimit %s must contain 1 `=` (equal)", ulimit) + } + + // split each value into soft and hard limits + limitParts := strings.Split(parts[1], ":") + + switch { + case len(limitParts) == 1: + // capture value for soft and hard limit + value, err := strconv.ParseInt(limitParts[0], 10, 64) + if err != nil { + return err + } + + // append the element to the ulimit slice + *u = append(*u, &Ulimit{ + Name: parts[0], + Soft: value, + Hard: value, + }) + + continue + case len(limitParts) == 2: + // capture value for soft limit + firstValue, err := strconv.ParseInt(limitParts[0], 10, 64) + if err != nil { + return err + } + + // capture value for hard limit + secondValue, err := strconv.ParseInt(limitParts[1], 10, 64) + if err != nil { + return err + } + + // append the element to the ulimit slice + *u = append(*u, &Ulimit{ + Name: parts[0], + Soft: firstValue, + Hard: secondValue, + }) + + continue + default: + return fmt.Errorf("ulimit %s can only contain 1 `:` (colon)", ulimit) + } + } + + return nil + } + + // ulimit slice we try unmarshalling to + ulimits := new([]*Ulimit) + + // attempt to unmarshal as a ulimit slice type + err = unmarshal(ulimits) + if err != nil { + return err + } + + // iterate through each element in the volume slice + for _, ulimit := range *ulimits { + // implicitly set `hard` field if empty + if ulimit.Hard == 0 { + ulimit.Hard = ulimit.Soft + } + } + + // overwrite existing UlimitSlice + *u = UlimitSlice(*ulimits) + + return nil +} + +func (u *Ulimit) ToYAML() *yaml.Ulimit { + if u == nil { + return nil + } + + return &yaml.Ulimit{ + Name: u.Name, + Soft: u.Soft, + Hard: u.Hard, + } +} + +func (u *UlimitSlice) ToYAML() *yaml.UlimitSlice { + // ulimit slice we want to return + ulimitSlice := new(yaml.UlimitSlice) + + // iterate through each element in the ulimit slice + for _, ulimit := range *u { + // append the element to the yaml ulimit slice + *ulimitSlice = append(*ulimitSlice, ulimit.ToYAML()) + } + + return ulimitSlice +} diff --git a/compiler/types/yaml/ulimit_test.go b/compiler/types/yaml/buildkite/ulimit_test.go similarity index 99% rename from compiler/types/yaml/ulimit_test.go rename to compiler/types/yaml/buildkite/ulimit_test.go index 3a2d9fcfb..5337007fa 100644 --- a/compiler/types/yaml/ulimit_test.go +++ b/compiler/types/yaml/buildkite/ulimit_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -package yaml +package buildkite import ( "os" diff --git a/compiler/types/yaml/buildkite/volume.go b/compiler/types/yaml/buildkite/volume.go new file mode 100644 index 000000000..ee7328075 --- /dev/null +++ b/compiler/types/yaml/buildkite/volume.go @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "fmt" + "strings" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +type ( + // VolumeSlice is the yaml representation of + // the volumes block for a step in a pipeline. + VolumeSlice []*Volume + + // Volume is the yaml representation of a volume + // from a volumes block for a step in a pipeline. + Volume struct { + Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"required,minLength=1,description=Set the source directory to be mounted.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` + Destination string `yaml:"destination,omitempty" json:"destination,omitempty" jsonschema:"required,minLength=1,description=Set the destination directory for the mount in the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` + AccessMode string `yaml:"access_mode,omitempty" json:"access_mode,omitempty" jsonschema:"default=ro,description=Set the access mode for the mounted volume.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` + } +) + +// ToPipeline converts the VolumeSlice type +// to a pipeline VolumeSlice type. +func (v *VolumeSlice) ToPipeline() *pipeline.VolumeSlice { + // volume slice we want to return + volumes := new(pipeline.VolumeSlice) + + // iterate through each element in the volume slice + for _, volume := range *v { + // append the element to the pipeline volume slice + *volumes = append(*volumes, &pipeline.Volume{ + Source: volume.Source, + Destination: volume.Destination, + AccessMode: volume.AccessMode, + }) + } + + return volumes +} + +// UnmarshalYAML implements the Unmarshaler interface for the VolumeSlice type. +func (v *VolumeSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { + // string slice we try unmarshalling to + stringSlice := new(raw.StringSlice) + + // attempt to unmarshal as a string slice type + err := unmarshal(stringSlice) + if err == nil { + // iterate through each element in the string slice + for _, volume := range *stringSlice { + // split each slice element into source, destination and access mode + parts := strings.Split(volume, ":") + + switch { + case len(parts) == 1: + // append the element to the volume slice + *v = append(*v, &Volume{ + Source: parts[0], + Destination: parts[0], + AccessMode: "ro", + }) + + continue + case len(parts) == 2: + // append the element to the volume slice + *v = append(*v, &Volume{ + Source: parts[0], + Destination: parts[1], + AccessMode: "ro", + }) + + continue + case len(parts) == 3: + // append the element to the volume slice + *v = append(*v, &Volume{ + Source: parts[0], + Destination: parts[1], + AccessMode: parts[2], + }) + + continue + default: + return fmt.Errorf("volume %s must contain at least 1 but no more than 2 `:`(colons)", volume) + } + } + + return nil + } + + // volume slice we try unmarshalling to + volumes := new([]*Volume) + + // attempt to unmarshal as a volume slice type + err = unmarshal(volumes) + if err != nil { + return err + } + + // iterate through each element in the volume slice + for _, volume := range *volumes { + // implicitly set `destination` field if empty + if len(volume.Destination) == 0 { + volume.Destination = volume.Source + } + + // implicitly set `access_mode` field if empty + if len(volume.AccessMode) == 0 { + volume.AccessMode = "ro" + } + } + + // overwrite existing VolumeSlice + *v = VolumeSlice(*volumes) + + return nil +} + +func (v *Volume) ToYAML() *yaml.Volume { + if v == nil { + return nil + } + + return &yaml.Volume{ + Source: v.Source, + Destination: v.Destination, + AccessMode: v.AccessMode, + } +} + +func (v *VolumeSlice) ToYAML() *yaml.VolumeSlice { + // volume slice we want to return + volumeSlice := new(yaml.VolumeSlice) + + // iterate through each element in the volume slice + for _, volume := range *v { + // append the element to the yaml volume slice + *volumeSlice = append(*volumeSlice, volume.ToYAML()) + } + + return volumeSlice +} diff --git a/compiler/types/yaml/buildkite/volume_test.go b/compiler/types/yaml/buildkite/volume_test.go new file mode 100644 index 000000000..c2192f944 --- /dev/null +++ b/compiler/types/yaml/buildkite/volume_test.go @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "os" + "reflect" + "testing" + + "github.com/buildkite/yaml" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_VolumeSlice_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + volumes *VolumeSlice + want *pipeline.VolumeSlice + }{ + { + volumes: &VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + want: &pipeline.VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.volumes.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_VolumeSlice_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + failure bool + file string + want *VolumeSlice + }{ + { + failure: false, + file: "testdata/volume_slice.yml", + want: &VolumeSlice{ + { + Source: "/foo", + Destination: "/foo", + AccessMode: "ro", + }, + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + { + Source: "/foo", + Destination: "/foobar", + AccessMode: "ro", + }, + }, + }, + { + failure: false, + file: "testdata/volume_string.yml", + want: &VolumeSlice{ + { + Source: "/foo", + Destination: "/foo", + AccessMode: "ro", + }, + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + { + Source: "/foo", + Destination: "/foobar", + AccessMode: "ro", + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + { + failure: true, + file: "testdata/volume_error.yml", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(VolumeSlice) + + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/buildkite/worker.go b/compiler/types/yaml/buildkite/worker.go new file mode 100644 index 000000000..d7fa9e891 --- /dev/null +++ b/compiler/types/yaml/buildkite/worker.go @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +// Worker is the yaml representation of a worker +// from a worker block in a pipeline. +type Worker struct { + Flavor string `yaml:"flavor,omitempty" json:"flavor,omitempty" jsonschema:"minLength=1,description=Flavor identifier for worker.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/#the-flavor-key,example=large"` + Platform string `yaml:"platform,omitempty" json:"platform,omitempty" jsonschema:"minLength=1,description=Platform identifier for the worker.\nReference: https://go-vela.github.io/docs/reference/yaml/worker/#the-platform-key,example=kubernetes"` +} + +// ToPipeline converts the Worker type +// to a pipeline Worker type. +func (w *Worker) ToPipeline() *pipeline.Worker { + return &pipeline.Worker{ + Flavor: w.Flavor, + Platform: w.Platform, + } +} + +func (w *Worker) ToYAML() *yaml.Worker { + return &yaml.Worker{ + Flavor: w.Flavor, + Platform: w.Platform, + } +} diff --git a/compiler/types/yaml/buildkite/worker_test.go b/compiler/types/yaml/buildkite/worker_test.go new file mode 100644 index 000000000..049fd090c --- /dev/null +++ b/compiler/types/yaml/buildkite/worker_test.go @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 + +package buildkite + +import ( + "reflect" + "testing" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Worker_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + worker *Worker + want *pipeline.Worker + }{ + { + worker: &Worker{ + Flavor: "8cpu16gb", + Platform: "gcp", + }, + want: &pipeline.Worker{ + Flavor: "8cpu16gb", + Platform: "gcp", + }, + }, + } + + // run tests + for _, test := range tests { + got := test.worker.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/doc.go b/compiler/types/yaml/doc.go deleted file mode 100644 index 2a2e39cd3..000000000 --- a/compiler/types/yaml/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -// Package yaml provides the defined yaml types for Vela. -// -// Usage: -// -// import "github.com/go-vela/server/compiler/types/yaml" -package yaml diff --git a/compiler/types/yaml/build.go b/compiler/types/yaml/yaml/build.go similarity index 100% rename from compiler/types/yaml/build.go rename to compiler/types/yaml/yaml/build.go diff --git a/compiler/types/yaml/yaml/build_test.go b/compiler/types/yaml/yaml/build_test.go new file mode 100644 index 000000000..f0c284f75 --- /dev/null +++ b/compiler/types/yaml/yaml/build_test.go @@ -0,0 +1,686 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/compiler/types/raw" +) + +func TestYaml_Build_ToAPI(t *testing.T) { + build := new(api.Pipeline) + build.SetFlavor("16cpu8gb") + build.SetPlatform("gcp") + build.SetVersion("1") + build.SetExternalSecrets(true) + build.SetInternalSecrets(true) + build.SetServices(true) + build.SetStages(false) + build.SetSteps(true) + build.SetTemplates(true) + + stages := new(api.Pipeline) + stages.SetFlavor("") + stages.SetPlatform("") + stages.SetVersion("1") + stages.SetExternalSecrets(false) + stages.SetInternalSecrets(false) + stages.SetServices(false) + stages.SetStages(true) + stages.SetSteps(false) + stages.SetTemplates(false) + + steps := new(api.Pipeline) + steps.SetFlavor("") + steps.SetPlatform("") + steps.SetVersion("1") + steps.SetExternalSecrets(false) + steps.SetInternalSecrets(false) + steps.SetServices(false) + steps.SetStages(false) + steps.SetSteps(true) + steps.SetTemplates(false) + + // setup tests + tests := []struct { + name string + file string + want *api.Pipeline + }{ + { + name: "build", + file: "testdata/build.yml", + want: build, + }, + { + name: "stages", + file: "testdata/build_anchor_stage.yml", + want: stages, + }, + { + name: "steps", + file: "testdata/build_anchor_step.yml", + want: steps, + }, + } + + // run tests + for _, test := range tests { + b := new(Build) + + data, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file %s for %s: %v", test.file, test.name, err) + } + + err = yaml.Unmarshal(data, b) + if err != nil { + t.Errorf("unable to unmarshal YAML for %s: %v", test.name, err) + } + + got := b.ToPipelineAPI() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipelineAPI for %s is %v, want %v", test.name, got, test.want) + } + } +} + +func TestYaml_Build_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + file string + want *Build + }{ + { + file: "testdata/build.yml", + want: &Build{ + Version: "1", + Metadata: Metadata{ + Template: false, + Clone: nil, + Environment: []string{"steps", "services", "secrets"}, + }, + Environment: raw.StringSliceMap{ + "HELLO": "Hello, Global Message", + }, + Worker: Worker{ + Flavor: "16cpu8gb", + Platform: "gcp", + }, + Services: ServiceSlice{ + { + Ports: []string{"5432:5432"}, + Environment: raw.StringSliceMap{ + "POSTGRES_DB": "foo", + }, + Name: "postgres", + Image: "postgres:latest", + Pull: "not_present", + }, + }, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"./gradlew downloadDependencies"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "install", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:edited"}}, + Matcher: "filepath", + Operator: "and", + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + }, + { + Commands: raw.StringSlice{"./gradlew check"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "test", + Image: "openjdk:latest", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + { + Commands: raw.StringSlice{"./gradlew build"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "build", + Image: "openjdk:latest", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + { + Name: "docker_build", + Parameters: map[string]interface{}{ + "dry_run": true, + "registry": "index.docker.io", + "repo": "github/octocat", + "tags": []interface{}{"latest", "dev"}, + }, + Image: "plugins/docker:18.09", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + }, + { + Name: "docker_publish", + Parameters: map[string]interface{}{ + "registry": "index.docker.io", + "repo": "github/octocat", + "tags": []interface{}{"latest", "dev"}, + }, + Image: "plugins/docker:18.09", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Branch: []string{"main"}, Event: []string{"push"}}, + Matcher: "filepath", + Operator: "and", + }, + Secrets: StepSecretSlice{ + { + Source: "docker_username", + Target: "PLUGIN_USERNAME", + }, + { + Source: "docker_password", + Target: "PLUGIN_PASSWORD", + }, + }, + }, + }, + Secrets: SecretSlice{ + { + Name: "docker_username", + Key: "org/repo/docker/username", + Engine: "native", + Type: "repo", + Pull: "build_start", + }, + { + Name: "docker_password", + Key: "org/repo/docker/password", + Engine: "vault", + Type: "repo", + Pull: "build_start", + }, + { + Name: "docker_username", + Key: "org/docker/username", + Engine: "native", + Type: "org", + Pull: "build_start", + }, + { + Name: "docker_password", + Key: "org/docker/password", + Engine: "vault", + Type: "org", + Pull: "build_start", + }, + { + Name: "docker_username", + Key: "org/team/docker/username", + Engine: "native", + Type: "shared", + Pull: "build_start", + }, + { + Name: "docker_password", + Key: "org/team/docker/password", + Engine: "vault", + Type: "shared", + Pull: "build_start", + }, + { + Origin: Origin{ + Image: "target/vela-vault:latest", + Parameters: map[string]interface{}{ + "addr": "vault.example.com", + }, + Pull: "always", + Secrets: StepSecretSlice{ + { + Source: "docker_username", + Target: "DOCKER_USERNAME", + }, + { + Source: "docker_password", + Target: "DOCKER_PASSWORD", + }, + }, + }, + }, + }, + Templates: TemplateSlice{ + { + Name: "docker_publish", + Source: "github.com/go-vela/atlas/stable/docker_publish", + Type: "github", + }, + }, + }, + }, + { + file: "testdata/build_anchor_stage.yml", + want: &Build{ + Version: "1", + Metadata: Metadata{ + Template: false, + Clone: nil, + Environment: []string{"steps", "services", "secrets"}, + }, + Stages: StageSlice{ + { + Name: "dependencies", + Needs: []string{"clone"}, + Independent: false, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"./gradlew downloadDependencies"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "install", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + }, + }, + { + Name: "test", + Needs: []string{"dependencies", "clone"}, + Independent: false, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"./gradlew check"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "test", + Image: "openjdk:latest", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + }, + }, + { + Name: "build", + Needs: []string{"dependencies", "clone"}, + Independent: true, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"./gradlew build"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "build", + Image: "openjdk:latest", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + }, + }, + }, + }, + }, + { + file: "testdata/build_anchor_step.yml", + want: &Build{ + Version: "1", + Metadata: Metadata{ + Template: false, + Clone: nil, + Environment: []string{"steps", "services", "secrets"}, + }, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"./gradlew downloadDependencies"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "install", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + { + Commands: raw.StringSlice{"./gradlew check"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "test", + Image: "openjdk:latest", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + { + Commands: raw.StringSlice{"./gradlew build"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "build", + Image: "openjdk:latest", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + }, + }, + }, + { + file: "testdata/build_empty_env.yml", + want: &Build{ + Version: "1", + Metadata: Metadata{ + Template: false, + Clone: nil, + Environment: []string{}, + }, + Environment: raw.StringSliceMap{ + "HELLO": "Hello, Global Message", + }, + Worker: Worker{ + Flavor: "16cpu8gb", + Platform: "gcp"}, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"./gradlew downloadDependencies"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "install", + Pull: "always", + Ruleset: Ruleset{ + If: Rules{Event: []string{"push", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened"}}, + Matcher: "filepath", + Operator: "and", + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + }, + }, + }, + }, + { + file: "testdata/merge_anchor.yml", + want: &Build{ + Version: "1", + Metadata: Metadata{ + Template: false, + Clone: nil, + Environment: []string{"steps", "services", "secrets"}, + }, + Services: ServiceSlice{ + { + Name: "service-a", + Ports: []string{"5432:5432"}, + Environment: raw.StringSliceMap{ + "REGION": "dev", + }, + Image: "postgres", + Pull: "not_present", + }, + }, + Steps: StepSlice{ + { + Commands: raw.StringSlice{"echo alpha"}, + Name: "alpha", + Image: "alpine:latest", + Pull: "not_present", + Ruleset: Ruleset{ + If: Rules{ + Event: []string{"push"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + { + Commands: raw.StringSlice{"echo beta"}, + Name: "beta", + Image: "alpine:latest", + Pull: "not_present", + Ruleset: Ruleset{ + If: Rules{ + Event: []string{"push"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + { + Commands: raw.StringSlice{"echo gamma"}, + Name: "gamma", + Image: "alpine:latest", + Pull: "not_present", + Environment: raw.StringSliceMap{ + "REGION": "dev", + }, + Ruleset: Ruleset{ + If: Rules{ + Event: []string{"push"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := new(Build) + + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("Reading file for UnmarshalYAML returned err: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/deployment.go b/compiler/types/yaml/yaml/deployment.go similarity index 100% rename from compiler/types/yaml/deployment.go rename to compiler/types/yaml/yaml/deployment.go diff --git a/compiler/types/yaml/deployment_test.go b/compiler/types/yaml/yaml/deployment_test.go similarity index 100% rename from compiler/types/yaml/deployment_test.go rename to compiler/types/yaml/yaml/deployment_test.go diff --git a/compiler/types/yaml/yaml/doc.go b/compiler/types/yaml/yaml/doc.go new file mode 100644 index 000000000..30870a01c --- /dev/null +++ b/compiler/types/yaml/yaml/doc.go @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 + +// package yaml provides the defined yaml types for Vela. +// +// Usage: +// +// import "github.com/go-vela/server/compiler/types/yaml/yaml" +package yaml diff --git a/compiler/types/yaml/git.go b/compiler/types/yaml/yaml/git.go similarity index 100% rename from compiler/types/yaml/git.go rename to compiler/types/yaml/yaml/git.go diff --git a/compiler/types/yaml/git_test.go b/compiler/types/yaml/yaml/git_test.go similarity index 100% rename from compiler/types/yaml/git_test.go rename to compiler/types/yaml/yaml/git_test.go diff --git a/compiler/types/yaml/metadata.go b/compiler/types/yaml/yaml/metadata.go similarity index 100% rename from compiler/types/yaml/metadata.go rename to compiler/types/yaml/yaml/metadata.go diff --git a/compiler/types/yaml/metadata_test.go b/compiler/types/yaml/yaml/metadata_test.go similarity index 100% rename from compiler/types/yaml/metadata_test.go rename to compiler/types/yaml/yaml/metadata_test.go diff --git a/compiler/types/yaml/ruleset.go b/compiler/types/yaml/yaml/ruleset.go similarity index 100% rename from compiler/types/yaml/ruleset.go rename to compiler/types/yaml/yaml/ruleset.go diff --git a/compiler/types/yaml/yaml/ruleset_test.go b/compiler/types/yaml/yaml/ruleset_test.go new file mode 100644 index 000000000..cb4e0466f --- /dev/null +++ b/compiler/types/yaml/yaml/ruleset_test.go @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Ruleset_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + ruleset *Ruleset + want *pipeline.Ruleset + }{ + { + ruleset: &Ruleset{ + If: Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push", "pull_request:labeled"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + Label: []string{"enhancement"}, + Instance: []string{"http://localhost:8080"}, + }, + Unless: Rules{ + Branch: []string{"main"}, + Comment: []string{"real comment"}, + Event: []string{"pull_request"}, + Path: []string{"bar.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octokitty"}, + Status: []string{"failure"}, + Tag: []string{"v0.2.0"}, + Target: []string{"production"}, + Instance: []string{"http://localhost:8080"}, + }, + Matcher: "filepath", + Operator: "and", + Continue: false, + }, + want: &pipeline.Ruleset{ + If: pipeline.Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push", "pull_request:labeled"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + Label: []string{"enhancement"}, + Instance: []string{"http://localhost:8080"}, + }, + Unless: pipeline.Rules{ + Branch: []string{"main"}, + Comment: []string{"real comment"}, + Event: []string{"pull_request"}, + Path: []string{"bar.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octokitty"}, + Status: []string{"failure"}, + Tag: []string{"v0.2.0"}, + Target: []string{"production"}, + Instance: []string{"http://localhost:8080"}, + }, + Matcher: "filepath", + Operator: "and", + Continue: false, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.ruleset.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_Ruleset_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + file string + want *Ruleset + }{ + { + file: "testdata/ruleset_simple.yml", + want: &Ruleset{ + If: Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Instance: []string{"vela-server"}, + Label: []string{"bug"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + }, + Matcher: "filepath", + Operator: "and", + Continue: true, + }, + }, + { + file: "testdata/ruleset_advanced.yml", + want: &Ruleset{ + If: Rules{ + Branch: []string{"main"}, + Event: []string{"push"}, + Tag: []string{"^refs/tags/(\\d+\\.)+\\d+$"}, + }, + Unless: Rules{ + Event: []string{"deployment:created", "pull_request:opened", "pull_request:synchronize", "pull_request:reopened", "comment:created", "comment:edited", "schedule"}, + Path: []string{"foo.txt", "/foo/bar.txt"}, + }, + Matcher: "regexp", + Operator: "or", + Continue: true, + }, + }, + { + file: "testdata/ruleset_regex.yml", + want: &Ruleset{ + If: Rules{ + Branch: []string{"main"}, + Event: []string{"tag"}, + Tag: []string{"^refs/tags/(\\d+\\.)+\\d+$"}, + }, + Operator: "and", + Matcher: "regex", + }, + }, + } + + // run tests + for _, test := range tests { + got := new(Ruleset) + + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} + +func TestYaml_Rules_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + rules *Rules + want *pipeline.Rules + }{ + { + rules: &Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push", "pull_request:labeled"}, + Instance: []string{"vela-server"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + Label: []string{"enhancement"}, + }, + want: &pipeline.Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push", "pull_request:labeled"}, + Instance: []string{"vela-server"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + Label: []string{"enhancement"}, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.rules.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_Rules_UnmarshalYAML(t *testing.T) { + // setup types + var ( + b []byte + err error + ) + + // setup tests + tests := []struct { + failure bool + file string + want *Rules + }{ + { + failure: false, + file: "testdata/ruleset_simple.yml", + want: &Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Instance: []string{"vela-server"}, + Label: []string{"bug"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Sender: []string{"octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + }, + }, + { + failure: true, + file: "", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(Rules) + + if len(test.file) > 0 { + b, err = os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + } else { + b = []byte("``") + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/secret.go b/compiler/types/yaml/yaml/secret.go similarity index 100% rename from compiler/types/yaml/secret.go rename to compiler/types/yaml/yaml/secret.go diff --git a/compiler/types/yaml/yaml/secret_test.go b/compiler/types/yaml/yaml/secret_test.go new file mode 100644 index 000000000..d35b91171 --- /dev/null +++ b/compiler/types/yaml/yaml/secret_test.go @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_Origin_MergeEnv(t *testing.T) { + // setup tests + tests := []struct { + origin *Origin + environment map[string]string + failure bool + }{ + { + origin: &Origin{ + Name: "vault", + Environment: map[string]string{"FOO": "bar"}, + Image: "target/vela-vault:latest", + Parameters: map[string]interface{}{ + "addr": "vault.example.com", + "auth_method": "token", + "items": []interface{}{ + map[string]string{"source": "secret/docker", "path": "docker"}, + }, + }, + Pull: "always", + Secrets: StepSecretSlice{ + { + Source: "vault_token", + Target: "vault_token", + }, + }, + }, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + origin: &Origin{}, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + origin: nil, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + origin: &Origin{ + Name: "vault", + Environment: map[string]string{"FOO": "bar"}, + Image: "target/vela-vault:latest", + Parameters: map[string]interface{}{ + "addr": "vault.example.com", + "auth_method": "token", + "items": []interface{}{ + map[string]string{"source": "secret/docker", "path": "docker"}, + }, + }, + Pull: "always", + Secrets: StepSecretSlice{ + { + Source: "vault_token", + Target: "vault_token", + }, + }, + }, + environment: nil, + failure: true, + }, + } + + // run tests + for _, test := range tests { + err := test.origin.MergeEnv(test.environment) + + if test.failure { + if err == nil { + t.Errorf("MergeEnv should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("MergeEnv returned err: %v", err) + } + } +} + +func TestYaml_SecretSlice_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + secrets *SecretSlice + want *pipeline.SecretSlice + }{ + { + secrets: &SecretSlice{ + { + Name: "docker_username", + Key: "github/octocat/docker/username", + Engine: "native", + Type: "repo", + Origin: Origin{}, + Pull: "build_start", + }, + { + Name: "docker_username", + Key: "", + Engine: "", + Type: "", + Origin: Origin{ + Name: "vault", + Environment: map[string]string{"FOO": "bar"}, + Image: "target/vela-vault:latest", + Parameters: map[string]interface{}{ + "addr": "vault.company.com", + }, + Pull: "always", + Ruleset: Ruleset{ + If: Rules{ + Event: []string{"push"}, + }, + Operator: "and", + }, + Secrets: StepSecretSlice{ + { + Source: "foo", + Target: "foo", + }, + { + Source: "foobar", + Target: "foobar", + }, + }, + }, + Pull: "build_start", + }, + }, + want: &pipeline.SecretSlice{ + { + Name: "docker_username", + Key: "github/octocat/docker/username", + Engine: "native", + Type: "repo", + Origin: &pipeline.Container{}, + Pull: "build_start", + }, + { + Name: "docker_username", + Key: "", + Engine: "", + Type: "", + Origin: &pipeline.Container{ + Name: "vault", + Environment: map[string]string{"FOO": "bar"}, + Image: "target/vela-vault:latest", + Pull: "always", + Ruleset: pipeline.Ruleset{ + If: pipeline.Rules{ + Event: []string{"push"}, + }, + Operator: "and", + }, + Secrets: pipeline.StepSecretSlice{ + { + Source: "foo", + Target: "foo", + }, + { + Source: "foobar", + Target: "foobar", + }, + }, + }, + Pull: "build_start", + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.secrets.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_SecretSlice_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + failure bool + file string + want *SecretSlice + }{ + { + failure: false, + file: "testdata/secret.yml", + want: &SecretSlice{ + { + Name: "foo", + Key: "bar", + Engine: "native", + Type: "repo", + Pull: "build_start", + }, + { + Name: "noKey", + Key: "noKey", + Engine: "native", + Type: "repo", + Pull: "build_start", + }, + { + Name: "noType", + Key: "bar", + Engine: "native", + Type: "repo", + Pull: "build_start", + }, + { + Name: "noEngine", + Key: "bar", + Engine: "native", + Type: "repo", + Pull: "build_start", + }, + { + Name: "noKeyEngineAndType", + Key: "noKeyEngineAndType", + Engine: "native", + Type: "repo", + Pull: "build_start", + }, + { + Name: "externalSecret", + Key: "", + Engine: "", + Type: "", + Origin: Origin{ + Environment: map[string]string{"FOO": "bar"}, + Image: "target/vela-vault:latest", + Parameters: map[string]interface{}{ + "addr": "vault.company.com", + }, + Pull: "always", + Ruleset: Ruleset{ + If: Rules{ + Event: []string{"push"}, + }, + Operator: "and", + Matcher: "filepath", + }, + Secrets: StepSecretSlice{ + { + Source: "foo", + Target: "FOO", + }, + { + Source: "foobar", + Target: "FOOBAR", + }, + }, + }, + Pull: "", + }, + { + Name: "", + Key: "", + Engine: "", + Type: "", + Origin: Origin{ + Environment: map[string]string{"FOO": "bar"}, + Image: "target/vela-vault:latest", + Parameters: map[string]interface{}{ + "addr": "vault.company.com", + }, + Pull: "always", + Ruleset: Ruleset{ + If: Rules{ + Event: []string{"push"}, + }, + Operator: "and", + Matcher: "filepath", + }, + Secrets: StepSecretSlice{ + { + Source: "foo", + Target: "FOO", + }, + { + Source: "foobar", + Target: "FOOBAR", + }, + }, + }, + Pull: "", + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(SecretSlice) + + // run test + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} + +func TestYaml_StepSecretSlice_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + secrets *StepSecretSlice + want *pipeline.StepSecretSlice + }{ + { + secrets: &StepSecretSlice{ + { + Source: "docker_username", + Target: "plugin_username", + }, + }, + want: &pipeline.StepSecretSlice{ + { + Source: "docker_username", + Target: "plugin_username", + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.secrets.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_StepSecretSlice_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + failure bool + file string + want *StepSecretSlice + }{ + { + failure: false, + file: "testdata/step_secret_slice.yml", + want: &StepSecretSlice{ + { + Source: "foo", + Target: "BAR", + }, + { + Source: "hello", + Target: "WORLD", + }, + }, + }, + { + failure: false, + file: "testdata/step_secret_string.yml", + want: &StepSecretSlice{ + { + Source: "foo", + Target: "FOO", + }, + { + Source: "hello", + Target: "HELLO", + }, + }, + }, + { + failure: true, + file: "testdata/step_secret_slice_invalid_no_source.yml", + want: nil, + }, + { + failure: true, + file: "testdata/step_secret_slice_invalid_no_target.yml", + want: nil, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(StepSecretSlice) + + // run test + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/service.go b/compiler/types/yaml/yaml/service.go similarity index 100% rename from compiler/types/yaml/service.go rename to compiler/types/yaml/yaml/service.go diff --git a/compiler/types/yaml/yaml/service_test.go b/compiler/types/yaml/yaml/service_test.go new file mode 100644 index 000000000..85c432d8b --- /dev/null +++ b/compiler/types/yaml/yaml/service_test.go @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" +) + +func TestYaml_ServiceSlice_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + services *ServiceSlice + want *pipeline.ContainerSlice + }{ + { + services: &ServiceSlice{ + { + Entrypoint: []string{"/usr/local/bin/docker-entrypoint.sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "postgres:12-alpine", + Name: "postgres", + Ports: []string{"5432:5432"}, + Pull: "not_present", + }, + }, + want: &pipeline.ContainerSlice{ + { + Detach: true, + Entrypoint: []string{"/usr/local/bin/docker-entrypoint.sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "postgres:12-alpine", + Name: "postgres", + Ports: []string{"5432:5432"}, + Pull: "not_present", + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.services.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_ServiceSlice_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + failure bool + file string + want *ServiceSlice + }{ + { + failure: false, + file: "testdata/service.yml", + want: &ServiceSlice{ + { + Environment: raw.StringSliceMap{ + "POSTGRES_DB": "foo", + }, + Image: "postgres:latest", + Name: "postgres", + Ports: []string{"5432:5432"}, + Pull: "not_present", + }, + { + Environment: raw.StringSliceMap{ + "MYSQL_DATABASE": "foo", + }, + Image: "mysql:latest", + Name: "mysql", + Ports: []string{"3061:3061"}, + Pull: "not_present", + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + { + failure: true, + file: "testdata/service_nil.yml", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(ServiceSlice) + + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} + +func TestYaml_Service_MergeEnv(t *testing.T) { + // setup tests + tests := []struct { + service *Service + environment map[string]string + failure bool + }{ + { + service: &Service{ + Environment: map[string]string{"FOO": "bar"}, + Image: "postgres:latest", + Name: "postgres", + Ports: []string{"5432:5432"}, + Pull: "not_present", + }, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + service: &Service{}, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + service: nil, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + service: &Service{ + Environment: map[string]string{"FOO": "bar"}, + Image: "postgres:latest", + Name: "postgres", + Ports: []string{"5432:5432"}, + Pull: "not_present", + }, + environment: nil, + failure: true, + }, + } + + // run tests + for _, test := range tests { + err := test.service.MergeEnv(test.environment) + + if test.failure { + if err == nil { + t.Errorf("MergeEnv should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("MergeEnv returned err: %v", err) + } + } +} diff --git a/compiler/types/yaml/yaml/stage.go b/compiler/types/yaml/yaml/stage.go new file mode 100644 index 000000000..0d7a5deef --- /dev/null +++ b/compiler/types/yaml/yaml/stage.go @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "fmt" + + "github.com/invopop/jsonschema" + "gopkg.in/yaml.v3" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" +) + +type ( + // StageSlice is the yaml representation + // of the stages block for a pipeline. + StageSlice []*Stage + + // Stage is the yaml representation + // of a stage in a pipeline. + Stage struct { + Environment raw.StringSliceMap `yaml:"environment,omitempty" json:"environment,omitempty" jsonschema:"description=Provide environment variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/#the-environment-key"` + Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"minLength=1,description=Unique identifier for the stage in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/#the-name-key"` + Needs raw.StringSlice `yaml:"needs,omitempty,flow" json:"needs,omitempty" jsonschema:"description=Stages that must complete before starting the current one.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/#the-needs-key"` + Independent bool `yaml:"independent,omitempty" json:"independent,omitempty" jsonschema:"description=Stage will continue executing if other stage fails"` + Steps StepSlice `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"required,description=Sequential execution instructions for the stage.\nReference: https://go-vela.github.io/docs/reference/yaml/stages/#the-steps-key"` + } +) + +// ToPipeline converts the StageSlice type +// to a pipeline StageSlice type. +func (s *StageSlice) ToPipeline() *pipeline.StageSlice { + // stage slice we want to return + stageSlice := new(pipeline.StageSlice) + + // iterate through each element in the stage slice + for _, stage := range *s { + // append the element to the pipeline stage slice + *stageSlice = append(*stageSlice, &pipeline.Stage{ + Done: make(chan error, 1), + Environment: stage.Environment, + Name: stage.Name, + Needs: stage.Needs, + Independent: stage.Independent, + Steps: *stage.Steps.ToPipeline(), + }) + } + + return stageSlice +} + +// UnmarshalYAML implements the Unmarshaler interface for the StageSlice type. +func (s *StageSlice) UnmarshalYAML(v *yaml.Node) error { + if v.Kind != yaml.MappingNode { + return fmt.Errorf("invalid yaml: expected map node for stage") + } + + // iterate through each element in the map slice + for i := 0; i < len(v.Content); i += 2 { + key := v.Content[i] + value := v.Content[i+1] + + stage := new(Stage) + + // unmarshal value into stage + err := value.Decode(stage) + if err != nil { + return err + } + + // implicitly set stage `name` if empty + if len(stage.Name) == 0 { + stage.Name = fmt.Sprintf("%v", key.Value) + } + + // implicitly set the stage `needs` + if stage.Name != "clone" && stage.Name != "init" { + // add clone if not present + stage.Needs = func(needs []string) []string { + for _, s := range needs { + if s == "clone" { + return needs + } + } + + return append(needs, "clone") + }(stage.Needs) + } + // append stage to stage slice + *s = append(*s, stage) + } + + return nil +} + +// MarshalYAML implements the marshaler interface for the StageSlice type. +func (s StageSlice) MarshalYAML() (interface{}, error) { + output := new(yaml.Node) + output.Kind = yaml.MappingNode + + for _, inputStage := range s { + n := new(yaml.Node) + + // create new stage with existing properties + outputStage := &Stage{ + Name: inputStage.Name, + Needs: inputStage.Needs, + Independent: inputStage.Independent, + Steps: inputStage.Steps, + } + + err := n.Encode(outputStage) + if err != nil { + return nil, err + } + + // append stage to map output + output.Content = append(output.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: inputStage.Name}) + output.Content = append(output.Content, n) + } + + return output, nil +} + +// JSONSchemaExtend handles some overrides that need to be in place +// for this type for the jsonschema generation. +// +// Stages are not really a slice of stages to the user. This change +// supports the map they really are. +func (StageSlice) JSONSchemaExtend(schema *jsonschema.Schema) { + schema.AdditionalProperties = jsonschema.FalseSchema + schema.Items = nil + schema.PatternProperties = map[string]*jsonschema.Schema{ + ".*": { + Ref: "#/$defs/Stage", + }, + } + schema.Type = "object" +} + +// MergeEnv takes a list of environment variables and attempts +// to set them in the stage environment. If the environment +// variable already exists in the stage, than this will +// overwrite the existing environment variable. +func (s *Stage) MergeEnv(environment map[string]string) error { + // check if the stage is empty + if s == nil || s.Environment == nil { + // TODO: evaluate if we should error here + // + // immediately return and do nothing + // + // treated as a no-op + return nil + } + + // check if the environment provided is empty + if environment == nil { + return fmt.Errorf("empty environment provided for stage %s", s.Name) + } + + // iterate through all environment variables provided + for key, value := range environment { + // set or update the stage environment variable + s.Environment[key] = value + } + + return nil +} diff --git a/compiler/types/yaml/yaml/stage_test.go b/compiler/types/yaml/yaml/stage_test.go new file mode 100644 index 000000000..86169f373 --- /dev/null +++ b/compiler/types/yaml/yaml/stage_test.go @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + "gopkg.in/yaml.v3" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_StageSlice_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + stages *StageSlice + want *pipeline.StageSlice + }{ + { + stages: &StageSlice{ + { + Name: "echo", + Needs: []string{"clone"}, + Steps: StepSlice{ + { + Commands: []string{"echo hello"}, + Detach: false, + Entrypoint: []string{"/bin/sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "alpine:latest", + Name: "echo", + Privileged: false, + Pull: "not_present", + Ruleset: Ruleset{ + If: Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + }, + Unless: Rules{ + Branch: []string{"main"}, + Comment: []string{"real comment"}, + Event: []string{"pull_request"}, + Path: []string{"bar.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"failure"}, + Tag: []string{"v0.2.0"}, + Target: []string{"production"}, + }, + Operator: "and", + Continue: false, + }, + Secrets: StepSecretSlice{ + { + Source: "docker_username", + Target: "plugin_username", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + }, + }, + }, + }, + want: &pipeline.StageSlice{ + { + Name: "echo", + Needs: []string{"clone"}, + Steps: pipeline.ContainerSlice{ + { + Commands: []string{"echo hello"}, + Detach: false, + Entrypoint: []string{"/bin/sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "alpine:latest", + Name: "echo", + Privileged: false, + Pull: "not_present", + Ruleset: pipeline.Ruleset{ + If: pipeline.Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + }, + Unless: pipeline.Rules{ + Branch: []string{"main"}, + Comment: []string{"real comment"}, + Event: []string{"pull_request"}, + Path: []string{"bar.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"failure"}, + Tag: []string{"v0.2.0"}, + Target: []string{"production"}, + }, + Operator: "and", + Continue: false, + }, + Secrets: pipeline.StepSecretSlice{ + { + Source: "docker_username", + Target: "plugin_username", + }, + }, + Ulimits: pipeline.UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + Volumes: pipeline.VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + }, + }, + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.stages.ToPipeline() + + // WARNING: hack to compare stages + // + // Channel values can only be compared for equality. + // Two channel values are considered equal if they + // originated from the same make call meaning they + // refer to the same channel value in memory. + for i, stage := range *got { + tmp := *test.want + + tmp[i].Done = stage.Done + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_StageSlice_UnmarshalYAML(t *testing.T) { + // setup types + var ( + b []byte + err error + ) + + // setup tests + tests := []struct { + failure bool + file string + want *StageSlice + }{ + { + failure: false, + file: "testdata/stage.yml", + want: &StageSlice{ + { + Name: "dependencies", + Needs: []string{"clone"}, + Environment: map[string]string{ + "STAGE_ENV_VAR": "stage", + }, + Independent: true, + Steps: StepSlice{ + { + Commands: []string{"./gradlew downloadDependencies"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "install", + Pull: "always", + }, + }, + }, + { + Name: "test", + Needs: []string{"dependencies", "clone"}, + Environment: map[string]string{ + "STAGE_ENV_VAR": "stage", + "SECOND_STAGE_ENV": "stage2", + }, + Independent: false, + Steps: StepSlice{ + { + Commands: []string{"./gradlew check"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "test", + Image: "openjdk:latest", + Pull: "always", + }, + }, + }, + { + Name: "build", + Needs: []string{"dependencies", "clone"}, + Environment: map[string]string{ + "STAGE_ENV_VAR": "stage", + }, + Independent: false, + Steps: StepSlice{ + { + Commands: []string{"./gradlew build"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "build", + Image: "openjdk:latest", + Pull: "always", + }, + }, + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + { + failure: true, + file: "", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(StageSlice) + + if len(test.file) > 0 { + b, err = os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + } else { + b = []byte("- foo") + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("(Unmarshal mismatch: -want +got):\n%s", diff) + } + } +} + +func TestYaml_StageSlice_MarshalYAML(t *testing.T) { + // setup types + var ( + b []byte + err error + ) + + // setup tests + tests := []struct { + failure bool + file string + want *StageSlice + }{ + { + failure: false, + file: "testdata/stage.yml", + want: &StageSlice{ + { + Name: "dependencies", + Needs: []string{"clone"}, + Independent: true, + Steps: StepSlice{ + { + Commands: []string{"./gradlew downloadDependencies"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "install", + Pull: "always", + }, + }, + }, + { + Name: "test", + Needs: []string{"dependencies", "clone"}, + Independent: false, + Steps: StepSlice{ + { + Commands: []string{"./gradlew check"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "test", + Image: "openjdk:latest", + Pull: "always", + }, + }, + }, + { + Name: "build", + Needs: []string{"dependencies", "clone"}, + Independent: false, + Steps: StepSlice{ + { + Commands: []string{"./gradlew build"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "build", + Image: "openjdk:latest", + Pull: "always", + }, + }, + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + { + failure: true, + file: "", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(StageSlice) + got2 := new(StageSlice) + + if len(test.file) > 0 { + b, err = os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + } else { + b = []byte("- foo") + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + out, err := yaml.Marshal(got) + if err != nil { + t.Errorf("MarshalYAML returned err: %v", err) + } + + err = yaml.Unmarshal(out, got2) + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if diff := cmp.Diff(got2, test.want); diff != "" { + t.Errorf("(Marshal mismatch: -got +want):\n%s", diff) + } + } +} + +func TestYaml_Stage_MergeEnv(t *testing.T) { + // setup tests + tests := []struct { + stage *Stage + environment map[string]string + failure bool + }{ + { + stage: &Stage{ + Environment: map[string]string{"FOO": "bar"}, + Name: "testStage", + }, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + stage: &Stage{}, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + stage: nil, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + stage: &Stage{ + Environment: map[string]string{"FOO": "bar"}, + Name: "testStage", + }, + environment: nil, + failure: true, + }, + } + + // run tests + for _, test := range tests { + err := test.stage.MergeEnv(test.environment) + + if test.failure { + if err == nil { + t.Errorf("MergeEnv should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("MergeEnv returned err: %v", err) + } + } +} diff --git a/compiler/types/yaml/step.go b/compiler/types/yaml/yaml/step.go similarity index 100% rename from compiler/types/yaml/step.go rename to compiler/types/yaml/yaml/step.go diff --git a/compiler/types/yaml/yaml/step_test.go b/compiler/types/yaml/yaml/step_test.go new file mode 100644 index 000000000..343daf230 --- /dev/null +++ b/compiler/types/yaml/yaml/step_test.go @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/raw" +) + +func TestYaml_StepSlice_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + steps *StepSlice + want *pipeline.ContainerSlice + }{ + { + steps: &StepSlice{ + { + Commands: []string{"echo hello"}, + Detach: false, + Entrypoint: []string{"/bin/sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "alpine:latest", + Name: "echo", + Privileged: false, + Pull: "not_present", + ReportAs: "my-step", + IDRequest: "yes", + Ruleset: Ruleset{ + If: Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + }, + Unless: Rules{ + Branch: []string{"main"}, + Comment: []string{"real comment"}, + Event: []string{"pull_request"}, + Path: []string{"bar.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"failure"}, + Tag: []string{"v0.2.0"}, + Target: []string{"production"}, + }, + Operator: "and", + Continue: false, + }, + Secrets: StepSecretSlice{ + { + Source: "docker_username", + Target: "plugin_username", + }, + }, + Ulimits: UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + Volumes: VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + }, + }, + want: &pipeline.ContainerSlice{ + { + Commands: []string{"echo hello"}, + Detach: false, + Entrypoint: []string{"/bin/sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "alpine:latest", + Name: "echo", + Privileged: false, + Pull: "not_present", + ReportAs: "my-step", + IDRequest: "yes", + Ruleset: pipeline.Ruleset{ + If: pipeline.Rules{ + Branch: []string{"main"}, + Comment: []string{"test comment"}, + Event: []string{"push"}, + Path: []string{"foo.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"success"}, + Tag: []string{"v0.1.0"}, + Target: []string{"production"}, + }, + Unless: pipeline.Rules{ + Branch: []string{"main"}, + Comment: []string{"real comment"}, + Event: []string{"pull_request"}, + Path: []string{"bar.txt"}, + Repo: []string{"github/octocat"}, + Status: []string{"failure"}, + Tag: []string{"v0.2.0"}, + Target: []string{"production"}, + }, + Operator: "and", + Continue: false, + }, + Secrets: pipeline.StepSecretSlice{ + { + Source: "docker_username", + Target: "plugin_username", + }, + }, + Ulimits: pipeline.UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + Volumes: pipeline.VolumeSlice{ + { + Source: "/foo", + Destination: "/bar", + AccessMode: "ro", + }, + }, + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.steps.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_StepSlice_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + failure bool + file string + want *StepSlice + }{ + { + failure: false, + file: "testdata/step.yml", + want: &StepSlice{ + { + Commands: raw.StringSlice{"./gradlew downloadDependencies"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "install", + Image: "openjdk:latest", + Pull: "always", + }, + { + Commands: raw.StringSlice{"./gradlew check"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "test", + Image: "openjdk:latest", + Pull: "always", + }, + { + Commands: raw.StringSlice{"./gradlew build"}, + Environment: raw.StringSliceMap{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Name: "build", + Image: "openjdk:latest", + Pull: "always", + }, + { + Name: "docker_build", + Image: "plugins/docker:18.09", + Pull: "always", + ReportAs: "docker", + Parameters: map[string]interface{}{ + "registry": "index.docker.io", + "repo": "github/octocat", + "tags": []interface{}{"latest", "dev"}, + }, + }, + { + Name: "templated_publish", + Pull: "not_present", + Template: StepTemplate{ + Name: "docker_publish", + Variables: map[string]interface{}{ + "registry": "index.docker.io", + "repo": "github/octocat", + "tags": []interface{}{"latest", "dev"}, + }, + }, + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + { + failure: true, + file: "testdata/step_malformed.yml", + want: nil, + }, + { + failure: true, + file: "testdata/step_nil.yml", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(StepSlice) + + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} + +func TestYaml_Step_MergeEnv(t *testing.T) { + // setup tests + tests := []struct { + step *Step + environment map[string]string + failure bool + }{ + { + step: &Step{ + Commands: []string{"echo hello"}, + Detach: false, + Entrypoint: []string{"/bin/sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "alpine:latest", + Name: "echo", + Privileged: false, + Pull: "not_present", + }, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + step: &Step{}, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + step: nil, + environment: map[string]string{"BAR": "baz"}, + failure: false, + }, + { + step: &Step{ + Commands: []string{"echo hello"}, + Detach: false, + Entrypoint: []string{"/bin/sh"}, + Environment: map[string]string{"FOO": "bar"}, + Image: "alpine:latest", + Name: "echo", + Privileged: false, + Pull: "not_present", + }, + environment: nil, + failure: true, + }, + } + + // run tests + for _, test := range tests { + err := test.step.MergeEnv(test.environment) + + if test.failure { + if err == nil { + t.Errorf("MergeEnv should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("MergeEnv returned err: %v", err) + } + } +} diff --git a/compiler/types/yaml/template.go b/compiler/types/yaml/yaml/template.go similarity index 100% rename from compiler/types/yaml/template.go rename to compiler/types/yaml/yaml/template.go diff --git a/compiler/types/yaml/yaml/template_test.go b/compiler/types/yaml/yaml/template_test.go new file mode 100644 index 000000000..128c998a0 --- /dev/null +++ b/compiler/types/yaml/yaml/template_test.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + api "github.com/go-vela/server/api/types" +) + +func TestBuild_TemplateSlice_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + failure bool + file string + want *TemplateSlice + }{ + { + failure: false, + file: "testdata/template.yml", + want: &TemplateSlice{ + { + Name: "docker_build", + Source: "github.com/go-vela/atlas/stable/docker_create", + Type: "github", + }, + { + Name: "docker_build", + Source: "github.com/go-vela/atlas/stable/docker_build", + Format: "go", + Type: "github", + }, + { + Name: "docker_publish", + Source: "github.com/go-vela/atlas/stable/docker_publish", + Format: "starlark", + Type: "github", + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(TemplateSlice) + + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} + +func TestYAML_Template_ToAPI(t *testing.T) { + // setup types + want := new(api.Template) + want.SetName("docker_build") + want.SetSource("github.com/go-vela/atlas/stable/docker_build") + want.SetType("github") + + tmpl := &Template{ + Name: "docker_build", + Source: "github.com/go-vela/atlas/stable/docker_build", + Type: "github", + } + + // run test + got := tmpl.ToAPI() + + if !reflect.DeepEqual(got, want) { + t.Errorf("ToAPI is %v, want %v", got, want) + } +} + +func TestYAML_TemplateFromAPI(t *testing.T) { + // setup types + want := &Template{ + Name: "docker_build", + Source: "github.com/go-vela/atlas/stable/docker_build", + Type: "github", + } + + tmpl := new(api.Template) + tmpl.SetName("docker_build") + tmpl.SetSource("github.com/go-vela/atlas/stable/docker_build") + tmpl.SetType("github") + + // run test + got := TemplateFromAPI(tmpl) + + if !reflect.DeepEqual(got, want) { + t.Errorf("TemplateFromAPI is %v, want %v", got, want) + } +} diff --git a/compiler/types/yaml/yaml/testdata/build.yml b/compiler/types/yaml/yaml/testdata/build.yml new file mode 100644 index 000000000..e1a7fbc9f --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build.yml @@ -0,0 +1,144 @@ +--- +version: "1" + +environment: + HELLO: "Hello, Global Message" + +templates: + - name: docker_publish + source: github.com/go-vela/atlas/stable/docker_publish + type: github + +worker: + flavor: 16cpu8gb + platform: gcp + +services: + - name: postgres + image: postgres:latest + environment: + POSTGRES_DB: foo + ports: + - "5432:5432" + +steps: + - name: install + commands: + - ./gradlew downloadDependencies + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + ruleset: + event: [ push, pull_request:opened, pull_request:synchronize, pull_request:edited ] + volumes: [ /foo:/bar:ro ] + ulimits: [ foo=1024:2048 ] + + - name: test + commands: + - ./gradlew check + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + ruleset: + event: [ push, pull_request ] + volumes: [ /foo:/bar:ro ] + ulimits: [ foo=1024:2048 ] + + - name: build + commands: + - ./gradlew build + environment: + - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + - GRADLE_USER_HOME=.gradle + image: openjdk:latest + pull: true + ruleset: + event: [ push, pull_request ] + volumes: + - source: /foo + destination: /bar + access_mode: ro + ulimits: + - name: foo + soft: 1024 + hard: 2048 + + - name: docker_build + image: plugins/docker:18.09 + parameters: + dry_run: true + registry: index.docker.io + repo: github/octocat + tags: + - latest + - dev + pull: true + ruleset: + if: + event: [ push, pull_request ] + operator: and + + - name: docker_publish + image: plugins/docker:18.09 + parameters: + registry: index.docker.io + repo: github/octocat + tags: + - latest + - dev + pull: true + ruleset: + if: + branch: main + event: push + operator: and + secrets: + - source: docker_username + target: plugin_username + - source: docker_password + target: plugin_password + +secrets: + # Repo secrets + - name: docker_username + key: org/repo/docker/username + engine: native + type: repo + + - name: docker_password + key: org/repo/docker/password + engine: vault + type: repo + + # Org secrets + - name: docker_username + key: org/docker/username + engine: native + type: org + + - name: docker_password + key: org/docker/password + engine: vault + type: org + + # Shared secrets + - name: docker_username + key: org/team/docker/username + engine: native + type: shared + + - name: docker_password + key: org/team/docker/password + engine: vault + type: shared + + - origin: + image: target/vela-vault:latest + pull: always + parameters: + addr: vault.example.com + secrets: [ docker_username, docker_password ] diff --git a/compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline0.yml b/compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline0.yml new file mode 100644 index 000000000..8cbe12806 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline0.yml @@ -0,0 +1 @@ +version: 1 \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline1.yml b/compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline1.yml new file mode 100644 index 000000000..b1db03665 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build/validate/bad_pipeline1.yml @@ -0,0 +1,3 @@ +version: "1" +steps: +stages: \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/build/validate/bad_version.yml b/compiler/types/yaml/yaml/testdata/build/validate/bad_version.yml new file mode 100644 index 000000000..e2489f424 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build/validate/bad_version.yml @@ -0,0 +1,2 @@ +--- +steps: \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/build/validate/step.yml b/compiler/types/yaml/yaml/testdata/build/validate/step.yml new file mode 100644 index 000000000..a70942591 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build/validate/step.yml @@ -0,0 +1,47 @@ +--- +version: 1 +steps: + - name: install + commands: + - ./gradlew downloadDependencies + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + + - name: test + commands: + - ./gradlew check + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + + - name: build + commands: + - ./gradlew build + environment: + - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + - GRADLE_USER_HOME=.gradle + image: openjdk:latest + pull: true + + - name: docker_build + image: plugins/docker:18.09 + parameters: + registry: index.docker.io + repo: github/octocat + tags: + - latest + - dev + pull: true + + - name: templated_publish + template: + name: docker_publish + vars: + registry: index.docker.io + repo: github/octocat + tags: [ latest, dev ] diff --git a/compiler/types/yaml/yaml/testdata/build_anchor_stage.yml b/compiler/types/yaml/yaml/testdata/build_anchor_stage.yml new file mode 100644 index 000000000..2fc87932b --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build_anchor_stage.yml @@ -0,0 +1,57 @@ +--- +version: "1" + +metadata: + template: false + +stage-anchor: &stage-anchor + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + +stages: + dependencies: + steps: + - name: install + commands: + - ./gradlew downloadDependencies + <<: *stage-anchor + pull: true + ruleset: + event: [ push, pull_request ] + volumes: [ /foo:/bar:ro ] + ulimits: [ foo=1024:2048 ] + + test: + needs: [ dependencies ] + steps: + - name: test + commands: + - ./gradlew check + <<: *stage-anchor + pull: true + ruleset: + event: [ push, pull_request ] + volumes: [ /foo:/bar:ro ] + ulimits: [ foo=1024:2048 ] + + build: + needs: [ dependencies ] + independent: true + steps: + - name: build + commands: + - ./gradlew build + <<: *stage-anchor + pull: true + ruleset: + event: [ push, pull_request ] + volumes: + - source: /foo + destination: /bar + access_mode: ro + ulimits: + - name: foo + soft: 1024 + hard: 2048 diff --git a/compiler/types/yaml/yaml/testdata/build_anchor_step.yml b/compiler/types/yaml/yaml/testdata/build_anchor_step.yml new file mode 100644 index 000000000..ffc7abf50 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build_anchor_step.yml @@ -0,0 +1,48 @@ +--- +version: "1" + +metadata: + template: false + +step-anchor: &step-anchor + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + +steps: + - name: install + commands: + - ./gradlew downloadDependencies + <<: *step-anchor + pull: true + ruleset: + event: [ push, pull_request ] + volumes: [ /foo:/bar:ro ] + ulimits: [ foo=1024:2048 ] + + - name: test + commands: + - ./gradlew check + <<: *step-anchor + pull: true + ruleset: + event: [ push, pull_request ] + volumes: [ /foo:/bar:ro ] + ulimits: [ foo=1024:2048 ] + + - name: build + commands: + - ./gradlew build + <<: *step-anchor + pull: true + ruleset: + event: [ push, pull_request ] + volumes: + - source: /foo + destination: /bar + access_mode: ro + ulimits: + - name: foo + soft: 1024 + hard: 2048 diff --git a/compiler/types/yaml/yaml/testdata/build_empty_env.yml b/compiler/types/yaml/yaml/testdata/build_empty_env.yml new file mode 100644 index 000000000..6b4f7d063 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build_empty_env.yml @@ -0,0 +1,27 @@ +--- +version: "1" + +metadata: + template: false + environment: [] + +environment: + HELLO: "Hello, Global Message" + +worker: + flavor: 16cpu8gb + platform: gcp + +steps: + - name: install + commands: + - ./gradlew downloadDependencies + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + ruleset: + event: [ push, pull_request ] + volumes: [ /foo:/bar:ro ] + ulimits: [ foo=1024:2048 ] \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/build_with_deploy_config.yml b/compiler/types/yaml/yaml/testdata/build_with_deploy_config.yml new file mode 100644 index 000000000..61aea8b48 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/build_with_deploy_config.yml @@ -0,0 +1,36 @@ +version: "1" + +deployment: + targets: [ dev, stage, production ] + parameters: + alpha: + description: primary node name + required: true + type: string + options: + - north + - south + + beta: + description: secondary node name + required: false + type: string + options: + - east + - west + + cluster_count: + description: number of clusters to deploy + required: false + type: integer + + canary: + description: deploy with canary strategy + required: true + type: boolean + +steps: + - name: deploy plugin + image: awesome-plugin:latest + commands: + - ./deploy.sh diff --git a/compiler/types/yaml/yaml/testdata/deploy_parameter.yml b/compiler/types/yaml/yaml/testdata/deploy_parameter.yml new file mode 100644 index 000000000..89a9e0ae6 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/deploy_parameter.yml @@ -0,0 +1,11 @@ +--- +foo: + description: bar + required: true + type: string + options: + - baz +hello: + description: baz + required: false + type: string \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/invalid.yml b/compiler/types/yaml/yaml/testdata/invalid.yml new file mode 100644 index 000000000..23809fe06 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/invalid.yml @@ -0,0 +1,2 @@ +--- +foo: bar diff --git a/compiler/types/yaml/yaml/testdata/merge_anchor.yml b/compiler/types/yaml/yaml/testdata/merge_anchor.yml new file mode 100644 index 000000000..626b695f5 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/merge_anchor.yml @@ -0,0 +1,45 @@ +# test file that uses the non-standard multiple anchor keys in one step to test custom step unmarshaler + +version: "1" + +aliases: + images: + alpine: &alpine-image + image: alpine:latest + postgres: &pg-image + image: postgres + + events: + push: &event-push + ruleset: + event: + - push + env: + dev-env: &dev-environment + environment: + REGION: dev + +services: + - name: service-a + <<: [ *pg-image, *dev-environment ] + ports: + - "5432:5432" + +steps: + - name: alpha + <<: [ *alpine-image, *event-push ] + commands: + - echo alpha + + - name: beta + <<: [ *alpine-image, *event-push ] + commands: + - echo beta + + - name: gamma + <<: + - *alpine-image + - *event-push + - *dev-environment + commands: + - echo gamma \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/metadata.yml b/compiler/types/yaml/yaml/testdata/metadata.yml new file mode 100644 index 000000000..6946050b0 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/metadata.yml @@ -0,0 +1,2 @@ +--- +template: false diff --git a/compiler/types/yaml/yaml/testdata/metadata_env.yml b/compiler/types/yaml/yaml/testdata/metadata_env.yml new file mode 100644 index 000000000..0b7932a30 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/metadata_env.yml @@ -0,0 +1,3 @@ +--- +template: false +environment: [ steps ] diff --git a/compiler/types/yaml/yaml/testdata/ruleset_advanced.yml b/compiler/types/yaml/yaml/testdata/ruleset_advanced.yml new file mode 100644 index 000000000..24039e2a6 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ruleset_advanced.yml @@ -0,0 +1,15 @@ +--- +if: + branch: [ main ] + event: push + tag: "^refs/tags/(\\d+\\.)+\\d+$" +unless: + event: + - deployment + - pull_request + - comment + - schedule + path: [ foo.txt, /foo/bar.txt ] +matcher: regexp +operator: or +continue: true \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/ruleset_regex.yml b/compiler/types/yaml/yaml/testdata/ruleset_regex.yml new file mode 100644 index 000000000..eb6b1fd31 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ruleset_regex.yml @@ -0,0 +1,7 @@ +--- +if: + branch: main + event: tag + tag: [ "^refs/tags/(\\d+\\.)+\\d+$" ] + operator: and +matcher: regex diff --git a/compiler/types/yaml/yaml/testdata/ruleset_simple.yml b/compiler/types/yaml/yaml/testdata/ruleset_simple.yml new file mode 100644 index 000000000..acbe4e198 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ruleset_simple.yml @@ -0,0 +1,13 @@ +--- +branch: main +comment: "test comment" +continue: true +event: push +instance: vela-server +label: bug +path: foo.txt +repo: github/octocat +sender: octocat +status: success +tag: v0.1.0 +target: production diff --git a/compiler/types/yaml/yaml/testdata/secret.yml b/compiler/types/yaml/yaml/testdata/secret.yml new file mode 100644 index 000000000..c432eb291 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret.yml @@ -0,0 +1,39 @@ +--- +- source: foo + target: bar +- name: foo + key: bar + engine: native + type: repo + pull: build_start +- name: noKey + engine: native + type: repo +- name: noType + key: bar + engine: native +- name: noEngine + key: bar + type: repo +- name: noKeyEngineAndType +- name: externalSecret + origin: + environment: + FOO: bar + image: target/vela-vault:latest + pull: true + parameters: + addr: vault.company.com + ruleset: + event: [ push ] + secrets: [ foo, foobar ] +- origin: + environment: + FOO: bar + image: target/vela-vault:latest + pull: true + parameters: + addr: vault.company.com + ruleset: + event: [ push ] + secrets: [ foo, foobar ] diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/no_name.yml b/compiler/types/yaml/yaml/testdata/secret/validate/no_name.yml new file mode 100644 index 000000000..d674817c5 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/no_name.yml @@ -0,0 +1,11 @@ +secrets: +# Declarative repository secret definition. + - key: github/ocotocat/foob + engine: native + type: repo + - key: github/ocotocat + engine: native + type: org + - key: github/octokitties/foobar + engine: native + type: org \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/org.yml b/compiler/types/yaml/yaml/testdata/secret/validate/org.yml new file mode 100644 index 000000000..a5aad5e0d --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/org.yml @@ -0,0 +1,5 @@ +secrets: + - name: foobar + key: github/foobar + engine: native + type: org \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/org_bad_engine.yml b/compiler/types/yaml/yaml/testdata/secret/validate/org_bad_engine.yml new file mode 100644 index 000000000..18f9f8c6f --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/org_bad_engine.yml @@ -0,0 +1,9 @@ +secrets: + - name: foo + key: github/foobar + type: org + + - name: foobar + key: github/foobar + engine: badengine + type: org \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/org_bad_key.yml b/compiler/types/yaml/yaml/testdata/secret/validate/org_bad_key.yml new file mode 100644 index 000000000..bae2fcc9a --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/org_bad_key.yml @@ -0,0 +1,9 @@ +secrets: + - name: foo + engine: native + type: org + + - name: foobar + key: github + engine: native + type: org \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/plugin.yml b/compiler/types/yaml/yaml/testdata/secret/validate/plugin.yml new file mode 100644 index 000000000..180ab5da4 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/plugin.yml @@ -0,0 +1,8 @@ +secrets: + - origin: + name: vault secrets + image: target/vela/secret-vault:latest + parameters: + items: + - source: secret/vela/dev/docker + path: docker \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_image.yml b/compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_image.yml new file mode 100644 index 000000000..c21be424e --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_image.yml @@ -0,0 +1,15 @@ +secrets: + - origin: + name: vault secrets + parameters: + items: + - source: secret/vela/dev/docker + path: docker + + - origin: + name: vault secrets + image: bazel/:java:3240943c9ea3f72db51bea0a2428e83f3c5fa1312e19af017d026f9bcf70f84b + parameters: + items: + - source: secret/vela/dev/docker + path: docker \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_name.yml b/compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_name.yml new file mode 100644 index 000000000..6ebb1505f --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/plugin_bad_name.yml @@ -0,0 +1,7 @@ +secrets: + - origin: + image: target/vela/secret-vault:latest + parameters: + items: + - source: secret/vela/dev/docker + path: docker \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/repo.yml b/compiler/types/yaml/yaml/testdata/secret/validate/repo.yml new file mode 100644 index 000000000..fcad31edb --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/repo.yml @@ -0,0 +1,13 @@ +secrets: + # Implicit native secret definition. + - name: foo + + # Declarative repository secret definition. + - name: foob + key: github/ocotocat/foob + engine: native + type: repo + - name: foo_bar + key: github/ocotocat/foo/bar + engine: native + type: repo \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_engine.yml b/compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_engine.yml new file mode 100644 index 000000000..3eac1d359 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_engine.yml @@ -0,0 +1,5 @@ +secrets: + - name: foobar + key: github/ocotocat/foobar + engine: badengine + type: repo \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_key.yml b/compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_key.yml new file mode 100644 index 000000000..cf031b30c --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/repo_bad_key.yml @@ -0,0 +1,14 @@ +secrets: + - name: foo + engine: native + type: repo + + - name: bar + key: github/ocotocat + engine: native + type: repo + + - name: foobar + key: github + engine: native + type: repo \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/shared.yml b/compiler/types/yaml/yaml/testdata/secret/validate/shared.yml new file mode 100644 index 000000000..4037a97a3 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/shared.yml @@ -0,0 +1,5 @@ +secrets: + - name: foobar + key: github/ocotokitties/foo + engine: native + type: shared \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_engine.yml b/compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_engine.yml new file mode 100644 index 000000000..ca22067dd --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_engine.yml @@ -0,0 +1,9 @@ +secrets: + - name: foo + key: github/ocotokitties/foo + type: shared + + - name: foobar + key: github/ocotokitties/foo + engine: badengine + type: shared \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_key.yml b/compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_key.yml new file mode 100644 index 000000000..b80945618 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/secret/validate/shared_bad_key.yml @@ -0,0 +1,9 @@ +secrets: + - name: foo + engine: native + type: shared + + - name: foobar + key: github/ocotokitties + engine: native + type: shared \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/service.yml b/compiler/types/yaml/yaml/testdata/service.yml new file mode 100644 index 000000000..b6bc0456f --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/service.yml @@ -0,0 +1,14 @@ +--- +- name: postgres + image: postgres:latest + environment: + POSTGRES_DB: foo + ports: + - "5432:5432" + +- name: mysql + image: mysql:latest + environment: + MYSQL_DATABASE: foo + ports: + - "3061:3061" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/service/validate/bad_image.yml b/compiler/types/yaml/yaml/testdata/service/validate/bad_image.yml new file mode 100644 index 000000000..47a875e67 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/service/validate/bad_image.yml @@ -0,0 +1,3 @@ +services: + - name: badimage + image: bazel/:java:3240943c9ea3f72db51bea0a2428e83f3c5fa1312e19af017d026f9bcf70f84b \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/service/validate/minimal.yml b/compiler/types/yaml/yaml/testdata/service/validate/minimal.yml new file mode 100644 index 000000000..469d2dddf --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/service/validate/minimal.yml @@ -0,0 +1,3 @@ +services: + - name: postgres + image: postgres:latest diff --git a/compiler/types/yaml/yaml/testdata/service/validate/missing_image.yml b/compiler/types/yaml/yaml/testdata/service/validate/missing_image.yml new file mode 100644 index 000000000..3c2b76f1f --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/service/validate/missing_image.yml @@ -0,0 +1,2 @@ +services: + - name: postgres \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/service/validate/missing_name.yml b/compiler/types/yaml/yaml/testdata/service/validate/missing_name.yml new file mode 100644 index 000000000..0c1034e5d --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/service/validate/missing_name.yml @@ -0,0 +1,2 @@ +services: + - image: postgres:latest \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/service_nil.yml b/compiler/types/yaml/yaml/testdata/service_nil.yml new file mode 100644 index 000000000..41cd65e60 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/service_nil.yml @@ -0,0 +1,2 @@ +--- +- diff --git a/compiler/types/yaml/yaml/testdata/stage.yml b/compiler/types/yaml/yaml/testdata/stage.yml new file mode 100644 index 000000000..543ffdf83 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/stage.yml @@ -0,0 +1,44 @@ +--- +dependencies: + environment: + STAGE_ENV_VAR: stage + independent: true + steps: + - name: install + commands: + - ./gradlew downloadDependencies + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + +test: + needs: [ dependencies ] + environment: + STAGE_ENV_VAR: stage + SECOND_STAGE_ENV: stage2 + independent: false + steps: + - name: test + commands: + - ./gradlew check + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + +build: + needs: [ dependencies ] + environment: + STAGE_ENV_VAR: stage + steps: + - name: build + commands: + - ./gradlew build + environment: + - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + - GRADLE_USER_HOME=.gradle + image: openjdk:latest + pull: true diff --git a/compiler/types/yaml/yaml/testdata/stage/validate/bad_image.yml b/compiler/types/yaml/yaml/testdata/stage/validate/bad_image.yml new file mode 100644 index 000000000..3dbed3107 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/stage/validate/bad_image.yml @@ -0,0 +1,7 @@ +stages: + badimage: + steps: + - name: badimage + image: bazel/:java:3240943c9ea3f72db51bea0a2428e83f3c5fa1312e19af017d026f9bcf70f84b + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/stage/validate/minimal.yml b/compiler/types/yaml/yaml/testdata/stage/validate/minimal.yml new file mode 100644 index 000000000..665887c87 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/stage/validate/minimal.yml @@ -0,0 +1,7 @@ +stages: + hello: + steps: + - name: hello + image: alpine:latest + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/stage/validate/missing.yml b/compiler/types/yaml/yaml/testdata/stage/validate/missing.yml new file mode 100644 index 000000000..954fdeabe --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/stage/validate/missing.yml @@ -0,0 +1,5 @@ +stages: + hello: + steps: + - name: hello + image: alpine:latest \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/stage/validate/missing_image.yml b/compiler/types/yaml/yaml/testdata/stage/validate/missing_image.yml new file mode 100644 index 000000000..90b361748 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/stage/validate/missing_image.yml @@ -0,0 +1,6 @@ +stages: + hello: + steps: + - name: hello + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/stage/validate/missing_name.yml b/compiler/types/yaml/yaml/testdata/stage/validate/missing_name.yml new file mode 100644 index 000000000..1394939a3 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/stage/validate/missing_name.yml @@ -0,0 +1,6 @@ +stages: + hello: + steps: + - image: alpine:latest + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/testdata/step.yml b/compiler/types/yaml/yaml/testdata/step.yml similarity index 100% rename from compiler/types/yaml/testdata/step.yml rename to compiler/types/yaml/yaml/testdata/step.yml diff --git a/compiler/types/yaml/yaml/testdata/step/validate/bad_image.yml b/compiler/types/yaml/yaml/testdata/step/validate/bad_image.yml new file mode 100644 index 000000000..a97e8f12c --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step/validate/bad_image.yml @@ -0,0 +1,5 @@ +steps: + - name: badimage + image: bazel/:java:3240943c9ea3f72db51bea0a2428e83f3c5fa1312e19af017d026f9bcf70f84b + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/step/validate/minimal.yml b/compiler/types/yaml/yaml/testdata/step/validate/minimal.yml new file mode 100644 index 000000000..da2283d4b --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step/validate/minimal.yml @@ -0,0 +1,5 @@ +steps: + - name: hello + image: alpine:latest + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/step/validate/missing.yml b/compiler/types/yaml/yaml/testdata/step/validate/missing.yml new file mode 100644 index 000000000..0aa30db52 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step/validate/missing.yml @@ -0,0 +1,3 @@ +steps: + - name: hello + image: alpine:latest \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/step/validate/missing_image.yml b/compiler/types/yaml/yaml/testdata/step/validate/missing_image.yml new file mode 100644 index 000000000..b36acf70d --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step/validate/missing_image.yml @@ -0,0 +1,4 @@ +steps: + - name: hello + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/step/validate/missing_name.yml b/compiler/types/yaml/yaml/testdata/step/validate/missing_name.yml new file mode 100644 index 000000000..228ac30ec --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step/validate/missing_name.yml @@ -0,0 +1,4 @@ +steps: + - image: alpine:latest + commands: + - echo "hello vela" \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/step_malformed.yml b/compiler/types/yaml/yaml/testdata/step_malformed.yml new file mode 100644 index 000000000..5d70e5a4f --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step_malformed.yml @@ -0,0 +1,4 @@ +--- +- name: Testing + environment: + - 'This: Shouldnt Panic' diff --git a/compiler/types/yaml/yaml/testdata/step_nil.yml b/compiler/types/yaml/yaml/testdata/step_nil.yml new file mode 100644 index 000000000..41cd65e60 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step_nil.yml @@ -0,0 +1,2 @@ +--- +- diff --git a/compiler/types/yaml/yaml/testdata/step_secret_slice.yml b/compiler/types/yaml/yaml/testdata/step_secret_slice.yml new file mode 100644 index 000000000..64bc68b7f --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step_secret_slice.yml @@ -0,0 +1,5 @@ +--- +- source: foo + target: bar +- source: hello + target: world diff --git a/compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_source.yml b/compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_source.yml new file mode 100644 index 000000000..1f7e6fc3e --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_source.yml @@ -0,0 +1,2 @@ +--- +- target: foo \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_target.yml b/compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_target.yml new file mode 100644 index 000000000..3e0e29b1c --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step_secret_slice_invalid_no_target.yml @@ -0,0 +1,2 @@ +--- +- source: foo \ No newline at end of file diff --git a/compiler/types/yaml/yaml/testdata/step_secret_string.yml b/compiler/types/yaml/yaml/testdata/step_secret_string.yml new file mode 100644 index 000000000..930977980 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/step_secret_string.yml @@ -0,0 +1,2 @@ +--- +[ foo, hello ] diff --git a/compiler/types/yaml/yaml/testdata/template.yml b/compiler/types/yaml/yaml/testdata/template.yml new file mode 100644 index 000000000..6d5615e01 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/template.yml @@ -0,0 +1,12 @@ +--- +- name: docker_build + source: github.com/go-vela/atlas/stable/docker_create + type: github +- name: docker_build + source: github.com/go-vela/atlas/stable/docker_build + format: go + type: github +- name: docker_publish + source: github.com/go-vela/atlas/stable/docker_publish + format: starlark + type: github diff --git a/compiler/types/yaml/yaml/testdata/ulimit_colon_error.yml b/compiler/types/yaml/yaml/testdata/ulimit_colon_error.yml new file mode 100644 index 000000000..3e948cb08 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ulimit_colon_error.yml @@ -0,0 +1,2 @@ +--- +[ foo=bar:1024:2048 ] diff --git a/compiler/types/yaml/yaml/testdata/ulimit_equal_error.yml b/compiler/types/yaml/yaml/testdata/ulimit_equal_error.yml new file mode 100644 index 000000000..f72b3b461 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ulimit_equal_error.yml @@ -0,0 +1,2 @@ +--- +[ foo=1024=2048 ] diff --git a/compiler/types/yaml/yaml/testdata/ulimit_hardlimit1_error.yml b/compiler/types/yaml/yaml/testdata/ulimit_hardlimit1_error.yml new file mode 100644 index 000000000..1472c22b7 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ulimit_hardlimit1_error.yml @@ -0,0 +1,2 @@ +--- +[ foo=bar:1024 ] diff --git a/compiler/types/yaml/yaml/testdata/ulimit_hardlimit2_error.yml b/compiler/types/yaml/yaml/testdata/ulimit_hardlimit2_error.yml new file mode 100644 index 000000000..4569bc3ad --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ulimit_hardlimit2_error.yml @@ -0,0 +1,2 @@ +--- +[ foo=1024:bar ] diff --git a/compiler/types/yaml/yaml/testdata/ulimit_slice.yml b/compiler/types/yaml/yaml/testdata/ulimit_slice.yml new file mode 100644 index 000000000..9ee862c06 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ulimit_slice.yml @@ -0,0 +1,6 @@ +--- +- name: foo + soft: 1024 +- name: bar + soft: 1024 + hard: 2048 diff --git a/compiler/types/yaml/yaml/testdata/ulimit_softlimit_error.yml b/compiler/types/yaml/yaml/testdata/ulimit_softlimit_error.yml new file mode 100644 index 000000000..63f68f1c4 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ulimit_softlimit_error.yml @@ -0,0 +1,2 @@ +--- +[ foo=bar ] diff --git a/compiler/types/yaml/yaml/testdata/ulimit_string.yml b/compiler/types/yaml/yaml/testdata/ulimit_string.yml new file mode 100644 index 000000000..59669af36 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/ulimit_string.yml @@ -0,0 +1,2 @@ +--- +[ foo=1024, bar=1024:2048 ] diff --git a/compiler/types/yaml/yaml/testdata/volume_error.yml b/compiler/types/yaml/yaml/testdata/volume_error.yml new file mode 100644 index 000000000..8c36e5057 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/volume_error.yml @@ -0,0 +1,2 @@ +--- +[ /foo:/bar:/foo:bar ] diff --git a/compiler/types/yaml/yaml/testdata/volume_slice.yml b/compiler/types/yaml/yaml/testdata/volume_slice.yml new file mode 100644 index 000000000..fbad0133b --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/volume_slice.yml @@ -0,0 +1,7 @@ +--- +- source: /foo +- source: /foo + destination: /bar +- source: /foo + destination: /foobar + access_mode: ro diff --git a/compiler/types/yaml/yaml/testdata/volume_string.yml b/compiler/types/yaml/yaml/testdata/volume_string.yml new file mode 100644 index 000000000..a596a9116 --- /dev/null +++ b/compiler/types/yaml/yaml/testdata/volume_string.yml @@ -0,0 +1,2 @@ +--- +[ /foo, /foo:/bar, /foo:/foobar:ro ] diff --git a/compiler/types/yaml/ulimit.go b/compiler/types/yaml/yaml/ulimit.go similarity index 100% rename from compiler/types/yaml/ulimit.go rename to compiler/types/yaml/yaml/ulimit.go diff --git a/compiler/types/yaml/yaml/ulimit_test.go b/compiler/types/yaml/yaml/ulimit_test.go new file mode 100644 index 000000000..a8da414ee --- /dev/null +++ b/compiler/types/yaml/yaml/ulimit_test.go @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "os" + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + "github.com/go-vela/server/compiler/types/pipeline" +) + +func TestYaml_UlimitSlice_ToPipeline(t *testing.T) { + // setup tests + tests := []struct { + ulimits *UlimitSlice + want *pipeline.UlimitSlice + }{ + { + ulimits: &UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + want: &pipeline.UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 2048, + }, + }, + }, + } + + // run tests + for _, test := range tests { + got := test.ulimits.ToPipeline() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ToPipeline is %v, want %v", got, test.want) + } + } +} + +func TestYaml_UlimitSlice_UnmarshalYAML(t *testing.T) { + // setup tests + tests := []struct { + failure bool + file string + want *UlimitSlice + }{ + { + failure: false, + file: "testdata/ulimit_slice.yml", + want: &UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 1024, + }, + { + Name: "bar", + Soft: 1024, + Hard: 2048, + }, + }, + }, + { + failure: false, + file: "testdata/ulimit_string.yml", + want: &UlimitSlice{ + { + Name: "foo", + Soft: 1024, + Hard: 1024, + }, + { + Name: "bar", + Soft: 1024, + Hard: 2048, + }, + }, + }, + { + failure: true, + file: "testdata/invalid.yml", + want: nil, + }, + { + failure: true, + file: "testdata/ulimit_equal_error.yml", + want: nil, + }, + { + failure: true, + file: "testdata/ulimit_colon_error.yml", + want: nil, + }, + { + failure: true, + file: "testdata/ulimit_softlimit_error.yml", + want: nil, + }, + { + failure: true, + file: "testdata/ulimit_hardlimit1_error.yml", + want: nil, + }, + { + failure: true, + file: "testdata/ulimit_hardlimit2_error.yml", + want: nil, + }, + } + + // run tests + for _, test := range tests { + got := new(UlimitSlice) + + b, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + err = yaml.Unmarshal(b, got) + + if test.failure { + if err == nil { + t.Errorf("UnmarshalYAML should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("UnmarshalYAML returned err: %v", err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("UnmarshalYAML is %v, want %v", got, test.want) + } + } +} diff --git a/compiler/types/yaml/volume.go b/compiler/types/yaml/yaml/volume.go similarity index 100% rename from compiler/types/yaml/volume.go rename to compiler/types/yaml/yaml/volume.go diff --git a/compiler/types/yaml/volume_test.go b/compiler/types/yaml/yaml/volume_test.go similarity index 100% rename from compiler/types/yaml/volume_test.go rename to compiler/types/yaml/yaml/volume_test.go diff --git a/compiler/types/yaml/worker.go b/compiler/types/yaml/yaml/worker.go similarity index 100% rename from compiler/types/yaml/worker.go rename to compiler/types/yaml/yaml/worker.go diff --git a/compiler/types/yaml/worker_test.go b/compiler/types/yaml/yaml/worker_test.go similarity index 100% rename from compiler/types/yaml/worker_test.go rename to compiler/types/yaml/yaml/worker_test.go diff --git a/go.mod b/go.mod index 290078d95..02d450edb 100644 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( golang.org/x/oauth2 v0.23.0 golang.org/x/sync v0.8.0 golang.org/x/time v0.6.0 + gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/postgres v1.5.9 gorm.io/driver/sqlite v1.5.6 gorm.io/gorm v1.25.12 @@ -148,7 +149,6 @@ require ( google.golang.org/grpc v1.66.1 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect ) diff --git a/internal/testdata/buildkite.yml b/internal/testdata/buildkite.yml new file mode 100644 index 000000000..1a74a344b --- /dev/null +++ b/internal/testdata/buildkite.yml @@ -0,0 +1,18 @@ +version: "legacy" + +aliases: + images: + alpine: &alpine-image + image: alpine:latest + + env: + dev-env: &dev-environment + environment: + REGION: dev + +steps: + - name: example + <<: *alpine-image + <<: *dev-environment + commands: + - echo $REGION \ No newline at end of file diff --git a/internal/testdata/go-yaml.yml b/internal/testdata/go-yaml.yml new file mode 100644 index 000000000..7a9d284f5 --- /dev/null +++ b/internal/testdata/go-yaml.yml @@ -0,0 +1,19 @@ +version: "1" + +aliases: + images: + alpine: &alpine-image + image: alpine:latest + + env: + dev-env: &dev-environment + environment: + REGION: dev + +steps: + - name: example + <<: + - *alpine-image + - *dev-environment + commands: + - echo $REGION diff --git a/internal/testdata/invalid.yml b/internal/testdata/invalid.yml new file mode 100644 index 000000000..a1cde504a --- /dev/null +++ b/internal/testdata/invalid.yml @@ -0,0 +1,2 @@ +- sliceNodeA +- sliceNodeB \ No newline at end of file diff --git a/internal/testdata/no_version.yml b/internal/testdata/no_version.yml new file mode 100644 index 000000000..5b59dea2a --- /dev/null +++ b/internal/testdata/no_version.yml @@ -0,0 +1,17 @@ +aliases: + images: + alpine: &alpine-image + image: alpine:latest + + env: + dev-env: &dev-environment + environment: + REGION: dev + +steps: + - name: example + <<: + - *alpine-image + - *dev-environment + commands: + - echo $REGION \ No newline at end of file diff --git a/mock/server/pipeline.go b/mock/server/pipeline.go index f7eabde04..f4d65c596 100644 --- a/mock/server/pipeline.go +++ b/mock/server/pipeline.go @@ -8,11 +8,11 @@ import ( "net/http" "strings" - yml "github.com/buildkite/yaml" "github.com/gin-gonic/gin" + yml "gopkg.in/yaml.v3" api "github.com/go-vela/server/api/types" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" ) const ( diff --git a/schema/pipeline.go b/schema/pipeline.go index 0daf45115..165d77d14 100644 --- a/schema/pipeline.go +++ b/schema/pipeline.go @@ -16,7 +16,7 @@ import ( "github.com/invopop/jsonschema" - types "github.com/go-vela/server/compiler/types/yaml" + types "github.com/go-vela/server/compiler/types/yaml/yaml" ) // NewPipelineSchema generates the JSON schema object for a Vela pipeline configuration. diff --git a/scm/github/repo.go b/scm/github/repo.go index d1648caa6..060876e48 100644 --- a/scm/github/repo.go +++ b/scm/github/repo.go @@ -14,7 +14,7 @@ import ( "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" "github.com/go-vela/server/database" ) diff --git a/scm/github/repo_test.go b/scm/github/repo_test.go index 036639674..1a86f089a 100644 --- a/scm/github/repo_test.go +++ b/scm/github/repo_test.go @@ -17,7 +17,7 @@ import ( "github.com/google/go-github/v65/github" api "github.com/go-vela/server/api/types" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" ) diff --git a/scm/service.go b/scm/service.go index 7d1d12ec6..191b3d880 100644 --- a/scm/service.go +++ b/scm/service.go @@ -7,7 +7,7 @@ import ( "net/http" api "github.com/go-vela/server/api/types" - "github.com/go-vela/server/compiler/types/yaml" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/database" "github.com/go-vela/server/internal" ) From 3156794ce0f85abb3dab93c66653b6d186dab4e7 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Fri, 17 Jan 2025 12:17:31 -0600 Subject: [PATCH 18/41] resolve conflicts --- go.mod | 38 ++++++++++++++++++++++++++-------- go.sum | 64 +++++----------------------------------------------------- 2 files changed, 35 insertions(+), 67 deletions(-) diff --git a/go.mod b/go.mod index 02d450edb..6fbf43bb0 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,11 @@ require ( github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/distribution/reference v0.6.0 github.com/drone/envsubst v1.0.3 + github.com/dustin/go-humanize v1.0.1 github.com/ghodss/yaml v1.0.0 github.com/gin-gonic/gin v1.10.0 github.com/go-playground/assert/v2 v2.2.0 + github.com/go-vela/archiver/v3 v3.4.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v65 v65.0.0 @@ -25,15 +27,18 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/vault/api v1.15.0 + github.com/invopop/jsonschema v0.13.0 github.com/joho/godotenv v1.5.1 github.com/lestrrat-go/jwx/v2 v2.1.1 github.com/lib/pq v1.10.9 github.com/microcosm-cc/bluemonday v1.0.27 + github.com/minio/minio-go/v7 v7.0.83 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.4 github.com/redis/go-redis/v9 v9.6.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.11.0 + github.com/stretchr/testify v1.9.0 github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 github.com/urfave/cli/v2 v2.27.4 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.55.0 @@ -45,9 +50,9 @@ require ( go.opentelemetry.io/otel/sdk v1.30.0 go.opentelemetry.io/otel/trace v1.30.0 go.starlark.net v0.0.0-20240925182052-1207426daebd - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.31.0 golang.org/x/oauth2 v0.23.0 - golang.org/x/sync v0.8.0 + golang.org/x/sync v0.10.0 golang.org/x/time v0.6.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/postgres v1.5.9 @@ -62,8 +67,11 @@ require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect + github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/sonic v1.12.2 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -71,20 +79,24 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // 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.22.1 // indirect - github.com/goccy/go-json v0.10.3 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.2 // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -105,16 +117,19 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -122,11 +137,15 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nwaples/rardecode v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pierrec/lz4/v4 v4.1.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/segmentio/asm v1.2.0 // indirect @@ -134,16 +153,19 @@ require ( github.com/spf13/cast v1.7.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/ulikunitz/xz v0.5.9 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/arch v0.10.0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/grpc v1.66.1 // indirect diff --git a/go.sum b/go.sum index 18a403659..fd4582b91 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,6 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGn github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE= github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= -github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= -github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= @@ -62,7 +60,6 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -75,17 +72,11 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= -github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= -github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= -github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -98,8 +89,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= -github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -117,25 +106,17 @@ github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27 github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-vela/archiver v3.1.1+incompatible h1:xTTMMwKyHwDgNFn+c1XsKHrHFg6UyWmK4oSceSduH7A= -github.com/go-vela/archiver v3.1.1+incompatible/go.mod h1:pYn/vJ47tuVltHdnA8YrDWOZ6yix4k0ZSKUBbWmkAFM= -github.com/go-vela/archiver/v3 v3.4.0 h1:c6GQRNTzr4Pn8HaxjzslIEiN89+kgZB4hLkXuBCOczI= -github.com/go-vela/archiver/v3 v3.4.0/go.mod h1:1HbXVOHBXsfzwSog3x7T6ZU9BUv+VEWnuaPLZS/v0/8= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= -github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v65 v65.0.0 h1:pQ7BmO3DZivvFk92geC0jB0q2m3gyn8vnYPgV7GSLhQ= @@ -207,25 +188,14 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= -github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -260,10 +230,6 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o github.com/mattn/go-sqlite3 v1.14.22/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/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.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA= -github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 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= @@ -282,16 +248,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= -github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 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/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= -github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -308,11 +268,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -343,9 +300,6 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= -github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 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= @@ -356,8 +310,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ 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= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -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/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -395,9 +347,8 @@ golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 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/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -405,9 +356,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -423,14 +373,12 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -450,10 +398,8 @@ google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= From 57f8f9a745082340436264ab73af9fbc6655f7c3 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Fri, 17 Jan 2025 14:49:23 -0600 Subject: [PATCH 19/41] rebase --- compiler/types/yaml/buildkite/stage.go | 1 - compiler/types/yaml/buildkite/step.go | 1 + compiler/types/yaml/buildkite/test_report.go | 40 +++++++++ compiler/types/yaml/{ => yaml}/test_report.go | 0 go.sum | 86 +++++++++++++++---- 5 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 compiler/types/yaml/buildkite/test_report.go rename compiler/types/yaml/{ => yaml}/test_report.go (100%) diff --git a/compiler/types/yaml/buildkite/stage.go b/compiler/types/yaml/buildkite/stage.go index c7909d2f7..de5e775cc 100644 --- a/compiler/types/yaml/buildkite/stage.go +++ b/compiler/types/yaml/buildkite/stage.go @@ -4,7 +4,6 @@ package buildkite import ( "fmt" - bkYaml "github.com/buildkite/yaml" "github.com/invopop/jsonschema" diff --git a/compiler/types/yaml/buildkite/step.go b/compiler/types/yaml/buildkite/step.go index 4b36b93fd..d03004928 100644 --- a/compiler/types/yaml/buildkite/step.go +++ b/compiler/types/yaml/buildkite/step.go @@ -24,6 +24,7 @@ type ( Commands raw.StringSlice `yaml:"commands,omitempty" json:"commands,omitempty" jsonschema:"description=Execution instructions to run inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-commands-key"` Entrypoint raw.StringSlice `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty" jsonschema:"description=Command to execute inside the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-entrypoint-key"` Secrets StepSecretSlice `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"description=Sensitive variables injected into the container environment.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-secrets-key"` + TestReport TestReport `yaml:"test_report,omitempty" json:"test_report,omitempty" jsonschema:"description=Test report configuration for the step.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-test_report-key"` Template StepTemplate `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"oneof_required=template,description=Name of template to expand in the pipeline.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-template-key"` Ulimits UlimitSlice `yaml:"ulimits,omitempty" json:"ulimits,omitempty" jsonschema:"description=Set the user limits for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-ulimits-key"` Volumes VolumeSlice `yaml:"volumes,omitempty" json:"volumes,omitempty" jsonschema:"description=Mount volumes for the container.\nReference: https://go-vela.github.io/docs/reference/yaml/steps/#the-volume-key"` diff --git a/compiler/types/yaml/buildkite/test_report.go b/compiler/types/yaml/buildkite/test_report.go new file mode 100644 index 000000000..7727e8475 --- /dev/null +++ b/compiler/types/yaml/buildkite/test_report.go @@ -0,0 +1,40 @@ +package buildkite + +import "github.com/go-vela/server/compiler/types/pipeline" + +// TestReport represents the structure for test report configuration. +type TestReport struct { + Results []string `yaml:"results,omitempty" json:"results,omitempty"` + Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` +} + +// ToPipeline converts the TestReport type +// to a pipeline TestReport type. +func (t *TestReport) ToPipeline() *pipeline.TestReport { + return &pipeline.TestReport{ + Results: t.Results, + Attachments: t.Attachments, + } +} + +// UnmarshalYAML implements the Unmarshaler interface for the TestReport type. +func (t *TestReport) UnmarshalYAML(unmarshal func(interface{}) error) error { + // test report we try unmarshalling to + testReport := new(struct { + Results []string `yaml:"results,omitempty" json:"results,omitempty"` + Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` + }) + + // attempt to unmarshal test report type + err := unmarshal(testReport) + if err != nil { + return err + } + + // set the results field + t.Results = testReport.Results + // set the attachments field + t.Attachments = testReport.Attachments + + return nil +} diff --git a/compiler/types/yaml/test_report.go b/compiler/types/yaml/yaml/test_report.go similarity index 100% rename from compiler/types/yaml/test_report.go rename to compiler/types/yaml/yaml/test_report.go diff --git a/go.sum b/go.sum index fd4582b91..023f6d8c3 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGn github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE= github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= +github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= @@ -60,6 +62,7 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -72,11 +75,17 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= +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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -89,6 +98,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -106,17 +117,25 @@ github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27 github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= -github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/go-vela/archiver v3.1.1+incompatible h1:xTTMMwKyHwDgNFn+c1XsKHrHFg6UyWmK4oSceSduH7A= +github.com/go-vela/archiver v3.1.1+incompatible/go.mod h1:pYn/vJ47tuVltHdnA8YrDWOZ6yix4k0ZSKUBbWmkAFM= +github.com/go-vela/archiver/v3 v3.4.0 h1:c6GQRNTzr4Pn8HaxjzslIEiN89+kgZB4hLkXuBCOczI= +github.com/go-vela/archiver/v3 v3.4.0/go.mod h1:1HbXVOHBXsfzwSog3x7T6ZU9BUv+VEWnuaPLZS/v0/8= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v65 v65.0.0 h1:pQ7BmO3DZivvFk92geC0jB0q2m3gyn8vnYPgV7GSLhQ= @@ -162,8 +181,8 @@ github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/ github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= -github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= +github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= +github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -188,14 +207,24 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -230,6 +259,10 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o github.com/mattn/go-sqlite3 v1.14.22/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/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.83 h1:W4Kokksvlz3OKf3OqIlzDNKd4MERlC2oN8YptwJ0+GA= +github.com/minio/minio-go/v7 v7.0.83/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 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= @@ -248,10 +281,16 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= +github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 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/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= +github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -268,8 +307,11 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -300,6 +342,9 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= +github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 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= @@ -310,6 +355,8 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ 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= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +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/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -347,8 +394,8 @@ golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 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/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -356,29 +403,28 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -398,8 +444,10 @@ google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= From 09da5dfd898e2c6634cb4d4009c19ba0113f3a4c Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:23:18 -0600 Subject: [PATCH 20/41] enhance(yaml): allow for users to set version legacy for buildkite (#1230) Co-authored-by: David May <49894298+wass3rw3rk@users.noreply.github.com> --- compiler/native/compile_test.go | 197 ++++++++++++++++++ compiler/native/expand_test.go | 4 +- compiler/native/parse.go | 13 +- compiler/native/parse_test.go | 78 +++++++ compiler/native/testdata/stages_merged.yml | 38 ++++ .../native/testdata/steps_merge_anchor.yml | 46 ++++ compiler/template/native/render.go | 13 +- compiler/template/starlark/render.go | 15 +- internal/yaml.go | 63 ++++++ internal/yaml_test.go | 88 ++++++++ 10 files changed, 526 insertions(+), 29 deletions(-) create mode 100644 compiler/native/testdata/stages_merged.yml create mode 100644 compiler/native/testdata/steps_merge_anchor.yml create mode 100644 internal/yaml.go create mode 100644 internal/yaml_test.go diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index e2134c9db..3767760c2 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -1981,6 +1981,203 @@ func TestNative_Compile_StepsandStages(t *testing.T) { } } +func TestNative_Compile_LegacyMergeAnchor(t *testing.T) { + // setup types + set := flag.NewFlagSet("test", 0) + set.String("clone-image", defaultCloneImage, "doc") + c := cli.NewContext(nil, set, nil) + name := "foo" + author := "author" + event := "push" + number := 1 + + m := &internal.Metadata{ + Database: &internal.Database{ + Driver: "foo", + Host: "foo", + }, + Queue: &internal.Queue{ + Channel: "foo", + Driver: "foo", + Host: "foo", + }, + Source: &internal.Source{ + Driver: "foo", + Host: "foo", + }, + Vela: &internal.Vela{ + Address: "foo", + WebAddress: "foo", + }, + } + + compiler, err := FromCLIContext(c) + if err != nil { + t.Errorf("Creating compiler returned err: %v", err) + } + + compiler.repo = &api.Repo{Name: &author} + compiler.build = &api.Build{Author: &name, Number: &number, Event: &event} + compiler.WithMetadata(m) + + testEnv := environment(&api.Build{Author: &name, Number: &number, Event: &event}, m, &api.Repo{Name: &author}, nil, nil) + + serviceEnv := environment(&api.Build{Author: &name, Number: &number, Event: &event}, m, &api.Repo{Name: &author}, nil, nil) + serviceEnv["REGION"] = "dev" + + alphaEnv := environment(&api.Build{Author: &name, Number: &number, Event: &event}, m, &api.Repo{Name: &author}, nil, nil) + alphaEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"echo alpha"}) + alphaEnv["HOME"] = "/root" + alphaEnv["SHELL"] = "/bin/sh" + + betaEnv := environment(&api.Build{Author: &name, Number: &number, Event: &event}, m, &api.Repo{Name: &author}, nil, nil) + betaEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"echo beta"}) + betaEnv["HOME"] = "/root" + betaEnv["SHELL"] = "/bin/sh" + + gammaEnv := environment(&api.Build{Author: &name, Number: &number, Event: &event}, m, &api.Repo{Name: &author}, nil, nil) + gammaEnv["VELA_BUILD_SCRIPT"] = generateScriptPosix([]string{"echo gamma"}) + gammaEnv["HOME"] = "/root" + gammaEnv["SHELL"] = "/bin/sh" + gammaEnv["REGION"] = "dev" + + want := &pipeline.Build{ + Version: "legacy", + ID: "_author_1", + Metadata: pipeline.Metadata{ + Clone: true, + Template: false, + Environment: []string{"steps", "services", "secrets"}, + AutoCancel: &pipeline.CancelOptions{ + Running: false, + Pending: false, + DefaultBranch: false, + }, + }, + Worker: pipeline.Worker{ + Flavor: "", + Platform: "", + }, + Services: pipeline.ContainerSlice{ + &pipeline.Container{ + ID: "service__author_1_service-a", + Detach: true, + Directory: "", + Environment: serviceEnv, + Image: "postgres", + Name: "service-a", + Number: 1, + Pull: "not_present", + Ports: []string{"5432:5432"}, + }, + }, + Steps: pipeline.ContainerSlice{ + &pipeline.Container{ + ID: "step__author_1_init", + Directory: "/vela/src/foo//author", + Environment: testEnv, + Image: "#init", + Name: "init", + Number: 1, + Pull: "not_present", + }, + &pipeline.Container{ + ID: "step__author_1_clone", + Directory: "/vela/src/foo//author", + Environment: testEnv, + Image: defaultCloneImage, + Name: "clone", + Number: 2, + Pull: "not_present", + }, + &pipeline.Container{ + ID: "step__author_1_alpha", + Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"}, + Directory: "/vela/src/foo//author", + Entrypoint: []string{"/bin/sh", "-c"}, + Environment: alphaEnv, + Image: "alpine:latest", + Name: "alpha", + Number: 3, + Pull: "not_present", + Ruleset: pipeline.Ruleset{ + If: pipeline.Rules{ + Event: []string{"push"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + &pipeline.Container{ + ID: "step__author_1_beta", + Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"}, + Directory: "/vela/src/foo//author", + Entrypoint: []string{"/bin/sh", "-c"}, + Environment: betaEnv, + Image: "alpine:latest", + Name: "beta", + Number: 4, + Pull: "not_present", + Ruleset: pipeline.Ruleset{ + If: pipeline.Rules{ + Event: []string{"push"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + &pipeline.Container{ + ID: "step__author_1_gamma", + Commands: []string{"echo $VELA_BUILD_SCRIPT | base64 -d | /bin/sh -e"}, + Directory: "/vela/src/foo//author", + Entrypoint: []string{"/bin/sh", "-c"}, + Environment: gammaEnv, + Image: "alpine:latest", + Name: "gamma", + Number: 5, + Pull: "not_present", + Ruleset: pipeline.Ruleset{ + If: pipeline.Rules{ + Event: []string{"push"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + }, + } + + // run test on legacy version + yaml, err := os.ReadFile("testdata/steps_merge_anchor.yml") + if err != nil { + t.Errorf("Reading yaml file return err: %v", err) + } + + got, _, err := compiler.Compile(context.Background(), yaml) + if err != nil { + t.Errorf("Compile returned err: %v", err) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Compile() mismatch (-want +got):\n%s", diff) + } + + // run test on current version (should fail) + yaml, err = os.ReadFile("../types/yaml/buildkite/testdata/merge_anchor.yml") // has `version: "1"` instead of `version: "legacy"` + if err != nil { + t.Errorf("Reading yaml file return err: %v", err) + } + + got, _, err = compiler.Compile(context.Background(), yaml) + if err == nil { + t.Errorf("Compile should have returned err") + } + + if got != nil { + t.Errorf("Compile is %v, want %v", got, nil) + } +} + // convertResponse converts the build to the ModifyResponse. func convertResponse(build *yaml.Build) (*ModifyResponse, error) { data, err := yml.Marshal(build) diff --git a/compiler/native/expand_test.go b/compiler/native/expand_test.go index 0ca596338..a47df02ed 100644 --- a/compiler/native/expand_test.go +++ b/compiler/native/expand_test.go @@ -694,7 +694,7 @@ func TestNative_ExpandStepsMulti(t *testing.T) { "auth_method": "token", "username": "octocat", "items": []interface{}{ - map[interface{}]interface{}{"path": "docker", "source": "secret/docker"}, + map[string]interface{}{"path": "docker", "source": "secret/docker"}, }, }, }, @@ -715,7 +715,7 @@ func TestNative_ExpandStepsMulti(t *testing.T) { "auth_method": "token", "username": "octocat", "items": []interface{}{ - map[interface{}]interface{}{"path": "docker", "source": "secret/docker"}, + map[string]interface{}{"path": "docker", "source": "secret/docker"}, }, }, }, diff --git a/compiler/native/parse.go b/compiler/native/parse.go index 804b1e015..317c52600 100644 --- a/compiler/native/parse.go +++ b/compiler/native/parse.go @@ -7,14 +7,12 @@ import ( "io" "os" - bkYaml "github.com/buildkite/yaml" - "github.com/go-vela/server/compiler/template/native" "github.com/go-vela/server/compiler/template/starlark" typesRaw "github.com/go-vela/server/compiler/types/raw" - bkYamlTypes "github.com/go-vela/server/compiler/types/yaml/buildkite" "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/constants" + "github.com/go-vela/server/internal" ) // ParseRaw converts an object to a string. @@ -114,12 +112,9 @@ func (c *client) Parse(v interface{}, pipelineType string, template *yaml.Templa // ParseBytes converts a byte slice to a yaml configuration. func ParseBytes(data []byte) (*yaml.Build, []byte, error) { - config := new(bkYamlTypes.Build) - - // unmarshal the bytes into the yaml configuration - err := bkYaml.Unmarshal(data, config) + config, err := internal.ParseYAML(data) if err != nil { - return nil, data, fmt.Errorf("unable to unmarshal yaml: %w", err) + return nil, nil, err } // initializing Environment to prevent nil error @@ -129,7 +124,7 @@ func ParseBytes(data []byte) (*yaml.Build, []byte, error) { config.Environment = typesRaw.StringSliceMap{} } - return config.ToYAML(), data, nil + return config, data, nil } // ParseFile converts an os.File into a yaml configuration. diff --git a/compiler/native/parse_test.go b/compiler/native/parse_test.go index ae5b263b7..1ac5953b9 100644 --- a/compiler/native/parse_test.go +++ b/compiler/native/parse_test.go @@ -604,6 +604,84 @@ func TestNative_Parse_Stages(t *testing.T) { } } +func TestNative_Parse_StagesLegacyMergeAnchor(t *testing.T) { + // setup types + client, _ := FromCLIContext(cli.NewContext(nil, flag.NewFlagSet("test", 0), nil)) + want := &yaml.Build{ + Version: "legacy", + Metadata: yaml.Metadata{ + Environment: []string{"steps", "services", "secrets"}, + }, + Environment: raw.StringSliceMap{}, + Stages: yaml.StageSlice{ + &yaml.Stage{ + Name: "install", + Needs: raw.StringSlice{"clone"}, + Steps: yaml.StepSlice{ + &yaml.Step{ + Commands: []string{"./gradlew downloadDependencies"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "install", + Pull: "always", + }, + }, + }, + &yaml.Stage{ + Name: "test", + Needs: []string{"install", "clone"}, + Steps: yaml.StepSlice{ + &yaml.Step{ + Commands: []string{"./gradlew check"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "test", + Pull: "always", + }, + }, + }, + &yaml.Stage{ + Name: "build", + Needs: []string{"install", "clone"}, + Steps: yaml.StepSlice{ + &yaml.Step{ + Commands: []string{"./gradlew build"}, + Environment: map[string]string{ + "GRADLE_OPTS": "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false", + "GRADLE_USER_HOME": ".gradle", + }, + Image: "openjdk:latest", + Name: "build", + Pull: "always", + }, + }, + }, + }, + } + + // run test + b, err := os.ReadFile("testdata/stages_merged.yml") + if err != nil { + t.Errorf("Reading file returned err: %v", err) + } + + got, _, err := client.Parse(b, "", new(yaml.Template)) + + if err != nil { + t.Errorf("Parse returned err: %v", err) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Parse() mismatch (-want +got):\n%s", diff) + } +} + func TestNative_Parse_Steps(t *testing.T) { // setup types client, _ := FromCLIContext(cli.NewContext(nil, flag.NewFlagSet("test", 0), nil)) diff --git a/compiler/native/testdata/stages_merged.yml b/compiler/native/testdata/stages_merged.yml new file mode 100644 index 000000000..6bb17c276 --- /dev/null +++ b/compiler/native/testdata/stages_merged.yml @@ -0,0 +1,38 @@ +version: "legacy" + +stages: + install: + steps: + - name: install + commands: + - ./gradlew downloadDependencies + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + + test: + needs: [ install ] + steps: + - name: test + commands: + - ./gradlew check + environment: + GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + GRADLE_USER_HOME: .gradle + image: openjdk:latest + pull: true + +stages: + build: + needs: [ install ] + steps: + - name: build + commands: + - ./gradlew build + environment: + - GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1 -Dorg.gradle.parallel=false + - GRADLE_USER_HOME=.gradle + image: openjdk:latest + pull: true \ No newline at end of file diff --git a/compiler/native/testdata/steps_merge_anchor.yml b/compiler/native/testdata/steps_merge_anchor.yml new file mode 100644 index 000000000..1d488edd2 --- /dev/null +++ b/compiler/native/testdata/steps_merge_anchor.yml @@ -0,0 +1,46 @@ +# test file that uses the non-standard multiple anchor keys in one step to test custom step unmarshaler + +version: "legacy" + +aliases: + images: + alpine: &alpine-image + image: alpine:latest + postgres: &pg-image + image: postgres + + events: + push: &event-push + ruleset: + event: + - push + env: + dev-env: &dev-environment + environment: + REGION: dev + +services: + - name: service-a + <<: *pg-image + <<: *dev-environment + ports: + - "5432:5432" + +steps: + - name: alpha + <<: *alpine-image + <<: *event-push + commands: + - echo alpha + + - name: beta + <<: [ *alpine-image, *event-push ] + commands: + - echo beta + + - name: gamma + <<: *alpine-image + <<: *event-push + <<: *dev-environment + commands: + - echo gamma \ No newline at end of file diff --git a/compiler/template/native/render.go b/compiler/template/native/render.go index 9fec0fd50..1004d62d1 100644 --- a/compiler/template/native/render.go +++ b/compiler/template/native/render.go @@ -8,17 +8,15 @@ import ( "text/template" "github.com/Masterminds/sprig/v3" - "github.com/buildkite/yaml" "github.com/go-vela/server/compiler/types/raw" - bkTypes "github.com/go-vela/server/compiler/types/yaml/buildkite" types "github.com/go-vela/server/compiler/types/yaml/yaml" + "github.com/go-vela/server/internal" ) // Render combines the template with the step in the yaml pipeline. func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}) (*types.Build, error) { buffer := new(bytes.Buffer) - config := new(bkTypes.Build) velaFuncs := funcHandler{envs: convertPlatformVars(environment, name)} templateFuncMap := map[string]interface{}{ @@ -48,7 +46,7 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM } // unmarshal the template to the pipeline - err = yaml.Unmarshal(buffer.Bytes(), config) + config, err := internal.ParseYAML(buffer.Bytes()) if err != nil { return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } @@ -58,13 +56,12 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM config.Steps[index].Name = fmt.Sprintf("%s_%s", name, newStep.Name) } - return &types.Build{Metadata: *config.Metadata.ToYAML(), Steps: *config.Steps.ToYAML(), Secrets: *config.Secrets.ToYAML(), Services: *config.Services.ToYAML(), Environment: config.Environment, Templates: *config.Templates.ToYAML(), Deployment: *config.Deployment.ToYAML()}, nil + return &types.Build{Metadata: config.Metadata, Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment, Templates: config.Templates, Deployment: config.Deployment}, nil } // RenderBuild renders the templated build. func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}) (*types.Build, error) { buffer := new(bytes.Buffer) - config := new(bkTypes.Build) velaFuncs := funcHandler{envs: convertPlatformVars(envs, tmpl)} templateFuncMap := map[string]interface{}{ @@ -94,10 +91,10 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st } // unmarshal the template to the pipeline - err = yaml.Unmarshal(buffer.Bytes(), config) + config, err := internal.ParseYAML(buffer.Bytes()) if err != nil { return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } - return config.ToYAML(), nil + return config, nil } diff --git a/compiler/template/starlark/render.go b/compiler/template/starlark/render.go index a89d63f99..1abc52319 100644 --- a/compiler/template/starlark/render.go +++ b/compiler/template/starlark/render.go @@ -7,14 +7,13 @@ import ( "errors" "fmt" - "github.com/buildkite/yaml" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" "go.starlark.net/syntax" "github.com/go-vela/server/compiler/types/raw" - bkTypes "github.com/go-vela/server/compiler/types/yaml/buildkite" types "github.com/go-vela/server/compiler/types/yaml/yaml" + "github.com/go-vela/server/internal" ) var ( @@ -33,8 +32,6 @@ var ( // Render combines the template with the step in the yaml pipeline. func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}, limit int64) (*types.Build, error) { - config := new(bkTypes.Build) - thread := &starlark.Thread{Name: name} if limit < 0 { @@ -125,7 +122,7 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM } // unmarshal the template to the pipeline - err = yaml.Unmarshal(buf.Bytes(), config) + config, err := internal.ParseYAML(buf.Bytes()) if err != nil { return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } @@ -135,15 +132,13 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM config.Steps[index].Name = fmt.Sprintf("%s_%s", name, newStep.Name) } - return &types.Build{Steps: *config.Steps.ToYAML(), Secrets: *config.Secrets.ToYAML(), Services: *config.Services.ToYAML(), Environment: config.Environment}, nil + return &types.Build{Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment}, nil } // RenderBuild renders the templated build. // //nolint:lll // ignore function length due to input args func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}, limit int64) (*types.Build, error) { - config := new(bkTypes.Build) - thread := &starlark.Thread{Name: "templated-base"} if limit < 0 { @@ -234,10 +229,10 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st } // unmarshal the template to the pipeline - err = yaml.Unmarshal(buf.Bytes(), config) + config, err := internal.ParseYAML(buf.Bytes()) if err != nil { return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } - return config.ToYAML(), nil + return config, nil } diff --git a/internal/yaml.go b/internal/yaml.go new file mode 100644 index 000000000..8267486e8 --- /dev/null +++ b/internal/yaml.go @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "fmt" + + bkYaml "github.com/buildkite/yaml" + yaml "gopkg.in/yaml.v3" + + legacyTypes "github.com/go-vela/server/compiler/types/yaml/buildkite" + types "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +// ParseYAML is a helper function for transitioning teams away from legacy buildkite YAML parser. +func ParseYAML(data []byte) (*types.Build, error) { + var ( + rootNode yaml.Node + version string + ) + + err := yaml.Unmarshal(data, &rootNode) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal pipeline version yaml: %w", err) + } + + if len(rootNode.Content) == 0 || rootNode.Content[0].Kind != yaml.MappingNode { + return nil, fmt.Errorf("unable to find pipeline version in yaml") + } + + for i, subNode := range rootNode.Content[0].Content { + if subNode.Kind == yaml.ScalarNode && subNode.Value == "version" { + if len(rootNode.Content[0].Content) > i { + version = rootNode.Content[0].Content[i+1].Value + + break + } + } + } + + config := new(types.Build) + + switch version { + case "legacy": + legacyConfig := new(legacyTypes.Build) + + err := bkYaml.Unmarshal(data, legacyConfig) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal legacy yaml: %w", err) + } + + config = legacyConfig.ToYAML() + + default: + // unmarshal the bytes into the yaml configuration + err := yaml.Unmarshal(data, config) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + } + } + + return config, nil +} diff --git a/internal/yaml_test.go b/internal/yaml_test.go new file mode 100644 index 000000000..627139229 --- /dev/null +++ b/internal/yaml_test.go @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "os" + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/go-vela/server/compiler/types/yaml/yaml" +) + +func TestInternal_ParseYAML(t *testing.T) { + // wantBuild + wantBuild := &yaml.Build{ + Version: "1", + Metadata: yaml.Metadata{ + Environment: []string{"steps", "services", "secrets"}, + }, + Steps: yaml.StepSlice{ + &yaml.Step{ + Name: "example", + Image: "alpine:latest", + Environment: map[string]string{ + "REGION": "dev", + }, + Pull: "not_present", + Commands: []string{ + "echo $REGION", + }, + }, + }, + } + + // set up tests + tests := []struct { + file string + want *yaml.Build + wantErr bool + }{ + { + file: "testdata/go-yaml.yml", + want: wantBuild, + }, + { + file: "testdata/buildkite.yml", + want: wantBuild, + }, + { + file: "testdata/no_version.yml", + want: wantBuild, + }, + { + file: "testdata/invalid.yml", + want: nil, + wantErr: true, + }, + } + + // run tests + for _, test := range tests { + bytes, err := os.ReadFile(test.file) + if err != nil { + t.Errorf("unable to read file: %v", err) + } + + gotBuild, err := ParseYAML(bytes) + if err != nil && !test.wantErr { + t.Errorf("ParseYAML returned err: %v", err) + } + + if err == nil && test.wantErr { + t.Errorf("ParseYAML returned nil error") + } + + if err != nil && test.wantErr { + continue + } + + // different versions expected + wantBuild.Version = gotBuild.Version + + if diff := cmp.Diff(gotBuild, test.want); diff != "" { + t.Errorf("ParseYAML returned diff (-got +want):\n%s", diff) + } + } +} From 114ae7b6e800910cfdbf45ce1c0f2ed08cfebd6d Mon Sep 17 00:00:00 2001 From: David May <49894298+wass3rw3rk@users.noreply.github.com> Date: Fri, 27 Dec 2024 09:53:04 -0600 Subject: [PATCH 21/41] refactor(db/build): drop source index, add event index (#1228) Co-authored-by: david may <1301201+wass3r@users.noreply.github.com> --- database/build/build_test.go | 4 ++-- database/build/index.go | 26 +++++++++++++------------- database/build/index_test.go | 2 +- database/resource_test.go | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/database/build/build_test.go b/database/build/build_test.go index 383c0573c..e97a06faf 100644 --- a/database/build/build_test.go +++ b/database/build/build_test.go @@ -28,8 +28,8 @@ func TestBuild_New(t *testing.T) { _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateEventIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _config := &gorm.Config{SkipDefaultTransaction: true} @@ -152,8 +152,8 @@ func testPostgres(t *testing.T) (*engine, sqlmock.Sqlmock) { _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateEventIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) // create the new mock Postgres database client diff --git a/database/build/index.go b/database/build/index.go index f224ccacc..4765ba534 100644 --- a/database/build/index.go +++ b/database/build/index.go @@ -12,6 +12,15 @@ CREATE INDEX IF NOT EXISTS builds_created ON builds (created); +` + + // CreateEventIndex represents a query to create an + // index on the builds table for the event column. + CreateEventIndex = ` +CREATE INDEX +IF NOT EXISTS +builds_event +ON builds (event); ` // CreateRepoIDIndex represents a query to create an @@ -21,15 +30,6 @@ CREATE INDEX IF NOT EXISTS builds_repo_id ON builds (repo_id); -` - - // CreateSourceIndex represents a query to create an - // index on the builds table for the source column. - CreateSourceIndex = ` -CREATE INDEX -IF NOT EXISTS -builds_source -ON builds (source); ` // CreateStatusIndex represents a query to create an @@ -54,18 +54,18 @@ func (e *engine) CreateBuildIndexes(ctx context.Context) error { return err } - // create the repo_id column index for the builds table + // create the event column index for the builds table err = e.client. WithContext(ctx). - Exec(CreateRepoIDIndex).Error + Exec(CreateEventIndex).Error if err != nil { return err } - // create the source column index for the builds table + // create the repo_id column index for the builds table err = e.client. WithContext(ctx). - Exec(CreateSourceIndex).Error + Exec(CreateRepoIDIndex).Error if err != nil { return err } diff --git a/database/build/index_test.go b/database/build/index_test.go index 03d02fc8d..1a0a00099 100644 --- a/database/build/index_test.go +++ b/database/build/index_test.go @@ -15,8 +15,8 @@ func TestBuild_Engine_CreateBuildIndexes(t *testing.T) { defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() _mock.ExpectExec(CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateEventIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _sqlite := testSqlite(t) diff --git a/database/resource_test.go b/database/resource_test.go index cdc5dbaa3..794ed980e 100644 --- a/database/resource_test.go +++ b/database/resource_test.go @@ -35,8 +35,8 @@ func TestDatabase_Engine_NewResources(t *testing.T) { // ensure the mock expects the build queries _mock.ExpectExec(build.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(build.CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateEventIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(build.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(build.CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(build.CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the dashboard queries _mock.ExpectExec(dashboard.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) From fd16b7b27d5db6ebbd0a761d3186a8698682c2ad Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:02:46 -0600 Subject: [PATCH 22/41] feat(repo)!: add pending approval timeout (#1227) * init commit * feat(repo): add pending approval timeout * fix test * remove dead code --------- Co-authored-by: David May <49894298+wass3rw3rk@users.noreply.github.com> --- api/build/auto_cancel.go | 5 +- api/build/enqueue.go | 48 ++ api/build/gatekeep.go | 54 ++ api/build/restart.go | 41 +- api/repo/create.go | 17 + api/repo/update.go | 6 + api/types/repo.go | 108 ++-- api/types/repo_test.go | 81 ++- api/webhook/post.go | 161 ++--- cmd/vela-server/cleanup.go | 49 ++ cmd/vela-server/main.go | 6 + cmd/vela-server/server.go | 18 + compiler/native/environment_test.go | 617 ++++++++++--------- constants/limit.go | 9 + database/build/interface.go | 2 + database/build/list_pending_approval.go | 48 ++ database/build/list_pending_approval_test.go | 144 +++++ database/integration_test.go | 18 +- database/repo/create_test.go | 6 +- database/repo/table.go | 90 +-- database/repo/update_test.go | 7 +- database/testutils/api_resources.go | 45 +- database/types/repo.go | 106 ++-- database/types/repo_test.go | 47 +- database/types/schedule_test.go | 1 + docker-compose.yml | 4 +- mock/server/repo.go | 3 +- router/middleware/default_timeout.go | 9 + router/middleware/repo/repo_test.go | 1 + 29 files changed, 1100 insertions(+), 651 deletions(-) create mode 100644 api/build/gatekeep.go create mode 100644 cmd/vela-server/cleanup.go create mode 100644 database/build/list_pending_approval.go create mode 100644 database/build/list_pending_approval_test.go diff --git a/api/build/auto_cancel.go b/api/build/auto_cancel.go index c34d24917..87f4694b6 100644 --- a/api/build/auto_cancel.go +++ b/api/build/auto_cancel.go @@ -91,7 +91,7 @@ func AutoCancel(c *gin.Context, b *types.Build, rB *types.Build, cancelOpts *pip }).Info("build updated - build canceled") } - return true, nil + return false, nil } // cancelRunning is a helper function that determines the executor currently running a build and sends an API call @@ -220,7 +220,8 @@ func isCancelable(target *types.Build, current *types.Build) bool { // target is cancelable if current build is also a push event and the branches are the same return strings.EqualFold(current.GetEvent(), constants.EventPush) && strings.EqualFold(current.GetBranch(), target.GetBranch()) case constants.EventPull: - cancelableAction := strings.EqualFold(target.GetEventAction(), constants.ActionOpened) || strings.EqualFold(target.GetEventAction(), constants.ActionSynchronize) + cancelableAction := strings.EqualFold(target.GetEventAction(), constants.ActionOpened) || + strings.EqualFold(target.GetEventAction(), constants.ActionSynchronize) // target is cancelable if current build is also a pull event, target is an opened / synchronize action, and the current head ref matches target head ref return strings.EqualFold(current.GetEvent(), constants.EventPull) && cancelableAction && strings.EqualFold(current.GetHeadRef(), target.GetHeadRef()) diff --git a/api/build/enqueue.go b/api/build/enqueue.go index 695967ca0..da4b4c102 100644 --- a/api/build/enqueue.go +++ b/api/build/enqueue.go @@ -7,11 +7,15 @@ import ( "encoding/json" "time" + "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database" "github.com/go-vela/server/queue" "github.com/go-vela/server/queue/models" + "github.com/go-vela/server/scm" ) // Enqueue is a helper function that pushes a queue item (build, repo, user) to the queue. @@ -65,3 +69,47 @@ func Enqueue(ctx context.Context, queue queue.Service, db database.Interface, it l.Info("updated build as enqueued") } + +// ShouldEnqueue is a helper function that will determine whether to publish a build to the queue or place it +// in pending approval status. +func ShouldEnqueue(c *gin.Context, l *logrus.Entry, b *types.Build, r *types.Repo) (bool, error) { + // if the webhook was from a Pull event from a forked repository, verify it is allowed to run + if b.GetFork() { + l.Tracef("inside %s workflow for fork PR build %s/%d", r.GetApproveBuild(), r.GetFullName(), b.GetNumber()) + + switch r.GetApproveBuild() { + case constants.ApproveForkAlways: + return false, nil + case constants.ApproveForkNoWrite: + // determine if build sender has write access to parent repo. If not, this call will result in an error + level, err := scm.FromContext(c).RepoAccess(c.Request.Context(), b.GetSender(), r.GetOwner().GetToken(), r.GetOrg(), r.GetName()) + if err != nil || (level != "admin" && level != "write") { + //nolint:nilerr // an error here is not something we want to return since we are gating it anyway + return false, nil + } + + l.Debugf("fork PR build %s/%d automatically running without approval. sender %s has %s access", r.GetFullName(), b.GetNumber(), b.GetSender(), level) + case constants.ApproveOnce: + // determine if build sender is in the contributors list for the repo + // + // NOTE: this call is cumbersome for repos with lots of contributors. Potential TODO: improve this if + // GitHub adds a single-contributor API endpoint. + contributor, err := scm.FromContext(c).RepoContributor(c.Request.Context(), r.GetOwner(), b.GetSender(), r.GetOrg(), r.GetName()) + if err != nil { + return false, err + } + + if !contributor { + return false, nil + } + + fallthrough + case constants.ApproveNever: + fallthrough + default: + l.Debugf("fork PR build %s/%d automatically running without approval", r.GetFullName(), b.GetNumber()) + } + } + + return true, nil +} diff --git a/api/build/gatekeep.go b/api/build/gatekeep.go new file mode 100644 index 000000000..35df7bd1d --- /dev/null +++ b/api/build/gatekeep.go @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache-2.0 + +package build + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database" + "github.com/go-vela/server/scm" +) + +// GatekeepBuild is a helper function that will set the status of a build to 'pending approval' and +// send a status update to the SCM. +func GatekeepBuild(c *gin.Context, b *types.Build, r *types.Repo) error { + l := c.MustGet("logger").(*logrus.Entry) + + l = l.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + "repo_id": r.GetID(), + "build": b.GetNumber(), + "build_id": b.GetID(), + }) + + l.Debug("fork PR build waiting for approval") + + b.SetStatus(constants.StatusPendingApproval) + + _, err := database.FromContext(c).UpdateBuild(c, b) + if err != nil { + return fmt.Errorf("unable to update build for %s/%d: %w", r.GetFullName(), b.GetNumber(), err) + } + + l.Info("build updated") + + // update the build components to pending approval status + err = UpdateComponentStatuses(c, b, constants.StatusPendingApproval) + if err != nil { + return fmt.Errorf("unable to update build components for %s/%d: %w", r.GetFullName(), b.GetNumber(), err) + } + + // send API call to set the status on the commit + err = scm.FromContext(c).Status(c, r.GetOwner(), b, r.GetOrg(), r.GetName()) + if err != nil { + l.Errorf("unable to set commit status for %s/%d: %v", r.GetFullName(), b.GetNumber(), err) + } + + return nil +} diff --git a/api/build/restart.go b/api/build/restart.go index 56cffae38..e4b86c86f 100644 --- a/api/build/restart.go +++ b/api/build/restart.go @@ -149,19 +149,42 @@ func RestartBuild(c *gin.Context) { return } + // determine whether or not to send compiled build to queue + shouldEnqueue, err := ShouldEnqueue(c, l, item.Build, r) + if err != nil { + util.HandleError(c, http.StatusInternalServerError, err) + + return + } + + if shouldEnqueue { + // send API call to set the status on the commit + err := scm.Status(c.Request.Context(), r.GetOwner(), b, r.GetOrg(), r.GetName()) + if err != nil { + l.Errorf("unable to set commit status for %s/%d: %v", r.GetFullName(), b.GetNumber(), err) + } + + // publish the build to the queue + go Enqueue( + context.WithoutCancel(c.Request.Context()), + queue.FromGinContext(c), + database.FromContext(c), + item, + b.GetHost(), + ) + } else { + err := GatekeepBuild(c, b, r) + if err != nil { + util.HandleError(c, http.StatusInternalServerError, err) + + return + } + } + l.WithFields(logrus.Fields{ "new_build": item.Build.GetNumber(), "new_build_id": item.Build.GetID(), }).Info("build created via restart") c.JSON(http.StatusCreated, item.Build) - - // publish the build to the queue - go Enqueue( - context.WithoutCancel(ctx), - queue.FromGinContext(c), - database.FromContext(c), - item, - item.Build.GetHost(), - ) } diff --git a/api/repo/create.go b/api/repo/create.go index ae766c4c5..1aebcd4d2 100644 --- a/api/repo/create.go +++ b/api/repo/create.go @@ -79,6 +79,7 @@ func CreateRepo(c *gin.Context) { defaultBuildLimit := c.Value("defaultBuildLimit").(int64) defaultTimeout := c.Value("defaultTimeout").(int64) + defaultApprovalTimeout := c.Value("defaultApprovalTimeout").(int64) maxBuildLimit := c.Value("maxBuildLimit").(int64) defaultRepoEvents := c.Value("defaultRepoEvents").([]string) defaultRepoEventsMask := c.Value("defaultRepoEventsMask").(int64) @@ -138,10 +139,26 @@ func CreateRepo(c *gin.Context) { r.SetTimeout(constants.BuildTimeoutDefault) } else if input.GetTimeout() == 0 { r.SetTimeout(defaultTimeout) + } else if input.GetTimeout() > constants.BuildTimeoutMax { + // set build timeout to max value to prevent timeout from exceeding max + r.SetTimeout(constants.BuildTimeoutMax) } else { r.SetTimeout(input.GetTimeout()) } + // set the approval timeout field based of the input provided + if input.GetApprovalTimeout() == 0 && defaultApprovalTimeout == 0 { + // default approval timeout to 7d + r.SetApprovalTimeout(constants.ApprovalTimeoutDefault) + } else if input.GetApprovalTimeout() == 0 { + r.SetApprovalTimeout(defaultApprovalTimeout) + } else if input.GetApprovalTimeout() > constants.ApprovalTimeoutMax { + // set approval timeout to max value to prevent timeout from exceeding max + r.SetApprovalTimeout(constants.ApprovalTimeoutMax) + } else { + r.SetApprovalTimeout(input.GetApprovalTimeout()) + } + // set the visibility field based off the input provided if len(input.GetVisibility()) > 0 { // default visibility field to the input visibility diff --git a/api/repo/update.go b/api/repo/update.go index 001a67626..818b32e2d 100644 --- a/api/repo/update.go +++ b/api/repo/update.go @@ -117,6 +117,12 @@ func UpdateRepo(c *gin.Context) { r.SetTimeout(limit) } + if input.GetApprovalTimeout() > 0 { + // update build approval timeout if set + limit := max(constants.ApprovalTimeoutMin, min(input.GetApprovalTimeout(), constants.ApprovalTimeoutMax)) + r.SetApprovalTimeout(limit) + } + if input.GetCounter() > 0 { if input.GetCounter() <= r.GetCounter() { retErr := fmt.Errorf("unable to set counter for repo %s: must be greater than current %d", diff --git a/api/types/repo.go b/api/types/repo.go index b81a061e9..0d84b2417 100644 --- a/api/types/repo.go +++ b/api/types/repo.go @@ -11,51 +11,53 @@ import ( // // swagger:model Repo type Repo struct { - ID *int64 `json:"id,omitempty"` - Owner *User `json:"owner,omitempty"` - Hash *string `json:"-"` - Org *string `json:"org,omitempty"` - Name *string `json:"name,omitempty"` - FullName *string `json:"full_name,omitempty"` - Link *string `json:"link,omitempty"` - Clone *string `json:"clone,omitempty"` - Branch *string `json:"branch,omitempty"` - Topics *[]string `json:"topics,omitempty"` - BuildLimit *int64 `json:"build_limit,omitempty"` - Timeout *int64 `json:"timeout,omitempty"` - Counter *int `json:"counter,omitempty"` - Visibility *string `json:"visibility,omitempty"` - Private *bool `json:"private,omitempty"` - Trusted *bool `json:"trusted,omitempty"` - Active *bool `json:"active,omitempty"` - AllowEvents *Events `json:"allow_events,omitempty"` - PipelineType *string `json:"pipeline_type,omitempty"` - PreviousName *string `json:"previous_name,omitempty"` - ApproveBuild *string `json:"approve_build,omitempty"` - InstallID *int64 `json:"install_id,omitempty"` + ID *int64 `json:"id,omitempty"` + Owner *User `json:"owner,omitempty"` + Hash *string `json:"-"` + Org *string `json:"org,omitempty"` + Name *string `json:"name,omitempty"` + FullName *string `json:"full_name,omitempty"` + Link *string `json:"link,omitempty"` + Clone *string `json:"clone,omitempty"` + Branch *string `json:"branch,omitempty"` + Topics *[]string `json:"topics,omitempty"` + BuildLimit *int64 `json:"build_limit,omitempty"` + Timeout *int64 `json:"timeout,omitempty"` + Counter *int `json:"counter,omitempty"` + Visibility *string `json:"visibility,omitempty"` + Private *bool `json:"private,omitempty"` + Trusted *bool `json:"trusted,omitempty"` + Active *bool `json:"active,omitempty"` + AllowEvents *Events `json:"allow_events,omitempty"` + PipelineType *string `json:"pipeline_type,omitempty"` + PreviousName *string `json:"previous_name,omitempty"` + ApproveBuild *string `json:"approve_build,omitempty"` + ApprovalTimeout *int64 `json:"approval_timeout,omitempty"` + InstallID *int64 `json:"install_id,omitempty"` } // Environment returns a list of environment variables // provided from the fields of the Repo type. func (r *Repo) Environment() map[string]string { return map[string]string{ - "VELA_REPO_ACTIVE": ToString(r.GetActive()), - "VELA_REPO_ALLOW_EVENTS": strings.Join(r.GetAllowEvents().List()[:], ","), - "VELA_REPO_BRANCH": ToString(r.GetBranch()), - "VELA_REPO_TOPICS": strings.Join(r.GetTopics()[:], ","), - "VELA_REPO_BUILD_LIMIT": ToString(r.GetBuildLimit()), - "VELA_REPO_CLONE": ToString(r.GetClone()), - "VELA_REPO_FULL_NAME": ToString(r.GetFullName()), - "VELA_REPO_LINK": ToString(r.GetLink()), - "VELA_REPO_NAME": ToString(r.GetName()), - "VELA_REPO_ORG": ToString(r.GetOrg()), - "VELA_REPO_PRIVATE": ToString(r.GetPrivate()), - "VELA_REPO_TIMEOUT": ToString(r.GetTimeout()), - "VELA_REPO_TRUSTED": ToString(r.GetTrusted()), - "VELA_REPO_VISIBILITY": ToString(r.GetVisibility()), - "VELA_REPO_PIPELINE_TYPE": ToString(r.GetPipelineType()), - "VELA_REPO_APPROVE_BUILD": ToString(r.GetApproveBuild()), - "VELA_REPO_OWNER": ToString(r.GetOwner().GetName()), + "VELA_REPO_ACTIVE": ToString(r.GetActive()), + "VELA_REPO_ALLOW_EVENTS": strings.Join(r.GetAllowEvents().List()[:], ","), + "VELA_REPO_BRANCH": ToString(r.GetBranch()), + "VELA_REPO_TOPICS": strings.Join(r.GetTopics()[:], ","), + "VELA_REPO_BUILD_LIMIT": ToString(r.GetBuildLimit()), + "VELA_REPO_CLONE": ToString(r.GetClone()), + "VELA_REPO_FULL_NAME": ToString(r.GetFullName()), + "VELA_REPO_LINK": ToString(r.GetLink()), + "VELA_REPO_NAME": ToString(r.GetName()), + "VELA_REPO_ORG": ToString(r.GetOrg()), + "VELA_REPO_PRIVATE": ToString(r.GetPrivate()), + "VELA_REPO_TIMEOUT": ToString(r.GetTimeout()), + "VELA_REPO_TRUSTED": ToString(r.GetTrusted()), + "VELA_REPO_VISIBILITY": ToString(r.GetVisibility()), + "VELA_REPO_PIPELINE_TYPE": ToString(r.GetPipelineType()), + "VELA_REPO_APPROVE_BUILD": ToString(r.GetApproveBuild()), + "VELA_REPO_APPROVAL_TIMEOUT": ToString(r.GetApprovalTimeout()), + "VELA_REPO_OWNER": ToString(r.GetOwner().GetName()), // deprecated environment variables "REPOSITORY_ACTIVE": ToString(r.GetActive()), @@ -346,6 +348,19 @@ func (r *Repo) GetApproveBuild() string { return *r.ApproveBuild } +// GetApprovalTimeout returns the ApprovalTimeout field. +// +// When the provided Repo type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (r *Repo) GetApprovalTimeout() int64 { + // return zero value if Repo type or ApprovalTimeout field is nil + if r == nil || r.ApprovalTimeout == nil { + return 0 + } + + return *r.ApprovalTimeout +} + // GetInstallID returns the InstallID field. // // When the provided Repo type is nil, or the field within @@ -632,6 +647,19 @@ func (r *Repo) SetApproveBuild(v string) { r.ApproveBuild = &v } +// SetApprovalTimeout sets the ApprovalTimeout field. +// +// When the provided Repo type is nil, it +// will set nothing and immediately return. +func (r *Repo) SetApprovalTimeout(v int64) { + // return if Repo type is nil + if r == nil { + return + } + + r.ApprovalTimeout = &v +} + // SetInstallID sets the InstallID field. // // When the provided Repo type is nil, it @@ -650,6 +678,7 @@ func (r *Repo) String() string { return fmt.Sprintf(`{ Active: %t, AllowEvents: %s, + ApprovalTimeout: %d, ApproveBuild: %s, Branch: %s, BuildLimit: %d, @@ -672,6 +701,7 @@ func (r *Repo) String() string { }`, r.GetActive(), r.GetAllowEvents().List(), + r.GetApprovalTimeout(), r.GetApproveBuild(), r.GetBranch(), r.GetBuildLimit(), diff --git a/api/types/repo_test.go b/api/types/repo_test.go index fa8fb2970..1231a64b1 100644 --- a/api/types/repo_test.go +++ b/api/types/repo_test.go @@ -15,35 +15,36 @@ import ( func TestTypes_Repo_Environment(t *testing.T) { // setup types want := map[string]string{ - "VELA_REPO_ACTIVE": "true", - "VELA_REPO_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,pull_request:unlabeled,tag,comment:created,schedule,delete:branch", - "VELA_REPO_BRANCH": "main", - "VELA_REPO_TOPICS": "cloud,security", - "VELA_REPO_BUILD_LIMIT": "10", - "VELA_REPO_CLONE": "https://github.com/github/octocat.git", - "VELA_REPO_FULL_NAME": "github/octocat", - "VELA_REPO_LINK": "https://github.com/github/octocat", - "VELA_REPO_NAME": "octocat", - "VELA_REPO_ORG": "github", - "VELA_REPO_PRIVATE": "false", - "VELA_REPO_TIMEOUT": "30", - "VELA_REPO_TRUSTED": "false", - "VELA_REPO_VISIBILITY": "public", - "VELA_REPO_PIPELINE_TYPE": "", - "VELA_REPO_APPROVE_BUILD": "never", - "VELA_REPO_OWNER": "octocat", - "REPOSITORY_ACTIVE": "true", - "REPOSITORY_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,pull_request:unlabeled,tag,comment:created,schedule,delete:branch", - "REPOSITORY_BRANCH": "main", - "REPOSITORY_CLONE": "https://github.com/github/octocat.git", - "REPOSITORY_FULL_NAME": "github/octocat", - "REPOSITORY_LINK": "https://github.com/github/octocat", - "REPOSITORY_NAME": "octocat", - "REPOSITORY_ORG": "github", - "REPOSITORY_PRIVATE": "false", - "REPOSITORY_TIMEOUT": "30", - "REPOSITORY_TRUSTED": "false", - "REPOSITORY_VISIBILITY": "public", + "VELA_REPO_ACTIVE": "true", + "VELA_REPO_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,pull_request:unlabeled,tag,comment:created,schedule,delete:branch", + "VELA_REPO_BRANCH": "main", + "VELA_REPO_TOPICS": "cloud,security", + "VELA_REPO_BUILD_LIMIT": "10", + "VELA_REPO_CLONE": "https://github.com/github/octocat.git", + "VELA_REPO_FULL_NAME": "github/octocat", + "VELA_REPO_LINK": "https://github.com/github/octocat", + "VELA_REPO_NAME": "octocat", + "VELA_REPO_ORG": "github", + "VELA_REPO_PRIVATE": "false", + "VELA_REPO_TIMEOUT": "30", + "VELA_REPO_TRUSTED": "false", + "VELA_REPO_VISIBILITY": "public", + "VELA_REPO_PIPELINE_TYPE": "", + "VELA_REPO_APPROVE_BUILD": "never", + "VELA_REPO_APPROVAL_TIMEOUT": "7", + "VELA_REPO_OWNER": "octocat", + "REPOSITORY_ACTIVE": "true", + "REPOSITORY_ALLOW_EVENTS": "push,pull_request:opened,pull_request:synchronize,pull_request:reopened,pull_request:unlabeled,tag,comment:created,schedule,delete:branch", + "REPOSITORY_BRANCH": "main", + "REPOSITORY_CLONE": "https://github.com/github/octocat.git", + "REPOSITORY_FULL_NAME": "github/octocat", + "REPOSITORY_LINK": "https://github.com/github/octocat", + "REPOSITORY_NAME": "octocat", + "REPOSITORY_ORG": "github", + "REPOSITORY_PRIVATE": "false", + "REPOSITORY_TIMEOUT": "30", + "REPOSITORY_TRUSTED": "false", + "REPOSITORY_VISIBILITY": "public", } // run test @@ -151,6 +152,14 @@ func TestTypes_Repo_Getters(t *testing.T) { if test.repo.GetApproveBuild() != test.want.GetApproveBuild() { t.Errorf("GetApproveForkBuild is %v, want %v", test.repo.GetApproveBuild(), test.want.GetApproveBuild()) } + + if test.repo.GetApprovalTimeout() != test.want.GetApprovalTimeout() { + t.Errorf("GetApprovalTimeout is %v, want %v", test.repo.GetApprovalTimeout(), test.want.GetApprovalTimeout()) + } + + if test.repo.GetInstallID() != test.want.GetInstallID() { + t.Errorf("GetInstallID is %v, want %v", test.repo.GetInstallID(), test.want.GetInstallID()) + } } } @@ -196,6 +205,8 @@ func TestTypes_Repo_Setters(t *testing.T) { test.repo.SetPipelineType(test.want.GetPipelineType()) test.repo.SetPreviousName(test.want.GetPreviousName()) test.repo.SetApproveBuild(test.want.GetApproveBuild()) + test.repo.SetApprovalTimeout(test.want.GetApprovalTimeout()) + test.repo.SetInstallID(test.want.GetInstallID()) if test.repo.GetID() != test.want.GetID() { t.Errorf("SetID is %v, want %v", test.repo.GetID(), test.want.GetID()) @@ -276,6 +287,14 @@ func TestTypes_Repo_Setters(t *testing.T) { if test.repo.GetApproveBuild() != test.want.GetApproveBuild() { t.Errorf("SetApproveForkBuild is %v, want %v", test.repo.GetApproveBuild(), test.want.GetApproveBuild()) } + + if test.repo.GetApprovalTimeout() != test.want.GetApprovalTimeout() { + t.Errorf("SetApprovalTimeout is %v, want %v", test.repo.GetApprovalTimeout(), test.want.GetApprovalTimeout()) + } + + if test.repo.GetInstallID() != test.want.GetInstallID() { + t.Errorf("SetInstallID is %v, want %v", test.repo.GetInstallID(), test.want.GetInstallID()) + } } } @@ -286,6 +305,7 @@ func TestTypes_Repo_String(t *testing.T) { want := fmt.Sprintf(`{ Active: %t, AllowEvents: %s, + ApprovalTimeout: %d, ApproveBuild: %s, Branch: %s, BuildLimit: %d, @@ -308,6 +328,7 @@ func TestTypes_Repo_String(t *testing.T) { }`, r.GetActive(), r.GetAllowEvents().List(), + r.GetApprovalTimeout(), r.GetApproveBuild(), r.GetBranch(), r.GetBuildLimit(), @@ -364,6 +385,8 @@ func testRepo() *Repo { r.SetPipelineType("") r.SetPreviousName("") r.SetApproveBuild(constants.ApproveNever) + r.SetApprovalTimeout(7) + r.SetInstallID(123) return r } diff --git a/api/webhook/post.go b/api/webhook/post.go index 8ff5b4cc2..07cc51be6 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -535,100 +535,60 @@ func PostWebhook(c *gin.Context) { } } - l.WithFields(logrus.Fields{ - "build": rB.GetNumber(), - "build_id": rB.GetID(), - "org": repo.GetOrg(), - "repo": repo.GetName(), - "repo_id": repo.GetID(), - }).Debug("auto-canceled build") + if canceled { + l.WithFields(logrus.Fields{ + "build": rB.GetNumber(), + "build_id": rB.GetID(), + "org": repo.GetOrg(), + "repo": repo.GetName(), + "repo_id": repo.GetID(), + }).Debug("auto-canceled build") + } } } }() - // track if we have already responded to the http request - // helps prevent multiple responses to the same request in the event of errors - responded := false - - // if the webhook was from a Pull event from a forked repository, verify it is allowed to run - if b.GetFork() { - l.Tracef("inside %s workflow for fork PR build %s/%d", repo.GetApproveBuild(), repo.GetFullName(), b.GetNumber()) - - switch repo.GetApproveBuild() { - case constants.ApproveForkAlways: - err = gatekeepBuild(c, b, repo) - if err != nil { - util.HandleError(c, http.StatusInternalServerError, err) - } else { - c.JSON(http.StatusCreated, b) - } - - return - case constants.ApproveForkNoWrite: - // determine if build sender has write access to parent repo. If not, this call will result in an error - _, err = scm.FromContext(c).RepoAccess(ctx, b.GetSender(), repo.GetOwner().GetToken(), repo.GetOrg(), repo.GetName()) - if err != nil { - err = gatekeepBuild(c, b, repo) - if err != nil { - util.HandleError(c, http.StatusInternalServerError, err) - } else { - c.JSON(http.StatusCreated, b) - } + // determine whether to send compiled build to queue + shouldEnqueue, err := build.ShouldEnqueue(c, l, b, repo) + if err != nil { + retErr := fmt.Errorf("unable to process build destination: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) - return - } + h.SetStatus(constants.StatusFailure) + h.SetError(retErr.Error()) - l.Debugf("fork PR build %s/%d automatically running without approval", repo.GetFullName(), b.GetNumber()) - case constants.ApproveOnce: - // determine if build sender is in the contributors list for the repo - // - // NOTE: this call is cumbersome for repos with lots of contributors. Potential TODO: improve this if - // GitHub adds a single-contributor API endpoint. - contributor, err := scm.FromContext(c).RepoContributor(ctx, repo.GetOwner(), b.GetSender(), repo.GetOrg(), repo.GetName()) - if err != nil { - util.HandleError(c, http.StatusInternalServerError, err) + return + } - responded = true - } + if shouldEnqueue { + // send API call to set the status on the commit + err := scm.FromContext(c).Status(c.Request.Context(), r.GetOwner(), b, r.GetOrg(), r.GetName()) + if err != nil { + l.Errorf("unable to set commit status for %s/%d: %v", r.GetFullName(), b.GetNumber(), err) + } - if !contributor { - err = gatekeepBuild(c, b, repo) - if err != nil { - util.HandleError(c, http.StatusInternalServerError, err) - } else if !responded { - c.JSON(http.StatusCreated, b) - } + // publish the build to the queue + go build.Enqueue( + context.WithoutCancel(c.Request.Context()), + queue.FromGinContext(c), + database.FromContext(c), + item, + b.GetHost(), + ) + } else { + err := build.GatekeepBuild(c, b, r) + if err != nil { + retErr := fmt.Errorf("unable to gate build: %w", err) + util.HandleError(c, http.StatusInternalServerError, err) - return - } + h.SetStatus(constants.StatusFailure) + h.SetError(retErr.Error()) - fallthrough - case constants.ApproveNever: - fallthrough - default: - l.Debugf("fork PR build %s/%d automatically running without approval", repo.GetFullName(), b.GetNumber()) + return } } - // send API call to set the status on the commit - err = scm.FromContext(c).Status(ctx, repo.GetOwner(), b, repo.GetOrg(), repo.GetName()) - if err != nil { - l.Errorf("unable to set commit status for %s/%d: %v", repo.GetFullName(), b.GetNumber(), err) - } - - // publish the build to the queue - go build.Enqueue( - context.WithoutCancel(ctx), - queue.FromGinContext(c), - database.FromContext(c), - item, - b.GetHost(), - ) - - // respond only when necessary - if !responded { - c.JSON(http.StatusCreated, b) - } + c.JSON(http.StatusCreated, b) } // handleRepositoryEvent is a helper function that processes repository events from the SCM and updates @@ -891,42 +851,3 @@ func RenameRepository(ctx context.Context, l *logrus.Entry, db database.Interfac return dbR, nil } - -// gatekeepBuild is a helper function that will set the status of a build to 'pending approval' and -// send a status update to the SCM. -func gatekeepBuild(c *gin.Context, b *types.Build, r *types.Repo) error { - l := c.MustGet("logger").(*logrus.Entry) - - l = l.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), - "repo_id": r.GetID(), - "build": b.GetNumber(), - "build_id": b.GetID(), - }) - - l.Debug("fork PR build waiting for approval") - - b.SetStatus(constants.StatusPendingApproval) - - _, err := database.FromContext(c).UpdateBuild(c, b) - if err != nil { - return fmt.Errorf("unable to update build for %s/%d: %w", r.GetFullName(), b.GetNumber(), err) - } - - l.Info("build updated") - - // update the build components to pending approval status - err = build.UpdateComponentStatuses(c, b, constants.StatusPendingApproval) - if err != nil { - return fmt.Errorf("unable to update build components for %s/%d: %w", r.GetFullName(), b.GetNumber(), err) - } - - // send API call to set the status on the commit - err = scm.FromContext(c).Status(c, r.GetOwner(), b, r.GetOrg(), r.GetName()) - if err != nil { - l.Errorf("unable to set commit status for %s/%d: %v", r.GetFullName(), b.GetNumber(), err) - } - - return nil -} diff --git a/cmd/vela-server/cleanup.go b/cmd/vela-server/cleanup.go new file mode 100644 index 000000000..93d9546a1 --- /dev/null +++ b/cmd/vela-server/cleanup.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "strconv" + "time" + + "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" + + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database" +) + +// helper function to clean pending approval builds from the database. +func cleanupPendingApproval(c *cli.Context, db database.Interface) error { + logrus.Debug("cleaning pending approval builds") + + before := time.Now().Add(-(time.Duration(24*constants.ApprovalTimeoutMin) * time.Hour)).Unix() + + builds, err := db.ListPendingApprovalBuilds(c.Context, strconv.FormatInt(before, 10)) + if err != nil { + return err + } + + for _, build := range builds { + threshold := time.Now().Add(-(time.Duration(24*build.GetRepo().GetApprovalTimeout()) * time.Hour)).Unix() + + if build.GetCreated() < threshold { + _, err := db.PopBuildExecutable(c.Context, build.GetID()) + if err != nil { + return err + } + + build.SetStatus(constants.StatusError) + build.SetFinished(time.Now().Unix()) + build.SetError(fmt.Sprintf("build exceeded approval timeout of %d days", build.GetRepo().GetApprovalTimeout())) + + _, err = db.UpdateBuild(c.Context, build) + if err != nil { + return err + } + } + } + + return nil +} diff --git a/cmd/vela-server/main.go b/cmd/vela-server/main.go index de8ccfd49..864c8fce6 100644 --- a/cmd/vela-server/main.go +++ b/cmd/vela-server/main.go @@ -134,6 +134,12 @@ func main() { Usage: "override default build timeout (minutes)", Value: constants.BuildTimeoutDefault, }, + &cli.Int64Flag{ + EnvVars: []string{"VELA_DEFAULT_APPROVAL_TIMEOUT"}, + Name: "default-approval-timeout", + Usage: "override default approval timeout (days)", + Value: constants.ApprovalTimeoutDefault, + }, &cli.StringSliceFlag{ EnvVars: []string{"VELA_DEFAULT_REPO_EVENTS"}, Name: "default-repo-events", diff --git a/cmd/vela-server/server.go b/cmd/vela-server/server.go index 707c08210..c7e8ac986 100644 --- a/cmd/vela-server/server.go +++ b/cmd/vela-server/server.go @@ -204,6 +204,7 @@ func server(c *cli.Context) error { middleware.QueueAddress(c.String("queue.addr")), middleware.DefaultBuildLimit(c.Int64("default-build-limit")), middleware.DefaultTimeout(c.Int64("default-build-timeout")), + middleware.DefaultApprovalTimeout(c.Int64("default-approval-timeout")), middleware.MaxBuildLimit(c.Int64("max-build-limit")), middleware.WebhookValidation(!c.Bool("vela-disable-webhook-validation")), middleware.SecureCookie(c.Bool("vela-enable-secure-cookie")), @@ -304,6 +305,23 @@ func server(c *cli.Context) error { return err }) + // spawn go routine for cleaning up pending approval builds + g.Go(func() error { + logrus.Info("starting pending approval cleanup routine") + + ticker := time.NewTicker(1 * time.Hour) + defer ticker.Stop() + + for { + err := cleanupPendingApproval(c, database) + if err != nil { + logrus.WithError(err).Warn("unable to cleanup pending approval builds") + } + + <-ticker.C + } + }) + // spawn goroutine for starting the scheduler g.Go(func() error { logrus.Info("starting scheduler") diff --git a/compiler/native/environment_test.go b/compiler/native/environment_test.go index 24f319a08..ad57496aa 100644 --- a/compiler/native/environment_test.go +++ b/compiler/native/environment_test.go @@ -104,105 +104,106 @@ func TestNative_EnvironmentSteps(t *testing.T) { Name: str, Pull: "always", Environment: raw.StringSliceMap{ - "BUILD_AUTHOR": "", - "BUILD_AUTHOR_EMAIL": "", - "BUILD_BASE_REF": "", - "BUILD_BRANCH": "", - "BUILD_CHANNEL": "TODO", - "BUILD_CLONE": "", - "BUILD_COMMIT": "", - "BUILD_CREATED": "0", - "BUILD_ENQUEUED": "0", - "BUILD_EVENT": "", - "BUILD_HOST": "", - "BUILD_LINK": "", - "BUILD_MESSAGE": "", - "BUILD_NUMBER": "0", - "BUILD_PARENT": "0", - "BUILD_REF": "", - "BUILD_SENDER": "", - "BUILD_SOURCE": "", - "BUILD_STARTED": "0", - "BUILD_STATUS": "", - "BUILD_TITLE": "", - "BUILD_WORKSPACE": "/vela/src", - "CI": "true", - "REPOSITORY_ACTIVE": "false", - "REPOSITORY_ALLOW_EVENTS": "", - "REPOSITORY_BRANCH": "", - "REPOSITORY_CLONE": "", - "REPOSITORY_FULL_NAME": "", - "REPOSITORY_LINK": "", - "REPOSITORY_NAME": "", - "REPOSITORY_ORG": "", - "REPOSITORY_PRIVATE": "false", - "REPOSITORY_TIMEOUT": "0", - "REPOSITORY_TRUSTED": "false", - "REPOSITORY_VISIBILITY": "", - "VELA": "true", - "VELA_ADDR": "TODO", - "VELA_BUILD_APPROVED_AT": "0", - "VELA_BUILD_APPROVED_BY": "", - "VELA_BUILD_AUTHOR": "", - "VELA_BUILD_AUTHOR_EMAIL": "", - "VELA_BUILD_BASE_REF": "", - "VELA_BUILD_BRANCH": "", - "VELA_BUILD_CHANNEL": "TODO", - "VELA_BUILD_CLONE": "", - "VELA_BUILD_COMMIT": "", - "VELA_BUILD_CREATED": "0", - "VELA_BUILD_DISTRIBUTION": "", - "VELA_BUILD_ENQUEUED": "0", - "VELA_BUILD_EVENT": "", - "VELA_BUILD_EVENT_ACTION": "", - "VELA_BUILD_HOST": "", - "VELA_BUILD_LINK": "", - "VELA_BUILD_MESSAGE": "", - "VELA_BUILD_NUMBER": "0", - "VELA_BUILD_PARENT": "0", - "VELA_BUILD_REF": "", - "VELA_BUILD_RUNTIME": "", - "VELA_BUILD_SENDER": "", - "VELA_BUILD_SENDER_SCM_ID": "", - "VELA_BUILD_SOURCE": "", - "VELA_BUILD_STARTED": "0", - "VELA_BUILD_STATUS": "", - "VELA_BUILD_TITLE": "", - "VELA_BUILD_WORKSPACE": "/vela/src", - "VELA_CHANNEL": "TODO", - "VELA_DATABASE": "TODO", - "VELA_DISTRIBUTION": "TODO", - "VELA_HOST": "TODO", - "VELA_NETRC_MACHINE": "TODO", - "VELA_NETRC_PASSWORD": "TODO", - "VELA_NETRC_USERNAME": "x-oauth-basic", - "VELA_QUEUE": "TODO", - "VELA_REPO_ACTIVE": "false", - "VELA_REPO_ALLOW_EVENTS": "", - "VELA_REPO_APPROVE_BUILD": "", - "VELA_REPO_BRANCH": "", - "VELA_REPO_TOPICS": "", - "VELA_REPO_BUILD_LIMIT": "0", - "VELA_REPO_CLONE": "", - "VELA_REPO_FULL_NAME": "", - "VELA_REPO_LINK": "", - "VELA_REPO_NAME": "", - "VELA_REPO_ORG": "", - "VELA_REPO_OWNER": "", - "VELA_REPO_PIPELINE_TYPE": "", - "VELA_REPO_PRIVATE": "false", - "VELA_REPO_TIMEOUT": "0", - "VELA_REPO_TRUSTED": "false", - "VELA_REPO_VISIBILITY": "", - "VELA_RUNTIME": "TODO", - "VELA_SOURCE": "TODO", - "VELA_USER_ACTIVE": "false", - "VELA_USER_ADMIN": "false", - "VELA_USER_FAVORITES": "[]", - "VELA_USER_NAME": "", - "VELA_VERSION": "TODO", - "VELA_WORKSPACE": "/vela/src", - "HELLO": "Hello, Stage Message", + "BUILD_AUTHOR": "", + "BUILD_AUTHOR_EMAIL": "", + "BUILD_BASE_REF": "", + "BUILD_BRANCH": "", + "BUILD_CHANNEL": "TODO", + "BUILD_CLONE": "", + "BUILD_COMMIT": "", + "BUILD_CREATED": "0", + "BUILD_ENQUEUED": "0", + "BUILD_EVENT": "", + "BUILD_HOST": "", + "BUILD_LINK": "", + "BUILD_MESSAGE": "", + "BUILD_NUMBER": "0", + "BUILD_PARENT": "0", + "BUILD_REF": "", + "BUILD_SENDER": "", + "BUILD_SOURCE": "", + "BUILD_STARTED": "0", + "BUILD_STATUS": "", + "BUILD_TITLE": "", + "BUILD_WORKSPACE": "/vela/src", + "CI": "true", + "REPOSITORY_ACTIVE": "false", + "REPOSITORY_ALLOW_EVENTS": "", + "REPOSITORY_BRANCH": "", + "REPOSITORY_CLONE": "", + "REPOSITORY_FULL_NAME": "", + "REPOSITORY_LINK": "", + "REPOSITORY_NAME": "", + "REPOSITORY_ORG": "", + "REPOSITORY_PRIVATE": "false", + "REPOSITORY_TIMEOUT": "0", + "REPOSITORY_TRUSTED": "false", + "REPOSITORY_VISIBILITY": "", + "VELA": "true", + "VELA_ADDR": "TODO", + "VELA_BUILD_APPROVED_AT": "0", + "VELA_BUILD_APPROVED_BY": "", + "VELA_BUILD_AUTHOR": "", + "VELA_BUILD_AUTHOR_EMAIL": "", + "VELA_BUILD_BASE_REF": "", + "VELA_BUILD_BRANCH": "", + "VELA_BUILD_CHANNEL": "TODO", + "VELA_BUILD_CLONE": "", + "VELA_BUILD_COMMIT": "", + "VELA_BUILD_CREATED": "0", + "VELA_BUILD_DISTRIBUTION": "", + "VELA_BUILD_ENQUEUED": "0", + "VELA_BUILD_EVENT": "", + "VELA_BUILD_EVENT_ACTION": "", + "VELA_BUILD_HOST": "", + "VELA_BUILD_LINK": "", + "VELA_BUILD_MESSAGE": "", + "VELA_BUILD_NUMBER": "0", + "VELA_BUILD_PARENT": "0", + "VELA_BUILD_REF": "", + "VELA_BUILD_RUNTIME": "", + "VELA_BUILD_SENDER": "", + "VELA_BUILD_SENDER_SCM_ID": "", + "VELA_BUILD_SOURCE": "", + "VELA_BUILD_STARTED": "0", + "VELA_BUILD_STATUS": "", + "VELA_BUILD_TITLE": "", + "VELA_BUILD_WORKSPACE": "/vela/src", + "VELA_CHANNEL": "TODO", + "VELA_DATABASE": "TODO", + "VELA_DISTRIBUTION": "TODO", + "VELA_HOST": "TODO", + "VELA_NETRC_MACHINE": "TODO", + "VELA_NETRC_PASSWORD": "TODO", + "VELA_NETRC_USERNAME": "x-oauth-basic", + "VELA_QUEUE": "TODO", + "VELA_REPO_ACTIVE": "false", + "VELA_REPO_ALLOW_EVENTS": "", + "VELA_REPO_APPROVAL_TIMEOUT": "0", + "VELA_REPO_APPROVE_BUILD": "", + "VELA_REPO_BRANCH": "", + "VELA_REPO_TOPICS": "", + "VELA_REPO_BUILD_LIMIT": "0", + "VELA_REPO_CLONE": "", + "VELA_REPO_FULL_NAME": "", + "VELA_REPO_LINK": "", + "VELA_REPO_NAME": "", + "VELA_REPO_ORG": "", + "VELA_REPO_OWNER": "", + "VELA_REPO_PIPELINE_TYPE": "", + "VELA_REPO_PRIVATE": "false", + "VELA_REPO_TIMEOUT": "0", + "VELA_REPO_TRUSTED": "false", + "VELA_REPO_VISIBILITY": "", + "VELA_RUNTIME": "TODO", + "VELA_SOURCE": "TODO", + "VELA_USER_ACTIVE": "false", + "VELA_USER_ADMIN": "false", + "VELA_USER_FAVORITES": "[]", + "VELA_USER_NAME": "", + "VELA_VERSION": "TODO", + "VELA_WORKSPACE": "/vela/src", + "HELLO": "Hello, Stage Message", }, }, } @@ -281,105 +282,106 @@ func TestNative_EnvironmentServices(t *testing.T) { Name: str, Pull: "always", Environment: raw.StringSliceMap{ - "BUILD_AUTHOR": "", - "BUILD_AUTHOR_EMAIL": "", - "BUILD_BASE_REF": "", - "BUILD_BRANCH": "", - "BUILD_CHANNEL": "TODO", - "BUILD_CLONE": "", - "BUILD_COMMIT": "", - "BUILD_CREATED": "0", - "BUILD_ENQUEUED": "0", - "BUILD_EVENT": "", - "BUILD_HOST": "", - "BUILD_LINK": "", - "BUILD_MESSAGE": "", - "BUILD_NUMBER": "0", - "BUILD_PARENT": "0", - "BUILD_REF": "", - "BUILD_SENDER": "", - "BUILD_SOURCE": "", - "BUILD_STARTED": "0", - "BUILD_STATUS": "", - "BUILD_TITLE": "", - "BUILD_WORKSPACE": "/vela/src", - "CI": "true", - "REPOSITORY_ACTIVE": "false", - "REPOSITORY_ALLOW_EVENTS": "", - "REPOSITORY_BRANCH": "", - "REPOSITORY_CLONE": "", - "REPOSITORY_FULL_NAME": "", - "REPOSITORY_LINK": "", - "REPOSITORY_NAME": "", - "REPOSITORY_ORG": "", - "REPOSITORY_PRIVATE": "false", - "REPOSITORY_TIMEOUT": "0", - "REPOSITORY_TRUSTED": "false", - "REPOSITORY_VISIBILITY": "", - "VELA": "true", - "VELA_ADDR": "TODO", - "VELA_BUILD_APPROVED_AT": "0", - "VELA_BUILD_APPROVED_BY": "", - "VELA_BUILD_AUTHOR": "", - "VELA_BUILD_AUTHOR_EMAIL": "", - "VELA_BUILD_BASE_REF": "", - "VELA_BUILD_BRANCH": "", - "VELA_BUILD_CHANNEL": "TODO", - "VELA_BUILD_CLONE": "", - "VELA_BUILD_COMMIT": "", - "VELA_BUILD_CREATED": "0", - "VELA_BUILD_DISTRIBUTION": "", - "VELA_BUILD_ENQUEUED": "0", - "VELA_BUILD_EVENT": "", - "VELA_BUILD_EVENT_ACTION": "", - "VELA_BUILD_HOST": "", - "VELA_BUILD_LINK": "", - "VELA_BUILD_MESSAGE": "", - "VELA_BUILD_NUMBER": "0", - "VELA_BUILD_PARENT": "0", - "VELA_BUILD_REF": "", - "VELA_BUILD_RUNTIME": "", - "VELA_BUILD_SENDER": "", - "VELA_BUILD_SENDER_SCM_ID": "", - "VELA_BUILD_SOURCE": "", - "VELA_BUILD_STARTED": "0", - "VELA_BUILD_STATUS": "", - "VELA_BUILD_TITLE": "", - "VELA_BUILD_WORKSPACE": "/vela/src", - "VELA_CHANNEL": "TODO", - "VELA_DATABASE": "TODO", - "VELA_DISTRIBUTION": "TODO", - "VELA_HOST": "TODO", - "VELA_NETRC_MACHINE": "TODO", - "VELA_NETRC_PASSWORD": "TODO", - "VELA_NETRC_USERNAME": "x-oauth-basic", - "VELA_QUEUE": "TODO", - "VELA_REPO_ACTIVE": "false", - "VELA_REPO_ALLOW_EVENTS": "", - "VELA_REPO_APPROVE_BUILD": "", - "VELA_REPO_BRANCH": "", - "VELA_REPO_TOPICS": "", - "VELA_REPO_BUILD_LIMIT": "0", - "VELA_REPO_CLONE": "", - "VELA_REPO_FULL_NAME": "", - "VELA_REPO_LINK": "", - "VELA_REPO_NAME": "", - "VELA_REPO_ORG": "", - "VELA_REPO_OWNER": "", - "VELA_REPO_PIPELINE_TYPE": "", - "VELA_REPO_PRIVATE": "false", - "VELA_REPO_TIMEOUT": "0", - "VELA_REPO_TRUSTED": "false", - "VELA_REPO_VISIBILITY": "", - "VELA_RUNTIME": "TODO", - "VELA_SOURCE": "TODO", - "VELA_USER_ACTIVE": "false", - "VELA_USER_ADMIN": "false", - "VELA_USER_FAVORITES": "[]", - "VELA_USER_NAME": "", - "VELA_VERSION": "TODO", - "VELA_WORKSPACE": "/vela/src", - "HELLO": "Hello, Global Message", + "BUILD_AUTHOR": "", + "BUILD_AUTHOR_EMAIL": "", + "BUILD_BASE_REF": "", + "BUILD_BRANCH": "", + "BUILD_CHANNEL": "TODO", + "BUILD_CLONE": "", + "BUILD_COMMIT": "", + "BUILD_CREATED": "0", + "BUILD_ENQUEUED": "0", + "BUILD_EVENT": "", + "BUILD_HOST": "", + "BUILD_LINK": "", + "BUILD_MESSAGE": "", + "BUILD_NUMBER": "0", + "BUILD_PARENT": "0", + "BUILD_REF": "", + "BUILD_SENDER": "", + "BUILD_SOURCE": "", + "BUILD_STARTED": "0", + "BUILD_STATUS": "", + "BUILD_TITLE": "", + "BUILD_WORKSPACE": "/vela/src", + "CI": "true", + "REPOSITORY_ACTIVE": "false", + "REPOSITORY_ALLOW_EVENTS": "", + "REPOSITORY_BRANCH": "", + "REPOSITORY_CLONE": "", + "REPOSITORY_FULL_NAME": "", + "REPOSITORY_LINK": "", + "REPOSITORY_NAME": "", + "REPOSITORY_ORG": "", + "REPOSITORY_PRIVATE": "false", + "REPOSITORY_TIMEOUT": "0", + "REPOSITORY_TRUSTED": "false", + "REPOSITORY_VISIBILITY": "", + "VELA": "true", + "VELA_ADDR": "TODO", + "VELA_BUILD_APPROVED_AT": "0", + "VELA_BUILD_APPROVED_BY": "", + "VELA_BUILD_AUTHOR": "", + "VELA_BUILD_AUTHOR_EMAIL": "", + "VELA_BUILD_BASE_REF": "", + "VELA_BUILD_BRANCH": "", + "VELA_BUILD_CHANNEL": "TODO", + "VELA_BUILD_CLONE": "", + "VELA_BUILD_COMMIT": "", + "VELA_BUILD_CREATED": "0", + "VELA_BUILD_DISTRIBUTION": "", + "VELA_BUILD_ENQUEUED": "0", + "VELA_BUILD_EVENT": "", + "VELA_BUILD_EVENT_ACTION": "", + "VELA_BUILD_HOST": "", + "VELA_BUILD_LINK": "", + "VELA_BUILD_MESSAGE": "", + "VELA_BUILD_NUMBER": "0", + "VELA_BUILD_PARENT": "0", + "VELA_BUILD_REF": "", + "VELA_BUILD_RUNTIME": "", + "VELA_BUILD_SENDER": "", + "VELA_BUILD_SENDER_SCM_ID": "", + "VELA_BUILD_SOURCE": "", + "VELA_BUILD_STARTED": "0", + "VELA_BUILD_STATUS": "", + "VELA_BUILD_TITLE": "", + "VELA_BUILD_WORKSPACE": "/vela/src", + "VELA_CHANNEL": "TODO", + "VELA_DATABASE": "TODO", + "VELA_DISTRIBUTION": "TODO", + "VELA_HOST": "TODO", + "VELA_NETRC_MACHINE": "TODO", + "VELA_NETRC_PASSWORD": "TODO", + "VELA_NETRC_USERNAME": "x-oauth-basic", + "VELA_QUEUE": "TODO", + "VELA_REPO_ACTIVE": "false", + "VELA_REPO_ALLOW_EVENTS": "", + "VELA_REPO_APPROVAL_TIMEOUT": "0", + "VELA_REPO_APPROVE_BUILD": "", + "VELA_REPO_BRANCH": "", + "VELA_REPO_TOPICS": "", + "VELA_REPO_BUILD_LIMIT": "0", + "VELA_REPO_CLONE": "", + "VELA_REPO_FULL_NAME": "", + "VELA_REPO_LINK": "", + "VELA_REPO_NAME": "", + "VELA_REPO_ORG": "", + "VELA_REPO_OWNER": "", + "VELA_REPO_PIPELINE_TYPE": "", + "VELA_REPO_PRIVATE": "false", + "VELA_REPO_TIMEOUT": "0", + "VELA_REPO_TRUSTED": "false", + "VELA_REPO_VISIBILITY": "", + "VELA_RUNTIME": "TODO", + "VELA_SOURCE": "TODO", + "VELA_USER_ACTIVE": "false", + "VELA_USER_ADMIN": "false", + "VELA_USER_FAVORITES": "[]", + "VELA_USER_NAME": "", + "VELA_VERSION": "TODO", + "VELA_WORKSPACE": "/vela/src", + "HELLO": "Hello, Global Message", }, }, } @@ -439,106 +441,107 @@ func TestNative_EnvironmentSecrets(t *testing.T) { "foo": "bar", }, Environment: raw.StringSliceMap{ - "BUILD_AUTHOR": "", - "BUILD_AUTHOR_EMAIL": "", - "BUILD_BASE_REF": "", - "BUILD_BRANCH": "", - "BUILD_CHANNEL": "TODO", - "BUILD_CLONE": "", - "BUILD_COMMIT": "", - "BUILD_CREATED": "0", - "BUILD_ENQUEUED": "0", - "BUILD_EVENT": "", - "BUILD_HOST": "", - "BUILD_LINK": "", - "BUILD_MESSAGE": "", - "BUILD_NUMBER": "0", - "BUILD_PARENT": "0", - "BUILD_REF": "", - "BUILD_SENDER": "", - "BUILD_SOURCE": "", - "BUILD_STARTED": "0", - "BUILD_STATUS": "", - "BUILD_TITLE": "", - "BUILD_WORKSPACE": "/vela/src", - "CI": "true", - "PARAMETER_FOO": "bar", - "REPOSITORY_ACTIVE": "false", - "REPOSITORY_ALLOW_EVENTS": "", - "REPOSITORY_BRANCH": "", - "REPOSITORY_CLONE": "", - "REPOSITORY_FULL_NAME": "", - "REPOSITORY_LINK": "", - "REPOSITORY_NAME": "", - "REPOSITORY_ORG": "", - "REPOSITORY_PRIVATE": "false", - "REPOSITORY_TIMEOUT": "0", - "REPOSITORY_TRUSTED": "false", - "REPOSITORY_VISIBILITY": "", - "VELA": "true", - "VELA_ADDR": "TODO", - "VELA_BUILD_APPROVED_AT": "0", - "VELA_BUILD_APPROVED_BY": "", - "VELA_BUILD_AUTHOR": "", - "VELA_BUILD_AUTHOR_EMAIL": "", - "VELA_BUILD_BASE_REF": "", - "VELA_BUILD_BRANCH": "", - "VELA_BUILD_CHANNEL": "TODO", - "VELA_BUILD_CLONE": "", - "VELA_BUILD_COMMIT": "", - "VELA_BUILD_CREATED": "0", - "VELA_BUILD_DISTRIBUTION": "", - "VELA_BUILD_ENQUEUED": "0", - "VELA_BUILD_EVENT": "", - "VELA_BUILD_EVENT_ACTION": "", - "VELA_BUILD_HOST": "", - "VELA_BUILD_LINK": "", - "VELA_BUILD_MESSAGE": "", - "VELA_BUILD_NUMBER": "0", - "VELA_BUILD_PARENT": "0", - "VELA_BUILD_REF": "", - "VELA_BUILD_RUNTIME": "", - "VELA_BUILD_SENDER": "", - "VELA_BUILD_SENDER_SCM_ID": "", - "VELA_BUILD_SOURCE": "", - "VELA_BUILD_STARTED": "0", - "VELA_BUILD_STATUS": "", - "VELA_BUILD_TITLE": "", - "VELA_BUILD_WORKSPACE": "/vela/src", - "VELA_CHANNEL": "TODO", - "VELA_DATABASE": "TODO", - "VELA_DISTRIBUTION": "TODO", - "VELA_HOST": "TODO", - "VELA_NETRC_MACHINE": "TODO", - "VELA_NETRC_PASSWORD": "TODO", - "VELA_NETRC_USERNAME": "x-oauth-basic", - "VELA_QUEUE": "TODO", - "VELA_REPO_ACTIVE": "false", - "VELA_REPO_ALLOW_EVENTS": "", - "VELA_REPO_APPROVE_BUILD": "", - "VELA_REPO_BRANCH": "", - "VELA_REPO_TOPICS": "", - "VELA_REPO_BUILD_LIMIT": "0", - "VELA_REPO_CLONE": "", - "VELA_REPO_FULL_NAME": "", - "VELA_REPO_LINK": "", - "VELA_REPO_NAME": "", - "VELA_REPO_ORG": "", - "VELA_REPO_OWNER": "", - "VELA_REPO_PIPELINE_TYPE": "", - "VELA_REPO_PRIVATE": "false", - "VELA_REPO_TIMEOUT": "0", - "VELA_REPO_TRUSTED": "false", - "VELA_REPO_VISIBILITY": "", - "VELA_RUNTIME": "TODO", - "VELA_SOURCE": "TODO", - "VELA_USER_ACTIVE": "false", - "VELA_USER_ADMIN": "false", - "VELA_USER_FAVORITES": "[]", - "VELA_USER_NAME": "", - "VELA_VERSION": "TODO", - "VELA_WORKSPACE": "/vela/src", - "HELLO": "Hello, Global Message", + "BUILD_AUTHOR": "", + "BUILD_AUTHOR_EMAIL": "", + "BUILD_BASE_REF": "", + "BUILD_BRANCH": "", + "BUILD_CHANNEL": "TODO", + "BUILD_CLONE": "", + "BUILD_COMMIT": "", + "BUILD_CREATED": "0", + "BUILD_ENQUEUED": "0", + "BUILD_EVENT": "", + "BUILD_HOST": "", + "BUILD_LINK": "", + "BUILD_MESSAGE": "", + "BUILD_NUMBER": "0", + "BUILD_PARENT": "0", + "BUILD_REF": "", + "BUILD_SENDER": "", + "BUILD_SOURCE": "", + "BUILD_STARTED": "0", + "BUILD_STATUS": "", + "BUILD_TITLE": "", + "BUILD_WORKSPACE": "/vela/src", + "CI": "true", + "PARAMETER_FOO": "bar", + "REPOSITORY_ACTIVE": "false", + "REPOSITORY_ALLOW_EVENTS": "", + "REPOSITORY_BRANCH": "", + "REPOSITORY_CLONE": "", + "REPOSITORY_FULL_NAME": "", + "REPOSITORY_LINK": "", + "REPOSITORY_NAME": "", + "REPOSITORY_ORG": "", + "REPOSITORY_PRIVATE": "false", + "REPOSITORY_TIMEOUT": "0", + "REPOSITORY_TRUSTED": "false", + "REPOSITORY_VISIBILITY": "", + "VELA": "true", + "VELA_ADDR": "TODO", + "VELA_BUILD_APPROVED_AT": "0", + "VELA_BUILD_APPROVED_BY": "", + "VELA_BUILD_AUTHOR": "", + "VELA_BUILD_AUTHOR_EMAIL": "", + "VELA_BUILD_BASE_REF": "", + "VELA_BUILD_BRANCH": "", + "VELA_BUILD_CHANNEL": "TODO", + "VELA_BUILD_CLONE": "", + "VELA_BUILD_COMMIT": "", + "VELA_BUILD_CREATED": "0", + "VELA_BUILD_DISTRIBUTION": "", + "VELA_BUILD_ENQUEUED": "0", + "VELA_BUILD_EVENT": "", + "VELA_BUILD_EVENT_ACTION": "", + "VELA_BUILD_HOST": "", + "VELA_BUILD_LINK": "", + "VELA_BUILD_MESSAGE": "", + "VELA_BUILD_NUMBER": "0", + "VELA_BUILD_PARENT": "0", + "VELA_BUILD_REF": "", + "VELA_BUILD_RUNTIME": "", + "VELA_BUILD_SENDER": "", + "VELA_BUILD_SENDER_SCM_ID": "", + "VELA_BUILD_SOURCE": "", + "VELA_BUILD_STARTED": "0", + "VELA_BUILD_STATUS": "", + "VELA_BUILD_TITLE": "", + "VELA_BUILD_WORKSPACE": "/vela/src", + "VELA_CHANNEL": "TODO", + "VELA_DATABASE": "TODO", + "VELA_DISTRIBUTION": "TODO", + "VELA_HOST": "TODO", + "VELA_NETRC_MACHINE": "TODO", + "VELA_NETRC_PASSWORD": "TODO", + "VELA_NETRC_USERNAME": "x-oauth-basic", + "VELA_QUEUE": "TODO", + "VELA_REPO_ACTIVE": "false", + "VELA_REPO_ALLOW_EVENTS": "", + "VELA_REPO_APPROVAL_TIMEOUT": "0", + "VELA_REPO_APPROVE_BUILD": "", + "VELA_REPO_BRANCH": "", + "VELA_REPO_TOPICS": "", + "VELA_REPO_BUILD_LIMIT": "0", + "VELA_REPO_CLONE": "", + "VELA_REPO_FULL_NAME": "", + "VELA_REPO_LINK": "", + "VELA_REPO_NAME": "", + "VELA_REPO_ORG": "", + "VELA_REPO_OWNER": "", + "VELA_REPO_PIPELINE_TYPE": "", + "VELA_REPO_PRIVATE": "false", + "VELA_REPO_TIMEOUT": "0", + "VELA_REPO_TRUSTED": "false", + "VELA_REPO_VISIBILITY": "", + "VELA_RUNTIME": "TODO", + "VELA_SOURCE": "TODO", + "VELA_USER_ACTIVE": "false", + "VELA_USER_ADMIN": "false", + "VELA_USER_FAVORITES": "[]", + "VELA_USER_NAME": "", + "VELA_VERSION": "TODO", + "VELA_WORKSPACE": "/vela/src", + "HELLO": "Hello, Global Message", }, }, }, @@ -600,7 +603,7 @@ func TestNative_environment(t *testing.T) { r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "push", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "foo", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "push", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "foo", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "push", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "foo", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "push", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "foo", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // tag { @@ -610,7 +613,7 @@ func TestNative_environment(t *testing.T) { r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // pull_request { @@ -620,7 +623,7 @@ func TestNative_environment(t *testing.T) { r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // deployment { @@ -630,7 +633,7 @@ func TestNative_environment(t *testing.T) { r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, // netrc { @@ -640,7 +643,7 @@ func TestNative_environment(t *testing.T) { r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: nil, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "TODO", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "TODO", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, } @@ -733,14 +736,14 @@ func Test_client_EnvironmentBuild(t *testing.T) { repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "push", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "foo", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "push", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "foo", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}}, + }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "push", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "foo", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "push", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "foo", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}}, {"tag", fields{ build: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &tag, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &tagref, BaseRef: &str}, metadata: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "tag", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/tags/1", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TAG": "1", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "tag", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/tags/1", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TAG": "1", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, {"pull_request", fields{ build: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &pull, EventAction: &pullact, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &str, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Fork: &booL, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, @@ -748,7 +751,7 @@ func Test_client_EnvironmentBuild(t *testing.T) { repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "pull_request", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_PULL_REQUEST_NUMBER": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "pull_request", "VELA_BUILD_EVENT_ACTION": "opened", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_PULL_REQUEST": "1", "VELA_PULL_REQUEST_FORK": "false", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_PULL_REQUEST": "1", "VELA_PULL_REQUEST_SOURCE": "", "VELA_PULL_REQUEST_TARGET": "foo", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, {"deployment", fields{ build: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &deploy, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &target, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, @@ -756,7 +759,7 @@ func Test_client_EnvironmentBuild(t *testing.T) { repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, user: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, netrc: &netrc, - }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVAL_TIMEOUT": "0", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_OWNER": "foo", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, } for _, tt := range tests { diff --git a/constants/limit.go b/constants/limit.go index 4321c4e79..e3806a153 100644 --- a/constants/limit.go +++ b/constants/limit.go @@ -22,6 +22,15 @@ const ( // BuildTimeoutDefault defines the default value in minutes for repo build timeout. BuildTimeoutDefault = 30 + // ApprovalTimeoutMin defines the minimum value in days for the repo build approval timeout. + ApprovalTimeoutMin = 1 + + // ApprovalTimeoutMax defines the maximum value in days for the repo build approval timeout. + ApprovalTimeoutMax = 60 + + // ApprovalTimeout defines the default value in days for repo build approval timeout. + ApprovalTimeoutDefault = 7 + // FavoritesMaxSize defines the maximum size in characters for user favorites. FavoritesMaxSize = 5000 diff --git a/database/build/interface.go b/database/build/interface.go index fa330e00c..c723d838e 100644 --- a/database/build/interface.go +++ b/database/build/interface.go @@ -60,6 +60,8 @@ type BuildInterface interface { ListPendingAndRunningBuilds(context.Context, string) ([]*api.QueueBuild, error) // ListPendingAndRunningBuildsForRepo defines a function that gets a list of pending and running builds for a repo. ListPendingAndRunningBuildsForRepo(context.Context, *api.Repo) ([]*api.Build, error) + // ListPendingApprovalBuilds defines a function that gets a list of pending approval builds that were created before a given time. + ListPendingApprovalBuilds(context.Context, string) ([]*api.Build, error) // UpdateBuild defines a function that updates an existing build. UpdateBuild(context.Context, *api.Build) (*api.Build, error) } diff --git a/database/build/list_pending_approval.go b/database/build/list_pending_approval.go new file mode 100644 index 000000000..d5c95245e --- /dev/null +++ b/database/build/list_pending_approval.go @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 + +package build + +import ( + "context" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" +) + +// ListPendingAndRunningBuilds gets a list of all pending and running builds in the provided timeframe from the database. +func (e *engine) ListPendingApprovalBuilds(ctx context.Context, before string) ([]*api.Build, error) { + e.logger.Trace("listing all pending and running builds") + + // variables to store query results and return value + b := new([]types.Build) + builds := []*api.Build{} + + // send query to the database and store result in variable + err := e.client. + WithContext(ctx). + Preload("Repo"). + Table(constants.TableBuild). + Where("builds.created < ?", before). + Where("builds.status = 'pending approval'"). + Find(&b). + Error + if err != nil { + return nil, err + } + + // iterate through all query results + for _, build := range *b { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := build + + err = tmp.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo: %v", err) + } + + builds = append(builds, tmp.ToAPI()) + } + + return builds, nil +} diff --git a/database/build/list_pending_approval_test.go b/database/build/list_pending_approval_test.go new file mode 100644 index 000000000..2e37aa612 --- /dev/null +++ b/database/build/list_pending_approval_test.go @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: Apache-2.0 + +package build + +import ( + "context" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/google/go-cmp/cmp" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/testutils" + "github.com/go-vela/server/database/types" +) + +func TestBuild_Engine_ListPendingApprovalBuilds(t *testing.T) { + // setup types + _repoOwner := new(api.User) + _repoOwner.SetID(1) + + _repo := testutils.APIRepo() + _repo.SetID(1) + _repo.SetOwner(_repoOwner) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + + _buildOne := testutils.APIBuild() + _buildOne.SetID(1) + _buildOne.SetRepo(_repo) + _buildOne.SetNumber(1) + _buildOne.SetStatus("pending approval") + _buildOne.SetCreated(1) + _buildOne.SetDeployPayload(nil) + + _buildTwo := testutils.APIBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepo(_repo) + _buildTwo.SetNumber(2) + _buildTwo.SetStatus("pending approval") + _buildTwo.SetCreated(3) + _buildTwo.SetDeployPayload(nil) + + _buildThree := testutils.APIBuild() + _buildThree.SetID(3) + _buildThree.SetRepo(_repo) + _buildThree.SetNumber(3) + _buildThree.SetStatus("pending approval") + _buildThree.SetCreated(6) + _buildThree.SetDeployPayload(nil) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected name query result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "pending approval", "", 0, 1, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(2, 1, nil, 2, 0, "", "", "pending approval", "", 0, 3, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + _repoRows := sqlmock.NewRows( + []string{"id", "user_id", "hash", "org", "name", "full_name", "link", "clone", "branch", "topics", "build_limit", "timeout", "counter", "visibility", "private", "trusted", "active", "allow_events", "pipeline_type", "previous_name", "approve_build"}). + AddRow(1, 1, "baz", "foo", "bar", "foo/bar", "", "", "", nil, 0, 0, 0, "public", false, false, false, 0, "", "", "") + + // ensure the mock expects the name query + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE builds.created < $1 AND builds.status = 'pending approval'`).WithArgs("5").WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "repos" WHERE "repos"."id" = $1`).WithArgs(1).WillReturnRows(_repoRows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateBuild(context.TODO(), _buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + _, err = _sqlite.CreateBuild(context.TODO(), _buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + _, err = _sqlite.CreateBuild(context.TODO(), _buildThree) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.client.AutoMigrate(&types.Repo{}) + if err != nil { + t.Errorf("unable to create build table for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(types.RepoFromAPI(_repo)).Error + if err != nil { + t.Errorf("unable to create test user for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want []*api.Build + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: []*api.Build{_buildOne, _buildTwo}, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: []*api.Build{_buildOne, _buildTwo}, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.ListPendingApprovalBuilds(context.TODO(), "5") + + if test.failure { + if err == nil { + t.Errorf("ListPendingApprovalBuilds for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListPendingApprovalBuilds for %s returned err: %v", test.name, err) + } + + if diff := cmp.Diff(got, test.want); diff != "" { + t.Errorf("ListPendingApprovalBuilds for %s (-got +want): %s", test.name, diff) + } + }) + } +} diff --git a/database/integration_test.go b/database/integration_test.go index 9d6258fce..a0f1ad90c 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -383,10 +383,10 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { methods["CleanBuilds"] = true // update the builds - for _, build := range resources.Builds { + for i, build := range resources.Builds { prevStatus := build.GetStatus() - build.SetStatus("success") + build.SetStatus("pending approval") _, err = db.UpdateBuild(context.TODO(), build) if err != nil { t.Errorf("unable to update build %d: %v", build.GetID(), err) @@ -401,10 +401,22 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { t.Errorf("GetBuild() mismatch (-want +got):\n%s", diff) } + if i == 1 { + pABuilds, err := db.ListPendingApprovalBuilds(context.TODO(), "1663474076") + if err != nil { + t.Errorf("unable to list pending approval builds: %v", err) + } + + if len(pABuilds) != 2 { + t.Errorf("ListPendingApprovalBuilds() is %v, want %v", len(pABuilds), 2) + } + } + build.SetStatus(prevStatus) } methods["UpdateBuild"] = true methods["GetBuild"] = true + methods["ListPendingApprovalBuilds"] = true // delete the builds for _, build := range resources.Builds { @@ -2491,6 +2503,7 @@ func newResources() *Resources { repoOne.SetPreviousName("") repoOne.SetApproveBuild(constants.ApproveNever) repoOne.SetAllowEvents(api.NewEventsFromMask(1)) + repoOne.SetApprovalTimeout(7) repoOne.SetInstallID(0) repoTwo := new(api.Repo) @@ -2515,6 +2528,7 @@ func newResources() *Resources { repoTwo.SetPreviousName("") repoTwo.SetApproveBuild(constants.ApproveForkAlways) repoTwo.SetAllowEvents(api.NewEventsFromMask(1)) + repoTwo.SetApprovalTimeout(7) repoTwo.SetInstallID(0) buildOne := new(api.Build) diff --git a/database/repo/create_test.go b/database/repo/create_test.go index 939fc186b..fc0e84276 100644 --- a/database/repo/create_test.go +++ b/database/repo/create_test.go @@ -34,9 +34,9 @@ func TestRepo_Engine_CreateRepo(t *testing.T) { // ensure the mock expects the query _mock.ExpectQuery(`INSERT INTO "repos" -("user_id","hash","org","name","full_name","link","clone","branch","topics","build_limit","timeout","counter","visibility","private","trusted","active","allow_events","pipeline_type","previous_name","approve_build","install_id","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22) RETURNING "id"`). - WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, nil, "yaml", "oldName", nil, 0, 1). +("user_id","hash","org","name","full_name","link","clone","branch","topics","build_limit","timeout","counter","visibility","private","trusted","active","allow_events","pipeline_type","previous_name","approve_build","approval_timeout","install_id","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23) RETURNING "id"`). + WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, nil, "yaml", "oldName", nil, nil, 0, 1). WillReturnRows(_rows) _sqlite := testSqlite(t) diff --git a/database/repo/table.go b/database/repo/table.go index daba7a367..3ac97f633 100644 --- a/database/repo/table.go +++ b/database/repo/table.go @@ -14,28 +14,29 @@ const ( CREATE TABLE IF NOT EXISTS repos ( - id SERIAL PRIMARY KEY, - user_id INTEGER, - hash VARCHAR(500), - org VARCHAR(250), - name VARCHAR(250), - full_name VARCHAR(500), - link VARCHAR(1000), - clone VARCHAR(1000), - branch VARCHAR(250), - topics VARCHAR(1020), - build_limit INTEGER, - timeout INTEGER, - counter INTEGER, - visibility TEXT, - private BOOLEAN, - trusted BOOLEAN, - active BOOLEAN, - allow_events INTEGER, - pipeline_type TEXT, - previous_name VARCHAR(100), - approve_build VARCHAR(20), - install_id INTEGER, + id SERIAL PRIMARY KEY, + user_id INTEGER, + hash VARCHAR(500), + org VARCHAR(250), + name VARCHAR(250), + full_name VARCHAR(500), + link VARCHAR(1000), + clone VARCHAR(1000), + branch VARCHAR(250), + topics VARCHAR(1020), + build_limit INTEGER, + timeout INTEGER, + counter INTEGER, + visibility TEXT, + private BOOLEAN, + trusted BOOLEAN, + active BOOLEAN, + allow_events INTEGER, + pipeline_type TEXT, + previous_name VARCHAR(100), + approve_build VARCHAR(20), + approval_timeout INTEGER, + install_id INTEGER, UNIQUE(full_name) ); ` @@ -45,28 +46,29 @@ repos ( CREATE TABLE IF NOT EXISTS repos ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER, - hash TEXT, - org TEXT, - name TEXT, - full_name TEXT, - link TEXT, - clone TEXT, - branch TEXT, - topics TEXT, - build_limit INTEGER, - timeout INTEGER, - counter INTEGER, - visibility TEXT, - private BOOLEAN, - trusted BOOLEAN, - active BOOLEAN, - allow_events INTEGER, - pipeline_type TEXT, - previous_name TEXT, - approve_build TEXT, - install_id INTEGER, + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + hash TEXT, + org TEXT, + name TEXT, + full_name TEXT, + link TEXT, + clone TEXT, + branch TEXT, + topics TEXT, + build_limit INTEGER, + timeout INTEGER, + counter INTEGER, + visibility TEXT, + private BOOLEAN, + trusted BOOLEAN, + active BOOLEAN, + allow_events INTEGER, + pipeline_type TEXT, + previous_name TEXT, + approve_build TEXT, + approval_timeout INTEGER, + install_id INTEGER, UNIQUE(full_name) ); ` diff --git a/database/repo/update_test.go b/database/repo/update_test.go index dab936f9b..f432dd827 100644 --- a/database/repo/update_test.go +++ b/database/repo/update_test.go @@ -29,15 +29,16 @@ func TestRepo_Engine_UpdateRepo(t *testing.T) { _repo.SetApproveBuild(constants.ApproveForkAlways) _repo.SetTopics([]string{}) _repo.SetAllowEvents(api.NewEventsFromMask(1)) + _repo.SetApprovalTimeout(5) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // ensure the mock expects the query _mock.ExpectExec(`UPDATE "repos" -SET "user_id"=$1,"hash"=$2,"org"=$3,"name"=$4,"full_name"=$5,"link"=$6,"clone"=$7,"branch"=$8,"topics"=$9,"build_limit"=$10,"timeout"=$11,"counter"=$12,"visibility"=$13,"private"=$14,"trusted"=$15,"active"=$16,"allow_events"=$17,"pipeline_type"=$18,"previous_name"=$19,"approve_build"=$20,"install_id"=$21 -WHERE "id" = $22`). - WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, 1, "yaml", "oldName", constants.ApproveForkAlways, 0, 1). +SET "user_id"=$1,"hash"=$2,"org"=$3,"name"=$4,"full_name"=$5,"link"=$6,"clone"=$7,"branch"=$8,"topics"=$9,"build_limit"=$10,"timeout"=$11,"counter"=$12,"visibility"=$13,"private"=$14,"trusted"=$15,"active"=$16,"allow_events"=$17,"pipeline_type"=$18,"previous_name"=$19,"approve_build"=$20,"approval_timeout"=$21,"install_id"=$22 +WHERE "id" = $23`). + WithArgs(1, AnyArgument{}, "foo", "bar", "foo/bar", nil, nil, nil, AnyArgument{}, AnyArgument{}, AnyArgument{}, AnyArgument{}, "public", false, false, false, 1, "yaml", "oldName", constants.ApproveForkAlways, 5, 0, 1). WillReturnResult(sqlmock.NewResult(1, 1)) _sqlite := testSqlite(t) diff --git a/database/testutils/api_resources.go b/database/testutils/api_resources.go index 738602534..7689a5f4e 100644 --- a/database/testutils/api_resources.go +++ b/database/testutils/api_resources.go @@ -106,28 +106,29 @@ func APIEvents() *api.Events { func APIRepo() *api.Repo { return &api.Repo{ - ID: new(int64), - Owner: APIUser(), - BuildLimit: new(int64), - Timeout: new(int64), - Counter: new(int), - PipelineType: new(string), - Hash: new(string), - Org: new(string), - Name: new(string), - FullName: new(string), - Link: new(string), - Clone: new(string), - Branch: new(string), - Visibility: new(string), - PreviousName: new(string), - Private: new(bool), - Trusted: new(bool), - Active: new(bool), - AllowEvents: APIEvents(), - Topics: new([]string), - ApproveBuild: new(string), - InstallID: new(int64), + ID: new(int64), + Owner: APIUser(), + BuildLimit: new(int64), + Timeout: new(int64), + Counter: new(int), + PipelineType: new(string), + Hash: new(string), + Org: new(string), + Name: new(string), + FullName: new(string), + Link: new(string), + Clone: new(string), + Branch: new(string), + Visibility: new(string), + PreviousName: new(string), + Private: new(bool), + Trusted: new(bool), + Active: new(bool), + AllowEvents: APIEvents(), + Topics: new([]string), + ApproveBuild: new(string), + ApprovalTimeout: new(int64), + InstallID: new(int64), } } diff --git a/database/types/repo.go b/database/types/repo.go index f02949f2e..674dac631 100644 --- a/database/types/repo.go +++ b/database/types/repo.go @@ -46,28 +46,29 @@ var ( // Repo is the database representation of a repo. type Repo struct { - ID sql.NullInt64 `sql:"id"` - UserID sql.NullInt64 `sql:"user_id"` - Hash sql.NullString `sql:"hash"` - Org sql.NullString `sql:"org"` - Name sql.NullString `sql:"name"` - FullName sql.NullString `sql:"full_name"` - Link sql.NullString `sql:"link"` - Clone sql.NullString `sql:"clone"` - Branch sql.NullString `sql:"branch"` - Topics pq.StringArray `sql:"topics" gorm:"type:varchar(1020)"` - BuildLimit sql.NullInt64 `sql:"build_limit"` - Timeout sql.NullInt64 `sql:"timeout"` - Counter sql.NullInt64 `sql:"counter"` - Visibility sql.NullString `sql:"visibility"` - Private sql.NullBool `sql:"private"` - Trusted sql.NullBool `sql:"trusted"` - Active sql.NullBool `sql:"active"` - AllowEvents sql.NullInt64 `sql:"allow_events"` - PipelineType sql.NullString `sql:"pipeline_type"` - PreviousName sql.NullString `sql:"previous_name"` - ApproveBuild sql.NullString `sql:"approve_build"` - InstallID sql.NullInt64 `sql:"install_id"` + ID sql.NullInt64 `sql:"id"` + UserID sql.NullInt64 `sql:"user_id"` + Hash sql.NullString `sql:"hash"` + Org sql.NullString `sql:"org"` + Name sql.NullString `sql:"name"` + FullName sql.NullString `sql:"full_name"` + Link sql.NullString `sql:"link"` + Clone sql.NullString `sql:"clone"` + Branch sql.NullString `sql:"branch"` + Topics pq.StringArray `sql:"topics" gorm:"type:varchar(1020)"` + BuildLimit sql.NullInt64 `sql:"build_limit"` + Timeout sql.NullInt64 `sql:"timeout"` + Counter sql.NullInt64 `sql:"counter"` + Visibility sql.NullString `sql:"visibility"` + Private sql.NullBool `sql:"private"` + Trusted sql.NullBool `sql:"trusted"` + Active sql.NullBool `sql:"active"` + AllowEvents sql.NullInt64 `sql:"allow_events"` + PipelineType sql.NullString `sql:"pipeline_type"` + PreviousName sql.NullString `sql:"previous_name"` + ApproveBuild sql.NullString `sql:"approve_build"` + ApprovalTimeout sql.NullInt64 `sql:"approval_timeout"` + InstallID sql.NullInt64 `sql:"install_id"` Owner User `gorm:"foreignKey:UserID"` } @@ -222,6 +223,11 @@ func (r *Repo) Nullify() *Repo { r.ApproveBuild.Valid = false } + // check if the ApprovalTimeout field should be false + if r.ApprovalTimeout.Int64 == 0 { + r.ApprovalTimeout.Valid = false + } + return r } @@ -230,8 +236,16 @@ func (r *Repo) Nullify() *Repo { func (r *Repo) ToAPI() *api.Repo { repo := new(api.Repo) + var owner *api.User + if r.Owner.ID.Valid { + owner = r.Owner.ToAPI() + } else { + owner = new(api.User) + owner.SetID(r.UserID.Int64) + } + repo.SetID(r.ID.Int64) - repo.SetOwner(r.Owner.ToAPI().Crop()) + repo.SetOwner(owner.Crop()) repo.SetHash(r.Hash.String) repo.SetOrg(r.Org.String) repo.SetName(r.Name.String) @@ -251,6 +265,7 @@ func (r *Repo) ToAPI() *api.Repo { repo.SetPipelineType(r.PipelineType.String) repo.SetPreviousName(r.PreviousName.String) repo.SetApproveBuild(r.ApproveBuild.String) + repo.SetApprovalTimeout(r.ApprovalTimeout.Int64) repo.SetInstallID(r.InstallID.Int64) return repo @@ -323,28 +338,29 @@ func (r *Repo) Validate() error { // to a database repo type. func RepoFromAPI(r *api.Repo) *Repo { repo := &Repo{ - ID: sql.NullInt64{Int64: r.GetID(), Valid: true}, - UserID: sql.NullInt64{Int64: r.GetOwner().GetID(), Valid: true}, - Hash: sql.NullString{String: r.GetHash(), Valid: true}, - Org: sql.NullString{String: r.GetOrg(), Valid: true}, - Name: sql.NullString{String: r.GetName(), Valid: true}, - FullName: sql.NullString{String: r.GetFullName(), Valid: true}, - Link: sql.NullString{String: r.GetLink(), Valid: true}, - Clone: sql.NullString{String: r.GetClone(), Valid: true}, - Branch: sql.NullString{String: r.GetBranch(), Valid: true}, - Topics: pq.StringArray(r.GetTopics()), - BuildLimit: sql.NullInt64{Int64: r.GetBuildLimit(), Valid: true}, - Timeout: sql.NullInt64{Int64: r.GetTimeout(), Valid: true}, - Counter: sql.NullInt64{Int64: int64(r.GetCounter()), Valid: true}, - Visibility: sql.NullString{String: r.GetVisibility(), Valid: true}, - Private: sql.NullBool{Bool: r.GetPrivate(), Valid: true}, - Trusted: sql.NullBool{Bool: r.GetTrusted(), Valid: true}, - Active: sql.NullBool{Bool: r.GetActive(), Valid: true}, - AllowEvents: sql.NullInt64{Int64: r.GetAllowEvents().ToDatabase(), Valid: true}, - PipelineType: sql.NullString{String: r.GetPipelineType(), Valid: true}, - PreviousName: sql.NullString{String: r.GetPreviousName(), Valid: true}, - ApproveBuild: sql.NullString{String: r.GetApproveBuild(), Valid: true}, - InstallID: sql.NullInt64{Int64: r.GetInstallID(), Valid: true}, + ID: sql.NullInt64{Int64: r.GetID(), Valid: true}, + UserID: sql.NullInt64{Int64: r.GetOwner().GetID(), Valid: true}, + Hash: sql.NullString{String: r.GetHash(), Valid: true}, + Org: sql.NullString{String: r.GetOrg(), Valid: true}, + Name: sql.NullString{String: r.GetName(), Valid: true}, + FullName: sql.NullString{String: r.GetFullName(), Valid: true}, + Link: sql.NullString{String: r.GetLink(), Valid: true}, + Clone: sql.NullString{String: r.GetClone(), Valid: true}, + Branch: sql.NullString{String: r.GetBranch(), Valid: true}, + Topics: pq.StringArray(r.GetTopics()), + BuildLimit: sql.NullInt64{Int64: r.GetBuildLimit(), Valid: true}, + Timeout: sql.NullInt64{Int64: r.GetTimeout(), Valid: true}, + Counter: sql.NullInt64{Int64: int64(r.GetCounter()), Valid: true}, + Visibility: sql.NullString{String: r.GetVisibility(), Valid: true}, + Private: sql.NullBool{Bool: r.GetPrivate(), Valid: true}, + Trusted: sql.NullBool{Bool: r.GetTrusted(), Valid: true}, + Active: sql.NullBool{Bool: r.GetActive(), Valid: true}, + AllowEvents: sql.NullInt64{Int64: r.GetAllowEvents().ToDatabase(), Valid: true}, + PipelineType: sql.NullString{String: r.GetPipelineType(), Valid: true}, + PreviousName: sql.NullString{String: r.GetPreviousName(), Valid: true}, + ApproveBuild: sql.NullString{String: r.GetApproveBuild(), Valid: true}, + ApprovalTimeout: sql.NullInt64{Int64: r.GetApprovalTimeout(), Valid: true}, + InstallID: sql.NullInt64{Int64: r.GetInstallID(), Valid: true}, } return repo.Nullify() diff --git a/database/types/repo_test.go b/database/types/repo_test.go index 1027e5fc2..3c1c213a5 100644 --- a/database/types/repo_test.go +++ b/database/types/repo_test.go @@ -193,6 +193,7 @@ func TestTypes_Repo_ToAPI(t *testing.T) { want.SetPipelineType("yaml") want.SetPreviousName("oldName") want.SetApproveBuild(constants.ApproveNever) + want.SetApprovalTimeout(7) want.SetInstallID(0) // run test @@ -346,6 +347,7 @@ func TestTypes_RepoFromAPI(t *testing.T) { r.SetPipelineType("yaml") r.SetPreviousName("oldName") r.SetApproveBuild(constants.ApproveNever) + r.SetApprovalTimeout(7) r.SetInstallID(0) want := testRepo() @@ -363,28 +365,29 @@ func TestTypes_RepoFromAPI(t *testing.T) { // type with all fields set to a fake value. func testRepo() *Repo { return &Repo{ - ID: sql.NullInt64{Int64: 1, Valid: true}, - UserID: sql.NullInt64{Int64: 1, Valid: true}, - Hash: sql.NullString{String: "superSecretHash", Valid: true}, - Org: sql.NullString{String: "github", Valid: true}, - Name: sql.NullString{String: "octocat", Valid: true}, - FullName: sql.NullString{String: "github/octocat", Valid: true}, - Link: sql.NullString{String: "https://github.com/github/octocat", Valid: true}, - Clone: sql.NullString{String: "https://github.com/github/octocat.git", Valid: true}, - Branch: sql.NullString{String: "main", Valid: true}, - Topics: []string{"cloud", "security"}, - BuildLimit: sql.NullInt64{Int64: 10, Valid: true}, - Timeout: sql.NullInt64{Int64: 30, Valid: true}, - Counter: sql.NullInt64{Int64: 0, Valid: true}, - Visibility: sql.NullString{String: "public", Valid: true}, - Private: sql.NullBool{Bool: false, Valid: true}, - Trusted: sql.NullBool{Bool: false, Valid: true}, - Active: sql.NullBool{Bool: true, Valid: true}, - AllowEvents: sql.NullInt64{Int64: 1, Valid: true}, - PipelineType: sql.NullString{String: "yaml", Valid: true}, - PreviousName: sql.NullString{String: "oldName", Valid: true}, - ApproveBuild: sql.NullString{String: constants.ApproveNever, Valid: true}, - InstallID: sql.NullInt64{Int64: 0, Valid: true}, + ID: sql.NullInt64{Int64: 1, Valid: true}, + UserID: sql.NullInt64{Int64: 1, Valid: true}, + Hash: sql.NullString{String: "superSecretHash", Valid: true}, + Org: sql.NullString{String: "github", Valid: true}, + Name: sql.NullString{String: "octocat", Valid: true}, + FullName: sql.NullString{String: "github/octocat", Valid: true}, + Link: sql.NullString{String: "https://github.com/github/octocat", Valid: true}, + Clone: sql.NullString{String: "https://github.com/github/octocat.git", Valid: true}, + Branch: sql.NullString{String: "main", Valid: true}, + Topics: []string{"cloud", "security"}, + BuildLimit: sql.NullInt64{Int64: 10, Valid: true}, + Timeout: sql.NullInt64{Int64: 30, Valid: true}, + Counter: sql.NullInt64{Int64: 0, Valid: true}, + Visibility: sql.NullString{String: "public", Valid: true}, + Private: sql.NullBool{Bool: false, Valid: true}, + Trusted: sql.NullBool{Bool: false, Valid: true}, + Active: sql.NullBool{Bool: true, Valid: true}, + AllowEvents: sql.NullInt64{Int64: 1, Valid: true}, + PipelineType: sql.NullString{String: "yaml", Valid: true}, + PreviousName: sql.NullString{String: "oldName", Valid: true}, + ApproveBuild: sql.NullString{String: constants.ApproveNever, Valid: true}, + ApprovalTimeout: sql.NullInt64{Int64: 7, Valid: true}, + InstallID: sql.NullInt64{Int64: 0, Valid: true}, Owner: *testUser(), } diff --git a/database/types/schedule_test.go b/database/types/schedule_test.go index 2937c6c0a..aa0513cb7 100644 --- a/database/types/schedule_test.go +++ b/database/types/schedule_test.go @@ -95,6 +95,7 @@ func TestTypes_Schedule_ToAPI(t *testing.T) { repo.SetPipelineType("yaml") repo.SetPreviousName("oldName") repo.SetApproveBuild(constants.ApproveNever) + repo.SetApprovalTimeout(7) currTime := time.Now().UTC() nextTime, _ := gronx.NextTickAfter("0 0 * * *", currTime, false) diff --git a/docker-compose.yml b/docker-compose.yml index 8a869433a..8aa009e7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -version: '3' - services: # The `server` compose service hosts the Vela server and API. # @@ -13,7 +11,7 @@ services: server: build: context: . - dockerfile: Dockerfile + dockerfile: ${VELA_SERVER_DOCKERFILE:-Dockerfile} container_name: server networks: - vela diff --git a/mock/server/repo.go b/mock/server/repo.go index 1db1fc733..fec472275 100644 --- a/mock/server/repo.go +++ b/mock/server/repo.go @@ -59,9 +59,10 @@ const ( } }, "approve_build": "fork-always", + "approval_timeout": 7, "previous_name": "", "install_id": 0 - }` +}` // ReposResp represents a JSON return for one to many repos. ReposResp = `[ diff --git a/router/middleware/default_timeout.go b/router/middleware/default_timeout.go index 49c98de32..140ecaf31 100644 --- a/router/middleware/default_timeout.go +++ b/router/middleware/default_timeout.go @@ -14,3 +14,12 @@ func DefaultTimeout(defaultTimeout int64) gin.HandlerFunc { c.Next() } } + +// DefaultApprovalTimeout is a middleware function that attaches the defaultApprovalTimeout +// to enable the server to override the default build approval timeout. +func DefaultApprovalTimeout(defaultApprovalTimeout int64) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("defaultApprovalTimeout", defaultApprovalTimeout) + c.Next() + } +} diff --git a/router/middleware/repo/repo_test.go b/router/middleware/repo/repo_test.go index a331b5ae8..9b44649b7 100644 --- a/router/middleware/repo/repo_test.go +++ b/router/middleware/repo/repo_test.go @@ -66,6 +66,7 @@ func TestRepo_Establish(t *testing.T) { want.SetPipelineType("yaml") want.SetPreviousName("") want.SetApproveBuild("") + want.SetApprovalTimeout(7) want.SetInstallID(0) got := new(api.Repo) From 68547d660bbe0fbd9714eb03b0f60bd00d283923 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:29:03 -0600 Subject: [PATCH 23/41] enhance(yaml): silent fix anchor merges in YAML maps (#1231) --- compiler/native/compile_test.go | 14 ++-- .../native/testdata/steps_merge_anchor_1.yml | 46 ++++++++++ internal/testdata/buildkite_new_version.yml | 18 ++++ internal/yaml.go | 84 ++++++++++++++++++- internal/yaml_test.go | 4 + 5 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 compiler/native/testdata/steps_merge_anchor_1.yml create mode 100644 internal/testdata/buildkite_new_version.yml diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index 3767760c2..09a1ff5dd 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -2162,19 +2162,21 @@ func TestNative_Compile_LegacyMergeAnchor(t *testing.T) { t.Errorf("Compile() mismatch (-want +got):\n%s", diff) } - // run test on current version (should fail) - yaml, err = os.ReadFile("../types/yaml/buildkite/testdata/merge_anchor.yml") // has `version: "1"` instead of `version: "legacy"` + // run test on current version + yaml, err = os.ReadFile("testdata/steps_merge_anchor_1.yml") // has `version: "1"` instead of `version: "legacy"` if err != nil { t.Errorf("Reading yaml file return err: %v", err) } got, _, err = compiler.Compile(context.Background(), yaml) - if err == nil { - t.Errorf("Compile should have returned err") + if err != nil { + t.Errorf("Compile returned err: %v", err) } - if got != nil { - t.Errorf("Compile is %v, want %v", got, nil) + want.Version = "1" + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Compile() mismatch (-want +got):\n%s", diff) } } diff --git a/compiler/native/testdata/steps_merge_anchor_1.yml b/compiler/native/testdata/steps_merge_anchor_1.yml new file mode 100644 index 000000000..3de1e64b4 --- /dev/null +++ b/compiler/native/testdata/steps_merge_anchor_1.yml @@ -0,0 +1,46 @@ +# test file that uses the non-standard multiple anchor keys in one step to test custom step unmarshaler + +version: "1" + +aliases: + images: + alpine: &alpine-image + image: alpine:latest + postgres: &pg-image + image: postgres + + events: + push: &event-push + ruleset: + event: + - push + env: + dev-env: &dev-environment + environment: + REGION: dev + +services: + - name: service-a + <<: *pg-image + <<: *dev-environment + ports: + - "5432:5432" + +steps: + - name: alpha + <<: *alpine-image + <<: *event-push + commands: + - echo alpha + + - name: beta + <<: [ *alpine-image, *event-push ] + commands: + - echo beta + + - name: gamma + <<: *alpine-image + <<: *event-push + <<: *dev-environment + commands: + - echo gamma \ No newline at end of file diff --git a/internal/testdata/buildkite_new_version.yml b/internal/testdata/buildkite_new_version.yml new file mode 100644 index 000000000..50e3ba513 --- /dev/null +++ b/internal/testdata/buildkite_new_version.yml @@ -0,0 +1,18 @@ +version: "1" + +aliases: + images: + alpine: &alpine-image + image: alpine:latest + + env: + dev-env: &dev-environment + environment: + REGION: dev + +steps: + - name: example + <<: *alpine-image + <<: *dev-environment + commands: + - echo $REGION \ No newline at end of file diff --git a/internal/yaml.go b/internal/yaml.go index 8267486e8..363e9c9c3 100644 --- a/internal/yaml.go +++ b/internal/yaml.go @@ -4,6 +4,7 @@ package internal import ( "fmt" + "strings" bkYaml "github.com/buildkite/yaml" yaml "gopkg.in/yaml.v3" @@ -55,9 +56,90 @@ func ParseYAML(data []byte) (*types.Build, error) { // unmarshal the bytes into the yaml configuration err := yaml.Unmarshal(data, config) if err != nil { - return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + // if error is related to duplicate `<<` keys, attempt to fix + if strings.Contains(err.Error(), "mapping key \"<<\" already defined") { + root := new(yaml.Node) + + if err := yaml.Unmarshal(data, root); err != nil { + fmt.Println("error unmarshalling YAML:", err) + + return nil, err + } + + collapseMergeAnchors(root.Content[0]) + + newData, err := yaml.Marshal(root) + if err != nil { + return nil, err + } + + err = yaml.Unmarshal(newData, config) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + } + } else { + return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + } } } return config, nil } + +// collapseMergeAnchors traverses the entire pipeline and replaces duplicate `<<` keys with a single key->sequence. +func collapseMergeAnchors(node *yaml.Node) { + // only replace on maps + if node.Kind == yaml.MappingNode { + var ( + anchors []*yaml.Node + keysToRemove []int + firstIndex int + firstFound bool + ) + + // traverse mapping node content + for i := 0; i < len(node.Content); i += 2 { + keyNode := node.Content[i] + + // anchor found + if keyNode.Value == "<<" { + if (i+1) < len(node.Content) && node.Content[i+1].Kind == yaml.AliasNode { + anchors = append(anchors, node.Content[i+1]) + } + + if !firstFound { + firstIndex = i + firstFound = true + } else { + keysToRemove = append(keysToRemove, i) + } + } + } + + // only replace if there were duplicates + if len(anchors) > 1 && firstFound { + seqNode := &yaml.Node{ + Kind: yaml.SequenceNode, + Content: anchors, + } + + node.Content[firstIndex] = &yaml.Node{Kind: yaml.ScalarNode, Value: "<<"} + node.Content[firstIndex+1] = seqNode + + for i := len(keysToRemove) - 1; i >= 0; i-- { + index := keysToRemove[i] + + node.Content = append(node.Content[:index], node.Content[index+2:]...) + } + } + + // go to next level + for _, content := range node.Content { + collapseMergeAnchors(content) + } + } else if node.Kind == yaml.SequenceNode { + for _, item := range node.Content { + collapseMergeAnchors(item) + } + } +} diff --git a/internal/yaml_test.go b/internal/yaml_test.go index 627139229..0a12c6e44 100644 --- a/internal/yaml_test.go +++ b/internal/yaml_test.go @@ -47,6 +47,10 @@ func TestInternal_ParseYAML(t *testing.T) { file: "testdata/buildkite.yml", want: wantBuild, }, + { + file: "testdata/buildkite_new_version.yml", + want: wantBuild, + }, { file: "testdata/no_version.yml", want: wantBuild, From c7c1d0ae34fc3949591fbb84ab368a9eaa4de6e5 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Tue, 31 Dec 2024 10:34:43 -0600 Subject: [PATCH 24/41] fix(webhook): use correct repo variable for enqueue (#1234) --- api/webhook/post.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/webhook/post.go b/api/webhook/post.go index 07cc51be6..f71dd7eb2 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -562,9 +562,9 @@ func PostWebhook(c *gin.Context) { if shouldEnqueue { // send API call to set the status on the commit - err := scm.FromContext(c).Status(c.Request.Context(), r.GetOwner(), b, r.GetOrg(), r.GetName()) + err := scm.FromContext(c).Status(c.Request.Context(), repo.GetOwner(), b, repo.GetOrg(), repo.GetName()) if err != nil { - l.Errorf("unable to set commit status for %s/%d: %v", r.GetFullName(), b.GetNumber(), err) + l.Errorf("unable to set commit status for %s/%d: %v", repo.GetFullName(), b.GetNumber(), err) } // publish the build to the queue @@ -576,7 +576,7 @@ func PostWebhook(c *gin.Context) { b.GetHost(), ) } else { - err := build.GatekeepBuild(c, b, r) + err := build.GatekeepBuild(c, b, repo) if err != nil { retErr := fmt.Errorf("unable to gate build: %w", err) util.HandleError(c, http.StatusInternalServerError, err) From f43e2d1713fdbfccbc00a2560c2460b39817b9ff Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 6 Jan 2025 11:59:49 -0600 Subject: [PATCH 25/41] feat(pipeline)!: add warnings to pipelines that use legacy YAML lib or have dupl anchors in map (#1232) --- api/pipeline/template.go | 2 +- api/types/pipeline.go | 57 ++++++++++++---- api/types/pipeline_test.go | 12 ++++ compiler/engine.go | 2 +- compiler/native/compile.go | 8 ++- compiler/native/expand.go | 12 ++-- compiler/native/parse.go | 47 +++++++------ compiler/native/parse_test.go | 46 ++++++------- compiler/template/native/render.go | 36 ++++++---- compiler/template/native/render_test.go | 4 +- compiler/template/starlark/render.go | 74 +++++++++++---------- compiler/template/starlark/render_test.go | 4 +- constants/limit.go | 3 + database/integration_test.go | 2 + database/pipeline/create_test.go | 6 +- database/pipeline/table.go | 2 + database/pipeline/update_test.go | 6 +- database/testutils/api_resources.go | 1 + database/types/pipeline.go | 23 +++++++ database/types/pipeline_test.go | 4 ++ internal/yaml.go | 32 +++++---- internal/yaml_test.go | 54 +++++++++------ mock/server/pipeline.go | 3 + router/middleware/pipeline/pipeline_test.go | 1 + 24 files changed, 278 insertions(+), 163 deletions(-) diff --git a/api/pipeline/template.go b/api/pipeline/template.go index 0ff2a54bc..c1b1bb6fd 100644 --- a/api/pipeline/template.go +++ b/api/pipeline/template.go @@ -101,7 +101,7 @@ func GetTemplates(c *gin.Context) { compiler := compiler.FromContext(c).Duplicate().WithCommit(p.GetCommit()).WithMetadata(m).WithRepo(r).WithUser(u) // parse the pipeline configuration - pipeline, _, err := compiler.Parse(p.GetData(), p.GetType(), new(yaml.Template)) + pipeline, _, _, err := compiler.Parse(p.GetData(), p.GetType(), new(yaml.Template)) if err != nil { util.HandleError(c, http.StatusBadRequest, fmt.Errorf("unable to parse pipeline %s: %w", entry, err)) diff --git a/api/types/pipeline.go b/api/types/pipeline.go index f626e5c6e..dd0ff617d 100644 --- a/api/types/pipeline.go +++ b/api/types/pipeline.go @@ -10,20 +10,21 @@ import ( // // swagger:model Pipeline type Pipeline struct { - ID *int64 `json:"id,omitempty"` - Repo *Repo `json:"repo,omitempty"` - Commit *string `json:"commit,omitempty"` - Flavor *string `json:"flavor,omitempty"` - Platform *string `json:"platform,omitempty"` - Ref *string `json:"ref,omitempty"` - Type *string `json:"type,omitempty"` - Version *string `json:"version,omitempty"` - ExternalSecrets *bool `json:"external_secrets,omitempty"` - InternalSecrets *bool `json:"internal_secrets,omitempty"` - Services *bool `json:"services,omitempty"` - Stages *bool `json:"stages,omitempty"` - Steps *bool `json:"steps,omitempty"` - Templates *bool `json:"templates,omitempty"` + ID *int64 `json:"id,omitempty"` + Repo *Repo `json:"repo,omitempty"` + Commit *string `json:"commit,omitempty"` + Flavor *string `json:"flavor,omitempty"` + Platform *string `json:"platform,omitempty"` + Ref *string `json:"ref,omitempty"` + Type *string `json:"type,omitempty"` + Version *string `json:"version,omitempty"` + ExternalSecrets *bool `json:"external_secrets,omitempty"` + InternalSecrets *bool `json:"internal_secrets,omitempty"` + Services *bool `json:"services,omitempty"` + Stages *bool `json:"stages,omitempty"` + Steps *bool `json:"steps,omitempty"` + Templates *bool `json:"templates,omitempty"` + Warnings *[]string `json:"warnings,omitempty"` // swagger:strfmt base64 Data *[]byte `json:"data,omitempty"` } @@ -210,6 +211,19 @@ func (p *Pipeline) GetTemplates() bool { return *p.Templates } +// GetWarnings returns the Warnings field. +// +// When the provided Pipeline type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *Pipeline) GetWarnings() []string { + // return zero value if Pipeline type or Warnings field is nil + if p == nil || p.Warnings == nil { + return []string{} + } + + return *p.Warnings +} + // GetData returns the Data field. // // When the provided Pipeline type is nil, or the field within @@ -405,6 +419,19 @@ func (p *Pipeline) SetTemplates(v bool) { p.Templates = &v } +// SetWarnings sets the Warnings field. +// +// When the provided Pipeline type is nil, it +// will set nothing and immediately return. +func (p *Pipeline) SetWarnings(v []string) { + // return if Pipeline type is nil + if p == nil { + return + } + + p.Warnings = &v +} + // SetData sets the Data field. // // When the provided Pipeline type is nil, it @@ -436,6 +463,7 @@ func (p *Pipeline) String() string { Templates: %t, Type: %s, Version: %s, + Warnings: %v, }`, p.GetCommit(), p.GetData(), @@ -452,5 +480,6 @@ func (p *Pipeline) String() string { p.GetTemplates(), p.GetType(), p.GetVersion(), + p.GetWarnings(), ) } diff --git a/api/types/pipeline_test.go b/api/types/pipeline_test.go index a925d9940..8c14c5af2 100644 --- a/api/types/pipeline_test.go +++ b/api/types/pipeline_test.go @@ -82,6 +82,10 @@ func TestAPI_Pipeline_Getters(t *testing.T) { t.Errorf("GetTemplates is %v, want %v", test.pipeline.GetTemplates(), test.want.GetTemplates()) } + if !reflect.DeepEqual(test.pipeline.GetWarnings(), test.want.GetWarnings()) { + t.Errorf("GetWarnings is %v, want %v", test.pipeline.GetWarnings(), test.want.GetWarnings()) + } + if !reflect.DeepEqual(test.pipeline.GetData(), test.want.GetData()) { t.Errorf("GetData is %v, want %v", test.pipeline.GetData(), test.want.GetData()) } @@ -123,6 +127,7 @@ func TestAPI_Pipeline_Setters(t *testing.T) { test.pipeline.SetStages(test.want.GetStages()) test.pipeline.SetSteps(test.want.GetSteps()) test.pipeline.SetTemplates(test.want.GetTemplates()) + test.pipeline.SetWarnings(test.want.GetWarnings()) test.pipeline.SetData(test.want.GetData()) if test.pipeline.GetID() != test.want.GetID() { @@ -181,6 +186,10 @@ func TestAPI_Pipeline_Setters(t *testing.T) { t.Errorf("SetTemplates is %v, want %v", test.pipeline.GetTemplates(), test.want.GetTemplates()) } + if !reflect.DeepEqual(test.pipeline.GetWarnings(), test.want.GetWarnings()) { + t.Errorf("SetWarnings is %v, want %v", test.pipeline.GetWarnings(), test.want.GetWarnings()) + } + if !reflect.DeepEqual(test.pipeline.GetData(), test.want.GetData()) { t.Errorf("SetData is %v, want %v", test.pipeline.GetData(), test.want.GetData()) } @@ -207,6 +216,7 @@ func TestAPI_Pipeline_String(t *testing.T) { Templates: %t, Type: %s, Version: %s, + Warnings: %v, }`, p.GetCommit(), p.GetData(), @@ -223,6 +233,7 @@ func TestAPI_Pipeline_String(t *testing.T) { p.GetTemplates(), p.GetType(), p.GetVersion(), + p.GetWarnings(), ) // run test @@ -253,6 +264,7 @@ func testPipeline() *Pipeline { p.SetSteps(true) p.SetTemplates(false) p.SetData(testPipelineData()) + p.SetWarnings([]string{"42:this is a warning"}) return p } diff --git a/compiler/engine.go b/compiler/engine.go index b3503b77a..fd38599ef 100644 --- a/compiler/engine.go +++ b/compiler/engine.go @@ -36,7 +36,7 @@ type Engine interface { // Parse defines a function that converts // an object to a yaml configuration. - Parse(interface{}, string, *yaml.Template) (*yaml.Build, []byte, error) + Parse(interface{}, string, *yaml.Template) (*yaml.Build, []byte, []string, error) // ParseRaw defines a function that converts // an object to a string. diff --git a/compiler/native/compile.go b/compiler/native/compile.go index 7f0a71f41..e8152d81b 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -39,7 +39,7 @@ type ModifyResponse struct { // Compile produces an executable pipeline from a yaml configuration. func (c *client) Compile(ctx context.Context, v interface{}) (*pipeline.Build, *api.Pipeline, error) { - p, data, err := c.Parse(v, c.repo.GetPipelineType(), new(yaml.Template)) + p, data, warnings, err := c.Parse(v, c.repo.GetPipelineType(), new(yaml.Template)) if err != nil { return nil, nil, err } @@ -61,6 +61,7 @@ func (c *client) Compile(ctx context.Context, v interface{}) (*pipeline.Build, * _pipeline := p.ToPipelineAPI() _pipeline.SetData(data) _pipeline.SetType(c.repo.GetPipelineType()) + _pipeline.SetWarnings(warnings) // create map of templates for easy lookup templates := mapFromTemplates(p.Templates) @@ -117,7 +118,7 @@ func (c *client) Compile(ctx context.Context, v interface{}) (*pipeline.Build, * // CompileLite produces a partial of an executable pipeline from a yaml configuration. func (c *client) CompileLite(ctx context.Context, v interface{}, ruleData *pipeline.RuleData, substitute bool) (*yaml.Build, *api.Pipeline, error) { - p, data, err := c.Parse(v, c.repo.GetPipelineType(), new(yaml.Template)) + p, data, warnings, err := c.Parse(v, c.repo.GetPipelineType(), new(yaml.Template)) if err != nil { return nil, nil, err } @@ -126,6 +127,7 @@ func (c *client) CompileLite(ctx context.Context, v interface{}, ruleData *pipel _pipeline := p.ToPipelineAPI() _pipeline.SetData(data) _pipeline.SetType(c.repo.GetPipelineType()) + _pipeline.SetWarnings(warnings) if p.Metadata.RenderInline { newPipeline, err := c.compileInline(ctx, p, c.GetTemplateDepth()) @@ -267,7 +269,7 @@ func (c *client) compileInline(ctx context.Context, p *yaml.Build, depth int) (* // inject template name into variables template.Variables["VELA_TEMPLATE_NAME"] = template.Name - parsed, _, err := c.Parse(bytes, format, template) + parsed, _, _, err := c.Parse(bytes, format, template) if err != nil { return nil, err } diff --git a/compiler/native/expand.go b/compiler/native/expand.go index ac86d09ce..df3de903b 100644 --- a/compiler/native/expand.go +++ b/compiler/native/expand.go @@ -138,7 +138,7 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin // inject template name into variables step.Template.Variables["VELA_TEMPLATE_NAME"] = step.Template.Name - tmplBuild, err := c.mergeTemplate(bytes, tmpl, step) + tmplBuild, _, err := c.mergeTemplate(bytes, tmpl, step) if err != nil { return s, err } @@ -247,7 +247,7 @@ func (c *client) ExpandDeployment(ctx context.Context, b *yaml.Build, tmpls map[ b.Deployment.Template.Variables = make(map[string]interface{}) } - tmplBuild, err := c.mergeDeployTemplate(bytes, tmpl, &b.Deployment) + tmplBuild, _, err := c.mergeDeployTemplate(bytes, tmpl, &b.Deployment) if err != nil { return b, err } @@ -391,7 +391,7 @@ func (c *client) getTemplate(ctx context.Context, tmpl *yaml.Template, name stri } //nolint:lll // ignore long line length due to input arguments -func (c *client) mergeTemplate(bytes []byte, tmpl *yaml.Template, step *yaml.Step) (*yaml.Build, error) { +func (c *client) mergeTemplate(bytes []byte, tmpl *yaml.Template, step *yaml.Step) (*yaml.Build, []string, error) { switch tmpl.Format { case constants.PipelineTypeGo, "golang", "": //nolint:lll // ignore long line length due to return @@ -401,11 +401,11 @@ func (c *client) mergeTemplate(bytes []byte, tmpl *yaml.Template, step *yaml.Ste return starlark.Render(string(bytes), step.Name, step.Template.Name, step.Environment, step.Template.Variables, c.GetStarlarkExecLimit()) default: //nolint:lll // ignore long line length due to return - return &yaml.Build{}, fmt.Errorf("format of %s is unsupported", tmpl.Format) + return &yaml.Build{}, nil, fmt.Errorf("format of %s is unsupported", tmpl.Format) } } -func (c *client) mergeDeployTemplate(bytes []byte, tmpl *yaml.Template, d *yaml.Deployment) (*yaml.Build, error) { +func (c *client) mergeDeployTemplate(bytes []byte, tmpl *yaml.Template, d *yaml.Deployment) (*yaml.Build, []string, error) { switch tmpl.Format { case constants.PipelineTypeGo, "golang", "": //nolint:lll // ignore long line length due to return @@ -415,7 +415,7 @@ func (c *client) mergeDeployTemplate(bytes []byte, tmpl *yaml.Template, d *yaml. return starlark.Render(string(bytes), "", d.Template.Name, make(raw.StringSliceMap), d.Template.Variables, c.GetStarlarkExecLimit()) default: //nolint:lll // ignore long line length due to return - return &yaml.Build{}, fmt.Errorf("format of %s is unsupported", tmpl.Format) + return &yaml.Build{}, nil, fmt.Errorf("format of %s is unsupported", tmpl.Format) } } diff --git a/compiler/native/parse.go b/compiler/native/parse.go index 317c52600..b9f47e4dc 100644 --- a/compiler/native/parse.go +++ b/compiler/native/parse.go @@ -40,10 +40,11 @@ func (c *client) ParseRaw(v interface{}) (string, error) { } // Parse converts an object to a yaml configuration. -func (c *client) Parse(v interface{}, pipelineType string, template *yaml.Template) (*yaml.Build, []byte, error) { +func (c *client) Parse(v interface{}, pipelineType string, template *yaml.Template) (*yaml.Build, []byte, []string, error) { var ( - p *yaml.Build - raw []byte + p *yaml.Build + warnings []string + raw []byte ) switch pipelineType { @@ -51,29 +52,29 @@ func (c *client) Parse(v interface{}, pipelineType string, template *yaml.Templa // expand the base configuration parsedRaw, err := c.ParseRaw(v) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // capture the raw pipeline configuration raw = []byte(parsedRaw) - p, err = native.RenderBuild(template.Name, parsedRaw, c.EnvironmentBuild(), template.Variables) + p, warnings, err = native.RenderBuild(template.Name, parsedRaw, c.EnvironmentBuild(), template.Variables) if err != nil { - return nil, raw, err + return nil, raw, nil, err } case constants.PipelineTypeStarlark: // expand the base configuration parsedRaw, err := c.ParseRaw(v) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // capture the raw pipeline configuration raw = []byte(parsedRaw) - p, err = starlark.RenderBuild(template.Name, parsedRaw, c.EnvironmentBuild(), template.Variables, c.GetStarlarkExecLimit()) + p, warnings, err = starlark.RenderBuild(template.Name, parsedRaw, c.EnvironmentBuild(), template.Variables, c.GetStarlarkExecLimit()) if err != nil { - return nil, raw, err + return nil, raw, nil, err } case constants.PipelineTypeYAML, "": switch v := v.(type) { @@ -90,14 +91,13 @@ func (c *client) Parse(v interface{}, pipelineType string, template *yaml.Templa // parse string as path to yaml configuration return ParsePath(v) } - // parse string as yaml configuration return ParseString(v) default: - return nil, nil, fmt.Errorf("unable to parse yaml: unrecognized type %T", v) + return nil, nil, nil, fmt.Errorf("unable to parse yaml: unrecognized type %T", v) } default: - return nil, nil, fmt.Errorf("unable to parse config: unrecognized pipeline_type of %s", c.repo.GetPipelineType()) + return nil, nil, nil, fmt.Errorf("unable to parse config: unrecognized pipeline_type of %s", c.repo.GetPipelineType()) } // initializing Environment to prevent nil error @@ -107,14 +107,14 @@ func (c *client) Parse(v interface{}, pipelineType string, template *yaml.Templa p.Environment = typesRaw.StringSliceMap{} } - return p, raw, nil + return p, raw, warnings, nil } // ParseBytes converts a byte slice to a yaml configuration. -func ParseBytes(data []byte) (*yaml.Build, []byte, error) { - config, err := internal.ParseYAML(data) +func ParseBytes(data []byte) (*yaml.Build, []byte, []string, error) { + config, warnings, err := internal.ParseYAML(data) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // initializing Environment to prevent nil error @@ -124,11 +124,11 @@ func ParseBytes(data []byte) (*yaml.Build, []byte, error) { config.Environment = typesRaw.StringSliceMap{} } - return config, data, nil + return config, data, warnings, nil } // ParseFile converts an os.File into a yaml configuration. -func ParseFile(f *os.File) (*yaml.Build, []byte, error) { +func ParseFile(f *os.File) (*yaml.Build, []byte, []string, error) { return ParseReader(f) } @@ -138,11 +138,11 @@ func ParseFileRaw(f *os.File) (string, error) { } // ParsePath converts a file path into a yaml configuration. -func ParsePath(p string) (*yaml.Build, []byte, error) { +func ParsePath(p string) (*yaml.Build, []byte, []string, error) { // open the file for reading f, err := os.Open(p) if err != nil { - return nil, nil, fmt.Errorf("unable to open yaml file %s: %w", p, err) + return nil, nil, nil, fmt.Errorf("unable to open yaml file %s: %w", p, err) } defer f.Close() @@ -157,18 +157,17 @@ func ParsePathRaw(p string) (string, error) { if err != nil { return "", fmt.Errorf("unable to open yaml file %s: %w", p, err) } - defer f.Close() return ParseReaderRaw(f) } // ParseReader converts an io.Reader into a yaml configuration. -func ParseReader(r io.Reader) (*yaml.Build, []byte, error) { +func ParseReader(r io.Reader) (*yaml.Build, []byte, []string, error) { // read all the bytes from the reader data, err := io.ReadAll(r) if err != nil { - return nil, nil, fmt.Errorf("unable to read bytes for yaml: %w", err) + return nil, nil, nil, fmt.Errorf("unable to read bytes for yaml: %w", err) } return ParseBytes(data) @@ -186,6 +185,6 @@ func ParseReaderRaw(r io.Reader) (string, error) { } // ParseString converts a string into a yaml configuration. -func ParseString(s string) (*yaml.Build, []byte, error) { +func ParseString(s string) (*yaml.Build, []byte, []string, error) { return ParseBytes([]byte(s)) } diff --git a/compiler/native/parse_test.go b/compiler/native/parse_test.go index 1ac5953b9..4ae17ce11 100644 --- a/compiler/native/parse_test.go +++ b/compiler/native/parse_test.go @@ -38,7 +38,7 @@ func TestNative_Parse_Metadata_Bytes(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -69,7 +69,7 @@ func TestNative_Parse_Metadata_File(t *testing.T) { defer f.Close() - got, _, err := client.Parse(f, "", new(yaml.Template)) + got, _, _, err := client.Parse(f, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -84,7 +84,7 @@ func TestNative_Parse_Metadata_Invalid(t *testing.T) { client, _ := FromCLIContext(cli.NewContext(nil, flag.NewFlagSet("test", 0), nil)) // run test - got, _, err := client.Parse(nil, "", new(yaml.Template)) + got, _, _, err := client.Parse(nil, "", new(yaml.Template)) if err == nil { t.Error("Parse should have returned err") @@ -109,7 +109,7 @@ func TestNative_Parse_Metadata_Path(t *testing.T) { } // run test - got, _, err := client.Parse("testdata/metadata.yml", "", new(yaml.Template)) + got, _, _, err := client.Parse("testdata/metadata.yml", "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -138,7 +138,7 @@ func TestNative_Parse_Metadata_Reader(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(bytes.NewReader(b), "", new(yaml.Template)) + got, _, _, err := client.Parse(bytes.NewReader(b), "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -167,7 +167,7 @@ func TestNative_Parse_Metadata_String(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(string(b), "", new(yaml.Template)) + got, _, _, err := client.Parse(string(b), "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -215,7 +215,7 @@ func TestNative_Parse_Parameters(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -343,7 +343,7 @@ func TestNative_Parse_StagesPipeline(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -446,7 +446,7 @@ func TestNative_Parse_StepsPipeline(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) } @@ -516,7 +516,7 @@ func TestNative_Parse_Secrets(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) @@ -593,7 +593,7 @@ func TestNative_Parse_Stages(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) @@ -671,7 +671,7 @@ func TestNative_Parse_StagesLegacyMergeAnchor(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) @@ -730,7 +730,7 @@ func TestNative_Parse_Steps(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := client.Parse(b, "", new(yaml.Template)) + got, _, _, err := client.Parse(b, "", new(yaml.Template)) if err != nil { t.Errorf("Parse returned err: %v", err) @@ -759,7 +759,7 @@ func TestNative_ParseBytes_Metadata(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := ParseBytes(b) + got, _, _, err := ParseBytes(b) if err != nil { t.Errorf("ParseBytes returned err: %v", err) @@ -777,7 +777,7 @@ func TestNative_ParseBytes_Invalid(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := ParseBytes(b) + got, _, _, err := ParseBytes(b) if err == nil { t.Error("ParseBytes should have returned err") @@ -808,7 +808,7 @@ func TestNative_ParseFile_Metadata(t *testing.T) { defer f.Close() - got, _, err := ParseFile(f) + got, _, _, err := ParseFile(f) if err != nil { t.Errorf("ParseFile returned err: %v", err) @@ -828,7 +828,7 @@ func TestNative_ParseFile_Invalid(t *testing.T) { f.Close() - got, _, err := ParseFile(f) + got, _, _, err := ParseFile(f) if err == nil { t.Error("ParseFile should have returned err") @@ -852,7 +852,7 @@ func TestNative_ParsePath_Metadata(t *testing.T) { } // run test - got, _, err := ParsePath("testdata/metadata.yml") + got, _, _, err := ParsePath("testdata/metadata.yml") if err != nil { t.Errorf("ParsePath returned err: %v", err) @@ -865,7 +865,7 @@ func TestNative_ParsePath_Metadata(t *testing.T) { func TestNative_ParsePath_Invalid(t *testing.T) { // run test - got, _, err := ParsePath("testdata/foobar.yml") + got, _, _, err := ParsePath("testdata/foobar.yml") if err == nil { t.Error("ParsePath should have returned err") @@ -894,7 +894,7 @@ func TestNative_ParseReader_Metadata(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := ParseReader(bytes.NewReader(b)) + got, _, _, err := ParseReader(bytes.NewReader(b)) if err != nil { t.Errorf("ParseReader returned err: %v", err) @@ -907,7 +907,7 @@ func TestNative_ParseReader_Metadata(t *testing.T) { func TestNative_ParseReader_Invalid(t *testing.T) { // run test - got, _, err := ParseReader(FailReader{}) + got, _, _, err := ParseReader(FailReader{}) if err == nil { t.Error("ParseFile should have returned err") @@ -936,7 +936,7 @@ func TestNative_ParseString_Metadata(t *testing.T) { t.Errorf("Reading file returned err: %v", err) } - got, _, err := ParseString(string(b)) + got, _, _, err := ParseString(string(b)) if err != nil { t.Errorf("ParseString returned err: %v", err) @@ -1012,7 +1012,7 @@ func Test_client_Parse(t *testing.T) { } } - got, _, err := c.Parse(content, tt.args.pipelineType, new(yaml.Template)) + got, _, _, err := c.Parse(content, tt.args.pipelineType, new(yaml.Template)) if (err != nil) != tt.wantErr { t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/compiler/template/native/render.go b/compiler/template/native/render.go index 1004d62d1..41ff0560a 100644 --- a/compiler/template/native/render.go +++ b/compiler/template/native/render.go @@ -15,7 +15,7 @@ import ( ) // Render combines the template with the step in the yaml pipeline. -func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}) (*types.Build, error) { +func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}) (*types.Build, []string, error) { buffer := new(bytes.Buffer) velaFuncs := funcHandler{envs: convertPlatformVars(environment, name)} @@ -30,25 +30,24 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM sf := sprig.TxtFuncMap() delete(sf, "env") delete(sf, "expandenv") - // parse the template with Masterminds/sprig functions // // https://pkg.go.dev/github.com/Masterminds/sprig?tab=doc#TxtFuncMap t, err := template.New(name).Funcs(sf).Funcs(templateFuncMap).Parse(tmpl) if err != nil { - return nil, fmt.Errorf("unable to parse template %s: %w", tName, err) + return nil, nil, fmt.Errorf("unable to parse template %s: %w", tName, err) } // apply the variables to the parsed template err = t.Execute(buffer, variables) if err != nil { - return nil, fmt.Errorf("unable to execute template %s: %w", tName, err) + return nil, nil, fmt.Errorf("unable to execute template %s: %w", tName, err) } // unmarshal the template to the pipeline - config, err := internal.ParseYAML(buffer.Bytes()) + config, warnings, err := internal.ParseYAML(buffer.Bytes()) if err != nil { - return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } // ensure all templated steps have template prefix @@ -56,11 +55,21 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM config.Steps[index].Name = fmt.Sprintf("%s_%s", name, newStep.Name) } - return &types.Build{Metadata: config.Metadata, Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment, Templates: config.Templates, Deployment: config.Deployment}, nil + return &types.Build{ + Metadata: config.Metadata, + Steps: config.Steps, + Secrets: config.Secrets, + Services: config.Services, + Environment: config.Environment, + Templates: config.Templates, + Deployment: config.Deployment, + }, + warnings, + nil } // RenderBuild renders the templated build. -func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}) (*types.Build, error) { +func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}) (*types.Build, []string, error) { buffer := new(bytes.Buffer) velaFuncs := funcHandler{envs: convertPlatformVars(envs, tmpl)} @@ -75,26 +84,25 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st sf := sprig.TxtFuncMap() delete(sf, "env") delete(sf, "expandenv") - // parse the template with Masterminds/sprig functions // // https://pkg.go.dev/github.com/Masterminds/sprig?tab=doc#TxtFuncMap t, err := template.New(tmpl).Funcs(sf).Funcs(templateFuncMap).Parse(b) if err != nil { - return nil, err + return nil, nil, err } // execute the template err = t.Execute(buffer, variables) if err != nil { - return nil, fmt.Errorf("unable to execute template: %w", err) + return nil, nil, fmt.Errorf("unable to execute template: %w", err) } // unmarshal the template to the pipeline - config, err := internal.ParseYAML(buffer.Bytes()) + config, warnings, err := internal.ParseYAML(buffer.Bytes()) if err != nil { - return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } - return config, nil + return config, warnings, nil } diff --git a/compiler/template/native/render_test.go b/compiler/template/native/render_test.go index d204f7572..822af98ae 100644 --- a/compiler/template/native/render_test.go +++ b/compiler/template/native/render_test.go @@ -59,7 +59,7 @@ func TestNative_Render(t *testing.T) { t.Error(err) } - tmplBuild, err := Render(string(tmpl), b.Steps[0].Name, b.Steps[0].Template.Name, b.Steps[0].Environment, b.Steps[0].Template.Variables) + tmplBuild, _, err := Render(string(tmpl), b.Steps[0].Name, b.Steps[0].Template.Name, b.Steps[0].Environment, b.Steps[0].Template.Variables) if (err != nil) != tt.wantErr { t.Errorf("Render() error = %v, wantErr %v", err, tt.wantErr) return @@ -120,7 +120,7 @@ func TestNative_RenderBuild(t *testing.T) { t.Error(err) } - got, err := RenderBuild("build", string(sFile), map[string]string{ + got, _, err := RenderBuild("build", string(sFile), map[string]string{ "VELA_REPO_FULL_NAME": "octocat/hello-world", "VELA_BUILD_BRANCH": "main", }, map[string]interface{}{}) diff --git a/compiler/template/starlark/render.go b/compiler/template/starlark/render.go index 1abc52319..d9c1e4bd0 100644 --- a/compiler/template/starlark/render.go +++ b/compiler/template/starlark/render.go @@ -31,11 +31,11 @@ var ( ) // Render combines the template with the step in the yaml pipeline. -func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}, limit int64) (*types.Build, error) { +func Render(tmpl string, name string, tName string, environment raw.StringSliceMap, variables map[string]interface{}, limit int64) (*types.Build, []string, error) { thread := &starlark.Thread{Name: name} if limit < 0 { - return nil, fmt.Errorf("starlark exec limit must be non-negative") + return nil, nil, fmt.Errorf("starlark exec limit must be non-negative") } thread.SetMaxExecutionSteps(uint64(limit)) @@ -44,31 +44,31 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM globals, err := starlark.ExecFileOptions(syntax.LegacyFileOptions(), thread, "templated-base", tmpl, predeclared) if err != nil { - return nil, err + return nil, nil, err } // check the provided template has a main function mainVal, ok := globals["main"] if !ok { - return nil, fmt.Errorf("%w: %s", ErrMissingMainFunc, tName) + return nil, nil, fmt.Errorf("%w: %s", ErrMissingMainFunc, tName) } // check the provided main is a function main, ok := mainVal.(starlark.Callable) if !ok { - return nil, fmt.Errorf("%w: %s", ErrInvalidMainFunc, tName) + return nil, nil, fmt.Errorf("%w: %s", ErrInvalidMainFunc, tName) } // load the user provided vars into a starlark type userVars, err := convertTemplateVars(variables) if err != nil { - return nil, err + return nil, nil, err } // load the platform provided vars into a starlark type velaVars, err := convertPlatformVars(environment, name) if err != nil { - return nil, err + return nil, nil, err } // add the user and platform vars to a context to be used @@ -77,12 +77,12 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM err = context.SetKey(starlark.String("vela"), velaVars) if err != nil { - return nil, err + return nil, nil, err } err = context.SetKey(starlark.String("vars"), userVars) if err != nil { - return nil, err + return nil, nil, err } args := starlark.Tuple([]starlark.Value{context}) @@ -90,11 +90,10 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM // execute Starlark program from Go. mainVal, err = starlark.Call(thread, main, args, nil) if err != nil { - return nil, err + return nil, nil, err } buf := new(bytes.Buffer) - // extract the pipeline from the starlark program switch v := mainVal.(type) { case *starlark.List: @@ -105,7 +104,7 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM err = writeJSON(buf, item) if err != nil { - return nil, err + return nil, nil, err } buf.WriteString("\n") @@ -115,16 +114,16 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM err = writeJSON(buf, v) if err != nil { - return nil, err + return nil, nil, err } default: - return nil, fmt.Errorf("%w: %s", ErrInvalidPipelineReturn, mainVal.Type()) + return nil, nil, fmt.Errorf("%w: %s", ErrInvalidPipelineReturn, mainVal.Type()) } // unmarshal the template to the pipeline - config, err := internal.ParseYAML(buf.Bytes()) + config, warnings, err := internal.ParseYAML(buf.Bytes()) if err != nil { - return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } // ensure all templated steps have template prefix @@ -132,17 +131,25 @@ func Render(tmpl string, name string, tName string, environment raw.StringSliceM config.Steps[index].Name = fmt.Sprintf("%s_%s", name, newStep.Name) } - return &types.Build{Steps: config.Steps, Secrets: config.Secrets, Services: config.Services, Environment: config.Environment}, nil + return &types.Build{ + Steps: config.Steps, + Secrets: config.Secrets, + Services: config.Services, + Environment: config.Environment, + Deployment: config.Deployment, + }, + warnings, + nil } // RenderBuild renders the templated build. // //nolint:lll // ignore function length due to input args -func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}, limit int64) (*types.Build, error) { +func RenderBuild(tmpl string, b string, envs map[string]string, variables map[string]interface{}, limit int64) (*types.Build, []string, error) { thread := &starlark.Thread{Name: "templated-base"} if limit < 0 { - return nil, fmt.Errorf("starlark exec limit must be non-negative") + return nil, nil, fmt.Errorf("starlark exec limit must be non-negative") } thread.SetMaxExecutionSteps(uint64(limit)) @@ -151,31 +158,31 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st globals, err := starlark.ExecFileOptions(syntax.LegacyFileOptions(), thread, "templated-base", b, predeclared) if err != nil { - return nil, err + return nil, nil, err } // check the provided template has a main function mainVal, ok := globals["main"] if !ok { - return nil, fmt.Errorf("%w: %s", ErrMissingMainFunc, "templated-base") + return nil, nil, fmt.Errorf("%w: %s", ErrMissingMainFunc, "templated-base") } // check the provided main is a function main, ok := mainVal.(starlark.Callable) if !ok { - return nil, fmt.Errorf("%w: %s", ErrInvalidMainFunc, "templated-base") + return nil, nil, fmt.Errorf("%w: %s", ErrInvalidMainFunc, "templated-base") } // load the user provided vars into a starlark type userVars, err := convertTemplateVars(variables) if err != nil { - return nil, err + return nil, nil, err } // load the platform provided vars into a starlark type velaVars, err := convertPlatformVars(envs, tmpl) if err != nil { - return nil, err + return nil, nil, err } // add the user and platform vars to a context to be used @@ -184,12 +191,12 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st err = context.SetKey(starlark.String("vela"), velaVars) if err != nil { - return nil, err + return nil, nil, err } err = context.SetKey(starlark.String("vars"), userVars) if err != nil { - return nil, err + return nil, nil, err } args := starlark.Tuple([]starlark.Value{context}) @@ -197,11 +204,10 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st // execute Starlark program from Go. mainVal, err = starlark.Call(thread, main, args, nil) if err != nil { - return nil, err + return nil, nil, err } buf := new(bytes.Buffer) - // extract the pipeline from the starlark program switch v := mainVal.(type) { case *starlark.List: @@ -212,7 +218,7 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st err = writeJSON(buf, item) if err != nil { - return nil, err + return nil, nil, err } buf.WriteString("\n") @@ -222,17 +228,17 @@ func RenderBuild(tmpl string, b string, envs map[string]string, variables map[st err = writeJSON(buf, v) if err != nil { - return nil, err + return nil, nil, err } default: - return nil, fmt.Errorf("%w: %s", ErrInvalidPipelineReturn, mainVal.Type()) + return nil, nil, fmt.Errorf("%w: %s", ErrInvalidPipelineReturn, mainVal.Type()) } // unmarshal the template to the pipeline - config, err := internal.ParseYAML(buf.Bytes()) + config, warnings, err := internal.ParseYAML(buf.Bytes()) if err != nil { - return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } - return config, nil + return config, warnings, nil } diff --git a/compiler/template/starlark/render_test.go b/compiler/template/starlark/render_test.go index b9d184dae..fdab8657d 100644 --- a/compiler/template/starlark/render_test.go +++ b/compiler/template/starlark/render_test.go @@ -92,7 +92,7 @@ func TestStarlark_Render(t *testing.T) { t.Error(err) } - tmplBuild, err := Render(string(tmpl), b.Steps[0].Name, b.Steps[0].Template.Name, b.Steps[0].Environment, b.Steps[0].Template.Variables, 7500) + tmplBuild, _, err := Render(string(tmpl), b.Steps[0].Name, b.Steps[0].Template.Name, b.Steps[0].Environment, b.Steps[0].Template.Variables, 7500) if (err != nil) != tt.wantErr { t.Errorf("Render() error = %v, wantErr %v", err, tt.wantErr) return @@ -207,7 +207,7 @@ func TestNative_RenderBuild(t *testing.T) { t.Error(err) } - got, err := RenderBuild("build", string(sFile), map[string]string{ + got, _, err := RenderBuild("build", string(sFile), map[string]string{ "VELA_REPO_FULL_NAME": "octocat/hello-world", "VELA_BUILD_BRANCH": "main", "VELA_REPO_ORG": "octocat", diff --git a/constants/limit.go b/constants/limit.go index e3806a153..aad72e75c 100644 --- a/constants/limit.go +++ b/constants/limit.go @@ -54,4 +54,7 @@ const ( // DashboardAdminMaxSize defines the maximum size in characters for dashboard admins. DashboardAdminMaxSize = 5000 + + // PipelineWarningsMaxSize defines the maximum size in characters for the pipeline warnings. + PipelineWarningsMaxSize = 5000 ) diff --git a/database/integration_test.go b/database/integration_test.go index a0f1ad90c..c1073df7d 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -2788,6 +2788,7 @@ func newResources() *Resources { pipelineOne.SetStages(false) pipelineOne.SetSteps(true) pipelineOne.SetTemplates(false) + pipelineOne.SetWarnings([]string{}) pipelineOne.SetData([]byte("version: 1")) pipelineTwo := new(api.Pipeline) @@ -2805,6 +2806,7 @@ func newResources() *Resources { pipelineTwo.SetStages(false) pipelineTwo.SetSteps(true) pipelineTwo.SetTemplates(false) + pipelineTwo.SetWarnings([]string{"42:this is a warning"}) pipelineTwo.SetData([]byte("version: 1")) currTime := time.Now().UTC() diff --git a/database/pipeline/create_test.go b/database/pipeline/create_test.go index 5768e6f88..a01d7d605 100644 --- a/database/pipeline/create_test.go +++ b/database/pipeline/create_test.go @@ -34,9 +34,9 @@ func TestPipeline_Engine_CreatePipeline(t *testing.T) { // ensure the mock expects the query _mock.ExpectQuery(`INSERT INTO "pipelines" -("repo_id","commit","flavor","platform","ref","type","version","external_secrets","internal_secrets","services","stages","steps","templates","data","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) RETURNING "id"`). - WithArgs(1, "48afb5bdc41ad69bf22588491333f7cf71135163", nil, nil, "refs/heads/main", "yaml", "1", false, false, false, false, false, false, AnyArgument{}, 1). +("repo_id","commit","flavor","platform","ref","type","version","external_secrets","internal_secrets","services","stages","steps","templates","warnings","data","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16) RETURNING "id"`). + WithArgs(1, "48afb5bdc41ad69bf22588491333f7cf71135163", nil, nil, "refs/heads/main", "yaml", "1", false, false, false, false, false, false, nil, AnyArgument{}, 1). WillReturnRows(_rows) _sqlite := testSqlite(t) diff --git a/database/pipeline/table.go b/database/pipeline/table.go index 4bd874c83..f09aa96ac 100644 --- a/database/pipeline/table.go +++ b/database/pipeline/table.go @@ -28,6 +28,7 @@ pipelines ( stages BOOLEAN, steps BOOLEAN, templates BOOLEAN, + warnings VARCHAR(5000), data BYTEA, UNIQUE(repo_id, commit) ); @@ -52,6 +53,7 @@ pipelines ( stages BOOLEAN, steps BOOLEAN, templates BOOLEAN, + warnings TEXT, data BLOB, UNIQUE(repo_id, 'commit') ); diff --git a/database/pipeline/update_test.go b/database/pipeline/update_test.go index e8be0e457..ae5de556e 100644 --- a/database/pipeline/update_test.go +++ b/database/pipeline/update_test.go @@ -31,9 +31,9 @@ func TestPipeline_Engine_UpdatePipeline(t *testing.T) { // ensure the mock expects the query _mock.ExpectExec(`UPDATE "pipelines" -SET "repo_id"=$1,"commit"=$2,"flavor"=$3,"platform"=$4,"ref"=$5,"type"=$6,"version"=$7,"external_secrets"=$8,"internal_secrets"=$9,"services"=$10,"stages"=$11,"steps"=$12,"templates"=$13,"data"=$14 -WHERE "id" = $15`). - WithArgs(1, "48afb5bdc41ad69bf22588491333f7cf71135163", nil, nil, "refs/heads/main", "yaml", "1", false, false, false, false, false, false, AnyArgument{}, 1). +SET "repo_id"=$1,"commit"=$2,"flavor"=$3,"platform"=$4,"ref"=$5,"type"=$6,"version"=$7,"external_secrets"=$8,"internal_secrets"=$9,"services"=$10,"stages"=$11,"steps"=$12,"templates"=$13,"warnings"=$14,"data"=$15 +WHERE "id" = $16`). + WithArgs(1, "48afb5bdc41ad69bf22588491333f7cf71135163", nil, nil, "refs/heads/main", "yaml", "1", false, false, false, false, false, false, nil, AnyArgument{}, 1). WillReturnResult(sqlmock.NewResult(1, 1)) _sqlite := testSqlite(t) diff --git a/database/testutils/api_resources.go b/database/testutils/api_resources.go index 7689a5f4e..c33ab256c 100644 --- a/database/testutils/api_resources.go +++ b/database/testutils/api_resources.go @@ -270,6 +270,7 @@ func APIPipeline() *api.Pipeline { Stages: new(bool), Steps: new(bool), Templates: new(bool), + Warnings: new([]string), Data: new([]byte), } } diff --git a/database/types/pipeline.go b/database/types/pipeline.go index 425f193e0..f5edfd7c7 100644 --- a/database/types/pipeline.go +++ b/database/types/pipeline.go @@ -6,7 +6,10 @@ import ( "database/sql" "errors" + "github.com/lib/pq" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/util" ) @@ -30,6 +33,10 @@ var ( // ErrEmptyPipelineVersion defines the error type when a // Pipeline type has an empty Version field provided. ErrEmptyPipelineVersion = errors.New("empty pipeline version provided") + + // ErrExceededWarningsLimit defines the error type when a + // Pipeline warnings field has too many total characters. + ErrExceededWarningsLimit = errors.New("exceeded character limit for pipeline warnings") ) // Pipeline is the database representation of a pipeline. @@ -48,6 +55,7 @@ type Pipeline struct { Stages sql.NullBool `sql:"stages"` Steps sql.NullBool `sql:"steps"` Templates sql.NullBool `sql:"templates"` + Warnings pq.StringArray `sql:"warnings" gorm:"type:varchar(5000)"` Data []byte `sql:"data"` Repo Repo `gorm:"foreignKey:RepoID"` @@ -160,6 +168,7 @@ func (p *Pipeline) ToAPI() *api.Pipeline { pipeline.SetStages(p.Stages.Bool) pipeline.SetSteps(p.Steps.Bool) pipeline.SetTemplates(p.Templates.Bool) + pipeline.SetWarnings(p.Warnings) pipeline.SetData(p.Data) return pipeline @@ -193,6 +202,19 @@ func (p *Pipeline) Validate() error { return ErrEmptyPipelineVersion } + // calculate total size of warnings + total := 0 + for _, w := range p.Warnings { + total += len(w) + } + + // verify the Warnings field is within the database constraints + // len is to factor in number of comma separators included in the database field, + // removing 1 due to the last item not having an appended comma + if (total + len(p.Warnings) - 1) > constants.PipelineWarningsMaxSize { + return ErrExceededWarningsLimit + } + // ensure that all Pipeline string fields // that can be returned as JSON are sanitized // to avoid unsafe HTML content @@ -224,6 +246,7 @@ func PipelineFromAPI(p *api.Pipeline) *Pipeline { Stages: sql.NullBool{Bool: p.GetStages(), Valid: true}, Steps: sql.NullBool{Bool: p.GetSteps(), Valid: true}, Templates: sql.NullBool{Bool: p.GetTemplates(), Valid: true}, + Warnings: pq.StringArray(p.GetWarnings()), Data: p.GetData(), } diff --git a/database/types/pipeline_test.go b/database/types/pipeline_test.go index 659e3fb9c..2a03afff1 100644 --- a/database/types/pipeline_test.go +++ b/database/types/pipeline_test.go @@ -285,6 +285,7 @@ func TestDatabase_Pipeline_ToAPI(t *testing.T) { want.SetStages(false) want.SetSteps(true) want.SetTemplates(false) + want.SetWarnings([]string{"42:this is a warning"}) want.SetData(testPipelineData()) // run test @@ -393,6 +394,7 @@ func TestDatabase_PipelineFromAPI(t *testing.T) { Stages: sql.NullBool{Bool: false, Valid: true}, Steps: sql.NullBool{Bool: true, Valid: true}, Templates: sql.NullBool{Bool: false, Valid: true}, + Warnings: []string{"42:this is a warning"}, Data: testPipelineData(), } @@ -412,6 +414,7 @@ func TestDatabase_PipelineFromAPI(t *testing.T) { p.SetStages(false) p.SetSteps(true) p.SetTemplates(false) + p.SetWarnings([]string{"42:this is a warning"}) p.SetData(testPipelineData()) // run test @@ -440,6 +443,7 @@ func testPipeline() *Pipeline { Stages: sql.NullBool{Bool: false, Valid: true}, Steps: sql.NullBool{Bool: true, Valid: true}, Templates: sql.NullBool{Bool: false, Valid: true}, + Warnings: []string{"42:this is a warning"}, Data: testPipelineData(), Repo: *testRepo(), diff --git a/internal/yaml.go b/internal/yaml.go index 363e9c9c3..34add6d9d 100644 --- a/internal/yaml.go +++ b/internal/yaml.go @@ -14,19 +14,20 @@ import ( ) // ParseYAML is a helper function for transitioning teams away from legacy buildkite YAML parser. -func ParseYAML(data []byte) (*types.Build, error) { +func ParseYAML(data []byte) (*types.Build, []string, error) { var ( rootNode yaml.Node + warnings []string version string ) err := yaml.Unmarshal(data, &rootNode) if err != nil { - return nil, fmt.Errorf("unable to unmarshal pipeline version yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal pipeline version yaml: %w", err) } if len(rootNode.Content) == 0 || rootNode.Content[0].Kind != yaml.MappingNode { - return nil, fmt.Errorf("unable to find pipeline version in yaml") + return nil, nil, fmt.Errorf("unable to find pipeline version in yaml") } for i, subNode := range rootNode.Content[0].Content { @@ -47,11 +48,13 @@ func ParseYAML(data []byte) (*types.Build, error) { err := bkYaml.Unmarshal(data, legacyConfig) if err != nil { - return nil, fmt.Errorf("unable to unmarshal legacy yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal legacy yaml: %w", err) } config = legacyConfig.ToYAML() + warnings = append(warnings, "using legacy version. Upgrade to go-yaml v3") + default: // unmarshal the bytes into the yaml configuration err := yaml.Unmarshal(data, config) @@ -63,31 +66,31 @@ func ParseYAML(data []byte) (*types.Build, error) { if err := yaml.Unmarshal(data, root); err != nil { fmt.Println("error unmarshalling YAML:", err) - return nil, err + return nil, nil, err } - collapseMergeAnchors(root.Content[0]) + warnings = collapseMergeAnchors(root.Content[0], warnings) newData, err := yaml.Marshal(root) if err != nil { - return nil, err + return nil, nil, err } err = yaml.Unmarshal(newData, config) if err != nil { - return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } } else { - return nil, fmt.Errorf("unable to unmarshal yaml: %w", err) + return nil, nil, fmt.Errorf("unable to unmarshal yaml: %w", err) } } } - return config, nil + return config, warnings, nil } // collapseMergeAnchors traverses the entire pipeline and replaces duplicate `<<` keys with a single key->sequence. -func collapseMergeAnchors(node *yaml.Node) { +func collapseMergeAnchors(node *yaml.Node, warnings []string) []string { // only replace on maps if node.Kind == yaml.MappingNode { var ( @@ -129,17 +132,20 @@ func collapseMergeAnchors(node *yaml.Node) { for i := len(keysToRemove) - 1; i >= 0; i-- { index := keysToRemove[i] + warnings = append(warnings, fmt.Sprintf("%d:duplicate << keys in single YAML map", node.Content[index].Line)) node.Content = append(node.Content[:index], node.Content[index+2:]...) } } // go to next level for _, content := range node.Content { - collapseMergeAnchors(content) + warnings = collapseMergeAnchors(content, warnings) } } else if node.Kind == yaml.SequenceNode { for _, item := range node.Content { - collapseMergeAnchors(item) + warnings = collapseMergeAnchors(item, warnings) } } + + return warnings } diff --git a/internal/yaml_test.go b/internal/yaml_test.go index 0a12c6e44..e91ccaa21 100644 --- a/internal/yaml_test.go +++ b/internal/yaml_test.go @@ -4,6 +4,7 @@ package internal import ( "os" + "reflect" "testing" "github.com/google/go-cmp/cmp" @@ -35,30 +36,39 @@ func TestInternal_ParseYAML(t *testing.T) { // set up tests tests := []struct { - file string - want *yaml.Build - wantErr bool + name string + file string + wantBuild *yaml.Build + wantWarnings []string + wantErr bool }{ { - file: "testdata/go-yaml.yml", - want: wantBuild, + name: "go-yaml", + file: "testdata/go-yaml.yml", + wantBuild: wantBuild, }, { - file: "testdata/buildkite.yml", - want: wantBuild, + name: "buildkite legacy", + file: "testdata/buildkite.yml", + wantBuild: wantBuild, + wantWarnings: []string{"using legacy version. Upgrade to go-yaml v3"}, }, { - file: "testdata/buildkite_new_version.yml", - want: wantBuild, + name: "anchor collapse", + file: "testdata/buildkite_new_version.yml", + wantBuild: wantBuild, + wantWarnings: []string{"16:duplicate << keys in single YAML map"}, }, { - file: "testdata/no_version.yml", - want: wantBuild, + name: "no version", + file: "testdata/no_version.yml", + wantBuild: wantBuild, }, { - file: "testdata/invalid.yml", - want: nil, - wantErr: true, + name: "invalid yaml", + file: "testdata/invalid.yml", + wantBuild: nil, + wantErr: true, }, } @@ -66,16 +76,16 @@ func TestInternal_ParseYAML(t *testing.T) { for _, test := range tests { bytes, err := os.ReadFile(test.file) if err != nil { - t.Errorf("unable to read file: %v", err) + t.Errorf("unable to read file for test %s: %v", test.name, err) } - gotBuild, err := ParseYAML(bytes) + gotBuild, gotWarnings, err := ParseYAML(bytes) if err != nil && !test.wantErr { - t.Errorf("ParseYAML returned err: %v", err) + t.Errorf("ParseYAML for test %s returned err: %v", test.name, err) } if err == nil && test.wantErr { - t.Errorf("ParseYAML returned nil error") + t.Errorf("ParseYAML for test %s returned nil error", test.name) } if err != nil && test.wantErr { @@ -85,8 +95,12 @@ func TestInternal_ParseYAML(t *testing.T) { // different versions expected wantBuild.Version = gotBuild.Version - if diff := cmp.Diff(gotBuild, test.want); diff != "" { - t.Errorf("ParseYAML returned diff (-got +want):\n%s", diff) + if diff := cmp.Diff(gotBuild, test.wantBuild); diff != "" { + t.Errorf("ParseYAML for test %s returned diff (-got +want):\n%s", test.name, diff) + } + + if !reflect.DeepEqual(gotWarnings, test.wantWarnings) { + t.Errorf("ParseYAML for test %s returned warnings %v, want %v", test.name, gotWarnings, test.wantWarnings) } } } diff --git a/mock/server/pipeline.go b/mock/server/pipeline.go index f4d65c596..a55fc1e93 100644 --- a/mock/server/pipeline.go +++ b/mock/server/pipeline.go @@ -169,6 +169,9 @@ templates: "stages": false, "steps": true, "templates": false, + "warnings": [ + "42:this is a warning" + ], "data": "LS0tCnZlcnNpb246ICIxIgoKc3RlcHM6CiAgLSBuYW1lOiBlY2hvCiAgICBpbWFnZTogYWxwaW5lOmxhdGVzdAogICAgY29tbWFuZHM6IFtlY2hvIGZvb10=" }` diff --git a/router/middleware/pipeline/pipeline_test.go b/router/middleware/pipeline/pipeline_test.go index a12dd544b..3d305225c 100644 --- a/router/middleware/pipeline/pipeline_test.go +++ b/router/middleware/pipeline/pipeline_test.go @@ -98,6 +98,7 @@ func TestPipeline_Establish(t *testing.T) { want.SetStages(false) want.SetSteps(false) want.SetTemplates(false) + want.SetWarnings([]string{}) want.SetData([]byte{}) got := new(api.Pipeline) From f1a98ac666120419bba404bbde08f5fe458b4cbe Mon Sep 17 00:00:00 2001 From: dave vader <48764154+plyr4@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:32:13 -0600 Subject: [PATCH 26/41] feat: status endpoints (#1235) --- api/build/status.go | 69 +++++++++++++++++++++++++++++++++++++++++++++ api/repo/status.go | 64 +++++++++++++++++++++++++++++++++++++++++ api/types/build.go | 11 ++++++++ api/types/repo.go | 6 ++++ router/router.go | 10 +++++++ 5 files changed, 160 insertions(+) create mode 100644 api/build/status.go create mode 100644 api/repo/status.go diff --git a/api/build/status.go b/api/build/status.go new file mode 100644 index 000000000..20d36c1d8 --- /dev/null +++ b/api/build/status.go @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 + +package build + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + "github.com/go-vela/server/router/middleware/build" +) + +// swagger:operation GET /status/{org}/{repo}/{build} builds GetBuildStatus +// +// Get a build status +// +// --- +// produces: +// - application/json +// parameters: +// - in: path +// name: org +// description: Name of the organization +// required: true +// type: string +// - in: path +// name: repo +// description: Name of the repository +// required: true +// type: string +// - in: path +// name: build +// description: Build number +// required: true +// type: integer +// security: +// - ApiKeyAuth: [] +// responses: +// '200': +// description: Successfully retrieved the build +// schema: +// "$ref": "#/definitions/Build" +// '400': +// description: Invalid request payload or path +// schema: +// "$ref": "#/definitions/Build" +// '401': +// description: Unauthorized +// schema: +// "$ref": "#/definitions/Build" +// '404': +// description: Not found +// schema: +// "$ref": "#/definitions/Build" + +// GetBuildStatus represents the API handler to return "status", a lite representation of the resource with limited fields for unauthenticated access. +func GetBuildStatus(c *gin.Context) { + // capture middleware values + l := c.MustGet("logger").(*logrus.Entry) + b := build.Retrieve(c) + + l.Debug("reading status for build") + + // sanitize fields for the unauthenticated response + b.StatusSanitize() + + c.JSON(http.StatusOK, b) +} diff --git a/api/repo/status.go b/api/repo/status.go new file mode 100644 index 000000000..c1b59a5b1 --- /dev/null +++ b/api/repo/status.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 + +package repo + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + "github.com/go-vela/server/router/middleware/repo" +) + +// swagger:operation GET /status/{org}/{repo} repos GetRepoStatus +// +// Get a repository status +// +// --- +// produces: +// - application/json +// parameters: +// - in: path +// name: org +// description: Name of the organization +// required: true +// type: string +// - in: path +// name: repo +// description: Name of the repository +// required: true +// type: string +// security: +// - ApiKeyAuth: [] +// responses: +// '200': +// description: Successfully retrieved the repo +// schema: +// "$ref": "#/definitions/Repo" +// '400': +// description: Invalid request payload or path +// schema: +// "$ref": "#/definitions/Repo" +// '401': +// description: Unauthorized +// schema: +// "$ref": "#/definitions/Repo" +// '404': +// description: Not found +// schema: +// "$ref": "#/definitions/Repo" + +// GetRepoStatus represents the API handler to return "status", a lite representation of the resource with limited fields for unauthenticated access. +func GetRepoStatus(c *gin.Context) { + // capture middleware values + l := c.MustGet("logger").(*logrus.Entry) + r := repo.Retrieve(c) + + l.Debug("reading status for repo") + + // sanitize fields for the unauthenticated response + r.StatusSanitize() + + c.JSON(http.StatusOK, r) +} diff --git a/api/types/build.go b/api/types/build.go index 2d6fa7041..9051b4cc1 100644 --- a/api/types/build.go +++ b/api/types/build.go @@ -1233,3 +1233,14 @@ func (b *Build) String() string { b.GetTitle(), ) } + +// StatusSanitize removes sensitive information before producing a "status". +func (b *Build) StatusSanitize() { + // sanitize repo + if b.Repo != nil { + b.Repo.StatusSanitize() + } + + b.Email = nil + b.DeployPayload = nil +} diff --git a/api/types/repo.go b/api/types/repo.go index 0d84b2417..bf9438fa0 100644 --- a/api/types/repo.go +++ b/api/types/repo.go @@ -723,3 +723,9 @@ func (r *Repo) String() string { r.GetInstallID(), ) } + +// StatusSanitize removes sensitive information before producing a "status". +func (r *Repo) StatusSanitize() { + // remove allowed events + r.AllowEvents = nil +} diff --git a/router/router.go b/router/router.go index 2e7aebbb2..ca131ae63 100644 --- a/router/router.go +++ b/router/router.go @@ -34,8 +34,11 @@ import ( "github.com/go-vela/server/api" "github.com/go-vela/server/api/auth" + apiBuild "github.com/go-vela/server/api/build" + apiRepo "github.com/go-vela/server/api/repo" "github.com/go-vela/server/api/webhook" "github.com/go-vela/server/router/middleware" + "github.com/go-vela/server/router/middleware/build" "github.com/go-vela/server/router/middleware/claims" "github.com/go-vela/server/router/middleware/org" "github.com/go-vela/server/router/middleware/repo" @@ -62,6 +65,13 @@ func Load(options ...gin.HandlerFunc) *gin.Engine { // Badge endpoint r.GET("/badge/:org/:repo/status.svg", org.Establish(), repo.Establish(), api.GetBadge) + // Status endpoints + status := r.Group("/status/:org/:repo", org.Establish(), repo.Establish()) + { + status.GET("", org.Establish(), repo.Establish(), apiRepo.GetRepoStatus) + status.GET("/:build", org.Establish(), repo.Establish(), build.Establish(), apiBuild.GetBuildStatus) + } + // Health endpoint r.GET("/health", api.Health) From bf2313d56ddd81ac9f3b0435d15e5405fe2d8947 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:27:01 -0600 Subject: [PATCH 27/41] enhance(deploy): validate on CreateDeployment (#1236) --- api/deployment/create.go | 23 +++++++++++++++ api/deployment/get_config.go | 40 +++++++++++++++------------ compiler/types/pipeline/deployment.go | 2 +- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/api/deployment/create.go b/api/deployment/create.go index f4c3f5ca1..11ccd9bf3 100644 --- a/api/deployment/create.go +++ b/api/deployment/create.go @@ -101,6 +101,29 @@ func CreateDeployment(c *gin.Context) { input.SetRef(fmt.Sprintf("refs/heads/%s", r.GetBranch())) } + deployConfigYAML, err := getDeploymentConfig(c, l, u, r, input.GetRef()) + if err != nil { + retErr := fmt.Errorf("unable to get deployment config for %s: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + + deployConfig := deployConfigYAML.ToPipeline() + + if !deployConfig.Empty() { + err := deployConfig.Validate(input.GetTarget(), input.GetPayload()) + + if err != nil { + retErr := fmt.Errorf("unable to validate deployment config for %s: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + } + // send API call to create the deployment err = scm.FromContext(c).CreateDeployment(ctx, u, r, input) if err != nil { diff --git a/api/deployment/get_config.go b/api/deployment/get_config.go index bf8323e33..987f7dced 100644 --- a/api/deployment/get_config.go +++ b/api/deployment/get_config.go @@ -11,7 +11,9 @@ import ( "github.com/sirupsen/logrus" "gorm.io/gorm" + "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler" + "github.com/go-vela/server/compiler/types/yaml/yaml" "github.com/go-vela/server/database" "github.com/go-vela/server/router/middleware/repo" "github.com/go-vela/server/router/middleware/user" @@ -72,11 +74,25 @@ func GetDeploymentConfig(c *gin.Context) { r := repo.Retrieve(c) u := user.Retrieve(c) - ctx := c.Request.Context() - // capture ref from parameters - use default branch if not provided ref := util.QueryParameter(c, "ref", r.GetBranch()) + deployConfig, err := getDeploymentConfig(c, l, u, r, ref) + if err != nil { + retErr := fmt.Errorf("unable to get deployment config for %s: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + + c.JSON(http.StatusOK, deployConfig) +} + +// getDeploymentConfig is a helper function that lightly compiles a Vela pipeline to retrieve the deployment configuration. +func getDeploymentConfig(c *gin.Context, l *logrus.Entry, u *types.User, r *types.Repo, ref string) (yaml.Deployment, error) { + ctx := c.Request.Context() + entry := fmt.Sprintf("%s@%s", r.GetFullName(), ref) l.Debugf("reading deployment config %s", entry) @@ -91,19 +107,11 @@ func GetDeploymentConfig(c *gin.Context) { config, err = scm.FromContext(c).ConfigBackoff(ctx, u, r, ref) if err != nil { - retErr := fmt.Errorf("unable to get pipeline configuration for %s: %w", entry, err) - - util.HandleError(c, http.StatusNotFound, retErr) - - return + return yaml.Deployment{}, fmt.Errorf("unable to get pipeline configuration for %s: %w", entry, err) } } else { // some other error - retErr := fmt.Errorf("unable to get pipeline for %s: %w", entry, err) - - util.HandleError(c, http.StatusInternalServerError, retErr) - - return + return yaml.Deployment{}, fmt.Errorf("unable to get pipeline for %s: %w", entry, err) } } else { l.Debugf("pipeline %s found in database", entry) @@ -117,12 +125,8 @@ func GetDeploymentConfig(c *gin.Context) { // compile the pipeline pipeline, _, err := compiler.CompileLite(ctx, config, nil, true) if err != nil { - retErr := fmt.Errorf("unable to compile pipeline %s: %w", entry, err) - - util.HandleError(c, http.StatusBadRequest, retErr) - - return + return yaml.Deployment{}, fmt.Errorf("unable to compile pipeline %s: %w", entry, err) } - c.JSON(http.StatusOK, pipeline.Deployment) + return pipeline.Deployment, nil } diff --git a/compiler/types/pipeline/deployment.go b/compiler/types/pipeline/deployment.go index 475946dea..26b2efefa 100644 --- a/compiler/types/pipeline/deployment.go +++ b/compiler/types/pipeline/deployment.go @@ -58,7 +58,7 @@ func (d *Deployment) Validate(target string, inputParams map[string]string) erro // validate targets if len(d.Targets) > 0 && !slices.Contains(d.Targets, target) { - return fmt.Errorf("deployment target %s not found in deployment config targets", target) + return fmt.Errorf("deployment target `%s` not found in deployment config targets", target) } // validate params From 9a532cf3aec149bce77e7be97bd1e51d188fa309 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:00:25 -0600 Subject: [PATCH 28/41] enhance(metrics): include route specific queue length (#1237) --- api/metrics.go | 19 ++++++--- queue/redis/route_length.go | 19 +++++++++ queue/redis/route_length_test.go | 68 ++++++++++++++++++++++++++++++++ queue/service.go | 6 ++- 4 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 queue/redis/route_length.go create mode 100644 queue/redis/route_length_test.go diff --git a/api/metrics.go b/api/metrics.go index 77609c497..d4562b00c 100644 --- a/api/metrics.go +++ b/api/metrics.go @@ -3,6 +3,7 @@ package api import ( + "fmt" "net/http" "time" @@ -15,6 +16,7 @@ import ( "github.com/go-vela/server/constants" "github.com/go-vela/server/database" "github.com/go-vela/server/queue" + "github.com/go-vela/server/router/middleware/settings" ) // MetricsQueryParameters holds query parameter information pertaining to requested metrics. @@ -303,13 +305,20 @@ func recordGauges(c *gin.Context) { // queued_build_count if q.QueuedBuildCount { - // send API call to capture the total number of queued builds - t, err := queue.FromContext(c).Length(c) - if err != nil { - logrus.Errorf("unable to get count of all queued builds: %v", err) + queueTotal := int64(0) + + for _, route := range settings.FromContext(c).GetRoutes() { + t, err := queue.FromContext(c).RouteLength(c, route) + if err != nil { + logrus.Errorf("unable to get count of all queued builds for route %s: %v", route, err) + } + + totals.WithLabelValues("build", "status", fmt.Sprintf("queued_%s", route)).Set(float64(t)) + + queueTotal += t } - totals.WithLabelValues("build", "status", "queued").Set(float64(t)) + totals.WithLabelValues("build", "status", "queued").Set(float64(queueTotal)) } // failure_build_count diff --git a/queue/redis/route_length.go b/queue/redis/route_length.go new file mode 100644 index 000000000..b91854751 --- /dev/null +++ b/queue/redis/route_length.go @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 + +package redis + +import ( + "context" +) + +// RouteLength returns count of all items present in the given route. +func (c *client) RouteLength(ctx context.Context, channel string) (int64, error) { + c.Logger.Tracef("reading length of all configured routes in queue") + + items, err := c.Redis.LLen(ctx, channel).Result() + if err != nil { + return 0, err + } + + return items, nil +} diff --git a/queue/redis/route_length_test.go b/queue/redis/route_length_test.go new file mode 100644 index 000000000..d545dbb8c --- /dev/null +++ b/queue/redis/route_length_test.go @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 + +package redis + +import ( + "context" + "encoding/json" + "testing" + + "github.com/go-vela/server/queue/models" +) + +func TestRedis_RouteLength(t *testing.T) { + // setup types + // use global variables in redis_test.go + _item := &models.Item{ + Build: _build, + } + + // setup queue item + bytes, err := json.Marshal(_item) + if err != nil { + t.Errorf("unable to marshal queue item: %v", err) + } + + // setup redis mock + _redis, err := NewTest(_signingPrivateKey, _signingPublicKey, "vela", "vela:second", "vela:third") + if err != nil { + t.Errorf("unable to create queue service: %v", err) + } + + // setup tests + tests := []struct { + routes []string + want int64 + }{ + { + routes: []string{"vela"}, + want: 1, + }, + { + routes: []string{"vela", "vela:second", "vela:third"}, + want: 2, + }, + { + routes: []string{"vela", "vela:second", "phony"}, + want: 3, + }, + } + + // run tests + for _, test := range tests { + for _, channel := range test.routes { + err := _redis.Push(context.Background(), channel, bytes) + if err != nil { + t.Errorf("unable to push item to queue: %v", err) + } + } + got, err := _redis.RouteLength(context.Background(), "vela") + if err != nil { + t.Errorf("RouteLength returned err: %v", err) + } + + if got != test.want { + t.Errorf("Length is %v, want %v", got, test.want) + } + } +} diff --git a/queue/service.go b/queue/service.go index 82bfe4d57..6b7ccac0c 100644 --- a/queue/service.go +++ b/queue/service.go @@ -20,9 +20,13 @@ type Service interface { Driver() string // Length defines a function that outputs - // the length of a queue channel + // the length of all queue channels Length(context.Context) (int64, error) + // RouteLength defines a function that outputs + // the length of a defined queue route + RouteLength(context.Context, string) (int64, error) + // Pop defines a function that grabs an // item off the queue. Pop(context.Context, []string) (*models.Item, error) From 9f13f9756cb4f593fc356d1e8b64aa8c5fae6040 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:37:35 -0600 Subject: [PATCH 29/41] chore(deps): update codecov/codecov-action action to v5 (#1222) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4987db435..7bb5f0f74 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: make test-jsonschema - name: coverage - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 + uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.out From 3920eabc93d7a0e8ce3292c5c3a8d099c85e956b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:43:35 -0600 Subject: [PATCH 30/41] fix(deps): update module github.com/google/go-github/v65 to v68 (#1229) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- compiler/native/compile_test.go | 2 +- compiler/registry/github/github.go | 2 +- compiler/registry/github/github_test.go | 2 +- compiler/registry/github/template.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- scm/github/access.go | 2 +- scm/github/app_permissions.go | 2 +- scm/github/app_permissions_test.go | 2 +- scm/github/app_transport.go | 2 +- scm/github/authentication.go | 2 +- scm/github/changeset.go | 2 +- scm/github/deployment.go | 2 +- scm/github/github.go | 2 +- scm/github/github_client.go | 2 +- scm/github/github_client_test.go | 2 +- scm/github/github_test.go | 2 +- scm/github/repo.go | 2 +- scm/github/repo_test.go | 2 +- scm/github/webhook.go | 2 +- 20 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index 09a1ff5dd..c4d6852dc 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -15,7 +15,7 @@ import ( "github.com/gin-gonic/gin" "github.com/google/go-cmp/cmp" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "github.com/urfave/cli/v2" yml "gopkg.in/yaml.v3" diff --git a/compiler/registry/github/github.go b/compiler/registry/github/github.go index f5027a5b5..8eb268bf7 100644 --- a/compiler/registry/github/github.go +++ b/compiler/registry/github/github.go @@ -7,7 +7,7 @@ import ( "net/url" "strings" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "golang.org/x/oauth2" ) diff --git a/compiler/registry/github/github_test.go b/compiler/registry/github/github_test.go index 2b5f069a2..1a02b8837 100644 --- a/compiler/registry/github/github_test.go +++ b/compiler/registry/github/github_test.go @@ -10,7 +10,7 @@ import ( "reflect" "testing" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "golang.org/x/oauth2" ) diff --git a/compiler/registry/github/template.go b/compiler/registry/github/template.go index 5e1cd927b..e24569c44 100644 --- a/compiler/registry/github/template.go +++ b/compiler/registry/github/template.go @@ -7,7 +7,7 @@ import ( "fmt" "net/http" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/registry" diff --git a/go.mod b/go.mod index 6fbf43bb0..3cc4dd061 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/go-vela/archiver/v3 v3.4.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.6.0 - github.com/google/go-github/v65 v65.0.0 + github.com/google/go-github/v68 v68.0.0 github.com/google/uuid v1.6.0 github.com/goware/urlx v0.3.2 github.com/hashicorp/go-cleanhttp v0.5.2 diff --git a/go.sum b/go.sum index 023f6d8c3..c8a32b0ca 100644 --- a/go.sum +++ b/go.sum @@ -138,8 +138,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v65 v65.0.0 h1:pQ7BmO3DZivvFk92geC0jB0q2m3gyn8vnYPgV7GSLhQ= -github.com/google/go-github/v65 v65.0.0/go.mod h1:DvrqWo5hvsdhJvHd4WyVF9ttANN3BniqjP8uTFMNb60= +github.com/google/go-github/v68 v68.0.0 h1:ZW57zeNZiXTdQ16qrDiZ0k6XucrxZ2CGmoTvcCyQG6s= +github.com/google/go-github/v68 v68.0.0/go.mod h1:K9HAUBovM2sLwM408A18h+wd9vqdLOEqTUCbnRIcx68= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/scm/github/access.go b/scm/github/access.go index 1bd4dd2e3..2c7f59ea9 100644 --- a/scm/github/access.go +++ b/scm/github/access.go @@ -6,7 +6,7 @@ import ( "context" "strings" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" diff --git a/scm/github/app_permissions.go b/scm/github/app_permissions.go index 411ee3d4a..172a99798 100644 --- a/scm/github/app_permissions.go +++ b/scm/github/app_permissions.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" ) // see: https://docs.github.com/en/rest/authentication/permissions-required-for-github-apps?apiVersion=2022-11-28 diff --git a/scm/github/app_permissions_test.go b/scm/github/app_permissions_test.go index 74b94a071..d6c99d831 100644 --- a/scm/github/app_permissions_test.go +++ b/scm/github/app_permissions_test.go @@ -5,7 +5,7 @@ package github import ( "testing" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" ) func TestGetInstallationPermission(t *testing.T) { diff --git a/scm/github/app_transport.go b/scm/github/app_transport.go index b0db1f5f8..d2dd6170b 100644 --- a/scm/github/app_transport.go +++ b/scm/github/app_transport.go @@ -19,7 +19,7 @@ import ( "time" "github.com/golang-jwt/jwt/v5" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) diff --git a/scm/github/authentication.go b/scm/github/authentication.go index bd1f4d62c..de91f2aae 100644 --- a/scm/github/authentication.go +++ b/scm/github/authentication.go @@ -10,7 +10,7 @@ import ( "net/url" "strings" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/random" diff --git a/scm/github/changeset.go b/scm/github/changeset.go index 7a9732fc4..8adb1363c 100644 --- a/scm/github/changeset.go +++ b/scm/github/changeset.go @@ -6,7 +6,7 @@ import ( "context" "fmt" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" diff --git a/scm/github/deployment.go b/scm/github/deployment.go index f1c32db88..5a8ac9f86 100644 --- a/scm/github/deployment.go +++ b/scm/github/deployment.go @@ -6,7 +6,7 @@ import ( "context" "encoding/json" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" diff --git a/scm/github/github.go b/scm/github/github.go index b8a3082df..be83a2006 100644 --- a/scm/github/github.go +++ b/scm/github/github.go @@ -12,7 +12,7 @@ import ( "os" "strings" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "github.com/sirupsen/logrus" "golang.org/x/oauth2" diff --git a/scm/github/github_client.go b/scm/github/github_client.go index ac79436c8..b282cd6e0 100644 --- a/scm/github/github_client.go +++ b/scm/github/github_client.go @@ -11,7 +11,7 @@ import ( "net/url" "strings" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "golang.org/x/oauth2" diff --git a/scm/github/github_client_test.go b/scm/github/github_client_test.go index 57de6f7d7..d34fc111e 100644 --- a/scm/github/github_client_test.go +++ b/scm/github/github_client_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/gin-gonic/gin" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/constants" diff --git a/scm/github/github_test.go b/scm/github/github_test.go index 3ce90df3a..6ee1f5909 100644 --- a/scm/github/github_test.go +++ b/scm/github/github_test.go @@ -10,7 +10,7 @@ import ( "reflect" "testing" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "golang.org/x/oauth2" ) diff --git a/scm/github/repo.go b/scm/github/repo.go index 060876e48..28df414ec 100644 --- a/scm/github/repo.go +++ b/scm/github/repo.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" diff --git a/scm/github/repo_test.go b/scm/github/repo_test.go index 1a86f089a..833217070 100644 --- a/scm/github/repo_test.go +++ b/scm/github/repo_test.go @@ -14,7 +14,7 @@ import ( "github.com/gin-gonic/gin" "github.com/google/go-cmp/cmp" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/compiler/types/yaml/yaml" diff --git a/scm/github/webhook.go b/scm/github/webhook.go index a23c6be1b..b312a5ed4 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -13,7 +13,7 @@ import ( "strings" "time" - "github.com/google/go-github/v65/github" + "github.com/google/go-github/v68/github" "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" From 084fea3495d93dc8f9c67b4f0d1c92d4a433ba7b Mon Sep 17 00:00:00 2001 From: David May <49894298+wass3rw3rk@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:50:05 -0600 Subject: [PATCH 31/41] chore: bump go (#1238) --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3cc4dd061..71b844f48 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-vela/server -go 1.23.1 +go 1.23.4 require ( github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb From 52df828ea94aa0baeafe8759501be23b500623a4 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:25:13 -0600 Subject: [PATCH 32/41] fix(deployment): use Config instead of ConfigBackoff when fetching deploy config (#1239) --- api/deployment/get_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/deployment/get_config.go b/api/deployment/get_config.go index 987f7dced..8c03cafc0 100644 --- a/api/deployment/get_config.go +++ b/api/deployment/get_config.go @@ -105,7 +105,7 @@ func getDeploymentConfig(c *gin.Context, l *logrus.Entry, u *types.User, r *type if errors.Is(err, gorm.ErrRecordNotFound) { l.Debugf("pipeline %s not found in database, fetching from scm", entry) - config, err = scm.FromContext(c).ConfigBackoff(ctx, u, r, ref) + config, err = scm.FromContext(c).Config(ctx, u, r, ref) if err != nil { return yaml.Deployment{}, fmt.Errorf("unable to get pipeline configuration for %s: %w", entry, err) } From 4eee25d96a078d5b626b39976856ba1e0291edb0 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:40:26 -0600 Subject: [PATCH 33/41] fix(build): use item build host (#1241) --- api/build/restart.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/build/restart.go b/api/build/restart.go index e4b86c86f..4d71b0ca0 100644 --- a/api/build/restart.go +++ b/api/build/restart.go @@ -170,7 +170,7 @@ func RestartBuild(c *gin.Context) { queue.FromGinContext(c), database.FromContext(c), item, - b.GetHost(), + item.Build.GetHost(), ) } else { err := GatekeepBuild(c, b, r) From df21b7a39d4b048c5c8264760bfccf86ef724858 Mon Sep 17 00:00:00 2001 From: David May <49894298+wass3rw3rk@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:23:06 -0600 Subject: [PATCH 34/41] fix(build): ensure count/pagination links account for filter/query params (#1242) --- api/build/compile_publish.go | 2 +- api/pagination.go | 15 ++++++++++++--- api/webhook/post.go | 2 +- database/build/count_repo.go | 4 +++- database/build/count_repo_test.go | 7 +++++-- database/build/interface.go | 2 +- database/build/list_repo.go | 2 +- database/build/list_repo_test.go | 2 +- database/integration_test.go | 2 +- 9 files changed, 26 insertions(+), 12 deletions(-) diff --git a/api/build/compile_publish.go b/api/build/compile_publish.go index 7167d1357..ce1aacacd 100644 --- a/api/build/compile_publish.go +++ b/api/build/compile_publish.go @@ -119,7 +119,7 @@ func CompileAndPublish( } // send API call to capture the number of pending or running builds for the repo - builds, err := database.CountBuildsForRepo(ctx, r, filters) + builds, err := database.CountBuildsForRepo(ctx, r, filters, time.Now().Unix(), 0) if err != nil { retErr := fmt.Errorf("%s: unable to get count of builds for repo %s", baseErr, r.GetFullName()) diff --git a/api/pagination.go b/api/pagination.go index 6abb3f3d9..126b36372 100644 --- a/api/pagination.go +++ b/api/pagination.go @@ -30,6 +30,9 @@ func (p *Pagination) SetHeaderLink(c *gin.Context) { l := []string{} r := c.Request + // grab the current query params + q := r.URL.Query() + hl := HeaderLink{ "first": 1, "last": p.TotalPages(), @@ -42,6 +45,9 @@ func (p *Pagination) SetHeaderLink(c *gin.Context) { return } + // reset per config + q.Set("per_page", strconv.Itoa(p.PerPage)) + // drop first, prev on the first page if p.Page == 1 { delete(hl, "first") @@ -54,14 +60,17 @@ func (p *Pagination) SetHeaderLink(c *gin.Context) { delete(hl, "next") } + // loop over the fields that make up the header links for rel, page := range hl { + // set the page info for the current field + q.Set("page", strconv.Itoa(page)) + ls := fmt.Sprintf( - `<%s://%s%s?per_page=%d&page=%d>; rel="%s"`, + `<%s://%s%s?%s>; rel="%s"`, resolveScheme(r), r.Host, r.URL.Path, - p.PerPage, - page, + q.Encode(), rel, ) diff --git a/api/webhook/post.go b/api/webhook/post.go index f71dd7eb2..e5f644889 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -785,7 +785,7 @@ func RenameRepository(ctx context.Context, l *logrus.Entry, db database.Interfac } // get total number of builds associated with repository - t, err = db.CountBuildsForRepo(ctx, dbR, nil) + t, err = db.CountBuildsForRepo(ctx, dbR, nil, time.Now().Unix(), 0) if err != nil { return nil, fmt.Errorf("unable to get build count for repo %s: %w", dbR.GetFullName(), err) } diff --git a/database/build/count_repo.go b/database/build/count_repo.go index df59ee2fe..a3eb4ba4b 100644 --- a/database/build/count_repo.go +++ b/database/build/count_repo.go @@ -12,7 +12,7 @@ import ( ) // CountBuildsForRepo gets the count of builds by repo ID from the database. -func (e *engine) CountBuildsForRepo(ctx context.Context, r *api.Repo, filters map[string]interface{}) (int64, error) { +func (e *engine) CountBuildsForRepo(ctx context.Context, r *api.Repo, filters map[string]interface{}, before, after int64) (int64, error) { e.logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -26,6 +26,8 @@ func (e *engine) CountBuildsForRepo(ctx context.Context, r *api.Repo, filters ma WithContext(ctx). Table(constants.TableBuild). Where("repo_id = ?", r.GetID()). + Where("created < ?", before). + Where("created > ?", after). Where(filters). Count(&b). Error diff --git a/database/build/count_repo_test.go b/database/build/count_repo_test.go index 77bbc69d6..10019e551 100644 --- a/database/build/count_repo_test.go +++ b/database/build/count_repo_test.go @@ -6,6 +6,7 @@ import ( "context" "reflect" "testing" + "time" "github.com/DATA-DOG/go-sqlmock" @@ -33,12 +34,14 @@ func TestBuild_Engine_CountBuildsForRepo(t *testing.T) { _buildOne.SetRepo(_repo) _buildOne.SetNumber(1) _buildOne.SetDeployPayload(nil) + _buildOne.SetCreated(1) _buildTwo := testutils.APIBuild() _buildTwo.SetID(2) _buildTwo.SetRepo(_repo) _buildTwo.SetNumber(2) _buildTwo.SetDeployPayload(nil) + _buildTwo.SetCreated(2) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -47,7 +50,7 @@ func TestBuild_Engine_CountBuildsForRepo(t *testing.T) { _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) // ensure the mock expects the query - _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1`).WithArgs(1).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1 AND created < $2 AND created > $3`).WithArgs(1, AnyArgument{}, 0).WillReturnRows(_rows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() @@ -88,7 +91,7 @@ func TestBuild_Engine_CountBuildsForRepo(t *testing.T) { // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := test.database.CountBuildsForRepo(context.TODO(), _repo, filters) + got, err := test.database.CountBuildsForRepo(context.TODO(), _repo, filters, time.Now().Unix(), 0) if test.failure { if err == nil { diff --git a/database/build/interface.go b/database/build/interface.go index c723d838e..4b4f91f3e 100644 --- a/database/build/interface.go +++ b/database/build/interface.go @@ -35,7 +35,7 @@ type BuildInterface interface { // CountBuildsForOrg defines a function that gets the count of builds by org name. CountBuildsForOrg(context.Context, string, map[string]interface{}) (int64, error) // CountBuildsForRepo defines a function that gets the count of builds by repo ID. - CountBuildsForRepo(context.Context, *api.Repo, map[string]interface{}) (int64, error) + CountBuildsForRepo(context.Context, *api.Repo, map[string]interface{}, int64, int64) (int64, error) // CountBuildsForStatus defines a function that gets the count of builds by status. CountBuildsForStatus(context.Context, string, map[string]interface{}) (int64, error) // CreateBuild defines a function that creates a new build. diff --git a/database/build/list_repo.go b/database/build/list_repo.go index 470b448d3..3de7b2da4 100644 --- a/database/build/list_repo.go +++ b/database/build/list_repo.go @@ -27,7 +27,7 @@ func (e *engine) ListBuildsForRepo(ctx context.Context, r *api.Repo, filters map builds := []*api.Build{} // count the results - count, err := e.CountBuildsForRepo(ctx, r, filters) + count, err := e.CountBuildsForRepo(ctx, r, filters, before, after) if err != nil { return builds, 0, err } diff --git a/database/build/list_repo_test.go b/database/build/list_repo_test.go index 8071e3a67..c384405bf 100644 --- a/database/build/list_repo_test.go +++ b/database/build/list_repo_test.go @@ -55,7 +55,7 @@ func TestBuild_Engine_ListBuildsForRepo(t *testing.T) { _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) // ensure the mock expects the count query - _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1`).WithArgs(1).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1 AND created < $2 AND created > $3`).WithArgs(1, AnyArgument{}, 0).WillReturnRows(_rows) // create expected query result in mock _rows = sqlmock.NewRows( diff --git a/database/integration_test.go b/database/integration_test.go index c1073df7d..a629c1e95 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -258,7 +258,7 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { methods["CountBuildsForOrg"] = true // count the builds for a repo - count, err = db.CountBuildsForRepo(context.TODO(), resources.Repos[0], nil) + count, err = db.CountBuildsForRepo(context.TODO(), resources.Repos[0], nil, time.Now().Unix(), 0) if err != nil { t.Errorf("unable to count builds for repo %d: %v", resources.Repos[0].GetID(), err) } From d24fd37532014dec13b59f72700929da7db3f026 Mon Sep 17 00:00:00 2001 From: David May <49894298+wass3rw3rk@users.noreply.github.com> Date: Wed, 15 Jan 2025 13:57:58 -0600 Subject: [PATCH 35/41] fix(yaml): improved messaging (#1243) --- internal/yaml.go | 2 +- internal/yaml_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/yaml.go b/internal/yaml.go index 34add6d9d..b45cc4983 100644 --- a/internal/yaml.go +++ b/internal/yaml.go @@ -53,7 +53,7 @@ func ParseYAML(data []byte) (*types.Build, []string, error) { config = legacyConfig.ToYAML() - warnings = append(warnings, "using legacy version. Upgrade to go-yaml v3") + warnings = append(warnings, `using legacy version - address any incompatibilities and use "1" instead`) default: // unmarshal the bytes into the yaml configuration diff --git a/internal/yaml_test.go b/internal/yaml_test.go index e91ccaa21..fbf441439 100644 --- a/internal/yaml_test.go +++ b/internal/yaml_test.go @@ -51,7 +51,7 @@ func TestInternal_ParseYAML(t *testing.T) { name: "buildkite legacy", file: "testdata/buildkite.yml", wantBuild: wantBuild, - wantWarnings: []string{"using legacy version. Upgrade to go-yaml v3"}, + wantWarnings: []string{`using legacy version - address any incompatibilities and use "1" instead`}, }, { name: "anchor collapse", From a051afae4b92a9608b36e39224a925933fa1ba99 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Fri, 17 Jan 2025 12:05:20 -0600 Subject: [PATCH 36/41] keys for test report step --- compiler/types/yaml/test_report.go | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 compiler/types/yaml/test_report.go diff --git a/compiler/types/yaml/test_report.go b/compiler/types/yaml/test_report.go new file mode 100644 index 000000000..038db367c --- /dev/null +++ b/compiler/types/yaml/test_report.go @@ -0,0 +1,40 @@ +package yaml + +import "github.com/go-vela/server/compiler/types/pipeline" + +// TestReport represents the structure for test report configuration. +type TestReport struct { + Results []string `yaml:"results,omitempty" json:"results,omitempty"` + Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` +} + +// ToPipeline converts the TestReport type +// to a pipeline TestReport type. +func (t *TestReport) ToPipeline() *pipeline.TestReport { + return &pipeline.TestReport{ + Results: t.Results, + Attachments: t.Attachments, + } +} + +// UnmarshalYAML implements the Unmarshaler interface for the TestReport type. +func (t *TestReport) UnmarshalYAML(unmarshal func(interface{}) error) error { + // test report we try unmarshalling to + testReport := new(struct { + Results []string `yaml:"results,omitempty" json:"results,omitempty"` + Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` + }) + + // attempt to unmarshal test report type + err := unmarshal(testReport) + if err != nil { + return err + } + + // set the results field + t.Results = testReport.Results + // set the attachments field + t.Attachments = testReport.Attachments + + return nil +} From 5254aadd1f7de9bdc48fcf0e0e944a9ac02f6c38 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Fri, 17 Jan 2025 12:13:04 -0600 Subject: [PATCH 37/41] resolve conflicts with buildkite --- compiler/types/yaml/test_report.go | 40 ------------------------------ 1 file changed, 40 deletions(-) delete mode 100644 compiler/types/yaml/test_report.go diff --git a/compiler/types/yaml/test_report.go b/compiler/types/yaml/test_report.go deleted file mode 100644 index 038db367c..000000000 --- a/compiler/types/yaml/test_report.go +++ /dev/null @@ -1,40 +0,0 @@ -package yaml - -import "github.com/go-vela/server/compiler/types/pipeline" - -// TestReport represents the structure for test report configuration. -type TestReport struct { - Results []string `yaml:"results,omitempty" json:"results,omitempty"` - Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` -} - -// ToPipeline converts the TestReport type -// to a pipeline TestReport type. -func (t *TestReport) ToPipeline() *pipeline.TestReport { - return &pipeline.TestReport{ - Results: t.Results, - Attachments: t.Attachments, - } -} - -// UnmarshalYAML implements the Unmarshaler interface for the TestReport type. -func (t *TestReport) UnmarshalYAML(unmarshal func(interface{}) error) error { - // test report we try unmarshalling to - testReport := new(struct { - Results []string `yaml:"results,omitempty" json:"results,omitempty"` - Attachments []string `yaml:"attachments,omitempty" json:"attachments,omitempty"` - }) - - // attempt to unmarshal test report type - err := unmarshal(testReport) - if err != nil { - return err - } - - // set the results field - t.Results = testReport.Results - // set the attachments field - t.Attachments = testReport.Attachments - - return nil -} From 14660f52f79c059dd8690daa94deccf659c9b5e9 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Fri, 17 Jan 2025 15:07:29 -0600 Subject: [PATCH 38/41] mod tidy --- go.mod | 6 +++++- go.sum | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 0cbd9d47c..7e1f21ecd 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/redis/go-redis/v9 v9.7.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.12.0 + github.com/stretchr/testify v1.10.0 github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 github.com/urfave/cli/v2 v2.27.5 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.58.0 @@ -66,6 +67,7 @@ require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect + github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -77,6 +79,7 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect @@ -114,8 +117,9 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect diff --git a/go.sum b/go.sum index 87b829110..7e0381a92 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGn github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE= github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8= +github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= @@ -60,6 +62,7 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -114,6 +117,10 @@ github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-vela/archiver v3.1.1+incompatible h1:xTTMMwKyHwDgNFn+c1XsKHrHFg6UyWmK4oSceSduH7A= +github.com/go-vela/archiver v3.1.1+incompatible/go.mod h1:pYn/vJ47tuVltHdnA8YrDWOZ6yix4k0ZSKUBbWmkAFM= +github.com/go-vela/archiver/v3 v3.4.0 h1:c6GQRNTzr4Pn8HaxjzslIEiN89+kgZB4hLkXuBCOczI= +github.com/go-vela/archiver/v3 v3.4.0/go.mod h1:1HbXVOHBXsfzwSog3x7T6ZU9BUv+VEWnuaPLZS/v0/8= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -122,6 +129,8 @@ github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17w github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -209,6 +218,8 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -298,8 +309,11 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 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/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= From 05a09bb7e14d633aee221fcf39f2267807a3f091 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Mon, 20 Jan 2025 13:43:16 -0600 Subject: [PATCH 39/41] fields for buildkite and ymal --- compiler/types/yaml/buildkite/step.go | 2 ++ compiler/types/yaml/buildkite/step_test.go | 8 ++++---- compiler/types/yaml/buildkite/test_report.go | 12 +++++++++++- compiler/types/yaml/buildkite/testdata/step.yml | 7 +++++++ compiler/types/yaml/yaml/step.go | 2 +- compiler/types/yaml/yaml/step_test.go | 17 +++++++++++++++++ 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/compiler/types/yaml/buildkite/step.go b/compiler/types/yaml/buildkite/step.go index d03004928..88cde5fa7 100644 --- a/compiler/types/yaml/buildkite/step.go +++ b/compiler/types/yaml/buildkite/step.go @@ -61,6 +61,7 @@ func (s *StepSlice) ToPipeline() *pipeline.ContainerSlice { Pull: step.Pull, Ruleset: *step.Ruleset.ToPipeline(), Secrets: *step.Secrets.ToPipeline(), + TestReport: *step.TestReport.ToPipeline(), Ulimits: *step.Ulimits.ToPipeline(), Volumes: *step.Volumes.ToPipeline(), User: step.User, @@ -166,6 +167,7 @@ func (s *Step) ToYAML() *yaml.Step { Ruleset: *s.Ruleset.ToYAML(), Secrets: *s.Secrets.ToYAML(), Template: s.Template.ToYAML(), + TestReport: s.TestReport.ToYAML(), Ulimits: *s.Ulimits.ToYAML(), Volumes: *s.Volumes.ToYAML(), Parameters: s.Parameters, diff --git a/compiler/types/yaml/buildkite/step_test.go b/compiler/types/yaml/buildkite/step_test.go index cb3329dc0..18479c1b1 100644 --- a/compiler/types/yaml/buildkite/step_test.go +++ b/compiler/types/yaml/buildkite/step_test.go @@ -77,8 +77,8 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { }, }, TestReport: TestReport{ - Results: []string{"test.txt"}, - Attachments: []string{"test.log"}, + Results: []string{"test-results/*.xml"}, + Attachments: []string{"screenshots/**/*.png", " video/*.mp4"}, }, }, }, @@ -139,8 +139,8 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { }, }, TestReport: pipeline.TestReport{ - Results: []string{"test.txt"}, - Attachments: []string{"test.log"}, + Results: []string{"test-results/*.xml"}, + Attachments: []string{"screenshots/**/*.png", " video/*.mp4"}, }, }, }, diff --git a/compiler/types/yaml/buildkite/test_report.go b/compiler/types/yaml/buildkite/test_report.go index 7727e8475..0de529da0 100644 --- a/compiler/types/yaml/buildkite/test_report.go +++ b/compiler/types/yaml/buildkite/test_report.go @@ -1,6 +1,9 @@ package buildkite -import "github.com/go-vela/server/compiler/types/pipeline" +import ( + "github.com/go-vela/server/compiler/types/pipeline" + "github.com/go-vela/server/compiler/types/yaml/yaml" +) // TestReport represents the structure for test report configuration. type TestReport struct { @@ -38,3 +41,10 @@ func (t *TestReport) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } + +func (t *TestReport) ToYAML() yaml.TestReport { + return yaml.TestReport{ + Results: t.Results, + Attachments: t.Attachments, + } +} diff --git a/compiler/types/yaml/buildkite/testdata/step.yml b/compiler/types/yaml/buildkite/testdata/step.yml index 1d6d9cc93..1e15dd239 100644 --- a/compiler/types/yaml/buildkite/testdata/step.yml +++ b/compiler/types/yaml/buildkite/testdata/step.yml @@ -44,3 +44,10 @@ registry: index.docker.io repo: github/octocat tags: [ latest, dev ] + +- name: test-reports + image: golang:1.20 + pull: true + test_report: + results: ["test-results/*.xml"] + attachments: ["screenshots/**/*.png", " video/*.mp4"] \ No newline at end of file diff --git a/compiler/types/yaml/yaml/step.go b/compiler/types/yaml/yaml/step.go index 45e8bb800..8289520b3 100644 --- a/compiler/types/yaml/yaml/step.go +++ b/compiler/types/yaml/yaml/step.go @@ -60,12 +60,12 @@ func (s *StepSlice) ToPipeline() *pipeline.ContainerSlice { Pull: step.Pull, Ruleset: *step.Ruleset.ToPipeline(), Secrets: *step.Secrets.ToPipeline(), + TestReport: *step.TestReport.ToPipeline(), Ulimits: *step.Ulimits.ToPipeline(), Volumes: *step.Volumes.ToPipeline(), User: step.User, ReportAs: step.ReportAs, IDRequest: step.IDRequest, - TestReport: *step.TestReport.ToPipeline(), }) } diff --git a/compiler/types/yaml/yaml/step_test.go b/compiler/types/yaml/yaml/step_test.go index 343daf230..dabbc7c3f 100644 --- a/compiler/types/yaml/yaml/step_test.go +++ b/compiler/types/yaml/yaml/step_test.go @@ -76,6 +76,10 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { AccessMode: "ro", }, }, + TestReport: TestReport{ + Results: []string{"test-results/*.xml"}, + Attachments: []string{"screenshots/**/*.png", " video/*.mp4"}, + }, }, }, want: &pipeline.ContainerSlice{ @@ -134,6 +138,10 @@ func TestYaml_StepSlice_ToPipeline(t *testing.T) { AccessMode: "ro", }, }, + TestReport: pipeline.TestReport{ + Results: []string{"test-results/*.xml"}, + Attachments: []string{"screenshots/**/*.png", " video/*.mp4"}, + }, }, }, }, @@ -213,6 +221,15 @@ func TestYaml_StepSlice_UnmarshalYAML(t *testing.T) { }, }, }, + { + Name: "test-reports", + Image: "golang:1.20", + Pull: "always", + TestReport: TestReport{ + Results: []string{"test-results/*.xml"}, + Attachments: []string{"screenshots/**/*.png", " video/*.mp4"}, + }, + }, }, }, { From 657fe54f4854848e1fa9b7390c9b8adb51900784 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Tue, 11 Feb 2025 20:43:58 -0600 Subject: [PATCH 40/41] updates to storage client --- api/admin/storage.go | 76 --------------- api/build/storage.go | 87 +++++++++++++++++ api/storage/doc.go | 8 ++ api/storage/storage.go | 135 ++++++++++++++++++++++++++ api/types/pipeline.go | 29 ++++++ api/types/storage_info.go | 90 +++++++++++++++++ api/types/storage_info_test.go | 89 +++++++++++++++++ cmd/vela-server/metadata.go | 22 +++++ cmd/vela-server/server.go | 3 + compiler/native/validate.go | 4 +- compiler/native/validate_test.go | 8 +- compiler/types/yaml/buildkite/step.go | 14 +-- docker-compose.yml | 9 +- internal/metadata.go | 7 ++ mock/server/server.go | 3 + mock/server/worker.go | 30 ++++++ router/admin.go | 2 +- router/build.go | 6 +- router/middleware/signing.go | 27 ++++++ router/middleware/signing_test.go | 96 ++++++++++++++++++ router/router.go | 3 + router/storage.go | 23 +++++ storage/flags.go | 12 +-- storage/minio/create_bucket.go | 16 ++- storage/minio/upload.go | 11 +++ storage/service.go | 2 + storage/setup.go | 12 +-- storage/storage.go | 6 +- 28 files changed, 717 insertions(+), 113 deletions(-) create mode 100644 api/build/storage.go create mode 100644 api/storage/doc.go create mode 100644 api/storage/storage.go create mode 100644 api/types/storage_info.go create mode 100644 api/types/storage_info_test.go create mode 100644 router/storage.go diff --git a/api/admin/storage.go b/api/admin/storage.go index 3e74e0fdb..8120863b6 100644 --- a/api/admin/storage.go +++ b/api/admin/storage.go @@ -253,82 +253,6 @@ func SetBucketLifecycle(c *gin.Context) { c.Status(http.StatusOK) } -// swagger:operation POST /api/v1/admin/storage/bucket/upload admin UploadObject -// -// # Upload an object to a bucket -// -// --- -// produces: -// - application/json -// parameters: -// - in: body -// name: body -// description: The object to be uploaded -// required: true -// schema: -// type: object -// properties: -// bucketName: -// type: string -// objectName: -// type: string -// objectData: -// type: string -// -// security: -// - ApiKeyAuth: [] -// -// responses: -// -// '201': -// description: Successfully uploaded the object -// '400': -// description: Invalid request payload -// schema: -// "$ref": "#/definitions/Error" -// '500': -// description: Unexpected server error -// schema: -// "$ref": "#/definitions/Error" -// -// UploadObject represents the API handler to upload an object to a bucket. -func UploadObject(c *gin.Context) { - l := c.MustGet("logger").(*logrus.Entry) - ctx := c.Request.Context() - - l.Debug("platform admin: uploading object") - - // capture body from API request - input := new(types.Object) - - err := c.Bind(input) - if err != nil { - retErr := fmt.Errorf("unable to decode JSON for object %s: %w", input.ObjectName, err) - - util.HandleError(c, http.StatusBadRequest, retErr) - - return - } - if input.Bucket.BucketName == "" || input.ObjectName == "" { - retErr := fmt.Errorf("bucketName and objectName are required") - util.HandleError(c, http.StatusBadRequest, retErr) - return - } - if input.FilePath == "" { - retErr := fmt.Errorf("file path is required") - util.HandleError(c, http.StatusBadRequest, retErr) - return - } - err = storage.FromGinContext(c).Upload(ctx, input) - if err != nil { - retErr := fmt.Errorf("unable to upload object: %w", err) - util.HandleError(c, http.StatusInternalServerError, retErr) - return - } - - c.Status(http.StatusCreated) -} - // swagger:operation GET /api/v1/admin/storage/bucket/download admin DownloadObject // // # Download an object from a bucket diff --git a/api/build/storage.go b/api/build/storage.go new file mode 100644 index 000000000..bd94b97d5 --- /dev/null +++ b/api/build/storage.go @@ -0,0 +1,87 @@ +package build + +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/go-vela/server/api/types" + "github.com/go-vela/server/storage" + "github.com/go-vela/server/util" + "github.com/sirupsen/logrus" + "net/http" +) + +// swagger:operation POST /api/v1/repos/{org}/{repo}/builds/{build}/storage/upload builds UploadObject +// +// # Upload an object to a bucket +// +// --- +// produces: +// - application/json +// parameters: +// - in: body +// name: body +// description: The object to be uploaded +// required: true +// schema: +// type: object +// properties: +// bucketName: +// type: string +// objectName: +// type: string +// objectData: +// type: string +// +// security: +// - ApiKeyAuth: [] +// +// responses: +// +// '201': +// description: Successfully uploaded the object +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" +// +// UploadObject represents the API handler to upload an object to a bucket. +func UploadObject(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: uploading object") + + // capture body from API request + input := new(types.Object) + + err := c.Bind(input) + if err != nil { + retErr := fmt.Errorf("unable to decode JSON for object %s: %w", input.ObjectName, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + if input.Bucket.BucketName == "" || input.ObjectName == "" { + retErr := fmt.Errorf("bucketName and objectName are required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + if input.FilePath == "" { + retErr := fmt.Errorf("file path is required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + err = storage.FromGinContext(c).Upload(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to upload object: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.Status(http.StatusCreated) +} diff --git a/api/storage/doc.go b/api/storage/doc.go new file mode 100644 index 000000000..aae3e99dd --- /dev/null +++ b/api/storage/doc.go @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Package storage provides the storage handlers for the Vela API. +// +// Usage: +// +// import "github.com/go-vela/server/api/storage" +package storage diff --git a/api/storage/storage.go b/api/storage/storage.go new file mode 100644 index 000000000..1981e92ea --- /dev/null +++ b/api/storage/storage.go @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: Apache-2.0 + +package storage + +import ( + "fmt" + "github.com/go-vela/server/storage" + "github.com/go-vela/server/util" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + "github.com/go-vela/server/api/types" +) + +// swagger:operation POST /api/v1/storage/info storage Info +// +// Get storage credentials +// +// --- +// produces: +// - application/json +// security: +// - ApiKeyAuth: [] +// responses: +// '200': +// description: Successfully retrieved storage credentials +// schema: +// "$ref": "#/definitions/StorageInfo" +// '401': +// description: Unauthorized +// schema: +// "$ref": "#/definitions/Error" + +// Info represents the API handler to +// retrieve storage credentials as part of worker onboarding. +func Info(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + + l.Info("requesting storage credentials with registration token") + + // extract the public key that was packed into gin context + k := c.MustGet("access-key").(string) + + // extract the storage-address that was packed into gin context + a := c.MustGet("storage-address").(string) + + // extract the secret key that was packed into gin context + s := c.MustGet("secret-key").(string) + + wr := types.StorageInfo{ + StorageAccessKey: &k, + StorageAddress: &a, + StorageSecretKey: &s, + } + + c.JSON(http.StatusOK, wr) +} + +// swagger:operation POST /api/v1/repos/{org}/{repo}/builds/{build}/storage/upload builds UploadObject +// +// # Upload an object to a bucket +// +// --- +// produces: +// - application/json +// parameters: +// - in: body +// name: body +// description: The object to be uploaded +// required: true +// schema: +// type: object +// properties: +// bucketName: +// type: string +// objectName: +// type: string +// objectData: +// type: string +// +// security: +// - ApiKeyAuth: [] +// +// responses: +// +// '201': +// description: Successfully uploaded the object +// '400': +// description: Invalid request payload +// schema: +// "$ref": "#/definitions/Error" +// '500': +// description: Unexpected server error +// schema: +// "$ref": "#/definitions/Error" +// +// UploadObject represents the API handler to upload an object to a bucket. +func UploadObject(c *gin.Context) { + l := c.MustGet("logger").(*logrus.Entry) + ctx := c.Request.Context() + + l.Debug("platform admin: uploading object") + + // capture body from API request + input := new(types.Object) + + err := c.Bind(input) + if err != nil { + retErr := fmt.Errorf("unable to decode JSON for object %s: %w", input.ObjectName, err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + if input.Bucket.BucketName == "" || input.ObjectName == "" { + retErr := fmt.Errorf("bucketName and objectName are required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + if input.FilePath == "" { + retErr := fmt.Errorf("file path is required") + util.HandleError(c, http.StatusBadRequest, retErr) + return + } + err = storage.FromGinContext(c).Upload(ctx, input) + if err != nil { + retErr := fmt.Errorf("unable to upload object: %w", err) + util.HandleError(c, http.StatusInternalServerError, retErr) + return + } + + c.Status(http.StatusCreated) +} diff --git a/api/types/pipeline.go b/api/types/pipeline.go index dd0ff617d..6fcb2744c 100644 --- a/api/types/pipeline.go +++ b/api/types/pipeline.go @@ -24,6 +24,7 @@ type Pipeline struct { Stages *bool `json:"stages,omitempty"` Steps *bool `json:"steps,omitempty"` Templates *bool `json:"templates,omitempty"` + TestReport *bool `json:"test_report,omitempty"` Warnings *[]string `json:"warnings,omitempty"` // swagger:strfmt base64 Data *[]byte `json:"data,omitempty"` @@ -237,6 +238,19 @@ func (p *Pipeline) GetData() []byte { return *p.Data } +// GetTestReport returns the TestReport results field. +// +// When the provided Pipeline type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (p *Pipeline) GetTestReport() bool { + // return zero value if Pipeline type or TestReport field is nil + if p == nil || p.TestReport == nil { + return false + } + + return *p.TestReport +} + // SetID sets the ID field. // // When the provided Pipeline type is nil, it @@ -419,6 +433,19 @@ func (p *Pipeline) SetTemplates(v bool) { p.Templates = &v } +// SetTestReport sets the TestReport field. +// +// When the provided Pipeline type is nil, it +// will set nothing and immediately return. +func (p *Pipeline) SetTestReport(v bool) { + // return if Pipeline type is nil + if p == nil { + return + } + + p.TestReport = &v +} + // SetWarnings sets the Warnings field. // // When the provided Pipeline type is nil, it @@ -461,6 +488,7 @@ func (p *Pipeline) String() string { Stages: %t, Steps: %t, Templates: %t, + TestReport: %t, Type: %s, Version: %s, Warnings: %v, @@ -478,6 +506,7 @@ func (p *Pipeline) String() string { p.GetStages(), p.GetSteps(), p.GetTemplates(), + p.GetTestReport(), p.GetType(), p.GetVersion(), p.GetWarnings(), diff --git a/api/types/storage_info.go b/api/types/storage_info.go new file mode 100644 index 000000000..94ab1650d --- /dev/null +++ b/api/types/storage_info.go @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +// StorageInfo is the API representation of a StorageInfo. +// +// swagger:model StorageInfo +type StorageInfo struct { + StorageAccessKey *string `json:"storage_access_key,omitempty"` + StorageSecretKey *string `json:"storage_secret_key,omitempty"` + StorageAddress *string `json:"storage_address,omitempty"` +} + +// GetAccessKey returns the StorageAccessKey field. +// +// When the provided StorageInfo type is nil, or the field within +// the type is nil, it returns an empty string for the field. +func (w *StorageInfo) GetAccessKey() string { + // return zero value if StorageInfo type or StorageAccessKey field is nil + if w == nil || w.StorageAccessKey == nil { + return "" + } + + return *w.StorageAccessKey +} + +// GetSecretKey returns the StorageSecretKey field. +// +// When the provided StorageInfo type is nil, or the field within +// the type is nil, it returns an empty string for the field. +func (w *StorageInfo) GetSecretKey() string { + // return zero value if StorageInfo type or StorageSecretKey field is nil + if w == nil || w.StorageSecretKey == nil { + return "" + } + + return *w.StorageSecretKey +} + +// GetStorageAddress returns the StorageAddress field. +// +// When the provided StorageInfo type is nil, or the field within +// the type is nil, it returns an empty string for the field. +func (w *StorageInfo) GetStorageAddress() string { + // return zero value if StorageInfo type or StorageAddress field is nil + if w == nil || w.StorageAddress == nil { + return "" + } + + return *w.StorageAddress +} + +// SetAccessKey sets the StorageAccessKey field. +// +// When the provided StorageInfo type is nil, it +// will set nothing and immediately return. +func (w *StorageInfo) SetAccessKey(v string) { + // return if StorageInfo type is nil + if w == nil { + return + } + + w.StorageAccessKey = &v +} + +// SetSecretKey sets the StorageSecretKey field. +// +// When the provided StorageInfo type is nil, it +// will set nothing and immediately return. +func (w *StorageInfo) SetSecretKey(v string) { + // return if StorageInfo type is nil + if w == nil { + return + } + + w.StorageSecretKey = &v +} + +// SetStorageAddress sets the StorageAddress field. +// +// When the provided StorageInfo type is nil, it +// will set nothing and immediately return. +func (w *StorageInfo) SetStorageAddress(v string) { + // return if StorageInfo type is nil + if w == nil { + return + } + + w.StorageAddress = &v +} diff --git a/api/types/storage_info_test.go b/api/types/storage_info_test.go new file mode 100644 index 000000000..7dd75f004 --- /dev/null +++ b/api/types/storage_info_test.go @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "testing" +) + +func TestTypes_StorageInfo_Getters(t *testing.T) { + // setup tests + tests := []struct { + sI *StorageInfo + want *StorageInfo + }{ + { + sI: testStorageInfo(), + want: testStorageInfo(), + }, + { + sI: new(StorageInfo), + want: new(StorageInfo), + }, + } + + // run tests + for _, test := range tests { + if test.sI.GetAccessKey() != test.want.GetAccessKey() { + t.Errorf("GetAccessKey is %v, want %v", test.sI.GetAccessKey(), test.want.GetAccessKey()) + } + + if test.sI.GetSecretKey() != test.want.GetSecretKey() { + t.Errorf("GetSecretKey is %v, want %v", test.sI.GetSecretKey(), test.want.GetSecretKey()) + } + + if test.sI.GetStorageAddress() != test.want.GetStorageAddress() { + t.Errorf("GetStorageAddress is %v, want %v", test.sI.GetStorageAddress(), test.want.GetStorageAddress()) + } + } +} + +func TestTypes_StorageInfo_Setters(t *testing.T) { + // setup types + var sI *StorageInfo + + // setup tests + tests := []struct { + sI *StorageInfo + want *StorageInfo + }{ + { + sI: testStorageInfo(), + want: testStorageInfo(), + }, + { + sI: sI, + want: new(StorageInfo), + }, + } + + // run tests + for _, test := range tests { + test.sI.SetAccessKey(test.want.GetAccessKey()) + test.sI.SetSecretKey(test.want.GetSecretKey()) + test.sI.SetStorageAddress(test.want.GetStorageAddress()) + + if test.sI.GetAccessKey() != test.want.GetAccessKey() { + t.Errorf("GetAccessKey is %v, want %v", test.sI.GetAccessKey(), test.want.GetAccessKey()) + } + + if test.sI.GetSecretKey() != test.want.GetSecretKey() { + t.Errorf("GetSecretKey is %v, want %v", test.sI.GetSecretKey(), test.want.GetSecretKey()) + } + + if test.sI.GetStorageAddress() != test.want.GetStorageAddress() { + t.Errorf("GetStorageAddress is %v, want %v", test.sI.GetStorageAddress(), test.want.GetStorageAddress()) + } + } +} + +// testStorageInfo is a test helper function to register a StorageInfo +// type with all fields set to a fake value. +func testStorageInfo() *StorageInfo { + sI := new(StorageInfo) + sI.SetAccessKey("fakeAccessKey") + sI.SetSecretKey("fakeSecretKey") + sI.SetStorageAddress("http://localhost:8080") + + return sI +} diff --git a/cmd/vela-server/metadata.go b/cmd/vela-server/metadata.go index 11da44af4..b09d34ed0 100644 --- a/cmd/vela-server/metadata.go +++ b/cmd/vela-server/metadata.go @@ -45,6 +45,13 @@ func setupMetadata(c *cli.Context) (*internal.Metadata, error) { m.Vela = vela + storage, err := metadataStorage(c) + if err != nil { + return nil, err + } + + m.Storage = storage + return m, nil } @@ -93,6 +100,21 @@ func metadataSource(c *cli.Context) (*internal.Source, error) { }, nil } +// helper function to capture the queue metadata from the CLI arguments. +func metadataStorage(c *cli.Context) (*internal.Storage, error) { + logrus.Trace("creating storage metadata from CLI configuration") + + u, err := url.Parse(c.String("storage.addr")) + if err != nil { + return nil, err + } + + return &internal.Storage{ + Driver: c.String("storage.driver"), + Host: u.Host, + }, nil +} + // helper function to capture the Vela metadata from the CLI arguments. // //nolint:unparam // ignore unparam for now diff --git a/cmd/vela-server/server.go b/cmd/vela-server/server.go index c7e8ac986..ebca360dd 100644 --- a/cmd/vela-server/server.go +++ b/cmd/vela-server/server.go @@ -215,6 +215,9 @@ func server(c *cli.Context) error { middleware.ScheduleFrequency(c.Duration("schedule-minimum-frequency")), middleware.TracingClient(tc), middleware.TracingInstrumentation(tc), + middleware.StorageAddress(c.String("storage.addr")), + middleware.StorageAccessKey(c.String("storage.access.key")), + middleware.StorageSecretKey(c.String("storage.secret.key")), ) addr, err := url.Parse(c.String("server-addr")) diff --git a/compiler/native/validate.go b/compiler/native/validate.go index 77a8578f2..ca83442b3 100644 --- a/compiler/native/validate.go +++ b/compiler/native/validate.go @@ -152,7 +152,9 @@ func validateSteps(s yaml.StepSlice) error { if len(step.Commands) == 0 && len(step.Environment) == 0 && len(step.Parameters) == 0 && len(step.Secrets) == 0 && - len(step.Template.Name) == 0 && !step.Detach { + len(step.Template.Name) == 0 && len(step.TestReport.Results) == 0 && + len(step.TestReport.Attachments) == 0 && !step.Detach { + return fmt.Errorf("no commands, environment, parameters, secrets or template provided for step %s", step.Name) } } diff --git a/compiler/native/validate_test.go b/compiler/native/validate_test.go index 9c27e82e7..016173527 100644 --- a/compiler/native/validate_test.go +++ b/compiler/native/validate_test.go @@ -640,10 +640,10 @@ func TestNative_Validate_TestReport(t *testing.T) { Image: "alpine", Name: str, Pull: "always", - TestReport: yaml.TestReport{ - Results: []string{"results.xml"}, - Attachments: []string{"attachments"}, - }, + //TestReport: yaml.TestReport{ + // Results: []string{"results.xml"}, + // Attachments: []string{"attachments"}, + //}, }, }, } diff --git a/compiler/types/yaml/buildkite/step.go b/compiler/types/yaml/buildkite/step.go index 88cde5fa7..7389c0754 100644 --- a/compiler/types/yaml/buildkite/step.go +++ b/compiler/types/yaml/buildkite/step.go @@ -167,13 +167,13 @@ func (s *Step) ToYAML() *yaml.Step { Ruleset: *s.Ruleset.ToYAML(), Secrets: *s.Secrets.ToYAML(), Template: s.Template.ToYAML(), - TestReport: s.TestReport.ToYAML(), - Ulimits: *s.Ulimits.ToYAML(), - Volumes: *s.Volumes.ToYAML(), - Parameters: s.Parameters, - User: s.User, - ReportAs: s.ReportAs, - IDRequest: s.IDRequest, + //TestReport: s.TestReport.ToYAML(), + Ulimits: *s.Ulimits.ToYAML(), + Volumes: *s.Volumes.ToYAML(), + Parameters: s.Parameters, + User: s.User, + ReportAs: s.ReportAs, + IDRequest: s.IDRequest, } } diff --git a/docker-compose.yml b/docker-compose.yml index 8aa009e7c..8ca4fe35c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,7 @@ services: VELA_OTEL_TRACING_SAMPLER_RATELIMIT_PER_SECOND: 100 VELA_STORAGE_ENABLE: 'true' VELA_STORAGE_DRIVER: minio - VELA_STORAGE_ENDPOINT: "http://minio:9000" + VELA_STORAGE_ADDRESS: "http://minio:9000" VELA_STORAGE_ACCESS_KEY: minio_access_user VELA_STORAGE_SECRET_KEY: minio_secret_key VELA_STORAGE_USE_SSL: 'false' @@ -75,7 +75,7 @@ services: # container_name: worker # image: target/vela-worker:latest build: - context: ../. + context: ../worker dockerfile: Dockerfile container_name: worker networks: @@ -94,6 +94,11 @@ services: VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' WORKER_ADDR: 'http://worker:8080' WORKER_CHECK_IN: 2m + VELA_EXECUTOR_OUTPUTS_IMAGE: 'alpine:latest' + VELA_STORAGE_ENABLE: 'true' + VELA_STORAGE_DRIVER: minio + VELA_STORAGE_ADDRESS: "http://minio:9000" + VELA_STORAGE_BUCKET: vela restart: always ports: - '8081:8080' diff --git a/internal/metadata.go b/internal/metadata.go index e87868d4e..5b34159d2 100644 --- a/internal/metadata.go +++ b/internal/metadata.go @@ -24,6 +24,12 @@ type ( Host string `json:"host"` } + // Storage is the extra set of Storage data passed to the compiler. + Storage struct { + Driver string `json:"driver"` + Host string `json:"host"` + } + // Vela is the extra set of Vela data passed to the compiler. Vela struct { Address string `json:"address"` @@ -41,5 +47,6 @@ type ( Queue *Queue `json:"queue"` Source *Source `json:"source"` Vela *Vela `json:"vela"` + Storage *Storage `json:"storage"` } ) diff --git a/mock/server/server.go b/mock/server/server.go index 62cdc47fc..cd775288d 100644 --- a/mock/server/server.go +++ b/mock/server/server.go @@ -157,6 +157,9 @@ func FakeHandler() http.Handler { // mock endpoint for queue credentials e.GET("/api/v1/queue/info", getQueueCreds) + + // mock endpoint for storage credentials + e.GET("/api/v1/storage/info", getStorageCreds) return e } diff --git a/mock/server/worker.go b/mock/server/worker.go index c92b508ec..4dc4c53e9 100644 --- a/mock/server/worker.go +++ b/mock/server/worker.go @@ -188,6 +188,15 @@ const ( "queue_public_key": "DXeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ98zmko=", "queue_address": "redis://redis:6000" }` + + // StorageInfoResp represents a JSON return for an admin requesting a storage registration info. + // + //not actual credentials. + StorageInfoResp = `{ + "storage_access_key": "DXeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ98zmko=", + "storage_secret_key": "DXeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ98zmko=", + "storage_address": "http://storage:9000" + }` ) // getWorkers returns mock JSON for a http GET. @@ -334,3 +343,24 @@ func getQueueCreds(c *gin.Context) { c.JSON(http.StatusCreated, body) } + +// getStorageCreds returns mock JSON for a http GET. +// Pass "" to Authorization header to test receiving a http 401 response. +func getStorageCreds(c *gin.Context) { + token := c.Request.Header.Get("Authorization") + // verify token if empty + if token == "" { + msg := "unable get storage credentials; invalid registration token" + + c.AbortWithStatusJSON(http.StatusUnauthorized, api.Error{Message: &msg}) + + return + } + + data := []byte(StorageInfoResp) + + var body api.StorageInfo + _ = json.Unmarshal(data, &body) + + c.JSON(http.StatusCreated, body) +} diff --git a/router/admin.go b/router/admin.go index cff6dd869..d24e967ca 100644 --- a/router/admin.go +++ b/router/admin.go @@ -71,7 +71,7 @@ func AdminHandlers(base *gin.RouterGroup) { // Admin storage object endpoints _admin.POST("/storage/object/download", admin.DownloadObject) - _admin.POST("/storage/object", admin.UploadObject) + //_admin.POST("/storage/object", admin.UploadObject) // Admin storage presign endpoints _admin.GET("/storage/presign", admin.GetPresignedURL) diff --git a/router/build.go b/router/build.go index 924576763..12455e464 100644 --- a/router/build.go +++ b/router/build.go @@ -4,7 +4,6 @@ package router import ( "github.com/gin-gonic/gin" - "github.com/go-vela/server/api/build" "github.com/go-vela/server/api/log" "github.com/go-vela/server/router/middleware" @@ -44,7 +43,8 @@ import ( // POST /api/v1/repos/:org/:repo/builds/:build/steps/:step/logs // GET /api/v1/repos/:org/:repo/builds/:build/steps/:step/logs // PUT /api/v1/repos/:org/:repo/builds/:build/steps/:step/logs -// DELETE /api/v1/repos/:org/:repo/builds/:build/steps/:step/logs . +// DELETE /api/v1/repos/:org/:repo/builds/:build/steps/:step/logs +// POST /api/v1/repos/:org/:repo/builds/:build/storage/upload func BuildHandlers(base *gin.RouterGroup) { // Builds endpoints builds := base.Group("/builds") @@ -68,6 +68,8 @@ func BuildHandlers(base *gin.RouterGroup) { b.GET("/graph", perm.MustRead(), build.GetBuildGraph) b.GET("/executable", perm.MustBuildAccess(), build.GetBuildExecutable) + //b.POST("/storage/upload", perm.MustBuildAccess(), build.UploadObject) + // Service endpoints // * Log endpoints ServiceHandlers(b) diff --git a/router/middleware/signing.go b/router/middleware/signing.go index 6b9dd4c1f..796c7c8a3 100644 --- a/router/middleware/signing.go +++ b/router/middleware/signing.go @@ -32,3 +32,30 @@ func QueueAddress(address string) gin.HandlerFunc { c.Next() } } + +// StorageAccessKey is a middleware function that attaches the access key used +// to open the connection to the storage. +func StorageAccessKey(key string) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("access-key", key) + c.Next() + } +} + +// StorageSecretKey is a middleware function that attaches the secret key used +// to open the connection to the storage. +func StorageSecretKey(key string) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("secret-key", key) + c.Next() + } +} + +// StorageAddress is a middleware function that attaches the storage address used +// to open the connection to the queue. +func StorageAddress(address string) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("storage-address", address) + c.Next() + } +} diff --git a/router/middleware/signing_test.go b/router/middleware/signing_test.go index 3f152a3c9..839448ca6 100644 --- a/router/middleware/signing_test.go +++ b/router/middleware/signing_test.go @@ -106,3 +106,99 @@ func TestMiddleware_QueueAddress(t *testing.T) { t.Errorf("QueueAddress is %v, want %v", got, want) } } + +func TestMiddleware_StorageAddress(t *testing.T) { + // setup types + got := "" + want := "foobar" + + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + context, engine := gin.CreateTestContext(resp) + context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) + + // setup mock server + engine.Use(StorageAddress(want)) + engine.GET("/health", func(c *gin.Context) { + got = c.Value("storage-address").(string) + + c.Status(http.StatusOK) + }) + + // run test + engine.ServeHTTP(context.Writer, context.Request) + + if resp.Code != http.StatusOK { + t.Errorf("StorageAddress returned %v, want %v", resp.Code, http.StatusOK) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("StorageAddress is %v, want %v", got, want) + } +} + +func TestMiddleware_StorageAccessKey(t *testing.T) { + // setup types + got := "" + want := "foobar" + + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + context, engine := gin.CreateTestContext(resp) + context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) + + // setup mock server + engine.Use(StorageAccessKey(want)) + engine.GET("/health", func(c *gin.Context) { + got = c.Value("access-key").(string) + + c.Status(http.StatusOK) + }) + + // run test + engine.ServeHTTP(context.Writer, context.Request) + + if resp.Code != http.StatusOK { + t.Errorf("StorageAccessKey returned %v, want %v", resp.Code, http.StatusOK) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("StorageAccessKey is %v, want %v", got, want) + } +} + +func TestMiddleware_StorageSecretKey(t *testing.T) { + // setup types + got := "" + want := "foobar" + + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + context, engine := gin.CreateTestContext(resp) + context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) + + // setup mock server + engine.Use(StorageSecretKey(want)) + engine.GET("/health", func(c *gin.Context) { + got = c.Value("secret-key").(string) + + c.Status(http.StatusOK) + }) + + // run test + engine.ServeHTTP(context.Writer, context.Request) + + if resp.Code != http.StatusOK { + t.Errorf("StorageSecretKey returned %v, want %v", resp.Code, http.StatusOK) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("StorageSecretKey is %v, want %v", got, want) + } +} diff --git a/router/router.go b/router/router.go index ca131ae63..1b1fb57d4 100644 --- a/router/router.go +++ b/router/router.go @@ -155,6 +155,9 @@ func Load(options ...gin.HandlerFunc) *gin.Engine { // Queue endpoints QueueHandlers(baseAPI) + + // Storage endpoints + StorageHandlers(baseAPI) } // end of api return r diff --git a/router/storage.go b/router/storage.go new file mode 100644 index 000000000..c969caea2 --- /dev/null +++ b/router/storage.go @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 + +package router + +import ( + "github.com/gin-gonic/gin" + "github.com/go-vela/server/api/storage" + + "github.com/go-vela/server/router/middleware/perm" +) + +// StorageHandlers is a function that extends the provided base router group +// with the API handlers for storage functionality. +// +// GET /api/v1/storage/info . +func StorageHandlers(base *gin.RouterGroup) { + // Storage endpoints + _storage := base.Group("/storage") + { + _storage.GET("/info", perm.MustWorkerRegisterToken(), storage.Info) + _storage.POST("/upload", storage.UploadObject) + } // end of storage endpoints +} diff --git a/storage/flags.go b/storage/flags.go index 7eb89fd84..12567f8f7 100644 --- a/storage/flags.go +++ b/storage/flags.go @@ -15,23 +15,23 @@ var Flags = []cli.Flag{ Usage: "enable object storage", }, &cli.StringFlag{ - EnvVars: []string{"VELA_STORAGE_DRIVER"}, - Name: "storage.driver.name", + EnvVars: []string{"VELA_STORAGE_DRIVER", "STORAGE_DRIVER"}, + Name: "storage.driver", Usage: "object storage driver", }, &cli.StringFlag{ - EnvVars: []string{"VELA_STORAGE_ENDPOINT"}, - Name: "storage.endpoint.name", + EnvVars: []string{"VELA_STORAGE_ADDRESS", "STORAGE_ADDRESS"}, + Name: "storage.addr", Usage: "set the storage endpoint (ex. scheme://host:port)", }, &cli.StringFlag{ - EnvVars: []string{"VELA_STORAGE_ACCESS_KEY"}, + EnvVars: []string{"VELA_STORAGE_ACCESS_KEY", "STORAGE_ACCESS_KEY"}, Name: "storage.access.key", Usage: "set storage access key", }, &cli.StringFlag{ - EnvVars: []string{"VELA_STORAGE_SECRET_KEY"}, + EnvVars: []string{"VELA_STORAGE_SECRET_KEY", "STORAGE_SECRET_KEY"}, Name: "storage.secret.key", Usage: "set storage secret key", }, diff --git a/storage/minio/create_bucket.go b/storage/minio/create_bucket.go index 0a53554d6..8b7bb9a31 100644 --- a/storage/minio/create_bucket.go +++ b/storage/minio/create_bucket.go @@ -2,6 +2,7 @@ package minio import ( "context" + "fmt" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" ) @@ -19,13 +20,18 @@ func (c *MinioClient) CreateBucket(ctx context.Context, bucket *api.Bucket) erro ObjectLocking: bucket.Options.ObjectLocking, } } + + exists, errBucketExists := c.BucketExists(ctx, bucket) + if errBucketExists != nil && exists { + c.Logger.Tracef("Bucket %s already exists", bucket.BucketName) + + return fmt.Errorf("bucket %s already exists", bucket.BucketName) + } + err := c.client.MakeBucket(ctx, bucket.BucketName, opts) if err != nil { - exists, errBucketExists := c.BucketExists(ctx, bucket) - if errBucketExists == nil && exists { - c.Logger.Tracef("Bucket %s already exists", bucket.BucketName) - return err - } + + c.Logger.Errorf("unable to create bucket %s: %v", bucket.BucketName, err) return err } return nil diff --git a/storage/minio/upload.go b/storage/minio/upload.go index ca12ffc12..1394ac652 100644 --- a/storage/minio/upload.go +++ b/storage/minio/upload.go @@ -4,11 +4,22 @@ import ( "context" api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" + "io" ) // Upload uploads an object to a bucket in MinIO.ts func (c *MinioClient) Upload(ctx context.Context, object *api.Object) error { c.Logger.Tracef("uploading data to bucket %s", object.Bucket.BucketName) _, err := c.client.FPutObject(ctx, object.Bucket.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) + + return err +} + +// UploadObject uploads an object to a bucket in MinIO.ts +func (c *MinioClient) UploadObject(ctx context.Context, object *api.Object, reader io.Reader, size int64) error { + c.Logger.Tracef("uploading data to bucket %s", object.Bucket.BucketName) + //_, err := c.client.FPutObject(ctx, object.Bucket.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) + _, err := c.client.PutObject(ctx, object.Bucket.BucketName, object.ObjectName, reader, size, minio.PutObjectOptions{}) + return err } diff --git a/storage/service.go b/storage/service.go index f45af8e75..cdc225823 100644 --- a/storage/service.go +++ b/storage/service.go @@ -3,6 +3,7 @@ package storage import ( "context" api "github.com/go-vela/server/api/types" + "io" ) // Storage defines the service interface for object storage operations. @@ -15,6 +16,7 @@ type Storage interface { // Object Operations StatObject(ctx context.Context, object *api.Object) (*api.Object, error) Upload(ctx context.Context, object *api.Object) error + UploadObject(ctx context.Context, object *api.Object, reader io.Reader, size int64) error Download(ctx context.Context, object *api.Object) error Delete(ctx context.Context, object *api.Object) error ListObjects(ctx context.Context, bucketName string) ([]string, error) diff --git a/storage/setup.go b/storage/setup.go index f89354668..740b5b824 100644 --- a/storage/setup.go +++ b/storage/setup.go @@ -44,9 +44,9 @@ func (s *Setup) Validate() error { logrus.Trace("validating Storage setup for client") // verify storage is enabled - if !s.Enable { - return fmt.Errorf("Storage is not enabled") - } + //if !s.Enable { + // return fmt.Errorf("Storage is not enabled") + //} // verify an endpoint was provided if len(s.Endpoint) == 0 { @@ -64,9 +64,9 @@ func (s *Setup) Validate() error { } // verify a bucket was provided - if len(s.Bucket) == 0 { - return fmt.Errorf("no storage bucket provided") - } + //if len(s.Bucket) == 0 { + // return fmt.Errorf("no storage bucket provided") + //} // setup is valid return nil diff --git a/storage/storage.go b/storage/storage.go index 44469e6b3..5c57be0df 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -15,8 +15,8 @@ func FromCLIContext(c *cli.Context) (Storage, error) { // S3 configuration _setup := &Setup{ Enable: c.Bool("storage.enable"), - Driver: c.String("storage.driver.name"), - Endpoint: c.String("storage.endpoint.name"), + Driver: c.String("storage.driver"), + Endpoint: c.String("storage.addr"), AccessKey: c.String("storage.access.key"), SecretKey: c.String("storage.secret.key"), Bucket: c.String("storage.bucket.name"), @@ -38,7 +38,7 @@ func New(s *Setup) (Storage, error) { // err := s.Validate() if err != nil { - return nil, err + return nil, fmt.Errorf("unable to validate storage setup: %w", err) } logrus.Debug("creating storage client from setup") // process the storage driver being provided From d190aaf7c6b6c5d771277c66d2cdba093aae8b11 Mon Sep 17 00:00:00 2001 From: TimHuynh Date: Tue, 18 Feb 2025 18:56:37 -0600 Subject: [PATCH 41/41] upload with content type and size --- storage/minio/upload.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/storage/minio/upload.go b/storage/minio/upload.go index 1394ac652..e325070b6 100644 --- a/storage/minio/upload.go +++ b/storage/minio/upload.go @@ -5,6 +5,8 @@ import ( api "github.com/go-vela/server/api/types" "github.com/minio/minio-go/v7" "io" + "mime" + "path/filepath" ) // Upload uploads an object to a bucket in MinIO.ts @@ -17,9 +19,17 @@ func (c *MinioClient) Upload(ctx context.Context, object *api.Object) error { // UploadObject uploads an object to a bucket in MinIO.ts func (c *MinioClient) UploadObject(ctx context.Context, object *api.Object, reader io.Reader, size int64) error { - c.Logger.Tracef("uploading data to bucket %s", object.Bucket.BucketName) - //_, err := c.client.FPutObject(ctx, object.Bucket.BucketName, object.ObjectName, object.FilePath, minio.PutObjectOptions{}) - _, err := c.client.PutObject(ctx, object.Bucket.BucketName, object.ObjectName, reader, size, minio.PutObjectOptions{}) + c.Logger.Infof("uploading data to bucket %s", object.Bucket.BucketName) + ext := filepath.Ext(object.FilePath) + contentType := mime.TypeByExtension(ext) - return err + c.Logger.Infof("uploading object %s with content type %s", object.ObjectName, contentType) + info, err := c.client.PutObject(ctx, object.Bucket.BucketName, object.ObjectName, reader, size, + minio.PutObjectOptions{ContentType: contentType}) + if err != nil { + c.Logger.Errorf("unable to upload object %s: %v", object.ObjectName, err) + return err + } + c.Logger.Infof("uploaded object %v with size %d", info, info.Size) + return nil }