Skip to content

Commit 8429b0f

Browse files
NotedopJSticklerashwanthgoli
authored
feat: add S3 chunk delimiter config to support MinIO running on Windows (#16319)
Signed-off-by: Notedop <raoul2405@hotmail.com> Co-authored-by: J Stickler <julie.stickler@grafana.com> Co-authored-by: Ashwanth <iamashwanth@gmail.com>
1 parent 62f826e commit 8429b0f

File tree

3 files changed

+29
-6
lines changed

3 files changed

+29
-6
lines changed

‎docs/sources/shared/configuration.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5111,6 +5111,12 @@ The `s3_storage_config` block configures the connection to Amazon S3 object stor
51115111
# CLI flag: -<prefix>.s3.insecure
51125112
[insecure: <boolean> | default = false]
51135113

5114+
# Delimiter used to replace the default delimiter ':' in chunk IDs when storing
5115+
# chunks. This is mainly intended when you run a MinIO instance on a Windows
5116+
# machine. You should not change this value inflight.
5117+
# CLI flag: -<prefix>.s3.chunk-delimiter
5118+
[chunk_delimiter: <string> | default = ""]
5119+
51145120
http_config:
51155121
# Timeout specifies a time limit for requests made by s3 Client.
51165122
# CLI flag: -<prefix>.s3.http.timeout

‎pkg/loki/config_wrapper_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ memberlist:
274274
secret_access_key: def789
275275
insecure: true
276276
disable_dualstack: true
277+
chunk_delimiter: "-"
277278
http_config:
278279
response_header_timeout: 5m`
279280

@@ -299,6 +300,7 @@ memberlist:
299300
assert.Equal(t, "", actual.SessionToken.String())
300301
assert.Equal(t, true, actual.Insecure)
301302
assert.True(t, actual.DisableDualstack)
303+
assert.Equal(t, "-", actual.ChunkDelimiter)
302304
assert.Equal(t, 5*time.Minute, actual.HTTPConfig.ResponseHeaderTimeout)
303305
assert.Equal(t, false, actual.HTTPConfig.InsecureSkipVerify)
304306

@@ -364,6 +366,7 @@ memberlist:
364366
assert.Equal(t, "456abc", actual.SessionToken.String())
365367
assert.Equal(t, true, actual.Insecure)
366368
assert.False(t, actual.DisableDualstack)
369+
assert.Equal(t, "", actual.ChunkDelimiter)
367370
assert.Equal(t, 5*time.Minute, actual.HTTPConfig.ResponseHeaderTimeout)
368371
assert.Equal(t, false, actual.HTTPConfig.InsecureSkipVerify)
369372

‎pkg/storage/chunk/client/aws/s3_storage_client.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ type S3Config struct {
7676
SecretAccessKey flagext.Secret `yaml:"secret_access_key"`
7777
SessionToken flagext.Secret `yaml:"session_token"`
7878
Insecure bool `yaml:"insecure"`
79+
ChunkDelimiter string `yaml:"chunk_delimiter"`
7980
HTTPConfig HTTPConfig `yaml:"http_config"`
8081
SignatureVersion string `yaml:"signature_version"`
8182
StorageClass string `yaml:"storage_class"`
@@ -113,6 +114,7 @@ func (cfg *S3Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
113114
f.Var(&cfg.SecretAccessKey, prefix+"s3.secret-access-key", "AWS Secret Access Key")
114115
f.Var(&cfg.SessionToken, prefix+"s3.session-token", "AWS Session Token")
115116
f.BoolVar(&cfg.Insecure, prefix+"s3.insecure", false, "Disable https on s3 connection.")
117+
f.StringVar(&cfg.ChunkDelimiter, prefix+"s3.chunk-delimiter", "", "Delimiter used to replace the default delimiter ':' in chunk IDs when storing chunks. This is mainly intended when you run a MinIO instance on a Windows machine. You should not change this value inflight.")
116118
f.BoolVar(&cfg.DisableDualstack, prefix+"s3.disable-dualstack", false, "Disable forcing S3 dualstack endpoint usage.")
117119

118120
cfg.SSEConfig.RegisterFlagsWithPrefix(prefix+"s3.sse.", f)
@@ -335,7 +337,7 @@ func (a *S3ObjectClient) objectAttributes(ctx context.Context, objectKey, method
335337
lastErr = instrument.CollectedRequest(ctx, method, s3RequestDuration, instrument.ErrorCode, func(_ context.Context) error {
336338
headObjectInput := &s3.HeadObjectInput{
337339
Bucket: aws.String(a.bucketFromKey(objectKey)),
338-
Key: aws.String(objectKey),
340+
Key: aws.String(a.convertObjectKey(objectKey, true)),
339341
}
340342
headOutput, requestErr := a.S3.HeadObject(headObjectInput)
341343
if requestErr != nil {
@@ -365,7 +367,7 @@ func (a *S3ObjectClient) DeleteObject(ctx context.Context, objectKey string) err
365367
return instrument.CollectedRequest(ctx, "S3.DeleteObject", s3RequestDuration, instrument.ErrorCode, func(ctx context.Context) error {
366368
deleteObjectInput := &s3.DeleteObjectInput{
367369
Bucket: aws.String(a.bucketFromKey(objectKey)),
368-
Key: aws.String(objectKey),
370+
Key: aws.String(a.convertObjectKey(objectKey, true)),
369371
}
370372

371373
_, err := a.S3.DeleteObjectWithContext(ctx, deleteObjectInput)
@@ -405,7 +407,7 @@ func (a *S3ObjectClient) GetObject(ctx context.Context, objectKey string) (io.Re
405407
var requestErr error
406408
resp, requestErr = a.hedgedS3.GetObjectWithContext(ctx, &s3.GetObjectInput{
407409
Bucket: aws.String(bucket),
408-
Key: aws.String(objectKey),
410+
Key: aws.String(a.convertObjectKey(objectKey, true)),
409411
})
410412
return requestErr
411413
})
@@ -442,7 +444,7 @@ func (a *S3ObjectClient) GetObjectRange(ctx context.Context, objectKey string, o
442444
var requestErr error
443445
resp, requestErr = a.hedgedS3.GetObjectWithContext(ctx, &s3.GetObjectInput{
444446
Bucket: aws.String(bucket),
445-
Key: aws.String(objectKey),
447+
Key: aws.String(a.convertObjectKey(objectKey, true)),
446448
Range: aws.String(fmt.Sprintf("bytes=%d-%d", offset, offset+length-1)),
447449
})
448450
return requestErr
@@ -467,7 +469,7 @@ func (a *S3ObjectClient) PutObject(ctx context.Context, objectKey string, object
467469
putObjectInput := &s3.PutObjectInput{
468470
Body: readSeeker,
469471
Bucket: aws.String(a.bucketFromKey(objectKey)),
470-
Key: aws.String(objectKey),
472+
Key: aws.String(a.convertObjectKey(objectKey, true)),
471473
StorageClass: aws.String(a.cfg.StorageClass),
472474
}
473475

@@ -504,7 +506,7 @@ func (a *S3ObjectClient) List(ctx context.Context, prefix, delimiter string) ([]
504506

505507
for _, content := range output.Contents {
506508
storageObjects = append(storageObjects, client.StorageObject{
507-
Key: *content.Key,
509+
Key: a.convertObjectKey(*content.Key, false),
508510
ModifiedAt: *content.LastModified,
509511
})
510512
}
@@ -617,3 +619,15 @@ func IsRetryableErr(err error) bool {
617619
func (a *S3ObjectClient) IsRetryableErr(err error) bool {
618620
return IsRetryableErr(err)
619621
}
622+
623+
// convertObjectKey modifies the object key based on a delimiter and a mode flag determining conversion.
624+
func (a *S3ObjectClient) convertObjectKey(objectKey string, toS3 bool) string {
625+
if len(a.cfg.ChunkDelimiter) == 1 {
626+
if toS3 {
627+
objectKey = strings.ReplaceAll(objectKey, ":", a.cfg.ChunkDelimiter)
628+
} else {
629+
objectKey = strings.ReplaceAll(objectKey, a.cfg.ChunkDelimiter, ":")
630+
}
631+
}
632+
return objectKey
633+
}

0 commit comments

Comments
 (0)