mirror of https://github.com/grafana/loki
Add support for openshift-logging tenant mode (#93)
parent
54b334eb9e
commit
ac07fc06ee
@ -0,0 +1,13 @@ |
||||
apiVersion: loki.openshift.io/v1beta1 |
||||
kind: LokiStack |
||||
metadata: |
||||
name: lokistack-dev |
||||
spec: |
||||
size: 1x.extra-small |
||||
replicationFactor: 1 |
||||
storage: |
||||
secret: |
||||
name: test |
||||
storageClassName: gp2 |
||||
tenants: |
||||
mode: openshift-logging |
||||
@ -0,0 +1,134 @@ |
||||
package gateway |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/ViaQ/logerr/kverrors" |
||||
|
||||
lokiv1beta1 "github.com/ViaQ/loki-operator/api/v1beta1" |
||||
"github.com/ViaQ/loki-operator/internal/external/k8s" |
||||
"github.com/ViaQ/loki-operator/internal/handlers/internal/secrets" |
||||
"github.com/ViaQ/loki-operator/internal/manifests" |
||||
"github.com/ViaQ/loki-operator/internal/status" |
||||
|
||||
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" |
||||
ctrl "sigs.k8s.io/controller-runtime" |
||||
"sigs.k8s.io/controller-runtime/pkg/client" |
||||
) |
||||
|
||||
// GetTenantSecrets returns the list to gateway tenant secrets for a tenant mode.
|
||||
// For modes static and dynamic the secrets are fetched from external provided
|
||||
// secrets. For mode openshift-logging a secret per default tenants are created.
|
||||
// All secrets live in the same namespace as the lokistack request.
|
||||
func GetTenantSecrets( |
||||
ctx context.Context, |
||||
k k8s.Client, |
||||
req ctrl.Request, |
||||
scheme *runtime.Scheme, |
||||
stack *lokiv1beta1.LokiStack, |
||||
) ([]*manifests.TenantSecrets, error) { |
||||
switch stack.Spec.Tenants.Mode { |
||||
case lokiv1beta1.Static, lokiv1beta1.Dynamic: |
||||
return extractUserProvidedSecrets(ctx, k, req, stack) |
||||
case lokiv1beta1.OpenshiftLogging: |
||||
return createOpenShiftLoggingSecrets(ctx, k, req, scheme, stack) |
||||
} |
||||
|
||||
return nil, nil |
||||
} |
||||
|
||||
func extractUserProvidedSecrets( |
||||
ctx context.Context, |
||||
k k8s.Client, |
||||
req ctrl.Request, |
||||
stack *lokiv1beta1.LokiStack, |
||||
) ([]*manifests.TenantSecrets, error) { |
||||
var ( |
||||
tenantSecrets []*manifests.TenantSecrets |
||||
gatewaySecret corev1.Secret |
||||
) |
||||
|
||||
for _, tenant := range stack.Spec.Tenants.Authentication { |
||||
key := client.ObjectKey{Name: tenant.OIDC.Secret.Name, Namespace: req.Namespace} |
||||
if err := k.Get(ctx, key, &gatewaySecret); err != nil { |
||||
if apierrors.IsNotFound(err) { |
||||
return nil, status.SetDegradedCondition(ctx, k, req, |
||||
fmt.Sprintf("Missing secrets for tenant %s", tenant.TenantName), |
||||
lokiv1beta1.ReasonMissingGatewayTenantSecret, |
||||
) |
||||
} |
||||
return nil, kverrors.Wrap(err, "failed to lookup lokistack gateway tenant secret", |
||||
"name", key) |
||||
} |
||||
|
||||
var ts *manifests.TenantSecrets |
||||
ts, err := secrets.ExtractGatewaySecret(&gatewaySecret, tenant.TenantName) |
||||
if err != nil { |
||||
return nil, status.SetDegradedCondition(ctx, k, req, |
||||
"Invalid gateway tenant secret contents", |
||||
lokiv1beta1.ReasonInvalidGatewayTenantSecret, |
||||
) |
||||
} |
||||
tenantSecrets = append(tenantSecrets, ts) |
||||
} |
||||
|
||||
return tenantSecrets, nil |
||||
} |
||||
|
||||
func createOpenShiftLoggingSecrets( |
||||
ctx context.Context, |
||||
k k8s.Client, |
||||
req ctrl.Request, |
||||
scheme *runtime.Scheme, |
||||
stack *lokiv1beta1.LokiStack, |
||||
) ([]*manifests.TenantSecrets, error) { |
||||
var tenantSecrets []*manifests.TenantSecrets |
||||
gatewayName := manifests.GatewayName(stack.Name) |
||||
|
||||
for _, name := range manifests.OpenShiftDefaultTenants { |
||||
s := &corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: fmt.Sprintf("%s-%s", gatewayName, name), |
||||
Namespace: stack.Namespace, |
||||
}, |
||||
Data: map[string][]byte{ |
||||
// TODO Fill these with production data when we integrate dex.
|
||||
"clientID": []byte("clientID"), |
||||
"clientSecret": []byte("clientSecret"), |
||||
"issuerCAPath": []byte("/path/to/ca/file"), |
||||
}, |
||||
} |
||||
|
||||
if err := ctrl.SetControllerReference(stack, s, scheme); err != nil { |
||||
return nil, status.SetDegradedCondition(ctx, k, req, |
||||
fmt.Sprintf("Missing secrets for tenant %s", name), |
||||
lokiv1beta1.ReasonMissingGatewayTenantSecret, |
||||
) |
||||
} |
||||
|
||||
if err := k.Create(ctx, s, &client.CreateOptions{}); err != nil { |
||||
if !apierrors.IsAlreadyExists(err) { |
||||
return nil, status.SetDegradedCondition(ctx, k, req, |
||||
fmt.Sprintf("Missing secrets for tenant %s", name), |
||||
lokiv1beta1.ReasonMissingGatewayTenantSecret, |
||||
) |
||||
} |
||||
} |
||||
|
||||
var ts *manifests.TenantSecrets |
||||
ts, err := secrets.ExtractGatewaySecret(s, name) |
||||
if err != nil { |
||||
return nil, status.SetDegradedCondition(ctx, k, req, |
||||
"Invalid gateway tenant secret contents", |
||||
lokiv1beta1.ReasonInvalidGatewayTenantSecret, |
||||
) |
||||
} |
||||
tenantSecrets = append(tenantSecrets, ts) |
||||
} |
||||
|
||||
return tenantSecrets, nil |
||||
} |
||||
@ -0,0 +1,214 @@ |
||||
package gateway |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
|
||||
lokiv1beta1 "github.com/ViaQ/loki-operator/api/v1beta1" |
||||
"github.com/ViaQ/loki-operator/internal/external/k8s/k8sfakes" |
||||
"github.com/ViaQ/loki-operator/internal/manifests" |
||||
|
||||
corev1 "k8s.io/api/core/v1" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apimachinery/pkg/types" |
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime" |
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme" |
||||
ctrl "sigs.k8s.io/controller-runtime" |
||||
"sigs.k8s.io/controller-runtime/pkg/client" |
||||
) |
||||
|
||||
var scheme = runtime.NewScheme() |
||||
|
||||
func TestGetTenantSecrets_StaticMode(t *testing.T) { |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
s := &lokiv1beta1.LokiStack{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "mystack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Spec: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.Static, |
||||
Authentication: []lokiv1beta1.AuthenticationSpec{ |
||||
{ |
||||
TenantName: "test", |
||||
TenantID: "test", |
||||
OIDC: &lokiv1beta1.OIDCSpec{ |
||||
Secret: &lokiv1beta1.TenantSecretSpec{ |
||||
Name: "test", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object) error { |
||||
if name.Name == "test" && name.Namespace == "some-ns" { |
||||
k.SetClientObject(object, &corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "test", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{ |
||||
"clientID": []byte("test"), |
||||
"clientSecret": []byte("test"), |
||||
"issuerCAPath": []byte("/path/to/ca/file"), |
||||
}, |
||||
}) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
ts, err := GetTenantSecrets(context.TODO(), k, r, scheme, s) |
||||
require.NoError(t, err) |
||||
|
||||
expected := []*manifests.TenantSecrets{ |
||||
{ |
||||
TenantName: "test", |
||||
ClientID: "test", |
||||
ClientSecret: "test", |
||||
IssuerCAPath: "/path/to/ca/file", |
||||
}, |
||||
} |
||||
require.ElementsMatch(t, ts, expected) |
||||
} |
||||
|
||||
func TestGetTenantSecrets_DynamicMode(t *testing.T) { |
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
s := &lokiv1beta1.LokiStack{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "mystack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Spec: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.Dynamic, |
||||
Authentication: []lokiv1beta1.AuthenticationSpec{ |
||||
{ |
||||
TenantName: "test", |
||||
TenantID: "test", |
||||
OIDC: &lokiv1beta1.OIDCSpec{ |
||||
Secret: &lokiv1beta1.TenantSecretSpec{ |
||||
Name: "test", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object) error { |
||||
if name.Name == "test" && name.Namespace == "some-ns" { |
||||
k.SetClientObject(object, &corev1.Secret{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "test", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Data: map[string][]byte{ |
||||
"clientID": []byte("test"), |
||||
"clientSecret": []byte("test"), |
||||
"issuerCAPath": []byte("/path/to/ca/file"), |
||||
}, |
||||
}) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
ts, err := GetTenantSecrets(context.TODO(), k, r, scheme, s) |
||||
require.NoError(t, err) |
||||
|
||||
expected := []*manifests.TenantSecrets{ |
||||
{ |
||||
TenantName: "test", |
||||
ClientID: "test", |
||||
ClientSecret: "test", |
||||
IssuerCAPath: "/path/to/ca/file", |
||||
}, |
||||
} |
||||
require.ElementsMatch(t, ts, expected) |
||||
} |
||||
|
||||
func TestGetTenantSecrets_OpenShiftLoggingMode(t *testing.T) { |
||||
// Register the clientgo and CRD schemes
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme)) |
||||
utilruntime.Must(lokiv1beta1.AddToScheme(scheme)) |
||||
|
||||
k := &k8sfakes.FakeClient{} |
||||
r := ctrl.Request{ |
||||
NamespacedName: types.NamespacedName{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
} |
||||
|
||||
s := &lokiv1beta1.LokiStack{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "my-stack", |
||||
Namespace: "some-ns", |
||||
}, |
||||
Spec: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.OpenshiftLogging, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
expectedNames := []string{ |
||||
"lokistack-gateway-my-stack-application", |
||||
"lokistack-gateway-my-stack-infrastructure", |
||||
"lokistack-gateway-my-stack-audit", |
||||
} |
||||
|
||||
k.CreateStub = func(_ context.Context, object client.Object, _ ...client.CreateOption) error { |
||||
require.Contains(t, expectedNames, object.GetName()) |
||||
require.Equal(t, "some-ns", object.GetNamespace()) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
ts, err := GetTenantSecrets(context.TODO(), k, r, scheme, s) |
||||
require.NoError(t, err) |
||||
require.Equal(t, 3, k.CreateCallCount()) |
||||
|
||||
expected := []*manifests.TenantSecrets{ |
||||
{ |
||||
TenantName: "application", |
||||
ClientID: "clientID", |
||||
ClientSecret: "clientSecret", |
||||
IssuerCAPath: "/path/to/ca/file", |
||||
}, |
||||
{ |
||||
TenantName: "infrastructure", |
||||
ClientID: "clientID", |
||||
ClientSecret: "clientSecret", |
||||
IssuerCAPath: "/path/to/ca/file", |
||||
}, |
||||
{ |
||||
TenantName: "audit", |
||||
ClientID: "clientID", |
||||
ClientSecret: "clientSecret", |
||||
IssuerCAPath: "/path/to/ca/file", |
||||
}, |
||||
} |
||||
require.ElementsMatch(t, ts, expected) |
||||
} |
||||
@ -0,0 +1,269 @@ |
||||
package manifests |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
"path" |
||||
|
||||
"github.com/ViaQ/logerr/kverrors" |
||||
"github.com/google/uuid" |
||||
"github.com/imdario/mergo" |
||||
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" |
||||
|
||||
lokiv1beta1 "github.com/ViaQ/loki-operator/api/v1beta1" |
||||
"github.com/ViaQ/loki-operator/internal/manifests/internal/gateway" |
||||
|
||||
appsv1 "k8s.io/api/apps/v1" |
||||
corev1 "k8s.io/api/core/v1" |
||||
"k8s.io/apimachinery/pkg/util/intstr" |
||||
) |
||||
|
||||
const ( |
||||
envRelatedImageOPA = "RELATED_IMAGE_OPA" |
||||
|
||||
defaultOPAImage = "quay.io/observatorium/opa-openshift:latest" |
||||
|
||||
opaContainerName = "opa-openshift" |
||||
opaDefaultPackage = "lokistack" |
||||
opaDefaultAPIGroup = "loki.openshift.io" |
||||
opaMetricsPortName = "opa-metrics" |
||||
|
||||
// OpenShiftApplicationTenant is the tenant name for tenant holding application logs.
|
||||
OpenShiftApplicationTenant = "application" |
||||
// OpenShiftInfraTenant is the tenant name for tenant holding infrastructure logs.
|
||||
OpenShiftInfraTenant = "infrastructure" |
||||
// OpenShiftAuditTenant is the tenant name for tenant holding audit logs.
|
||||
OpenShiftAuditTenant = "audit" |
||||
) |
||||
|
||||
// OpenShiftDefaultTenants represents the slice of all supported LokiStack on OpenShift.
|
||||
var OpenShiftDefaultTenants = []string{ |
||||
OpenShiftApplicationTenant, |
||||
OpenShiftInfraTenant, |
||||
OpenShiftAuditTenant, |
||||
} |
||||
|
||||
// ApplyGatewayDefaultOptions applies defaults on the LokiStackSpec depending on selected
|
||||
// tenant mode. Currently nothing is applied for modes static and dynamic. For mode openshift-logging
|
||||
// the tenant spec is filled with defaults for authentication and authorization.
|
||||
func ApplyGatewayDefaultOptions(opts *Options) error { |
||||
switch opts.Stack.Tenants.Mode { |
||||
case lokiv1beta1.Static, lokiv1beta1.Dynamic: |
||||
return nil // continue using user input
|
||||
|
||||
case lokiv1beta1.OpenshiftLogging: |
||||
var authn []lokiv1beta1.AuthenticationSpec |
||||
for _, name := range OpenShiftDefaultTenants { |
||||
authn = append(authn, lokiv1beta1.AuthenticationSpec{ |
||||
TenantName: name, |
||||
TenantID: uuid.New().String(), |
||||
OIDC: &lokiv1beta1.OIDCSpec{ |
||||
// TODO Setup when we integrate dex as a separate sidecar here
|
||||
IssuerURL: "https://127.0.0.1:5556/dex", |
||||
RedirectURL: fmt.Sprintf("http://localhost:%d/oidc/%s/callback", gatewayHTTPPort, name), |
||||
UsernameClaim: "name", |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
defaults := &lokiv1beta1.TenantsSpec{ |
||||
Authentication: authn, |
||||
Authorization: &lokiv1beta1.AuthorizationSpec{ |
||||
OPA: &lokiv1beta1.OPASpec{ |
||||
URL: fmt.Sprintf("http://localhost:%d/data/%s/allow", gatewayOPAHTTPPort, opaDefaultPackage), |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
if err := mergo.Merge(opts.Stack.Tenants, defaults, mergo.WithOverride); err != nil { |
||||
return kverrors.Wrap(err, "failed to merge defaults for mode openshift logging") |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func configureDeploymentForMode(d *appsv1.DeploymentSpec, mode lokiv1beta1.ModeType, flags FeatureFlags) error { |
||||
switch mode { |
||||
case lokiv1beta1.Static, lokiv1beta1.Dynamic: |
||||
return nil // nothing to configure
|
||||
case lokiv1beta1.OpenshiftLogging: |
||||
return configureDeploymentForOpenShiftLogging(d, flags) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func configureServiceForMode(s *corev1.ServiceSpec, mode lokiv1beta1.ModeType) error { |
||||
switch mode { |
||||
case lokiv1beta1.Static, lokiv1beta1.Dynamic: |
||||
return nil // nothing to configure
|
||||
case lokiv1beta1.OpenshiftLogging: |
||||
return configureServiceForOpenShiftLogging(s) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func configureServiceMonitorForMode(sm *monitoringv1.ServiceMonitor, mode lokiv1beta1.ModeType, flags FeatureFlags) error { |
||||
switch mode { |
||||
case lokiv1beta1.Static, lokiv1beta1.Dynamic: |
||||
return nil // nothing to configure
|
||||
case lokiv1beta1.OpenshiftLogging: |
||||
return configureServiceMonitorForOpenShiftLogging(sm, flags) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func configureDeploymentForOpenShiftLogging(d *appsv1.DeploymentSpec, flags FeatureFlags) error { |
||||
p := corev1.PodSpec{ |
||||
Containers: []corev1.Container{ |
||||
newOPAOpenShiftContainer(flags), |
||||
}, |
||||
} |
||||
|
||||
if err := mergo.Merge(&d.Template.Spec, p, mergo.WithAppendSlice); err != nil { |
||||
return kverrors.Wrap(err, "failed to merge sidecar containers") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func newOPAOpenShiftContainer(flags FeatureFlags) corev1.Container { |
||||
var ( |
||||
image string |
||||
args []string |
||||
uriScheme corev1.URIScheme |
||||
volumeMounts []corev1.VolumeMount |
||||
) |
||||
|
||||
image = os.Getenv(envRelatedImageOPA) |
||||
if image == "" { |
||||
image = defaultOPAImage |
||||
} |
||||
|
||||
uriScheme = corev1.URISchemeHTTP |
||||
args = []string{ |
||||
"--log.level=warn", |
||||
fmt.Sprintf("--opa.package=%s", opaDefaultPackage), |
||||
fmt.Sprintf("--web.listen=:%d", gatewayOPAHTTPPort), |
||||
fmt.Sprintf("--web.internal.listen=:%d", gatewayOPAInternalPort), |
||||
fmt.Sprintf("--web.healthchecks.url=http://localhost:%d", gatewayOPAHTTPPort), |
||||
} |
||||
|
||||
if flags.EnableTLSServiceMonitorConfig { |
||||
certFile := path.Join(gateway.LokiGatewayTLSDir, gateway.LokiGatewayCertFile) |
||||
keyFile := path.Join(gateway.LokiGatewayTLSDir, gateway.LokiGatewayKeyFile) |
||||
|
||||
args = append(args, []string{ |
||||
fmt.Sprintf("--tls.internal.server.cert-file=%s", certFile), |
||||
fmt.Sprintf("--tls.internal.server.key-file=%s", keyFile), |
||||
}...) |
||||
|
||||
uriScheme = corev1.URISchemeHTTPS |
||||
|
||||
volumeMounts = []corev1.VolumeMount{ |
||||
{ |
||||
Name: tlsMetricsSercetVolume, |
||||
ReadOnly: true, |
||||
MountPath: gateway.LokiGatewayTLSDir, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
for _, t := range OpenShiftDefaultTenants { |
||||
args = append(args, fmt.Sprintf(`--openshift.mappings=%s=%s`, t, opaDefaultAPIGroup)) |
||||
} |
||||
|
||||
return corev1.Container{ |
||||
Name: opaContainerName, |
||||
Image: image, |
||||
Args: args, |
||||
Ports: []corev1.ContainerPort{ |
||||
{ |
||||
Name: gatewayOPAHTTPPortName, |
||||
ContainerPort: gatewayOPAHTTPPort, |
||||
Protocol: corev1.ProtocolTCP, |
||||
}, |
||||
{ |
||||
Name: gatewayOPAInternalPortName, |
||||
ContainerPort: gatewayOPAInternalPort, |
||||
Protocol: corev1.ProtocolTCP, |
||||
}, |
||||
}, |
||||
LivenessProbe: &corev1.Probe{ |
||||
Handler: corev1.Handler{ |
||||
HTTPGet: &corev1.HTTPGetAction{ |
||||
Path: "/live", |
||||
Port: intstr.FromInt(gatewayOPAInternalPort), |
||||
Scheme: uriScheme, |
||||
}, |
||||
}, |
||||
TimeoutSeconds: 2, |
||||
PeriodSeconds: 30, |
||||
FailureThreshold: 10, |
||||
}, |
||||
ReadinessProbe: &corev1.Probe{ |
||||
Handler: corev1.Handler{ |
||||
HTTPGet: &corev1.HTTPGetAction{ |
||||
Path: "/ready", |
||||
Port: intstr.FromInt(gatewayOPAInternalPort), |
||||
Scheme: uriScheme, |
||||
}, |
||||
}, |
||||
TimeoutSeconds: 1, |
||||
PeriodSeconds: 5, |
||||
FailureThreshold: 12, |
||||
}, |
||||
VolumeMounts: volumeMounts, |
||||
} |
||||
} |
||||
|
||||
func configureServiceForOpenShiftLogging(s *corev1.ServiceSpec) error { |
||||
spec := corev1.ServiceSpec{ |
||||
Ports: []corev1.ServicePort{ |
||||
{ |
||||
Name: opaMetricsPortName, |
||||
Port: gatewayOPAInternalPort, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
if err := mergo.Merge(s, spec, mergo.WithAppendSlice); err != nil { |
||||
return kverrors.Wrap(err, "failed to merge sidecar containers") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func configureServiceMonitorForOpenShiftLogging(sm *monitoringv1.ServiceMonitor, flags FeatureFlags) error { |
||||
var opaEndpoint monitoringv1.Endpoint |
||||
|
||||
if flags.EnableTLSServiceMonitorConfig { |
||||
tlsConfig := sm.Spec.Endpoints[0].TLSConfig |
||||
opaEndpoint = monitoringv1.Endpoint{ |
||||
Port: opaMetricsPortName, |
||||
Path: "/metrics", |
||||
Scheme: "https", |
||||
BearerTokenFile: BearerTokenFile, |
||||
TLSConfig: tlsConfig, |
||||
} |
||||
} else { |
||||
opaEndpoint = monitoringv1.Endpoint{ |
||||
Port: opaMetricsPortName, |
||||
Path: "/metrics", |
||||
Scheme: "http", |
||||
} |
||||
} |
||||
|
||||
spec := monitoringv1.ServiceMonitorSpec{ |
||||
Endpoints: []monitoringv1.Endpoint{opaEndpoint}, |
||||
} |
||||
|
||||
if err := mergo.Merge(&sm.Spec, spec, mergo.WithAppendSlice); err != nil { |
||||
return kverrors.Wrap(err, "failed to merge sidecar service monitor endpoints") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,438 @@ |
||||
package manifests |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" |
||||
"github.com/stretchr/testify/require" |
||||
|
||||
lokiv1beta1 "github.com/ViaQ/loki-operator/api/v1beta1" |
||||
"github.com/ViaQ/loki-operator/internal/manifests/internal/gateway" |
||||
|
||||
appsv1 "k8s.io/api/apps/v1" |
||||
corev1 "k8s.io/api/core/v1" |
||||
"k8s.io/apimachinery/pkg/util/intstr" |
||||
) |
||||
|
||||
func TestApplyGatewayDefaultsOptions(t *testing.T) { |
||||
type tt struct { |
||||
desc string |
||||
opts *Options |
||||
want *Options |
||||
} |
||||
|
||||
tc := []tt{ |
||||
{ |
||||
desc: "static mode", |
||||
opts: &Options{ |
||||
Stack: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.Static, |
||||
}, |
||||
}, |
||||
}, |
||||
want: &Options{ |
||||
Stack: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.Static, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
desc: "dynamic mode", |
||||
opts: &Options{ |
||||
Stack: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.Dynamic, |
||||
}, |
||||
}, |
||||
}, |
||||
want: &Options{ |
||||
Stack: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.Dynamic, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
desc: "openshift-logging mode", |
||||
opts: &Options{ |
||||
Stack: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.OpenshiftLogging, |
||||
}, |
||||
}, |
||||
}, |
||||
want: &Options{ |
||||
Stack: lokiv1beta1.LokiStackSpec{ |
||||
Tenants: &lokiv1beta1.TenantsSpec{ |
||||
Mode: lokiv1beta1.OpenshiftLogging, |
||||
Authentication: []lokiv1beta1.AuthenticationSpec{ |
||||
{ |
||||
TenantName: "application", |
||||
TenantID: "", |
||||
OIDC: &lokiv1beta1.OIDCSpec{ |
||||
IssuerURL: "https://127.0.0.1:5556/dex", |
||||
RedirectURL: "http://localhost:8080/oidc/application/callback", |
||||
UsernameClaim: "name", |
||||
}, |
||||
}, |
||||
{ |
||||
TenantName: "infrastructure", |
||||
TenantID: "", |
||||
OIDC: &lokiv1beta1.OIDCSpec{ |
||||
IssuerURL: "https://127.0.0.1:5556/dex", |
||||
RedirectURL: "http://localhost:8080/oidc/infrastructure/callback", |
||||
UsernameClaim: "name", |
||||
}, |
||||
}, |
||||
{ |
||||
TenantName: "audit", |
||||
TenantID: "", |
||||
OIDC: &lokiv1beta1.OIDCSpec{ |
||||
IssuerURL: "https://127.0.0.1:5556/dex", |
||||
RedirectURL: "http://localhost:8080/oidc/audit/callback", |
||||
UsernameClaim: "name", |
||||
}, |
||||
}, |
||||
}, |
||||
Authorization: &lokiv1beta1.AuthorizationSpec{ |
||||
OPA: &lokiv1beta1.OPASpec{ |
||||
URL: "http://localhost:8082/data/lokistack/allow", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
for _, tc := range tc { |
||||
tc := tc |
||||
t.Run(tc.desc, func(t *testing.T) { |
||||
t.Parallel() |
||||
err := ApplyGatewayDefaultOptions(tc.opts) |
||||
require.NoError(t, err) |
||||
|
||||
for i, a := range tc.opts.Stack.Tenants.Authentication { |
||||
a.TenantID = "" |
||||
tc.opts.Stack.Tenants.Authentication[i] = a |
||||
} |
||||
|
||||
require.Equal(t, tc.want, tc.opts) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestConfigureDeploymentForMode(t *testing.T) { |
||||
type tt struct { |
||||
desc string |
||||
mode lokiv1beta1.ModeType |
||||
flags FeatureFlags |
||||
dpl *appsv1.DeploymentSpec |
||||
want *appsv1.DeploymentSpec |
||||
} |
||||
|
||||
tc := []tt{ |
||||
{ |
||||
desc: "static mode", |
||||
mode: lokiv1beta1.Static, |
||||
dpl: &appsv1.DeploymentSpec{}, |
||||
want: &appsv1.DeploymentSpec{}, |
||||
}, |
||||
{ |
||||
desc: "dynamic mode", |
||||
mode: lokiv1beta1.Dynamic, |
||||
dpl: &appsv1.DeploymentSpec{}, |
||||
want: &appsv1.DeploymentSpec{}, |
||||
}, |
||||
{ |
||||
desc: "openshift-logging mode", |
||||
mode: lokiv1beta1.OpenshiftLogging, |
||||
dpl: &appsv1.DeploymentSpec{}, |
||||
want: &appsv1.DeploymentSpec{ |
||||
Template: corev1.PodTemplateSpec{ |
||||
Spec: corev1.PodSpec{ |
||||
Containers: []corev1.Container{ |
||||
{ |
||||
Name: "opa-openshift", |
||||
Image: "quay.io/observatorium/opa-openshift:latest", |
||||
Args: []string{ |
||||
"--log.level=warn", |
||||
"--opa.package=lokistack", |
||||
"--web.listen=:8082", |
||||
"--web.internal.listen=:8083", |
||||
"--web.healthchecks.url=http://localhost:8082", |
||||
`--openshift.mappings=application=loki.openshift.io`, |
||||
`--openshift.mappings=infrastructure=loki.openshift.io`, |
||||
`--openshift.mappings=audit=loki.openshift.io`, |
||||
}, |
||||
Ports: []corev1.ContainerPort{ |
||||
{ |
||||
Name: gatewayOPAHTTPPortName, |
||||
ContainerPort: gatewayOPAHTTPPort, |
||||
Protocol: corev1.ProtocolTCP, |
||||
}, |
||||
{ |
||||
Name: gatewayOPAInternalPortName, |
||||
ContainerPort: gatewayOPAInternalPort, |
||||
Protocol: corev1.ProtocolTCP, |
||||
}, |
||||
}, |
||||
LivenessProbe: &corev1.Probe{ |
||||
Handler: corev1.Handler{ |
||||
HTTPGet: &corev1.HTTPGetAction{ |
||||
Path: "/live", |
||||
Port: intstr.FromInt(gatewayOPAInternalPort), |
||||
Scheme: corev1.URISchemeHTTP, |
||||
}, |
||||
}, |
||||
TimeoutSeconds: 2, |
||||
PeriodSeconds: 30, |
||||
FailureThreshold: 10, |
||||
}, |
||||
ReadinessProbe: &corev1.Probe{ |
||||
Handler: corev1.Handler{ |
||||
HTTPGet: &corev1.HTTPGetAction{ |
||||
Path: "/ready", |
||||
Port: intstr.FromInt(gatewayOPAInternalPort), |
||||
Scheme: corev1.URISchemeHTTP, |
||||
}, |
||||
}, |
||||
TimeoutSeconds: 1, |
||||
PeriodSeconds: 5, |
||||
FailureThreshold: 12, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
desc: "openshift-logging mode with-tls-service-monitor-config", |
||||
mode: lokiv1beta1.OpenshiftLogging, |
||||
flags: FeatureFlags{ |
||||
EnableTLSServiceMonitorConfig: true, |
||||
}, |
||||
dpl: &appsv1.DeploymentSpec{}, |
||||
want: &appsv1.DeploymentSpec{ |
||||
Template: corev1.PodTemplateSpec{ |
||||
Spec: corev1.PodSpec{ |
||||
Containers: []corev1.Container{ |
||||
{ |
||||
Name: "opa-openshift", |
||||
Image: "quay.io/observatorium/opa-openshift:latest", |
||||
Args: []string{ |
||||
"--log.level=warn", |
||||
"--opa.package=lokistack", |
||||
"--web.listen=:8082", |
||||
"--web.internal.listen=:8083", |
||||
"--web.healthchecks.url=http://localhost:8082", |
||||
"--tls.internal.server.cert-file=/var/run/tls/tls.crt", |
||||
"--tls.internal.server.key-file=/var/run/tls/tls.key", |
||||
`--openshift.mappings=application=loki.openshift.io`, |
||||
`--openshift.mappings=infrastructure=loki.openshift.io`, |
||||
`--openshift.mappings=audit=loki.openshift.io`, |
||||
}, |
||||
Ports: []corev1.ContainerPort{ |
||||
{ |
||||
Name: gatewayOPAHTTPPortName, |
||||
ContainerPort: gatewayOPAHTTPPort, |
||||
Protocol: corev1.ProtocolTCP, |
||||
}, |
||||
{ |
||||
Name: gatewayOPAInternalPortName, |
||||
ContainerPort: gatewayOPAInternalPort, |
||||
Protocol: corev1.ProtocolTCP, |
||||
}, |
||||
}, |
||||
LivenessProbe: &corev1.Probe{ |
||||
Handler: corev1.Handler{ |
||||
HTTPGet: &corev1.HTTPGetAction{ |
||||
Path: "/live", |
||||
Port: intstr.FromInt(gatewayOPAInternalPort), |
||||
Scheme: corev1.URISchemeHTTPS, |
||||
}, |
||||
}, |
||||
TimeoutSeconds: 2, |
||||
PeriodSeconds: 30, |
||||
FailureThreshold: 10, |
||||
}, |
||||
ReadinessProbe: &corev1.Probe{ |
||||
Handler: corev1.Handler{ |
||||
HTTPGet: &corev1.HTTPGetAction{ |
||||
Path: "/ready", |
||||
Port: intstr.FromInt(gatewayOPAInternalPort), |
||||
Scheme: corev1.URISchemeHTTPS, |
||||
}, |
||||
}, |
||||
TimeoutSeconds: 1, |
||||
PeriodSeconds: 5, |
||||
FailureThreshold: 12, |
||||
}, |
||||
VolumeMounts: []corev1.VolumeMount{ |
||||
{ |
||||
Name: tlsMetricsSercetVolume, |
||||
ReadOnly: true, |
||||
MountPath: gateway.LokiGatewayTLSDir, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
for _, tc := range tc { |
||||
tc := tc |
||||
t.Run(tc.desc, func(t *testing.T) { |
||||
t.Parallel() |
||||
err := configureDeploymentForMode(tc.dpl, tc.mode, tc.flags) |
||||
require.NoError(t, err) |
||||
require.Equal(t, tc.want, tc.dpl) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestConfigureServiceForMode(t *testing.T) { |
||||
type tt struct { |
||||
desc string |
||||
mode lokiv1beta1.ModeType |
||||
svc *corev1.ServiceSpec |
||||
want *corev1.ServiceSpec |
||||
} |
||||
|
||||
tc := []tt{ |
||||
{ |
||||
desc: "static mode", |
||||
mode: lokiv1beta1.Static, |
||||
svc: &corev1.ServiceSpec{}, |
||||
want: &corev1.ServiceSpec{}, |
||||
}, |
||||
{ |
||||
desc: "dynamic mode", |
||||
mode: lokiv1beta1.Dynamic, |
||||
svc: &corev1.ServiceSpec{}, |
||||
want: &corev1.ServiceSpec{}, |
||||
}, |
||||
{ |
||||
desc: "openshift-logging mode", |
||||
mode: lokiv1beta1.OpenshiftLogging, |
||||
svc: &corev1.ServiceSpec{}, |
||||
want: &corev1.ServiceSpec{ |
||||
Ports: []corev1.ServicePort{ |
||||
{ |
||||
Name: opaMetricsPortName, |
||||
Port: gatewayOPAInternalPort, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
for _, tc := range tc { |
||||
tc := tc |
||||
t.Run(tc.desc, func(t *testing.T) { |
||||
t.Parallel() |
||||
err := configureServiceForMode(tc.svc, tc.mode) |
||||
require.NoError(t, err) |
||||
require.Equal(t, tc.want, tc.svc) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestConfigureServiceMonitorForMode(t *testing.T) { |
||||
type tt struct { |
||||
desc string |
||||
mode lokiv1beta1.ModeType |
||||
flags FeatureFlags |
||||
sm *monitoringv1.ServiceMonitor |
||||
want *monitoringv1.ServiceMonitor |
||||
} |
||||
|
||||
tc := []tt{ |
||||
{ |
||||
desc: "static mode", |
||||
mode: lokiv1beta1.Static, |
||||
sm: &monitoringv1.ServiceMonitor{}, |
||||
want: &monitoringv1.ServiceMonitor{}, |
||||
}, |
||||
{ |
||||
desc: "dynamic mode", |
||||
mode: lokiv1beta1.Dynamic, |
||||
sm: &monitoringv1.ServiceMonitor{}, |
||||
want: &monitoringv1.ServiceMonitor{}, |
||||
}, |
||||
{ |
||||
desc: "openshift-logging mode", |
||||
mode: lokiv1beta1.OpenshiftLogging, |
||||
sm: &monitoringv1.ServiceMonitor{}, |
||||
want: &monitoringv1.ServiceMonitor{ |
||||
Spec: monitoringv1.ServiceMonitorSpec{ |
||||
Endpoints: []monitoringv1.Endpoint{ |
||||
{ |
||||
Port: opaMetricsPortName, |
||||
Path: "/metrics", |
||||
Scheme: "http", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
desc: "openshift-logging mode with-tls-service-monitor-config", |
||||
mode: lokiv1beta1.OpenshiftLogging, |
||||
flags: FeatureFlags{ |
||||
EnableTLSServiceMonitorConfig: true, |
||||
}, |
||||
sm: &monitoringv1.ServiceMonitor{ |
||||
Spec: monitoringv1.ServiceMonitorSpec{ |
||||
Endpoints: []monitoringv1.Endpoint{ |
||||
{ |
||||
TLSConfig: &monitoringv1.TLSConfig{ |
||||
CAFile: "/path/to/ca/file", |
||||
CertFile: "/path/to/cert/file", |
||||
KeyFile: "/path/to/key/file", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
want: &monitoringv1.ServiceMonitor{ |
||||
Spec: monitoringv1.ServiceMonitorSpec{ |
||||
Endpoints: []monitoringv1.Endpoint{ |
||||
{ |
||||
TLSConfig: &monitoringv1.TLSConfig{ |
||||
CAFile: "/path/to/ca/file", |
||||
CertFile: "/path/to/cert/file", |
||||
KeyFile: "/path/to/key/file", |
||||
}, |
||||
}, |
||||
{ |
||||
Port: opaMetricsPortName, |
||||
Path: "/metrics", |
||||
Scheme: "https", |
||||
BearerTokenFile: BearerTokenFile, |
||||
TLSConfig: &monitoringv1.TLSConfig{ |
||||
CAFile: "/path/to/ca/file", |
||||
CertFile: "/path/to/cert/file", |
||||
KeyFile: "/path/to/key/file", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
for _, tc := range tc { |
||||
tc := tc |
||||
t.Run(tc.desc, func(t *testing.T) { |
||||
t.Parallel() |
||||
err := configureServiceMonitorForMode(tc.sm, tc.mode, tc.flags) |
||||
require.NoError(t, err) |
||||
require.Equal(t, tc.want, tc.sm) |
||||
}) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue