diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 430f7613..8d41977a 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -21,8 +21,8 @@ on: workflow_dispatch: env: - GO_VERSION: "1.20" - GO_LINT_VERSION: v1.51.2 + GO_VERSION: "1.22" + GO_LINT_VERSION: v1.58.1 NODE_VERSION: 20 ARTIFACT_RETENTION_DAYS: 7 CONTAINER_REGISTRY: ghcr.io @@ -102,7 +102,7 @@ jobs: cache-dependency-path: api/go.sum - name: Lint code - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v6 with: version: ${{ env.GO_LINT_VERSION }} skip-go-installation: true @@ -111,14 +111,6 @@ jobs: - name: Run Integration Test run: make it-test-api - - uses: codecov/codecov-action@v4 - with: - flags: api-test - name: api-test - token: ${{ secrets.CODECOV_TOKEN }} - working-directory: ./api - codecov_yml_path: ../.github/workflows/codecov-config/codecov.yml - e2e-test: runs-on: ubuntu-latest needs: diff --git a/.github/workflows/codecov-config/codecov.yml b/.github/workflows/codecov-config/codecov.yml deleted file mode 100644 index 8901728c..00000000 --- a/.github/workflows/codecov-config/codecov.yml +++ /dev/null @@ -1,5 +0,0 @@ -coverage: - status: - patch: - default: - threshold: 0.03% \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 7ab62e9a..bed17f78 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,7 +7,6 @@ run: linters: enable: - bodyclose - - deadcode - errcheck - gocyclo - gofmt @@ -19,9 +18,7 @@ linters: - misspell - revive - staticcheck - - structcheck - unused - - varcheck linters-settings: gocyclo: diff --git a/Dockerfile b/Dockerfile index 915b46e9..99bfaf27 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN yarn app build # ============================================================ # Build stage 2: Build API # ============================================================ -FROM golang:1.20-alpine as go-builder +FROM golang:1.22-alpine as go-builder WORKDIR /src/api COPY api api/ COPY go.mod . diff --git a/Makefile b/Makefile index d26da686..40444559 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ all: setup init-dep lint test clean build run setup: @echo "> Setting up tools..." @test -x $(shell go env GOPATH)/bin/golangci-lint || \ - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/v1.53.3/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.53.3 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/v1.58.1/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.58.1 .PHONY: init-dep init-dep: init-dep-ui init-dep-api diff --git a/api.Dockerfile b/api.Dockerfile index ad0369ca..cb04dfd6 100644 --- a/api.Dockerfile +++ b/api.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20-alpine as go-builder +FROM golang:1.22-alpine as go-builder WORKDIR /src/api diff --git a/api/api/projects_api.go b/api/api/projects_api.go index 1a0ce1cb..ac8751a8 100644 --- a/api/api/projects_api.go +++ b/api/api/projects_api.go @@ -104,7 +104,7 @@ func (c *ProjectsController) UpdateProject(r *http.Request, vars map[string]stri return Ok(updatedProject) } -func (c *ProjectsController) GetProject(r *http.Request, vars map[string]string, body interface{}) *Response { +func (c *ProjectsController) GetProject(_ *http.Request, vars map[string]string, _ interface{}) *Response { projectID, _ := models.ParseID(vars["project_id"]) project, err := c.ProjectsService.FindByID(projectID) if err != nil { diff --git a/api/api/secrets_api.go b/api/api/secrets_api.go index 34aafc93..c40f5879 100644 --- a/api/api/secrets_api.go +++ b/api/api/secrets_api.go @@ -14,7 +14,7 @@ type SecretsController struct { *AppContext } -func (c *SecretsController) GetSecret(r *http.Request, vars map[string]string, _ interface{}) *Response { +func (c *SecretsController) GetSecret(_ *http.Request, vars map[string]string, _ interface{}) *Response { projectID, _ := models.ParseID(vars["project_id"]) secretID, _ := models.ParseID(vars["secret_id"]) if projectID <= 0 || secretID <= 0 { @@ -31,7 +31,7 @@ func (c *SecretsController) GetSecret(r *http.Request, vars map[string]string, _ return Ok(secret) } -func (c *SecretsController) CreateSecret(r *http.Request, vars map[string]string, body interface{}) *Response { +func (c *SecretsController) CreateSecret(_ *http.Request, vars map[string]string, body interface{}) *Response { projectID, _ := models.ParseID(vars["project_id"]) _, err := c.ProjectsService.FindByID(projectID) if err != nil { @@ -56,7 +56,7 @@ func (c *SecretsController) CreateSecret(r *http.Request, vars map[string]string return Created(secret) } -func (c *SecretsController) UpdateSecret(r *http.Request, vars map[string]string, body interface{}) *Response { +func (c *SecretsController) UpdateSecret(_ *http.Request, vars map[string]string, body interface{}) *Response { updateRequest, ok := body.(*models.Secret) if !ok { return BadRequest("Invalid request body") @@ -87,7 +87,7 @@ func (c *SecretsController) UpdateSecret(r *http.Request, vars map[string]string return Ok(updatedSecret) } -func (c *SecretsController) DeleteSecret(r *http.Request, vars map[string]string, _ interface{}) *Response { +func (c *SecretsController) DeleteSecret(_ *http.Request, vars map[string]string, _ interface{}) *Response { projectID, _ := models.ParseID(vars["project_id"]) secretID, _ := models.ParseID(vars["secret_id"]) if projectID <= 0 || secretID <= 0 { @@ -102,7 +102,7 @@ func (c *SecretsController) DeleteSecret(r *http.Request, vars map[string]string return NoContent() } -func (c *SecretsController) ListSecret(r *http.Request, vars map[string]string, body interface{}) *Response { +func (c *SecretsController) ListSecret(_ *http.Request, vars map[string]string, _ interface{}) *Response { projectID, _ := models.ParseID(vars["project_id"]) _, err := c.ProjectsService.FindByID(projectID) if err != nil { diff --git a/api/cmd/bootstrap.go b/api/cmd/bootstrap.go index e69ac9a6..5904620e 100644 --- a/api/cmd/bootstrap.go +++ b/api/cmd/bootstrap.go @@ -25,7 +25,7 @@ var ( bootstrapCmd = &cobra.Command{ Use: "bootstrap", Short: "Start bootstrap job to populate Keto", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { bootstrapConfig, err := loadBootstrapConfig(bootstrapConfigFile) if err != nil { log.Panicf("unable to load role members from input file: %v", err) diff --git a/api/cmd/serve.go b/api/cmd/serve.go index e142c743..7546e65a 100644 --- a/api/cmd/serve.go +++ b/api/cmd/serve.go @@ -28,7 +28,7 @@ var ( serveCmd = &cobra.Command{ Use: "serve", Short: "Start MLP API server", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { serveConfig, err := config.LoadAndValidate(configFiles...) if err != nil { log.Fatalf("failed initializing config: %v", err) @@ -118,7 +118,7 @@ type uiEnvHandler struct { LabelsBlacklist []string `json:"REACT_APP_LABELS_BLACKLIST"` } -func (h uiEnvHandler) handler(w http.ResponseWriter, r *http.Request) { +func (h uiEnvHandler) handler(w http.ResponseWriter, _ *http.Request) { envJSON, err := json.Marshal(h) if err != nil { envJSON = []byte("{}") diff --git a/api/it/database/database.go b/api/it/database/database.go index 5c969e46..e62676e7 100644 --- a/api/it/database/database.go +++ b/api/it/database/database.go @@ -25,16 +25,19 @@ func connectionString(db string) string { } func create(conn *sql.DB, dbName string) (*sql.DB, error) { + var err error if _, err := conn.Exec("CREATE DATABASE " + dbName); err != nil { return nil, err - } else if testDb, err := sql.Open("postgres", connectionString(dbName)); err != nil { + } + + var testDb *sql.DB + if testDb, err = sql.Open("postgres", connectionString(dbName)); err != nil { if _, err := conn.Exec("DROP DATABASE " + dbName); err != nil { log.Fatalf("Failed to cleanup integration test database: \n%s", err) } return nil, err - } else { - return testDb, nil } + return testDb, nil } func migrate(db *sql.DB, dbName string) (*sql.DB, error) { @@ -84,12 +87,14 @@ func CreateTestDatabase() (*gorm.DB, func(), error) { cleanup() return nil, nil, err - } else if gormDb, err := gorm.Open("postgres", testDb); err != nil { + } + + var gormDb *gorm.DB + if gormDb, err = gorm.Open("postgres", testDb); err != nil { cleanup() return nil, nil, err - } else { - return gormDb, cleanup, nil } + return gormDb, cleanup, nil } func WithTestDatabase(t *testing.T, test func(t *testing.T, db *gorm.DB)) { diff --git a/api/pkg/artifact/artifact.go b/api/pkg/artifact/artifact.go index af7857bc..f0d1551a 100644 --- a/api/pkg/artifact/artifact.go +++ b/api/pkg/artifact/artifact.go @@ -1,16 +1,33 @@ package artifact import ( + "bytes" "context" + "errors" "fmt" + "os" + "io" "net/url" "strings" "cloud.google.com/go/storage" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/aws/aws-sdk-go-v2/service/s3/types" "google.golang.org/api/iterator" ) +const ( + gcsArtifactClientType = "gcs" + gcsURLScheme = "gs" + s3ArtifactClientType = "s3" + s3URLScheme = "s3" +) + +var ErrObjectNotExist = errors.New("storage: object doesn't exist") + // URL contains the information needed to identify the location of an object // located in Google Cloud Storage. type URL struct { @@ -23,44 +40,38 @@ type URL struct { Object string } -type Service interface { - ParseURL(gsURL string) (*URL, error) - - ReadArtifact(ctx context.Context, url string) ([]byte, error) - WriteArtifact(ctx context.Context, url string, content []byte) error - DeleteArtifact(ctx context.Context, url string) error -} +type URLScheme string -type GcsArtifactClient struct { - API *storage.Client +type SchemeInterface interface { + GetURLScheme() string + ParseURL(gsURL string) (*URL, error) } -func NewGcsArtifactClient(api *storage.Client) Service { - return &GcsArtifactClient{ - API: api, - } +func (urlScheme URLScheme) GetURLScheme() string { + return string(urlScheme) } -// Parse parses a Google Cloud Storage string into a URL struct. The expected -// format of the string is gs://[bucket-name]/[object-path]. If the provided +// ParseURL parses an artifact storage string into a URL struct. The expected +// format of the string is [url-scheme]://[bucket-name]/[object-path]. If the provided // URL is formatted incorrectly an error will be returned. -func (gac *GcsArtifactClient) ParseURL(gsURL string) (*URL, error) { +func (urlScheme URLScheme) ParseURL(gsURL string) (*URL, error) { u, err := url.Parse(gsURL) if err != nil { return nil, err } - if u.Scheme != "gs" { - return nil, err + if u.Scheme != string(urlScheme) { + return nil, fmt.Errorf("the scheme specified in the given URL is '%s' but the expected scheme is '%s'", + u.Scheme, urlScheme) } bucket, object := u.Host, strings.TrimLeft(u.Path, "/") if bucket == "" { - return nil, err + return nil, fmt.Errorf("the bucket in the given URL is an empty string") } if object == "" { - return nil, err + return nil, fmt.Errorf("the object in the given URL is an empty string") } return &URL{ @@ -69,14 +80,46 @@ func (gac *GcsArtifactClient) ParseURL(gsURL string) (*URL, error) { }, nil } +type Service interface { + GetType() string + GetURLScheme() string + ParseURL(gsURL string) (*URL, error) + ReadArtifact(ctx context.Context, url string) ([]byte, error) + WriteArtifact(ctx context.Context, url string, content []byte) error + DeleteArtifact(ctx context.Context, url string) error +} + +type GcsArtifactClient struct { + URLScheme + api *storage.Client +} + +func NewGcsArtifactClient() (Service, error) { + api, err := storage.NewClient(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed initializing gcs for the artifact client with error: %s", err.Error()) + } + return &GcsArtifactClient{ + URLScheme: gcsURLScheme, + api: api, + }, nil +} + +func (gac *GcsArtifactClient) GetType() string { + return gcsArtifactClientType +} + func (gac *GcsArtifactClient) ReadArtifact(ctx context.Context, url string) ([]byte, error) { u, err := gac.ParseURL(url) if err != nil { return nil, err } - reader, err := gac.API.Bucket(u.Bucket).Object(u.Object).NewReader(ctx) + reader, err := gac.api.Bucket(u.Bucket).Object(u.Object).NewReader(ctx) if err != nil { + if errors.Is(err, storage.ErrObjectNotExist) { + return nil, ErrObjectNotExist + } return nil, err } defer reader.Close() //nolint:errcheck @@ -93,7 +136,7 @@ func (gac *GcsArtifactClient) WriteArtifact(ctx context.Context, url string, con if err != nil { return err } - w := gac.API.Bucket(u.Bucket).Object(u.Object).NewWriter(ctx) + w := gac.api.Bucket(u.Bucket).Object(u.Object).NewWriter(ctx) if _, err := fmt.Fprintf(w, "%s", content); err != nil { return err @@ -113,7 +156,7 @@ func (gac *GcsArtifactClient) DeleteArtifact(ctx context.Context, url string) er } // Sets the name for the bucket. - bucket := gac.API.Bucket(u.Bucket) + bucket := gac.api.Bucket(u.Bucket) it := bucket.Objects(ctx, &storage.Query{ Prefix: u.Object, @@ -133,24 +176,135 @@ func (gac *GcsArtifactClient) DeleteArtifact(ctx context.Context, url string) er return nil } +type S3ArtifactClient struct { + URLScheme + client *s3.Client +} + +func NewS3ArtifactClient() (Service, error) { + cfg, err := config.LoadDefaultConfig(context.Background()) + if err != nil { + return nil, fmt.Errorf("%s,failed loading s3 config for the artifact client", err.Error()) + } + client := s3.NewFromConfig(cfg, func(o *s3.Options) { + o.BaseEndpoint = aws.String(os.Getenv("AWS_ENDPOINT_URL")) + }) + return &S3ArtifactClient{ + URLScheme: s3URLScheme, + client: client, + }, nil +} + +func (s3c *S3ArtifactClient) GetType() string { + return s3ArtifactClientType +} + +func (s3c *S3ArtifactClient) ReadArtifact(ctx context.Context, url string) ([]byte, error) { + u, err := s3c.ParseURL(url) + if err != nil { + return nil, err + } + + reader, err := s3c.client.GetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(u.Bucket), + Key: aws.String(u.Object), + }) + if err != nil { + var nsk *types.NoSuchKey + if errors.As(err, &nsk) { + return nil, ErrObjectNotExist + } + return nil, err + } + defer reader.Body.Close() //nolint:errcheck + + bytes, err := io.ReadAll(reader.Body) + if err != nil { + return nil, err + } + return bytes, nil +} + +func (s3c *S3ArtifactClient) WriteArtifact(ctx context.Context, url string, content []byte) error { + u, err := s3c.ParseURL(url) + if err != nil { + return err + } + + _, err = s3c.client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(u.Bucket), + Key: aws.String(u.Object), + Body: bytes.NewReader(content), + }) + if err != nil { + return err + } + + return nil +} + +func (s3c *S3ArtifactClient) DeleteArtifact(ctx context.Context, url string) error { + u, err := s3c.ParseURL(url) + if err != nil { + return err + } + + out, err := s3c.client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ + Bucket: aws.String(u.Bucket), + Prefix: aws.String(u.Object), + }) + if err != nil { + return err + } + + // TODO: To confirm and refactor if versioning is enabled on S3 and to specify the versionId to be deleted + var objects []types.ObjectIdentifier + for _, item := range out.Contents { + objects = append(objects, types.ObjectIdentifier{ + Key: item.Key, + }) + } + if objects != nil { + _, err = s3c.client.DeleteObjects(ctx, &s3.DeleteObjectsInput{ + Bucket: aws.String(u.Bucket), + Delete: &types.Delete{ + Objects: objects, + Quiet: aws.Bool(true), + }, + }) + if err != nil { + return err + } + } + return nil +} + type NopArtifactClient struct{} func NewNopArtifactClient() Service { return &NopArtifactClient{} } -func (nac *NopArtifactClient) ParseURL(gsURL string) (*URL, error) { +func (nac *NopArtifactClient) GetType() string { + return "" +} + +func (nac *NopArtifactClient) GetURLScheme() string { + return "" +} + +func (nac *NopArtifactClient) ParseURL(_ string) (*URL, error) { return nil, nil } -func (nac *NopArtifactClient) ReadArtifact(ctx context.Context, url string) ([]byte, error) { +func (nac *NopArtifactClient) ReadArtifact(_ context.Context, _ string) ([]byte, error) { return nil, nil } -func (nac *NopArtifactClient) WriteArtifact(ctx context.Context, url string, content []byte) error { +func (nac *NopArtifactClient) WriteArtifact(_ context.Context, _ string, _ []byte) error { return nil } -func (nac *NopArtifactClient) DeleteArtifact(ctx context.Context, url string) error { +func (nac *NopArtifactClient) DeleteArtifact(_ context.Context, _ string) error { return nil } diff --git a/api/pkg/artifact/artifact_test.go b/api/pkg/artifact/artifact_test.go index ee7c1e3d..9bca45af 100644 --- a/api/pkg/artifact/artifact_test.go +++ b/api/pkg/artifact/artifact_test.go @@ -3,29 +3,52 @@ package artifact import ( "reflect" "testing" - - "cloud.google.com/go/storage" ) -func TestGcsArtifactClient_ParseURL(t *testing.T) { - type fields struct { - API *storage.Client +func TestArtifactClient_GetURLScheme(t *testing.T) { + tests := []struct { + name string + artifactClient SchemeInterface + want string + }{ + { + name: "gcs client", + artifactClient: &GcsArtifactClient{ + URLScheme: "gs", + }, + want: "gs", + }, + { + name: "s3 client", + artifactClient: &S3ArtifactClient{ + URLScheme: "gs", + }, + want: "gs", + }, } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.artifactClient.GetURLScheme(); got != tt.want { + t.Errorf("GcsArtifactClient.GetScheme() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestURLScheme_ParseURL(t *testing.T) { type args struct { gsURL string } tests := []struct { - name string - fields fields - args args - want *URL - wantErr bool + name string + urlScheme URLScheme + args args + want *URL + wantErr bool }{ { - name: "valid short url", - fields: fields{ - API: nil, - }, + name: "valid short url", + urlScheme: "gs", args: args{ gsURL: "gs://bucket-name/object-path", }, @@ -36,10 +59,8 @@ func TestGcsArtifactClient_ParseURL(t *testing.T) { wantErr: false, }, { - name: "valid url", - fields: fields{ - API: nil, - }, + name: "valid url", + urlScheme: "gs", args: args{ gsURL: "gs://bucket-name/object-path/object-path-2/object-path-3/file-1.txt", }, @@ -49,19 +70,48 @@ func TestGcsArtifactClient_ParseURL(t *testing.T) { }, wantErr: false, }, + { + name: "invalid url", + urlScheme: "gs", + args: args{ + gsURL: "!@#$%^&*()", + }, + wantErr: true, + }, + { + name: "invalid scheme", + urlScheme: "gs", + args: args{ + gsURL: "s3://bucket-name/object-path/object-path-2/object-path-3/file-1.txt", + }, + wantErr: true, + }, + { + name: "empty bucket", + urlScheme: "gs", + args: args{ + gsURL: "gs:///object-path/object-path-2/object-path-3/file-1.txt", + }, + wantErr: true, + }, + { + name: "empty object", + urlScheme: "gs", + args: args{ + gsURL: "gs://bucket-name/", + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gac := &GcsArtifactClient{ - API: tt.fields.API, - } - got, err := gac.ParseURL(tt.args.gsURL) + got, err := tt.urlScheme.ParseURL(tt.args.gsURL) if (err != nil) != tt.wantErr { - t.Errorf("GcsArtifactClient.ParseURL() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("URLScheme.ParseURL() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GcsArtifactClient.ParseURL() = %v, want %v", got, tt.want) + t.Errorf("URLScheme.ParseURL() = %v, want %v", got, tt.want) } }) } diff --git a/api/pkg/artifact/mocks/artifact.go b/api/pkg/artifact/mocks/artifact.go index 36e785c7..6af52123 100644 --- a/api/pkg/artifact/mocks/artifact.go +++ b/api/pkg/artifact/mocks/artifact.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. +// Code generated by mockery v2.45.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type Service struct { func (_m *Service) DeleteArtifact(ctx context.Context, url string) error { ret := _m.Called(ctx, url) + if len(ret) == 0 { + panic("no return value specified for DeleteArtifact") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, url) @@ -29,10 +33,50 @@ func (_m *Service) DeleteArtifact(ctx context.Context, url string) error { return r0 } +// GetType provides a mock function with given fields: +func (_m *Service) GetType() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetType") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetURLScheme provides a mock function with given fields: +func (_m *Service) GetURLScheme() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetURLScheme") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + // ParseURL provides a mock function with given fields: gsURL func (_m *Service) ParseURL(gsURL string) (*artifact.URL, error) { ret := _m.Called(gsURL) + if len(ret) == 0 { + panic("no return value specified for ParseURL") + } + var r0 *artifact.URL var r1 error if rf, ok := ret.Get(0).(func(string) (*artifact.URL, error)); ok { @@ -59,6 +103,10 @@ func (_m *Service) ParseURL(gsURL string) (*artifact.URL, error) { func (_m *Service) ReadArtifact(ctx context.Context, url string) ([]byte, error) { ret := _m.Called(ctx, url) + if len(ret) == 0 { + panic("no return value specified for ReadArtifact") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]byte, error)); ok { @@ -85,6 +133,10 @@ func (_m *Service) ReadArtifact(ctx context.Context, url string) ([]byte, error) func (_m *Service) WriteArtifact(ctx context.Context, url string, content []byte) error { ret := _m.Called(ctx, url, content) + if len(ret) == 0 { + panic("no return value specified for WriteArtifact") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, []byte) error); ok { r0 = rf(ctx, url, content) @@ -95,13 +147,12 @@ func (_m *Service) WriteArtifact(ctx context.Context, url string, content []byte return r0 } -type mockConstructorTestingTNewService interface { +// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewService(t interface { mock.TestingT Cleanup(func()) -} - -// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewService(t mockConstructorTestingTNewService) *Service { +}) *Service { mock := &Service{} mock.Mock.Test(t) diff --git a/api/pkg/authz/enforcer/enforcer.go b/api/pkg/authz/enforcer/enforcer.go index 1ba30545..cb352c59 100644 --- a/api/pkg/authz/enforcer/enforcer.go +++ b/api/pkg/authz/enforcer/enforcer.go @@ -158,7 +158,7 @@ func (e *enforcer) GetUserPermissions(ctx context.Context, user string) ([]strin return nil, err } permissions := make([]string, 0) - permissionSet.Range(func(key, value interface{}) bool { + permissionSet.Range(func(key, _ interface{}) bool { permissions = append(permissions, key.(string)) return true }) diff --git a/api/pkg/client/mlflow/mlflow.go b/api/pkg/client/mlflow/mlflow.go index 5fce5396..7b05050f 100644 --- a/api/pkg/client/mlflow/mlflow.go +++ b/api/pkg/client/mlflow/mlflow.go @@ -7,8 +7,6 @@ import ( "fmt" "net/http" - "cloud.google.com/go/storage" - "github.com/caraml-dev/mlp/api/pkg/artifact" ) @@ -25,14 +23,20 @@ type mlflowService struct { func NewMlflowService(httpClient *http.Client, config Config) (Service, error) { var artifactService artifact.Service + var err error + // TODO: To consider removing since it doesn't make sense to have nop artifact service if config.ArtifactServiceType == "nop" { artifactService = artifact.NewNopArtifactClient() } else if config.ArtifactServiceType == "gcs" { - api, err := storage.NewClient(context.Background()) + artifactService, err = artifact.NewGcsArtifactClient() if err != nil { return &mlflowService{}, fmt.Errorf("failed initializing gcs for mlflow delete package") } - artifactService = artifact.NewGcsArtifactClient(api) + } else if config.ArtifactServiceType == "s3" { + artifactService, err = artifact.NewS3ArtifactClient() + if err != nil { + return &mlflowService{}, fmt.Errorf("failed initializing s3 for mlflow delete package") + } } else { return &mlflowService{}, fmt.Errorf("invalid artifact service type") } @@ -115,7 +119,6 @@ func (mfs *mlflowService) searchRunData(RunID string) (SearchRunResponse, error) } func (mfs *mlflowService) DeleteExperiment(ctx context.Context, ExperimentID string, deleteArtifact bool) error { - relatedRunData, err := mfs.searchRunsForExperiment(ExperimentID) if err != nil { return err diff --git a/api/pkg/client/mlflow/mlflow_test.go b/api/pkg/client/mlflow/mlflow_test.go index 619cfd26..dd66bf9a 100644 --- a/api/pkg/client/mlflow/mlflow_test.go +++ b/api/pkg/client/mlflow/mlflow_test.go @@ -7,8 +7,6 @@ import ( "net/http/httptest" "testing" - "cloud.google.com/go/storage" - "github.com/caraml-dev/mlp/api/pkg/artifact" "github.com/stretchr/testify/assert" @@ -225,8 +223,6 @@ var DeleteRunAlreadyDeleted = ` func TestNewMlflowService(t *testing.T) { httpClient := http.Client{} - ctx := context.Background() - api, _ := storage.NewClient(ctx) tests := []struct { name string @@ -239,11 +235,19 @@ func TestNewMlflowService(t *testing.T) { artifactType: "gcs", expectedError: nil, expectedResult: &mlflowService{ - API: &httpClient, - Config: Config{TrackingURL: "", ArtifactServiceType: "gcs"}, - ArtifactService: &artifact.GcsArtifactClient{ - API: api, - }, + API: &httpClient, + Config: Config{TrackingURL: "", ArtifactServiceType: "gcs"}, + ArtifactService: &artifact.GcsArtifactClient{}, + }, + }, + { + name: "Mlflow Service with s3 Artifact", + artifactType: "s3", + expectedError: nil, + expectedResult: &mlflowService{ + API: &httpClient, + Config: Config{TrackingURL: "", ArtifactServiceType: "s3"}, + ArtifactService: &artifact.S3ArtifactClient{}, }, }, { @@ -338,7 +342,7 @@ func TestMlflowClient_SearchRunForExperiment(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) _, err := w.Write([]byte(tc.expectedRespJSON)) @@ -406,7 +410,7 @@ func TestMlflowClient_SearchRunData(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(tc.httpStatus) _, err := w.Write([]byte(tc.expectedRespJSON)) @@ -467,13 +471,13 @@ func TestMlflowClient_DeleteExperiment(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mux := http.NewServeMux() - mux.HandleFunc("/api/2.0/mlflow/runs/delete", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/2.0/mlflow/runs/delete", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(tc.httpStatus) _, err := w.Write([]byte(tc.expectedRespJSON)) require.NoError(t, err) }) - mux.HandleFunc("/api/2.0/mlflow/runs/search", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/2.0/mlflow/runs/search", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(tc.httpStatus) _, err := w.Write([]byte(tc.expectedRunsRespJSON)) @@ -594,13 +598,13 @@ func TestMlflowClient_DeleteRun(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mux := http.NewServeMux() - mux.HandleFunc("/api/2.0/mlflow/runs/delete", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/2.0/mlflow/runs/delete", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(tc.httpStatus) _, err := w.Write([]byte(tc.expectedRespJSON)) require.NoError(t, err) }) - mux.HandleFunc("/api/2.0/mlflow/runs/get", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/2.0/mlflow/runs/get", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(tc.httpStatus) _, err := w.Write([]byte(tc.expectedRunRespJSON)) diff --git a/api/pkg/instrumentation/metrics/nop.go b/api/pkg/instrumentation/metrics/nop.go index 7df378a2..e95df10a 100644 --- a/api/pkg/instrumentation/metrics/nop.go +++ b/api/pkg/instrumentation/metrics/nop.go @@ -35,6 +35,6 @@ func (NopMetricsCollector) RecordGauge(MetricName, float64, map[string]string) e } // Inc satisfies the Collector interface -func (c NopMetricsCollector) Inc(key MetricName, labels map[string]string) error { +func (c NopMetricsCollector) Inc(_ MetricName, _ map[string]string) error { return nil } diff --git a/api/pkg/instrumentation/metrics/prometheus_test.go b/api/pkg/instrumentation/metrics/prometheus_test.go index 81fd72c6..1f3aa85c 100644 --- a/api/pkg/instrumentation/metrics/prometheus_test.go +++ b/api/pkg/instrumentation/metrics/prometheus_test.go @@ -45,7 +45,7 @@ type mockCounterVec struct { counter *mockCounter } -func (m mockCounterVec) GetMetricWith(labels prometheus.Labels) (prometheus.Counter, error) { +func (m mockCounterVec) GetMetricWith(_ prometheus.Labels) (prometheus.Counter, error) { return m.counter, nil } @@ -95,7 +95,7 @@ type mockGaugeVec struct { gauge *mockGauge } -func (g *mockGaugeVec) GetMetricWith(labels prometheus.Labels) (prometheus.Gauge, error) { +func (g *mockGaugeVec) GetMetricWith(_ prometheus.Labels) (prometheus.Gauge, error) { return g.gauge, nil } @@ -105,7 +105,7 @@ func createMockGaugeVec(testValue float64) *mockGaugeVec { gauge := &mockGauge{ value: 0, } - gauge.On("Set", mock.Anything).Run(func(args mock.Arguments) { + gauge.On("Set", mock.Anything).Run(func(_ mock.Arguments) { gauge.value = testValue }).Return(nil) gaugeVec := &mockGaugeVec{ @@ -145,7 +145,7 @@ type mockHistogramVec struct { histogram *mockHistogram } -func (h *mockHistogramVec) GetMetricWith(labels prometheus.Labels) (prometheus.Observer, error) { +func (h *mockHistogramVec) GetMetricWith(_ prometheus.Labels) (prometheus.Observer, error) { return h.histogram, nil } @@ -207,7 +207,7 @@ func createMockHistVec(testDuration float64) *mockHistogramVec { hist := &mockHistogram{ duration: 0, } - hist.On("Observe", mock.Anything).Run(func(args mock.Arguments) { + hist.On("Observe", mock.Anything).Run(func(_ mock.Arguments) { hist.duration = testDuration }).Return(nil) return &mockHistogramVec{ diff --git a/api/pkg/instrumentation/newrelic/newrelic_test.go b/api/pkg/instrumentation/newrelic/newrelic_test.go index a3a23f62..6ad08ba2 100644 --- a/api/pkg/instrumentation/newrelic/newrelic_test.go +++ b/api/pkg/instrumentation/newrelic/newrelic_test.go @@ -84,7 +84,7 @@ func TestInitNewRelic(t *testing.T) { } func TestWrapHandleFunc(t *testing.T) { - pattern, handler := WrapHandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { + pattern, handler := WrapHandleFunc("/ping", func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("pong")) }) @@ -133,6 +133,6 @@ func TestRecordCustomMetric(t *testing.T) { assert.Nil(t, err) } -func TestShutdown(t *testing.T) { +func TestShutdown(_ *testing.T) { Shutdown(100) } diff --git a/api/pkg/instrumentation/newrelic/noop.go b/api/pkg/instrumentation/newrelic/noop.go index bba66202..df361d94 100644 --- a/api/pkg/instrumentation/newrelic/noop.go +++ b/api/pkg/instrumentation/newrelic/noop.go @@ -12,25 +12,25 @@ import ( type NoopApp struct{} // StartTransaction implements newrelic.Application interface. -func (na NoopApp) StartTransaction(name string, w http.ResponseWriter, r *http.Request) newrelic.Transaction { +func (na NoopApp) StartTransaction(_ string, w http.ResponseWriter, _ *http.Request) newrelic.Transaction { return &NoopTx{ w: w, } } // RecordCustomEvent implements newrelic.Application interface. -func (na NoopApp) RecordCustomEvent(eventType string, params map[string]interface{}) error { +func (na NoopApp) RecordCustomEvent(_ string, _ map[string]interface{}) error { return nil } // RecordCustomMetric implements newrelic.Application interface. -func (na NoopApp) RecordCustomMetric(name string, value float64) error { return nil } +func (na NoopApp) RecordCustomMetric(_ string, _ float64) error { return nil } // WaitForConnection implements newrelic.Application interface. -func (na NoopApp) WaitForConnection(timeout time.Duration) error { return nil } +func (na NoopApp) WaitForConnection(_ time.Duration) error { return nil } // Shutdown implements newrelic.Application interface. -func (na NoopApp) Shutdown(timeout time.Duration) { +func (na NoopApp) Shutdown(_ time.Duration) { // Do nothing } @@ -55,17 +55,17 @@ func (nt *NoopTx) Ignore() error { } // SetName implements newrelic.Transaction interface. -func (nt *NoopTx) SetName(name string) error { +func (nt *NoopTx) SetName(_ string) error { return nil } // NoticeError implements newrelic.Transaction interface. -func (nt *NoopTx) NoticeError(err error) error { +func (nt *NoopTx) NoticeError(_ error) error { return nil } // AddAttribute implements newrelic.Transaction interface. -func (nt *NoopTx) AddAttribute(key string, value interface{}) error { +func (nt *NoopTx) AddAttribute(_ string, _ interface{}) error { return nil } @@ -90,7 +90,7 @@ func (nt *NoopTx) CreateDistributedTracePayload() newrelic.DistributedTracePaylo } // AcceptDistributedTracePayload implements newrelic.Transaction interface. -func (nt *NoopTx) AcceptDistributedTracePayload(t newrelic.TransportType, payload interface{}) error { +func (nt *NoopTx) AcceptDistributedTracePayload(_ newrelic.TransportType, _ interface{}) error { return nil } diff --git a/api/pkg/instrumentation/newrelic/noop_test.go b/api/pkg/instrumentation/newrelic/noop_test.go index 02a75f1e..b4f8a4b2 100644 --- a/api/pkg/instrumentation/newrelic/noop_test.go +++ b/api/pkg/instrumentation/newrelic/noop_test.go @@ -8,7 +8,7 @@ import ( newrelic "github.com/newrelic/go-agent" ) -func TestNoopApp(t *testing.T) { +func TestNoopApp(_ *testing.T) { na := NoopApp{} _ = na.StartTransaction("test", httptest.NewRecorder(), &http.Request{}) _ = na.RecordCustomEvent("test", nil) @@ -17,7 +17,7 @@ func TestNoopApp(t *testing.T) { na.Shutdown(0) } -func TestNoopTx(t *testing.T) { +func TestNoopTx(_ *testing.T) { nt := NoopTx{ w: httptest.NewRecorder(), } diff --git a/api/pkg/instrumentation/sentry/noop.go b/api/pkg/instrumentation/sentry/noop.go index 960ec969..210b6748 100644 --- a/api/pkg/instrumentation/sentry/noop.go +++ b/api/pkg/instrumentation/sentry/noop.go @@ -7,12 +7,12 @@ import raven "github.com/getsentry/raven-go" type NoopClient struct{} // Capture implements Client interface. -func (nc *NoopClient) Capture(packet *raven.Packet, captureTags map[string]string) (eventID string, ch chan error) { +func (nc *NoopClient) Capture(_ *raven.Packet, _ map[string]string) (eventID string, ch chan error) { return "", nil } // CaptureError implements Client interface. -func (nc *NoopClient) CaptureError(err error, tags map[string]string, interfaces ...raven.Interface) string { +func (nc *NoopClient) CaptureError(_ error, _ map[string]string, _ ...raven.Interface) string { return "" } diff --git a/api/pkg/instrumentation/sentry/noop_test.go b/api/pkg/instrumentation/sentry/noop_test.go index 7f62960f..d3a4bbad 100644 --- a/api/pkg/instrumentation/sentry/noop_test.go +++ b/api/pkg/instrumentation/sentry/noop_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func TestNoopClient(t *testing.T) { +func TestNoopClient(_ *testing.T) { nc := &NoopClient{} nc.Capture(nil, nil) nc.CaptureError(nil, nil, nil) diff --git a/api/pkg/instrumentation/sentry/sentry_test.go b/api/pkg/instrumentation/sentry/sentry_test.go index c9d9b723..ffff52e8 100644 --- a/api/pkg/instrumentation/sentry/sentry_test.go +++ b/api/pkg/instrumentation/sentry/sentry_test.go @@ -60,7 +60,7 @@ func TestSentry(t *testing.T) { sentry := Sentry() assert.NotNil(t, sentry) - panicHandler := RecoveryHandler(func(w http.ResponseWriter, r *http.Request) { + panicHandler := RecoveryHandler(func(_ http.ResponseWriter, _ *http.Request) { panic("at the disco") }) assert.NotNil(t, panicHandler) diff --git a/api/service/projects_service_test.go b/api/service/projects_service_test.go index 88587ff9..7114b814 100644 --- a/api/service/projects_service_test.go +++ b/api/service/projects_service_test.go @@ -898,7 +898,7 @@ func Test_sendUpdateRequest(t *testing.T) { "stream": "team-2", } - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) _, err := w.Write([]byte(`{"status":"success","message":"success-message"}`)) assert.NoError(t, err) diff --git a/go.mod b/go.mod index 9135dff1..e6da7445 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,15 @@ module github.com/caraml-dev/mlp -go 1.20 +go 1.22 require ( cloud.google.com/go/storage v1.29.0 github.com/antihax/optional v1.0.0 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/avast/retry-go/v4 v4.6.0 + github.com/aws/aws-sdk-go-v2 v1.30.6-0.20240906182417-827d25db0048 + github.com/aws/aws-sdk-go-v2/config v1.8.3 + github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2 github.com/gavv/httpexpect/v2 v2.15.0 github.com/getsentry/raven-go v0.2.0 github.com/go-playground/locales v0.14.0 @@ -54,6 +57,20 @@ require ( cloud.google.com/go/iam v0.8.0 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.4 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.4.3 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.4.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.7.2 // indirect + github.com/aws/smithy-go v1.20.4 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40 // indirect @@ -87,6 +104,7 @@ require ( github.com/imkira/go-interpol v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect diff --git a/go.sum b/go.sum index 3be93896..d2f44431 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,7 @@ cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1 cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -89,15 +90,43 @@ github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinR github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2 v1.30.6-0.20240906182417-827d25db0048 h1:wXvkIvYQ3EPVO5MhCoEv2u5LDwfWp+kLTQMIGyyvi/0= +github.com/aws/aws-sdk-go-v2 v1.30.6-0.20240906182417-827d25db0048/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4/go.mod h1:/MQxMqci8tlqDH+pjmoLu1i0tbWCUP1hhyMRuFxpQCw= +github.com/aws/aws-sdk-go-v2/config v1.8.3 h1:o5583X4qUfuRrOGOgmOcDgvr5gJVSu57NK08cWAhIDk= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= +github.com/aws/aws-sdk-go-v2/credentials v1.4.3 h1:LTdD5QhK073MpElh9umLLP97wxphkgVC/OjQaEbBwZA= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0 h1:9tfxW/icbSu98C2pcNynm5jmDwU3/741F11688B6QnU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4 h1:leSJ6vCqtPpTmBIgE7044B1wql1E4n//McF+mEgNrYg= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 h1:Roo69qTpfu8OlJ2Tb7pAYVuF0CpuUMB0IYWwYP/4DZM= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17/go.mod h1:NcWPxQzGM1USQggaTVwz6VpqMZPX1CvDJLDh6jnOCa4= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19 h1:FLMkfEiRjhgeDTCjjLoc3URo/TBkgeQbocA78lfkzSI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19/go.mod h1:Vx+GucNSsdhaxs3aZIKfSUjKVGsxN25nX2SRcdhuw08= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 h1:rfprUlsdzgl7ZL2KlXiUAoJnI/VxfHCvDFr2QDFj6u4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19/go.mod h1:SCWkEdRq8/7EK60NcvvQ6NXKuTcchAD4ROAsC37VEZE= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 h1:u+EfGmksnJc/x5tq3A+OD7LrMbSSR/5TrKLvkdy/fhY= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17/go.mod h1:VaMx6302JHax2vHJWgRo+5n9zvbacs3bLU/23DNQrTY= +github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2 h1:Kp6PWAlXwP1UvIflkIP6MFZYBNDCa4mFCGtxrpICVOg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2/go.mod h1:5FmD/Dqq57gP+XwaUnd5WFPipAuzrf0HmupX27Gvjvc= +github.com/aws/aws-sdk-go-v2/service/sso v1.4.2 h1:pZwkxZbspdqRGzddDB92bkZBoB7lg85sMRE7OqdB3V0= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.7.2 h1:ol2Y5DWqnJeKqNd8th7JWzBtqu63xpOfs1Is+n1t8/4= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= +github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= 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= @@ -162,6 +191,7 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -203,8 +233,11 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -275,6 +308,7 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW 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/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 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= @@ -444,12 +478,15 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= github.com/jinzhu/now v1.0.1/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/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -492,6 +529,7 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -549,6 +587,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= @@ -561,8 +600,10 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ 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/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= @@ -699,6 +740,7 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1236,6 +1278,7 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= +k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= @@ -1243,6 +1286,7 @@ k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=