|
|
|
@ -14,6 +14,7 @@ import ( |
|
|
|
|
|
|
|
|
|
|
|
"github.com/Azure/azure-pipeline-go/pipeline" |
|
|
|
"github.com/Azure/azure-pipeline-go/pipeline" |
|
|
|
"github.com/Azure/azure-storage-blob-go/azblob" |
|
|
|
"github.com/Azure/azure-storage-blob-go/azblob" |
|
|
|
|
|
|
|
"github.com/Azure/go-autorest/autorest/adal" |
|
|
|
"github.com/mattn/go-ieproxy" |
|
|
|
"github.com/mattn/go-ieproxy" |
|
|
|
"github.com/prometheus/client_golang/prometheus" |
|
|
|
"github.com/prometheus/client_golang/prometheus" |
|
|
|
|
|
|
|
|
|
|
|
@ -93,6 +94,7 @@ type BlobStorageConfig struct { |
|
|
|
MaxRetries int `yaml:"max_retries"` |
|
|
|
MaxRetries int `yaml:"max_retries"` |
|
|
|
MinRetryDelay time.Duration `yaml:"min_retry_delay"` |
|
|
|
MinRetryDelay time.Duration `yaml:"min_retry_delay"` |
|
|
|
MaxRetryDelay time.Duration `yaml:"max_retry_delay"` |
|
|
|
MaxRetryDelay time.Duration `yaml:"max_retry_delay"` |
|
|
|
|
|
|
|
UseManagedIdentity bool `yaml:"use_managed_identity"` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// RegisterFlags adds the flags required to config this to the given FlagSet
|
|
|
|
// RegisterFlags adds the flags required to config this to the given FlagSet
|
|
|
|
@ -113,6 +115,7 @@ func (c *BlobStorageConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagS |
|
|
|
f.IntVar(&c.MaxRetries, prefix+"azure.max-retries", 5, "Number of retries for a request which times out.") |
|
|
|
f.IntVar(&c.MaxRetries, prefix+"azure.max-retries", 5, "Number of retries for a request which times out.") |
|
|
|
f.DurationVar(&c.MinRetryDelay, prefix+"azure.min-retry-delay", 10*time.Millisecond, "Minimum time to wait before retrying a request.") |
|
|
|
f.DurationVar(&c.MinRetryDelay, prefix+"azure.min-retry-delay", 10*time.Millisecond, "Minimum time to wait before retrying a request.") |
|
|
|
f.DurationVar(&c.MaxRetryDelay, prefix+"azure.max-retry-delay", 500*time.Millisecond, "Maximum time to wait before retrying a request.") |
|
|
|
f.DurationVar(&c.MaxRetryDelay, prefix+"azure.max-retry-delay", 500*time.Millisecond, "Maximum time to wait before retrying a request.") |
|
|
|
|
|
|
|
f.BoolVar(&c.UseManagedIdentity, prefix+"azure.use-managed-identity", false, "Use Managed Identity or not.") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (c *BlobStorageConfig) ToCortexAzureConfig() cortex_azure.BlobStorageConfig { |
|
|
|
func (c *BlobStorageConfig) ToCortexAzureConfig() cortex_azure.BlobStorageConfig { |
|
|
|
@ -241,11 +244,7 @@ func (b *BlobStorage) buildContainerURL() (azblob.ContainerURL, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (b *BlobStorage) newPipeline(hedgingCfg hedging.Config, hedging bool) (pipeline.Pipeline, error) { |
|
|
|
func (b *BlobStorage) newPipeline(hedgingCfg hedging.Config, hedging bool) (pipeline.Pipeline, error) { |
|
|
|
credential, err := azblob.NewSharedKeyCredential(b.cfg.AccountName, b.cfg.AccountKey.Value) |
|
|
|
// defining the Azure Pipeline Options
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
opts := azblob.PipelineOptions{ |
|
|
|
opts := azblob.PipelineOptions{ |
|
|
|
Retry: azblob.RetryOptions{ |
|
|
|
Retry: azblob.RetryOptions{ |
|
|
|
Policy: azblob.RetryPolicyExponential, |
|
|
|
Policy: azblob.RetryPolicyExponential, |
|
|
|
@ -255,6 +254,12 @@ func (b *BlobStorage) newPipeline(hedgingCfg hedging.Config, hedging bool) (pipe |
|
|
|
MaxRetryDelay: b.cfg.MaxRetryDelay, |
|
|
|
MaxRetryDelay: b.cfg.MaxRetryDelay, |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
credential, err := azblob.NewSharedKeyCredential(b.cfg.AccountName, b.cfg.AccountKey.Value) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
client := defaultClientFactory() |
|
|
|
client := defaultClientFactory() |
|
|
|
|
|
|
|
|
|
|
|
opts.HTTPSender = pipeline.FactoryFunc(func(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.PolicyFunc { |
|
|
|
opts.HTTPSender = pipeline.FactoryFunc(func(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.PolicyFunc { |
|
|
|
@ -277,7 +282,61 @@ func (b *BlobStorage) newPipeline(hedgingCfg hedging.Config, hedging bool) (pipe |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return azblob.NewPipeline(credential, opts), nil |
|
|
|
if !b.cfg.UseManagedIdentity { |
|
|
|
|
|
|
|
return azblob.NewPipeline(credential, opts), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenCredential, err := b.getOAuthToken() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return azblob.NewPipeline(*tokenCredential, opts), nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (b *BlobStorage) getOAuthToken() (*azblob.TokenCredential, error) { |
|
|
|
|
|
|
|
spt, err := b.fetchMSIToken() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Refresh obtains a fresh token
|
|
|
|
|
|
|
|
err = spt.Refresh() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tc := azblob.NewTokenCredential(spt.Token().AccessToken, func(tc azblob.TokenCredential) time.Duration { |
|
|
|
|
|
|
|
err := spt.Refresh() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
// something went wrong, prevent the refresher from being triggered again
|
|
|
|
|
|
|
|
return 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set the new token value
|
|
|
|
|
|
|
|
tc.SetToken(spt.Token().AccessToken) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get the next token slightly before the current one expires
|
|
|
|
|
|
|
|
return time.Until(spt.Token().Expires()) - 10*time.Second |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return &tc, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (b *BlobStorage) fetchMSIToken() (*adal.ServicePrincipalToken, error) { |
|
|
|
|
|
|
|
// msiEndpoint is the well known endpoint for getting MSI authentications tokens
|
|
|
|
|
|
|
|
// msiEndpoint := "http://169.254.169.254/metadata/identity/oauth2/token" for production Jobs
|
|
|
|
|
|
|
|
msiEndpoint, _ := adal.GetMSIVMEndpoint() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// both can be empty, systemAssignedMSI scenario
|
|
|
|
|
|
|
|
spt, err := adal.NewServicePrincipalTokenFromMSI(msiEndpoint, "https://storage.azure.com/") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return spt, spt.Refresh() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// List implements chunk.ObjectClient.
|
|
|
|
// List implements chunk.ObjectClient.
|
|
|
|
|