From 5cc7486fcb749c4867a7d3f4843a3999ae05ae35 Mon Sep 17 00:00:00 2001 From: Periklis Tsirakidis Date: Mon, 11 Dec 2023 20:08:48 +0100 Subject: [PATCH] operator: Fix storing authentication credentials in the Loki ConfigMap (#11357) Co-authored-by: Robert Jacob Co-authored-by: Robert Jacob --- operator/CHANGELOG.md | 4 + operator/cmd/loki-broker/main.go | 10 - .../handlers/internal/storage/secrets.go | 147 +++-- .../handlers/internal/storage/secrets_test.go | 80 ++- operator/internal/manifests/compactor.go | 2 +- operator/internal/manifests/compactor_test.go | 35 +- operator/internal/manifests/config.go | 6 +- operator/internal/manifests/distributor.go | 2 +- .../internal/manifests/distributor_test.go | 32 +- operator/internal/manifests/gateway.go | 2 +- operator/internal/manifests/gateway_test.go | 48 +- operator/internal/manifests/indexgateway.go | 2 +- .../internal/manifests/indexgateway_test.go | 34 +- operator/internal/manifests/ingester.go | 2 +- operator/internal/manifests/ingester_test.go | 34 +- .../manifests/internal/config/build_test.go | 140 ++--- .../internal/config/loki-config.yaml | 12 +- operator/internal/manifests/querier.go | 2 +- operator/internal/manifests/querier_test.go | 33 +- operator/internal/manifests/query-frontend.go | 2 +- .../internal/manifests/query-frontend_test.go | 33 +- operator/internal/manifests/ruler.go | 2 +- operator/internal/manifests/ruler_test.go | 34 +- .../internal/manifests/storage/configure.go | 150 ++++- .../manifests/storage/configure_test.go | 591 +++++++++++++++++- .../internal/manifests/storage/options.go | 17 +- .../internal/manifests/storage/schema_test.go | 4 +- operator/internal/manifests/storage/var.go | 99 +++ operator/internal/manifests/var.go | 15 +- 29 files changed, 1276 insertions(+), 298 deletions(-) create mode 100644 operator/internal/manifests/storage/var.go diff --git a/operator/CHANGELOG.md b/operator/CHANGELOG.md index 146f68a988132..409138a548cac 100644 --- a/operator/CHANGELOG.md +++ b/operator/CHANGELOG.md @@ -1,5 +1,9 @@ ## Main +## Release 5.7.10 + +- [11357](https://github.com/grafana/loki/pull/11357) **periklis**: Fix storing authentication credentials in the Loki ConfigMap + ## Release 5.7.9 - [11393](https://github.com/grafana/loki/pull/11393) **periklis**: Add infra annotations for OpenShift based deployments diff --git a/operator/cmd/loki-broker/main.go b/operator/cmd/loki-broker/main.go index 091221ce6655d..9ef8b3819ac5d 100644 --- a/operator/cmd/loki-broker/main.go +++ b/operator/cmd/loki-broker/main.go @@ -57,8 +57,6 @@ func (c *config) registerFlags(f *flag.FlagSet) { f.StringVar(&c.objectStorage.S3.Endpoint, "object-storage.s3.endpoint", "", "The S3 endpoint location.") f.StringVar(&c.objectStorage.S3.Buckets, "object-storage.s3.buckets", "", "A comma-separated list of S3 buckets.") f.StringVar(&c.objectStorage.S3.Region, "object-storage.s3.region", "", "An S3 region.") - f.StringVar(&c.objectStorage.S3.AccessKeyID, "object-storage.s3.access-key-id", "", "The access key id for S3.") - f.StringVar(&c.objectStorage.S3.AccessKeySecret, "object-storage.s3.access-key-secret", "", "The access key secret for S3.") // Input and output file/dir options f.StringVar(&c.crFilepath, "custom-resource.path", "", "Path to a custom resource YAML file.") f.StringVar(&c.writeToDir, "output.write-dir", "", "write each file to the specified directory.") @@ -88,14 +86,6 @@ func (c *config) validateFlags(log logr.Logger) { log.Info("-object-storage.s3.buckets flag is required") os.Exit(1) } - if cfg.objectStorage.S3.AccessKeyID == "" { - log.Info("-object-storage.s3.access.key.id flag is required") - os.Exit(1) - } - if cfg.objectStorage.S3.AccessKeySecret == "" { - log.Info("-object-storage.s3.access.key.secret flag is required") - os.Exit(1) - } // Validate feature flags if cfg.featureFlags.LokiStackAlerts && !cfg.featureFlags.ServiceMonitors { log.Info("-with-prometheus-alerts flag requires -with-service-monitors") diff --git a/operator/internal/handlers/internal/storage/secrets.go b/operator/internal/handlers/internal/storage/secrets.go index d7603123fc5a8..93303af604264 100644 --- a/operator/internal/handlers/internal/storage/secrets.go +++ b/operator/internal/handlers/internal/storage/secrets.go @@ -1,6 +1,10 @@ package storage import ( + "crypto/sha1" + "fmt" + "sort" + "github.com/ViaQ/logerr/v2/kverrors" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" "github.com/grafana/loki/operator/internal/manifests/storage" @@ -8,11 +12,18 @@ import ( corev1 "k8s.io/api/core/v1" ) +var hashSeparator = []byte(",") + // ExtractSecret reads a k8s secret into a manifest object storage struct if valid. func ExtractSecret(s *corev1.Secret, secretType lokiv1.ObjectStorageSecretType) (*storage.Options, error) { - var err error + hash, err := hashSecretData(s) + if err != nil { + return nil, kverrors.Wrap(err, "error calculating hash for secret", "type", secretType) + } + storageOpts := storage.Options{ SecretName: s.Name, + SecretSHA1: hash, SharedStore: secretType, } @@ -35,44 +46,71 @@ func ExtractSecret(s *corev1.Secret, secretType lokiv1.ObjectStorageSecretType) return &storageOpts, nil } +func hashSecretData(s *corev1.Secret) (string, error) { + keys := make([]string, 0, len(s.Data)) + for k := range s.Data { + keys = append(keys, k) + } + sort.Strings(keys) + + h := sha1.New() + for _, k := range keys { + if _, err := h.Write([]byte(k)); err != nil { + return "", err + } + + if _, err := h.Write(hashSeparator); err != nil { + return "", err + } + + if _, err := h.Write(s.Data[k]); err != nil { + return "", err + } + + if _, err := h.Write(hashSeparator); err != nil { + return "", err + } + } + + return fmt.Sprintf("%x", h.Sum(nil)), nil +} + func extractAzureConfigSecret(s *corev1.Secret) (*storage.AzureStorageConfig, error) { // Extract and validate mandatory fields - env := s.Data["environment"] + env := s.Data[storage.KeyAzureEnvironmentName] if len(env) == 0 { - return nil, kverrors.New("missing secret field", "field", "environment") + return nil, kverrors.New("missing secret field", "field", storage.KeyAzureEnvironmentName) } - container := s.Data["container"] + container := s.Data[storage.KeyAzureStorageContainerName] if len(container) == 0 { - return nil, kverrors.New("missing secret field", "field", "container") + return nil, kverrors.New("missing secret field", "field", storage.KeyAzureStorageContainerName) } - name := s.Data["account_name"] + name := s.Data[storage.KeyAzureStorageAccountName] if len(name) == 0 { - return nil, kverrors.New("missing secret field", "field", "account_name") + return nil, kverrors.New("missing secret field", "field", storage.KeyAzureStorageAccountName) } - key := s.Data["account_key"] + key := s.Data[storage.KeyAzureStorageAccountKey] if len(key) == 0 { - return nil, kverrors.New("missing secret field", "field", "account_key") + return nil, kverrors.New("missing secret field", "field", storage.KeyAzureStorageAccountKey) } return &storage.AzureStorageConfig{ - Env: string(env), - Container: string(container), - AccountName: string(name), - AccountKey: string(key), + Env: string(env), + Container: string(container), }, nil } func extractGCSConfigSecret(s *corev1.Secret) (*storage.GCSStorageConfig, error) { // Extract and validate mandatory fields - bucket := s.Data["bucketname"] + bucket := s.Data[storage.KeyGCPStorageBucketName] if len(bucket) == 0 { - return nil, kverrors.New("missing secret field", "field", "bucketname") + return nil, kverrors.New("missing secret field", "field", storage.KeyGCPStorageBucketName) } // Check if google authentication credentials is provided - keyJSON := s.Data["key.json"] + keyJSON := s.Data[storage.KeyGCPServiceAccountKeyFilename] if len(keyJSON) == 0 { - return nil, kverrors.New("missing google authentication credentials", "field", "key.json") + return nil, kverrors.New("missing google authentication credentials", "field", storage.KeyGCPServiceAccountKeyFilename) } return &storage.GCSStorageConfig{ @@ -82,89 +120,84 @@ func extractGCSConfigSecret(s *corev1.Secret) (*storage.GCSStorageConfig, error) func extractS3ConfigSecret(s *corev1.Secret) (*storage.S3StorageConfig, error) { // Extract and validate mandatory fields - endpoint := s.Data["endpoint"] + endpoint := s.Data[storage.KeyAWSEndpoint] if len(endpoint) == 0 { - return nil, kverrors.New("missing secret field", "field", "endpoint") + return nil, kverrors.New("missing secret field", "field", storage.KeyAWSEndpoint) } - buckets := s.Data["bucketnames"] + buckets := s.Data[storage.KeyAWSBucketNames] if len(buckets) == 0 { - return nil, kverrors.New("missing secret field", "field", "bucketnames") + return nil, kverrors.New("missing secret field", "field", storage.KeyAWSBucketNames) } - // TODO buckets are comma-separated list - id := s.Data["access_key_id"] + id := s.Data[storage.KeyAWSAccessKeyID] if len(id) == 0 { - return nil, kverrors.New("missing secret field", "field", "access_key_id") + return nil, kverrors.New("missing secret field", "field", storage.KeyAWSAccessKeyID) } - secret := s.Data["access_key_secret"] + secret := s.Data[storage.KeyAWSAccessKeySecret] if len(secret) == 0 { - return nil, kverrors.New("missing secret field", "field", "access_key_secret") + return nil, kverrors.New("missing secret field", "field", storage.KeyAWSAccessKeySecret) } // Extract and validate optional fields - region := s.Data["region"] + region := s.Data[storage.KeyAWSRegion] return &storage.S3StorageConfig{ - Endpoint: string(endpoint), - Buckets: string(buckets), - AccessKeyID: string(id), - AccessKeySecret: string(secret), - Region: string(region), + Endpoint: string(endpoint), + Buckets: string(buckets), + Region: string(region), }, nil } func extractSwiftConfigSecret(s *corev1.Secret) (*storage.SwiftStorageConfig, error) { // Extract and validate mandatory fields - url := s.Data["auth_url"] + url := s.Data[storage.KeySwiftAuthURL] if len(url) == 0 { - return nil, kverrors.New("missing secret field", "field", "auth_url") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftAuthURL) } - username := s.Data["username"] + username := s.Data[storage.KeySwiftUsername] if len(username) == 0 { - return nil, kverrors.New("missing secret field", "field", "username") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftUsername) } - userDomainName := s.Data["user_domain_name"] + userDomainName := s.Data[storage.KeySwiftUserDomainName] if len(userDomainName) == 0 { - return nil, kverrors.New("missing secret field", "field", "user_domain_name") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftUserDomainName) } - userDomainID := s.Data["user_domain_id"] + userDomainID := s.Data[storage.KeySwiftUserDomainID] if len(userDomainID) == 0 { - return nil, kverrors.New("missing secret field", "field", "user_domain_id") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftUserDomainID) } - userID := s.Data["user_id"] + userID := s.Data[storage.KeySwiftUserID] if len(userID) == 0 { - return nil, kverrors.New("missing secret field", "field", "user_id") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftUserID) } - password := s.Data["password"] + password := s.Data[storage.KeySwiftPassword] if len(password) == 0 { - return nil, kverrors.New("missing secret field", "field", "password") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftPassword) } - domainID := s.Data["domain_id"] + domainID := s.Data[storage.KeySwiftDomainID] if len(domainID) == 0 { - return nil, kverrors.New("missing secret field", "field", "domain_id") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftDomainID) } - domainName := s.Data["domain_name"] + domainName := s.Data[storage.KeySwiftDomainName] if len(domainName) == 0 { - return nil, kverrors.New("missing secret field", "field", "domain_name") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftDomainName) } - containerName := s.Data["container_name"] + containerName := s.Data[storage.KeySwiftContainerName] if len(containerName) == 0 { - return nil, kverrors.New("missing secret field", "field", "container_name") + return nil, kverrors.New("missing secret field", "field", storage.KeySwiftContainerName) } // Extract and validate optional fields - projectID := s.Data["project_id"] - projectName := s.Data["project_name"] - projectDomainID := s.Data["project_domain_id"] - projectDomainName := s.Data["project_domain_name"] - region := s.Data["region"] + projectID := s.Data[storage.KeySwiftProjectID] + projectName := s.Data[storage.KeySwiftProjectName] + projectDomainID := s.Data[storage.KeySwiftProjectDomainId] + projectDomainName := s.Data[storage.KeySwiftProjectDomainName] + region := s.Data[storage.KeySwiftRegion] return &storage.SwiftStorageConfig{ AuthURL: string(url), - Username: string(username), UserDomainName: string(userDomainName), UserDomainID: string(userDomainID), UserID: string(userID), - Password: string(password), DomainID: string(domainID), DomainName: string(domainName), ProjectID: string(projectID), diff --git a/operator/internal/handlers/internal/storage/secrets_test.go b/operator/internal/handlers/internal/storage/secrets_test.go index df69e2180b75d..f0112bf69e8aa 100644 --- a/operator/internal/handlers/internal/storage/secrets_test.go +++ b/operator/internal/handlers/internal/storage/secrets_test.go @@ -1,14 +1,66 @@ -package storage_test +package storage import ( "testing" - lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" - "github.com/grafana/loki/operator/internal/handlers/internal/storage" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" ) +func TestHashSecretData(t *testing.T) { + tt := []struct { + desc string + data map[string][]byte + wantHash string + }{ + { + desc: "nil", + data: nil, + wantHash: "da39a3ee5e6b4b0d3255bfef95601890afd80709", + }, + { + desc: "empty", + data: map[string][]byte{}, + wantHash: "da39a3ee5e6b4b0d3255bfef95601890afd80709", + }, + { + desc: "single entry", + data: map[string][]byte{ + "key": []byte("value"), + }, + wantHash: "a8973b2094d3af1e43931132dee228909bf2b02a", + }, + { + desc: "multiple entries", + data: map[string][]byte{ + "key": []byte("value"), + "key3": []byte("value3"), + "key2": []byte("value2"), + }, + wantHash: "a3341093891ad4df9f07db586029be48e9e6e884", + }, + } + + for _, tc := range tt { + tc := tc + + t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + + s := &corev1.Secret{ + Data: tc.data, + } + + hash, err := hashSecretData(s) + require.NoError(t, err) + require.Equal(t, tc.wantHash, hash) + }) + } +} + func TestAzureExtract(t *testing.T) { type test struct { name string @@ -43,6 +95,7 @@ func TestAzureExtract(t *testing.T) { { name: "missing account_key", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "environment": []byte("here"), "container": []byte("this,that"), @@ -54,6 +107,7 @@ func TestAzureExtract(t *testing.T) { { name: "all set", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "environment": []byte("here"), "container": []byte("this,that"), @@ -68,9 +122,12 @@ func TestAzureExtract(t *testing.T) { t.Run(tst.name, func(t *testing.T) { t.Parallel() - _, err := storage.ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretAzure) + opts, err := ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretAzure) if !tst.wantErr { require.NoError(t, err) + require.NotEmpty(t, opts.SecretName) + require.NotEmpty(t, opts.SecretSHA1) + require.Equal(t, opts.SharedStore, lokiv1.ObjectStorageSecretAzure) } if tst.wantErr { require.NotNil(t, err) @@ -103,6 +160,7 @@ func TestGCSExtract(t *testing.T) { { name: "all set", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "bucketname": []byte("here"), "key.json": []byte("{\"type\": \"SA\"}"), @@ -115,7 +173,7 @@ func TestGCSExtract(t *testing.T) { t.Run(tst.name, func(t *testing.T) { t.Parallel() - _, err := storage.ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretGCS) + _, err := ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretGCS) if !tst.wantErr { require.NoError(t, err) } @@ -171,6 +229,7 @@ func TestS3Extract(t *testing.T) { { name: "all set", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "endpoint": []byte("here"), "bucketnames": []byte("this,that"), @@ -185,9 +244,12 @@ func TestS3Extract(t *testing.T) { t.Run(tst.name, func(t *testing.T) { t.Parallel() - _, err := storage.ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretS3) + opts, err := ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretS3) if !tst.wantErr { require.NoError(t, err) + require.NotEmpty(t, opts.SecretName) + require.NotEmpty(t, opts.SecretSHA1) + require.Equal(t, opts.SharedStore, lokiv1.ObjectStorageSecretS3) } if tst.wantErr { require.NotNil(t, err) @@ -311,6 +373,7 @@ func TestSwiftExtract(t *testing.T) { { name: "all set", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "auth_url": []byte("here"), "username": []byte("this,that"), @@ -330,9 +393,12 @@ func TestSwiftExtract(t *testing.T) { t.Run(tst.name, func(t *testing.T) { t.Parallel() - _, err := storage.ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretSwift) + opts, err := ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretSwift) if !tst.wantErr { require.NoError(t, err) + require.NotEmpty(t, opts.SecretName) + require.NotEmpty(t, opts.SecretSHA1) + require.Equal(t, opts.SharedStore, lokiv1.ObjectStorageSecretSwift) } if tst.wantErr { require.NotNil(t, err) diff --git a/operator/internal/manifests/compactor.go b/operator/internal/manifests/compactor.go index f619466b9bfe5..2e99e54b32a2b 100644 --- a/operator/internal/manifests/compactor.go +++ b/operator/internal/manifests/compactor.go @@ -130,7 +130,7 @@ func NewCompactorStatefulSet(opts Options) *appsv1.StatefulSet { } l := ComponentLabels(LabelCompactorComponent, opts.Name) - a := commonAnnotations(opts.ConfigSHA1, opts.CertRotationRequiredAt) + a := commonAnnotations(opts.ConfigSHA1, opts.ObjectStorage.SecretSHA1, opts.CertRotationRequiredAt) return &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ Kind: "StatefulSet", diff --git a/operator/internal/manifests/compactor_test.go b/operator/internal/manifests/compactor_test.go index b2348b50faf5b..de44a1b5c4a00 100644 --- a/operator/internal/manifests/compactor_test.go +++ b/operator/internal/manifests/compactor_test.go @@ -4,6 +4,7 @@ import ( "testing" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/stretchr/testify/require" ) @@ -48,10 +49,32 @@ func TestNewCompactorStatefulSet_HasTemplateConfigHashAnnotation(t *testing.T) { }, }, }) - expected := "loki.grafana.com/config-hash" + annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], "deadbeef") +} + +func TestNewCompactorStatefulSet_HasTemplateObjectStorageHashAnnotation(t *testing.T) { + ss := NewCompactorStatefulSet(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + StorageClassName: "standard", + Template: &lokiv1.LokiTemplateSpec{ + Compactor: &lokiv1.LokiComponentSpec{ + Replicas: 1, + }, + }, + }, + }) + + annotations := ss.Spec.Template.Annotations + require.Contains(t, annotations, AnnotationLokiObjectStoreHash) + require.Equal(t, annotations[AnnotationLokiObjectStoreHash], "deadbeef") } func TestNewCompactorStatefulSet_HasTemplateCertRotationRequiredAtAnnotation(t *testing.T) { @@ -68,8 +91,8 @@ func TestNewCompactorStatefulSet_HasTemplateCertRotationRequiredAtAnnotation(t * }, }, }) - expected := "loki.grafana.com/certRotationRequiredAt" + annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } diff --git a/operator/internal/manifests/config.go b/operator/internal/manifests/config.go index 9e19aa3530bb3..f338bd02fc66a 100644 --- a/operator/internal/manifests/config.go +++ b/operator/internal/manifests/config.go @@ -47,9 +47,9 @@ func LokiConfigMap(opt Options) (*corev1.ConfigMap, string, error) { Name: lokiConfigMapName(opt.Name), Labels: commonLabels(opt.Name), }, - BinaryData: map[string][]byte{ - config.LokiConfigFileName: c, - config.LokiRuntimeConfigFileName: rc, + Data: map[string]string{ + config.LokiConfigFileName: string(c), + config.LokiRuntimeConfigFileName: string(rc), }, }, sha1C, nil } diff --git a/operator/internal/manifests/distributor.go b/operator/internal/manifests/distributor.go index 2a4f1c328384b..273f4aeba48ec 100644 --- a/operator/internal/manifests/distributor.go +++ b/operator/internal/manifests/distributor.go @@ -124,7 +124,7 @@ func NewDistributorDeployment(opts Options) *appsv1.Deployment { } l := ComponentLabels(LabelDistributorComponent, opts.Name) - a := commonAnnotations(opts.ConfigSHA1, opts.CertRotationRequiredAt) + a := commonAnnotations(opts.ConfigSHA1, opts.ObjectStorage.SecretSHA1, opts.CertRotationRequiredAt) return &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ diff --git a/operator/internal/manifests/distributor_test.go b/operator/internal/manifests/distributor_test.go index 925d5053eefbf..f635b77eb55a3 100644 --- a/operator/internal/manifests/distributor_test.go +++ b/operator/internal/manifests/distributor_test.go @@ -4,6 +4,7 @@ import ( "testing" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/stretchr/testify/require" ) @@ -41,10 +42,30 @@ func TestNewDistributorDeployment_HasTemplateConfigHashAnnotation(t *testing.T) }, }) - expected := "loki.grafana.com/config-hash" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], "deadbeef") +} + +func TestNewDistributorDeployment_HasTemplateObjectStoreHashAnnotation(t *testing.T) { + ss := NewDistributorDeployment(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + Template: &lokiv1.LokiTemplateSpec{ + Distributor: &lokiv1.LokiComponentSpec{ + Replicas: 1, + }, + }, + }, + }) + + annotations := ss.Spec.Template.Annotations + require.Contains(t, annotations, AnnotationLokiObjectStoreHash) + require.Equal(t, annotations[AnnotationLokiObjectStoreHash], "deadbeef") } func TestNewDistributorDeployment_HasTemplateCertRotationRequiredAtAnnotation(t *testing.T) { @@ -61,8 +82,7 @@ func TestNewDistributorDeployment_HasTemplateCertRotationRequiredAtAnnotation(t }, }) - expected := "loki.grafana.com/certRotationRequiredAt" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } diff --git a/operator/internal/manifests/gateway.go b/operator/internal/manifests/gateway.go index 70513eb586bb1..bfac0b2ae1ef2 100644 --- a/operator/internal/manifests/gateway.go +++ b/operator/internal/manifests/gateway.go @@ -207,7 +207,7 @@ func NewGatewayDeployment(opts Options, sha1C string) *appsv1.Deployment { } l := ComponentLabels(LabelGatewayComponent, opts.Name) - a := commonAnnotations(sha1C, opts.CertRotationRequiredAt) + a := commonAnnotations(sha1C, "", opts.CertRotationRequiredAt) return &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ diff --git a/operator/internal/manifests/gateway_test.go b/operator/internal/manifests/gateway_test.go index 115d3b79bc0a8..4a6f021928680 100644 --- a/operator/internal/manifests/gateway_test.go +++ b/operator/internal/manifests/gateway_test.go @@ -10,6 +10,7 @@ import ( lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" "github.com/grafana/loki/operator/internal/manifests/internal/gateway" "github.com/grafana/loki/operator/internal/manifests/openshift" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/google/uuid" "github.com/stretchr/testify/require" @@ -49,10 +50,46 @@ func TestNewGatewayDeployment_HasTemplateConfigHashAnnotation(t *testing.T) { Timeouts: defaultTimeoutConfig, }, sha1C) - expected := "loki.grafana.com/config-hash" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], sha1C) + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], sha1C) +} + +func TestNewGatewayDeployment_HasNotTemplateObjectStoreHashAnnotation(t *testing.T) { + sha1C := "deadbeef" + ss := NewGatewayDeployment(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + Template: &lokiv1.LokiTemplateSpec{ + Compactor: &lokiv1.LokiComponentSpec{ + Replicas: rand.Int31(), + }, + Distributor: &lokiv1.LokiComponentSpec{ + Replicas: rand.Int31(), + }, + Gateway: &lokiv1.LokiComponentSpec{ + Replicas: rand.Int31(), + }, + Ingester: &lokiv1.LokiComponentSpec{ + Replicas: rand.Int31(), + }, + Querier: &lokiv1.LokiComponentSpec{ + Replicas: rand.Int31(), + }, + QueryFrontend: &lokiv1.LokiComponentSpec{ + Replicas: rand.Int31(), + }, + }, + }, + Timeouts: defaultTimeoutConfig, + }, sha1C) + + annotations := ss.Spec.Template.Annotations + require.NotContains(t, annotations, AnnotationLokiObjectStoreHash) } func TestNewGatewayDeployment_HasNodeSelector(t *testing.T) { @@ -132,10 +169,9 @@ func TestNewGatewayDeployment_HasTemplateCertRotationRequiredAtAnnotation(t *tes Timeouts: defaultTimeoutConfig, }, sha1C) - expected := "loki.grafana.com/certRotationRequiredAt" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } func TestGatewayConfigMap_ReturnsSHA1OfBinaryContents(t *testing.T) { diff --git a/operator/internal/manifests/indexgateway.go b/operator/internal/manifests/indexgateway.go index 0c31ab4528f7a..e390fa92c33bb 100644 --- a/operator/internal/manifests/indexgateway.go +++ b/operator/internal/manifests/indexgateway.go @@ -130,7 +130,7 @@ func NewIndexGatewayStatefulSet(opts Options) *appsv1.StatefulSet { } l := ComponentLabels(LabelIndexGatewayComponent, opts.Name) - a := commonAnnotations(opts.ConfigSHA1, opts.CertRotationRequiredAt) + a := commonAnnotations(opts.ConfigSHA1, opts.ObjectStorage.SecretSHA1, opts.CertRotationRequiredAt) return &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ diff --git a/operator/internal/manifests/indexgateway_test.go b/operator/internal/manifests/indexgateway_test.go index a6dba0d5f5f92..0807b30e43704 100644 --- a/operator/internal/manifests/indexgateway_test.go +++ b/operator/internal/manifests/indexgateway_test.go @@ -4,6 +4,7 @@ import ( "testing" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/stretchr/testify/require" ) @@ -22,10 +23,31 @@ func TestNewIndexGatewayStatefulSet_HasTemplateConfigHashAnnotation(t *testing.T }, }) - expected := "loki.grafana.com/config-hash" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], "deadbeef") +} + +func TestNewIndexGatewayStatefulSet_HasTemplateObjectStoreHashAnnotation(t *testing.T) { + ss := NewIndexGatewayStatefulSet(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + StorageClassName: "standard", + Template: &lokiv1.LokiTemplateSpec{ + IndexGateway: &lokiv1.LokiComponentSpec{ + Replicas: 1, + }, + }, + }, + }) + + annotations := ss.Spec.Template.Annotations + require.Contains(t, annotations, AnnotationLokiObjectStoreHash) + require.Equal(t, annotations[AnnotationLokiObjectStoreHash], "deadbeef") } func TestNewIndexGatewayStatefulSet_HasTemplateCertRotationRequiredAtAnnotation(t *testing.T) { @@ -42,10 +64,10 @@ func TestNewIndexGatewayStatefulSet_HasTemplateCertRotationRequiredAtAnnotation( }, }, }) - expected := "loki.grafana.com/certRotationRequiredAt" + annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } func TestNewIndexGatewayStatefulSet_SelectorMatchesLabels(t *testing.T) { diff --git a/operator/internal/manifests/ingester.go b/operator/internal/manifests/ingester.go index 38518f54b8df1..1f43288a19068 100644 --- a/operator/internal/manifests/ingester.go +++ b/operator/internal/manifests/ingester.go @@ -140,7 +140,7 @@ func NewIngesterStatefulSet(opts Options) *appsv1.StatefulSet { } l := ComponentLabels(LabelIngesterComponent, opts.Name) - a := commonAnnotations(opts.ConfigSHA1, opts.CertRotationRequiredAt) + a := commonAnnotations(opts.ConfigSHA1, opts.ObjectStorage.SecretSHA1, opts.CertRotationRequiredAt) return &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ Kind: "StatefulSet", diff --git a/operator/internal/manifests/ingester_test.go b/operator/internal/manifests/ingester_test.go index b3538be6d4604..efa7ebf3cb19f 100644 --- a/operator/internal/manifests/ingester_test.go +++ b/operator/internal/manifests/ingester_test.go @@ -4,6 +4,7 @@ import ( "testing" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/stretchr/testify/require" ) @@ -22,10 +23,31 @@ func TestNewIngesterStatefulSet_HasTemplateConfigHashAnnotation(t *testing.T) { }, }) - expected := "loki.grafana.com/config-hash" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], "deadbeef") +} + +func TestNewIngesterStatefulSet_HasTemplateObjectStoreHashAnnotation(t *testing.T) { + ss := NewIngesterStatefulSet(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + StorageClassName: "standard", + Template: &lokiv1.LokiTemplateSpec{ + Ingester: &lokiv1.LokiComponentSpec{ + Replicas: 1, + }, + }, + }, + }) + + annotations := ss.Spec.Template.Annotations + require.Contains(t, annotations, AnnotationLokiObjectStoreHash) + require.Equal(t, annotations[AnnotationLokiObjectStoreHash], "deadbeef") } func TestNewIngesterStatefulSet_HasTemplateCertRotationRequiredAtAnnotation(t *testing.T) { @@ -42,10 +64,10 @@ func TestNewIngesterStatefulSet_HasTemplateCertRotationRequiredAtAnnotation(t *t }, }, }) - expected := "loki.grafana.com/certRotationRequiredAt" + annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } func TestNewIngesterStatefulSet_SelectorMatchesLabels(t *testing.T) { diff --git a/operator/internal/manifests/internal/config/build_test.go b/operator/internal/manifests/internal/config/build_test.go index eb3eed2ad6f12..7ad2da891b7e5 100644 --- a/operator/internal/manifests/internal/config/build_test.go +++ b/operator/internal/manifests/internal/config/build_test.go @@ -26,8 +26,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -235,11 +235,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -276,8 +274,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -516,11 +514,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -598,11 +594,9 @@ func TestBuild_ConfigAndRuntimeConfig_CreateLokiConfigFailed(t *testing.T) { ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -633,8 +627,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -943,11 +937,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -984,8 +976,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -1295,11 +1287,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -1336,8 +1326,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -1677,11 +1667,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -1718,8 +1706,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -2001,11 +1989,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -2045,8 +2031,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -2416,11 +2402,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -2457,8 +2441,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -2755,11 +2739,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -2796,8 +2778,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -3244,11 +3226,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -3285,8 +3265,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -3497,11 +3477,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { @@ -3538,8 +3516,8 @@ common: s3: http://test.default.svc.cluster.local.:9000 bucketnames: loki region: us-east - access_key_id: test - secret_access_key: test123 + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -3752,11 +3730,9 @@ overrides: ObjectStorage: storage.Options{ SharedStore: lokiv1.ObjectStorageSecretS3, S3: &storage.S3StorageConfig{ - Endpoint: "http://test.default.svc.cluster.local.:9000", - Region: "us-east", - Buckets: "loki", - AccessKeyID: "test", - AccessKeySecret: "test123", + Endpoint: "http://test.default.svc.cluster.local.:9000", + Region: "us-east", + Buckets: "loki", }, Schemas: []lokiv1.ObjectStorageSchema{ { diff --git a/operator/internal/manifests/internal/config/loki-config.yaml b/operator/internal/manifests/internal/config/loki-config.yaml index f0c614b229bd5..edacbcd4d9ff6 100644 --- a/operator/internal/manifests/internal/config/loki-config.yaml +++ b/operator/internal/manifests/internal/config/loki-config.yaml @@ -12,8 +12,8 @@ common: azure: environment: {{ .Env }} container_name: {{ .Container }} - account_name: {{ .AccountName }} - account_key: {{ .AccountKey }} + account_name: ${AZURE_STORAGE_ACCOUNT_NAME} + account_key: ${AZURE_STORAGE_ACCOUNT_KEY} {{- end }} {{- with .ObjectStorage.GCS }} gcs: @@ -24,18 +24,18 @@ common: s3: {{ .Endpoint }} bucketnames: {{ .Buckets }} region: {{ .Region }} - access_key_id: {{ .AccessKeyID }} - secret_access_key: {{ .AccessKeySecret }} + access_key_id: ${AWS_ACCESS_KEY_ID} + secret_access_key: ${AWS_ACCESS_KEY_SECRET} s3forcepathstyle: true {{- end }} {{- with .ObjectStorage.Swift }} swift: auth_url: {{ .AuthURL }} - username: {{ .Username }} + username: ${SWIFT_USERNAME} user_domain_name: {{ .UserDomainName }} user_domain_id: {{ .UserDomainID }} user_id: {{ .UserID }} - password: {{ .Password }} + password: ${SWIFT_PASSWORD} domain_id: {{ .DomainID }} domain_name: {{ .DomainName }} project_id: {{ .ProjectID }} diff --git a/operator/internal/manifests/querier.go b/operator/internal/manifests/querier.go index 4d0bc3a7e79ba..cc7e9c0921838 100644 --- a/operator/internal/manifests/querier.go +++ b/operator/internal/manifests/querier.go @@ -129,7 +129,7 @@ func NewQuerierDeployment(opts Options) *appsv1.Deployment { } l := ComponentLabels(LabelQuerierComponent, opts.Name) - a := commonAnnotations(opts.ConfigSHA1, opts.CertRotationRequiredAt) + a := commonAnnotations(opts.ConfigSHA1, opts.ObjectStorage.SecretSHA1, opts.CertRotationRequiredAt) return &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ diff --git a/operator/internal/manifests/querier_test.go b/operator/internal/manifests/querier_test.go index a254695121401..27d3847106f49 100644 --- a/operator/internal/manifests/querier_test.go +++ b/operator/internal/manifests/querier_test.go @@ -4,6 +4,7 @@ import ( "testing" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/stretchr/testify/require" ) @@ -22,10 +23,31 @@ func TestNewQuerierDeployment_HasTemplateConfigHashAnnotation(t *testing.T) { }, }) - expected := "loki.grafana.com/config-hash" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], "deadbeef") +} + +func TestNewQuerierDeployment_HasTemplateObjectStoreHashAnnotation(t *testing.T) { + ss := NewQuerierDeployment(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + StorageClassName: "standard", + Template: &lokiv1.LokiTemplateSpec{ + Querier: &lokiv1.LokiComponentSpec{ + Replicas: 1, + }, + }, + }, + }) + + annotations := ss.Spec.Template.Annotations + require.Contains(t, annotations, AnnotationLokiObjectStoreHash) + require.Equal(t, annotations[AnnotationLokiObjectStoreHash], "deadbeef") } func TestNewQuerierDeployment_HasTemplateCertRotationRequiredAtAnnotation(t *testing.T) { @@ -42,10 +64,9 @@ func TestNewQuerierDeployment_HasTemplateCertRotationRequiredAtAnnotation(t *tes }, }) - expected := "loki.grafana.com/certRotationRequiredAt" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } func TestNewQuerierDeployment_SelectorMatchesLabels(t *testing.T) { diff --git a/operator/internal/manifests/query-frontend.go b/operator/internal/manifests/query-frontend.go index 405da3daa6a32..cc0eeeae04b5a 100644 --- a/operator/internal/manifests/query-frontend.go +++ b/operator/internal/manifests/query-frontend.go @@ -136,7 +136,7 @@ func NewQueryFrontendDeployment(opts Options) *appsv1.Deployment { } l := ComponentLabels(LabelQueryFrontendComponent, opts.Name) - a := commonAnnotations(opts.ConfigSHA1, opts.CertRotationRequiredAt) + a := commonAnnotations(opts.ConfigSHA1, opts.ObjectStorage.SecretSHA1, opts.CertRotationRequiredAt) return &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ diff --git a/operator/internal/manifests/query-frontend_test.go b/operator/internal/manifests/query-frontend_test.go index c31bc1004aef6..84c1f96eb1b85 100644 --- a/operator/internal/manifests/query-frontend_test.go +++ b/operator/internal/manifests/query-frontend_test.go @@ -4,6 +4,7 @@ import ( "testing" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/stretchr/testify/require" ) @@ -39,10 +40,31 @@ func TestNewQueryFrontendDeployment_HasTemplateConfigHashAnnotation(t *testing.T }, }, }) - expected := "loki.grafana.com/config-hash" + + annotations := ss.Spec.Template.Annotations + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], "deadbeef") +} + +func TestNewQueryFrontendDeployment_HasTemplateObjectStoreHashAnnotation(t *testing.T) { + ss := NewQueryFrontendDeployment(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + Template: &lokiv1.LokiTemplateSpec{ + QueryFrontend: &lokiv1.LokiComponentSpec{ + Replicas: 1, + }, + }, + }, + }) + annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationLokiObjectStoreHash) + require.Equal(t, annotations[AnnotationLokiObjectStoreHash], "deadbeef") } func TestNewQueryFrontendDeployment_HasTemplateCertRotationRequiredAtAnnotation(t *testing.T) { @@ -59,8 +81,7 @@ func TestNewQueryFrontendDeployment_HasTemplateCertRotationRequiredAtAnnotation( }, }) - expected := "loki.grafana.com/certRotationRequiredAt" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } diff --git a/operator/internal/manifests/ruler.go b/operator/internal/manifests/ruler.go index 5d8ee4e2084e0..66089fb9d3271 100644 --- a/operator/internal/manifests/ruler.go +++ b/operator/internal/manifests/ruler.go @@ -177,7 +177,7 @@ func NewRulerStatefulSet(opts Options) *appsv1.StatefulSet { } l := ComponentLabels(LabelRulerComponent, opts.Name) - a := commonAnnotations(opts.ConfigSHA1, opts.CertRotationRequiredAt) + a := commonAnnotations(opts.ConfigSHA1, opts.ObjectStorage.SecretSHA1, opts.CertRotationRequiredAt) return &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ diff --git a/operator/internal/manifests/ruler_test.go b/operator/internal/manifests/ruler_test.go index b3992f2454502..54d7d33ba1621 100644 --- a/operator/internal/manifests/ruler_test.go +++ b/operator/internal/manifests/ruler_test.go @@ -6,6 +6,7 @@ import ( lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" "github.com/grafana/loki/operator/internal/manifests/openshift" + "github.com/grafana/loki/operator/internal/manifests/storage" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" ) @@ -25,10 +26,31 @@ func TestNewRulerStatefulSet_HasTemplateConfigHashAnnotation(t *testing.T) { }, }) - expected := "loki.grafana.com/config-hash" annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationLokiConfigHash) + require.Equal(t, annotations[AnnotationLokiConfigHash], "deadbeef") +} + +func TestNewRulerStatefulSet_HasTemplateObjectStoreHashAnnotation(t *testing.T) { + ss := NewRulerStatefulSet(Options{ + Name: "abcd", + Namespace: "efgh", + ObjectStorage: storage.Options{ + SecretSHA1: "deadbeef", + }, + Stack: lokiv1.LokiStackSpec{ + StorageClassName: "standard", + Template: &lokiv1.LokiTemplateSpec{ + Ruler: &lokiv1.LokiComponentSpec{ + Replicas: 1, + }, + }, + }, + }) + + annotations := ss.Spec.Template.Annotations + require.Contains(t, annotations, AnnotationLokiObjectStoreHash) + require.Equal(t, annotations[AnnotationLokiObjectStoreHash], "deadbeef") } func TestNewRulerStatefulSet_HasTemplateCertRotationRequiredAtAnnotation(t *testing.T) { @@ -45,10 +67,10 @@ func TestNewRulerStatefulSet_HasTemplateCertRotationRequiredAtAnnotation(t *test }, }, }) - expected := "loki.grafana.com/certRotationRequiredAt" + annotations := ss.Spec.Template.Annotations - require.Contains(t, annotations, expected) - require.Equal(t, annotations[expected], "deadbeef") + require.Contains(t, annotations, AnnotationCertRotationRequiredAt) + require.Equal(t, annotations[AnnotationCertRotationRequiredAt], "deadbeef") } func TestBuildRuler_HasExtraObjectsForTenantMode(t *testing.T) { diff --git a/operator/internal/manifests/storage/configure.go b/operator/internal/manifests/storage/configure.go index 8a2003b976115..5c148116341aa 100644 --- a/operator/internal/manifests/storage/configure.go +++ b/operator/internal/manifests/storage/configure.go @@ -12,28 +12,19 @@ import ( lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" ) -const ( - // EnvGoogleApplicationCredentials is the environment variable to specify path to key.json - EnvGoogleApplicationCredentials = "GOOGLE_APPLICATION_CREDENTIALS" - // GCSFileName is the file containing the Google credentials for authentication - GCSFileName = "key.json" - - secretDirectory = "/etc/storage/secrets" - storageTLSVolume = "storage-tls" - caDirectory = "/etc/storage/ca" -) - // ConfigureDeployment appends additional pod volumes and container env vars, args, volume mounts // based on the object storage type. Currently supported amendments: +// - All: Ensure object storage secret mounted and auth projected as env vars. // - GCS: Ensure env var GOOGLE_APPLICATION_CREDENTIALS in container // - S3: Ensure mounting custom CA configmap if any TLSConfig given func ConfigureDeployment(d *appsv1.Deployment, opts Options) error { switch opts.SharedStore { - case lokiv1.ObjectStorageSecretGCS: - return configureDeployment(d, opts.SecretName) + case lokiv1.ObjectStorageSecretAzure, lokiv1.ObjectStorageSecretGCS, lokiv1.ObjectStorageSecretSwift: + return configureDeployment(d, opts) case lokiv1.ObjectStorageSecretS3: - if opts.TLS == nil { - return nil + err := configureDeployment(d, opts) + if err != nil { + return err } return configureDeploymentCA(d, opts.TLS) default: @@ -43,15 +34,16 @@ func ConfigureDeployment(d *appsv1.Deployment, opts Options) error { // ConfigureStatefulSet appends additional pod volumes and container env vars, args, volume mounts // based on the object storage type. Currently supported amendments: +// - All: Ensure object storage secret mounted and auth projected as env vars. // - GCS: Ensure env var GOOGLE_APPLICATION_CREDENTIALS in container // - S3: Ensure mounting custom CA configmap if any TLSConfig given func ConfigureStatefulSet(d *appsv1.StatefulSet, opts Options) error { switch opts.SharedStore { - case lokiv1.ObjectStorageSecretGCS: - return configureStatefulSet(d, opts.SecretName) + case lokiv1.ObjectStorageSecretAzure, lokiv1.ObjectStorageSecretGCS, lokiv1.ObjectStorageSecretSwift: + return configureStatefulSet(d, opts) case lokiv1.ObjectStorageSecretS3: - if opts.TLS == nil { - return nil + if err := configureStatefulSet(d, opts); err != nil { + return err } return configureStatefulSetCA(d, opts.TLS) default: @@ -59,10 +51,10 @@ func ConfigureStatefulSet(d *appsv1.StatefulSet, opts Options) error { } } -// ConfigureDeployment merges a GCS Object Storage volume into the deployment spec. -// With this, the deployment will expose an environment variable for Google authentication. -func configureDeployment(d *appsv1.Deployment, secretName string) error { - p := ensureCredentialsForGCS(&d.Spec.Template.Spec, secretName) +// ConfigureDeployment merges the object storage secret volume into the deployment spec. +// With this, the deployment will expose credentials specific environment variables. +func configureDeployment(d *appsv1.Deployment, opts Options) error { + p := ensureObjectStoreCredentials(&d.Spec.Template.Spec, opts) if err := mergo.Merge(&d.Spec.Template.Spec, p, mergo.WithOverride); err != nil { return kverrors.Wrap(err, "failed to merge gcs object storage spec ") @@ -73,6 +65,10 @@ func configureDeployment(d *appsv1.Deployment, secretName string) error { // ConfigureDeploymentCA merges a S3 CA ConfigMap volume into the deployment spec. func configureDeploymentCA(d *appsv1.Deployment, tls *TLSConfig) error { + if tls == nil { + return nil + } + p := ensureCAForS3(&d.Spec.Template.Spec, tls) if err := mergo.Merge(&d.Spec.Template.Spec, p, mergo.WithOverride); err != nil { @@ -82,10 +78,10 @@ func configureDeploymentCA(d *appsv1.Deployment, tls *TLSConfig) error { return nil } -// ConfigureStatefulSet merges a GCS Object Storage volume into the statefulset spec. -// With this, the statefulset will expose an environment variable for Google authentication. -func configureStatefulSet(s *appsv1.StatefulSet, secretName string) error { - p := ensureCredentialsForGCS(&s.Spec.Template.Spec, secretName) +// ConfigureStatefulSet merges a the object storage secrect volume into the statefulset spec. +// With this, the statefulset will expose credentials specific environment variable. +func configureStatefulSet(s *appsv1.StatefulSet, opts Options) error { + p := ensureObjectStoreCredentials(&s.Spec.Template.Spec, opts) if err := mergo.Merge(&s.Spec.Template.Spec, p, mergo.WithOverride); err != nil { return kverrors.Wrap(err, "failed to merge gcs object storage spec ") @@ -96,6 +92,10 @@ func configureStatefulSet(s *appsv1.StatefulSet, secretName string) error { // ConfigureStatefulSetCA merges a S3 CA ConfigMap volume into the statefulset spec. func configureStatefulSetCA(s *appsv1.StatefulSet, tls *TLSConfig) error { + if tls == nil { + return nil + } + p := ensureCAForS3(&s.Spec.Template.Spec, tls) if err := mergo.Merge(&s.Spec.Template.Spec, p, mergo.WithOverride); err != nil { @@ -105,9 +105,11 @@ func configureStatefulSetCA(s *appsv1.StatefulSet, tls *TLSConfig) error { return nil } -func ensureCredentialsForGCS(p *corev1.PodSpec, secretName string) corev1.PodSpec { +func ensureObjectStoreCredentials(p *corev1.PodSpec, opts Options) corev1.PodSpec { container := p.Containers[0].DeepCopy() volumes := p.Volumes + secretName := opts.SecretName + storeType := opts.SharedStore volumes = append(volumes, corev1.Volume{ Name: secretName, @@ -124,10 +126,94 @@ func ensureCredentialsForGCS(p *corev1.PodSpec, secretName string) corev1.PodSpe MountPath: secretDirectory, }) - container.Env = append(container.Env, corev1.EnvVar{ - Name: EnvGoogleApplicationCredentials, - Value: path.Join(secretDirectory, GCSFileName), - }) + var storeEnvVars []corev1.EnvVar + switch storeType { + case lokiv1.ObjectStorageSecretAzure: + storeEnvVars = []corev1.EnvVar{ + { + Name: EnvAzureStorageAccountName, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeyAzureStorageAccountName, + }, + }, + }, + { + Name: EnvAzureStorageAccountKey, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeyAzureStorageAccountKey, + }, + }, + }, + } + case lokiv1.ObjectStorageSecretGCS: + storeEnvVars = []corev1.EnvVar{ + { + Name: EnvGoogleApplicationCredentials, + Value: path.Join(secretDirectory, KeyGCPServiceAccountKeyFilename), + }, + } + case lokiv1.ObjectStorageSecretS3: + storeEnvVars = []corev1.EnvVar{ + { + Name: EnvAWSAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeyAWSAccessKeyID, + }, + }, + }, + { + Name: EnvAWSAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeyAWSAccessKeySecret, + }, + }, + }, + } + + case lokiv1.ObjectStorageSecretSwift: + storeEnvVars = []corev1.EnvVar{ + { + Name: EnvSwiftUsername, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeySwiftUsername, + }, + }, + }, + { + Name: EnvSwiftPassword, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeySwiftPassword, + }, + }, + }, + } + } + + container.Env = append(container.Env, storeEnvVars...) return corev1.PodSpec{ Containers: []corev1.Container{ diff --git a/operator/internal/manifests/storage/configure_test.go b/operator/internal/manifests/storage/configure_test.go index 792a452229e1e..53fb0bfea1199 100644 --- a/operator/internal/manifests/storage/configure_test.go +++ b/operator/internal/manifests/storage/configure_test.go @@ -1,4 +1,4 @@ -package storage_test +package storage import ( "testing" @@ -8,23 +8,22 @@ import ( corev1 "k8s.io/api/core/v1" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" - "github.com/grafana/loki/operator/internal/manifests/storage" ) func TestConfigureDeploymentForStorageType(t *testing.T) { type tt struct { desc string - opts storage.Options + opts Options dpl *appsv1.Deployment want *appsv1.Deployment } tc := []tt{ { - desc: "object storage other than GCS", - opts: storage.Options{ + desc: "object storage Azure", + opts: Options{ SecretName: "test", - SharedStore: lokiv1.ObjectStorageSecretS3, + SharedStore: lokiv1.ObjectStorageSecretAzure, }, dpl: &appsv1.Deployment{ Spec: appsv1.DeploymentSpec{ @@ -46,6 +45,47 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { Containers: []corev1.Container{ { Name: "loki-ingester", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvAzureStorageAccountName, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAzureStorageAccountName, + }, + }, + }, + { + Name: EnvAzureStorageAccountKey, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAzureStorageAccountKey, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, }, }, }, @@ -55,7 +95,7 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, { desc: "object storage GCS", - opts: storage.Options{ + opts: Options{ SecretName: "test", SharedStore: lokiv1.ObjectStorageSecretGCS, }, @@ -88,7 +128,7 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, Env: []corev1.EnvVar{ { - Name: storage.EnvGoogleApplicationCredentials, + Name: EnvGoogleApplicationCredentials, Value: "/etc/storage/secrets/key.json", }, }, @@ -109,13 +149,161 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, }, }, + { + desc: "object storage S3", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretS3, + }, + dpl: &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + }, + }, + }, + }, + }, + }, + want: &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvAWSAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeyID, + }, + }, + }, + { + Name: EnvAWSAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeySecret, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + desc: "object storage Swift", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretSwift, + }, + dpl: &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + }, + }, + }, + }, + }, + }, + want: &appsv1.Deployment{ + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvSwiftUsername, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftUsername, + }, + }, + }, + { + Name: EnvSwiftPassword, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftPassword, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, + }, + }, + }, + }, + }, + }, } for _, tc := range tc { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() - err := storage.ConfigureDeployment(tc.dpl, tc.opts) + err := ConfigureDeployment(tc.dpl, tc.opts) require.NoError(t, err) require.Equal(t, tc.want, tc.dpl) }) @@ -125,17 +313,17 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { func TestConfigureStatefulSetForStorageType(t *testing.T) { type tt struct { desc string - opts storage.Options + opts Options sts *appsv1.StatefulSet want *appsv1.StatefulSet } tc := []tt{ { - desc: "object storage other than GCS", - opts: storage.Options{ + desc: "object storage Azure", + opts: Options{ SecretName: "test", - SharedStore: lokiv1.ObjectStorageSecretS3, + SharedStore: lokiv1.ObjectStorageSecretAzure, }, sts: &appsv1.StatefulSet{ Spec: appsv1.StatefulSetSpec{ @@ -157,6 +345,47 @@ func TestConfigureStatefulSetForStorageType(t *testing.T) { Containers: []corev1.Container{ { Name: "loki-ingester", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvAzureStorageAccountName, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAzureStorageAccountName, + }, + }, + }, + { + Name: EnvAzureStorageAccountKey, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAzureStorageAccountKey, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, }, }, }, @@ -166,7 +395,7 @@ func TestConfigureStatefulSetForStorageType(t *testing.T) { }, { desc: "object storage GCS", - opts: storage.Options{ + opts: Options{ SecretName: "test", SharedStore: lokiv1.ObjectStorageSecretGCS, }, @@ -199,7 +428,7 @@ func TestConfigureStatefulSetForStorageType(t *testing.T) { }, Env: []corev1.EnvVar{ { - Name: storage.EnvGoogleApplicationCredentials, + Name: EnvGoogleApplicationCredentials, Value: "/etc/storage/secrets/key.json", }, }, @@ -220,13 +449,161 @@ func TestConfigureStatefulSetForStorageType(t *testing.T) { }, }, }, + { + desc: "object storage S3", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretS3, + }, + sts: &appsv1.StatefulSet{ + Spec: appsv1.StatefulSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + }, + }, + }, + }, + }, + }, + want: &appsv1.StatefulSet{ + Spec: appsv1.StatefulSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvAWSAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeyID, + }, + }, + }, + { + Name: EnvAWSAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeySecret, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + desc: "object storage Swift", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretSwift, + }, + sts: &appsv1.StatefulSet{ + Spec: appsv1.StatefulSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + }, + }, + }, + }, + }, + }, + want: &appsv1.StatefulSet{ + Spec: appsv1.StatefulSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "loki-ingester", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvSwiftUsername, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftUsername, + }, + }, + }, + { + Name: EnvSwiftPassword, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftPassword, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, + }, + }, + }, + }, + }, + }, } for _, tc := range tc { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() - err := storage.ConfigureStatefulSet(tc.sts, tc.opts) + err := ConfigureStatefulSet(tc.sts, tc.opts) require.NoError(t, err) require.Equal(t, tc.want, tc.sts) }) @@ -236,7 +613,7 @@ func TestConfigureStatefulSetForStorageType(t *testing.T) { func TestConfigureDeploymentForStorageCA(t *testing.T) { type tt struct { desc string - opts storage.Options + opts Options dpl *appsv1.Deployment want *appsv1.Deployment } @@ -244,9 +621,9 @@ func TestConfigureDeploymentForStorageCA(t *testing.T) { tc := []tt{ { desc: "object storage other than S3", - opts: storage.Options{ + opts: Options{ SecretName: "test", - SharedStore: lokiv1.ObjectStorageSecretAzure, + SharedStore: lokiv1.ObjectStorageSecretSwift, }, dpl: &appsv1.Deployment{ Spec: appsv1.DeploymentSpec{ @@ -268,6 +645,47 @@ func TestConfigureDeploymentForStorageCA(t *testing.T) { Containers: []corev1.Container{ { Name: "loki-querier", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvSwiftUsername, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftUsername, + }, + }, + }, + { + Name: EnvSwiftPassword, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftPassword, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, }, }, }, @@ -277,10 +695,10 @@ func TestConfigureDeploymentForStorageCA(t *testing.T) { }, { desc: "object storage S3", - opts: storage.Options{ + opts: Options{ SecretName: "test", SharedStore: lokiv1.ObjectStorageSecretS3, - TLS: &storage.TLSConfig{ + TLS: &TLSConfig{ CA: "test", Key: "service-ca.crt", }, @@ -306,6 +724,11 @@ func TestConfigureDeploymentForStorageCA(t *testing.T) { { Name: "loki-querier", VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, { Name: "storage-tls", ReadOnly: false, @@ -315,9 +738,41 @@ func TestConfigureDeploymentForStorageCA(t *testing.T) { Args: []string{ "-s3.http.ca-file=/etc/storage/ca/service-ca.crt", }, + Env: []corev1.EnvVar{ + { + Name: EnvAWSAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeyID, + }, + }, + }, + { + Name: EnvAWSAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeySecret, + }, + }, + }, + }, }, }, Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, { Name: "storage-tls", VolumeSource: corev1.VolumeSource{ @@ -340,7 +795,7 @@ func TestConfigureDeploymentForStorageCA(t *testing.T) { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() - err := storage.ConfigureDeployment(tc.dpl, tc.opts) + err := ConfigureDeployment(tc.dpl, tc.opts) require.NoError(t, err) require.Equal(t, tc.want, tc.dpl) }) @@ -350,7 +805,7 @@ func TestConfigureDeploymentForStorageCA(t *testing.T) { func TestConfigureStatefulSetForStorageCA(t *testing.T) { type tt struct { desc string - opts storage.Options + opts Options sts *appsv1.StatefulSet want *appsv1.StatefulSet } @@ -358,10 +813,10 @@ func TestConfigureStatefulSetForStorageCA(t *testing.T) { tc := []tt{ { desc: "object storage other than S3", - opts: storage.Options{ + opts: Options{ SecretName: "test", - SharedStore: lokiv1.ObjectStorageSecretAzure, - TLS: &storage.TLSConfig{ + SharedStore: lokiv1.ObjectStorageSecretSwift, + TLS: &TLSConfig{ CA: "test", }, }, @@ -385,6 +840,47 @@ func TestConfigureStatefulSetForStorageCA(t *testing.T) { Containers: []corev1.Container{ { Name: "loki-ingester", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, + }, + Env: []corev1.EnvVar{ + { + Name: EnvSwiftUsername, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftUsername, + }, + }, + }, + { + Name: EnvSwiftPassword, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeySwiftPassword, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, }, }, }, @@ -394,10 +890,10 @@ func TestConfigureStatefulSetForStorageCA(t *testing.T) { }, { desc: "object storage S3", - opts: storage.Options{ + opts: Options{ SecretName: "test", SharedStore: lokiv1.ObjectStorageSecretS3, - TLS: &storage.TLSConfig{ + TLS: &TLSConfig{ CA: "test", Key: "service-ca.crt", }, @@ -423,6 +919,11 @@ func TestConfigureStatefulSetForStorageCA(t *testing.T) { { Name: "loki-ingester", VolumeMounts: []corev1.VolumeMount{ + { + Name: "test", + ReadOnly: false, + MountPath: "/etc/storage/secrets", + }, { Name: "storage-tls", ReadOnly: false, @@ -432,9 +933,41 @@ func TestConfigureStatefulSetForStorageCA(t *testing.T) { Args: []string{ "-s3.http.ca-file=/etc/storage/ca/service-ca.crt", }, + Env: []corev1.EnvVar{ + { + Name: EnvAWSAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeyID, + }, + }, + }, + { + Name: EnvAWSAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSAccessKeySecret, + }, + }, + }, + }, }, }, Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, { Name: "storage-tls", VolumeSource: corev1.VolumeSource{ @@ -457,7 +990,7 @@ func TestConfigureStatefulSetForStorageCA(t *testing.T) { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() - err := storage.ConfigureStatefulSet(tc.sts, tc.opts) + err := ConfigureStatefulSet(tc.sts, tc.opts) require.NoError(t, err) require.Equal(t, tc.want, tc.sts) }) diff --git a/operator/internal/manifests/storage/options.go b/operator/internal/manifests/storage/options.go index b6750b45adc56..7b4d2be7bd5d1 100644 --- a/operator/internal/manifests/storage/options.go +++ b/operator/internal/manifests/storage/options.go @@ -16,15 +16,14 @@ type Options struct { Swift *SwiftStorageConfig SecretName string + SecretSHA1 string TLS *TLSConfig } // AzureStorageConfig for Azure storage config type AzureStorageConfig struct { - Env string - Container string - AccountName string - AccountKey string + Env string + Container string } // GCSStorageConfig for GCS storage config @@ -34,21 +33,17 @@ type GCSStorageConfig struct { // S3StorageConfig for S3 storage config type S3StorageConfig struct { - Endpoint string - Region string - Buckets string - AccessKeyID string - AccessKeySecret string + Endpoint string + Region string + Buckets string } // SwiftStorageConfig for Swift storage config type SwiftStorageConfig struct { AuthURL string - Username string UserDomainName string UserDomainID string UserID string - Password string DomainID string DomainName string ProjectID string diff --git a/operator/internal/manifests/storage/schema_test.go b/operator/internal/manifests/storage/schema_test.go index 3663a5c0ddf2f..c3ca914658f9e 100644 --- a/operator/internal/manifests/storage/schema_test.go +++ b/operator/internal/manifests/storage/schema_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" - "github.com/stretchr/testify/require" + + lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" ) func TestBuildSchemaConfig_NoSchemas(t *testing.T) { diff --git a/operator/internal/manifests/storage/var.go b/operator/internal/manifests/storage/var.go new file mode 100644 index 0000000000000..aae6e1ea0e583 --- /dev/null +++ b/operator/internal/manifests/storage/var.go @@ -0,0 +1,99 @@ +package storage + +const ( + // EnvAlibabaCloudAccessKeyID is the environment variable to specify the AlibabaCloud client id to access S3. + EnvAlibabaCloudAccessKeyID = "ALIBABA_CLOUD_ACCESS_KEY_ID" + // EnvAlibabaCloudAccessKeySecret is the environment variable to specify the AlibabaCloud client secret to access S3. + EnvAlibabaCloudAccessKeySecret = "ALIBABA_CLOUD_ACCESS_KEY_SECRET" + // EnvAWSAccessKeyID is the environment variable to specify the AWS client id to access S3. + EnvAWSAccessKeyID = "AWS_ACCESS_KEY_ID" + // EnvAWSAccessKeySecret is the environment variable to specify the AWS client secret to access S3. + EnvAWSAccessKeySecret = "AWS_ACCESS_KEY_SECRET" + // EnvAWSSseKmsEncryptionContext is the environment variable to specity the AWS KMS encryption context when using type SSE-KMS. + EnvAWSSseKmsEncryptionContext = "AWS_SSE_KMS_ENCRYPTION_CONTEXT" + // EnvAzureStorageAccountName is the environment variable to specify the Azure storage account name to access the container. + EnvAzureStorageAccountName = "AZURE_STORAGE_ACCOUNT_NAME" + // EnvAzureStorageAccountKey is the environment variable to specify the Azure storage account key to access the container. + EnvAzureStorageAccountKey = "AZURE_STORAGE_ACCOUNT_KEY" + // EnvGoogleApplicationCredentials is the environment variable to specify path to key.json + EnvGoogleApplicationCredentials = "GOOGLE_APPLICATION_CREDENTIALS" + // EnvSwiftPassword is the environment variable to specify the OpenStack Swift password. + EnvSwiftPassword = "SWIFT_PASSWORD" + // EnvSwiftUsername is the environment variable to specify the OpenStack Swift username. + EnvSwiftUsername = "SWIFT_USERNAME" + + // KeyAlibabaCloudAccessKeyID is the secret data key for the AlibabaCloud client id to access S3. + KeyAlibabaCloudAccessKeyID = "access_key_id" + // KeyAlibabaCloudSecretAccessKey is the secret data key for the AlibabaCloud client secret to access S3. + KeyAlibabaCloudSecretAccessKey = "secret_access_key" + // KeyAlibabaCloudBucket is the secret data key for the S3 bucket name. + KeyAlibabaCloudBucket = "bucket" + // KeyAlibabaCloudEndpoint is the secret data key for the S3 endpoint URL. + KeyAlibabaCloudEndpoint = "endpoint" + + // KeyAWSAccessKeyID is the secret data key for the AWS client id to access S3. + KeyAWSAccessKeyID = "access_key_id" + // KeyAWSAccessKeySecret is the secret data key for the AWS client secret to access S3. + KeyAWSAccessKeySecret = "access_key_secret" + // KeyAWSBucketNames is the secret data key for the AWS S3 bucket names. + KeyAWSBucketNames = "bucketnames" + // KeyAWSEndpoint is the secret data key for the AWS endpoint URL. + KeyAWSEndpoint = "endpoint" + // KeyAWSRegion is the secret data key for the AWS region. + KeyAWSRegion = "region" + // KeyAWSSSEType is the secret data key for the AWS server-side encryption type. + KeyAWSSSEType = "sse_type" + // KeyAWSSseKmsEncryptionContext is the secret data key for the AWS SSE KMS encryption context. + KeyAWSSseKmsEncryptionContext = "sse_kms_encryption_context" + // KeyAWSSseKmsKeyID is the secret data key for the AWS SSE KMS key id. + KeyAWSSseKmsKeyID = "sse_kms_key_id" + + // KeyAzureStorageAccountKey is the secret data key for the Azure storage account key. + KeyAzureStorageAccountKey = "account_key" + // KeyAzureStorageAccountName is the secret data key for the Azure storage account name. + KeyAzureStorageAccountName = "account_name" + // KeyAzureStorageContainerName is the secret data key for the Azure storage container name. + KeyAzureStorageContainerName = "container" + // KeyAzureStorageEndpointSuffix is the secret data key for the Azure storage endpoint URL suffix. + KeyAzureStorageEndpointSuffix = "endpoint_suffix" + // KeyAzureEnvironmentName is the secret data key for the Azure cloud environment name. + KeyAzureEnvironmentName = "environment" + + // KeyGCPStorageBucketName is the secret data key for the GCS bucket name. + KeyGCPStorageBucketName = "bucketname" + // KeyGCPServiceAccountKeyFilename is the service account key filename containing the Google authentication credentials. + KeyGCPServiceAccountKeyFilename = "key.json" + + // KeySwiftAuthURL is the secret data key for the OpenStack Swift authentication URL. + KeySwiftAuthURL = "auth_url" + // KeySwiftContainerName is the secret data key for the OpenStack Swift container name. + KeySwiftContainerName = "container_name" + // KeySwiftDomainID is the secret data key for the OpenStack domain ID. + KeySwiftDomainID = "domain_id" + // KeySwiftDomainName is the secret data key for the OpenStack domain name. + KeySwiftDomainName = "domain_name" + // KeySwiftPassword is the secret data key for the OpenStack Swift password. + KeySwiftPassword = "password" + // KeySwiftProjectDomainId is the secret data key for the OpenStack project's domain id. + KeySwiftProjectDomainId = "project_domain_id" + // KeySwiftProjectDomainName is the secret data key for the OpenStack project's domain name. + KeySwiftProjectDomainName = "project_domain_name" + // KeySwiftProjectID is the secret data key for the OpenStack project id. + KeySwiftProjectID = "project_id" + // KeySwiftProjectName is the secret data key for the OpenStack project name. + KeySwiftProjectName = "project_name" + // KeySwiftRegion is the secret data key for the OpenStack Swift region. + KeySwiftRegion = "region" + // KeySwiftUserDomainID is the secret data key for the OpenStack Swift user domain id. + KeySwiftUserDomainID = "user_domain_id" + // KeySwiftUserDomainID is the secret data key for the OpenStack Swift user domain name. + KeySwiftUserDomainName = "user_domain_name" + // KeySwiftUserID is the secret data key for the OpenStack Swift user id. + KeySwiftUserID = "user_id" + // KeySwiftPassword is the secret data key for the OpenStack Swift password. + KeySwiftUsername = "username" + + secretDirectory = "/etc/storage/secrets" + storageTLSVolume = "storage-tls" + caDirectory = "/etc/storage/ca" +) diff --git a/operator/internal/manifests/var.go b/operator/internal/manifests/var.go index a259121467cf3..b837352a265c4 100644 --- a/operator/internal/manifests/var.go +++ b/operator/internal/manifests/var.go @@ -74,6 +74,8 @@ const ( AnnotationCertRotationRequiredAt string = "loki.grafana.com/certRotationRequiredAt" // AnnotationLokiConfigHash stores the last SHA1 hash of the loki configuration AnnotationLokiConfigHash string = "loki.grafana.com/config-hash" + // AnnotationLokiObjectStoreHash stores the last SHA1 hash of the loki object storage credetials. + AnnotationLokiObjectStoreHash string = "loki.grafana.com/object-store-hash" // LabelCompactorComponent is the label value for the compactor component LabelCompactorComponent string = "compactor" @@ -123,11 +125,18 @@ var ( volumeFileSystemMode = corev1.PersistentVolumeFilesystem ) -func commonAnnotations(configHash, rotationRequiredAt string) map[string]string { - return map[string]string{ - AnnotationLokiConfigHash: configHash, +func commonAnnotations(configHash, objStoreHash, rotationRequiredAt string) map[string]string { + a := map[string]string{ + AnnotationLokiConfigHash: configHash, + AnnotationCertRotationRequiredAt: rotationRequiredAt, } + + if objStoreHash != "" { + a[AnnotationLokiObjectStoreHash] = objStoreHash + } + + return a } func commonLabels(stackName string) map[string]string {