diff --git a/CHANGELOG.md b/CHANGELOG.md index 96d62005..01f6237e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Ability to run all unit tests on Windows. - Deprecated delete.WithDeleteAllVersions in favor of delete.WithAllVersions. +### Security +- Switched to new Azure SDK (from deprecated github.com/Azure/azure-storage-blob-go) ## [6.24.0] - 2024-12-16 ### Security diff --git a/backend/azure/client.go b/backend/azure/client.go index e037626e..e76439d0 100644 --- a/backend/azure/client.go +++ b/backend/azure/client.go @@ -2,14 +2,16 @@ package azure import ( "context" - "fmt" + "errors" "io" - "net/url" "strings" "time" - "github.com/Azure/azure-pipeline-go/pipeline" - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container" "github.com/c2fo/vfs/v6" "github.com/c2fo/vfs/v6/utils" @@ -24,7 +26,7 @@ type Client interface { // SetMetadata should add the metadata specified by the parameter metadata for the blob specified by the parameter // file. - SetMetadata(file vfs.File, metadata map[string]string) error + SetMetadata(file vfs.File, metadata map[string]*string) error // Upload should create or update the blob specified by the file parameter with the contents of the content // parameter @@ -48,7 +50,7 @@ type Client interface { // DefaultClient is the main implementation that actually makes the calls to Azure Blob Storage type DefaultClient struct { - pipeline pipeline.Pipeline + credential any } // NewClient initializes a new DefaultClient @@ -58,43 +60,39 @@ func NewClient(options *Options) (*DefaultClient, error) { return nil, err } - // This configures the client to use the default retry policy. The default policy uses exponential backoff with - // maxRetries = 4. If this behavior needs to be changed, add the Retry member to azblob.PipelineOptions. For - // more information on azure retry policies see https://pkg.go.dev/github.com/Azure/azure-storage-blob-go/azblob#RetryOptions - // - // Example (this is not the default): - // RetryOptions{ - // Policy: RetryPolicyExponential, // Use exponential backoff as opposed to linear - // MaxTries: 3, // Try at most 3 times to perform the operation (set to 1 to disable retries) - // TryTimeout: time.Second * 3, // Maximum time allowed for any single try - // RetryDelay: time.Second * 1, // Backoff amount for each retry (exponential or linear) - // MaxRetryDelay: time.Second * 3, // Max delay between retries - // } - pl := azblob.NewPipeline(credential, azblob.PipelineOptions{}) - - return &DefaultClient{pl}, nil + return &DefaultClient{credential}, nil +} + +func (a *DefaultClient) newContainerClient(containerURL string) (*container.Client, error) { + switch cred := a.credential.(type) { + case azcore.TokenCredential: + return container.NewClient(containerURL, cred, nil) + case *container.SharedKeyCredential: + return container.NewClientWithSharedKeyCredential(containerURL, cred, nil) + default: + return container.NewClientWithNoCredential(containerURL, nil) + } } // Properties fetches the properties for the blob specified by the parameters containerURI and filePath func (a *DefaultClient) Properties(containerURI, filePath string) (*BlobProperties, error) { - URL, err := url.Parse(containerURI) + cli, err := a.newContainerClient(containerURI) if err != nil { return nil, err } - containerURL := azblob.NewContainerURL(*URL, a.pipeline) if filePath == "" { // this is only used to check for the existence of a container so we don't care about anything but the // error - _, err := containerURL.GetProperties(context.Background(), azblob.LeaseAccessConditions{}) + _, err := cli.GetProperties(context.Background(), nil) if err != nil { return nil, err } return nil, nil } - blobURL := containerURL.NewBlockBlobURL(utils.RemoveLeadingSlash(filePath)) - resp, err := blobURL.GetProperties(context.Background(), azblob.BlobAccessConditions{}, azblob.ClientProvidedKeyOptions{}) + blobURL := cli.NewBlockBlobClient(utils.RemoveLeadingSlash(filePath)) + resp, err := blobURL.GetProperties(context.Background(), nil) if err != nil { return nil, err } @@ -103,45 +101,42 @@ func (a *DefaultClient) Properties(containerURI, filePath string) (*BlobProperti // Upload uploads a new file to Azure Blob Storage func (a *DefaultClient) Upload(file vfs.File, content io.ReadSeeker, contentType string) error { - URL, err := url.Parse(file.Location().(*Location).ContainerURL()) + cli, err := a.newContainerClient(file.Location().(*Location).ContainerURL()) if err != nil { return err } - - containerURL := azblob.NewContainerURL(*URL, a.pipeline) - blobURL := containerURL.NewBlockBlobURL(utils.RemoveLeadingSlash(file.Path())) - _, err = blobURL.Upload(context.Background(), content, azblob.BlobHTTPHeaders{ContentType: contentType}, azblob.Metadata{}, - azblob.BlobAccessConditions{}, azblob.DefaultAccessTier, nil, azblob.ClientProvidedKeyOptions{}, azblob.ImmutabilityPolicyOptions{}) + blobURL := cli.NewBlockBlobClient(utils.RemoveLeadingSlash(file.Path())) + body, ok := content.(io.ReadSeekCloser) + if !ok { + body = streaming.NopCloser(content) + } + _, err = blobURL.Upload(context.Background(), body, nil) return err } // SetMetadata sets the given metadata for the blob -func (a *DefaultClient) SetMetadata(file vfs.File, metadata map[string]string) error { - URL, err := url.Parse(file.Location().(*Location).ContainerURL()) +func (a *DefaultClient) SetMetadata(file vfs.File, metadata map[string]*string) error { + cli, err := a.newContainerClient(file.Location().(*Location).ContainerURL()) if err != nil { return err } - - containerURL := azblob.NewContainerURL(*URL, a.pipeline) - blobURL := containerURL.NewBlockBlobURL(utils.RemoveLeadingSlash(file.Path())) - _, err = blobURL.SetMetadata(context.Background(), metadata, azblob.BlobAccessConditions{}, azblob.ClientProvidedKeyOptions{}) + blobURL := cli.NewBlockBlobClient(utils.RemoveLeadingSlash(file.Path())) + _, err = blobURL.SetMetadata(context.Background(), metadata, nil) return err } // Download returns an io.ReadCloser for the given vfs.File func (a *DefaultClient) Download(file vfs.File) (io.ReadCloser, error) { - URL, err := url.Parse(file.Location().(*Location).ContainerURL()) + cli, err := a.newContainerClient(file.Location().(*Location).ContainerURL()) if err != nil { return nil, err } - - containerURL := azblob.NewContainerURL(*URL, a.pipeline) - blobURL := containerURL.NewBlockBlobURL(utils.RemoveLeadingSlash(file.Path())) - get, err := blobURL.Download(context.Background(), 0, 0, azblob.BlobAccessConditions{}, false, azblob.ClientProvidedKeyOptions{}) + blobURL := cli.NewBlockBlobClient(utils.RemoveLeadingSlash(file.Path())) + get, err := blobURL.DownloadStream(context.Background(), nil) if err != nil { return nil, err } - return get.Body(azblob.RetryReaderOptions{}), nil + return get.Body, nil } // Copy copies srcFile to the destination tgtFile within Azure Blob Storage. Note that in the case where we get @@ -149,58 +144,54 @@ func (a *DefaultClient) Download(file vfs.File) (io.ReadCloser, error) { // error. func (a *DefaultClient) Copy(srcFile, tgtFile vfs.File) error { // Can't use url.PathEscape here since that will escape everything (even the directory separators) - srcURL, err := url.Parse(strings.Replace(srcFile.URI(), "%", "%25", -1)) - if err != nil { - return err - } + srcURL := strings.Replace(srcFile.URI(), "%", "%25", -1) - tgtURL, err := url.Parse(tgtFile.Location().(*Location).ContainerURL()) + tgtURL := tgtFile.Location().(*Location).ContainerURL() + + cli, err := a.newContainerClient(tgtURL) if err != nil { return err } - - containerURL := azblob.NewContainerURL(*tgtURL, a.pipeline) - blobURL := containerURL.NewBlockBlobURL(utils.RemoveLeadingSlash(tgtFile.Path())) + blobURL := cli.NewBlockBlobClient(utils.RemoveLeadingSlash(tgtFile.Path())) ctx := context.Background() - resp, err := blobURL.StartCopyFromURL(ctx, *srcURL, azblob.Metadata{}, azblob.ModifiedAccessConditions{}, - azblob.BlobAccessConditions{}, azblob.DefaultAccessTier, nil) + resp, err := blobURL.StartCopyFromURL(ctx, srcURL, nil) if err != nil { return err } - for resp.CopyStatus() == azblob.CopyStatusPending { + for *resp.CopyStatus == blob.CopyStatusTypePending { time.Sleep(2 * time.Second) } - if resp.CopyStatus() == azblob.CopyStatusSuccess { + if *resp.CopyStatus == blob.CopyStatusTypeSuccess { return nil } - return fmt.Errorf("copy failed ERROR[%s]", resp.ErrorCode()) + return errors.New("copy failed") } // List will return a listing of the contents of the given location. Each item in the list will contain the full key // as specified by the azure blob (including the virtual 'path'). func (a *DefaultClient) List(l vfs.Location) ([]string, error) { - URL, err := url.Parse(l.(*Location).ContainerURL()) + cli, err := a.newContainerClient(l.(*Location).ContainerURL()) if err != nil { return []string{}, err } - containerURL := azblob.NewContainerURL(*URL, a.pipeline) + pager := cli.NewListBlobsHierarchyPager("/", &container.ListBlobsHierarchyOptions{ + Prefix: to.Ptr(utils.RemoveLeadingSlash(l.Path())), + Include: container.ListBlobsInclude{Metadata: true, Tags: true}, + }) ctx := context.Background() var list []string - for marker := (azblob.Marker{}); marker.NotDone(); { - listBlob, err := containerURL.ListBlobsHierarchySegment(ctx, marker, "/", - azblob.ListBlobsSegmentOptions{Prefix: utils.RemoveLeadingSlash(l.Path())}) + for pager.More() { + listBlob, err := pager.NextPage(ctx) if err != nil { return []string{}, err } - marker = listBlob.NextMarker - - for i := range listBlob.Segment.BlobItems { - list = append(list, listBlob.Segment.BlobItems[i].Name) + for i := range listBlob.ListBlobsHierarchySegmentResponse.Segment.BlobItems { + list = append(list, *listBlob.ListBlobsHierarchySegmentResponse.Segment.BlobItems[i].Name) } } return list, nil @@ -208,14 +199,12 @@ func (a *DefaultClient) List(l vfs.Location) ([]string, error) { // Delete deletes the given file from Azure Blob Storage. func (a *DefaultClient) Delete(file vfs.File) error { - URL, err := url.Parse(file.Location().(*Location).ContainerURL()) + cli, err := a.newContainerClient(file.Location().(*Location).ContainerURL()) if err != nil { return err } - - containerURL := azblob.NewContainerURL(*URL, a.pipeline) - blobURL := containerURL.NewBlockBlobURL(utils.RemoveLeadingSlash(file.Path())) - _, err = blobURL.Delete(context.Background(), azblob.DeleteSnapshotsOptionNone, azblob.BlobAccessConditions{}) + blobURL := cli.NewBlockBlobClient(utils.RemoveLeadingSlash(file.Path())) + _, err = blobURL.Delete(context.Background(), nil) return err } @@ -224,22 +213,24 @@ func (a *DefaultClient) Delete(file vfs.File) error { // If soft deletion is enabled for blobs in the storage account, each version will be marked for deletion and will be // permanently deleted by Azure as per the soft deletion policy. func (a *DefaultClient) DeleteAllVersions(file vfs.File) error { - URL, err := url.Parse(file.Location().(*Location).ContainerURL()) + cli, err := a.newContainerClient(file.Location().(*Location).ContainerURL()) if err != nil { return err } + blobURL := cli.NewBlockBlobClient(utils.RemoveLeadingSlash(file.Path())) - containerURL := azblob.NewContainerURL(*URL, a.pipeline) - blobURL := containerURL.NewBlockBlobURL(utils.RemoveLeadingSlash(file.Path())) - - versions, err := a.getBlobVersions(containerURL, utils.RemoveLeadingSlash(file.Path())) + versions, err := a.getBlobVersions(cli, utils.RemoveLeadingSlash(file.Path())) if err != nil { return err } for _, version := range versions { // Delete a specific version - _, err = blobURL.WithVersionID(*version).Delete(context.Background(), azblob.DeleteSnapshotsOptionNone, azblob.BlobAccessConditions{}) + cli, err := blobURL.WithVersionID(*version) + if err != nil { + return err + } + _, err = cli.Delete(context.Background(), nil) if err != nil { return err } @@ -248,23 +239,21 @@ func (a *DefaultClient) DeleteAllVersions(file vfs.File) error { return err } -func (a *DefaultClient) getBlobVersions(containerURL azblob.ContainerURL, blobName string) ([]*string, error) { +func (a *DefaultClient) getBlobVersions(cli *container.Client, blobName string) ([]*string, error) { ctx := context.Background() + pager := cli.NewListBlobsFlatPager(&container.ListBlobsFlatOptions{ + Prefix: &blobName, + Include: container.ListBlobsInclude{Versions: true}, + }) var versions []*string - for marker := (azblob.Marker{}); marker.NotDone(); { - listBlob, err := containerURL.ListBlobsFlatSegment(ctx, marker, - azblob.ListBlobsSegmentOptions{Prefix: blobName, Details: azblob.BlobListingDetails{Versions: true}}) + for pager.More() { + listBlob, err := pager.NextPage(ctx) if err != nil { - return nil, err + return []*string{}, err } - marker = listBlob.NextMarker - - for i := range listBlob.Segment.BlobItems { - blobItem := listBlob.Segment.BlobItems[i] - if blobItem.VersionID != nil { - versions = append(versions, blobItem.VersionID) - } + for i := range listBlob.ListBlobsFlatSegmentResponse.Segment.BlobItems { + versions = append(versions, listBlob.ListBlobsFlatSegmentResponse.Segment.BlobItems[i].VersionID) } } return versions, nil diff --git a/backend/azure/client_integration_test.go b/backend/azure/client_integration_test.go index b1139a83..7d80062c 100644 --- a/backend/azure/client_integration_test.go +++ b/backend/azure/client_integration_test.go @@ -7,19 +7,21 @@ import ( "context" "fmt" "io" - "net/url" "os" "strings" "testing" "time" - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container" "github.com/stretchr/testify/suite" ) type ClientIntegrationTestSuite struct { suite.Suite - testContainerURL azblob.ContainerURL + testContainerURL *container.Client accountName string accountKey string } @@ -32,28 +34,27 @@ func (s *ClientIntegrationTestSuite) SetupSuite() { panic(err) } - p := azblob.NewPipeline(credential, azblob.PipelineOptions{}) - baseURL, err := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net", s.accountName)) + cli, err := container.NewClientWithSharedKeyCredential(fmt.Sprintf("https://%s.blob.core.windows.net", s.accountName), credential, nil) s.NoError(err) - serviceURL := azblob.NewServiceURL(*baseURL, p) - s.testContainerURL = serviceURL.NewContainerURL("test-container") - _, err = s.testContainerURL.Create(context.Background(), azblob.Metadata{}, azblob.PublicAccessNone) + s.testContainerURL = cli + + _, err = s.testContainerURL.Create(context.Background(), nil) s.NoError(err) // The create function claims to be synchronous but for some reason it does not exist for a little bit so // we need to wait for it to be there. - _, err = s.testContainerURL.GetProperties(context.Background(), azblob.LeaseAccessConditions{}) + _, err = s.testContainerURL.GetProperties(context.Background(), nil) for { time.Sleep(2 * time.Second) - if err == nil || err.(azblob.StorageError).ServiceCode() != "BlobNotFound" { + if err == nil || !bloberror.HasCode(err, bloberror.BlobNotFound) { break } - _, err = s.testContainerURL.GetProperties(context.Background(), azblob.LeaseAccessConditions{}) + _, err = s.testContainerURL.GetProperties(context.Background(), nil) } } func (s *ClientIntegrationTestSuite) TearDownSuite() { - _, err := s.testContainerURL.Delete(context.Background(), azblob.ContainerAccessConditions{}) + _, err := s.testContainerURL.Delete(context.Background(), nil) s.NoError(err) } @@ -206,7 +207,7 @@ func (s *ClientIntegrationTestSuite) TestProperties_NonExistentFile() { _, err = client.Properties(f.Location().URI(), f.Path()) s.Error(err, "The file does not exist so we expect an error") - s.Equal(404, err.(azblob.ResponseError).Response().StatusCode) + s.Equal(404, err.(*azcore.ResponseError).StatusCode) } func (s *ClientIntegrationTestSuite) TestDelete_NonExistentFile() { diff --git a/backend/azure/doc.go b/backend/azure/doc.go index bb3a529a..2a2e409f 100644 --- a/backend/azure/doc.go +++ b/backend/azure/doc.go @@ -56,7 +56,7 @@ preferring the first location found: to containers from multiple storage accounts. 2. The ENV vars VFS_AZURE_STORAGE_ACCOUNT and VFS_AZURE_STORAGE_KEY, a shared key authenticator is used. This will allow access to any containers owned by the designated storage account. - 3. If none of the above are present, then an anonymous authenticator is created and only publicly accessible blobs + 3. If none of the above are present, then no credentials are used and only publicly accessible blobs will be available */ package azure diff --git a/backend/azure/file.go b/backend/azure/file.go index 58316607..d2ee1d9f 100644 --- a/backend/azure/file.go +++ b/backend/azure/file.go @@ -9,7 +9,8 @@ import ( "strings" "time" - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror" "github.com/c2fo/vfs/v6" "github.com/c2fo/vfs/v6/backend" @@ -131,7 +132,7 @@ func (f *File) Exists() (bool, error) { } _, err = client.Properties(f.Location().(*Location).ContainerURL(), f.Path()) if err != nil { - if err.(azblob.StorageError).ServiceCode() != "BlobNotFound" { + if !bloberror.HasCode(err, bloberror.BlobNotFound) { return false, err } return false, nil @@ -292,7 +293,7 @@ func (f *File) Size() (uint64, error) { if err != nil { return 0, err } - return props.Size, nil + return uint64(*props.Size), nil } // Path returns full path with leading slash. @@ -336,8 +337,8 @@ func (f *File) Touch() error { return err } - newMetadata := make(map[string]string) - newMetadata["updated"] = "true" + newMetadata := make(map[string]*string) + newMetadata["updated"] = to.Ptr("true") if err := client.SetMetadata(f, newMetadata); err != nil { return err } diff --git a/backend/azure/file_test.go b/backend/azure/file_test.go index 4d5a364a..c7f04d47 100644 --- a/backend/azure/file_test.go +++ b/backend/azure/file_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/stretchr/testify/suite" "github.com/c2fo/vfs/v6" @@ -32,11 +33,12 @@ func (s *FileTestSuite) TestClose() { } func (s *FileTestSuite) TestClose_FlushTempFile() { - client := MockAzureClient{PropertiesError: MockStorageError{}} + client := MockAzureClient{PropertiesError: blobNotFoundErr} fs := NewFileSystem().WithClient(&client) f, _ := fs.NewFile("test-container", "/foo.txt") - _, _ = f.Write([]byte("Hello, World!")) + _, err := f.Write([]byte("Hello, World!")) + s.Require().NoError(err) s.NoError(f.Close()) } @@ -105,7 +107,7 @@ func (s *FileTestSuite) TestExists() { } func (s *FileTestSuite) TestExists_NonExistentFile() { - client := MockAzureClient{PropertiesError: MockStorageError{}} + client := MockAzureClient{PropertiesError: blobNotFoundErr} fs := NewFileSystem().WithClient(&client) f, err := fs.NewFile("test-container", "/foo.txt") @@ -116,7 +118,7 @@ func (s *FileTestSuite) TestExists_NonExistentFile() { } func (s *FileTestSuite) TestCloseWithContentType() { - client := MockAzureClient{PropertiesError: MockStorageError{}} + client := MockAzureClient{PropertiesError: blobNotFoundErr} fs := NewFileSystem().WithClient(&client) f, _ := fs.NewFile("test-container", "/foo.txt", newfile.WithContentType("text/plain")) _, _ = f.Write([]byte("Hello, World!")) @@ -241,7 +243,7 @@ func (s *FileTestSuite) TestLastModified() { } func (s *FileTestSuite) TestSize() { - client := MockAzureClient{PropertiesResult: &BlobProperties{Size: 5}} + client := MockAzureClient{PropertiesResult: &BlobProperties{Size: to.Ptr[int64](5)}} fs := NewFileSystem().WithClient(&client) f, err := fs.NewFile("test-container", "/foo.txt") @@ -280,7 +282,7 @@ func (s *FileTestSuite) TestName() { } func (s *FileTestSuite) TestTouch() { - client := MockAzureClient{ExpectedResult: &BlobProperties{}, PropertiesError: MockStorageError{}} + client := MockAzureClient{PropertiesError: blobNotFoundErr} fs := NewFileSystem().WithClient(&client) f, err := fs.NewFile("test-container", "/foo.txt") @@ -298,7 +300,7 @@ func (s *FileTestSuite) TestTouch_NonexistentContainer() { } func (s *FileTestSuite) TestTouchWithContentType() { - client := MockAzureClient{ExpectedResult: &BlobProperties{}, PropertiesError: MockStorageError{}} + client := MockAzureClient{ExpectedResult: &BlobProperties{}, PropertiesError: blobNotFoundErr} fs := NewFileSystem().WithClient(&client) f, err := fs.NewFile("test-container", "/foo.txt", newfile.WithContentType("text/plain")) @@ -339,7 +341,7 @@ func (s *FileTestSuite) TestCheckTempFile() { } func (s *FileTestSuite) TestCheckTempFile_FileDoesNotExist() { - client := MockAzureClient{PropertiesError: MockStorageError{}} + client := MockAzureClient{PropertiesError: blobNotFoundErr} fs := NewFileSystem().WithClient(&client) f, err := fs.NewFile("test-container", "/foo.txt") diff --git a/backend/azure/mock_client.go b/backend/azure/mock_client.go index 13a8d3fa..e6965c5d 100644 --- a/backend/azure/mock_client.go +++ b/backend/azure/mock_client.go @@ -2,9 +2,9 @@ package azure import ( "io" - "net/http" - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror" "github.com/c2fo/vfs/v6" ) @@ -27,7 +27,7 @@ func (a *MockAzureClient) Properties(locationURI, filePath string) (*BlobPropert } // SetMetadata returns the value of ExpectedError -func (a *MockAzureClient) SetMetadata(file vfs.File, metadata map[string]string) error { +func (a *MockAzureClient) SetMetadata(vfs.File, map[string]*string) error { return a.ExpectedError } @@ -68,32 +68,4 @@ func (a *MockAzureClient) DeleteAllVersions(file vfs.File) error { return a.ExpectedError } -// MockStorageError is a mock for the azblob.StorageError interface -type MockStorageError struct { - azblob.ResponseError -} - -// ServiceCode always returns "BlobNotFound" to simulate the not found condition -func (mse MockStorageError) ServiceCode() azblob.ServiceCodeType { - return "BlobNotFound" -} - -// Response returns nil -func (mse MockStorageError) Response() *http.Response { - return nil -} - -// Timeout returns nil -func (mse MockStorageError) Timeout() bool { - return false -} - -// Temporary returns nil -func (mse MockStorageError) Temporary() bool { - return false -} - -// Error returns empty string -func (mse MockStorageError) Error() string { - return "" -} +var blobNotFoundErr = &azcore.ResponseError{ErrorCode: string(bloberror.BlobNotFound)} diff --git a/backend/azure/mock_token.go b/backend/azure/mock_token.go index ec14f11c..8c7d43cd 100644 --- a/backend/azure/mock_token.go +++ b/backend/azure/mock_token.go @@ -1,15 +1,11 @@ package azure import ( - "time" - - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" ) -// MockTokenCredentialFactory knows how to create a "do-nothing" credential used for unit testing -type MockTokenCredentialFactory struct{} - -// New creates a new azblob.TokenCredential struct -func (f *MockTokenCredentialFactory) New(tenantID, clientID, clientSecret, azureEnvName string) (azblob.TokenCredential, error) { - return azblob.NewTokenCredential("aaa", func(credential azblob.TokenCredential) time.Duration { return time.Second * 1 }), nil +// MockTokenCredentialFactory creates a new azcore.TokenCredential struct +func MockTokenCredentialFactory(_, _, _ string) (azcore.TokenCredential, error) { + return &fake.TokenCredential{}, nil } diff --git a/backend/azure/options.go b/backend/azure/options.go index b8844538..84772874 100644 --- a/backend/azure/options.go +++ b/backend/azure/options.go @@ -5,7 +5,7 @@ import ( "github.com/c2fo/vfs/v6" - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" ) // Options contains options necessary for the azure vfs implementation @@ -30,10 +30,6 @@ type Options struct { // based authentication. ClientSecret string - // AzureEnvName holds the name for the Azure environment. This field is used for OAuth token - // based authentication. - AzureEnvName string - // RetryFunc holds the retry function RetryFunc vfs.Retry @@ -59,26 +55,25 @@ func NewOptions() *Options { TenantID: os.Getenv("VFS_AZURE_TENANT_ID"), ClientID: os.Getenv("VFS_AZURE_CLIENT_ID"), ClientSecret: os.Getenv("VFS_AZURE_CLIENT_SECRET"), - AzureEnvName: os.Getenv("VFS_AZURE_ENV_NAME"), - tokenCredentialFactory: &DefaultTokenCredentialFactory{}, + tokenCredentialFactory: DefaultTokenCredentialFactory, } } -// Credential returns an azblob.Credential struct based on how options are configured. Options are checked +// Credential returns a credential based on how options are configured. Options are checked // and evaluated in the following order: -// 1. If TenantID, ClientID, and ClientSecret are non-empty, return azblob.TokenCredential. This form of authentication +// 1. If TenantID, ClientID, and ClientSecret are non-empty, return azcore.TokenCredential. This form of authentication // is used with service accounts and can be used to access containers across multiple storage accounts. // 2. If AccountName, and AccountKey are non-empty, return azblob.SharedKeyCredential. This form or authentication // is used with storage accounts and only provides access to a single storage account. -// 3. Returns an anonymous credential. This allows access only to public blobs. -func (o *Options) Credential() (azblob.Credential, error) { +// 3. Returns a nil credential. This allows access only to public blobs. +func (o *Options) Credential() (any, error) { if o.tokenCredentialFactory == nil { - o.tokenCredentialFactory = &DefaultTokenCredentialFactory{} + o.tokenCredentialFactory = DefaultTokenCredentialFactory } // Check to see if we have service account credentials if o.TenantID != "" && o.ClientID != "" && o.ClientSecret != "" { - return o.tokenCredentialFactory.New(o.TenantID, o.ClientID, o.ClientSecret, o.AzureEnvName) + return o.tokenCredentialFactory(o.TenantID, o.ClientID, o.ClientSecret) } // Check to see if we have storage account credentials @@ -86,6 +81,6 @@ func (o *Options) Credential() (azblob.Credential, error) { return azblob.NewSharedKeyCredential(o.AccountName, o.AccountKey) } - // 3. Return an anonymous credential - return azblob.NewAnonymousCredential(), nil + // 3. Return a nil credential + return nil, nil } diff --git a/backend/azure/options_test.go b/backend/azure/options_test.go index d9950ba9..1e53d52c 100644 --- a/backend/azure/options_test.go +++ b/backend/azure/options_test.go @@ -5,7 +5,8 @@ import ( "os" "testing" - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" "github.com/stretchr/testify/suite" ) @@ -32,44 +33,37 @@ func (s *OptionsTestSuite) TestCredentials_ServiceAccount() { TenantID: "foo", ClientID: "foo", ClientSecret: "foo", - tokenCredentialFactory: &MockTokenCredentialFactory{}, + tokenCredentialFactory: MockTokenCredentialFactory, } credential, err := options.Credential() s.NoError(err, "service account vars are present so no error") s.NotNil(credential, "expect a non-nil credential when service account vars are set") - _, ok := credential.(azblob.TokenCredential) - s.True(ok, "credentials type should be TokenCredential") + s.Implements((*azcore.TokenCredential)(nil), credential, "credentials type should be TokenCredential") } func (s *OptionsTestSuite) TestCredentials_StorageAccount() { options := Options{ AccountName: "foo", AccountKey: base64.StdEncoding.EncodeToString([]byte("bar")), - tokenCredentialFactory: &MockTokenCredentialFactory{}, + tokenCredentialFactory: MockTokenCredentialFactory, } credential, err := options.Credential() s.NoError(err, "service account vars are present so no error") s.NotNil(credential, "expect a non-nil credential when service account vars are set") - _, ok := credential.(*azblob.SharedKeyCredential) - s.True(ok, "credential type should be SharedKeyCredential") + s.IsType((*azblob.SharedKeyCredential)(nil), credential, "credentials type should be SharedKeyCredential") } func (s *OptionsTestSuite) TestCredentials_Anon() { options := Options{ AccountName: "foo", - tokenCredentialFactory: &MockTokenCredentialFactory{}, + tokenCredentialFactory: MockTokenCredentialFactory, } credential, err := options.Credential() s.NoError(err, "anon vars are present so no error") - s.NotNil(credential, "expect a non-nil credential when service account vars are set") - s.NotNil(credential, "when no env vars are set we should get a non-nil credential") - _, ok := credential.(azblob.TokenCredential) - s.False(ok) - _, ok = credential.(*azblob.SharedKeyCredential) - s.False(ok) + s.Nil(credential, "when no env vars are set we should get a nil credential") } func TestOptions(t *testing.T) { diff --git a/backend/azure/properties.go b/backend/azure/properties.go index d2fe9c09..c161df39 100644 --- a/backend/azure/properties.go +++ b/backend/azure/properties.go @@ -3,27 +3,27 @@ package azure import ( "time" - "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob" ) // BlobProperties holds a subset of information returned by Blob.GetProperties(..) type BlobProperties struct { // Size holds the size of the blob. - Size uint64 + Size *int64 // LastModified holds the last modified time.Time LastModified *time.Time // Metadata holds the Azure metadata - Metadata map[string]string + Metadata map[string]*string } // NewBlobProperties creates a new BlobProperties from an azblob.BlobGetPropertiesResponse -func NewBlobProperties(azureProps *azblob.BlobGetPropertiesResponse) *BlobProperties { - lastModified := azureProps.LastModified() +func NewBlobProperties(azureProps blob.GetPropertiesResponse) *BlobProperties { + lastModified := azureProps.LastModified return &BlobProperties{ - LastModified: &lastModified, - Metadata: azureProps.NewMetadata(), - Size: uint64(azureProps.ContentLength()), + LastModified: lastModified, + Metadata: azureProps.Metadata, + Size: azureProps.ContentLength, } } diff --git a/backend/azure/token.go b/backend/azure/token.go index 5b567e49..e692b230 100644 --- a/backend/azure/token.go +++ b/backend/azure/token.go @@ -1,55 +1,17 @@ package azure import ( - "time" - - "github.com/Azure/azure-storage-blob-go/azblob" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" ) -// TokenCredentialFactory is an interface that provides a single factory method to create azure.TokenCredentials. This -// interface is provided to allow for mocking in unit tests. -type TokenCredentialFactory interface { - // New creates a new azblob.TokenCredential struct - New(tenantID, clientID, clientSecret, azureEnvName string) (azblob.TokenCredential, error) -} +// TokenCredentialFactory creates azure.TokenCredentials. This function is provided to allow for mocking in unit tests. +type TokenCredentialFactory func(tenantID, clientID, clientSecret string) (azcore.TokenCredential, error) // DefaultTokenCredentialFactory knows how to make azblob.TokenCredential structs for OAuth authentication -type DefaultTokenCredentialFactory struct{} - -// New creates a new azblob.TokenCredential struct -func (f *DefaultTokenCredentialFactory) New(tenantID, clientID, clientSecret, azureEnvName string) (azblob.TokenCredential, error) { - env, err := azure.EnvironmentFromName(azureEnvName) - if err != nil { - return nil, err - } - - oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID) - if err != nil { - return nil, err - } - - spt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ResourceIdentifiers.Storage) - if err != nil { - return nil, err - } - - initialToken := spt.Token() - - var tokenRefresher = func(credential azblob.TokenCredential) time.Duration { - err := spt.Refresh() - if err != nil { - // Not sure what else to do here except panic... - // The documentation suggests returning 0 so the token will never attempt to refresh again - // and then call cancel() on any context in the pipeline that have this credential object - panic(err) - } - - token := spt.Token() - credential.SetToken(token.AccessToken) - return token.Expires().Sub(time.Now().Add(2 * time.Minute)) +func DefaultTokenCredentialFactory(tenantID, clientID, clientSecret string) (azcore.TokenCredential, error) { + if clientID != "" || clientSecret != "" { + return azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil) } - - return azblob.NewTokenCredential(initialToken.AccessToken, tokenRefresher), nil + return azidentity.NewEnvironmentCredential(nil) } diff --git a/docs/azure.md b/docs/azure.md index 0c85906c..00656a44 100644 --- a/docs/azure.md +++ b/docs/azure.md @@ -68,7 +68,7 @@ found: to containers from multiple storage accounts. 1. The ENV vars `VFS_AZURE_STORAGE_ACCOUNT` and `VFS_AZURE_STORAGE_KEY`, a shared key authenticator is used. This will allow access to any containers owned by the designated storage account. -1. If none of the above are present, then an anonymous authenticator is created and only publicly accessible blobs +1. If none of the above are present, then no credentials are used and only publicly accessible blobs will be available ## Usage @@ -238,22 +238,15 @@ func (a *DefaultClient) Upload(file vfs.File, content io.ReadSeeker) error ``` Upload uploads a new file to Azure Blob Storage -### type DefaultTokenCredentialFactory +### func DefaultTokenCredentialFactory ```go -type DefaultTokenCredentialFactory struct{} +func DefaultTokenCredentialFactory(tenantID, clientID, clientSecret string) (azblob.TokenCredential, error) ``` -DefaultTokenCredentialFactory knows how to make azblob.TokenCredential structs +DefaultTokenCredentialFactory knows how to make azcore.TokenCredential structs for OAuth authentication -#### func (*DefaultTokenCredentialFactory) New - -```go -func (f *DefaultTokenCredentialFactory) New(tenantID, clientID, clientSecret, azureEnvName string) (azblob.TokenCredential, error) -``` -New creates a new azblob.TokenCredential struct - ### type File ```go @@ -664,67 +657,15 @@ func (a *MockAzureClient) Upload(file vfs.File, content io.ReadSeeker) error ``` Upload returns the value of ExpectedError -### type MockStorageError - -```go -type MockStorageError struct { - azblob.ResponseError -} -``` - -MockStorageError is a mock for the azblob.StorageError interface - -#### func (MockStorageError) Error +### func MockTokenCredentialFactory ```go -func (mse MockStorageError) Error() string -``` -Error returns empty string - -#### func (MockStorageError) Response - -```go -func (mse MockStorageError) Response() *http.Response -``` -Response returns nil - -#### func (MockStorageError) ServiceCode - -```go -func (mse MockStorageError) ServiceCode() azblob.ServiceCodeType -``` -ServiceCode always returns "BlobNotFound" to simulate the not found condition - -#### func (MockStorageError) Temporary - -```go -func (mse MockStorageError) Temporary() bool -``` -Temporary returns nil - -#### func (MockStorageError) Timeout - -```go -func (mse MockStorageError) Timeout() bool -``` -Timeout returns nil - -### type MockTokenCredentialFactory - -```go -type MockTokenCredentialFactory struct{} +func MockTokenCredentialFactory(_, _, _ string) (azblob.TokenCredential, error) ``` MockTokenCredentialFactory knows how to create a "do-nothing" credential used for unit testing -#### func (*MockTokenCredentialFactory) New - -```go -func (f *MockTokenCredentialFactory) New(tenantID, clientID, clientSecret, azureEnvName string) (azblob.TokenCredential, error) -``` -New creates a new azblob.TokenCredential struct - ### type Options ```go @@ -749,10 +690,6 @@ type Options struct { // based authentication. ClientSecret string - // AzureEnvName holds the name for the Azure environment. This field is used for OAuth token - // based authentication. - AzureEnvName string - // RetryFunc holds the retry function RetryFunc vfs.Retry } @@ -779,26 +716,22 @@ variables. #### func (*Options) Credential ```go -func (o *Options) Credential() (azblob.Credential, error) +func (o *Options) Credential() (azcore.TokenCredential, error) ``` -Credential returns an azblob.Credential struct based on how options are +Credential returns an azcore.TokenCredential interface based on how options are configured. Options are checked and evaluated in the following order: - 1. If TenantID, ClientID, and ClientSecret are non-empty, return azblob.TokenCredential. This form of authentication + 1. If TenantID, ClientID, and ClientSecret are non-empty, return azcore.TokenCredential. This form of authentication is used with service accounts and can be used to access containers across multiple storage accounts. 2. If AccountName, and AccountKey are non-empty, return azblob.SharedKeyCredential. This form or authentication is used with storage accounts and only provides access to a single storage account. - 3. Returns an anonymous credential. This allows access only to public blobs. + 3. Returns a nil credential. This allows access only to public blobs. -### type TokenCredentialFactory +### func TokenCredentialFactory ```go -type TokenCredentialFactory interface { - // New creates a new azblob.TokenCredential struct - New(tenantID, clientID, clientSecret, azureEnvName string) (azblob.TokenCredential, error) -} +type TokenCredentialFactory func(tenantID, clientID, clientSecret string) (azcore.TokenCredential, error) ``` -TokenCredentialFactory is an interface that provides a single factory method to -create azure.TokenCredentials. This interface is provided to allow for mocking +TokenCredentialFactory creates azure.TokenCredentials. This function is provided to allow for mocking in unit tests. diff --git a/go.mod b/go.mod index 52dcee95..e1d99e95 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,9 @@ toolchain go1.23.0 require ( cloud.google.com/go/storage v1.48.0 - github.com/Azure/azure-pipeline-go v0.2.3 - github.com/Azure/azure-storage-blob-go v0.15.0 - github.com/Azure/go-autorest/autorest v0.11.29 - github.com/Azure/go-autorest/autorest/adal v0.9.24 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 github.com/aws/aws-sdk-go v1.55.5 github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 github.com/fatih/color v1.18.0 @@ -32,10 +31,8 @@ require ( cloud.google.com/go/iam v1.3.0 // indirect cloud.google.com/go/monitoring v1.22.0 // indirect cloud.google.com/go/pubsub v1.45.3 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect @@ -50,8 +47,8 @@ require ( github.com/go-errors/errors v1.5.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/renameio/v2 v2.0.0 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect @@ -63,33 +60,33 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-ieproxy v0.0.12 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/xattr v0.4.10 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/stretchr/objx v0.5.2 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect - go.opentelemetry.io/otel v1.32.0 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect - go.opentelemetry.io/otel/sdk v1.32.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect - go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.30.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect - google.golang.org/genproto v0.0.0-20241206012308-a4fef0638583 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 // indirect - google.golang.org/grpc v1.68.1 // indirect - google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 // indirect + google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/grpc v1.67.2 // indirect + google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c9fb40c0..fad94aaa 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ cloud.google.com/go/kms v1.20.1 h1:og29Wv59uf2FVaZlesaiDAqHFzHaoUyHI3HYp9VUHVg= cloud.google.com/go/kms v1.20.1/go.mod h1:LywpNiVCvzYNJWS9JUcGJSVTNSwPwi0vBAotzDqn2nc= cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= -cloud.google.com/go/longrunning v0.6.3 h1:A2q2vuyXysRcwzqDpMMLSI6mb6o39miS52UEG/Rd2ng= -cloud.google.com/go/longrunning v0.6.3/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= +cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= cloud.google.com/go/monitoring v1.22.0 h1:mQ0040B7dpuRq1+4YiQD43M2vW9HgoVxY98xhqGT+YI= cloud.google.com/go/monitoring v1.22.0/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= cloud.google.com/go/pubsub v1.45.3 h1:prYj8EEAAAwkp6WNoGTE4ahe0DgHoyJd5Pbop931zow= @@ -25,27 +25,22 @@ cloud.google.com/go/storage v1.48.0 h1:FhBDHACbVtdPx7S/AbcKujPWiHvfO6F8OXGgCEbB2 cloud.google.com/go/storage v1.48.0/go.mod h1:aFoDYNMAjv67lp+xcuZqjUKv/ctmplzQ3wJgodA7b+M= cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= -github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= -github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk= -github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4= -github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 h1:cf+OIKbkmMHBaC3u78AXomweqM0oxQSgBXRZf3WH4yM= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1/go.mod h1:ap1dmS6vQKJxSMNiGJcq4QuUQkOynyD93gLw6MDF7ek= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= @@ -69,6 +64,8 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1Ig github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8= github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= @@ -98,7 +95,6 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsouza/fake-gcs-server v1.50.2 h1:ulrS1pavCOCbMZfN5ZPgBRMFWclON9xDsuLBniXtQoE= github.com/fsouza/fake-gcs-server v1.50.2/go.mod h1:VU6Zgei4647KuT4XER8WHv5Hcj2NIySndyG8gfvwckA= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -116,17 +112,15 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -154,7 +148,6 @@ github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxeh github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= @@ -178,24 +171,22 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y 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/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-ieproxy v0.0.12 h1:OZkUFJC3ESNZPQ+6LzC3VJIFSnreeFLQyqvBWtvfL2M= -github.com/mattn/go-ieproxy v0.0.12/go.mod h1:Vn+N61199DAnVeTgaF8eoB9PvLO8P3OBnG95ENh7B7c= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -205,8 +196,8 @@ github.com/minio/minio-go/v7 v7.0.78 h1:LqW2zy52fxnI4gg8C2oZviTaKHcBV36scS+RzJnx github.com/minio/minio-go/v7 v7.0.78/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= 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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM= github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY= github.com/pkg/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA= @@ -216,6 +207,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= @@ -228,7 +221,6 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -236,31 +228,27 @@ go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw= go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8= -go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= +go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= +go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/sdk/metric v1.30.0 h1:QJLT8Pe11jyHBHfSAgYH7kEmT24eX792jZO1bo4BXkM= +go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= @@ -276,15 +264,12 @@ 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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -304,12 +289,10 @@ golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -317,6 +300,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -331,9 +315,7 @@ golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -359,21 +341,21 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20241206012308-a4fef0638583 h1:pjPnE7Rv3PAwHISLRJhA3HQTnM2uu5qcnroxTkRb5G8= -google.golang.org/genproto v0.0.0-20241206012308-a4fef0638583/go.mod h1:dW27OyXi0Ph+N43jeCWMFC86aTT5VgdeQtOSf0Hehdw= -google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 h1:v+j+5gpj0FopU0KKLDGfDo9ZRRpKdi5UBrCP0f76kuY= -google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 h1:IfdSdTcLFy4lqUQrQJLkLt1PB+AsqVz6lwkWPzWEz10= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f h1:M65LEviCfuZTfrfzwwEoxVtgvfkFkBUbFnRbxCXuXhU= +google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 h1:hUfOButuEtpc0UvYiaYRbNwxVYr0mQQOWq6X8beJ9Gc= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3/go.mod h1:jzYlkSMbKypzuu6xoAEijsNVo9ZeDF1u/zCfFgsx7jg= +google.golang.org/grpc v1.67.2 h1:Lq11HW1nr5m4OYV+ZVy2BjOK78/zqnTx24vyDBP1JcQ= +google.golang.org/grpc v1.67.2/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=