diff --git a/api/v1alpha1/observatorium_types.go b/api/v1alpha1/observatorium_types.go index 3ca202f0..109d1dd0 100644 --- a/api/v1alpha1/observatorium_types.go +++ b/api/v1alpha1/observatorium_types.go @@ -320,6 +320,31 @@ type RuleConfig struct { Key string `json:"key"` } +type VolumeMountType string + +var ( + VolumeMountTypeConfigMap VolumeMountType = "configMap" + VolumeMountTypeSecret VolumeMountType = "secret" +) + +type VolumeMount struct { + // Voume mount type, configMap or secret + Type VolumeMountType `json:"type"` + // Volume mount path in the pod + MountPath string `json:"mountPath"` + // Resource name for the volume mount source + Name string `json:"name"` + // File name for the mount + Key string `json:"key"` +} + +type AlertmanagerConfigFile struct { + // Alertmanager ConfigMap Name + Name string `json:"name"` + // Alertmanager ConfigMap key + Key string `json:"key"` +} + type RuleSpec struct { // Number of Rule replicas. Replicas *int32 `json:"replicas,omitempty"` @@ -331,6 +356,12 @@ type RuleSpec struct { // AlertmanagerURLs // +optional AlertmanagerURLs []string `json:"alertmanagerURLs,omitempty"` + // ExtraVolumeMounts + // +optional + ExtraVolumeMounts []VolumeMount `json:"extraVolumeMounts,omitempty"` + // AlertmanagerConfigFile + // +optional + AlertmanagerConfigFile AlertmanagerConfigFile `json:"alertmanagerConfigFile,omitempty"` // ReloaderImage is an image of configmap reloader // +optional ReloaderImage string `json:"reloaderImage,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 8644b86b..79794554 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -109,6 +109,21 @@ func (in *APITenant) DeepCopy() *APITenant { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AlertmanagerConfigFile) DeepCopyInto(out *AlertmanagerConfigFile) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertmanagerConfigFile. +func (in *AlertmanagerConfigFile) DeepCopy() *AlertmanagerConfigFile { + if in == nil { + return nil + } + out := new(AlertmanagerConfigFile) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { *out = *in @@ -564,6 +579,12 @@ func (in *RuleSpec) DeepCopyInto(out *RuleSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ExtraVolumeMounts != nil { + in, out := &in.ExtraVolumeMounts, &out.ExtraVolumeMounts + *out = make([]VolumeMount, len(*in)) + copy(*out, *in) + } + out.AlertmanagerConfigFile = in.AlertmanagerConfigFile in.Resources.DeepCopyInto(&out.Resources) in.ReloaderResources.DeepCopyInto(&out.ReloaderResources) } @@ -713,3 +734,18 @@ func (in *VolumeClaimTemplate) DeepCopy() *VolumeClaimTemplate { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VolumeMount) DeepCopyInto(out *VolumeMount) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeMount. +func (in *VolumeMount) DeepCopy() *VolumeMount { + if in == nil { + return nil + } + out := new(VolumeMount) + in.DeepCopyInto(out) + return out +} diff --git a/example/main.jsonnet b/example/main.jsonnet index b936a3fc..53a673ad 100644 --- a/example/main.jsonnet +++ b/example/main.jsonnet @@ -163,6 +163,6 @@ local dex = (import 'github.com/observatorium/deployments/components/dex.libsonn volumeClaimTemplate: obs.loki.config.volumeClaimTemplate, }, securityContext: { - } + }, }, } diff --git a/jsonnet/jsonnetfile.json b/jsonnet/jsonnetfile.json index 481a9397..a0a6139d 100644 --- a/jsonnet/jsonnetfile.json +++ b/jsonnet/jsonnetfile.json @@ -13,22 +13,21 @@ { "source": { "git": { - "remote": "https://github.com/thanos-io/kube-thanos.git", + "remote": "https://github.com/open-cluster-management/kube-thanos.git", "subdir": "jsonnet/kube-thanos" } - }, - "version": "main", - "name": "upstream-kube-thanos" + }, + "version": "release-2.3" }, { "source": { "git": { - "remote": "https://github.com/open-cluster-management/kube-thanos.git", + "remote": "https://github.com/thanos-io/kube-thanos.git", "subdir": "jsonnet/kube-thanos" } }, - "version": "release-2.3", - "name": "kube-thanos" + "version": "main", + "name": "upstream-kube-thanos" } ], "legacyImports": true diff --git a/jsonnet/jsonnetfile.lock.json b/jsonnet/jsonnetfile.lock.json index c1da4f5f..6e35e374 100644 --- a/jsonnet/jsonnetfile.lock.json +++ b/jsonnet/jsonnetfile.lock.json @@ -51,8 +51,8 @@ "subdir": "jsonnet/kube-thanos" } }, - "version": "e9fb845c5229f7972e70da1be2ae240467c91523", - "sum": "SnDBgbjieU2jfiA9C+KuLgAg5gC/rbvC5xrMMpoh83o=" + "version": "ae9971c285449364854d0ad4aab8b8258e4b7638", + "sum": "Ih4YDcFrAuARANPUzFBtxpTZb8on1dOeE9SseXWaLLU=" }, { "source": { @@ -61,8 +61,8 @@ "subdir": "jsonnet/kube-thanos" } }, - "version": "c288ec08feb24be5da0174f39b96d7c224857926", - "sum": "CItqZ740EF1q6L2YU+66pmx8nKX4mxzXWlYMXSvLoFg=", + "version": "f53ad9856c6f765989ea76ba8eff8dd1e77186b7", + "sum": "1wMHM/+NvluUAxS5cBW2c6APEKQNQYLYnv1ZCE1R3/A=", "name": "upstream-kube-thanos" } ], diff --git a/jsonnet/obs-operator.jsonnet b/jsonnet/obs-operator.jsonnet index 78630ff0..177e928b 100644 --- a/jsonnet/obs-operator.jsonnet +++ b/jsonnet/obs-operator.jsonnet @@ -36,18 +36,18 @@ local operatorObs = obs { stores+:: { local deleteDelay = if std.objectHas(cr.spec.thanos, 'compact') && std.objectHas(cr.spec.thanos.compact, 'deleteDelay') then cr.spec.thanos.compact.deleteDelay else obs.thanos.compact.config.deleteDelay, securityContext: if std.objectHas(cr.spec, 'securityContext') then cr.spec.securityContext else obs.thanos.stores.config.securityContext, - ignoreDeletionMarksDelay: std.parseInt(std.substr(deleteDelay, 0, std.length(deleteDelay)-1))/2 + std.substr(deleteDelay, std.length(deleteDelay)-1, std.length(deleteDelay)), + ignoreDeletionMarksDelay: std.parseInt(std.substr(deleteDelay, 0, std.length(deleteDelay) - 1)) / 2 + std.substr(deleteDelay, std.length(deleteDelay) - 1, std.length(deleteDelay)), } + if std.objectHas(cr.spec.thanos, 'store') then cr.spec.thanos.store else {}, storeCache+:: (if std.objectHas(cr.spec.thanos, 'store') && std.objectHas(cr.spec.thanos.store, 'cache') then cr.spec.thanos.store.cache else {}) + { memoryLimitMb: if std.objectHas(cr.spec.thanos.store, 'cache') && std.objectHas(cr.spec.thanos.store.cache, 'memoryLimitMb') then cr.spec.thanos.store.cache.memoryLimitMb else obs.thanos.storeCache.config.memoryLimitMb, resources+: ( if std.objectHas(cr.spec.thanos.store.cache, 'resources') then { - memcached: cr.spec.thanos.store.cache.resources + memcached: cr.spec.thanos.store.cache.resources, } else {} ) + ( if std.objectHas(cr.spec.thanos.store.cache, 'exporterResources') then { - exporter: cr.spec.thanos.store.cache.exporterResources + exporter: cr.spec.thanos.store.cache.exporterResources, } else {} ), securityContext: if std.objectHas(cr.spec, 'securityContext') then cr.spec.securityContext else obs.thanos.storeCache.config.securityContext, @@ -65,26 +65,26 @@ local operatorObs = obs { memoryLimitMb: if std.objectHas(cr.spec.thanos.queryFrontend, 'cache') && std.objectHas(cr.spec.thanos.queryFrontend.cache, 'memoryLimitMb') then cr.spec.thanos.queryFrontend.cache.memoryLimitMb else obs.thanos.queryFrontendCache.config.memoryLimitMb, resources+: ( if std.objectHas(cr.spec.thanos.queryFrontend.cache, 'resources') then { - memcached: cr.spec.thanos.queryFrontend.cache.resources + memcached: cr.spec.thanos.queryFrontend.cache.resources, } else {} ) + ( if std.objectHas(cr.spec.thanos.queryFrontend.cache, 'exporterResources') then { - exporter: cr.spec.thanos.queryFrontend.cache.exporterResources + exporter: cr.spec.thanos.queryFrontend.cache.exporterResources, } else {} ), securityContext: if std.objectHas(cr.spec, 'securityContext') then cr.spec.securityContext else obs.thanos.queryFrontendCache.config.securityContext, - } + }, }), loki:: if std.objectHas(cr.spec, 'loki') then loki(obs.loki.config { - local cfg = self, - name: cr.metadata.name + '-' + cfg.commonLabels['app.kubernetes.io/name'], - namespace: cr.metadata.namespace, - image: if std.objectHas(cr.spec.loki, 'image') then cr.spec.loki.image else obs.loki.config.image, - replicas: if std.objectHas(cr.spec.loki, 'replicas') then cr.spec.loki.replicas else obs.loki.config.replicas, - version: if std.objectHas(cr.spec.loki, 'version') then cr.spec.loki.version else obs.loki.config.version, - objectStorageConfig: if cr.spec.objectStorageConfig.loki != null then cr.spec.objectStorageConfig.loki else obs.loki.config.objectStorageConfig, - }) else {}, + local cfg = self, + name: cr.metadata.name + '-' + cfg.commonLabels['app.kubernetes.io/name'], + namespace: cr.metadata.namespace, + image: if std.objectHas(cr.spec.loki, 'image') then cr.spec.loki.image else obs.loki.config.image, + replicas: if std.objectHas(cr.spec.loki, 'replicas') then cr.spec.loki.replicas else obs.loki.config.replicas, + version: if std.objectHas(cr.spec.loki, 'version') then cr.spec.loki.version else obs.loki.config.version, + objectStorageConfig: if cr.spec.objectStorageConfig.loki != null then cr.spec.objectStorageConfig.loki else obs.loki.config.objectStorageConfig, + }) else {}, gubernator:: {}, @@ -151,14 +151,14 @@ local operatorObs = obs { ) + ( if (v.kind == 'StatefulSet' || v.kind == 'Deployment') then { template+: { - spec+:{ + spec+: { affinity: { podAntiAffinity: { preferredDuringSchedulingIgnoredDuringExecution: [ { podAffinityTerm: { labelSelector: { - matchExpressions:[ + matchExpressions: [ { key: 'app.kubernetes.io/name', operator: 'In', @@ -175,14 +175,14 @@ local operatorObs = obs { }, ], }, - topologyKey: "kubernetes.io/hostname", + topologyKey: 'kubernetes.io/hostname', }, weight: 30, }, { podAffinityTerm: { labelSelector: { - matchExpressions:[ + matchExpressions: [ { key: 'app.kubernetes.io/name', operator: 'In', @@ -199,7 +199,7 @@ local operatorObs = obs { }, ], }, - topologyKey: "topology.kubernetes.io/zone", + topologyKey: 'topology.kubernetes.io/zone', }, weight: 70, }, @@ -220,7 +220,7 @@ local operatorObs = obs { ) + ( if (std.objectHas(cr.spec, 'tolerations') && (v.kind == 'StatefulSet' || v.kind == 'Deployment')) then { template+: { - spec+:{ + spec+: { tolerations: cr.spec.tolerations, }, }, @@ -228,7 +228,7 @@ local operatorObs = obs { ) + ( if (std.objectHas(cr.spec.thanos.rule, 'reloaderResources') && (v.kind == 'StatefulSet') && v.metadata.name == obs.config.name + '-thanos-rule') then { template+: { - spec+:{ + spec+: { containers: [ if c.name == 'configmap-reloader' then c { resources: cr.spec.thanos.rule.reloaderResources, diff --git a/jsonnet/vendor/github.com/open-cluster-management/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet b/jsonnet/vendor/github.com/open-cluster-management/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet index 1c2cd560..17238738 100644 --- a/jsonnet/vendor/github.com/open-cluster-management/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet +++ b/jsonnet/vendor/github.com/open-cluster-management/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet @@ -15,6 +15,8 @@ local defaults = { retention: '48h', blockDuration: '2h', alertmanagersURLs: [], + alertmanagerConfigFile: {}, + extraVolumeMounts: [], queriers: [], logLevel: 'info', logFormat: 'logfmt', @@ -53,6 +55,8 @@ function(params) { // Safety checks for combined config of defaults and params assert std.isNumber(tr.config.replicas) && tr.config.replicas >= 0 : 'thanos rule replicas has to be number >= 0', assert std.isArray(tr.config.ruleFiles), + assert std.isObject(tr.config.alertmanagerConfigFile), + assert std.isArray(tr.config.extraVolumeMounts), assert std.isArray(tr.config.rulesConfig), assert std.isArray(tr.config.alertmanagersURLs), assert std.isObject(tr.config.resources), @@ -118,6 +122,11 @@ function(params) { (['--rule-file=%s' % path for path in tr.config.ruleFiles]) + (['--alertmanagers.url=%s' % url for url in tr.config.alertmanagersURLs]) + ( + if tr.config.alertmanagerConfigFile != {} then [ + '--alertmanagers.config-file=/etc/thanos/config/' + tr.config.alertmanagerConfigFile.name + '/' + tr.config.alertmanagerConfigFile.key + ] + else [] + ) + ( if std.length(tr.config.rulesConfig) > 0 then [ '--rule-file=/etc/thanos/rules/' + ruleConfig.name + '/' + ruleConfig.key for ruleConfig in tr.config.rulesConfig @@ -150,6 +159,15 @@ function(params) { { name: ruleConfig.name, mountPath: '/etc/thanos/rules/' + ruleConfig.name } for ruleConfig in tr.config.rulesConfig ] else [] + ) + ( + if std.length(tr.config.extraVolumeMounts) > 0 then [ + { name: volumeMount.name, mountPath: volumeMount.mountPath } + for volumeMount in tr.config.extraVolumeMounts + ] else [] + ) + ( + if tr.config.alertmanagerConfigFile != {} then [ + { name: tr.config.alertmanagerConfigFile.name, mountPath: '/etc/thanos/config/' + tr.config.alertmanagerConfigFile.name } + ] else [] ), livenessProbe: { failureThreshold: 24, periodSeconds: 5, httpGet: { scheme: 'HTTP', @@ -173,11 +191,32 @@ function(params) { [ '-webhook-url=http://localhost:' + tr.service.spec.ports[1].port + '/-/reload', ] + - (['-volume-dir=/etc/thanos/rules/' + ruleConfig.name for ruleConfig in tr.config.rulesConfig]), + ( + if std.length(tr.config.rulesConfig) > 0 then [ + '-volume-dir=/etc/thanos/rules/' + ruleConfig.name for ruleConfig in tr.config.rulesConfig + ] else [] + ) + ( + if std.length(tr.config.extraVolumeMounts) > 0 then [ + '-volume-dir=' + volumeMount.mountPath for volumeMount in tr.config.extraVolumeMounts + ] else [] + ) + ( + if tr.config.alertmanagerConfigFile != {} then [ + '-volume-dir=/etc/thanos/config/' + tr.config.alertmanagerConfigFile.name + ] else [] + ), volumeMounts: [ { name: ruleConfig.name, mountPath: '/etc/thanos/rules/' + ruleConfig.name } for ruleConfig in tr.config.rulesConfig - ], + ] + ( + if std.length(tr.config.extraVolumeMounts) > 0 then [ + { name: volumeMount.name, mountPath: volumeMount.mountPath } + for volumeMount in tr.config.extraVolumeMounts + ] else [] + ) + ( + if tr.config.alertmanagerConfigFile != {} then [ + { name: tr.config.alertmanagerConfigFile.name, mountPath: '/etc/thanos/config/' + tr.config.alertmanagerConfigFile.name } + ] else [] + ), }; { @@ -200,11 +239,35 @@ function(params) { serviceAccountName: tr.serviceAccount.metadata.name, securityContext: tr.config.securityContext, containers: [c] + - (if std.length(tr.config.rulesConfig) > 0 then [reloadContainer] else []), - volumes: [ - { name: ruleConfig.name, configMap: { name: ruleConfig.name } } - for ruleConfig in tr.config.rulesConfig - ], + (if std.length(tr.config.rulesConfig) > 0 || std.length(tr.config.extraVolumeMounts) > 0 || tr.config.alertmanagerConfigFile != {} then [ + reloadContainer + ] else []), + volumes: [] + + ( + if std.length(tr.config.rulesConfig) > 0 then [ + { name: ruleConfig.name, configMap: { name: ruleConfig.name } } + for ruleConfig in tr.config.rulesConfig + ] else [] + ) + + ( + if std.length(tr.config.extraVolumeMounts) > 0 then [ + { name: volumeMount.name, } + + ( + if volumeMount.type == "configMap" then { + configMap : { name: volumeMount.name } + } + else { + secret : { name: volumeMount.name } + } + ) + for volumeMount in tr.config.extraVolumeMounts + ] else [] + ) + + ( + if tr.config.alertmanagerConfigFile != {} then [{ + name: tr.config.alertmanagerConfigFile.name, configMap: { name: tr.config.alertmanagerConfigFile.name } + }] else [] + ), }, }, volumeClaimTemplates: if std.length(tr.config.volumeClaimTemplate) > 0 then [tr.config.volumeClaimTemplate { diff --git a/jsonnet/vendor/github.com/thanos-io/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet b/jsonnet/vendor/github.com/thanos-io/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet index 0d817c95..922cf7a4 100644 --- a/jsonnet/vendor/github.com/thanos-io/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet +++ b/jsonnet/vendor/github.com/thanos-io/kube-thanos/jsonnet/kube-thanos/kube-thanos-rule.libsonnet @@ -17,6 +17,8 @@ local defaults = { logLevel: 'info', logFormat: 'logfmt', resources: {}, + retention: '48h', + blockDuration: '2h', serviceMonitor: false, ports: { grpc: 10901, @@ -109,6 +111,8 @@ function(params) { '--data-dir=/var/thanos/rule', '--label=rule_replica="$(NAME)"', '--alert.label-drop=rule_replica', + '--tsdb.retention=' + tr.config.retention, + '--tsdb.block-duration=' + tr.config.blockDuration, ] + (['--query=%s' % querier for querier in tr.config.queriers]) + (['--rule-file=%s' % path for path in tr.config.ruleFiles]) + diff --git a/manifests/crds/core.observatorium.io_observatoria.yaml b/manifests/crds/core.observatorium.io_observatoria.yaml index a3a5a8f5..20c00761 100644 --- a/manifests/crds/core.observatorium.io_observatoria.yaml +++ b/manifests/crds/core.observatorium.io_observatoria.yaml @@ -1284,6 +1284,19 @@ spec: rule: description: Thanos RulerSpec properties: + alertmanagerConfigFile: + description: AlertmanagerConfigFile + properties: + key: + description: Alertmanager ConfigMap key + type: string + name: + description: Alertmanager ConfigMap Name + type: string + required: + - key + - name + type: object alertmanagerURLs: description: AlertmanagerURLs items: @@ -1292,6 +1305,29 @@ spec: blockDuration: description: Block duration for TSDB block type: string + extraVolumeMounts: + description: ExtraVolumeMounts + items: + properties: + key: + description: File name for the mount + type: string + mountPath: + description: Volume mount path in the pod + type: string + name: + description: Resource name for the volume mount source + type: string + type: + description: Voume mount type, configMap or secret + type: string + required: + - key + - mountPath + - name + - type + type: object + type: array reloaderImage: description: ReloaderImage is an image of configmap reloader type: string