Skip to content

Commit 9355a82

Browse files
Refactoring of azure obj store #3957 (#3970)
* Rebased feature - initial commit of azure obj store extend Signed-off-by: Wiard van Rij <[email protected]> * use custom cortex to fix config change Signed-off-by: Wiard van Rij <[email protected]> * modules acting up Signed-off-by: Wiard van Rij <[email protected]> * remove sprint Signed-off-by: Wiard van Rij <[email protected]> * adds dot Signed-off-by: Wiard van Rij <[email protected]> * removed need for changes on cortex side Signed-off-by: Wiard van Rij <[email protected]> * adds changelog Signed-off-by: Wiard van Rij <[email protected]> * Update docs/storage.md Co-authored-by: Philip Laine <[email protected]> Signed-off-by: Wiard van Rij <[email protected]> * Update pkg/objstore/azure/azure_test.go Co-authored-by: Philip Laine <[email protected]> Signed-off-by: Wiard van Rij <[email protected]> * Update pkg/objstore/azure/azure_test.go Co-authored-by: Philip Laine <[email protected]> Signed-off-by: Wiard van Rij <[email protected]> * update a few cleanups Signed-off-by: Wiard van Rij <[email protected]> * fixes go.mod and go.sum? Signed-off-by: Wiard van Rij <[email protected]> * fixes whitespace Signed-off-by: Wiard van Rij <[email protected]> * fixes space Signed-off-by: Wiard van Rij <[email protected]> * updates azure Go-autorest Signed-off-by: Wiard van Rij <[email protected]> * fixes readme Signed-off-by: Wiard van Rij <[email protected]> * fixes go.sum.. (╯°□°)╯︵ ┻━┻ Signed-off-by: Wiard van Rij <[email protected]> * e2e retest Signed-off-by: Wiard van Rij <[email protected]> Co-authored-by: Philip Laine <[email protected]>
1 parent 897f35f commit 9355a82

File tree

7 files changed

+389
-113
lines changed

7 files changed

+389
-113
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re
1919
- [#4239](https://github.com/thanos-io/thanos/pull/4239) Add penalty based deduplication mode for compactor.
2020
- [#4292](https://github.com/thanos-io/thanos/pull/4292) Receive: Enable exemplars ingestion and querying.
2121
- [#4392](https://github.com/thanos-io/thanos/pull/4392) Tools: Added `--delete-blocks` to bucket rewrite tool to mark the original blocks for deletion after rewriting is done.
22+
- [#3970](https://github.com/thanos-io/thanos/pull/3970) Azure: Adds more configuration options for Azure blob storage. This allows for pipeline and reader specific configuration. Implements HTTP transport configuration options. These options allows for more fine-grained control on timeouts and retries. Implements MSI authentication as second method of authentication via a service principal token.
2223

2324
### Fixed
2425

docs/storage.md

+20
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,28 @@ config:
319319
container: ""
320320
endpoint: ""
321321
max_retries: 0
322+
msi_resource: ""
323+
pipeline_config:
324+
max_tries: 0
325+
try_timeout: 0s
326+
retry_delay: 0s
327+
max_retry_delay: 0s
328+
reader_config:
329+
max_retry_requests: 0
330+
http_config:
331+
idle_conn_timeout: 0s
332+
response_header_timeout: 0s
333+
insecure_skip_verify: false
334+
tls_handshake_timeout: 0s
335+
expect_continue_timeout: 0s
336+
max_idle_conns: 0
337+
max_idle_conns_per_host: 0
338+
max_conns_per_host: 0
339+
disable_compression: false
322340
```
323341

342+
If `msi_resource` is used, authentication is done via ServicePrincipalToken. The value for Azure should be `https://<storage-account-name>.blob.core.windows.net`. The generic `max_retries` will be used as value for the `pipeline_config`'s `max_tries` and `reader_config`'s `max_retry_requests`. For more control, `max_retries` could be ignored (0) and one could set specific retry values.
343+
324344
#### OpenStack Swift
325345

326346
Thanos uses [ncw/swift](https://github.com/ncw/swift) client to upload Prometheus data into [OpenStack Swift](https://docs.openstack.org/swift/latest/).

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ require (
55
cloud.google.com/go/storage v1.10.0
66
github.com/Azure/azure-pipeline-go v0.2.2
77
github.com/Azure/azure-storage-blob-go v0.8.0
8+
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8
89
github.com/NYTimes/gziphandler v1.1.1
910
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15
1011
github.com/aliyun/aliyun-oss-go-sdk v2.0.4+incompatible
@@ -63,7 +64,7 @@ require (
6364
go.uber.org/atomic v1.7.0
6465
go.uber.org/automaxprocs v1.2.0
6566
go.uber.org/goleak v1.1.10
66-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
67+
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
6768
golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c
6869
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
6970
golang.org/x/text v0.3.6

go.sum

+11-1
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,19 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
6161
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
6262
github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
6363
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
64+
github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
6465
github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=
6566
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
6667
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
6768
github.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
6869
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
70+
github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk=
6971
github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
7072
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
73+
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg=
74+
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4=
75+
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY=
76+
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
7177
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
7278
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
7379
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
@@ -295,6 +301,9 @@ github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8
295301
github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc=
296302
github.com/digitalocean/godo v1.60.0 h1:o/vimtn/HKtYSakFAAZ59Zc5ASORd41S4z1X7pAXPn8=
297303
github.com/digitalocean/godo v1.60.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
304+
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
305+
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
306+
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
298307
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
299308
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
300309
github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
@@ -1401,8 +1410,9 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
14011410
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
14021411
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
14031412
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
1404-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
14051413
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
1414+
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
1415+
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
14061416
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
14071417
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
14081418
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=

pkg/objstore/azure/azure.go

+116-20
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import (
1111
"os"
1212
"strings"
1313
"testing"
14+
"time"
1415

1516
blob "github.com/Azure/azure-storage-blob-go/azblob"
1617
"github.com/go-kit/kit/log"
1718
"github.com/go-kit/kit/log/level"
1819
"github.com/pkg/errors"
20+
"github.com/prometheus/common/model"
1921
"github.com/thanos-io/thanos/pkg/objstore"
2022
yaml "gopkg.in/yaml.v2"
2123
)
@@ -24,13 +26,64 @@ const (
2426
azureDefaultEndpoint = "blob.core.windows.net"
2527
)
2628

29+
// Set default retry values to default Azure values. 0 = use Default Azure.
30+
var DefaultConfig = Config{
31+
PipelineConfig: PipelineConfig{
32+
MaxTries: 0,
33+
TryTimeout: 0,
34+
RetryDelay: 0,
35+
MaxRetryDelay: 0,
36+
},
37+
ReaderConfig: ReaderConfig{
38+
MaxRetryRequests: 0,
39+
},
40+
HTTPConfig: HTTPConfig{
41+
IdleConnTimeout: model.Duration(90 * time.Second),
42+
ResponseHeaderTimeout: model.Duration(2 * time.Minute),
43+
TLSHandshakeTimeout: model.Duration(10 * time.Second),
44+
ExpectContinueTimeout: model.Duration(1 * time.Second),
45+
MaxIdleConns: 100,
46+
MaxIdleConnsPerHost: 100,
47+
MaxConnsPerHost: 0,
48+
DisableCompression: false,
49+
},
50+
}
51+
2752
// Config Azure storage configuration.
2853
type Config struct {
29-
StorageAccountName string `yaml:"storage_account"`
30-
StorageAccountKey string `yaml:"storage_account_key"`
31-
ContainerName string `yaml:"container"`
32-
Endpoint string `yaml:"endpoint"`
33-
MaxRetries int `yaml:"max_retries"`
54+
StorageAccountName string `yaml:"storage_account"`
55+
StorageAccountKey string `yaml:"storage_account_key"`
56+
ContainerName string `yaml:"container"`
57+
Endpoint string `yaml:"endpoint"`
58+
MaxRetries int `yaml:"max_retries"`
59+
MSIResource string `yaml:"msi_resource"`
60+
PipelineConfig PipelineConfig `yaml:"pipeline_config"`
61+
ReaderConfig ReaderConfig `yaml:"reader_config"`
62+
HTTPConfig HTTPConfig `yaml:"http_config"`
63+
}
64+
65+
type ReaderConfig struct {
66+
MaxRetryRequests int `yaml:"max_retry_requests"`
67+
}
68+
69+
type PipelineConfig struct {
70+
MaxTries int32 `yaml:"max_tries"`
71+
TryTimeout model.Duration `yaml:"try_timeout"`
72+
RetryDelay model.Duration `yaml:"retry_delay"`
73+
MaxRetryDelay model.Duration `yaml:"max_retry_delay"`
74+
}
75+
76+
type HTTPConfig struct {
77+
IdleConnTimeout model.Duration `yaml:"idle_conn_timeout"`
78+
ResponseHeaderTimeout model.Duration `yaml:"response_header_timeout"`
79+
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
80+
81+
TLSHandshakeTimeout model.Duration `yaml:"tls_handshake_timeout"`
82+
ExpectContinueTimeout model.Duration `yaml:"expect_continue_timeout"`
83+
MaxIdleConns int `yaml:"max_idle_conns"`
84+
MaxIdleConnsPerHost int `yaml:"max_idle_conns_per_host"`
85+
MaxConnsPerHost int `yaml:"max_conns_per_host"`
86+
DisableCompression bool `yaml:"disable_compression"`
3487
}
3588

3689
// Bucket implements the store.Bucket interface against Azure APIs.
@@ -42,34 +95,77 @@ type Bucket struct {
4295

4396
// Validate checks to see if any of the config options are set.
4497
func (conf *Config) validate() error {
45-
if conf.StorageAccountName == "" ||
46-
conf.StorageAccountKey == "" {
47-
return errors.New("invalid Azure storage configuration")
48-
}
49-
if conf.StorageAccountName == "" && conf.StorageAccountKey != "" {
50-
return errors.New("no Azure storage_account specified while storage_account_key is present in config file; both should be present")
51-
}
52-
if conf.StorageAccountName != "" && conf.StorageAccountKey == "" {
53-
return errors.New("no Azure storage_account_key specified while storage_account is present in config file; both should be present")
98+
99+
var errMsg []string
100+
if conf.MSIResource == "" {
101+
if conf.StorageAccountName == "" ||
102+
conf.StorageAccountKey == "" {
103+
errMsg = append(errMsg, "invalid Azure storage configuration")
104+
}
105+
if conf.StorageAccountName == "" && conf.StorageAccountKey != "" {
106+
errMsg = append(errMsg, "no Azure storage_account specified while storage_account_key is present in config file; both should be present")
107+
}
108+
if conf.StorageAccountName != "" && conf.StorageAccountKey == "" {
109+
errMsg = append(errMsg, "no Azure storage_account_key specified while storage_account is present in config file; both should be present")
110+
}
111+
} else {
112+
if conf.StorageAccountName == "" {
113+
errMsg = append(errMsg, "MSI resource is configured but storage account name is missing")
114+
}
115+
if conf.StorageAccountKey != "" {
116+
errMsg = append(errMsg, "MSI resource is configured but storage account key is used")
117+
}
54118
}
119+
55120
if conf.ContainerName == "" {
56-
return errors.New("no Azure container specified")
121+
errMsg = append(errMsg, "no Azure container specified")
57122
}
58123
if conf.Endpoint == "" {
59124
conf.Endpoint = azureDefaultEndpoint
60125
}
61-
if conf.MaxRetries < 0 {
62-
return errors.New("the value of maxretries must be greater than or equal to 0 in the config file")
126+
127+
if conf.PipelineConfig.MaxTries < 0 {
128+
errMsg = append(errMsg, "The value of max_tries must be greater than or equal to 0 in the config file")
129+
}
130+
131+
if conf.ReaderConfig.MaxRetryRequests < 0 {
132+
errMsg = append(errMsg, "The value of max_retry_requests must be greater than or equal to 0 in the config file")
133+
}
134+
135+
if len(errMsg) > 0 {
136+
return errors.New(strings.Join(errMsg, ", "))
63137
}
138+
64139
return nil
65140
}
66141

142+
// parseConfig unmarshals a buffer into a Config with default values.
143+
func parseConfig(conf []byte) (Config, error) {
144+
config := DefaultConfig
145+
if err := yaml.UnmarshalStrict(conf, &config); err != nil {
146+
return Config{}, err
147+
}
148+
149+
// If we don't have config specific retry values but we do have the generic MaxRetries.
150+
// This is for backwards compatibility but also ease of configuration.
151+
if config.MaxRetries > 0 {
152+
if config.PipelineConfig.MaxTries == 0 {
153+
config.PipelineConfig.MaxTries = int32(config.MaxRetries)
154+
}
155+
if config.ReaderConfig.MaxRetryRequests == 0 {
156+
config.ReaderConfig.MaxRetryRequests = config.MaxRetries
157+
}
158+
}
159+
160+
return config, nil
161+
}
162+
67163
// NewBucket returns a new Bucket using the provided Azure config.
68164
func NewBucket(logger log.Logger, azureConfig []byte, component string) (*Bucket, error) {
69165
level.Debug(logger).Log("msg", "creating new Azure bucket connection", "component", component)
70166

71-
var conf Config
72-
if err := yaml.Unmarshal(azureConfig, &conf); err != nil {
167+
conf, err := parseConfig(azureConfig)
168+
if err != nil {
73169
return nil, err
74170
}
75171

@@ -227,7 +323,7 @@ func (b *Bucket) getBlobReader(ctx context.Context, name string, offset, length
227323
Parallelism: uint16(3),
228324
Progress: nil,
229325
RetryReaderOptionsPerBlock: blob.RetryReaderOptions{
230-
MaxRetryRequests: b.config.MaxRetries,
326+
MaxRetryRequests: b.config.ReaderConfig.MaxRetryRequests,
231327
},
232328
},
233329
); err != nil {

0 commit comments

Comments
 (0)