Add lokistack-gateway deployment (#65)

pull/4881/head
Sashank Agarwal 4 years ago committed by GitHub
parent e769d2dd3e
commit 2a8911352d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      api/v1beta1/lokistack_types.go
  2. 5
      api/v1beta1/zz_generated.deepcopy.go
  3. 8
      bundle/manifests/loki-operator.clusterserviceversion.yaml
  4. 36
      bundle/manifests/loki.openshift.io_lokistacks.yaml
  5. 1
      cmd/loki-broker/main.go
  6. 58
      config/crd/bases/loki.openshift.io_lokistacks.yaml
  7. 8
      config/manifests/bases/loki-operator.clusterserviceversion.yaml
  8. 9
      internal/manifests/build.go
  9. 65
      internal/manifests/build_test.go
  10. 303
      internal/manifests/gateway.go
  11. 73
      internal/manifests/gateway_test.go
  12. 58
      internal/manifests/internal/gateway/build.go
  13. 16
      internal/manifests/internal/gateway/gateway-rbac.yaml
  14. 9
      internal/manifests/internal/gateway/gateway-tenants.yaml
  15. 14
      internal/manifests/internal/gateway/options.go
  16. 28
      internal/manifests/internal/sizes.go
  17. 1
      internal/manifests/options.go
  18. 12
      internal/manifests/service_monitor.go
  19. 7
      internal/manifests/service_monitor_test.go
  20. 20
      internal/manifests/service_test.go
  21. 14
      internal/manifests/var.go
  22. 4
      main.go

@ -133,6 +133,13 @@ type LokiTemplateSpec struct {
// +kubebuilder:validation:Optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Query Frontend pods"
QueryFrontend *LokiComponentSpec `json:"queryFrontend,omitempty"`
// Gateway defines the lokistack-gateway component spec.
//
// +optional
// +kubebuilder:validation:Optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Gateway pods"
Gateway *LokiComponentSpec `json:"gateway,omitempty"`
}
// ObjectStorageSecretSpec is a secret reference containing name only, no namespace.

@ -348,6 +348,11 @@ func (in *LokiTemplateSpec) DeepCopyInto(out *LokiTemplateSpec) {
*out = new(LokiComponentSpec)
(*in).DeepCopyInto(*out)
}
if in.Gateway != nil {
in, out := &in.Gateway, &out.Gateway
*out = new(LokiComponentSpec)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LokiTemplateSpec.

@ -263,6 +263,14 @@ spec:
path: template.distributor.replicas
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: Gateway defines the lokistack-gateway component spec.
displayName: Gateway pods
path: template.gateway
- description: Replicas defines the number of replica pods of the component.
displayName: Replicas
path: template.gateway.replicas
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: Ingester defines the ingester component spec.
displayName: Ingester pods
path: template.ingester

@ -350,6 +350,42 @@ spec:
type: object
type: array
type: object
gateway:
description: Gateway defines the lokistack-gateway component spec.
properties:
nodeSelector:
additionalProperties:
type: string
description: NodeSelector defines the labels required by a node to schedule the component onto it.
type: object
replicas:
description: Replicas defines the number of replica pods of the component.
format: int32
type: integer
tolerations:
description: Tolerations defines the tolerations required by a node to schedule the component onto it.
items:
description: The pod this Toleration is attached to tolerates any taint that matches the triple <key,value,effect> using the matching operator <operator>.
properties:
effect:
description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
type: string
key:
description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.
type: string
operator:
description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.
type: string
tolerationSeconds:
description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.
format: int64
type: integer
value:
description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
type: string
type: object
type: array
type: object
ingester:
description: Ingester defines the ingester component spec.
properties:

@ -37,6 +37,7 @@ func (c *config) registerFlags(f *flag.FlagSet) {
f.BoolVar(&c.featureFlags.EnableCertificateSigningService, "with-cert-signing-service", false, "Enable usage of cert-signing service for scraping prometheus metrics via TLS.")
f.BoolVar(&c.featureFlags.EnableServiceMonitors, "with-service-monitors", false, "Enable service monitors for all LokiStack components.")
f.BoolVar(&c.featureFlags.EnableTLSServiceMonitorConfig, "with-tls-service-monitors", false, "Enable TLS endpoint for service monitors.")
f.BoolVar(&c.featureFlags.EnableGateway, "with-lokistack-gateway", false, "Enables the manifest creation for the entire lokistack-gateway.")
// Object storage options
c.objectStorage = manifests.ObjectStorage{}
f.StringVar(&c.objectStorage.Endpoint, "object-storage.endpoint", "", "The S3 endpoint location.")

@ -257,6 +257,64 @@ spec:
type: object
type: array
type: object
gateway:
description: Gateway defines the lokistack-gateway component spec.
properties:
nodeSelector:
additionalProperties:
type: string
description: NodeSelector defines the labels required by a
node to schedule the component onto it.
type: object
replicas:
description: Replicas defines the number of replica pods of
the component.
format: int32
type: integer
tolerations:
description: Tolerations defines the tolerations required
by a node to schedule the component onto it.
items:
description: The pod this Toleration is attached to tolerates
any taint that matches the triple <key,value,effect> using
the matching operator <operator>.
properties:
effect:
description: Effect indicates the taint effect to match.
Empty means match all taint effects. When specified,
allowed values are NoSchedule, PreferNoSchedule and
NoExecute.
type: string
key:
description: Key is the taint key that the toleration
applies to. Empty means match all taint keys. If the
key is empty, operator must be Exists; this combination
means to match all values and all keys.
type: string
operator:
description: Operator represents a key's relationship
to the value. Valid operators are Exists and Equal.
Defaults to Equal. Exists is equivalent to wildcard
for value, so that a pod can tolerate all taints of
a particular category.
type: string
tolerationSeconds:
description: TolerationSeconds represents the period
of time the toleration (which must be of effect NoExecute,
otherwise this field is ignored) tolerates the taint.
By default, it is not set, which means tolerate the
taint forever (do not evict). Zero and negative values
will be treated as 0 (evict immediately) by the system.
format: int64
type: integer
value:
description: Value is the taint value the toleration
matches to. If the operator is Exists, the value should
be empty, otherwise just a regular string.
type: string
type: object
type: array
type: object
ingester:
description: Ingester defines the ingester component spec.
properties:

@ -242,6 +242,14 @@ spec:
path: template.distributor.replicas
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: Gateway defines the lokistack-gateway component spec.
displayName: Gateway pods
path: template.gateway
- description: Replicas defines the number of replica pods of the component.
displayName: Replicas
path: template.gateway.replicas
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: Ingester defines the ingester component spec.
displayName: Ingester pods
path: template.ingester

@ -52,6 +52,15 @@ func BuildAll(opts Options) ([]client.Object, error) {
res = append(res, queryFrontendObjs...)
res = append(res, BuildLokiGossipRingService(opts.Name))
if opts.Flags.EnableGateway {
gatewayObjects, err := BuildGateway(opts)
if err != nil {
return nil, err
}
res = append(res, gatewayObjects...)
}
if opts.Flags.EnableServiceMonitors {
res = append(res, BuildServiceMonitors(opts)...)
}

@ -106,7 +106,7 @@ func TestBuildAll_WithFeatureFlags_EnableServiceMonitors(t *testing.T) {
},
{
desc: "service monitor per component created",
MonitorCount: 5,
MonitorCount: 6,
BuildOptions: Options{
Name: "test",
Namespace: "test",
@ -191,6 +191,7 @@ func TestBuildAll_WithFeatureFlags_EnableCertificateSigningService(t *testing.T)
NewQuerierHTTPService(tst.BuildOptions),
NewQueryFrontendHTTPService(tst.BuildOptions),
NewCompactorHTTPService(tst.BuildOptions),
NewGatewayHTTPService(tst.BuildOptions),
}
for _, service := range httpServices {
@ -204,6 +205,58 @@ func TestBuildAll_WithFeatureFlags_EnableCertificateSigningService(t *testing.T)
}
}
func TestBuildAll_WithFeatureFlags_EnableGateway(t *testing.T) {
type test struct {
desc string
BuildOptions Options
}
table := []test{
{
desc: "no lokistack-gateway created",
BuildOptions: Options{
Name: "test",
Namespace: "test",
Stack: lokiv1beta1.LokiStackSpec{
Size: lokiv1beta1.SizeOneXSmall,
},
Flags: FeatureFlags{
EnableGateway: false,
EnableTLSServiceMonitorConfig: false,
},
},
},
{
desc: "lokistack-gateway created",
BuildOptions: Options{
Name: "test",
Namespace: "test",
Stack: lokiv1beta1.LokiStackSpec{
Size: lokiv1beta1.SizeOneXSmall,
},
Flags: FeatureFlags{
EnableGateway: true,
EnableTLSServiceMonitorConfig: true,
},
},
},
}
for _, tst := range table {
tst := tst
t.Run(tst.desc, func(t *testing.T) {
t.Parallel()
err := ApplyDefaultSettings(&tst.BuildOptions)
require.NoError(t, err)
objects, buildErr := BuildAll(tst.BuildOptions)
require.NoError(t, buildErr)
if tst.BuildOptions.Flags.EnableGateway {
require.True(t, checkGatewayDeployed(objects, tst.BuildOptions.Name))
} else {
require.False(t, checkGatewayDeployed(objects, tst.BuildOptions.Name))
}
})
}
}
func serviceMonitorCount(objects []client.Object) int {
monitors := 0
for _, obj := range objects {
@ -213,3 +266,13 @@ func serviceMonitorCount(objects []client.Object) int {
}
return monitors
}
func checkGatewayDeployed(objects []client.Object, stackName string) bool {
for _, obj := range objects {
if obj.GetObjectKind().GroupVersionKind().Kind == "Deployment" &&
obj.GetName() == GatewayName(stackName) {
return true
}
}
return false
}

@ -0,0 +1,303 @@
package manifests
import (
"crypto/sha1"
"fmt"
"path"
"github.com/ViaQ/logerr/kverrors"
"github.com/imdario/mergo"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/ViaQ/loki-operator/internal/manifests/internal/gateway"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
)
// BuildGateway returns a list of k8s objects for Loki Stack Gateway
func BuildGateway(opts Options) ([]client.Object, error) {
gatewayCm, sha1C, err := gatewayConfigMap(opts)
if err != nil {
return nil, err
}
deployment := NewGatewayDeployment(opts, sha1C)
if opts.Flags.EnableTLSServiceMonitorConfig {
if err := configureGatewayMetricsPKI(&deployment.Spec.Template.Spec); err != nil {
return nil, err
}
}
return []client.Object{
gatewayCm,
deployment,
NewGatewayHTTPService(opts),
}, nil
}
// NewGatewayDeployment creates a deployment object for a lokiStack-gateway
func NewGatewayDeployment(opts Options, sha1C string) *appsv1.Deployment {
podSpec := corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "rbac",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: LabelGatewayComponent,
},
},
},
},
{
Name: "tenants",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: LabelGatewayComponent,
},
},
},
},
},
Containers: []corev1.Container{
{
Name: LabelGatewayComponent,
Image: DefaultLokiStackGatewayImage,
Resources: corev1.ResourceRequirements{
Limits: opts.ResourceRequirements.Gateway.Limits,
Requests: opts.ResourceRequirements.Gateway.Requests,
},
Args: []string{
fmt.Sprintf("--debug.name=%s", LabelGatewayComponent),
"--web.listen=0.0.0.0:8080",
"--web.internal.listen=0.0.0.0:8081",
"--log.level=debug",
fmt.Sprintf("--logs.read.endpoint=http://%s:%d", fqdn(serviceNameQueryFrontendHTTP(opts.Name), opts.Namespace), httpPort),
fmt.Sprintf("--logs.tail.endpoint=http://%s:%d", fqdn(serviceNameQueryFrontendHTTP(opts.Name), opts.Namespace), httpPort),
fmt.Sprintf("--logs.write.endpoint=http://%s:%d", fqdn(serviceNameDistributorHTTP(opts.Name), opts.Namespace), httpPort),
fmt.Sprintf("--rbac.config=%s", path.Join(gateway.LokiGatewayMountDir, gateway.LokiGatewayRbacFileName)),
fmt.Sprintf("--tenants.config=%s", path.Join(gateway.LokiGatewayMountDir, gateway.LokiGatewayTenantFileName)),
},
Ports: []corev1.ContainerPort{
{
Name: "internal",
ContainerPort: 8081,
},
{
Name: "public",
ContainerPort: 8080,
},
{
Name: "metrics",
ContainerPort: httpPort,
},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "rbac",
ReadOnly: true,
MountPath: path.Join(gateway.LokiGatewayMountDir, gateway.LokiGatewayRbacFileName),
SubPath: "rbac.yaml",
},
{
Name: "tenants",
ReadOnly: true,
MountPath: path.Join(gateway.LokiGatewayMountDir, gateway.LokiGatewayTenantFileName),
SubPath: "tenants.yaml",
},
},
LivenessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/live",
Port: intstr.FromInt(8081),
Scheme: corev1.URISchemeHTTP,
},
},
TimeoutSeconds: 2,
PeriodSeconds: 30,
FailureThreshold: 10,
},
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/ready",
Port: intstr.FromInt(8081),
Scheme: corev1.URISchemeHTTP,
},
},
TimeoutSeconds: 1,
PeriodSeconds: 5,
FailureThreshold: 12,
},
},
},
}
l := ComponentLabels(LabelGatewayComponent, opts.Name)
a := commonAnnotations(sha1C)
return &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: appsv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: GatewayName(opts.Name),
Labels: l,
},
Spec: appsv1.DeploymentSpec{
Replicas: pointer.Int32Ptr(1),
Selector: &metav1.LabelSelector{
MatchLabels: labels.Merge(l, GossipLabels()),
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Name: GatewayName(opts.Name),
Labels: labels.Merge(l, GossipLabels()),
Annotations: a,
},
Spec: podSpec,
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
},
}
}
// NewGatewayHTTPService creates a k8s service for the lokistack-gateway HTTP endpoint
func NewGatewayHTTPService(opts Options) *corev1.Service {
serviceName := serviceNameGatewayHTTP(opts.Name)
l := ComponentLabels(LabelGatewayComponent, opts.Name)
a := serviceAnnotations(serviceName, opts.Flags.EnableCertificateSigningService)
return &corev1.Service{
TypeMeta: metav1.TypeMeta{
Kind: "Service",
APIVersion: corev1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Labels: l,
Annotations: a,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "metrics",
Port: httpPort,
},
},
Selector: l,
},
}
}
// gatewayConfigMap creates a configMap for rbac.yaml and tenants.yaml
func gatewayConfigMap(opt Options) (*corev1.ConfigMap, string, error) {
cfg := gatewayConfigOptions(opt)
rbacConfig, tenantsConfig, err := gateway.Build(cfg)
if err != nil {
return nil, "", err
}
s := sha1.New()
_, err = s.Write(rbacConfig)
if err != nil {
return nil, "", err
}
sha1C := fmt.Sprintf("%x", s.Sum(nil))
return &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: corev1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: LabelGatewayComponent,
Labels: commonLabels(opt.Name),
},
BinaryData: map[string][]byte{
gateway.LokiGatewayRbacFileName: rbacConfig,
gateway.LokiGatewayTenantFileName: tenantsConfig,
},
}, sha1C, nil
}
// gatewayConfigOptions converts Options to gateway.Options
func gatewayConfigOptions(opt Options) gateway.Options {
return gateway.Options{
Stack: opt.Stack,
Namespace: opt.Namespace,
Name: opt.Name,
}
}
func configureGatewayMetricsPKI(podSpec *corev1.PodSpec) error {
secretVolumeSpec := corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "tls-secret",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: LabelGatewayComponent,
},
},
},
{
Name: "tls-configmap",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: LabelGatewayComponent,
},
},
},
},
},
}
secretContainerSpec := corev1.Container{
VolumeMounts: []corev1.VolumeMount{
{
Name: "tls-secret",
ReadOnly: true,
MountPath: path.Join(gateway.LokiGatewayTLSDir, "cert"),
SubPath: "cert",
},
{
Name: "tls-secret",
ReadOnly: true,
MountPath: path.Join(gateway.LokiGatewayTLSDir, "key"),
SubPath: "key",
},
{
Name: "tls-configmap",
ReadOnly: true,
MountPath: path.Join(gateway.LokiGatewayTLSDir, "ca"),
SubPath: "ca",
},
},
Args: []string{
fmt.Sprintf("--tls.internal.server.cert-file=%s", path.Join(gateway.LokiGatewayTLSDir, "cert")),
fmt.Sprintf("--tls.internal.server.key-file=%s", path.Join(gateway.LokiGatewayTLSDir, "key")),
fmt.Sprintf("--tls.healthchecks.server-ca-file=%s", path.Join(gateway.LokiGatewayTLSDir, "ca")),
},
}
if err := mergo.Merge(podSpec, secretVolumeSpec, mergo.WithAppendSlice); err != nil {
return kverrors.Wrap(err, "failed to merge volumes")
}
if err := mergo.Merge(&podSpec.Containers[0], secretContainerSpec, mergo.WithAppendSlice); err != nil {
return kverrors.Wrap(err, "failed to merge container")
}
return nil
}

@ -0,0 +1,73 @@
package manifests
import (
"math/rand"
"testing"
lokiv1beta1 "github.com/ViaQ/loki-operator/api/v1beta1"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
func TestNewGatewayDeployment_HasTemplateConfigHashAnnotation(t *testing.T) {
sha1C := "deadbeef"
ss := NewGatewayDeployment(Options{
Name: "abcd",
Namespace: "efgh",
Stack: lokiv1beta1.LokiStackSpec{
Template: &lokiv1beta1.LokiTemplateSpec{
Compactor: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
Distributor: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
Ingester: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
Querier: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
},
},
}, sha1C)
expected := "loki.openshift.io/config-hash"
annotations := ss.Spec.Template.Annotations
require.Contains(t, annotations, expected)
require.Equal(t, annotations[expected], sha1C)
}
func TestGatewayConfigMap_ReturnsSHA1OfBinaryContents(t *testing.T) {
opts := Options{
Name: uuid.New().String(),
Namespace: uuid.New().String(),
Image: uuid.New().String(),
Stack: lokiv1beta1.LokiStackSpec{
Template: &lokiv1beta1.LokiTemplateSpec{
Compactor: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
Distributor: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
Ingester: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
Querier: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: rand.Int31(),
},
},
},
}
_, sha1C, err := gatewayConfigMap(opts)
require.NoError(t, err)
require.NotEmpty(t, sha1C)
}

@ -0,0 +1,58 @@
package gateway
import (
"bytes"
"embed"
"io/ioutil"
"text/template"
"github.com/ViaQ/logerr/kverrors"
)
const (
// LokiGatewayTenantFileName is the name of the tenant config file in the configmap
LokiGatewayTenantFileName = "tenants.yaml"
// LokiGatewayRbacFileName is the name of the rbac config file in the configmap
LokiGatewayRbacFileName = "rbac.yaml"
// LokiGatewayMountDir is the path that is mounted from the configmap
LokiGatewayMountDir = "/etc/lokistack-gateway"
// LokiGatewayTLSDir is the path that is mounted from the configmap for TLS
LokiGatewayTLSDir = "/var/run/tls"
)
var (
//go:embed gateway-rbac.yaml
lokiGatewayRbacYAMLTmplFile embed.FS
//go:embed gateway-tenants.yaml
lokiGatewayTenantsYAMLTmplFile embed.FS
lokiGatewayRbacYAMLTmpl = template.Must(template.ParseFS(lokiGatewayRbacYAMLTmplFile, "gateway-rbac.yaml"))
lokiGatewayTenantsYAMLTmpl = template.Must(template.ParseFS(lokiGatewayTenantsYAMLTmplFile, "gateway-tenants.yaml"))
)
// Build builds a loki gateway configuration files
func Build(opts Options) ([]byte, []byte, error) {
// Build loki gateway rbac yaml
w := bytes.NewBuffer(nil)
err := lokiGatewayRbacYAMLTmpl.Execute(w, opts)
if err != nil {
return nil, nil, kverrors.Wrap(err, "failed to create loki gateway rbac configuration")
}
rbacCfg, err := ioutil.ReadAll(w)
if err != nil {
return nil, nil, kverrors.Wrap(err, "failed to read configuration from buffer")
}
// Build loki gateway tenants yaml
w = bytes.NewBuffer(nil)
err = lokiGatewayTenantsYAMLTmpl.Execute(w, opts)
if err != nil {
return nil, nil, kverrors.Wrap(err, "failed to create loki gateway tenants configuration")
}
tenantsCfg, err := ioutil.ReadAll(w)
if err != nil {
return nil, nil, kverrors.Wrap(err, "failed to read configuration from buffer")
}
return rbacCfg, tenantsCfg, nil
}

@ -0,0 +1,16 @@
roleBindings:
- name: telemeter
roles:
- read-write
subjects:
- kind: user
name: admin@example.com
roles:
- name: read-write
permissions:
- read
- write
resources:
- metrics
tenants:
- telemeter

@ -0,0 +1,9 @@
tenants:
- name: telemeter
id: FB870BF3-9F3A-44FF-9BF7-D7A047A52F43
oidc:
clientID: telemeter
clientSecret: test
issuerURL: http://127.0.0.1:5556/dex
redirectURL: http://localhost:8080/oidc/telemeter/callback
usernameClaim: email

@ -0,0 +1,14 @@
package gateway
import (
lokiv1beta1 "github.com/ViaQ/loki-operator/api/v1beta1"
)
// Options is used to render the rbac.yaml and tenants.yaml file template
type Options struct {
Stack lokiv1beta1.LokiStackSpec
Namespace string
Name string
StorageDirectory string
}

@ -14,6 +14,7 @@ type ComponentResources struct {
// these two don't need a PVCSize
Distributor corev1.ResourceRequirements
QueryFrontend corev1.ResourceRequirements
Gateway corev1.ResourceRequirements
}
// ResourceRequirements sets CPU, Memory, and PVC requirements for a component
@ -59,6 +60,12 @@ var ResourceRequirementsTable = map[lokiv1beta1.LokiStackSizeType]ComponentResou
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
},
Gateway: corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("256Mi"),
},
},
},
lokiv1beta1.SizeOneXSmall: {
Querier: ResourceRequirements{
@ -94,6 +101,12 @@ var ResourceRequirementsTable = map[lokiv1beta1.LokiStackSizeType]ComponentResou
corev1.ResourceMemory: resource.MustParse("4Gi"),
},
},
Gateway: corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
},
},
lokiv1beta1.SizeOneXMedium: {
Querier: ResourceRequirements{
@ -129,6 +142,12 @@ var ResourceRequirementsTable = map[lokiv1beta1.LokiStackSizeType]ComponentResou
corev1.ResourceMemory: resource.MustParse("4Gi"),
},
},
Gateway: corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
},
},
}
@ -173,6 +192,9 @@ var StackSizeTable = map[lokiv1beta1.LokiStackSizeType]lokiv1beta1.LokiStackSpec
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: 1,
},
Gateway: &lokiv1beta1.LokiComponentSpec{
Replicas: 2,
},
},
},
@ -216,6 +238,9 @@ var StackSizeTable = map[lokiv1beta1.LokiStackSizeType]lokiv1beta1.LokiStackSpec
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: 2,
},
Gateway: &lokiv1beta1.LokiComponentSpec{
Replicas: 2,
},
},
},
@ -259,6 +284,9 @@ var StackSizeTable = map[lokiv1beta1.LokiStackSizeType]lokiv1beta1.LokiStackSpec
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: 2,
},
Gateway: &lokiv1beta1.LokiComponentSpec{
Replicas: 2,
},
},
},
}

@ -35,4 +35,5 @@ type FeatureFlags struct {
EnableCertificateSigningService bool
EnableServiceMonitors bool
EnableTLSServiceMonitorConfig bool
EnableGateway bool
}

@ -20,6 +20,7 @@ func BuildServiceMonitors(opts Options) []client.Object {
NewQuerierServiceMonitor(opts),
NewCompactorServiceMonitor(opts),
NewQueryFrontendServiceMonitor(opts),
NewGatewayServiceMonitor(opts),
}
}
@ -78,6 +79,17 @@ func NewQueryFrontendServiceMonitor(opts Options) *monitoringv1.ServiceMonitor {
return newServiceMonitor(opts.Namespace, serviceMonitorName, l, lokiEndpoint)
}
// NewGatewayServiceMonitor creates a k8s service monitor for the lokistack-gateway component
func NewGatewayServiceMonitor(opts Options) *monitoringv1.ServiceMonitor {
l := ComponentLabels(LabelGatewayComponent, opts.Name)
serviceMonitorName := serviceMonitorName(GatewayName(opts.Name))
serviceName := serviceNameGatewayHTTP(opts.Name)
lokiEndpoint := serviceMonitorLokiEndPoint(opts.Name, serviceName, opts.Namespace, opts.Flags.EnableTLSServiceMonitorConfig)
return newServiceMonitor(opts.Namespace, serviceMonitorName, l, lokiEndpoint)
}
func newServiceMonitor(namespace, serviceMonitorName string, labels labels.Set, endpoint monitoringv1.Endpoint) *monitoringv1.ServiceMonitor {
return &monitoringv1.ServiceMonitor{
TypeMeta: metav1.TypeMeta{

@ -48,6 +48,9 @@ func TestServiceMonitorMatchLabels(t *testing.T) {
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: 1,
},
Gateway: &lokiv1beta1.LokiComponentSpec{
Replicas: 1,
},
},
},
}
@ -73,6 +76,10 @@ func TestServiceMonitorMatchLabels(t *testing.T) {
Service: NewCompactorHTTPService(opt),
ServiceMonitor: NewCompactorServiceMonitor(opt),
},
{
Service: NewGatewayHTTPService(opt),
ServiceMonitor: NewGatewayServiceMonitor(opt),
},
}
for _, tst := range table {

@ -38,9 +38,13 @@ func TestServicesMatchPorts(t *testing.T) {
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: 1,
},
Gateway: &lokiv1beta1.LokiComponentSpec{
Replicas: 1,
},
},
},
}
sha1C := "deadbef"
table := []test{
{
@ -78,6 +82,12 @@ func TestServicesMatchPorts(t *testing.T) {
NewCompactorHTTPService(opt),
},
},
{
Containers: NewGatewayDeployment(opt, sha1C).Spec.Template.Spec.Containers,
Services: []*corev1.Service{
NewGatewayHTTPService(opt),
},
},
}
containerHasPort := func(containers []corev1.Container, port int32) bool {
@ -137,9 +147,13 @@ func TestServicesMatchLabels(t *testing.T) {
QueryFrontend: &lokiv1beta1.LokiComponentSpec{
Replicas: 1,
},
Gateway: &lokiv1beta1.LokiComponentSpec{
Replicas: 1,
},
},
},
}
sha1C := "deadbef"
table := []test{
{
@ -177,6 +191,12 @@ func TestServicesMatchLabels(t *testing.T) {
NewCompactorHTTPService(opt),
},
},
{
Object: NewGatewayDeployment(opt, sha1C),
Services: []*corev1.Service{
NewGatewayHTTPService(opt),
},
},
}
for _, tst := range table {

@ -16,6 +16,9 @@ const (
// DefaultContainerImage declares the default fallback for loki image.
DefaultContainerImage = "docker.io/grafana/loki:2.2.1"
// DefaultLokiStackGatewayImage declares the default image for lokiStack-gateway.
DefaultLokiStackGatewayImage = "quay.io/observatorium/api:latest"
// PrometheusCAFile declares the path for prometheus CA file for service monitors.
PrometheusCAFile string = "/etc/prometheus/configmaps/serving-certs-ca-bundle/service-ca.crt"
// BearerTokenFile declares the path for bearer token file for service monitors.
@ -36,6 +39,8 @@ const (
LabelQuerierComponent string = "querier"
// LabelQueryFrontendComponent is the label value for the query frontend component
LabelQueryFrontendComponent string = "query-frontend"
// LabelGatewayComponent is the label value for the lokiStack-gateway component
LabelGatewayComponent string = "lokistack-gateway"
)
var (
@ -104,6 +109,11 @@ func QueryFrontendName(stackName string) string {
return fmt.Sprintf("loki-query-frontend-%s", stackName)
}
// GatewayName is the name of the lokiStack-gateway statefulset
func GatewayName(stackName string) string {
return fmt.Sprintf("lokistack-gateway-%s", stackName)
}
func serviceNameQuerierHTTP(stackName string) string {
return fmt.Sprintf("loki-querier-http-%s", stackName)
}
@ -144,6 +154,10 @@ func serviceNameQueryFrontendHTTP(stackName string) string {
return fmt.Sprintf("loki-query-frontend-http-%s", stackName)
}
func serviceNameGatewayHTTP(stackName string) string {
return fmt.Sprintf("lokistack-gateway-http-%s", stackName)
}
func serviceMonitorName(componentName string) string {
return fmt.Sprintf("monitor-%s", componentName)
}

@ -57,6 +57,7 @@ func main() {
enableCertSigning bool
enableServiceMonitors bool
enableTLSServiceMonitors bool
enableGateway bool
)
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
@ -69,6 +70,8 @@ func main() {
flag.BoolVar(&enableServiceMonitors, "with-service-monitors", false, "Enables service monitoring")
flag.BoolVar(&enableTLSServiceMonitors, "with-tls-service-monitors", false,
"Enables loading of a prometheus service monitor.")
flag.BoolVar(&enableGateway, "with-lokistack-gateway", false,
"Enables the manifest creation for the entire lokistack-gateway.")
flag.Parse()
log.Init("loki-operator")
@ -95,6 +98,7 @@ func main() {
EnableCertificateSigningService: enableCertSigning,
EnableServiceMonitors: enableServiceMonitors,
EnableTLSServiceMonitorConfig: enableTLSServiceMonitors,
EnableGateway: enableGateway,
}
if err = (&controllers.LokiStackReconciler{

Loading…
Cancel
Save