operator: Add support for configuring HTTP server timeouts (#9405)

Co-authored-by: Robert Jacob <rojacob@redhat.com>
pull/9485/head^2
Periklis Tsirakidis 3 years ago committed by GitHub
parent a205dce836
commit 1b87aeab4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      operator/CHANGELOG.md
  2. 4
      operator/apis/loki/v1/lokistack_types.go
  3. 2
      operator/bundle/community-openshift/manifests/loki-operator.clusterserviceversion.yaml
  4. 4
      operator/bundle/community-openshift/manifests/loki.grafana.com_lokistacks.yaml
  5. 2
      operator/bundle/community/manifests/loki-operator.clusterserviceversion.yaml
  6. 4
      operator/bundle/community/manifests/loki.grafana.com_lokistacks.yaml
  7. 2
      operator/bundle/openshift/manifests/loki-operator.clusterserviceversion.yaml
  8. 4
      operator/bundle/openshift/manifests/loki.grafana.com_lokistacks.yaml
  9. 4
      operator/config/crd/bases/loki.grafana.com_lokistacks.yaml
  10. 21
      operator/docs/operator/api.md
  11. 11
      operator/internal/handlers/lokistack_create_or_update.go
  12. 72
      operator/internal/handlers/lokistack_create_or_update_test.go
  13. 16
      operator/internal/manifests/build_test.go
  14. 1
      operator/internal/manifests/config.go
  15. 56
      operator/internal/manifests/config_test.go
  16. 3
      operator/internal/manifests/gateway.go
  17. 6
      operator/internal/manifests/gateway_tenants.go
  18. 45
      operator/internal/manifests/gateway_tenants_test.go
  19. 23
      operator/internal/manifests/gateway_test.go
  20. 101
      operator/internal/manifests/internal/config/build_test.go
  21. 5
      operator/internal/manifests/internal/config/loki-config.yaml
  22. 10
      operator/internal/manifests/internal/config/options.go
  23. 26
      operator/internal/manifests/openshift/build_test.go
  24. 4
      operator/internal/manifests/openshift/options.go
  25. 5
      operator/internal/manifests/openshift/route.go
  26. 7
      operator/internal/manifests/openshift/var.go
  27. 82
      operator/internal/manifests/options.go
  28. 142
      operator/internal/manifests/options_test.go
  29. 3
      operator/internal/manifests/service_test.go
  30. 14
      operator/internal/manifests/var.go

@ -1,5 +1,6 @@
## Main
- [9405](https://github.com/grafana/loki/pull/9405) **periklis**: Add support for configuring HTTP server timeouts
- [9378](https://github.com/grafana/loki/pull/9378) **aminesnow**: Add zone aware API spec validation
- [9408](https://github.com/grafana/loki/pull/9408) **JoaoBraveCoding**: Add PodAntiAffinity overwrites per component
- [9429](https://github.com/grafana/loki/pull/9429) **aminesnow**: Add default TopologySpreadContraints to Gateway

@ -561,7 +561,7 @@ type QueryLimitSpec struct {
//
// +optional
// +kubebuilder:validation:Optional
// +kubebuilder:default:="1m"
// +kubebuilder:default:="3m"
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Query Timeout"
QueryTimeout string `json:"queryTimeout,omitempty"`
}
@ -913,6 +913,8 @@ const (
ReasonMissingGatewayOpenShiftBaseDomain LokiStackConditionReason = "MissingGatewayOpenShiftBaseDomain"
// ReasonFailedCertificateRotation when the reconciler cannot rotate any of the required TLS certificates.
ReasonFailedCertificateRotation LokiStackConditionReason = "FailedCertificateRotation"
// ReasonQueryTimeoutInvalid when the QueryTimeout can not be parsed.
ReasonQueryTimeoutInvalid LokiStackConditionReason = "ReasonQueryTimeoutInvalid"
)
// PodStatusMap defines the type for mapping pod status to pod name.

@ -150,7 +150,7 @@ metadata:
categories: OpenShift Optional, Logging & Tracing
certified: "false"
containerImage: docker.io/grafana/loki-operator:main-ac1c1fd
createdAt: "2023-05-11T08:04:29Z"
createdAt: "2023-05-22T15:22:48Z"
description: The Community Loki Operator provides Kubernetes native deployment
and management of Loki and related logging components.
operators.operatorframework.io/builder: operator-sdk-unknown

@ -151,7 +151,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string
@ -264,7 +264,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string

@ -150,7 +150,7 @@ metadata:
categories: OpenShift Optional, Logging & Tracing
certified: "false"
containerImage: docker.io/grafana/loki-operator:main-ac1c1fd
createdAt: "2023-05-11T08:04:26Z"
createdAt: "2023-05-22T15:22:44Z"
description: The Community Loki Operator provides Kubernetes native deployment
and management of Loki and related logging components.
operators.operatorframework.io/builder: operator-sdk-unknown

@ -151,7 +151,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string
@ -264,7 +264,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string

@ -150,7 +150,7 @@ metadata:
categories: OpenShift Optional, Logging & Tracing
certified: "false"
containerImage: quay.io/openshift-logging/loki-operator:v0.1.0
createdAt: "2023-05-11T08:04:32Z"
createdAt: "2023-05-22T15:22:53Z"
description: |
The Loki Operator for OCP provides a means for configuring and managing a Loki stack for cluster logging.
## Prerequisites and Requirements

@ -151,7 +151,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string
@ -264,7 +264,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string

@ -134,7 +134,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string
@ -247,7 +247,7 @@ spec:
format: int32
type: integer
queryTimeout:
default: 1m
default: 3m
description: Timeout when querying ingesters or storage
during the execution of a query request.
type: string

@ -1302,6 +1302,21 @@ the component onto it.</p>
the component onto it.</p>
</td>
</tr>
<tr>
<td>
<code>podAntiAffinity</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#podantiaffinity-v1-core">
Kubernetes core/v1.PodAntiAffinity
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>PodAntiAffinity defines the pod anti affinity scheduling rules to schedule pods
of a component.</p>
</td>
</tr>
</tbody>
</table>
@ -1553,6 +1568,9 @@ for the ruler is missing.</p>
</tr><tr><td><p>&#34;PendingComponents&#34;</p></td>
<td><p>ReasonPendingComponents when all/some LokiStack components pending dependencies</p>
</td>
</tr><tr><td><p>&#34;ReasonQueryTimeoutInvalid&#34;</p></td>
<td><p>ReasonQueryTimeoutInvalid when the QueryTimeout can not be parsed.</p>
</td>
</tr><tr><td><p>&#34;ReadyComponents&#34;</p></td>
<td><p>ReasonReadyComponents when all LokiStack components are ready to serve traffic.</p>
</td>
@ -3329,7 +3347,8 @@ int32
</td>
<td>
<em>(Optional)</em>
<p>Zones defines an array of ZoneSpec that the scheduler will try to satisfy.</p>
<p>Zones defines an array of ZoneSpec that the scheduler will try to satisfy.
IMPORTANT: Make sure that the replication factor defined is less than or equal to the number of available zones.</p>
</td>
</tr>
</tbody>

@ -269,6 +269,16 @@ func CreateOrUpdateLokiStack(
certRotationRequiredAt = stack.Annotations[manifests.AnnotationCertRotationRequiredAt]
}
timeoutConfig, err := manifests.NewTimeoutConfig(stack.Spec.Limits)
if err != nil {
ll.Error(err, "failed to parse query timeout")
return &status.DegradedError{
Message: fmt.Sprintf("Error parsing query timeout: %s", err),
Reason: lokiv1.ReasonQueryTimeoutInvalid,
Requeue: false,
}
}
// Here we will translate the lokiv1.LokiStack options into manifest options
opts := manifests.Options{
Name: req.Name,
@ -286,6 +296,7 @@ func CreateOrUpdateLokiStack(
Spec: rulerConfig,
Secret: rulerSecret,
},
Timeouts: timeoutConfig,
Tenants: manifests.Tenants{
Secrets: tenantSecrets,
Configs: tenantConfigs,

@ -1385,6 +1385,78 @@ func TestCreateOrUpdateLokiStack_MissingTenantsSpec_SetDegraded(t *testing.T) {
require.Equal(t, degradedErr, err)
}
func TestCreateOrUpdateLokiStack_WhenInvalidQueryTimeout_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: `Error parsing query timeout: time: invalid duration "invalid"`,
Reason: lokiv1.ReasonQueryTimeoutInvalid,
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.ObjectStorageSchemaV12,
EffectiveDate: "2023-05-22",
},
},
Secret: lokiv1.ObjectStorageSecretSpec{
Name: defaultSecret.Name,
Type: lokiv1.ObjectStorageSecretS3,
},
},
Tenants: &lokiv1.TenantsSpec{
Mode: "openshift",
},
Limits: &lokiv1.LimitsSpec{
Global: &lokiv1.LimitsTemplateSpec{
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "invalid",
},
},
},
},
}
// Create looks up the CR first, so we need to return our fake stack
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object, _ ...client.GetOption) error {
if r.Name == name.Name && r.Namespace == name.Namespace {
k.SetClientObject(object, stack)
}
if defaultSecret.Name == name.Name {
k.SetClientObject(object, &defaultSecret)
}
return nil
}
k.StatusStub = func() client.StatusWriter { return sw }
err := handlers.CreateOrUpdateLokiStack(context.TODO(), logger, r, k, scheme, featureGates)
// make sure error is returned
require.Error(t, err)
require.Equal(t, degradedErr, err)
}
func TestCreateOrUpdateLokiStack_RemovesRulerResourcesWhenDisabled(t *testing.T) {
sw := &k8sfakes.FakeStatusWriter{}
k := &k8sfakes.FakeClient{}

@ -36,6 +36,7 @@ func TestApplyUserOptions_OverrideDefaults(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
}
err := ApplyDefaultSettings(&opt)
defs := internal.StackSizeTable[size]
@ -78,6 +79,7 @@ func TestApplyUserOptions_AlwaysSetCompactorReplicasToOne(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
}
err := ApplyDefaultSettings(&opt)
defs := internal.StackSizeTable[size]
@ -232,6 +234,7 @@ func TestBuildAll_WithFeatureGates_ServiceMonitors(t *testing.T) {
ServingCertsService: false,
},
},
Timeouts: defaultTimeoutConfig,
},
},
{
@ -250,6 +253,7 @@ func TestBuildAll_WithFeatureGates_ServiceMonitors(t *testing.T) {
ServingCertsService: false,
},
},
Timeouts: defaultTimeoutConfig,
},
},
}
@ -292,6 +296,7 @@ func TestBuildAll_WithFeatureGates_OpenShift_ServingCertsService(t *testing.T) {
ServingCertsService: false,
},
},
Timeouts: defaultTimeoutConfig,
},
},
{
@ -309,6 +314,7 @@ func TestBuildAll_WithFeatureGates_OpenShift_ServingCertsService(t *testing.T) {
ServingCertsService: true,
},
},
Timeouts: defaultTimeoutConfig,
},
},
}
@ -349,6 +355,7 @@ func TestBuildAll_WithFeatureGates_HTTPEncryption(t *testing.T) {
Gates: configv1.FeatureGates{
HTTPEncryption: true,
},
Timeouts: defaultTimeoutConfig,
}
err := ApplyDefaultSettings(&opts)
@ -422,6 +429,7 @@ func TestBuildAll_WithFeatureGates_ServiceMonitorTLSEndpoints(t *testing.T) {
HTTPEncryption: true,
ServiceMonitorTLSEndpoints: true,
},
Timeouts: defaultTimeoutConfig,
}
err := ApplyDefaultSettings(&opts)
@ -526,6 +534,7 @@ func TestBuildAll_WithFeatureGates_GRPCEncryption(t *testing.T) {
Gates: configv1.FeatureGates{
GRPCEncryption: false,
},
Timeouts: defaultTimeoutConfig,
},
},
{
@ -568,6 +577,7 @@ func TestBuildAll_WithFeatureGates_GRPCEncryption(t *testing.T) {
Gates: configv1.FeatureGates{
GRPCEncryption: true,
},
Timeouts: defaultTimeoutConfig,
},
},
}
@ -692,6 +702,7 @@ func TestBuildAll_WithFeatureGates_RuntimeSeccompProfile(t *testing.T) {
Gates: configv1.FeatureGates{
RuntimeSeccompProfile: false,
},
Timeouts: defaultTimeoutConfig,
},
},
{
@ -734,6 +745,7 @@ func TestBuildAll_WithFeatureGates_RuntimeSeccompProfile(t *testing.T) {
Gates: configv1.FeatureGates{
RuntimeSeccompProfile: true,
},
Timeouts: defaultTimeoutConfig,
},
},
}
@ -797,6 +809,7 @@ func TestBuildAll_WithFeatureGates_LokiStackGateway(t *testing.T) {
HTTPEncryption: true,
ServiceMonitorTLSEndpoints: false,
},
Timeouts: defaultTimeoutConfig,
},
},
{
@ -835,6 +848,7 @@ func TestBuildAll_WithFeatureGates_LokiStackGateway(t *testing.T) {
HTTPEncryption: true,
ServiceMonitorTLSEndpoints: true,
},
Timeouts: defaultTimeoutConfig,
},
},
}
@ -873,6 +887,7 @@ func TestBuildAll_WithFeatureGates_LokiStackAlerts(t *testing.T) {
ServiceMonitors: false,
LokiStackAlerts: false,
},
Timeouts: defaultTimeoutConfig,
},
},
{
@ -887,6 +902,7 @@ func TestBuildAll_WithFeatureGates_LokiStackAlerts(t *testing.T) {
ServiceMonitors: true,
LokiStackAlerts: true,
},
Timeouts: defaultTimeoutConfig,
},
},
}

@ -172,6 +172,7 @@ func ConfigOptions(opt Options) config.Options {
IngesterMemoryRequest: opt.ResourceRequirements.Ingester.Requests.Memory().Value(),
},
ObjectStorage: opt.ObjectStorage,
HTTPTimeouts: opt.Timeouts.Loki,
EnableRemoteReporting: opt.Gates.GrafanaLabsUsageReport,
Ruler: config.Ruler{
Enabled: rulerEnabled,

@ -4,6 +4,7 @@ import (
"encoding/json"
"math/rand"
"testing"
"time"
"github.com/google/uuid"
@ -41,11 +42,22 @@ func TestConfigOptions_UserOptionsTakePrecedence(t *testing.T) {
assert.JSONEq(t, string(expected), string(actual))
}
func testTimeoutConfig() TimeoutConfig {
return TimeoutConfig{
Loki: config.HTTPTimeoutConfig{
IdleTimeout: 1 * time.Second,
ReadTimeout: 1 * time.Minute,
WriteTimeout: 10 * time.Minute,
},
}
}
func randomConfigOptions() Options {
return Options{
Name: uuid.New().String(),
Namespace: uuid.New().String(),
Image: uuid.New().String(),
Timeouts: testTimeoutConfig(),
Stack: lokiv1.LokiStackSpec{
Size: lokiv1.SizeOneXExtraSmall,
Storage: lokiv1.ObjectStorageSpec{},
@ -256,6 +268,7 @@ func TestConfigOptions_GossipRingConfig(t *testing.T) {
Name: "my-stack",
Namespace: "my-ns",
Stack: tc.spec,
Timeouts: testTimeoutConfig(),
}
options := ConfigOptions(inOpt)
require.Equal(t, tc.wantOptions, options.GossipRing)
@ -361,7 +374,8 @@ func TestConfigOptions_RetentionConfig(t *testing.T) {
t.Parallel()
inOpt := Options{
Stack: tc.spec,
Stack: tc.spec,
Timeouts: testTimeoutConfig(),
}
options := ConfigOptions(inOpt)
require.Equal(t, tc.wantOptions, options.Retention)
@ -383,6 +397,7 @@ func TestConfigOptions_RulerAlertManager(t *testing.T) {
Mode: lokiv1.Static,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -394,6 +409,7 @@ func TestConfigOptions_RulerAlertManager(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -405,6 +421,7 @@ func TestConfigOptions_RulerAlertManager(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: testTimeoutConfig(),
OpenShiftOptions: openshift.Options{
BuildOpts: openshift.BuildOptions{
AlertManagerEnabled: true,
@ -426,6 +443,7 @@ func TestConfigOptions_RulerAlertManager(t *testing.T) {
Mode: lokiv1.OpenshiftNetwork,
},
},
Timeouts: testTimeoutConfig(),
OpenShiftOptions: openshift.Options{
BuildOpts: openshift.BuildOptions{
AlertManagerEnabled: true,
@ -469,6 +487,7 @@ func TestConfigOptions_RulerAlertManager_UserOverride(t *testing.T) {
Mode: lokiv1.Static,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -480,6 +499,7 @@ func TestConfigOptions_RulerAlertManager_UserOverride(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -494,6 +514,7 @@ func TestConfigOptions_RulerAlertManager_UserOverride(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -530,6 +551,7 @@ func TestConfigOptions_RulerAlertManager_UserOverride(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -584,6 +606,7 @@ func TestConfigOptions_RulerOverrides_OCPApplicationTenant(t *testing.T) {
Mode: lokiv1.Static,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -595,6 +618,7 @@ func TestConfigOptions_RulerOverrides_OCPApplicationTenant(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -609,6 +633,7 @@ func TestConfigOptions_RulerOverrides_OCPApplicationTenant(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -662,6 +687,7 @@ func TestConfigOptions_RulerOverrides_OCPApplicationTenant(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -711,6 +737,7 @@ func TestConfigOptions_RulerOverrides(t *testing.T) {
Mode: lokiv1.Static,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -722,6 +749,7 @@ func TestConfigOptions_RulerOverrides(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
},
@ -736,6 +764,7 @@ func TestConfigOptions_RulerOverrides(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -867,6 +896,7 @@ func TestConfigOptions_RulerOverrides(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -917,6 +947,7 @@ func TestConfigOptions_RulerOverrides_OCPUserWorkloadOnlyEnabled(t *testing.T) {
Mode: lokiv1.Static,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
wantOverridesOptions: nil,
@ -929,6 +960,7 @@ func TestConfigOptions_RulerOverrides_OCPUserWorkloadOnlyEnabled(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: testTimeoutConfig(),
},
wantOptions: nil,
wantOverridesOptions: nil,
@ -944,6 +976,7 @@ func TestConfigOptions_RulerOverrides_OCPUserWorkloadOnlyEnabled(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -1003,6 +1036,8 @@ func TestConfigOptions_RulerOverrides_OCPUserWorkloadOnlyEnabled(t *testing.T) {
Enabled: true,
},
},
Timeouts: testTimeoutConfig(),
Ruler: Ruler{
Spec: &lokiv1.RulerConfigSpec{
AlertManagerSpec: &lokiv1.AlertManagerSpec{
@ -1131,10 +1166,27 @@ func TestConfigOptions_Replication(t *testing.T) {
t.Parallel()
inOpt := Options{
Stack: tc.spec,
Stack: tc.spec,
Timeouts: testTimeoutConfig(),
}
options := ConfigOptions(inOpt)
require.Equal(t, tc.wantOptions, *options.Stack.Replication)
})
}
}
func TestConfigOptions_ServerOptions(t *testing.T) {
opt := Options{
Stack: lokiv1.LokiStackSpec{},
Timeouts: testTimeoutConfig(),
}
got := ConfigOptions(opt)
want := config.HTTPTimeoutConfig{
IdleTimeout: time.Second,
ReadTimeout: time.Minute,
WriteTimeout: 10 * time.Minute,
}
require.Equal(t, want, got.HTTPTimeouts)
}

@ -142,8 +142,11 @@ func NewGatewayDeployment(opts Options, sha1C string) *appsv1.Deployment {
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("--logs.write-timeout=%s", opts.Timeouts.Gateway.UpstreamWriteTimeout),
fmt.Sprintf("--rbac.config=%s", path.Join(gateway.LokiGatewayMountDir, gateway.LokiGatewayRbacFileName)),
fmt.Sprintf("--tenants.config=%s", path.Join(gateway.LokiGatewayMountDir, gateway.LokiGatewayTenantFileName)),
fmt.Sprintf("--server.read-timeout=%s", opts.Timeouts.Gateway.ReadTimeout),
fmt.Sprintf("--server.write-timeout=%s", opts.Timeouts.Gateway.WriteTimeout),
},
Ports: []corev1.ContainerPort{
{

@ -3,12 +3,13 @@ package manifests
import (
"github.com/ViaQ/logerr/v2/kverrors"
"github.com/imdario/mergo"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
configv1 "github.com/grafana/loki/operator/apis/config/v1"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
"github.com/grafana/loki/operator/internal/manifests/internal/config"
"github.com/grafana/loki/operator/internal/manifests/openshift"
"github.com/imdario/mergo"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@ -35,6 +36,7 @@ func ApplyGatewayDefaultOptions(opts *Options) error {
GatewayName(opts.Name),
serviceNameGatewayHTTP(opts.Name),
gatewayHTTPPortName,
opts.Timeouts.Gateway.WriteTimeout,
ComponentLabels(LabelGatewayComponent, opts.Name),
RulerName(opts.Name),
)

@ -3,6 +3,7 @@ package manifests
import (
"path"
"testing"
"time"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/stretchr/testify/require"
@ -58,6 +59,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.Static,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
},
want: &Options{
Name: "lokistack-ocp",
@ -73,6 +79,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.Static,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
OpenShiftOptions: openshift.Options{
BuildOpts: openshift.BuildOptions{
LokiStackName: "lokistack-ocp",
@ -80,6 +91,7 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
GatewayName: "lokistack-ocp-gateway",
GatewaySvcName: "lokistack-ocp-gateway-http",
GatewaySvcTargetPort: "public",
GatewayRouteTimeout: 75 * time.Second,
RulerName: "lokistack-ocp-ruler",
Labels: ComponentLabels(LabelGatewayComponent, "lokistack-ocp"),
},
@ -119,6 +131,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
},
want: &Options{
Name: "lokistack-ocp",
@ -134,6 +151,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
OpenShiftOptions: openshift.Options{
BuildOpts: openshift.BuildOptions{
LokiStackName: "lokistack-ocp",
@ -141,6 +163,7 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
GatewayName: "lokistack-ocp-gateway",
GatewaySvcName: "lokistack-ocp-gateway-http",
GatewaySvcTargetPort: "public",
GatewayRouteTimeout: 75 * time.Second,
RulerName: "lokistack-ocp-ruler",
Labels: ComponentLabels(LabelGatewayComponent, "lokistack-ocp"),
},
@ -163,6 +186,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
Tenants: Tenants{
Configs: map[string]TenantConfig{
"application": {
@ -197,6 +225,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
Tenants: Tenants{
Configs: map[string]TenantConfig{
"application": {
@ -223,6 +256,7 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
GatewayName: "lokistack-ocp-gateway",
GatewaySvcName: "lokistack-ocp-gateway-http",
GatewaySvcTargetPort: "public",
GatewayRouteTimeout: 75 * time.Second,
RulerName: "lokistack-ocp-ruler",
Labels: ComponentLabels(LabelGatewayComponent, "lokistack-ocp"),
},
@ -268,6 +302,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.OpenshiftNetwork,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
Tenants: Tenants{
Configs: map[string]TenantConfig{
"network": {
@ -292,6 +331,11 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
Mode: lokiv1.OpenshiftNetwork,
},
},
Timeouts: TimeoutConfig{
Gateway: GatewayTimeoutConfig{
WriteTimeout: 1 * time.Minute,
},
},
Tenants: Tenants{
Configs: map[string]TenantConfig{
"network": {
@ -308,6 +352,7 @@ func TestApplyGatewayDefaultsOptions(t *testing.T) {
GatewayName: "lokistack-ocp-gateway",
GatewaySvcName: "lokistack-ocp-gateway-http",
GatewaySvcTargetPort: "public",
GatewayRouteTimeout: 75 * time.Second,
RulerName: "lokistack-ocp-ruler",
Labels: ComponentLabels(LabelGatewayComponent, "lokistack-ocp"),
},

@ -48,6 +48,7 @@ func TestNewGatewayDeployment_HasTemplateConfigHashAnnotation(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
}, sha1C)
expected := "loki.grafana.com/config-hash"
@ -95,6 +96,7 @@ func TestNewGatewayDeployment_HasNodeSelector(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
}, "deadbeef")
require.Equal(t, dpl.Spec.Template.Spec.NodeSelector, selector)
@ -129,6 +131,7 @@ func TestNewGatewayDeployment_HasTemplateCertRotationRequiredAtAnnotation(t *tes
},
},
},
Timeouts: defaultTimeoutConfig,
}, sha1C)
expected := "loki.grafana.com/certRotationRequiredAt"
@ -187,6 +190,7 @@ func TestGatewayConfigMap_ReturnsSHA1OfBinaryContents(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
Tenants: Tenants{
Secrets: []*TenantSecrets{
{
@ -221,6 +225,7 @@ func TestBuildGateway_HasConfigForTenantMode(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
})
require.NoError(t, err)
@ -257,6 +262,7 @@ func TestBuildGateway_HasExtraObjectsForTenantMode(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
})
require.NoError(t, err)
@ -292,6 +298,7 @@ func TestBuildGateway_WithExtraObjectsForTenantMode_RouteSvcMatches(t *testing.T
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
})
require.NoError(t, err)
@ -329,6 +336,7 @@ func TestBuildGateway_WithExtraObjectsForTenantMode_ServiceAccountNameMatches(t
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
})
require.NoError(t, err)
@ -367,6 +375,7 @@ func TestBuildGateway_WithExtraObjectsForTenantMode_ReplacesIngressWithRoute(t *
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
})
require.NoError(t, err)
@ -432,6 +441,7 @@ func TestBuildGateway_WithTLSProfile(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
},
expectedArgs: []string{
"--tls.min-version=min-version",
@ -462,6 +472,7 @@ func TestBuildGateway_WithTLSProfile(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: defaultTimeoutConfig,
},
expectedArgs: []string{
"--tls.min-version=min-version",
@ -492,6 +503,7 @@ func TestBuildGateway_WithTLSProfile(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
},
expectedArgs: []string{
"--tls.min-version=min-version",
@ -563,6 +575,7 @@ func TestBuildGateway_WithRulesEnabled(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
},
missingArgs: []string{
"--logs.rules.endpoint=http://abcd-ruler-http.efgh.svc.cluster.local:3100",
@ -612,6 +625,7 @@ func TestBuildGateway_WithRulesEnabled(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
},
wantArgs: []string{
"--logs.rules.endpoint=http://abcd-ruler-http.efgh.svc.cluster.local:3100",
@ -639,6 +653,7 @@ func TestBuildGateway_WithRulesEnabled(t *testing.T) {
Mode: lokiv1.Dynamic,
},
},
Timeouts: defaultTimeoutConfig,
},
wantArgs: []string{
"--logs.rules.endpoint=http://abcd-ruler-http.efgh.svc.cluster.local:3100",
@ -670,6 +685,7 @@ func TestBuildGateway_WithRulesEnabled(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
},
wantArgs: []string{
"--logs.rules.endpoint=https://abcd-ruler-http.efgh.svc.cluster.local:3100",
@ -701,6 +717,7 @@ func TestBuildGateway_WithRulesEnabled(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
},
wantArgs: []string{
"--logs.rules.endpoint=https://abcd-ruler-http.efgh.svc.cluster.local:3100",
@ -754,6 +771,7 @@ func TestBuildGateway_WithHTTPEncryption(t *testing.T) {
Authentication: []lokiv1.AuthenticationSpec{},
},
},
Timeouts: defaultTimeoutConfig,
})
require.NoError(t, err)
@ -773,8 +791,11 @@ func TestBuildGateway_WithHTTPEncryption(t *testing.T) {
"--logs.read.endpoint=https://abcd-query-frontend-http.efgh.svc.cluster.local:3100",
"--logs.tail.endpoint=https://abcd-query-frontend-http.efgh.svc.cluster.local:3100",
"--logs.write.endpoint=https://abcd-distributor-http.efgh.svc.cluster.local:3100",
"--logs.write-timeout=4m0s",
"--rbac.config=/etc/lokistack-gateway/rbac.yaml",
"--tenants.config=/etc/lokistack-gateway/tenants.yaml",
"--server.read-timeout=48s",
"--server.write-timeout=6m0s",
"--logs.rules.endpoint=https://abcd-ruler-http.efgh.svc.cluster.local:3100",
"--logs.rules.read-only=true",
"--tls.client-auth-type=NoClientCert",
@ -926,6 +947,7 @@ func TestBuildGateway_PodDisruptionBudget(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
}
objs, err := BuildGateway(opts)
require.NoError(t, err)
@ -957,6 +979,7 @@ func TestBuildGateway_TopologySpreadConstraint(t *testing.T) {
Mode: lokiv1.OpenshiftLogging,
},
},
Timeouts: defaultTimeoutConfig,
}, "deadbeef")
require.EqualValues(t, dpl.Spec.Template.Spec.TopologySpreadConstraints, []corev1.TopologySpreadConstraint{

@ -2,6 +2,7 @@ package config
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"k8s.io/utils/pointer"
@ -152,8 +153,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -247,6 +249,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -395,8 +402,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -520,6 +528,11 @@ overrides:
},
},
},
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -799,8 +812,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -941,6 +955,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -1143,8 +1162,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -1286,6 +1306,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -1501,8 +1526,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -1661,6 +1687,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -1817,8 +1848,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -1981,6 +2013,11 @@ overrides:
Enabled: true,
DeleteWorkerCount: 50,
},
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -2209,8 +2246,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -2386,6 +2424,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -2572,8 +2615,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
tls_min_version: VersionTLS12
tls_cipher_suites: cipher1,cipher2
http_tls_config:
@ -2718,6 +2762,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -2946,8 +2995,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -3200,6 +3250,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)
@ -3350,8 +3405,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: 30s
http_server_read_timeout: 30s
http_server_write_timeout: 10m0s
log_level: info
storage_config:
boltdb_shipper:
@ -3446,6 +3502,11 @@ overrides:
},
},
EnableRemoteReporting: true,
HTTPTimeouts: HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Minute,
},
}
cfg, rCfg, err := Build(opts)
require.NoError(t, err)

@ -422,8 +422,9 @@ server:
grpc_server_max_recv_msg_size: 104857600
grpc_server_max_send_msg_size: 104857600
http_listen_port: 3100
http_server_idle_timeout: 120s
http_server_write_timeout: 1m
http_server_idle_timeout: {{ .HTTPTimeouts.IdleTimeout }}
http_server_read_timeout: {{ .HTTPTimeouts.ReadTimeout }}
http_server_write_timeout: {{ .HTTPTimeouts.WriteTimeout }}
{{- if or .Gates.HTTPEncryption .Gates.GRPCEncryption }}
tls_min_version: {{ .TLS.MinTLSVersion }}
tls_cipher_suites: {{ .TLS.CipherSuitesString }}

@ -4,6 +4,7 @@ import (
"fmt"
"math"
"strings"
"time"
configv1 "github.com/grafana/loki/operator/apis/config/v1"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
@ -31,6 +32,8 @@ type Options struct {
ObjectStorage storage.Options
HTTPTimeouts HTTPTimeoutConfig
Retention RetentionOptions
Overrides map[string]LokiOverrides
@ -67,6 +70,13 @@ type GossipRing struct {
MembersDiscoveryAddr string
}
// HTTPTimeoutConfig defines the HTTP server config options.
type HTTPTimeoutConfig struct {
IdleTimeout time.Duration
ReadTimeout time.Duration
WriteTimeout time.Duration
}
// Ruler configuration
type Ruler struct {
Enabled bool

@ -1,17 +1,20 @@
package openshift
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
)
func TestBuildGatewayTenantModeObjects_ClusterRoleRefMatches(t *testing.T) {
opts := NewOptions("abc", "ns", "abc", "abc", "abc", map[string]string{}, "abc").
opts := NewOptions("abc", "ns", "abc", "abc", "abc", 1*time.Minute, map[string]string{}, "abc").
WithTenantsForMode(lokiv1.OpenshiftLogging, "example.com", map[string]TenantData{})
objs := BuildGatewayTenantModeObjects(*opts)
@ -23,7 +26,7 @@ func TestBuildGatewayTenantModeObjects_ClusterRoleRefMatches(t *testing.T) {
}
func TestBuildGatewayObjects_MonitoringClusterRoleRefMatches(t *testing.T) {
opts := NewOptions("abc", "ns", "abc", "abc", "abc", map[string]string{}, "abc")
opts := NewOptions("abc", "ns", "abc", "abc", "abc", 1*time.Minute, map[string]string{}, "abc")
objs := BuildGatewayObjects(*opts)
cr := objs[2].(*rbacv1.Role)
@ -33,8 +36,23 @@ func TestBuildGatewayObjects_MonitoringClusterRoleRefMatches(t *testing.T) {
require.Equal(t, cr.Name, rb.RoleRef.Name)
}
func TestBuildGatewayObjets_RouteWithTimeoutAnnotation(t *testing.T) {
gwWriteTimeout := 1 * time.Minute
opts := NewOptions("abc", "ns", "abc", "abc", "abc", gwWriteTimeout, map[string]string{}, "abc")
objs := BuildGatewayObjects(*opts)
a := objs[0].GetAnnotations()
got, ok := a[annotationGatewayRouteTimeout]
require.True(t, ok)
routeTimeout := gwWriteTimeout + gatewayRouteTimeoutExtension
want := fmt.Sprintf("%.fs", routeTimeout.Seconds())
require.Equal(t, want, got)
}
func TestBuildRulerObjects_ClusterRoleRefMatches(t *testing.T) {
opts := NewOptions("abc", "ns", "abc", "abc", "abc", map[string]string{}, "abc")
opts := NewOptions("abc", "ns", "abc", "abc", "abc", 1*time.Minute, map[string]string{}, "abc")
objs := BuildRulerObjects(*opts)
sa := objs[1].(*corev1.ServiceAccount)

@ -3,6 +3,7 @@ package openshift
import (
"fmt"
"math/rand"
"time"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
)
@ -41,6 +42,7 @@ type BuildOptions struct {
GatewayName string
GatewaySvcName string
GatewaySvcTargetPort string
GatewayRouteTimeout time.Duration
RulerName string
Labels map[string]string
AlertManagerEnabled bool
@ -56,6 +58,7 @@ type TenantData struct {
func NewOptions(
stackName, stackNamespace string,
gwName, gwSvcName, gwPortName string,
gwWriteTimeout time.Duration,
gwLabels map[string]string,
rulerName string,
) *Options {
@ -66,6 +69,7 @@ func NewOptions(
GatewayName: gwName,
GatewaySvcName: gwSvcName,
GatewaySvcTargetPort: gwPortName,
GatewayRouteTimeout: gwWriteTimeout + gatewayRouteTimeoutExtension,
Labels: gwLabels,
RulerName: rulerName,
},

@ -1,6 +1,8 @@
package openshift
import (
"fmt"
routev1 "github.com/openshift/api/route/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
@ -19,6 +21,9 @@ func BuildRoute(opts Options) client.Object {
Name: routeName(opts),
Namespace: opts.BuildOpts.LokiStackNamespace,
Labels: opts.BuildOpts.Labels,
Annotations: map[string]string{
annotationGatewayRouteTimeout: fmt.Sprintf("%.fs", opts.BuildOpts.GatewayRouteTimeout.Seconds()),
},
},
Spec: routev1.RouteSpec{
To: routev1.RouteTargetReference{

@ -2,6 +2,13 @@ package openshift
import (
"fmt"
"time"
)
const (
annotationGatewayRouteTimeout = "haproxy.router.openshift.io/timeout"
gatewayRouteTimeoutExtension = 15 * time.Second
)
var (

@ -2,6 +2,9 @@ package manifests
import (
"strings"
"time"
"github.com/grafana/loki/operator/internal/manifests/internal/config"
configv1 "github.com/grafana/loki/operator/apis/config/v1"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
@ -34,11 +37,26 @@ type Options struct {
OpenShiftOptions openshift.Options
Timeouts TimeoutConfig
Tenants Tenants
TLSProfile TLSProfileSpec
}
// GatewayTimeoutConfig contains the http server configuration options for all Loki components.
type GatewayTimeoutConfig struct {
ReadTimeout time.Duration
WriteTimeout time.Duration
UpstreamWriteTimeout time.Duration
}
// TimeoutConfig contains the server configuration options for all Loki components
type TimeoutConfig struct {
Loki config.HTTPTimeoutConfig
Gateway GatewayTimeoutConfig
}
// Tenants contains the configuration per tenant and secrets for authn/authz.
// Secrets are required only for modes static and dynamic to reconcile the OIDC provider.
// Configs are required only for all modes to reconcile rules and gateway configuration.
@ -105,3 +123,67 @@ type TLSProfileSpec struct {
func (o Options) TLSCipherSuites() string {
return strings.Join(o.TLSProfile.Ciphers, ",")
}
// NewTimeoutConfig creates a TimeoutConfig from the QueryTimeout values in the spec's limits.
func NewTimeoutConfig(s *lokiv1.LimitsSpec) (TimeoutConfig, error) {
if s == nil {
return defaultTimeoutConfig, nil
}
if s.Global == nil && s.Tenants == nil {
return defaultTimeoutConfig, nil
}
queryTimeout := lokiDefaultQueryTimeout
if s.Global.QueryLimits != nil && s.Global.QueryLimits.QueryTimeout != "" {
var err error
globalQueryTimeout, err := time.ParseDuration(s.Global.QueryLimits.QueryTimeout)
if err != nil {
return TimeoutConfig{}, err
}
if globalQueryTimeout > queryTimeout {
queryTimeout = globalQueryTimeout
}
}
for _, tLimit := range s.Tenants {
if tLimit.QueryLimits == nil || tLimit.QueryLimits.QueryTimeout == "" {
continue
}
tenantQueryTimeout, err := time.ParseDuration(tLimit.QueryLimits.QueryTimeout)
if err != nil {
return TimeoutConfig{}, err
}
if tenantQueryTimeout > queryTimeout {
queryTimeout = tenantQueryTimeout
}
}
return calculateHTTPTimeouts(queryTimeout), nil
}
func calculateHTTPTimeouts(queryTimeout time.Duration) TimeoutConfig {
idleTimeout := lokiDefaultHTTPIdleTimeout
if queryTimeout < idleTimeout {
idleTimeout = queryTimeout
}
readTimeout := queryTimeout / 10
writeTimeout := queryTimeout + lokiQueryWriteDuration
return TimeoutConfig{
Loki: config.HTTPTimeoutConfig{
IdleTimeout: idleTimeout,
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
},
Gateway: GatewayTimeoutConfig{
ReadTimeout: readTimeout + gatewayReadDuration,
WriteTimeout: writeTimeout + gatewayWriteDuration,
UpstreamWriteTimeout: writeTimeout,
},
}
}

@ -0,0 +1,142 @@
package manifests
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/grafana/loki/operator/internal/manifests/internal/config"
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
)
func TestNewTimeoutConfig_ReturnsDefaults_WhenLimitsSpecEmpty(t *testing.T) {
s := lokiv1.LokiStack{}
got, err := NewTimeoutConfig(s.Spec.Limits)
require.NoError(t, err)
require.Equal(t, defaultTimeoutConfig, got)
}
func TestNewTimeoutConfig_ReturnsCustomConfig_WhenLimitsSpecNotEmpty(t *testing.T) {
s := lokiv1.LokiStack{
Spec: lokiv1.LokiStackSpec{
Limits: &lokiv1.LimitsSpec{
Global: &lokiv1.LimitsTemplateSpec{
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "10m",
},
},
},
},
}
got, err := NewTimeoutConfig(s.Spec.Limits)
require.NoError(t, err)
want := TimeoutConfig{
Loki: config.HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 1 * time.Minute,
WriteTimeout: 11 * time.Minute,
},
Gateway: GatewayTimeoutConfig{
ReadTimeout: 1*time.Minute + gatewayReadDuration,
WriteTimeout: 11*time.Minute + gatewayWriteDuration,
UpstreamWriteTimeout: 11 * time.Minute,
},
}
require.Equal(t, want, got)
}
func TestNewTimeoutConfig_ReturnsCustomConfig_WhenLimitsSpecNotEmpty_UseMaxTenantQueryTimeout(t *testing.T) {
s := lokiv1.LokiStack{
Spec: lokiv1.LokiStackSpec{
Limits: &lokiv1.LimitsSpec{
Global: &lokiv1.LimitsTemplateSpec{
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "10m",
},
},
Tenants: map[string]lokiv1.LimitsTemplateSpec{
"tenant-a": {
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "10m",
},
},
"tenant-b": {
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "20m",
},
},
},
},
},
}
got, err := NewTimeoutConfig(s.Spec.Limits)
require.NoError(t, err)
want := TimeoutConfig{
Loki: config.HTTPTimeoutConfig{
IdleTimeout: 30 * time.Second,
ReadTimeout: 2 * time.Minute,
WriteTimeout: 21 * time.Minute,
},
Gateway: GatewayTimeoutConfig{
ReadTimeout: 2*time.Minute + gatewayReadDuration,
WriteTimeout: 21*time.Minute + gatewayWriteDuration,
UpstreamWriteTimeout: 21 * time.Minute,
},
}
require.Equal(t, want, got)
}
func TestNewTimeoutConfig_ReturnsDefaults_WhenGlobalQueryTimeoutParseError(t *testing.T) {
s := lokiv1.LokiStack{
Spec: lokiv1.LokiStackSpec{
Limits: &lokiv1.LimitsSpec{
Global: &lokiv1.LimitsTemplateSpec{
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "invalid",
},
},
},
},
}
_, err := NewTimeoutConfig(s.Spec.Limits)
require.Error(t, err)
}
func TestNewTimeoutConfig_ReturnsDefaults_WhenTenantQueryTimeoutParseError(t *testing.T) {
s := lokiv1.LokiStack{
Spec: lokiv1.LokiStackSpec{
Limits: &lokiv1.LimitsSpec{
Global: &lokiv1.LimitsTemplateSpec{
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "10m",
},
},
Tenants: map[string]lokiv1.LimitsTemplateSpec{
"tenant-a": {
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "invalid",
},
},
"tenant-b": {
QueryLimits: &lokiv1.QueryLimitSpec{
QueryTimeout: "20m",
},
},
},
},
},
}
_, err := NewTimeoutConfig(s.Spec.Limits)
require.Error(t, err)
}

@ -54,6 +54,7 @@ func TestServicesMatchPorts(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
}
sha1C := "deadbef"
@ -183,6 +184,7 @@ func TestServicesMatchLabels(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
}
sha1C := "deadbef"
@ -305,6 +307,7 @@ func TestServices_WithEncryption(t *testing.T) {
},
},
},
Timeouts: defaultTimeoutConfig,
TLSProfile: TLSProfileSpec{
MinTLSVersion: "VersionTLS12",
Ciphers: []string{"cipher1", "cipher2"},

@ -3,6 +3,7 @@ package manifests
import (
"fmt"
"path"
"time"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
corev1 "k8s.io/api/core/v1"
@ -109,7 +110,20 @@ const (
kubernetesInstanceLabel = "app.kubernetes.io/instance"
)
const (
// lokiDefaultQueryTimeout contains the default query timeout. It should match the value mentioned in the CRD
// definition and also the default in the `sizes.go`.
lokiDefaultQueryTimeout = 3 * time.Minute
lokiDefaultHTTPIdleTimeout = 30 * time.Second
lokiQueryWriteDuration = 1 * time.Minute
gatewayReadDuration = 30 * time.Second
gatewayWriteDuration = 2 * time.Minute
)
var (
defaultTimeoutConfig = calculateHTTPTimeouts(lokiDefaultQueryTimeout)
defaultConfigMapMode = int32(420)
volumeFileSystemMode = corev1.PersistentVolumeFilesystem
podAntiAffinityComponents = map[string]struct{}{

Loading…
Cancel
Save