diff --git a/backend/azure/client.go b/backend/azure/client.go index 8302374..9974fa3 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) 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{}, 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 8e4a628..c2d8792 100644 --- a/backend/azure/client_integration_test.go +++ b/backend/azure/client_integration_test.go @@ -6,19 +6,22 @@ package azure import ( "context" "fmt" - "net/url" + "io" "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 } @@ -31,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) } @@ -205,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 bb3a529..2a2e409 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 1c74a06..43fb082 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" @@ -120,7 +121,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 @@ -281,7 +282,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. @@ -316,8 +317,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 76db4f7..8ad571d 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" @@ -31,11 +32,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()) } @@ -104,7 +106,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") @@ -231,7 +233,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") @@ -270,7 +272,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") @@ -319,7 +321,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 55e1be8..f82b844 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" ) @@ -26,7 +26,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 } @@ -66,32 +66,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 ec14f11..8c7d43c 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 b884453..8477287 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 d9950ba..1e53d52 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 d2fe9c0..c161df3 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 5b567e4..e692b23 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 a021645..3fd9830 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 @@ -241,10 +241,10 @@ Upload uploads a new file to Azure Blob Storage ### type 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 @@ -664,52 +664,7 @@ 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 - -```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 +### func MockTokenCredentialFactory ```go type MockTokenCredentialFactory struct{} @@ -749,10 +704,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,24 +730,21 @@ 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 ```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 diff --git a/go.mod b/go.mod index 588ed9c..459cbce 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,9 @@ go 1.22.0 require ( cloud.google.com/go/storage v1.43.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.17.0 @@ -16,8 +15,8 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/sftp v1.13.6 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.26.0 - golang.org/x/net v0.28.0 + golang.org/x/crypto v0.27.0 + golang.org/x/net v0.29.0 google.golang.org/api v0.194.0 ) @@ -28,17 +27,15 @@ require ( cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.2.0 // indirect cloud.google.com/go/pubsub v1.42.0 // 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/davecgh/go-spew v1.1.1 // indirect github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect github.com/felixge/httpsnoop v1.0.4 // indirect 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.0 // 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 @@ -51,13 +48,12 @@ 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/kr/pretty v0.3.1 // 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/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/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect @@ -67,8 +63,8 @@ require ( go.opentelemetry.io/otel/trace v1.29.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/genproto v0.0.0-20240823204242-4ba0660f739c // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240823204242-4ba0660f739c // indirect diff --git a/go.sum b/go.sum index 01013ce..dc6af09 100644 --- a/go.sum +++ b/go.sum @@ -17,37 +17,35 @@ cloud.google.com/go/pubsub v1.42.0 h1:PVTbzorLryFL5ue8esTS2BfehUs0ahyNOY9qcd+HMO cloud.google.com/go/pubsub v1.42.0/go.mod h1:KADJ6s4MbTwhXmse/50SebEhE4SmUwHi48z3/dHar1Y= cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= -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/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 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= @@ -73,7 +71,6 @@ github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 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.49.3 h1:RPt94uYjWb+t19dlZg4PVRJFCvqf7px0YZDvIiUfjcU= github.com/fsouza/fake-gcs-server v1.49.3/go.mod h1:WsE7OZKNd5WXgiry01oJO6mDvljOr+YLPR3VQtM2sDY= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -91,9 +88,8 @@ 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 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/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= @@ -128,7 +124,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.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= @@ -152,24 +147,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.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 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= @@ -179,9 +172,8 @@ github.com/minio/minio-go/v7 v7.0.75 h1:0uLrB6u6teY2Jt+cJUVi9cTvDRuBKWSRzSAcznRk github.com/minio/minio-go/v7 v7.0.75/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8= 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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -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.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pkg/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA= @@ -189,7 +181,8 @@ github.com/pkg/xattr v0.4.10/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6k 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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +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.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= @@ -202,7 +195,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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -224,43 +216,32 @@ go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt3 go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= 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.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-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.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -268,18 +249,15 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-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= @@ -288,31 +266,20 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc 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= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -322,7 +289,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.194.0 h1:dztZKG9HgtIpbI35FhfuSNR/zmaMVdxNlntHj1sIS4s=