diff --git a/backend/src/apiserver/client/minio.go b/backend/src/apiserver/client/minio.go index 27c08a67901..e322aed65e5 100644 --- a/backend/src/apiserver/client/minio.go +++ b/backend/src/apiserver/client/minio.go @@ -21,8 +21,8 @@ import ( "github.com/cenkalti/backoff" "github.com/golang/glog" - minio "github.com/minio/minio-go/v6" - credentials "github.com/minio/minio-go/v6/pkg/credentials" + minio "github.com/minio/minio-go/v7" + credentials "github.com/minio/minio-go/v7/pkg/credentials" "github.com/pkg/errors" ) @@ -50,7 +50,11 @@ func CreateMinioClient(minioServiceHost string, minioServicePort string, ) (*minio.Client, error) { endpoint := joinHostPort(minioServiceHost, minioServicePort) cred := createCredentialProvidersChain(endpoint, accessKey, secretKey) - minioClient, err := minio.NewWithCredentials(endpoint, cred, secure, region) + minioClient, err := minio.New(endpoint, &minio.Options{ + Creds: cred, + Secure: secure, + Region: region, + }) if err != nil { return nil, errors.Wrapf(err, "Error while creating object store client: %+v", err) } diff --git a/backend/src/apiserver/client_manager/client_manager.go b/backend/src/apiserver/client_manager/client_manager.go index 039d32cc688..537cddba4c1 100644 --- a/backend/src/apiserver/client_manager/client_manager.go +++ b/backend/src/apiserver/client_manager/client_manager.go @@ -24,7 +24,6 @@ import ( "time" "github.com/kubeflow/pipelines/backend/src/v2/metadata" - "github.com/minio/minio-go/v6" "github.com/cenkalti/backoff" "github.com/go-sql-driver/mysql" @@ -39,6 +38,7 @@ import ( "github.com/kubeflow/pipelines/backend/src/apiserver/storage" "github.com/kubeflow/pipelines/backend/src/common/util" k8sapi "github.com/kubeflow/pipelines/backend/src/crd/kubernetes/v2beta1" + "github.com/minio/minio-go/v7" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/cache" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -292,7 +292,7 @@ func (c *ClientManager) init(options *Options) error { c.dBStatusStore = storage.NewDBStatusStore(db) c.defaultExperimentStore = storage.NewDefaultExperimentStore(db) glog.Info("Initializing Minio client...") - c.objectStore = initMinioClient(common.GetDurationConfig(initConnectionTimeout)) + c.objectStore = initMinioClient(options.Context, common.GetDurationConfig(initConnectionTimeout)) glog.Info("Minio client initialized successfully") // Use default value of client QPS (5) & burst (10) defined in // k8s.io/client-go/rest/config.go#RESTClientFor @@ -637,7 +637,7 @@ func initDBDriver(driverName string, initConnectionTimeout time.Duration) string return sqlConfig } -func initMinioClient(initConnectionTimeout time.Duration) storage.ObjectStoreInterface { +func initMinioClient(ctx context.Context, initConnectionTimeout time.Duration) storage.ObjectStoreInterface { // Create minio client. minioServiceHost := common.GetStringConfigWithDefault( "ObjectStoreConfig.Host", os.Getenv(minioServiceHost)) @@ -655,14 +655,14 @@ func initMinioClient(initConnectionTimeout time.Duration) storage.ObjectStoreInt minioClient := client.CreateMinioClientOrFatal(minioServiceHost, minioServicePort, accessKey, secretKey, minioServiceSecure, minioServiceRegion, initConnectionTimeout) - createMinioBucket(minioClient, bucketName, minioServiceRegion) + createMinioBucket(ctx, minioClient, bucketName, minioServiceRegion) return storage.NewMinioObjectStore(&storage.MinioClient{Client: minioClient}, bucketName, pipelinePath, disableMultipart) } -func createMinioBucket(minioClient *minio.Client, bucketName, region string) { +func createMinioBucket(ctx context.Context, minioClient *minio.Client, bucketName, region string) { // Check to see if it exists, and we have permission to access it. - exists, err := minioClient.BucketExists(bucketName) + exists, err := minioClient.BucketExists(ctx, bucketName) if err != nil { glog.Fatalf("Failed to check if object store bucket exists. Error: %v", err) } @@ -671,7 +671,7 @@ func createMinioBucket(minioClient *minio.Client, bucketName, region string) { return } // Create bucket if it does not exist - err = minioClient.MakeBucket(bucketName, region) + err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: region}) if err != nil { glog.Fatalf("Failed to create object store bucket. Error: %v", err) } diff --git a/backend/src/apiserver/resource/resource_manager.go b/backend/src/apiserver/resource/resource_manager.go index 012a8e42d9b..757f632aa2f 100644 --- a/backend/src/apiserver/resource/resource_manager.go +++ b/backend/src/apiserver/resource/resource_manager.go @@ -1006,7 +1006,7 @@ func (r *ResourceManager) readRunLogFromArchive(workflowManifest string, nodeId return util.NewInternalServerError(err, "Failed to read logs from archive %v", nodeId) } - logContent, err := r.objectStore.GetFile(logPath) + logContent, err := r.objectStore.GetFile(context.TODO(), logPath) if err != nil { return util.NewInternalServerError(err, "Failed to read logs from archive %v due to error fetching the log file", nodeId) } @@ -1544,13 +1544,13 @@ func (r *ResourceManager) fetchTemplateFromPipelineVersion(pipelineVersion *mode return bytes, pipelineVersion.PipelineSpecURI, nil } else { // Try reading object store from pipeline_spec_uri - template, errUri := r.objectStore.GetFile(pipelineVersion.PipelineSpecURI) + template, errUri := r.objectStore.GetFile(context.TODO(), pipelineVersion.PipelineSpecURI) if errUri != nil { // Try reading object store from pipeline_version_id - template, errUUID := r.objectStore.GetFile(r.objectStore.GetPipelineKey(fmt.Sprint(pipelineVersion.UUID))) + template, errUUID := r.objectStore.GetFile(context.TODO(), r.objectStore.GetPipelineKey(fmt.Sprint(pipelineVersion.UUID))) if errUUID != nil { // Try reading object store from pipeline_id - template, errPipelineId := r.objectStore.GetFile(r.objectStore.GetPipelineKey(fmt.Sprint(pipelineVersion.PipelineId))) + template, errPipelineId := r.objectStore.GetFile(context.TODO(), r.objectStore.GetPipelineKey(fmt.Sprint(pipelineVersion.PipelineId))) if errPipelineId != nil { return nil, "", util.Wrap( util.Wrap( @@ -1639,7 +1639,7 @@ func (r *ResourceManager) ReadArtifact(runID string, nodeID string, artifactName return nil, util.NewResourceNotFoundError( "artifact", common.CreateArtifactPath(runID, nodeID, artifactName)) } - return r.objectStore.GetFile(artifactPath) + return r.objectStore.GetFile(context.TODO(), artifactPath) } // Fetches the default experiment id. @@ -2159,8 +2159,8 @@ func (r *ResourceManager) GetSecret(ctx context.Context, namespace, name string) } // GetSignedUrl retrieves a signed url for the associated artifact. -func (r *ResourceManager) GetSignedUrl(bucketConfig *objectstore.Config, secret *corev1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) { - signedUrl, err := r.objectStore.GetSignedUrl(bucketConfig, secret, expirySeconds, artifactURI, queryParams) +func (r *ResourceManager) GetSignedUrl(ctx context.Context, bucketConfig *objectstore.Config, secret *corev1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) { + signedUrl, err := r.objectStore.GetSignedUrl(ctx, bucketConfig, secret, expirySeconds, artifactURI, queryParams) if err != nil { return "", err } @@ -2168,8 +2168,8 @@ func (r *ResourceManager) GetSignedUrl(bucketConfig *objectstore.Config, secret } // GetObjectSize retrieves the size of the Artifact's object in bytes. -func (r *ResourceManager) GetObjectSize(bucketConfig *objectstore.Config, secret *corev1.Secret, artifactURI string) (int64, error) { - size, err := r.objectStore.GetObjectSize(bucketConfig, secret, artifactURI) +func (r *ResourceManager) GetObjectSize(ctx context.Context, bucketConfig *objectstore.Config, secret *corev1.Secret, artifactURI string) (int64, error) { + size, err := r.objectStore.GetObjectSize(ctx, bucketConfig, secret, artifactURI) if err != nil { return 0, err } diff --git a/backend/src/apiserver/resource/resource_manager_test.go b/backend/src/apiserver/resource/resource_manager_test.go index 7a0b348526d..9ee484ef214 100644 --- a/backend/src/apiserver/resource/resource_manager_test.go +++ b/backend/src/apiserver/resource/resource_manager_test.go @@ -67,31 +67,31 @@ func (m *FakeBadObjectStore) GetPipelineKey(pipelineID string) string { return pipelineID } -func (m *FakeBadObjectStore) AddFile(template []byte, filePath string) error { +func (m *FakeBadObjectStore) AddFile(ctx context.Context, template []byte, filePath string) error { return util.NewInternalServerError(errors.New("Error"), "bad object store") } -func (m *FakeBadObjectStore) DeleteFile(filePath string) error { +func (m *FakeBadObjectStore) DeleteFile(ctx context.Context, filePath string) error { return errors.New("Not implemented") } -func (m *FakeBadObjectStore) GetFile(filePath string) ([]byte, error) { +func (m *FakeBadObjectStore) GetFile(ctx context.Context, filePath string) ([]byte, error) { return []byte(""), nil } -func (m *FakeBadObjectStore) AddAsYamlFile(o interface{}, filePath string) error { +func (m *FakeBadObjectStore) AddAsYamlFile(ctx context.Context, o interface{}, filePath string) error { return util.NewInternalServerError(errors.New("Error"), "bad object store") } -func (m *FakeBadObjectStore) GetFromYamlFile(o interface{}, filePath string) error { +func (m *FakeBadObjectStore) GetFromYamlFile(ctx context.Context, o interface{}, filePath string) error { return util.NewInternalServerError(errors.New("Error"), "bad object store") } -func (m *FakeBadObjectStore) GetSignedUrl(bucketConfig *objectstore.Config, secret *corev1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) { +func (m *FakeBadObjectStore) GetSignedUrl(ctx context.Context, bucketConfig *objectstore.Config, secret *corev1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) { return "", util.NewInternalServerError(errors.New("Error"), "bad object store") } -func (m *FakeBadObjectStore) GetObjectSize(bucketConfig *objectstore.Config, secret *corev1.Secret, artifactURI string) (int64, error) { +func (m *FakeBadObjectStore) GetObjectSize(ctx context.Context, bucketConfig *objectstore.Config, secret *corev1.Secret, artifactURI string) (int64, error) { return 0, util.NewInternalServerError(errors.New("Error"), "bad object store") } @@ -1037,7 +1037,7 @@ func TestGetPipelineTemplate_FromPipelineURI(t *testing.T) { manager := NewResourceManager(store, &ResourceManagerOptions{CollectMetrics: false}) p, _ := manager.CreatePipeline(createPipelineV1("new_pipeline")) - manager.objectStore.AddFile([]byte(testWorkflow.ToStringForStore()), p.UUID) + manager.objectStore.AddFile(context.TODO(), []byte(testWorkflow.ToStringForStore()), p.UUID) pv := &model.PipelineVersion{ PipelineId: p.UUID, Name: "new_version", @@ -1070,7 +1070,7 @@ func TestGetPipelineTemplate_FromPipelineVersionId(t *testing.T) { pipelineStore.SetUUIDGenerator(util.NewFakeUUIDGeneratorOrFatal(FakeUUIDOne, nil)) assert.True(t, ok) - manager.objectStore.AddFile([]byte(testWorkflow.ToStringForStore()), manager.objectStore.GetPipelineKey("1000")) + manager.objectStore.AddFile(context.TODO(), []byte(testWorkflow.ToStringForStore()), manager.objectStore.GetPipelineKey("1000")) pv2, _ := manager.CreatePipelineVersion(pv) assert.NotEqual(t, p.UUID, pv2.UUID) @@ -1093,7 +1093,7 @@ func TestGetPipelineTemplate_FromPipelineId(t *testing.T) { PipelineSpecURI: p.UUID, } - manager.objectStore.AddFile([]byte(testWorkflow.ToStringForStore()), manager.objectStore.GetPipelineKey(p.UUID)) + manager.objectStore.AddFile(context.TODO(), []byte(testWorkflow.ToStringForStore()), manager.objectStore.GetPipelineKey(p.UUID)) pipelineStore, ok := manager.pipelineStore.(*storage.PipelineStore) assert.True(t, ok) @@ -1111,7 +1111,7 @@ func TestGetPipelineTemplate_PipelineMetadataNotFound(t *testing.T) { store := NewFakeClientManagerOrFatal(util.NewFakeTimeForEpoch()) defer store.Close() template := []byte("workflow: foo") - store.objectStore.AddFile(template, store.objectStore.GetPipelineKey(fmt.Sprint(1))) + store.objectStore.AddFile(context.TODO(), template, store.objectStore.GetPipelineKey(fmt.Sprint(1))) manager := NewResourceManager(store, &ResourceManagerOptions{CollectMetrics: false}) _, err := manager.GetPipelineLatestTemplate("1") assert.Equal(t, codes.NotFound, err.(*util.UserError).ExternalStatusCode()) @@ -3327,7 +3327,7 @@ func TestReadArtifact_Succeed(t *testing.T) { expectedContent := "test" filePath := "test/file.txt" - store.ObjectStore().AddFile([]byte(expectedContent), filePath) + store.ObjectStore().AddFile(context.TODO(), []byte(expectedContent), filePath) // Create a scheduled run // job, _ := manager.CreateJob(model.Job{ diff --git a/backend/src/apiserver/server/artifact_server.go b/backend/src/apiserver/server/artifact_server.go index 1b9b74cc20a..c6c615fab7b 100644 --- a/backend/src/apiserver/server/artifact_server.go +++ b/backend/src/apiserver/server/artifact_server.go @@ -222,7 +222,7 @@ func (s *ArtifactServer) generateResponseArtifact( if err != nil { return nil, err } - size, err := s.resourceManager.GetObjectSize(bucketConfig, secret, *artifact.Uri) + size, err := s.resourceManager.GetObjectSize(ctx, bucketConfig, secret, *artifact.Uri) if err != nil { return nil, err } @@ -243,7 +243,7 @@ func (s *ArtifactServer) generateResponseArtifact( case apiv2beta1.GetArtifactRequest_DOWNLOAD: queryParams := make(url.Values) queryParams.Set("response-content-disposition", "attachment") - shareUrl, err := s.resourceManager.GetSignedUrl(bucketConfig, secret, expiry, *artifact.Uri, queryParams) + shareUrl, err := s.resourceManager.GetSignedUrl(ctx, bucketConfig, secret, expiry, *artifact.Uri, queryParams) if err != nil { return nil, err } @@ -252,7 +252,7 @@ func (s *ArtifactServer) generateResponseArtifact( case apiv2beta1.GetArtifactRequest_RENDER: queryParams := make(url.Values) queryParams.Set("response-content-disposition", "inline") - renderUrl, err := s.resourceManager.GetSignedUrl(bucketConfig, secret, expiry, *artifact.Uri, queryParams) + renderUrl, err := s.resourceManager.GetSignedUrl(ctx, bucketConfig, secret, expiry, *artifact.Uri, queryParams) if err != nil { return nil, err } diff --git a/backend/src/apiserver/server/run_server_test.go b/backend/src/apiserver/server/run_server_test.go index fbd4446f620..4c6addfb460 100644 --- a/backend/src/apiserver/server/run_server_test.go +++ b/backend/src/apiserver/server/run_server_test.go @@ -1435,7 +1435,7 @@ func TestReadArtifactsV1_Succeed(t *testing.T) { expectedContent := "test" filePath := "test/file.txt" resourceManager, manager, run := initWithOneTimeRun(t) - resourceManager.ObjectStore().AddFile([]byte(expectedContent), filePath) + resourceManager.ObjectStore().AddFile(context.TODO(), []byte(expectedContent), filePath) workflow := util.NewWorkflow(&v1alpha1.Workflow{ TypeMeta: v1.TypeMeta{ APIVersion: "argoproj.io/v1alpha1", @@ -1582,7 +1582,7 @@ func TestReadArtifacts_Succeed(t *testing.T) { expectedContent := "test" filePath := "test/file.txt" resourceManager, manager, run := initWithOneTimeRun(t) - resourceManager.ObjectStore().AddFile([]byte(expectedContent), filePath) + resourceManager.ObjectStore().AddFile(context.TODO(), []byte(expectedContent), filePath) workflow := util.NewWorkflow(&v1alpha1.Workflow{ TypeMeta: v1.TypeMeta{ APIVersion: "argoproj.io/v1alpha1", diff --git a/backend/src/apiserver/storage/minio_client.go b/backend/src/apiserver/storage/minio_client.go index 4b0c790a2d6..d102162b4be 100644 --- a/backend/src/apiserver/storage/minio_client.go +++ b/backend/src/apiserver/storage/minio_client.go @@ -15,30 +15,35 @@ package storage import ( + "context" "io" - minio "github.com/minio/minio-go/v6" + minio "github.com/minio/minio-go/v7" ) // Create interface for minio client struct, making it more unit testable. type MinioClientInterface interface { - PutObject(bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (n int64, err error) - GetObject(bucketName, objectName string, opts minio.GetObjectOptions) (io.Reader, error) - DeleteObject(bucketName, objectName string) error + PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (n int64, err error) + GetObject(ctx context.Context, bucketName, objectName string, opts minio.GetObjectOptions) (io.Reader, error) + DeleteObject(ctx context.Context, bucketName, objectName string) error } type MinioClient struct { Client *minio.Client } -func (c *MinioClient) PutObject(bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (n int64, err error) { - return c.Client.PutObject(bucketName, objectName, reader, objectSize, opts) +func (c *MinioClient) PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (n int64, err error) { + info, err := c.Client.PutObject(ctx, bucketName, objectName, reader, objectSize, opts) + if err != nil { + return 0, err + } + return info.Size, nil } -func (c *MinioClient) GetObject(bucketName, objectName string, opts minio.GetObjectOptions) (io.Reader, error) { - return c.Client.GetObject(bucketName, objectName, opts) +func (c *MinioClient) GetObject(ctx context.Context, bucketName, objectName string, opts minio.GetObjectOptions) (io.Reader, error) { + return c.Client.GetObject(ctx, bucketName, objectName, opts) } -func (c *MinioClient) DeleteObject(bucketName, objectName string) error { - return c.Client.RemoveObject(bucketName, objectName) +func (c *MinioClient) DeleteObject(ctx context.Context, bucketName, objectName string) error { + return c.Client.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{}) } diff --git a/backend/src/apiserver/storage/minio_client_fake.go b/backend/src/apiserver/storage/minio_client_fake.go index 2e30faeb675..be140ff0772 100644 --- a/backend/src/apiserver/storage/minio_client_fake.go +++ b/backend/src/apiserver/storage/minio_client_fake.go @@ -16,9 +16,10 @@ package storage import ( "bytes" + "context" "io" - "github.com/minio/minio-go/v6" + "github.com/minio/minio-go/v7" "github.com/pkg/errors" ) @@ -32,7 +33,7 @@ func NewFakeMinioClient() *FakeMinioClient { } } -func (c *FakeMinioClient) PutObject(bucketName, objectName string, reader io.Reader, +func (c *FakeMinioClient) PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions, ) (int64, error) { buf := new(bytes.Buffer) @@ -41,7 +42,7 @@ func (c *FakeMinioClient) PutObject(bucketName, objectName string, reader io.Rea return 1, nil } -func (c *FakeMinioClient) GetObject(bucketName, objectName string, +func (c *FakeMinioClient) GetObject(ctx context.Context, bucketName, objectName string, opts minio.GetObjectOptions, ) (io.Reader, error) { if _, ok := c.minioClient[objectName]; !ok { @@ -50,7 +51,7 @@ func (c *FakeMinioClient) GetObject(bucketName, objectName string, return bytes.NewReader(c.minioClient[objectName]), nil } -func (c *FakeMinioClient) DeleteObject(bucketName, objectName string) error { +func (c *FakeMinioClient) DeleteObject(ctx context.Context, bucketName, objectName string) error { if _, ok := c.minioClient[objectName]; !ok { return errors.New("object not found") } diff --git a/backend/src/apiserver/storage/object_store.go b/backend/src/apiserver/storage/object_store.go index c3676537956..243499f820b 100644 --- a/backend/src/apiserver/storage/object_store.go +++ b/backend/src/apiserver/storage/object_store.go @@ -16,14 +16,18 @@ package storage import ( "bytes" + "context" "net/url" "path" "regexp" "time" + "github.com/minio/minio-go/v7/pkg/credentials" + + minio "github.com/minio/minio-go/v7" + "github.com/kubeflow/pipelines/backend/src/common/util" "github.com/kubeflow/pipelines/backend/src/v2/objectstore" - minio "github.com/minio/minio-go/v6" v1 "k8s.io/api/core/v1" "sigs.k8s.io/yaml" ) @@ -34,14 +38,14 @@ const ( // Interface for object store. type ObjectStoreInterface interface { - AddFile(template []byte, filePath string) error - DeleteFile(filePath string) error - GetFile(filePath string) ([]byte, error) - AddAsYamlFile(o interface{}, filePath string) error - GetFromYamlFile(o interface{}, filePath string) error + AddFile(ctx context.Context, template []byte, filePath string) error + DeleteFile(ctx context.Context, filePath string) error + GetFile(ctx context.Context, filePath string) ([]byte, error) + AddAsYamlFile(ctx context.Context, o interface{}, filePath string) error + GetFromYamlFile(ctx context.Context, o interface{}, filePath string) error GetPipelineKey(pipelineId string) string - GetSignedUrl(bucketConfig *objectstore.Config, secret *v1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) - GetObjectSize(bucketConfig *objectstore.Config, secret *v1.Secret, artifactURI string) (int64, error) + GetSignedUrl(ctx context.Context, bucketConfig *objectstore.Config, secret *v1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) + GetObjectSize(ctx context.Context, bucketConfig *objectstore.Config, secret *v1.Secret, artifactURI string) (int64, error) } // Managing pipeline using Minio. @@ -57,7 +61,7 @@ func (m *MinioObjectStore) GetPipelineKey(pipelineID string) string { return path.Join(m.baseFolder, pipelineID) } -func (m *MinioObjectStore) AddFile(file []byte, filePath string) error { +func (m *MinioObjectStore) AddFile(ctx context.Context, file []byte, filePath string) error { var parts int64 if m.disableMultipart { @@ -67,6 +71,7 @@ func (m *MinioObjectStore) AddFile(file []byte, filePath string) error { } _, err := m.minioClient.PutObject( + ctx, m.bucketName, filePath, bytes.NewReader(file), parts, minio.PutObjectOptions{ContentType: "application/octet-stream"}) if err != nil { @@ -75,16 +80,16 @@ func (m *MinioObjectStore) AddFile(file []byte, filePath string) error { return nil } -func (m *MinioObjectStore) DeleteFile(filePath string) error { - err := m.minioClient.DeleteObject(m.bucketName, filePath) +func (m *MinioObjectStore) DeleteFile(ctx context.Context, filePath string) error { + err := m.minioClient.DeleteObject(ctx, m.bucketName, filePath) if err != nil { return util.NewInternalServerError(err, "Failed to delete file %v", filePath) } return nil } -func (m *MinioObjectStore) GetFile(filePath string) ([]byte, error) { - reader, err := m.minioClient.GetObject(m.bucketName, filePath, minio.GetObjectOptions{}) +func (m *MinioObjectStore) GetFile(ctx context.Context, filePath string) ([]byte, error) { + reader, err := m.minioClient.GetObject(ctx, m.bucketName, filePath, minio.GetObjectOptions{}) if err != nil { return nil, util.NewInternalServerError(err, "Failed to get file %v", filePath) } @@ -103,20 +108,20 @@ func (m *MinioObjectStore) GetFile(filePath string) ([]byte, error) { return bytes, nil } -func (m *MinioObjectStore) AddAsYamlFile(o interface{}, filePath string) error { +func (m *MinioObjectStore) AddAsYamlFile(ctx context.Context, o interface{}, filePath string) error { bytes, err := yaml.Marshal(o) if err != nil { return util.NewInternalServerError(err, "Failed to marshal file %v: %v", filePath, err.Error()) } - err = m.AddFile(bytes, filePath) + err = m.AddFile(ctx, bytes, filePath) if err != nil { return util.Wrap(err, "Failed to add a yaml file") } return nil } -func (m *MinioObjectStore) GetFromYamlFile(o interface{}, filePath string) error { - bytes, err := m.GetFile(filePath) +func (m *MinioObjectStore) GetFromYamlFile(ctx context.Context, o interface{}, filePath string) error { + bytes, err := m.GetFile(ctx, filePath) if err != nil { return util.Wrap(err, "Failed to read from a yaml file") } @@ -132,7 +137,7 @@ func (m *MinioObjectStore) GetFromYamlFile(o interface{}, filePath string) error // store for this artifact. Signed URLs are built using the "GET" method, and are only intended for // Artifact downloads. // TODO: Add support for irsa and gcs app credentials pulled from environment -func (m *MinioObjectStore) GetSignedUrl(bucketConfig *objectstore.Config, secret *v1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) { +func (m *MinioObjectStore) GetSignedUrl(ctx context.Context, bucketConfig *objectstore.Config, secret *v1.Secret, expirySeconds time.Duration, artifactURI string, queryParams url.Values) (string, error) { s3Client, err := buildClientFromConfig(bucketConfig, secret) if err != nil { return "", err @@ -146,7 +151,7 @@ func (m *MinioObjectStore) GetSignedUrl(bucketConfig *objectstore.Config, secret queryParams = make(url.Values) } - signedUrl, err := s3Client.Presign("GET", bucketConfig.BucketName, key, expirySeconds, queryParams) + signedUrl, err := s3Client.Presign(ctx, "GET", bucketConfig.BucketName, key, expirySeconds, queryParams) if err != nil { return "", util.Wrap(err, "Failed to generate signed url") } @@ -156,7 +161,7 @@ func (m *MinioObjectStore) GetSignedUrl(bucketConfig *objectstore.Config, secret // GetObjectSize Retrieves the Size of the object in bytes. // Return zero with no error if artifact URI does not exist. -func (m *MinioObjectStore) GetObjectSize(bucketConfig *objectstore.Config, secret *v1.Secret, artifactURI string) (int64, error) { +func (m *MinioObjectStore) GetObjectSize(ctx context.Context, bucketConfig *objectstore.Config, secret *v1.Secret, artifactURI string) (int64, error) { s3Client, err := buildClientFromConfig(bucketConfig, secret) if err != nil { return 0, err @@ -165,7 +170,7 @@ func (m *MinioObjectStore) GetObjectSize(bucketConfig *objectstore.Config, secre if err != nil { return 0, err } - objectInfo, err := s3Client.StatObject(bucketConfig.BucketName, key, minio.StatObjectOptions{}) + objectInfo, err := s3Client.StatObject(ctx, bucketConfig.BucketName, key, minio.StatObjectOptions{}) if err != nil { errResponse := minio.ToErrorResponse(err) if errResponse.Code == "NoSuchKey" { @@ -198,10 +203,10 @@ func buildClientFromConfig(bucketConfig *objectstore.Config, secret *v1.Secret) secure = !params.DisableSSL } s3Client, err := minio.New( - parsedUrl.Host, - accessKey, - secretKey, - secure) + parsedUrl.Host, &minio.Options{ + Creds: credentials.NewStaticV4(accessKey, secretKey, ""), + Secure: secure, + }) if err != nil { return nil, util.Wrap(err, "Failed to create s3 client.") } diff --git a/backend/src/apiserver/storage/object_store_fake.go b/backend/src/apiserver/storage/object_store_fake.go index 1989b9c8b29..174e44ae3f9 100644 --- a/backend/src/apiserver/storage/object_store_fake.go +++ b/backend/src/apiserver/storage/object_store_fake.go @@ -15,6 +15,7 @@ package storage import ( + "context" "net/url" "time" @@ -30,27 +31,28 @@ func (m *fakeMinioObjectStore) GetPipelineKey(pipelineID string) string { return m.minioObjectStore.GetPipelineKey(pipelineID) } -func (m *fakeMinioObjectStore) AddFile(file []byte, filePath string) error { - return m.minioObjectStore.AddFile(file, filePath) +func (m *fakeMinioObjectStore) AddFile(ctx context.Context, file []byte, filePath string) error { + return m.minioObjectStore.AddFile(ctx, file, filePath) } -func (m *fakeMinioObjectStore) DeleteFile(filePath string) error { - return m.minioObjectStore.DeleteFile(filePath) +func (m *fakeMinioObjectStore) DeleteFile(ctx context.Context, filePath string) error { + return m.minioObjectStore.DeleteFile(ctx, filePath) } -func (m *fakeMinioObjectStore) GetFile(filePath string) ([]byte, error) { - return m.minioObjectStore.GetFile(filePath) +func (m *fakeMinioObjectStore) GetFile(ctx context.Context, filePath string) ([]byte, error) { + return m.minioObjectStore.GetFile(ctx, filePath) } -func (m *fakeMinioObjectStore) AddAsYamlFile(o interface{}, filePath string) error { - return m.minioObjectStore.AddAsYamlFile(o, filePath) +func (m *fakeMinioObjectStore) AddAsYamlFile(ctx context.Context, o interface{}, filePath string) error { + return m.minioObjectStore.AddAsYamlFile(ctx, o, filePath) } -func (m *fakeMinioObjectStore) GetFromYamlFile(o interface{}, filePath string) error { - return m.minioObjectStore.GetFromYamlFile(o, filePath) +func (m *fakeMinioObjectStore) GetFromYamlFile(ctx context.Context, o interface{}, filePath string) error { + return m.minioObjectStore.GetFromYamlFile(ctx, o, filePath) } func (m *fakeMinioObjectStore) GetSignedUrl( + ctx context.Context, bucketConfig *objectstore.Config, secret *v1.Secret, expiry time.Duration, uri string, queryParams url.Values) (string, error) { @@ -60,7 +62,7 @@ func (m *fakeMinioObjectStore) GetSignedUrl( return "dummy-signed-url", nil } -func (m *fakeMinioObjectStore) GetObjectSize(*objectstore.Config, *v1.Secret, string) (int64, error) { +func (m *fakeMinioObjectStore) GetObjectSize(context.Context, *objectstore.Config, *v1.Secret, string) (int64, error) { return 123, nil } diff --git a/backend/src/apiserver/storage/object_store_test.go b/backend/src/apiserver/storage/object_store_test.go index cbb16f39c77..e34a6aa9a47 100644 --- a/backend/src/apiserver/storage/object_store_test.go +++ b/backend/src/apiserver/storage/object_store_test.go @@ -16,11 +16,14 @@ package storage import ( "bytes" + "context" "io" "testing" + "github.com/stretchr/testify/require" + "github.com/kubeflow/pipelines/backend/src/common/util" - minio "github.com/minio/minio-go/v6" + minio "github.com/minio/minio-go/v7" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" @@ -30,26 +33,26 @@ type Foo struct{ ID int } type FakeBadMinioClient struct{} -func (c *FakeBadMinioClient) PutObject(bucketName, objectName string, reader io.Reader, +func (c *FakeBadMinioClient) PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions, ) (n int64, err error) { return 0, errors.New("some error") } -func (c *FakeBadMinioClient) GetObject(bucketName, objectName string, +func (c *FakeBadMinioClient) GetObject(ctx context.Context, bucketName, objectName string, opts minio.GetObjectOptions, ) (io.Reader, error) { return nil, errors.New("some error") } -func (c *FakeBadMinioClient) DeleteObject(bucketName, objectName string) error { +func (c *FakeBadMinioClient) DeleteObject(ctx context.Context, bucketName, objectName string) error { return errors.New("some error") } func TestAddFile(t *testing.T) { minioClient := NewFakeMinioClient() manager := &MinioObjectStore{minioClient: minioClient, baseFolder: "pipeline"} - error := manager.AddFile([]byte("abc"), manager.GetPipelineKey("1")) + error := manager.AddFile(context.TODO(), []byte("abc"), manager.GetPipelineKey("1")) assert.Nil(t, error) assert.Equal(t, 1, minioClient.GetObjectCount()) assert.True(t, minioClient.ExistObject("pipeline/1")) @@ -57,43 +60,43 @@ func TestAddFile(t *testing.T) { func TestAddFileError(t *testing.T) { manager := &MinioObjectStore{minioClient: &FakeBadMinioClient{}} - error := manager.AddFile([]byte("abc"), manager.GetPipelineKey("1")) + error := manager.AddFile(context.TODO(), []byte("abc"), manager.GetPipelineKey("1")) assert.Equal(t, codes.Internal, error.(*util.UserError).ExternalStatusCode()) } func TestGetFile(t *testing.T) { manager := &MinioObjectStore{minioClient: NewFakeMinioClient(), baseFolder: "pipeline"} - manager.AddFile([]byte("abc"), manager.GetPipelineKey("1")) - file, error := manager.GetFile(manager.GetPipelineKey("1")) + manager.AddFile(context.TODO(), []byte("abc"), manager.GetPipelineKey("1")) + file, error := manager.GetFile(context.TODO(), manager.GetPipelineKey("1")) assert.Nil(t, error) assert.Equal(t, file, []byte("abc")) } func TestGetFileError(t *testing.T) { manager := &MinioObjectStore{minioClient: &FakeBadMinioClient{}, baseFolder: "pipeline"} - _, error := manager.GetFile(manager.GetPipelineKey("1")) + _, error := manager.GetFile(context.TODO(), manager.GetPipelineKey("1")) assert.Equal(t, codes.Internal, error.(*util.UserError).ExternalStatusCode()) } func TestDeleteFile(t *testing.T) { minioClient := NewFakeMinioClient() manager := &MinioObjectStore{minioClient: minioClient, baseFolder: "pipeline"} - manager.AddFile([]byte("abc"), manager.GetPipelineKey("1")) - error := manager.DeleteFile(manager.GetPipelineKey("1")) + manager.AddFile(context.TODO(), []byte("abc"), manager.GetPipelineKey("1")) + error := manager.DeleteFile(context.TODO(), manager.GetPipelineKey("1")) assert.Nil(t, error) assert.Equal(t, 0, minioClient.GetObjectCount()) } func TestDeleteFileError(t *testing.T) { manager := &MinioObjectStore{minioClient: &FakeBadMinioClient{}} - error := manager.DeleteFile(manager.GetPipelineKey("1")) + error := manager.DeleteFile(context.TODO(), manager.GetPipelineKey("1")) assert.Equal(t, codes.Internal, error.(*util.UserError).ExternalStatusCode()) } func TestAddAsYamlFile(t *testing.T) { minioClient := NewFakeMinioClient() manager := &MinioObjectStore{minioClient: minioClient, baseFolder: "pipeline"} - error := manager.AddAsYamlFile(Foo{ID: 1}, manager.GetPipelineKey("1")) + error := manager.AddAsYamlFile(context.TODO(), Foo{ID: 1}, manager.GetPipelineKey("1")) assert.Nil(t, error) assert.Equal(t, 1, minioClient.GetObjectCount()) assert.True(t, minioClient.ExistObject("pipeline/1")) @@ -102,13 +105,19 @@ func TestAddAsYamlFile(t *testing.T) { func TestGetFromYamlFile(t *testing.T) { minioClient := NewFakeMinioClient() manager := &MinioObjectStore{minioClient: minioClient, baseFolder: "pipeline"} - manager.minioClient.PutObject( - "", manager.GetPipelineKey("1"), - bytes.NewReader([]byte("id: 1")), -1, - minio.PutObjectOptions{ContentType: "application/octet-stream"}) + + _, err := manager.minioClient.PutObject( + context.TODO(), + "", + manager.GetPipelineKey("1"), + bytes.NewReader([]byte("id: 1")), + -1, + minio.PutObjectOptions{ContentType: "application/octet-stream"}, + ) + require.Nil(t, err) expectedFoo := Foo{ID: 1} var foo Foo - error := manager.GetFromYamlFile(&foo, manager.GetPipelineKey("1")) + error := manager.GetFromYamlFile(context.TODO(), &foo, manager.GetPipelineKey("1")) assert.Nil(t, error) assert.Equal(t, expectedFoo, foo) } @@ -117,11 +126,12 @@ func TestGetFromYamlFile_UnmarshalError(t *testing.T) { minioClient := NewFakeMinioClient() manager := &MinioObjectStore{minioClient: minioClient, baseFolder: "pipeline"} manager.minioClient.PutObject( + context.TODO(), "", manager.GetPipelineKey("1"), bytes.NewReader([]byte("invalid")), -1, minio.PutObjectOptions{ContentType: "application/octet-stream"}) var foo Foo - error := manager.GetFromYamlFile(&foo, manager.GetPipelineKey("1")) + error := manager.GetFromYamlFile(context.TODO(), &foo, manager.GetPipelineKey("1")) assert.Equal(t, codes.Internal, error.(*util.UserError).ExternalStatusCode()) assert.Contains(t, error.Error(), "Failed to unmarshal") } diff --git a/go.mod b/go.mod index f38668c484a..6960f5d6627 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/kubeflow/pipelines/third_party/ml-metadata v0.0.0-20240416215826-da804407ad31 github.com/lestrrat-go/strftime v1.0.4 github.com/mattn/go-sqlite3 v1.14.19 - github.com/minio/minio-go/v6 v6.0.57 + github.com/minio/minio-go/v7 v7.0.65 github.com/peterhellberg/duration v0.0.0-20191119133758-ec6baeebcd10 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.1 @@ -100,6 +100,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denisenkom/go-mssqldb v0.12.3 // indirect github.com/doublerebel/bellows v0.0.0-20160303004610-f177d92a03d3 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect github.com/evanphx/json-patch v5.8.0+incompatible // indirect @@ -154,7 +155,6 @@ require ( github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.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/moby/spdystream v0.2.0 // indirect @@ -169,6 +169,7 @@ require ( github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect diff --git a/go.sum b/go.sum index 10626e13f4e..e65d64a6b4d 100644 --- a/go.sum +++ b/go.sum @@ -139,7 +139,7 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/doublerebel/bellows v0.0.0-20160303004610-f177d92a03d3 h1:7nllYTGLnq4CqBL27lV6oNfXzM2tJ2mrKF8E+aBXOV0= github.com/doublerebel/bellows v0.0.0-20160303004610-f177d92a03d3/go.mod h1:v/MTKot4he5oRHGirOYGN4/hEOONNnWtDBLAzllSGMw= -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.3.0 h1:RRL0nge+cWGlxXbUzJ7yMcq6w2XBEr19dCN6HECGaT0= github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= @@ -414,7 +414,6 @@ github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDP github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= @@ -480,11 +479,9 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 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.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/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -495,7 +492,6 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= @@ -540,20 +536,16 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -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-go/v6 v6.0.57 h1:ixPkbKkyD7IhnluRgQpGSpHdpvNVaW6OD5R9IAO/9Tw= -github.com/minio/minio-go/v6 v6.0.57/go.mod h1:5+R/nM9Pwrh0vqF+HbYYDQ84wdUFPyXHkrdT4AIkifM= github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/minio-go/v7 v7.0.65 h1:sOlB8T3nQK+TApTpuN3k4WD5KasvZIE3vVFzyyCa0go= +github.com/minio/minio-go/v7 v7.0.65/go.mod h1:R4WVUR6ZTedlCcGwZRauLMIKjgyaWxhs4Mqi/OMPmEc= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 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.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -631,6 +623,7 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= @@ -643,11 +636,8 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= 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/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -745,7 +735,6 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -788,7 +777,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -916,7 +904,6 @@ golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGm 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-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -998,7 +985,6 @@ 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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=