diff --git a/operator/CHANGELOG.md b/operator/CHANGELOG.md index 1a6cb57d66e67..61cb05abb8f10 100644 --- a/operator/CHANGELOG.md +++ b/operator/CHANGELOG.md @@ -1,5 +1,6 @@ ## Main +- [11357](https://github.com/grafana/loki/pull/11357) **periklis**: Fix storing authentication credentials in the Loki ConfigMap - [11393](https://github.com/grafana/loki/pull/11393) **periklis**: Add infra annotations for OpenShift based deployments - [11094](https://github.com/grafana/loki/pull/11094) **periklis**: Add support for blocking queries per tenant - [11288](https://github.com/grafana/loki/pull/11288) **periklis**: Fix custom CA for object-store in ruler component diff --git a/operator/cmd/loki-broker/main.go b/operator/cmd/loki-broker/main.go index aa4b66acb52d3..232a1e698a219 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/docs/lokistack/object_storage.md b/operator/docs/lokistack/object_storage.md index 8f118bc869677..44d756c962b9b 100644 --- a/operator/docs/lokistack/object_storage.md +++ b/operator/docs/lokistack/object_storage.md @@ -51,7 +51,7 @@ _Note_: Upon setting up LokiStack for any object storage provider, you should co --from-literal=sse_kms_encryption_context="" ``` - See also official docs on [AWS KMS Key ID](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id) and [AWS KMS Encryption Context](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#encrypt_context). + See also official docs on [AWS KMS Key ID](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id) and [AWS KMS Encryption Context](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#encrypt_context) (**Note:** Only content without newlines allowed, because it is exposed via environment variable to the containers). or with `SSE-S3` encryption diff --git a/operator/internal/handlers/internal/storage/secrets.go b/operator/internal/handlers/internal/storage/secrets.go index d5f195e3c0547..0e027be8f3260 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" corev1 "k8s.io/api/core/v1" @@ -8,11 +12,18 @@ import ( "github.com/grafana/loki/operator/internal/manifests/storage" ) +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, } @@ -37,48 +48,75 @@ 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) } // Extract and validate optional fields - endpointSuffix := s.Data["endpoint_suffix"] + endpointSuffix := s.Data[storage.KeyAzureStorageEndpointSuffix] return &storage.AzureStorageConfig{ Env: string(env), Container: string(container), - AccountName: string(name), - AccountKey: string(key), EndpointSuffix: string(endpointSuffix), }, 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{ @@ -88,25 +126,25 @@ 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) } - 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] sseCfg, err := extractS3SSEConfig(s.Data) if err != nil { @@ -114,12 +152,10 @@ func extractS3ConfigSecret(s *corev1.Secret) (*storage.S3StorageConfig, error) { } return &storage.S3StorageConfig{ - Endpoint: string(endpoint), - Buckets: string(buckets), - AccessKeyID: string(id), - AccessKeySecret: string(secret), - Region: string(region), - SSE: sseCfg, + Endpoint: string(endpoint), + Buckets: string(buckets), + Region: string(region), + SSE: sseCfg, }, nil } @@ -129,12 +165,12 @@ func extractS3SSEConfig(d map[string][]byte) (storage.S3SSEConfig, error) { kmsKeyId, kmsEncryptionCtx string ) - switch sseType = storage.S3SSEType(d["sse_type"]); sseType { + switch sseType = storage.S3SSEType(d[storage.KeyAWSSSEType]); sseType { case storage.SSEKMSType: - kmsEncryptionCtx = string(d["sse_kms_encryption_context"]) - kmsKeyId = string(d["sse_kms_key_id"]) + kmsEncryptionCtx = string(d[storage.KeyAWSSseKmsEncryptionContext]) + kmsKeyId = string(d[storage.KeyAWSSseKmsKeyID]) if kmsKeyId == "" { - return storage.S3SSEConfig{}, kverrors.New("missing secret field", "field", "sse_kms_key_id") + return storage.S3SSEConfig{}, kverrors.New("missing secret field", "field", storage.KeyAWSSseKmsKeyID) } case storage.SSES3Type: @@ -142,7 +178,7 @@ func extractS3SSEConfig(d map[string][]byte) (storage.S3SSEConfig, error) { return storage.S3SSEConfig{}, nil default: - return storage.S3SSEConfig{}, kverrors.New("unsupported secret field value (Supported: SSE-KMS, SSE-S3)", "field", "sse_type", "value", sseType) + return storage.S3SSEConfig{}, kverrors.New("unsupported secret field value (Supported: SSE-KMS, SSE-S3)", "field", storage.KeyAWSSSEType, "value", sseType) } return storage.S3SSEConfig{ @@ -154,57 +190,55 @@ func extractS3SSEConfig(d map[string][]byte) (storage.S3SSEConfig, error) { 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), @@ -218,28 +252,25 @@ func extractSwiftConfigSecret(s *corev1.Secret) (*storage.SwiftStorageConfig, er func extractAlibabaCloudConfigSecret(s *corev1.Secret) (*storage.AlibabaCloudStorageConfig, error) { // Extract and validate mandatory fields - endpoint := s.Data["endpoint"] + endpoint := s.Data[storage.KeyAlibabaCloudEndpoint] if len(endpoint) == 0 { - return nil, kverrors.New("missing secret field", "field", "endpoint") + return nil, kverrors.New("missing secret field", "field", storage.KeyAlibabaCloudEndpoint) } - bucket := s.Data["bucket"] + bucket := s.Data[storage.KeyAlibabaCloudBucket] if len(bucket) == 0 { - return nil, kverrors.New("missing secret field", "field", "bucket") + return nil, kverrors.New("missing secret field", "field", storage.KeyAlibabaCloudBucket) } - // TODO buckets are comma-separated list - id := s.Data["access_key_id"] + id := s.Data[storage.KeyAlibabaCloudAccessKeyID] if len(id) == 0 { - return nil, kverrors.New("missing secret field", "field", "access_key_id") + return nil, kverrors.New("missing secret field", "field", storage.KeyAlibabaCloudAccessKeyID) } - secret := s.Data["secret_access_key"] + secret := s.Data[storage.KeyAlibabaCloudSecretAccessKey] if len(secret) == 0 { - return nil, kverrors.New("missing secret field", "field", "secret_access_key") + return nil, kverrors.New("missing secret field", "field", storage.KeyAlibabaCloudSecretAccessKey) } return &storage.AlibabaCloudStorageConfig{ - Endpoint: string(endpoint), - Bucket: string(bucket), - AccessKeyID: string(id), - SecretAccessKey: string(secret), + Endpoint: string(endpoint), + Bucket: string(bucket), }, nil } diff --git a/operator/internal/handlers/internal/storage/secrets_test.go b/operator/internal/handlers/internal/storage/secrets_test.go index a9e474414e44f..eea31fbd522c8 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 mandatory set", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "environment": []byte("here"), "container": []byte("this,that"), @@ -65,6 +119,7 @@ func TestAzureExtract(t *testing.T) { { name: "all set including optional", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "environment": []byte("here"), "container": []byte("this,that"), @@ -80,9 +135,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) @@ -115,6 +173,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\"}"), @@ -127,7 +186,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) } @@ -210,6 +269,7 @@ func TestS3Extract(t *testing.T) { { name: "all set with SSE-KMS", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "endpoint": []byte("here"), "bucketnames": []byte("this,that"), @@ -223,6 +283,7 @@ func TestS3Extract(t *testing.T) { { name: "all set with SSE-KMS with encryption context", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "endpoint": []byte("here"), "bucketnames": []byte("this,that"), @@ -237,6 +298,7 @@ func TestS3Extract(t *testing.T) { { name: "all set with SSE-S3", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "endpoint": []byte("here"), "bucketnames": []byte("this,that"), @@ -249,6 +311,7 @@ func TestS3Extract(t *testing.T) { { name: "all set without SSE", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "endpoint": []byte("here"), "bucketnames": []byte("this,that"), @@ -263,9 +326,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) @@ -389,6 +455,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"), @@ -408,9 +475,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) @@ -464,6 +534,7 @@ func TestAlibabaCloudExtract(t *testing.T) { { name: "all set", secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, Data: map[string][]byte{ "endpoint": []byte("here"), "bucket": []byte("this,that"), @@ -478,9 +549,12 @@ func TestAlibabaCloudExtract(t *testing.T) { t.Run(tst.name, func(t *testing.T) { t.Parallel() - _, err := storage.ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretAlibabaCloud) + opts, err := ExtractSecret(tst.secret, lokiv1.ObjectStorageSecretAlibabaCloud) if !tst.wantErr { require.NoError(t, err) + require.NotEmpty(t, opts.SecretName) + require.NotEmpty(t, opts.SecretSHA1) + require.Equal(t, opts.SharedStore, lokiv1.ObjectStorageSecretAlibabaCloud) } if tst.wantErr { require.NotNil(t, err) diff --git a/operator/internal/manifests/compactor.go b/operator/internal/manifests/compactor.go index b67997d29fe9c..0362b8d40010c 100644 --- a/operator/internal/manifests/compactor.go +++ b/operator/internal/manifests/compactor.go @@ -67,7 +67,7 @@ func BuildCompactor(opts Options) ([]client.Object, error) { // NewCompactorStatefulSet creates a statefulset object for a compactor. 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) podSpec := corev1.PodSpec{ Affinity: configureAffinity(LabelCompactorComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.Compactor), Volumes: []corev1.Volume{ 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 33608b83994e6..4ec0b728d916d 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 3d60ae50144cb..ea856762cb6ac 100644 --- a/operator/internal/manifests/distributor.go +++ b/operator/internal/manifests/distributor.go @@ -67,7 +67,7 @@ func BuildDistributor(opts Options) ([]client.Object, error) { // NewDistributorDeployment creates a deployment object for a distributor 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) podSpec := corev1.PodSpec{ Affinity: configureAffinity(LabelDistributorComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.Distributor), Volumes: []corev1.Volume{ diff --git a/operator/internal/manifests/distributor_test.go b/operator/internal/manifests/distributor_test.go index 638e33d9cd269..a3b9e6abc932d 100644 --- a/operator/internal/manifests/distributor_test.go +++ b/operator/internal/manifests/distributor_test.go @@ -11,6 +11,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" ) func TestNewDistributorDeployment_SelectorMatchesLabels(t *testing.T) { @@ -47,10 +48,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) { @@ -67,10 +88,9 @@ 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") } func TestBuildDistributor_PodDisruptionBudget(t *testing.T) { diff --git a/operator/internal/manifests/gateway.go b/operator/internal/manifests/gateway.go index 08c8822177643..03bce6453f363 100644 --- a/operator/internal/manifests/gateway.go +++ b/operator/internal/manifests/gateway.go @@ -114,7 +114,7 @@ func BuildGateway(opts Options) ([]client.Object, error) { // NewGatewayDeployment creates a deployment object for a lokiStack-gateway func NewGatewayDeployment(opts Options, sha1C string) *appsv1.Deployment { l := ComponentLabels(LabelGatewayComponent, opts.Name) - a := commonAnnotations(sha1C, opts.CertRotationRequiredAt) + a := commonAnnotations(sha1C, "", opts.CertRotationRequiredAt) podSpec := corev1.PodSpec{ ServiceAccountName: GatewayName(opts.Name), Affinity: configureAffinity(LabelGatewayComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.Gateway), diff --git a/operator/internal/manifests/gateway_test.go b/operator/internal/manifests/gateway_test.go index 720b72f5b2fdf..3c58e00d42cf0 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" @@ -51,10 +52,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) { @@ -134,10 +171,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 16b8d6516743d..3f43075875108 100644 --- a/operator/internal/manifests/indexgateway.go +++ b/operator/internal/manifests/indexgateway.go @@ -73,7 +73,7 @@ func BuildIndexGateway(opts Options) ([]client.Object, error) { // NewIndexGatewayStatefulSet creates a statefulset object for an index-gateway 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) podSpec := corev1.PodSpec{ Affinity: configureAffinity(LabelIndexGatewayComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.IndexGateway), Volumes: []corev1.Volume{ diff --git a/operator/internal/manifests/indexgateway_test.go b/operator/internal/manifests/indexgateway_test.go index b25522b538073..93ab7a033e147 100644 --- a/operator/internal/manifests/indexgateway_test.go +++ b/operator/internal/manifests/indexgateway_test.go @@ -10,6 +10,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" ) func TestNewIndexGatewayStatefulSet_HasTemplateConfigHashAnnotation(t *testing.T) { @@ -27,10 +28,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) { @@ -47,10 +69,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 75934b66836da..0f20ce84776fd 100644 --- a/operator/internal/manifests/ingester.go +++ b/operator/internal/manifests/ingester.go @@ -73,7 +73,7 @@ func BuildIngester(opts Options) ([]client.Object, error) { // NewIngesterStatefulSet creates a deployment object for an ingester 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) podSpec := corev1.PodSpec{ Affinity: configureAffinity(LabelIngesterComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.Ingester), Volumes: []corev1.Volume{ diff --git a/operator/internal/manifests/ingester_test.go b/operator/internal/manifests/ingester_test.go index 605c5e5c588f3..83b0d94111301 100644 --- a/operator/internal/manifests/ingester_test.go +++ b/operator/internal/manifests/ingester_test.go @@ -13,6 +13,7 @@ import ( v1 "github.com/grafana/loki/operator/apis/config/v1" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" "github.com/grafana/loki/operator/internal/manifests/internal" + "github.com/grafana/loki/operator/internal/manifests/storage" ) func TestNewIngesterStatefulSet_HasTemplateConfigHashAnnotation(t *testing.T) { @@ -30,10 +31,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) { @@ -50,10 +72,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 8b448008c82d8..537ec84bf71a5 100644 --- a/operator/internal/manifests/internal/config/build_test.go +++ b/operator/internal/manifests/internal/config/build_test.go @@ -28,8 +28,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: @@ -245,11 +245,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{ { @@ -287,8 +285,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: @@ -580,11 +578,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{ { @@ -666,11 +662,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{ { @@ -702,8 +696,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: @@ -1020,11 +1014,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{ { @@ -1062,8 +1054,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: @@ -1381,11 +1373,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{ { @@ -1423,8 +1413,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: @@ -1772,11 +1762,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{ { @@ -1814,8 +1802,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: @@ -2109,11 +2097,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{ { @@ -2154,8 +2140,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: @@ -2533,11 +2519,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{ { @@ -2575,8 +2559,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: @@ -2881,11 +2865,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{ { @@ -2923,8 +2905,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: @@ -3379,11 +3361,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{ { @@ -3421,8 +3401,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: @@ -3641,11 +3621,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{ { @@ -3683,8 +3661,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: @@ -3905,11 +3883,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{ { @@ -3947,8 +3923,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: @@ -4167,11 +4143,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{ { @@ -4209,13 +4183,13 @@ 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} sse: type: SSE-KMS kms_key_id: test kms_encryption_context: | - {"key": "value", "another":"value1"} + ${AWS_SSE_KMS_ENCRYPTION_CONTEXT} s3forcepathstyle: true compactor_grpc_address: loki-compactor-grpc-lokistack-dev.default.svc.cluster.local:9095 ring: @@ -4466,11 +4440,10 @@ 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", + SSE: storage.S3SSEConfig{ Type: storage.SSEKMSType, KMSKeyID: "test", @@ -4512,8 +4485,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} sse: type: SSE-S3 s3forcepathstyle: true @@ -4766,11 +4739,10 @@ 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", + SSE: storage.S3SSEConfig{ Type: storage.SSES3Type, KMSKeyID: "test", @@ -4812,8 +4784,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: @@ -5025,11 +4997,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{ { @@ -5116,11 +5086,9 @@ func defaultOptions() Options { 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{ { @@ -5305,8 +5273,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: diff --git a/operator/internal/manifests/internal/config/loki-config.yaml b/operator/internal/manifests/internal/config/loki-config.yaml index 0ea4348367835..a11191627d375 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} {{- with .EndpointSuffix }} endpoint_suffix: {{ . }} {{- end }} @@ -27,8 +27,8 @@ 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} {{- with .SSE }} {{- if .Type }} sse: @@ -37,7 +37,7 @@ common: kms_key_id: {{ .KMSKeyID }} {{- with .KMSEncryptionContext }} kms_encryption_context: | - {{ . }} + ${AWS_SSE_KMS_ENCRYPTION_CONTEXT} {{- end }} {{- end}} {{- end }} @@ -47,11 +47,11 @@ common: {{- 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 }} @@ -65,8 +65,8 @@ common: alibabacloud: bucket: {{ .Bucket }} endpoint: {{ .Endpoint }} - access_key_id: {{ .AccessKeyID }} - secret_access_key: {{ .SecretAccessKey }} + access_key_id: ${ALIBABA_CLOUD_ACCESS_KEY_ID} + secret_access_key: ${ALIBABA_CLOUD_ACCESS_KEY_SECRET} {{- end }} compactor_grpc_address: {{ .Compactor.FQDN }}:{{ .Compactor.Port }} {{- with .GossipRing }} diff --git a/operator/internal/manifests/querier.go b/operator/internal/manifests/querier.go index a560c025d9e2b..b75997e4553c5 100644 --- a/operator/internal/manifests/querier.go +++ b/operator/internal/manifests/querier.go @@ -73,7 +73,7 @@ func BuildQuerier(opts Options) ([]client.Object, error) { // NewQuerierDeployment creates a deployment object for a querier 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) podSpec := corev1.PodSpec{ Affinity: configureAffinity(LabelQuerierComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.Querier), Volumes: []corev1.Volume{ diff --git a/operator/internal/manifests/querier_test.go b/operator/internal/manifests/querier_test.go index 89717edabaebc..b9d085866b784 100644 --- a/operator/internal/manifests/querier_test.go +++ b/operator/internal/manifests/querier_test.go @@ -11,6 +11,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" ) func TestNewQuerierDeployment_HasTemplateConfigHashAnnotation(t *testing.T) { @@ -28,10 +29,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) { @@ -48,10 +70,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 fab6f50bcb3a7..119f28f7e4f72 100644 --- a/operator/internal/manifests/query-frontend.go +++ b/operator/internal/manifests/query-frontend.go @@ -67,7 +67,7 @@ func BuildQueryFrontend(opts Options) ([]client.Object, error) { // NewQueryFrontendDeployment creates a deployment object for a query-frontend 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) podSpec := corev1.PodSpec{ Affinity: configureAffinity(LabelQueryFrontendComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.QueryFrontend), Volumes: []corev1.Volume{ diff --git a/operator/internal/manifests/query-frontend_test.go b/operator/internal/manifests/query-frontend_test.go index 6615e078a2bb0..d11fb968ce3ac 100644 --- a/operator/internal/manifests/query-frontend_test.go +++ b/operator/internal/manifests/query-frontend_test.go @@ -10,6 +10,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/grafana/loki/operator/internal/manifests/storage" ) func TestNewQueryFrontendDeployment_SelectorMatchesLabels(t *testing.T) { @@ -44,10 +45,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) { @@ -64,10 +86,9 @@ 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") } func TestBuildQueryFrontend_PodDisruptionBudget(t *testing.T) { diff --git a/operator/internal/manifests/ruler.go b/operator/internal/manifests/ruler.go index 4a432f7cc4339..8e44f5834fef1 100644 --- a/operator/internal/manifests/ruler.go +++ b/operator/internal/manifests/ruler.go @@ -97,7 +97,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) podSpec := corev1.PodSpec{ Affinity: configureAffinity(LabelRulerComponent, opts.Name, opts.Gates.DefaultNodeAffinity, opts.Stack.Template.Ruler), Volumes: []corev1.Volume{ diff --git a/operator/internal/manifests/ruler_test.go b/operator/internal/manifests/ruler_test.go index b40a9c4de5d9a..b753dc090de8c 100644 --- a/operator/internal/manifests/ruler_test.go +++ b/operator/internal/manifests/ruler_test.go @@ -12,6 +12,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" ) func TestNewRulerStatefulSet_HasTemplateConfigHashAnnotation(t *testing.T) { @@ -29,10 +30,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) { @@ -49,10 +71,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..8c953ae1c5951 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.ObjectStorageSecretAlibabaCloud, 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.ObjectStorageSecretAlibabaCloud, 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,133 @@ 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.ObjectStorageSecretAlibabaCloud: + storeEnvVars = []corev1.EnvVar{ + { + Name: EnvAlibabaCloudAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeyAlibabaCloudAccessKeyID, + }, + }, + }, + { + Name: EnvAlibabaCloudAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeyAlibabaCloudSecretAccessKey, + }, + }, + }, + } + 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, + }, + }, + }, + } + + if opts.S3 != nil && opts.S3.SSE.Type == SSEKMSType && opts.S3.SSE.KMSEncryptionContext != "" { + storeEnvVars = append(storeEnvVars, corev1.EnvVar{ + Name: EnvAWSSseKmsEncryptionContext, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + Key: KeyAWSSseKmsEncryptionContext, + }, + }, + }) + } + + 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..6614453df22d3 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,26 +8,564 @@ 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 AlibabaCloud", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretAlibabaCloud, + }, + 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: EnvAlibabaCloudAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAlibabaCloudAccessKeyID, + }, + }, + }, + { + Name: EnvAlibabaCloudAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAlibabaCloudSecretAccessKey, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + desc: "object storage Azure", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretAzure, + }, + 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: 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", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + desc: "object storage GCS", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretGCS, + }, + 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: EnvGoogleApplicationCredentials, + Value: "/etc/storage/secrets/key.json", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + 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 S3 with SSE KMS encryption context", + opts: Options{ SecretName: "test", SharedStore: lokiv1.ObjectStorageSecretS3, + S3: &S3StorageConfig{ + SSE: S3SSEConfig{ + Type: SSEKMSType, + KMSEncryptionContext: "test", + }, + }, + }, + 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, + }, + }, + }, + { + Name: EnvAWSSseKmsEncryptionContext, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSSseKmsEncryptionContext, + }, + }, + }, + }, + }, + }, + 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 := ConfigureDeployment(tc.dpl, tc.opts) + require.NoError(t, err) + require.Equal(t, tc.want, tc.dpl) + }) + } +} + +func TestConfigureStatefulSetForStorageType(t *testing.T) { + type tt struct { + desc string + opts Options + sts *appsv1.StatefulSet + want *appsv1.StatefulSet + } + + tc := []tt{ + { + desc: "object storage AlibabaCloud", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretAlibabaCloud, + }, + 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: EnvAlibabaCloudAccessKeyID, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAlibabaCloudAccessKeyID, + }, + }, + }, + { + Name: EnvAlibabaCloudAccessKeySecret, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAlibabaCloudSecretAccessKey, + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "test", + }, + }, + }, + }, + }, + }, + }, }, - dpl: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ + }, + { + desc: "object storage Azure", + opts: Options{ + SecretName: "test", + SharedStore: lokiv1.ObjectStorageSecretAzure, + }, + sts: &appsv1.StatefulSet{ + Spec: appsv1.StatefulSetSpec{ Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -39,13 +577,54 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, }, }, - want: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ + 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: 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,12 +634,12 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, { desc: "object storage GCS", - opts: storage.Options{ + opts: Options{ SecretName: "test", SharedStore: lokiv1.ObjectStorageSecretGCS, }, - dpl: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ + sts: &appsv1.StatefulSet{ + Spec: appsv1.StatefulSetSpec{ Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -72,8 +651,8 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, }, }, - want: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ + want: &appsv1.StatefulSet{ + Spec: appsv1.StatefulSetSpec{ Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -88,7 +667,7 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, Env: []corev1.EnvVar{ { - Name: storage.EnvGoogleApplicationCredentials, + Name: EnvGoogleApplicationCredentials, Value: "/etc/storage/secrets/key.json", }, }, @@ -109,31 +688,9 @@ func TestConfigureDeploymentForStorageType(t *testing.T) { }, }, }, - } - - for _, tc := range tc { - tc := tc - t.Run(tc.desc, func(t *testing.T) { - t.Parallel() - err := storage.ConfigureDeployment(tc.dpl, tc.opts) - require.NoError(t, err) - require.Equal(t, tc.want, tc.dpl) - }) - } -} - -func TestConfigureStatefulSetForStorageType(t *testing.T) { - type tt struct { - desc string - opts storage.Options - sts *appsv1.StatefulSet - want *appsv1.StatefulSet - } - - tc := []tt{ { - desc: "object storage other than GCS", - opts: storage.Options{ + desc: "object storage S3", + opts: Options{ SecretName: "test", SharedStore: lokiv1.ObjectStorageSecretS3, }, @@ -157,6 +714,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: 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", + }, + }, }, }, }, @@ -165,10 +763,16 @@ func TestConfigureStatefulSetForStorageType(t *testing.T) { }, }, { - desc: "object storage GCS", - opts: storage.Options{ + desc: "object storage S3 with SSE KMS encryption Context", + opts: Options{ SecretName: "test", - SharedStore: lokiv1.ObjectStorageSecretGCS, + SharedStore: lokiv1.ObjectStorageSecretS3, + S3: &S3StorageConfig{ + SSE: S3SSEConfig{ + Type: SSEKMSType, + KMSEncryptionContext: "test", + }, + }, }, sts: &appsv1.StatefulSet{ Spec: appsv1.StatefulSetSpec{ @@ -199,8 +803,111 @@ func TestConfigureStatefulSetForStorageType(t *testing.T) { }, Env: []corev1.EnvVar{ { - Name: storage.EnvGoogleApplicationCredentials, - Value: "/etc/storage/secrets/key.json", + 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, + }, + }, + }, + { + Name: EnvAWSSseKmsEncryptionContext, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test", + }, + Key: KeyAWSSseKmsEncryptionContext, + }, + }, + }, + }, + }, + }, + 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, + }, + }, }, }, }, @@ -226,7 +933,7 @@ func TestConfigureStatefulSetForStorageType(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) }) @@ -236,7 +943,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 +951,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 +975,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 +1025,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 +1054,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 +1068,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 +1125,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 +1135,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 +1143,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 +1170,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 +1220,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 +1249,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 +1263,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 +1320,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 b5d47ce593729..8767f576848a9 100644 --- a/operator/internal/manifests/storage/options.go +++ b/operator/internal/manifests/storage/options.go @@ -17,6 +17,7 @@ type Options struct { AlibabaCloud *AlibabaCloudStorageConfig SecretName string + SecretSHA1 string TLS *TLSConfig } @@ -24,8 +25,6 @@ type Options struct { type AzureStorageConfig struct { Env string Container string - AccountName string - AccountKey string EndpointSuffix string } @@ -36,12 +35,10 @@ type GCSStorageConfig struct { // S3StorageConfig for S3 storage config type S3StorageConfig struct { - Endpoint string - Region string - Buckets string - AccessKeyID string - AccessKeySecret string - SSE S3SSEConfig + Endpoint string + Region string + Buckets string + SSE S3SSEConfig } type S3SSEType string @@ -60,11 +57,9 @@ type S3SSEConfig struct { // 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 @@ -77,10 +72,8 @@ type SwiftStorageConfig struct { // AlibabaCloudStorageConfig for AlibabaCloud storage config type AlibabaCloudStorageConfig struct { - Endpoint string - Bucket string - AccessKeyID string - SecretAccessKey string + Endpoint string + Bucket string } // TLSConfig for object storage endpoints. Currently supported only by: 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 3f4644be4f322..e4d7c0d1a9b4f 100644 --- a/operator/internal/manifests/var.go +++ b/operator/internal/manifests/var.go @@ -76,6 +76,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" @@ -130,11 +132,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 {