Skip to content

Commit

Permalink
feat: refactor elastic-agent chart to utilise ksm subchart
Browse files Browse the repository at this point in the history
  • Loading branch information
pkoutsovasilis committed Jan 8, 2025
1 parent 546ec26 commit 27748fa
Show file tree
Hide file tree
Showing 42 changed files with 529 additions and 1,268 deletions.
9 changes: 5 additions & 4 deletions deploy/helm/elastic-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

# elastic-agent

![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square)
![Version: 9.0.0-beta](https://img.shields.io/badge/Version-9.0.0--beta-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 9.0.0](https://img.shields.io/badge/AppVersion-9.0.0-informational?style=flat-square)

Elastic-Agent Helm Chart

Expand Down Expand Up @@ -67,8 +67,9 @@ The chart built-in [kubernetes integration](https://docs.elastic.co/integrations
| kubernetes.namespace | string | `"default"` | kubernetes namespace |
| kubernetes.hints.enabled | bool | `false` | enable [elastic-agent autodiscovery](https://www.elastic.co/guide/en/fleet/current/elastic-agent-kubernetes-autodiscovery.html) feature |
| kubernetes.state.enabled | bool | `true` | integration global switch to enable state streams based on kube-state-metrics. Note that setting this to `false` results in overriding and *disabling all* the respective state streams |
| kubernetes.state.deployKSM | bool | `true` | deploy kube-state-metrics service as a sidecar container to the elastic agent of `ksmSharded` preset. If set to `false`, kube-state-metrics will *not* get deployed and `clusterWide` agent preset will be used for collecting kube-state-metrics. |
| kubernetes.state.host | string | `"kube-state-metrics:8080"` | host of the kube-state-metrics service. Note that this used only when `deployKSM` is set to `false`. |
| kubernetes.state.agentAsSidecar.enabled | bool | `false` | enable [ksm autosharding](https://github.com/kubernetes/kube-state-metrics?tab=readme-ov-file#automated-sharding) and deploy elastic-agent as a sidecar container. If `kube-state-metrics.enabled` is set to `false` this has no effect. |
| kubernetes.state.agentAsSidecar.resources | object | `{"limits":{"memory":"800Mi"},"requests":{"cpu":"100m","memory":"400Mi"}}` | resources of the elastic-agent sidecar if `agentAsSidecar.enabled` is set to `true` |
| kubernetes.state.host | string | `"kube-state-metrics:8080"` | host of the kube-state-metrics service. This used only when `kube-state-metrics.enabled` is set to `false`. |
| kubernetes.state.vars | object | `{}` | state streams variables such as `add_metadata`, `hosts`, `period`, `bearer_token_file`. Please note that colliding vars also defined in respective state streams will *not* be overridden. |
| kubernetes.metrics.enabled | bool | `true` | integration global switch to enable metric streams based on kubelet. Note that setting this to false results in overriding and *disabling all* the respective metric streams |
| kubernetes.metrics.vars | object | `{}` | metric streams variables such as `add_metadata`, `hosts`, `period`, `bearer_token_file`, `ssl.verification_mode`. Please note that colliding vars also defined in respective metric streams will *not* be overridden. |
Expand Down Expand Up @@ -148,7 +149,7 @@ The chart built-in [kubernetes integration](https://docs.elastic.co/integrations
| agent.imagePullSecrets | list | `[]` | image pull secrets |
| agent.engine | string | `"k8s"` | generate kubernetes manifests or [ECK](https://github.com/elastic/cloud-on-k8s) CRDs |
| agent.unprivileged | bool | `false` | enable unprivileged mode |
| agent.presets | map[string]{} | `{ "perNode" : {...}, "clusterWide": {...}, "ksmSharded": {...} }` | Map of deployment presets for the Elastic Agent. The key of the map is the name of the preset. See more for the presets required by the built-in Kubernetes integration [here](./values.yaml) |
| agent.presets | map[string]{} | `{ "perNode" : {...}, "clusterWide": {...}}` | Map of deployment presets for the Elastic Agent. The key of the map is the name of the preset. See more for the presets required by the built-in Kubernetes integration [here](./values.yaml) |

### 6.1 - Elastic-Agent Managed Configuration
| Key | Type | Default | Description |
Expand Down
5 changes: 5 additions & 0 deletions deploy/helm/elastic-agent/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Installed agent:

{{ if eq (index $.Values "kube-state-metrics" "enabled") true -}}
Installed kube-state-metrics at "{{ $.Release.Namespace }}" namespace.
{{- if eq $.Values.kubernetes.enabled true -}}
{{- if eq $.Values.kubernetes.state.agentAsSidecar.enabled true }}
- elastic-agent runs as a sidecar container
{{- end }}
{{- end }}
{{- end }}

{{ if eq $.Values.agent.fleet.enabled false -}}
Expand Down
87 changes: 19 additions & 68 deletions deploy/helm/elastic-agent/templates/agent/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ Validate fleet configuration
{{- if eq $.Values.agent.fleet.enabled true -}}
{{/* check if the preset exists */}}
{{- $fleetPresetName := $.Values.agent.fleet.preset -}}
{{- if $fleetPresetName -}}
{{- $fleetPresetVal := get $.Values.agent.presets $fleetPresetName -}}
{{- $_ := required (printf "preset with name \"%s\" of fleet not defined" $fleetPresetName) $fleetPresetVal -}}
{{- end -}}
{{/* disable all presets except the fleet one */}}
{{- range $presetName, $presetVal := $.Values.agent.presets}}
{{- if ne $presetName $fleetPresetName -}}
Expand Down Expand Up @@ -84,6 +86,8 @@ Validate and initialise the defined agent presets
*/}}
{{- define "elasticagent.init.presets" -}}
{{- $ := . -}}
{{- include "elasticagent.presets.pernode.init" $ -}}
{{- include "elasticagent.presets.ksm.sidecar.init" $ -}}
{{- range $presetName, $presetVal := $.Values.agent.presets -}}
{{- include "elasticagent.preset.mutate.unprivileged" (list $ $presetVal) -}}
{{- include "elasticagent.preset.mutate.fleet" (list $ $presetVal) -}}
Expand Down Expand Up @@ -221,20 +225,6 @@ app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Values.agent.version}}
{{- end }}

{{- define "elasticagent.preset.applyOnce" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- if not (hasKey $preset "_appliedMutationTemplates") -}}
{{- $_ := set $preset "_appliedMutationTemplates" dict }}
{{- end -}}
{{- $appliedMutationTemplates := get $preset "_appliedMutationTemplates" -}}
{{- if not (hasKey $appliedMutationTemplates $templateName) -}}
{{- include $templateName $ -}}
{{- $_ := set $appliedMutationTemplates $templateName dict}}
{{- end -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.inputs" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
Expand All @@ -245,9 +235,8 @@ app.kubernetes.io/version: {{ .Values.agent.version}}
{{- end -}}

{{- define "elasticagent.preset.mutate.securityContext.capabilities.add" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- $preset := index . 0 -}}
{{- $capabilities := index . 1 -}}
{{- if not (hasKey $preset "securityContext") -}}
{{- $_ := set $preset "securityContext" dict }}
{{- end -}}
Expand All @@ -260,15 +249,14 @@ app.kubernetes.io/version: {{ .Values.agent.version}}
{{- $_ := set $presetSecurityContextCapabilities "add" list }}
{{- end -}}
{{- $presetSecurityContextCapabilitiesAdd := get $presetSecurityContextCapabilities "add" }}
{{- $capabilitiesAddToAdd := dig "securityContext" "capabilities" "add" (list) (include $templateName $ | fromYaml) -}}
{{- $capabilitiesAddToAdd := dig "securityContext" "capabilities" "add" (list) $capabilities -}}
{{- $presetSecurityContextCapabilitiesAdd = uniq (concat $presetSecurityContextCapabilitiesAdd $capabilitiesAddToAdd) -}}
{{- $_ := set $presetSecurityContextCapabilities "add" $presetSecurityContextCapabilitiesAdd -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.providers.kubernetes.hints" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- $preset := index . 0 -}}
{{- $providers := index . 1 -}}
{{- if not (hasKey $preset "providers") -}}
{{- $_ := set $preset "providers" dict }}
{{- end -}}
Expand All @@ -281,23 +269,11 @@ app.kubernetes.io/version: {{ .Values.agent.version}}
{{- $_ := set $presetProvidersKubernetes "hints" dict }}
{{- end -}}
{{- $presetProvidersKubernetesHints := get $presetProvidersKubernetes "hints" }}
{{- $presetProvidersKubernetesHintsToAdd := dig "providers" "kubernetes" "hints" (dict) (include $templateName $ | fromYaml) -}}
{{- $presetProvidersKubernetesHintsToAdd := dig "providers" "kubernetes" "hints" (dict) $providers -}}
{{- $presetProvidersKubernetesHints = merge $presetProvidersKubernetesHintsToAdd $presetProvidersKubernetesHints -}}
{{- $_ := set $presetProvidersKubernetes "hints" $presetProvidersKubernetesHints -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.rules" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- if eq ($preset).clusterRole.create true -}}
{{- $presetClusterRoleRules := dig "rules" (list) ($preset).clusterRole -}}
{{- $rulesToAdd := get (include $templateName $ | fromYaml) "rules" -}}
{{- $presetClusterRoleRules = uniq (concat $presetClusterRoleRules $rulesToAdd) -}}
{{- $_ := set ($preset).clusterRole "rules" $presetClusterRoleRules -}}
{{- end -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.annotations" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
Expand All @@ -307,54 +283,29 @@ app.kubernetes.io/version: {{ .Values.agent.version}}
{{- $_ := set $preset "annotations" $presetAnnotations -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.containers" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- $presetContainers := dig "extraContainers" (list) $preset -}}
{{- $containersToAdd := get (include $templateName $ | fromYaml) "extraContainers"}}
{{- $presetContainers = uniq (concat $presetContainers $containersToAdd) -}}
{{- $_ := set $preset "extraContainers" $presetContainers -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.tolerations" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- $tolerationsToAdd := dig "tolerations" (list) (include $templateName $ | fromYaml) }}
{{- if $tolerationsToAdd -}}
{{- $preset := index . 0 -}}
{{- $tolerations := index . 1 -}}
{{- $tolerationsToAdd := dig "tolerations" (list) (include $tolerations $ | fromYaml) }}
{{- $presetTolerations := dig "tolerations" (list) $preset -}}
{{- $presetTolerations = uniq (concat $presetTolerations $tolerationsToAdd) -}}
{{- $_ := set $preset "tolerations" $tolerationsToAdd -}}
{{- end -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.initcontainers" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- $presetInitContainers := dig "initContainers" (list) $preset -}}
{{- $initContainersToAdd := get (include $templateName $ | fromYaml) "initContainers"}}
{{- $presetInitContainers = uniq (concat $presetInitContainers $initContainersToAdd) -}}
{{- $_ := set $preset "initContainers" $presetInitContainers -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.volumes" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- $preset := index . 0 -}}
{{- $volumes := index . 1 -}}
{{- $presetVolumes := dig "extraVolumes" (list) $preset -}}
{{- $volumesToAdd := get (include $templateName $ | fromYaml) "extraVolumes"}}
{{- $volumesToAdd := dig "extraVolumes" (list) $volumes -}}
{{- $presetVolumes = uniq (concat $presetVolumes $volumesToAdd) -}}
{{- $_ := set $preset "extraVolumes" $presetVolumes -}}
{{- end -}}

{{- define "elasticagent.preset.mutate.volumemounts" -}}
{{- $ := index . 0 -}}
{{- $preset := index . 1 -}}
{{- $templateName := index . 2 -}}
{{- $preset := index . 0 -}}
{{- $volumeMounts := index . 1 -}}
{{- $presetVolumeMounts := dig "extraVolumeMounts" (list) $preset -}}
{{- $volumeMountsToAdd := get (include $templateName $ | fromYaml) "extraVolumeMounts"}}
{{- $volumeMountsToAdd := dig "extraVolumeMounts" (list) $volumeMounts}}
{{- $presetVolumeMounts = uniq (concat $presetVolumeMounts $volumeMountsToAdd) -}}
{{- $_ := set $preset "extraVolumeMounts" $presetVolumeMounts -}}
{{- end -}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
{{- define "elasticagent.kubernetes.init" -}}
{{- if eq $.Values.kubernetes.enabled true -}}
{{- include "elasticagent.kubernetes.config.kube_apiserver.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.containers.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.cronjobs.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.daemonsets.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.deployments.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.jobs.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.namespaces.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.nodes.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.persistentvolumeclaims.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.persistentvolumes.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.pods.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.replicasets.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.resourcequotas.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.services.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.statefulsets.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.storageclasses.init" $ -}}
{{- include "elasticagent.kubernetes.config.kube_controller.init" $ -}}
{{- include "elasticagent.kubernetes.config.state.init" $ -}}
{{- include "elasticagent.kubernetes.config.audit_logs.init" $ -}}
{{- include "elasticagent.kubernetes.config.container_logs.init" $ -}}
{{- include "elasticagent.kubernetes.config.kubelet.containers.init" $ -}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{{- $preset := $.Values.agent.presets.clusterWide -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kube_apiserver.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.clusterwide.preset") -}}
{{- end -}}
{{- end -}}

Expand Down Expand Up @@ -38,4 +37,4 @@ period: "30s"
bearer_token_file: '/var/run/secrets/kubernetes.io/serviceaccount/token'
ssl.certificate_authorities:
- '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kube_controller.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kubelet.containers.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}
{{- end -}}
Expand Down Expand Up @@ -35,4 +34,4 @@ hosts:
period: "10s"
bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
ssl.verification_mode: "none"
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kubelet.nodes.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}
{{- end -}}
Expand Down Expand Up @@ -35,4 +34,4 @@ hosts:
period: "10s"
bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
ssl.verification_mode: "none"
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kubelet.pods.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}
{{- end -}}
Expand Down Expand Up @@ -35,4 +34,4 @@ hosts:
period: "10s"
bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
ssl.verification_mode: "none"
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kubelet.system.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}
{{- end -}}
Expand Down Expand Up @@ -35,4 +34,4 @@ hosts:
period: "10s"
bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
ssl.verification_mode: "none"
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kubelet.volumes.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}
{{- end -}}
Expand Down Expand Up @@ -35,4 +34,4 @@ hosts:
period: "10s"
bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
ssl.verification_mode: "none"
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.audit_logs.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.container_logs.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kube_proxy.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}

Expand All @@ -18,12 +17,12 @@ Config input for kube proxy
namespace: {{ .Values.kubernetes.namespace }}
use_output: {{ .Values.kubernetes.output }}
streams:
- id: kubernetes/metrics-kubernetes.proxy
data_stream:
type: metrics
dataset: kubernetes.proxy
metricsets:
- proxy
- id: kubernetes/metrics-kubernetes.proxy
data_stream:
type: metrics
dataset: kubernetes.proxy
metricsets:
- proxy
{{- mergeOverwrite $vars .Values.kubernetes.proxy.vars | toYaml | nindent 4 }}
{{- end -}}

Expand All @@ -35,4 +34,4 @@ Defaults for kube_proxy input streams
hosts:
- "localhost:10249"
period: "10s"
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{{- $preset := $.Values.agent.presets.perNode -}}
{{- $inputVal := (include "elasticagent.kubernetes.config.kube_scheduler.input" $ | fromYamlArray) -}}
{{- include "elasticagent.preset.mutate.inputs" (list $ $preset $inputVal) -}}
{{- include "elasticagent.preset.applyOnce" (list $ $preset "elasticagent.kubernetes.pernode.preset") -}}
{{- end -}}
{{- end -}}

Expand Down
Loading

0 comments on commit 27748fa

Please sign in to comment.