From 9c5ea9cb02acdc391ee4a29f65b3506062244ccd Mon Sep 17 00:00:00 2001
From: Periklis Tsirakidis
Date: Mon, 15 Jan 2024 10:37:57 +0100
Subject: [PATCH 1/8] operator: Replace deprecated ctrl-runtime cfg with custom
package
---
operator/apis/config/v1/doc.go | 4 +
.../apis/config/v1/projectconfig_types.go | 65 +++++++++++-
.../apis/config/v1/zz_generated.deepcopy.go | 99 +++++++++++++++++++
operator/go.mod | 2 +-
operator/internal/config/config.go | 98 ++++++++++++++++++
operator/internal/config/options.go | 82 +++++++++++++++
operator/main.go | 3 +-
7 files changed, 349 insertions(+), 4 deletions(-)
create mode 100644 operator/internal/config/config.go
create mode 100644 operator/internal/config/options.go
diff --git a/operator/apis/config/v1/doc.go b/operator/apis/config/v1/doc.go
index 032708a3bd20..3e963d0531c5 100644
--- a/operator/apis/config/v1/doc.go
+++ b/operator/apis/config/v1/doc.go
@@ -18,3 +18,7 @@ var (
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
+
+func init() {
+ SchemeBuilder.Register(&ControllerManagerConfiguration{})
+}
diff --git a/operator/apis/config/v1/projectconfig_types.go b/operator/apis/config/v1/projectconfig_types.go
index b6a80175266b..488f7b2cb64f 100644
--- a/operator/apis/config/v1/projectconfig_types.go
+++ b/operator/apis/config/v1/projectconfig_types.go
@@ -2,7 +2,7 @@ package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- cfg "sigs.k8s.io/controller-runtime/pkg/config/v1alpha1" //nolint:staticcheck
+ configv1alpha1 "k8s.io/component-base/config/v1alpha1"
)
// BuiltInCertManagement is the configuration for the built-in facility to generate and rotate
@@ -142,6 +142,67 @@ const (
TLSProfileModernType TLSProfileType = "Modern"
)
+// ControllerManagerConfigurationSpec defines the desired state of GenericControllerManagerConfiguration.
+type ControllerManagerConfigurationSpec struct {
+ // LeaderElection is the LeaderElection config to be used when configuring
+ // the manager.Manager leader election
+ // +optional
+ LeaderElection *configv1alpha1.LeaderElectionConfiguration `json:"leaderElection,omitempty"`
+
+ // Metrics contains the controller metrics configuration
+ // +optional
+ Metrics ControllerMetrics `json:"metrics,omitempty"`
+
+ // Health contains the controller health configuration
+ // +optional
+ Health ControllerHealth `json:"health,omitempty"`
+
+ // Webhook contains the controllers webhook configuration
+ // +optional
+ Webhook ControllerWebhook `json:"webhook,omitempty"`
+}
+
+// ControllerMetrics defines the metrics configs.
+type ControllerMetrics struct {
+ // BindAddress is the TCP address that the controller should bind to
+ // for serving prometheus metrics.
+ // It can be set to "0" to disable the metrics serving.
+ // +optional
+ BindAddress string `json:"bindAddress,omitempty"`
+}
+
+// ControllerHealth defines the health configs.
+type ControllerHealth struct {
+ // HealthProbeBindAddress is the TCP address that the controller should bind to
+ // for serving health probes
+ // It can be set to "0" or "" to disable serving the health probe.
+ // +optional
+ HealthProbeBindAddress string `json:"healthProbeBindAddress,omitempty"`
+}
+
+// ControllerWebhook defines the webhook server for the controller.
+type ControllerWebhook struct {
+ // Port is the port that the webhook server serves at.
+ // It is used to set webhook.Server.Port.
+ // +optional
+ Port *int `json:"port,omitempty"`
+}
+
+//+kubebuilder:object:root=true
+
+// ControllerManagerConfiguration is the Schema for the GenericControllerManagerConfigurations API.
+type ControllerManagerConfiguration struct {
+ metav1.TypeMeta `json:",inline"`
+
+ // ControllerManagerConfiguration returns the contfigurations for controllers
+ ControllerManagerConfigurationSpec `json:",inline"`
+}
+
+// Complete returns the configuration for controller-runtime.
+func (c *ControllerManagerConfigurationSpec) Complete() (ControllerManagerConfigurationSpec, error) {
+ return *c, nil
+}
+
//+kubebuilder:object:root=true
// ProjectConfig is the Schema for the projectconfigs API
@@ -149,7 +210,7 @@ type ProjectConfig struct {
metav1.TypeMeta `json:",inline"`
// ControllerManagerConfigurationSpec returns the contfigurations for controllers
- cfg.ControllerManagerConfigurationSpec `json:",inline"`
+ ControllerManagerConfigurationSpec `json:",inline"`
Gates FeatureGates `json:"featureGates,omitempty"`
}
diff --git a/operator/apis/config/v1/zz_generated.deepcopy.go b/operator/apis/config/v1/zz_generated.deepcopy.go
index ef20274e286e..f047818445aa 100644
--- a/operator/apis/config/v1/zz_generated.deepcopy.go
+++ b/operator/apis/config/v1/zz_generated.deepcopy.go
@@ -6,6 +6,7 @@ package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/component-base/config/v1alpha1"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@@ -23,6 +24,104 @@ func (in *BuiltInCertManagement) DeepCopy() *BuiltInCertManagement {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ControllerHealth) DeepCopyInto(out *ControllerHealth) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerHealth.
+func (in *ControllerHealth) DeepCopy() *ControllerHealth {
+ if in == nil {
+ return nil
+ }
+ out := new(ControllerHealth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ControllerManagerConfiguration) DeepCopyInto(out *ControllerManagerConfiguration) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ControllerManagerConfigurationSpec.DeepCopyInto(&out.ControllerManagerConfigurationSpec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerManagerConfiguration.
+func (in *ControllerManagerConfiguration) DeepCopy() *ControllerManagerConfiguration {
+ if in == nil {
+ return nil
+ }
+ out := new(ControllerManagerConfiguration)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ControllerManagerConfiguration) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ControllerManagerConfigurationSpec) DeepCopyInto(out *ControllerManagerConfigurationSpec) {
+ *out = *in
+ if in.LeaderElection != nil {
+ in, out := &in.LeaderElection, &out.LeaderElection
+ *out = new(v1alpha1.LeaderElectionConfiguration)
+ (*in).DeepCopyInto(*out)
+ }
+ out.Metrics = in.Metrics
+ out.Health = in.Health
+ in.Webhook.DeepCopyInto(&out.Webhook)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerManagerConfigurationSpec.
+func (in *ControllerManagerConfigurationSpec) DeepCopy() *ControllerManagerConfigurationSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ControllerManagerConfigurationSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ControllerMetrics) DeepCopyInto(out *ControllerMetrics) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerMetrics.
+func (in *ControllerMetrics) DeepCopy() *ControllerMetrics {
+ if in == nil {
+ return nil
+ }
+ out := new(ControllerMetrics)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ControllerWebhook) DeepCopyInto(out *ControllerWebhook) {
+ *out = *in
+ if in.Port != nil {
+ in, out := &in.Port, &out.Port
+ *out = new(int)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerWebhook.
+func (in *ControllerWebhook) DeepCopy() *ControllerWebhook {
+ if in == nil {
+ return nil
+ }
+ out := new(ControllerWebhook)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FeatureGates) DeepCopyInto(out *FeatureGates) {
*out = *in
diff --git a/operator/go.mod b/operator/go.mod
index 4ffc3899d11c..0ee7c037f16c 100644
--- a/operator/go.mod
+++ b/operator/go.mod
@@ -23,6 +23,7 @@ require (
k8s.io/apimachinery v0.27.7
k8s.io/apiserver v0.27.7
k8s.io/client-go v0.27.7
+ k8s.io/component-base v0.27.7
k8s.io/utils v0.0.0-20230505201702-9f6742963106
sigs.k8s.io/controller-runtime v0.15.3
sigs.k8s.io/yaml v1.3.0
@@ -150,7 +151,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.27.7 // indirect
- k8s.io/component-base v0.27.7 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
diff --git a/operator/internal/config/config.go b/operator/internal/config/config.go
new file mode 100644
index 000000000000..48d6402b696d
--- /dev/null
+++ b/operator/internal/config/config.go
@@ -0,0 +1,98 @@
+package v1
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "sync"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+
+ configv1 "github.com/grafana/loki/operator/apis/config/v1"
+)
+
+var (
+ errSchemeNotSupplied = errors.New("scheme not supplied to controller configuration loader")
+ errConfigFileLoading = errors.New("could not read file at path")
+ errRuntimeObjectDecoding = errors.New("could not decode file into runtime.Object")
+)
+
+// ControllerManagerConfiguration defines the functions necessary to parse a config file
+// and to configure the Options struct for the ctrl.Manager.
+type ControllerManagerConfiguration interface {
+ runtime.Object
+
+ // Complete returns the versioned configuration
+ Complete() (configv1.ControllerManagerConfigurationSpec, error)
+}
+
+// DeferredFileLoader is used to configure the decoder for loading controller
+// runtime component config types.
+type DeferredFileLoader struct {
+ ControllerManagerConfiguration
+ path string
+ scheme *runtime.Scheme
+ once sync.Once
+ err error
+}
+
+// File will set up the deferred file loader for the configuration
+// this will also configure the defaults for the loader if nothing is
+//
+// Defaults:
+// * Path: "./config.yaml"
+// * Kind: GenericControllerManagerConfiguration
+func File() *DeferredFileLoader {
+ scheme := runtime.NewScheme()
+ utilruntime.Must(configv1.AddToScheme(scheme))
+ return &DeferredFileLoader{
+ path: "./config.yaml",
+ ControllerManagerConfiguration: &configv1.ControllerManagerConfiguration{},
+ scheme: scheme,
+ }
+}
+
+// Complete will use sync.Once to set the scheme.
+func (d *DeferredFileLoader) Complete() (configv1.ControllerManagerConfigurationSpec, error) {
+ d.once.Do(d.loadFile)
+ if d.err != nil {
+ return configv1.ControllerManagerConfigurationSpec{}, d.err
+ }
+ return d.ControllerManagerConfiguration.Complete()
+}
+
+// AtPath will set the path to load the file for the decoder.
+func (d *DeferredFileLoader) AtPath(path string) *DeferredFileLoader {
+ d.path = path
+ return d
+}
+
+// OfKind will set the type to be used for decoding the file into.
+func (d *DeferredFileLoader) OfKind(obj ControllerManagerConfiguration) *DeferredFileLoader {
+ d.ControllerManagerConfiguration = obj
+ return d
+}
+
+// loadFile is used from the mutex.Once to load the file.
+func (d *DeferredFileLoader) loadFile() {
+ if d.scheme == nil {
+ d.err = errSchemeNotSupplied
+ return
+ }
+
+ content, err := os.ReadFile(d.path)
+ if err != nil {
+ d.err = fmt.Errorf("%w %s", errConfigFileLoading, d.path)
+ return
+ }
+
+ codecs := serializer.NewCodecFactory(d.scheme)
+
+ // Regardless of if the bytes are of any external version,
+ // it will be read successfully and converted into the internal version
+ if err = runtime.DecodeInto(codecs.UniversalDecoder(), content, d.ControllerManagerConfiguration); err != nil {
+ d.err = errRuntimeObjectDecoding
+ }
+}
diff --git a/operator/internal/config/options.go b/operator/internal/config/options.go
new file mode 100644
index 000000000000..0f75a76c2e25
--- /dev/null
+++ b/operator/internal/config/options.go
@@ -0,0 +1,82 @@
+package v1
+
+import (
+ "reflect"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/manager"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+
+ configv1 "github.com/grafana/loki/operator/apis/config/v1"
+)
+
+// OptionsAndFrom will use a supplied type and convert to Options
+// any options already set on Options will be ignored, this is used to allow
+// cli flags to override anything specified in the config file.
+func OptionsAndFrom(o manager.Options, loader ControllerManagerConfiguration) (manager.Options, error) {
+ newObj, err := loader.Complete()
+ if err != nil {
+ return o, err
+ }
+
+ o = setLeaderElectionConfig(o, newObj)
+
+ if o.MetricsBindAddress == "" && newObj.Metrics.BindAddress != "" {
+ o.MetricsBindAddress = newObj.Metrics.BindAddress
+ }
+
+ if o.HealthProbeBindAddress == "" && newObj.Health.HealthProbeBindAddress != "" {
+ o.HealthProbeBindAddress = newObj.Health.HealthProbeBindAddress
+ }
+
+ //nolint:staticcheck
+ if o.Port == 0 && newObj.Webhook.Port != nil {
+ o.Port = *newObj.Webhook.Port
+ }
+
+ //nolint:staticcheck
+ if o.WebhookServer == nil {
+ o.WebhookServer = webhook.NewServer(webhook.Options{
+ Port: o.Port,
+ })
+ }
+
+ return o, nil
+}
+
+func setLeaderElectionConfig(o manager.Options, obj configv1.ControllerManagerConfigurationSpec) manager.Options {
+ if obj.LeaderElection == nil {
+ // The source does not have any configuration; noop
+ return o
+ }
+
+ if !o.LeaderElection && obj.LeaderElection.LeaderElect != nil {
+ o.LeaderElection = *obj.LeaderElection.LeaderElect
+ }
+
+ if o.LeaderElectionResourceLock == "" && obj.LeaderElection.ResourceLock != "" {
+ o.LeaderElectionResourceLock = obj.LeaderElection.ResourceLock
+ }
+
+ if o.LeaderElectionNamespace == "" && obj.LeaderElection.ResourceNamespace != "" {
+ o.LeaderElectionNamespace = obj.LeaderElection.ResourceNamespace
+ }
+
+ if o.LeaderElectionID == "" && obj.LeaderElection.ResourceName != "" {
+ o.LeaderElectionID = obj.LeaderElection.ResourceName
+ }
+
+ if o.LeaseDuration == nil && !reflect.DeepEqual(obj.LeaderElection.LeaseDuration, metav1.Duration{}) {
+ o.LeaseDuration = &obj.LeaderElection.LeaseDuration.Duration
+ }
+
+ if o.RenewDeadline == nil && !reflect.DeepEqual(obj.LeaderElection.RenewDeadline, metav1.Duration{}) {
+ o.RenewDeadline = &obj.LeaderElection.RenewDeadline.Duration
+ }
+
+ if o.RetryPeriod == nil && !reflect.DeepEqual(obj.LeaderElection.RetryPeriod, metav1.Duration{}) {
+ o.RetryPeriod = &obj.LeaderElection.RetryPeriod.Duration
+ }
+
+ return o
+}
diff --git a/operator/main.go b/operator/main.go
index 6b101175407e..e064ec8dd8c0 100644
--- a/operator/main.go
+++ b/operator/main.go
@@ -21,6 +21,7 @@ import (
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
lokiv1beta1 "github.com/grafana/loki/operator/apis/loki/v1beta1"
lokictrl "github.com/grafana/loki/operator/controllers/loki"
+ operatorcfg "github.com/grafana/loki/operator/internal/config"
"github.com/grafana/loki/operator/internal/metrics"
"github.com/grafana/loki/operator/internal/operator"
"github.com/grafana/loki/operator/internal/validation"
@@ -62,7 +63,7 @@ func main() {
ctrlCfg := ctrlconfigv1.ProjectConfig{}
options := ctrl.Options{Scheme: scheme}
if configFile != "" {
- options, err = options.AndFrom(ctrl.ConfigFile().AtPath(configFile).OfKind(&ctrlCfg)) //nolint:staticcheck
+ options, err = operatorcfg.OptionsAndFrom(options, operatorcfg.File().AtPath(configFile).OfKind(&ctrlCfg))
if err != nil {
logger.Error(err, "failed to parse controller manager config file")
os.Exit(1)
From 6c667eb418d59636f628fe72ada7929c583cc373 Mon Sep 17 00:00:00 2001
From: Periklis Tsirakidis
Date: Mon, 15 Jan 2024 11:12:37 +0100
Subject: [PATCH 2/8] Fix docs generation
---
operator/config/docs/config.json | 22 +---
operator/docs/operator/feature-gates.md | 164 +++++++++++++-----------
2 files changed, 96 insertions(+), 90 deletions(-)
diff --git a/operator/config/docs/config.json b/operator/config/docs/config.json
index 3d912e857d2b..2d12922c9df9 100644
--- a/operator/config/docs/config.json
+++ b/operator/config/docs/config.json
@@ -4,7 +4,9 @@
],
"hideTypePatterns": [
"ParseError$",
- "List$"
+ "List$",
+ "ControllerManagerConfiguration$",
+ "ControllerManagerConfigurationSpec$"
],
"externalPackages": [
{
@@ -38,22 +40,6 @@
{
"typeMatchPrefix": "^k8s\\.io/component-base/config/v1alpha1\\.LeaderElectionConfiguration$",
"docsURLTemplate": "https://pkg.go.dev/k8s.io/component-base/config#LeaderElectionConfiguration"
- },
- {
- "typeMatchPrefix": "^sigs\\.k8s\\.io/controller-runtime/pkg/config/v1alpha1\\.ControllerConfigurationSpec$",
- "docsURLTemplate": "https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1#ControllerConfigurationSpec"
- },
- {
- "typeMatchPrefix": "^sigs\\.k8s\\.io/controller-runtime/pkg/config/v1alpha1\\.ControllerMetrics$",
- "docsURLTemplate": "https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1#ControllerMetrics"
- },
- {
- "typeMatchPrefix": "^sigs\\.k8s\\.io/controller-runtime/pkg/config/v1alpha1\\.ControllerHealth$",
- "docsURLTemplate": "https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1#ControllerHealth"
- },
- {
- "typeMatchPrefix": "^sigs\\.k8s\\.io/controller-runtime/pkg/config/v1alpha1\\.ControllerWebhook$",
- "docsURLTemplate": "https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1#ControllerWebhook"
}
],
"typeDisplayNamePrefixOverrides": {
@@ -66,4 +52,4 @@
"github.com/grafana/loki/operator/apis/loki/config/v1": "Feature Gates"
},
"markdownDisabled": false
-}
\ No newline at end of file
+}
diff --git a/operator/docs/operator/feature-gates.md b/operator/docs/operator/feature-gates.md
index 7475dfa8a1cc..a1831c936f9b 100644
--- a/operator/docs/operator/feature-gates.md
+++ b/operator/docs/operator/feature-gates.md
@@ -98,6 +98,92 @@ The refresh is applied to all LokiStack certificates at once.
+## ControllerHealth { #config-loki-grafana-com-v1-ControllerHealth }
+
+
ControllerHealth defines the health configs.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+healthProbeBindAddress
+
+string
+
+ |
+
+(Optional)
+ HealthProbeBindAddress is the TCP address that the controller should bind to
+for serving health probes
+It can be set to “0” or “” to disable serving the health probe.
+ |
+
+
+
+
+## ControllerMetrics { #config-loki-grafana-com-v1-ControllerMetrics }
+
+
ControllerMetrics defines the metrics configs.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+bindAddress
+
+string
+
+ |
+
+(Optional)
+ BindAddress is the TCP address that the controller should bind to
+for serving prometheus metrics.
+It can be set to “0” to disable the metrics serving.
+ |
+
+
+
+
+## ControllerWebhook { #config-loki-grafana-com-v1-ControllerWebhook }
+
+
ControllerWebhook defines the webhook server for the controller.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+port
+
+int
+
+ |
+
+(Optional)
+ Port is the port that the webhook server serves at.
+It is used to set webhook.Server.Port.
+ |
+
+
+
+
## FeatureGates { #config-loki-grafana-com-v1-FeatureGates }
(Appears on:ProjectConfig)
@@ -429,25 +515,6 @@ bool
-syncPeriod
-
-
-Kubernetes meta/v1.Duration
-
-
- |
-
-(Optional)
- SyncPeriod determines the minimum frequency at which watched resources are
-reconciled. A lower period will correct entropy more quickly, but reduce
-responsiveness to change if there are many watched resources. Change this
-value only if you know what you are doing. Defaults to 10 hours if unset.
-there will a 10 percent jitter between the SyncPeriod of all controllers
-so that all controllers will not send list requests simultaneously.
- |
-
-
-
leaderElection
@@ -463,57 +530,10 @@ the manager.Manager leader election
|
-cacheNamespace
-
-string
-
- |
-
-(Optional)
- CacheNamespace if specified restricts the manager’s cache to watch objects in
-the desired namespace Defaults to all namespaces
-Note: If a namespace is specified, controllers can still Watch for a
-cluster-scoped resource (e.g Node). For namespaced resources the cache
-will only hold objects from the desired namespace.
- |
-
-
-
-gracefulShutDown
-
-
-Kubernetes meta/v1.Duration
-
-
- |
-
- GracefulShutdownTimeout is the duration given to runnable to stop before the manager actually returns on stop.
-To disable graceful shutdown, set to time.Duration(0)
-To use graceful shutdown without timeout, set to a negative duration, e.G. time.Duration(-1)
-The graceful shutdown is skipped for safety reasons in case the leader election lease is lost.
- |
-
-
-
-controller
-
-
-K8S Controller-runtime v1alpha1.ControllerConfigurationSpec
-
-
- |
-
-(Optional)
- Controller contains global configuration options for controllers
-registered within this manager.
- |
-
-
-
metrics
-
-K8S Controller-runtime v1alpha1.ControllerMetrics
+
+ControllerMetrics
|
@@ -526,8 +546,8 @@ K8S Controller-runtime v1alpha1.ControllerMetrics
health
-
-K8S Controller-runtime v1alpha1.ControllerHealth
+
+ControllerHealth
|
@@ -540,8 +560,8 @@ K8S Controller-runtime v1alpha1.ControllerHealth
webhook
-
-K8S Controller-runtime v1alpha1.ControllerWebhook
+
+ControllerWebhook
|
From 40e805862f6405eb78c19699f570a92af3663d6c Mon Sep 17 00:00:00 2001
From: Robert Jacob
Date: Tue, 16 Jan 2024 19:03:54 +0100
Subject: [PATCH 3/8] Change package name
---
operator/internal/config/config.go | 2 +-
operator/internal/config/options.go | 2 +-
operator/main.go | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/operator/internal/config/config.go b/operator/internal/config/config.go
index 48d6402b696d..78ca4c29c89e 100644
--- a/operator/internal/config/config.go
+++ b/operator/internal/config/config.go
@@ -1,4 +1,4 @@
-package v1
+package config
import (
"errors"
diff --git a/operator/internal/config/options.go b/operator/internal/config/options.go
index 0f75a76c2e25..900a83c6bdb9 100644
--- a/operator/internal/config/options.go
+++ b/operator/internal/config/options.go
@@ -1,4 +1,4 @@
-package v1
+package config
import (
"reflect"
diff --git a/operator/main.go b/operator/main.go
index e064ec8dd8c0..e62a44967e15 100644
--- a/operator/main.go
+++ b/operator/main.go
@@ -21,7 +21,7 @@ import (
lokiv1 "github.com/grafana/loki/operator/apis/loki/v1"
lokiv1beta1 "github.com/grafana/loki/operator/apis/loki/v1beta1"
lokictrl "github.com/grafana/loki/operator/controllers/loki"
- operatorcfg "github.com/grafana/loki/operator/internal/config"
+ "github.com/grafana/loki/operator/internal/config"
"github.com/grafana/loki/operator/internal/metrics"
"github.com/grafana/loki/operator/internal/operator"
"github.com/grafana/loki/operator/internal/validation"
@@ -63,7 +63,7 @@ func main() {
ctrlCfg := ctrlconfigv1.ProjectConfig{}
options := ctrl.Options{Scheme: scheme}
if configFile != "" {
- options, err = operatorcfg.OptionsAndFrom(options, operatorcfg.File().AtPath(configFile).OfKind(&ctrlCfg))
+ options, err = config.OptionsAndFrom(options, config.File().AtPath(configFile).OfKind(&ctrlCfg))
if err != nil {
logger.Error(err, "failed to parse controller manager config file")
os.Exit(1)
From 3c297b2c3ffb027aa6e4b0b9452bdfe23c1ada4f Mon Sep 17 00:00:00 2001
From: Robert Jacob
Date: Tue, 16 Jan 2024 19:11:15 +0100
Subject: [PATCH 4/8] Move loader logic out of main
---
operator/internal/config/options.go | 28 ++++++++++++++++++++++++----
operator/main.go | 12 ++++--------
2 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/operator/internal/config/options.go b/operator/internal/config/options.go
index 900a83c6bdb9..1ce8892fb78d 100644
--- a/operator/internal/config/options.go
+++ b/operator/internal/config/options.go
@@ -1,19 +1,39 @@
package config
import (
+ "fmt"
"reflect"
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/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook"
configv1 "github.com/grafana/loki/operator/apis/config/v1"
)
-// OptionsAndFrom will use a supplied type and convert to Options
-// any options already set on Options will be ignored, this is used to allow
-// cli flags to override anything specified in the config file.
-func OptionsAndFrom(o manager.Options, loader ControllerManagerConfiguration) (manager.Options, error) {
+// LoadConfig initializes the controller configuration, optionally overriding the defaults
+// from a provided configuration file.
+func LoadConfig(scheme *runtime.Scheme, configFile string) (*configv1.ProjectConfig, ctrl.Options, error) {
+ options := ctrl.Options{Scheme: scheme}
+ if configFile == "" {
+ return &configv1.ProjectConfig{}, options, nil
+ }
+
+ ctrlCfg := &configv1.ProjectConfig{}
+ if configFile != "" {
+ var err error
+ options, err = optionsAndFrom(options, File().AtPath(configFile).OfKind(ctrlCfg))
+ if err != nil {
+ return nil, options, fmt.Errorf("failed to parse controller manager config file: %w", err)
+ }
+ }
+
+ return ctrlCfg, options, nil
+}
+
+func optionsAndFrom(o manager.Options, loader ControllerManagerConfiguration) (manager.Options, error) {
newObj, err := loader.Complete()
if err != nil {
return o, err
diff --git a/operator/main.go b/operator/main.go
index e62a44967e15..ffa16608707c 100644
--- a/operator/main.go
+++ b/operator/main.go
@@ -60,14 +60,10 @@ func main() {
var err error
- ctrlCfg := ctrlconfigv1.ProjectConfig{}
- options := ctrl.Options{Scheme: scheme}
- if configFile != "" {
- options, err = config.OptionsAndFrom(options, config.File().AtPath(configFile).OfKind(&ctrlCfg))
- if err != nil {
- logger.Error(err, "failed to parse controller manager config file")
- os.Exit(1)
- }
+ ctrlCfg, options, err := config.LoadConfig(scheme, configFile)
+ if err != nil {
+ logger.Error(err, "failed to load operator configuration")
+ os.Exit(1)
}
if ctrlCfg.Gates.LokiStackAlerts && !ctrlCfg.Gates.ServiceMonitors {
From 2285ceec6784ebe4250bac2d49ce7e1cf509f694 Mon Sep 17 00:00:00 2001
From: Robert Jacob
Date: Tue, 16 Jan 2024 19:26:29 +0100
Subject: [PATCH 5/8] Simplify reading config file
---
operator/internal/config/loader.go | 25 +++++++++++++++++++++
operator/internal/config/options.go | 34 +++++++++++------------------
2 files changed, 38 insertions(+), 21 deletions(-)
create mode 100644 operator/internal/config/loader.go
diff --git a/operator/internal/config/loader.go b/operator/internal/config/loader.go
new file mode 100644
index 000000000000..dc9c8e989d89
--- /dev/null
+++ b/operator/internal/config/loader.go
@@ -0,0 +1,25 @@
+package config
+
+import (
+ "fmt"
+ configv1 "github.com/grafana/loki/operator/apis/config/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "os"
+)
+
+func loadConfigFile(scheme *runtime.Scheme, configFile string) (*configv1.ProjectConfig, error) {
+ content, err := os.ReadFile(configFile)
+ if err != nil {
+ return nil, fmt.Errorf("%w %s", errConfigFileLoading, configFile)
+ }
+
+ codecs := serializer.NewCodecFactory(scheme)
+
+ outConfig := &configv1.ProjectConfig{}
+ if err = runtime.DecodeInto(codecs.UniversalDecoder(), content, outConfig); err != nil {
+ return nil, fmt.Errorf("could not decode file into runtime.Object: %w", err)
+ }
+
+ return outConfig, nil
+}
diff --git a/operator/internal/config/options.go b/operator/internal/config/options.go
index 1ce8892fb78d..8aeba0eb53b3 100644
--- a/operator/internal/config/options.go
+++ b/operator/internal/config/options.go
@@ -21,37 +21,29 @@ func LoadConfig(scheme *runtime.Scheme, configFile string) (*configv1.ProjectCon
return &configv1.ProjectConfig{}, options, nil
}
- ctrlCfg := &configv1.ProjectConfig{}
- if configFile != "" {
- var err error
- options, err = optionsAndFrom(options, File().AtPath(configFile).OfKind(ctrlCfg))
- if err != nil {
- return nil, options, fmt.Errorf("failed to parse controller manager config file: %w", err)
- }
+ ctrlCfg, err := loadConfigFile(scheme, configFile)
+ if err != nil {
+ return nil, options, fmt.Errorf("failed to parse controller manager config file: %w", err)
}
+ options = mergeOptionsFromFile(options, ctrlCfg)
return ctrlCfg, options, nil
}
-func optionsAndFrom(o manager.Options, loader ControllerManagerConfiguration) (manager.Options, error) {
- newObj, err := loader.Complete()
- if err != nil {
- return o, err
- }
+func mergeOptionsFromFile(o manager.Options, cfg *configv1.ProjectConfig) manager.Options {
+ o = setLeaderElectionConfig(o, cfg.ControllerManagerConfigurationSpec)
- o = setLeaderElectionConfig(o, newObj)
-
- if o.MetricsBindAddress == "" && newObj.Metrics.BindAddress != "" {
- o.MetricsBindAddress = newObj.Metrics.BindAddress
+ if o.MetricsBindAddress == "" && cfg.Metrics.BindAddress != "" {
+ o.MetricsBindAddress = cfg.Metrics.BindAddress
}
- if o.HealthProbeBindAddress == "" && newObj.Health.HealthProbeBindAddress != "" {
- o.HealthProbeBindAddress = newObj.Health.HealthProbeBindAddress
+ if o.HealthProbeBindAddress == "" && cfg.Health.HealthProbeBindAddress != "" {
+ o.HealthProbeBindAddress = cfg.Health.HealthProbeBindAddress
}
//nolint:staticcheck
- if o.Port == 0 && newObj.Webhook.Port != nil {
- o.Port = *newObj.Webhook.Port
+ if o.Port == 0 && cfg.Webhook.Port != nil {
+ o.Port = *cfg.Webhook.Port
}
//nolint:staticcheck
@@ -61,7 +53,7 @@ func optionsAndFrom(o manager.Options, loader ControllerManagerConfiguration) (m
})
}
- return o, nil
+ return o
}
func setLeaderElectionConfig(o manager.Options, obj configv1.ControllerManagerConfigurationSpec) manager.Options {
From b24cbdaec1a13db554702450ee05fc8f2a2aeb84 Mon Sep 17 00:00:00 2001
From: Robert Jacob
Date: Tue, 16 Jan 2024 19:49:17 +0100
Subject: [PATCH 6/8] Remove previous loader implementation
---
operator/internal/config/config.go | 98 ------------------------------
operator/internal/config/loader.go | 9 ++-
2 files changed, 7 insertions(+), 100 deletions(-)
delete mode 100644 operator/internal/config/config.go
diff --git a/operator/internal/config/config.go b/operator/internal/config/config.go
deleted file mode 100644
index 78ca4c29c89e..000000000000
--- a/operator/internal/config/config.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package config
-
-import (
- "errors"
- "fmt"
- "os"
- "sync"
-
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/serializer"
- utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-
- configv1 "github.com/grafana/loki/operator/apis/config/v1"
-)
-
-var (
- errSchemeNotSupplied = errors.New("scheme not supplied to controller configuration loader")
- errConfigFileLoading = errors.New("could not read file at path")
- errRuntimeObjectDecoding = errors.New("could not decode file into runtime.Object")
-)
-
-// ControllerManagerConfiguration defines the functions necessary to parse a config file
-// and to configure the Options struct for the ctrl.Manager.
-type ControllerManagerConfiguration interface {
- runtime.Object
-
- // Complete returns the versioned configuration
- Complete() (configv1.ControllerManagerConfigurationSpec, error)
-}
-
-// DeferredFileLoader is used to configure the decoder for loading controller
-// runtime component config types.
-type DeferredFileLoader struct {
- ControllerManagerConfiguration
- path string
- scheme *runtime.Scheme
- once sync.Once
- err error
-}
-
-// File will set up the deferred file loader for the configuration
-// this will also configure the defaults for the loader if nothing is
-//
-// Defaults:
-// * Path: "./config.yaml"
-// * Kind: GenericControllerManagerConfiguration
-func File() *DeferredFileLoader {
- scheme := runtime.NewScheme()
- utilruntime.Must(configv1.AddToScheme(scheme))
- return &DeferredFileLoader{
- path: "./config.yaml",
- ControllerManagerConfiguration: &configv1.ControllerManagerConfiguration{},
- scheme: scheme,
- }
-}
-
-// Complete will use sync.Once to set the scheme.
-func (d *DeferredFileLoader) Complete() (configv1.ControllerManagerConfigurationSpec, error) {
- d.once.Do(d.loadFile)
- if d.err != nil {
- return configv1.ControllerManagerConfigurationSpec{}, d.err
- }
- return d.ControllerManagerConfiguration.Complete()
-}
-
-// AtPath will set the path to load the file for the decoder.
-func (d *DeferredFileLoader) AtPath(path string) *DeferredFileLoader {
- d.path = path
- return d
-}
-
-// OfKind will set the type to be used for decoding the file into.
-func (d *DeferredFileLoader) OfKind(obj ControllerManagerConfiguration) *DeferredFileLoader {
- d.ControllerManagerConfiguration = obj
- return d
-}
-
-// loadFile is used from the mutex.Once to load the file.
-func (d *DeferredFileLoader) loadFile() {
- if d.scheme == nil {
- d.err = errSchemeNotSupplied
- return
- }
-
- content, err := os.ReadFile(d.path)
- if err != nil {
- d.err = fmt.Errorf("%w %s", errConfigFileLoading, d.path)
- return
- }
-
- codecs := serializer.NewCodecFactory(d.scheme)
-
- // Regardless of if the bytes are of any external version,
- // it will be read successfully and converted into the internal version
- if err = runtime.DecodeInto(codecs.UniversalDecoder(), content, d.ControllerManagerConfiguration); err != nil {
- d.err = errRuntimeObjectDecoding
- }
-}
diff --git a/operator/internal/config/loader.go b/operator/internal/config/loader.go
index dc9c8e989d89..b5af090ddb88 100644
--- a/operator/internal/config/loader.go
+++ b/operator/internal/config/loader.go
@@ -1,13 +1,18 @@
package config
import (
+ "errors"
"fmt"
- configv1 "github.com/grafana/loki/operator/apis/config/v1"
+ "os"
+
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
- "os"
+
+ configv1 "github.com/grafana/loki/operator/apis/config/v1"
)
+var errConfigFileLoading = errors.New("could not read file at path")
+
func loadConfigFile(scheme *runtime.Scheme, configFile string) (*configv1.ProjectConfig, error) {
content, err := os.ReadFile(configFile)
if err != nil {
From 4787af08f4b13a48a3b878f1fcd3bf6ac9c32850 Mon Sep 17 00:00:00 2001
From: Periklis Tsirakidis
Date: Thu, 18 Jan 2024 08:51:45 +0100
Subject: [PATCH 7/8] Apply suggestions from code review
Co-authored-by: Robert Jacob
---
operator/apis/config/v1/doc.go | 4 ----
operator/config/docs/config.json | 6 +++++-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/operator/apis/config/v1/doc.go b/operator/apis/config/v1/doc.go
index 3e963d0531c5..032708a3bd20 100644
--- a/operator/apis/config/v1/doc.go
+++ b/operator/apis/config/v1/doc.go
@@ -18,7 +18,3 @@ var (
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
-
-func init() {
- SchemeBuilder.Register(&ControllerManagerConfiguration{})
-}
diff --git a/operator/config/docs/config.json b/operator/config/docs/config.json
index 2d12922c9df9..fb1b7d8a11b2 100644
--- a/operator/config/docs/config.json
+++ b/operator/config/docs/config.json
@@ -5,8 +5,12 @@
"hideTypePatterns": [
"ParseError$",
"List$",
+ "ControllerHealth$",
"ControllerManagerConfiguration$",
- "ControllerManagerConfigurationSpec$"
+ "ControllerManagerConfigurationSpec$",
+ "ControllerMetrics$",
+ "ControllerWebhook$",
+ "ProjectConfig$"
],
"externalPackages": [
{
From a92a7873ef92d884a0d93ee382831572eb7872a4 Mon Sep 17 00:00:00 2001
From: Periklis Tsirakidis
Date: Thu, 18 Jan 2024 08:56:19 +0100
Subject: [PATCH 8/8] Regenerate docs
---
operator/docs/operator/feature-gates.md | 173 ------------------------
1 file changed, 173 deletions(-)
diff --git a/operator/docs/operator/feature-gates.md b/operator/docs/operator/feature-gates.md
index a1831c936f9b..1d5c046be775 100644
--- a/operator/docs/operator/feature-gates.md
+++ b/operator/docs/operator/feature-gates.md
@@ -98,96 +98,7 @@ The refresh is applied to all LokiStack certificates at once.
-## ControllerHealth { #config-loki-grafana-com-v1-ControllerHealth }
-
-
ControllerHealth defines the health configs.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-healthProbeBindAddress
-
-string
-
- |
-
-(Optional)
- HealthProbeBindAddress is the TCP address that the controller should bind to
-for serving health probes
-It can be set to “0” or “” to disable serving the health probe.
- |
-
-
-
-
-## ControllerMetrics { #config-loki-grafana-com-v1-ControllerMetrics }
-
-
ControllerMetrics defines the metrics configs.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-bindAddress
-
-string
-
- |
-
-(Optional)
- BindAddress is the TCP address that the controller should bind to
-for serving prometheus metrics.
-It can be set to “0” to disable the metrics serving.
- |
-
-
-
-
-## ControllerWebhook { #config-loki-grafana-com-v1-ControllerWebhook }
-
-
ControllerWebhook defines the webhook server for the controller.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-port
-
-int
-
- |
-
-(Optional)
- Port is the port that the webhook server serves at.
-It is used to set webhook.Server.Port.
- |
-
-
-
-
## FeatureGates { #config-loki-grafana-com-v1-FeatureGates }
-
-(Appears on:ProjectConfig)
-
FeatureGates is the supported set of all operator feature gates.
@@ -501,90 +412,6 @@ bool
-## ProjectConfig { #config-loki-grafana-com-v1-ProjectConfig }
-
-
ProjectConfig is the Schema for the projectconfigs API
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-leaderElection
-
-
-Kubernetes v1alpha1.LeaderElectionConfiguration
-
-
- |
-
-(Optional)
- LeaderElection is the LeaderElection config to be used when configuring
-the manager.Manager leader election
- |
-
-
-
-metrics
-
-
-ControllerMetrics
-
-
- |
-
-(Optional)
- Metrics contains the controller metrics configuration
- |
-
-
-
-health
-
-
-ControllerHealth
-
-
- |
-
-(Optional)
- Health contains the controller health configuration
- |
-
-
-
-webhook
-
-
-ControllerWebhook
-
-
- |
-
-(Optional)
- Webhook contains the controllers webhook configuration
- |
-
-
-
-featureGates
-
-
-FeatureGates
-
-
- |
-
- |
-
-
-
-
## TLSProfileType { #config-loki-grafana-com-v1-TLSProfileType }
(string
alias)