mirror of https://github.com/grafana/loki
operator: Refactor CreateOrUpdateLokiStack handler (#11592)
Co-authored-by: Robert Jacob <xperimental@solidproject.de>pull/11511/head^2
parent
edba360e43
commit
0065fd6e95
@ -0,0 +1,87 @@ |
||||
package gateway |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/go-logr/logr" |
||||
|
||||
configv1 "github.com/grafana/loki/operator/apis/config/v1" |
||||
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" |
||||
"github.com/grafana/loki/operator/internal/external/k8s" |
||||
"github.com/grafana/loki/operator/internal/handlers/internal/openshift" |
||||
"github.com/grafana/loki/operator/internal/manifests" |
||||
"github.com/grafana/loki/operator/internal/status" |
||||
) |
||||
|
||||
// BuildOptions returns the options needed to generate Kubernetes resource
|
||||
// manifests for the lokistack-gateway.
|
||||
// The returned error can be a status.DegradedError in the following cases:
|
||||
// - The tenants spec is missing.
|
||||
// - The tenants spec is invalid.
|
||||
func BuildOptions(ctx context.Context, log logr.Logger, k k8s.Client, stack *lokiv1.LokiStack, fg configv1.FeatureGates) (string, manifests.Tenants, error) { |
||||
var ( |
||||
err error |
||||
baseDomain string |
||||
secrets []*manifests.TenantSecrets |
||||
configs map[string]manifests.TenantConfig |
||||
tenants manifests.Tenants |
||||
) |
||||
|
||||
if !fg.LokiStackGateway { |
||||
return "", tenants, nil |
||||
} |
||||
|
||||
if stack.Spec.Tenants == nil { |
||||
return "", tenants, &status.DegradedError{ |
||||
Message: "Invalid tenants configuration: TenantsSpec cannot be nil when gateway flag is enabled", |
||||
Reason: lokiv1.ReasonInvalidTenantsConfiguration, |
||||
Requeue: false, |
||||
} |
||||
} |
||||
|
||||
if err = validateModes(stack); err != nil { |
||||
return "", tenants, &status.DegradedError{ |
||||
Message: fmt.Sprintf("Invalid tenants configuration: %s", err), |
||||
Reason: lokiv1.ReasonInvalidTenantsConfiguration, |
||||
Requeue: false, |
||||
} |
||||
} |
||||
|
||||
switch stack.Spec.Tenants.Mode { |
||||
case lokiv1.OpenshiftLogging, lokiv1.OpenshiftNetwork: |
||||
baseDomain, err = getOpenShiftBaseDomain(ctx, k) |
||||
if err != nil { |
||||
return "", tenants, err |
||||
} |
||||
|
||||
if stack.Spec.Proxy == nil { |
||||
// If the LokiStack has no proxy set but there is a cluster-wide proxy setting,
|
||||
// set the LokiStack proxy to that.
|
||||
ocpProxy, proxyErr := openshift.GetProxy(ctx, k) |
||||
if proxyErr != nil { |
||||
return "", tenants, proxyErr |
||||
} |
||||
|
||||
stack.Spec.Proxy = ocpProxy |
||||
} |
||||
default: |
||||
secrets, err = getTenantSecrets(ctx, k, stack) |
||||
if err != nil { |
||||
return "", tenants, err |
||||
} |
||||
} |
||||
|
||||
// extract the existing tenant's id, cookieSecret if exists, otherwise create new.
|
||||
configs, err = getTenantConfigFromSecret(ctx, k, stack) |
||||
if err != nil { |
||||
log.Error(err, "error in getting tenant secret data") |
||||
} |
||||
|
||||
tenants = manifests.Tenants{ |
||||
Secrets: secrets, |
||||
Configs: configs, |
||||
} |
||||
|
||||
return baseDomain, tenants, nil |
||||
} |
||||
@ -0,0 +1,390 @@ |
||||
package gateway |
||||
|
||||
import ( |
||||
"context" |
||||
"io" |
||||
"testing" |
||||
|
||||
"github.com/ViaQ/logerr/v2/log" |
||||
"github.com/stretchr/testify/require" |
||||
corev1 "k8s.io/api/core/v1" |
||||
apierrors "k8s.io/apimachinery/pkg/api/errors" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime/schema" |
||||
"k8s.io/apimachinery/pkg/types" |
||||
ctrl "sigs.k8s.io/controller-runtime" |
||||
"sigs.k8s.io/controller-runtime/pkg/client" |
||||
|
||||
configv1 "github.com/grafana/loki/operator/apis/config/v1" |
||||
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" |
||||
"github.com/grafana/loki/operator/internal/external/k8s/k8sfakes" |
||||
"github.com/grafana/loki/operator/internal/status" |
||||
) |
||||
|
||||
var ( |
||||
logger = log.NewLogger("testing", log.WithOutput(io.Discard)) |
||||
|
||||
defaultSecret = corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-secret", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{ |
||||
"endpoint": []byte("s3://your-endpoint"), |
||||
"region": []byte("a-region"), |
||||
"bucketnames": []byte("bucket1,bucket2"), |
||||
"access_key_id": []byte("a-secret-id"), |
||||
"access_key_secret": []byte("a-secret-key"), |
||||
}, |
||||
} |
||||
|
||||
defaultGatewaySecret = corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-gateway-secret", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{ |
||||
"clientID": []byte("client-123"), |
||||
"clientSecret": []byte("client-secret-xyz"), |
||||
"issuerCAPath": []byte("/tmp/test/ca.pem"), |
||||
}, |
||||
} |
||||
|
||||
invalidSecret = corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-secret", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{}, |
||||
} |
||||
) |
||||
|
||||
func TestBuildOptions_WhenInvalidTenantsConfiguration_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Invalid tenants configuration: mandatory configuration - missing OPA Url", |
||||
Reason: lokiv1.ReasonInvalidTenantsConfiguration, |
||||
Requeue: false, |
||||
} |
||||
|
||||
fg := configv1.FeatureGates{ |
||||
LokiStackGateway: true, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
Tenants: &lokiv1.TenantsSpec{ |
||||
Mode: "dynamic", |
||||
Authentication: []lokiv1.AuthenticationSpec{ |
||||
{ |
||||
TenantName: "test", |
||||
TenantID: "1234", |
||||
OIDC: &lokiv1.OIDCSpec{ |
||||
Secret: &lokiv1.TenantSecretSpec{ |
||||
Name: defaultGatewaySecret.Name, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Authorization: nil, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
_, isLokiStack := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(object, stack) |
||||
return nil |
||||
} |
||||
if defaultSecret.Name == name.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, _, err := BuildOptions(context.TODO(), logger, k, stack, fg) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_WhenMissingGatewaySecret_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Missing secrets for tenant test", |
||||
Reason: lokiv1.ReasonMissingGatewayTenantSecret, |
||||
Requeue: true, |
||||
} |
||||
|
||||
fg := configv1.FeatureGates{ |
||||
LokiStackGateway: true, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
Tenants: &lokiv1.TenantsSpec{ |
||||
Mode: "dynamic", |
||||
Authentication: []lokiv1.AuthenticationSpec{ |
||||
{ |
||||
TenantName: "test", |
||||
TenantID: "1234", |
||||
OIDC: &lokiv1.OIDCSpec{ |
||||
Secret: &lokiv1.TenantSecretSpec{ |
||||
Name: defaultGatewaySecret.Name, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Authorization: &lokiv1.AuthorizationSpec{ |
||||
OPA: &lokiv1.OPASpec{ |
||||
URL: "some-url", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
o, ok := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && ok { |
||||
k.SetClientObject(o, stack) |
||||
return nil |
||||
} |
||||
if defaultSecret.Name == name.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, _, err := BuildOptions(context.TODO(), logger, k, stack, fg) |
||||
|
||||
// make sure error is returned to re-trigger reconciliation
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_WhenInvalidGatewaySecret_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Invalid gateway tenant secret contents", |
||||
Reason: lokiv1.ReasonInvalidGatewayTenantSecret, |
||||
Requeue: true, |
||||
} |
||||
|
||||
fg := configv1.FeatureGates{ |
||||
LokiStackGateway: true, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
Tenants: &lokiv1.TenantsSpec{ |
||||
Mode: "dynamic", |
||||
Authentication: []lokiv1.AuthenticationSpec{ |
||||
{ |
||||
TenantName: "test", |
||||
TenantID: "1234", |
||||
OIDC: &lokiv1.OIDCSpec{ |
||||
Secret: &lokiv1.TenantSecretSpec{ |
||||
Name: invalidSecret.Name, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Authorization: &lokiv1.AuthorizationSpec{ |
||||
OPA: &lokiv1.OPASpec{ |
||||
URL: "some-url", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
o, ok := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && ok { |
||||
k.SetClientObject(o, stack) |
||||
return nil |
||||
} |
||||
if defaultSecret.Name == name.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
if name.Name == invalidSecret.Name { |
||||
k.SetClientObject(object, &invalidSecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, _, err := BuildOptions(context.TODO(), logger, k, stack, fg) |
||||
|
||||
// make sure error is returned to re-trigger reconciliation
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_MissingTenantsSpec_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Invalid tenants configuration: TenantsSpec cannot be nil when gateway flag is enabled", |
||||
Reason: lokiv1.ReasonInvalidTenantsConfiguration, |
||||
Requeue: false, |
||||
} |
||||
|
||||
fg := configv1.FeatureGates{ |
||||
LokiStackGateway: true, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
Tenants: nil, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
o, ok := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && ok { |
||||
k.SetClientObject(o, stack) |
||||
return nil |
||||
} |
||||
if defaultSecret.Name == name.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, _, err := BuildOptions(context.TODO(), logger, k, stack, fg) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
@ -0,0 +1,223 @@ |
||||
package rules |
||||
|
||||
import ( |
||||
"context" |
||||
"io" |
||||
"testing" |
||||
|
||||
"github.com/ViaQ/logerr/v2/log" |
||||
"github.com/stretchr/testify/assert" |
||||
"github.com/stretchr/testify/require" |
||||
appsv1 "k8s.io/api/apps/v1" |
||||
corev1 "k8s.io/api/core/v1" |
||||
apierrors "k8s.io/apimachinery/pkg/api/errors" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime/schema" |
||||
"k8s.io/apimachinery/pkg/types" |
||||
ctrl "sigs.k8s.io/controller-runtime" |
||||
"sigs.k8s.io/controller-runtime/pkg/client" |
||||
|
||||
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" |
||||
"github.com/grafana/loki/operator/internal/external/k8s/k8sfakes" |
||||
) |
||||
|
||||
var ( |
||||
logger = log.NewLogger("testing", log.WithOutput(io.Discard)) |
||||
|
||||
defaultSecret = corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-secret", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{ |
||||
"endpoint": []byte("s3://your-endpoint"), |
||||
"region": []byte("a-region"), |
||||
"bucketnames": []byte("bucket1,bucket2"), |
||||
"access_key_id": []byte("a-secret-id"), |
||||
"access_key_secret": []byte("a-secret-key"), |
||||
}, |
||||
} |
||||
|
||||
defaultGatewaySecret = corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-gateway-secret", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{ |
||||
"clientID": []byte("client-123"), |
||||
"clientSecret": []byte("client-secret-xyz"), |
||||
"issuerCAPath": []byte("/tmp/test/ca.pem"), |
||||
}, |
||||
} |
||||
|
||||
rulesCM = corev1.ConfigMap{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "ConfigMap", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack-rules-0", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
rulerSS = appsv1.StatefulSet{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "StatefulSet", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack-ruler", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
) |
||||
|
||||
func TestCleanup_RemovesRulerResourcesWhenDisabled(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
stack := lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
Rules: &lokiv1.RulesSpec{ |
||||
Enabled: true, |
||||
}, |
||||
Tenants: &lokiv1.TenantsSpec{ |
||||
Mode: "dynamic", |
||||
Authentication: []lokiv1.AuthenticationSpec{ |
||||
{ |
||||
TenantName: "test", |
||||
TenantID: "1234", |
||||
OIDC: &lokiv1.OIDCSpec{ |
||||
Secret: &lokiv1.TenantSecretSpec{ |
||||
Name: defaultGatewaySecret.Name, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Authorization: &lokiv1.AuthorizationSpec{ |
||||
OPA: &lokiv1.OPASpec{ |
||||
URL: "some-url", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, out client.Object, _ ...client.GetOption) error { |
||||
_, ok := out.(*lokiv1.RulerConfig) |
||||
if ok { |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "no ruler config") |
||||
} |
||||
|
||||
_, isLokiStack := out.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(out, &stack) |
||||
return nil |
||||
} |
||||
if defaultSecret.Name == name.Name { |
||||
k.SetClientObject(out, &defaultSecret) |
||||
return nil |
||||
} |
||||
if defaultGatewaySecret.Name == name.Name { |
||||
k.SetClientObject(out, &defaultGatewaySecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something wasn't found") |
||||
} |
||||
|
||||
k.CreateStub = func(_ context.Context, o client.Object, _ ...client.CreateOption) error { |
||||
assert.Equal(t, r.Namespace, o.GetNamespace()) |
||||
return nil |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
k.DeleteStub = func(_ context.Context, o client.Object, _ ...client.DeleteOption) error { |
||||
assert.Equal(t, r.Namespace, o.GetNamespace()) |
||||
return nil |
||||
} |
||||
|
||||
k.ListStub = func(_ context.Context, list client.ObjectList, options ...client.ListOption) error { |
||||
switch list.(type) { |
||||
case *corev1.ConfigMapList: |
||||
k.SetClientObjectList(list, &corev1.ConfigMapList{ |
||||
Items: []corev1.ConfigMap{ |
||||
rulesCM, |
||||
}, |
||||
}) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
err := Cleanup(context.TODO(), logger, k, &stack) |
||||
require.NoError(t, err) |
||||
|
||||
// make sure delete not called
|
||||
require.Zero(t, k.DeleteCallCount()) |
||||
|
||||
// Disable the ruler
|
||||
stack.Spec.Rules.Enabled = false |
||||
|
||||
// Get should return ruler resources
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, out client.Object, _ ...client.GetOption) error { |
||||
_, ok := out.(*lokiv1.RulerConfig) |
||||
if ok { |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "no ruler config") |
||||
} |
||||
if rulesCM.Name == name.Name { |
||||
k.SetClientObject(out, &rulesCM) |
||||
return nil |
||||
} |
||||
if rulerSS.Name == name.Name { |
||||
k.SetClientObject(out, &rulerSS) |
||||
return nil |
||||
} |
||||
|
||||
_, isLokiStack := out.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(out, &stack) |
||||
return nil |
||||
} |
||||
if defaultSecret.Name == name.Name { |
||||
k.SetClientObject(out, &defaultSecret) |
||||
return nil |
||||
} |
||||
if defaultGatewaySecret.Name == name.Name { |
||||
k.SetClientObject(out, &defaultGatewaySecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something wasn't found") |
||||
} |
||||
|
||||
err = Cleanup(context.TODO(), logger, k, &stack) |
||||
require.NoError(t, err) |
||||
|
||||
// make sure delete was called twice (delete rules configmap and ruler statefulset)
|
||||
require.Equal(t, 2, k.DeleteCallCount()) |
||||
} |
||||
@ -0,0 +1,91 @@ |
||||
package storage |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"time" |
||||
|
||||
configv1 "github.com/grafana/loki/operator/apis/config/v1" |
||||
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" |
||||
"github.com/grafana/loki/operator/internal/external/k8s" |
||||
"github.com/grafana/loki/operator/internal/manifests/storage" |
||||
"github.com/grafana/loki/operator/internal/status" |
||||
) |
||||
|
||||
// BuildOptions returns the object storage options to generate Kubernetes resource manifests
|
||||
// which require access to object storage buckets.
|
||||
// The returned error can be a status.DegradedError in the following cases:
|
||||
// - The user-provided object storage secret is missing.
|
||||
// - The object storage Secret data is invalid.
|
||||
// - The object storage schema config is invalid.
|
||||
// - The object storage CA ConfigMap is missing if one referenced.
|
||||
// - The object storage CA ConfigMap data is invalid.
|
||||
func BuildOptions(ctx context.Context, k k8s.Client, stack *lokiv1.LokiStack, fg configv1.FeatureGates) (storage.Options, error) { |
||||
storageSecret, err := getSecret(ctx, k, stack) |
||||
if err != nil { |
||||
return storage.Options{}, err |
||||
} |
||||
|
||||
objStore, err := extractSecret(storageSecret, stack.Spec.Storage.Secret.Type) |
||||
if err != nil { |
||||
return storage.Options{}, &status.DegradedError{ |
||||
Message: fmt.Sprintf("Invalid object storage secret contents: %s", err), |
||||
Reason: lokiv1.ReasonInvalidObjectStorageSecret, |
||||
Requeue: false, |
||||
} |
||||
} |
||||
objStore.OpenShiftEnabled = fg.OpenShift.Enabled |
||||
|
||||
storageSchemas, err := storage.BuildSchemaConfig( |
||||
time.Now().UTC(), |
||||
stack.Spec.Storage, |
||||
stack.Status.Storage, |
||||
) |
||||
if err != nil { |
||||
return storage.Options{}, &status.DegradedError{ |
||||
Message: fmt.Sprintf("Invalid object storage schema contents: %s", err), |
||||
Reason: lokiv1.ReasonInvalidObjectStorageSchema, |
||||
Requeue: false, |
||||
} |
||||
} |
||||
|
||||
objStore.Schemas = storageSchemas |
||||
|
||||
if stack.Spec.Storage.TLS == nil { |
||||
return objStore, nil |
||||
} |
||||
|
||||
tlsConfig := stack.Spec.Storage.TLS |
||||
if tlsConfig.CA == "" { |
||||
return storage.Options{}, &status.DegradedError{ |
||||
Message: "Missing object storage CA config map", |
||||
Reason: lokiv1.ReasonMissingObjectStorageCAConfigMap, |
||||
Requeue: false, |
||||
} |
||||
} |
||||
|
||||
cm, err := getCAConfigMap(ctx, k, stack, tlsConfig.CA) |
||||
if err != nil { |
||||
return storage.Options{}, err |
||||
} |
||||
|
||||
caKey := defaultCAKey |
||||
if tlsConfig.CAKey != "" { |
||||
caKey = tlsConfig.CAKey |
||||
} |
||||
|
||||
var caHash string |
||||
caHash, err = checkCAConfigMap(cm, caKey) |
||||
if err != nil { |
||||
return storage.Options{}, &status.DegradedError{ |
||||
Message: fmt.Sprintf("Invalid object storage CA configmap contents: %s", err), |
||||
Reason: lokiv1.ReasonInvalidObjectStorageCAConfigMap, |
||||
Requeue: false, |
||||
} |
||||
} |
||||
|
||||
objStore.SecretSHA1 = fmt.Sprintf("%s;%s", objStore.SecretSHA1, caHash) |
||||
objStore.TLS = &storage.TLSConfig{CA: cm.Name, Key: caKey} |
||||
|
||||
return objStore, nil |
||||
} |
||||
@ -0,0 +1,477 @@ |
||||
package storage |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
corev1 "k8s.io/api/core/v1" |
||||
apierrors "k8s.io/apimachinery/pkg/api/errors" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime/schema" |
||||
"k8s.io/apimachinery/pkg/types" |
||||
ctrl "sigs.k8s.io/controller-runtime" |
||||
"sigs.k8s.io/controller-runtime/pkg/client" |
||||
|
||||
configv1 "github.com/grafana/loki/operator/apis/config/v1" |
||||
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" |
||||
"github.com/grafana/loki/operator/internal/external/k8s/k8sfakes" |
||||
"github.com/grafana/loki/operator/internal/status" |
||||
) |
||||
|
||||
var ( |
||||
featureGates = configv1.FeatureGates{ |
||||
ServiceMonitors: false, |
||||
ServiceMonitorTLSEndpoints: false, |
||||
BuiltInCertManagement: configv1.BuiltInCertManagement{ |
||||
Enabled: true, |
||||
CACertValidity: "10m", |
||||
CACertRefresh: "5m", |
||||
CertValidity: "2m", |
||||
CertRefresh: "1m", |
||||
}, |
||||
} |
||||
|
||||
defaultSecret = corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-secret", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{ |
||||
"endpoint": []byte("s3://your-endpoint"), |
||||
"region": []byte("a-region"), |
||||
"bucketnames": []byte("bucket1,bucket2"), |
||||
"access_key_id": []byte("a-secret-id"), |
||||
"access_key_secret": []byte("a-secret-key"), |
||||
}, |
||||
} |
||||
|
||||
invalidSecret = corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-secret", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{}, |
||||
} |
||||
|
||||
invalidCAConfigMap = corev1.ConfigMap{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "some-stack-ca-configmap", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string]string{}, |
||||
} |
||||
) |
||||
|
||||
func TestBuildOptions_WhenMissingSecret_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Missing object storage secret", |
||||
Reason: lokiv1.ReasonMissingObjectStorageSecret, |
||||
Requeue: false, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
_, isLokiStack := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(object, stack) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, err := BuildOptions(context.TODO(), k, stack, featureGates) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_WhenInvalidSecret_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Invalid object storage secret contents: missing secret field", |
||||
Reason: lokiv1.ReasonInvalidObjectStorageSecret, |
||||
Requeue: false, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: invalidSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
_, isLokiStack := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(object, stack) |
||||
return nil |
||||
} |
||||
if name.Name == invalidSecret.Name { |
||||
k.SetClientObject(object, &invalidSecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, err := BuildOptions(context.TODO(), k, stack, featureGates) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_WithInvalidStorageSchema_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Invalid object storage schema contents: spec does not contain any schemas", |
||||
Reason: lokiv1.ReasonInvalidObjectStorageSchema, |
||||
Requeue: false, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
}, |
||||
}, |
||||
Status: lokiv1.LokiStackStatus{ |
||||
Storage: lokiv1.LokiStackStorageStatus{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV12, |
||||
EffectiveDate: "2021-10-11", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
_, isLokiStack := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(object, stack) |
||||
return nil |
||||
} |
||||
if name.Name == defaultSecret.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, err := BuildOptions(context.TODO(), k, stack, featureGates) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_WhenMissingCAConfigMap_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Missing object storage CA config map", |
||||
Reason: lokiv1.ReasonMissingObjectStorageCAConfigMap, |
||||
Requeue: false, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
TLS: &lokiv1.ObjectStorageTLSSpec{ |
||||
CASpec: lokiv1.CASpec{ |
||||
CA: "not-existing", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
_, isLokiStack := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(object, stack) |
||||
return nil |
||||
} |
||||
|
||||
if name.Name == defaultSecret.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
|
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, err := BuildOptions(context.TODO(), k, stack, featureGates) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_WhenEmptyCAConfigMapName_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Missing object storage CA config map", |
||||
Reason: lokiv1.ReasonMissingObjectStorageCAConfigMap, |
||||
Requeue: false, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
TLS: &lokiv1.ObjectStorageTLSSpec{ |
||||
CASpec: lokiv1.CASpec{ |
||||
CA: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
_, isLokiStack := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(object, stack) |
||||
return nil |
||||
} |
||||
|
||||
if name.Name == defaultSecret.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
|
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, err := BuildOptions(context.TODO(), k, stack, featureGates) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
|
||||
func TestBuildOptions_WhenInvalidCAConfigMap_SetDegraded(t *testing.T) { |
||||
sw := &k8sfakes.FakeStatusWriter{} |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
degradedErr := &status.DegradedError{ |
||||
Message: "Invalid object storage CA configmap contents: key not present or data empty: service-ca.crt", |
||||
Reason: lokiv1.ReasonInvalidObjectStorageCAConfigMap, |
||||
Requeue: false, |
||||
} |
||||
|
||||
stack := &lokiv1.LokiStack{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "LokiStack", |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
UID: "b23f9a38-9672-499f-8c29-15ede74d3ece", |
||||
}, |
||||
Spec: lokiv1.LokiStackSpec{ |
||||
Size: lokiv1.SizeOneXExtraSmall, |
||||
Storage: lokiv1.ObjectStorageSpec{ |
||||
Schemas: []lokiv1.ObjectStorageSchema{ |
||||
{ |
||||
Version: lokiv1.ObjectStorageSchemaV11, |
||||
EffectiveDate: "2020-10-11", |
||||
}, |
||||
}, |
||||
Secret: lokiv1.ObjectStorageSecretSpec{ |
||||
Name: defaultSecret.Name, |
||||
Type: lokiv1.ObjectStorageSecretS3, |
||||
}, |
||||
TLS: &lokiv1.ObjectStorageTLSSpec{ |
||||
CASpec: lokiv1.CASpec{ |
||||
CA: invalidCAConfigMap.Name, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error { |
||||
_, isLokiStack := object.(*lokiv1.LokiStack) |
||||
if r.Name == name.Name && r.Namespace == name.Namespace && isLokiStack { |
||||
k.SetClientObject(object, stack) |
||||
return nil |
||||
} |
||||
if name.Name == defaultSecret.Name { |
||||
k.SetClientObject(object, &defaultSecret) |
||||
return nil |
||||
} |
||||
|
||||
if name.Name == invalidCAConfigMap.Name { |
||||
k.SetClientObject(object, &invalidCAConfigMap) |
||||
return nil |
||||
} |
||||
return apierrors.NewNotFound(schema.GroupResource{}, "something is not found") |
||||
} |
||||
|
||||
k.StatusStub = func() client.StatusWriter { return sw } |
||||
|
||||
_, err := BuildOptions(context.TODO(), k, stack, featureGates) |
||||
|
||||
// make sure error is returned
|
||||
require.Error(t, err) |
||||
require.Equal(t, degradedErr, err) |
||||
} |
||||
Loading…
Reference in new issue