Skip to content

Commit

Permalink
feat: add support for statefulset agent type
Browse files Browse the repository at this point in the history
  • Loading branch information
pkoutsovasilis committed Dec 1, 2023
1 parent 58b5a73 commit db9ab00
Show file tree
Hide file tree
Showing 16 changed files with 25,541 additions and 88 deletions.
8,347 changes: 8,344 additions & 3 deletions config/crds/v1/all-crds.yaml

Large diffs are not rendered by default.

8,347 changes: 8,344 additions & 3 deletions config/crds/v1/bases/agent.k8s.elastic.co_agents.yaml

Large diffs are not rendered by default.

29 changes: 3 additions & 26 deletions config/recipes/elastic-agent/system-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ metadata:
name: elastic-agent
spec:
version: 8.10.4
elasticsearchRefs:
- name: elasticsearch
daemonSet:
statefulSet:
serviceName: elastic-agent
replicas: 2
podTemplate:
spec:
containers:
Expand Down Expand Up @@ -130,26 +130,3 @@ spec:
metricsets:
- uptime
period: 10s
---
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: elasticsearch
spec:
version: 8.10.4
nodeSets:
- name: default
count: 3
config:
node.store.allow_mmap: false
---
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: kibana
spec:
version: 8.10.4
count: 1
elasticsearchRef:
name: elasticsearch
...
8,347 changes: 8,344 additions & 3 deletions deploy/eck-operator/charts/eck-operator-crds/templates/all-crds.yaml

Large diffs are not rendered by default.

27 changes: 24 additions & 3 deletions docs/reference/api-docs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ AgentSpec defines the desired state of the Agent
| *`configRef`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-common-v1-configsource[$$ConfigSource$$]__ | ConfigRef contains a reference to an existing Kubernetes Secret holding the Agent configuration. Agent settings must be specified as yaml, under a single "agent.yml" entry. At most one of [`Config`, `ConfigRef`] can be specified.
| *`secureSettings`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-common-v1-secretsource[$$SecretSource$$] array__ | SecureSettings is a list of references to Kubernetes Secrets containing sensitive configuration options for the Agent. Secrets data can be then referenced in the Agent config using the Secret's keys or as specified in `Entries` field of each SecureSetting.
| *`serviceAccountName`* __string__ | ServiceAccountName is used to check access from the current resource to an Elasticsearch resource in a different namespace. Can only be used if ECK is enforcing RBAC on references.
| *`daemonSet`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-daemonsetspec[$$DaemonSetSpec$$]__ | DaemonSet specifies the Agent should be deployed as a DaemonSet, and allows providing its spec. Cannot be used along with `deployment`.
| *`deployment`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-deploymentspec[$$DeploymentSpec$$]__ | Deployment specifies the Agent should be deployed as a Deployment, and allows providing its spec. Cannot be used along with `daemonSet`.
| *`revisionHistoryLimit`* __integer__ | RevisionHistoryLimit is the number of revisions to retain to allow rollback in the underlying DaemonSet or Deployment.
| *`daemonSet`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-daemonsetspec[$$DaemonSetSpec$$]__ | DaemonSet specifies the Agent should be deployed as a DaemonSet, and allows providing its spec. Cannot be used along with `deployment` or `statefulSet`.
| *`deployment`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-deploymentspec[$$DeploymentSpec$$]__ | Deployment specifies the Agent should be deployed as a Deployment, and allows providing its spec. Cannot be used along with `daemonSet` or `statefulSet`.
| *`statefulSet`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-statefulsetspec[$$StatefulSetSpec$$]__ | StatefulSet specifies the Agent should be deployed as a StatefulSet, and allows providing its spec. Cannot be used along with `daemonSet` or `deployment`.
| *`revisionHistoryLimit`* __integer__ | RevisionHistoryLimit is the number of revisions to retain to allow rollback in the underlying DaemonSet or Deployment or StatefulSet.
| *`http`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-common-v1-httpconfig[$$HTTPConfig$$]__ | HTTP holds the HTTP layer configuration for the Agent in Fleet mode with Fleet Server enabled.
| *`mode`* __xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-agentmode[$$AgentMode$$]__ | Mode specifies the source of configuration for the Agent. The configuration can be specified locally through `config` or `configRef` (`standalone` mode), or come from Fleet during runtime (`fleet` mode). Defaults to `standalone` mode.
| *`fleetServerEnabled`* __boolean__ | FleetServerEnabled determines whether this Agent will launch Fleet Server. Don't set unless `mode` is set to `fleet`.
Expand Down Expand Up @@ -158,6 +159,26 @@ AgentSpec defines the desired state of the Agent
|===


[id="{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-statefulsetspec"]
=== StatefulSetSpec



.Appears In:
****
- xref:{anchor_prefix}-github-com-elastic-cloud-on-k8s-v2-pkg-apis-agent-v1alpha1-agentspec[$$AgentSpec$$]
****

[cols="25a,75a", options="header"]
|===
| Field | Description
| *`podTemplate`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core[$$PodTemplateSpec$$]__ |
| *`replicas`* __integer__ |
| *`serviceName`* __string__ |
| *`volumeClaimTemplates`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#persistentvolumeclaim-v1-core[$$PersistentVolumeClaim$$] array__ | VolumeClaimTemplates is a list of persistent volume claims to be used by each Pod. Every claim in this list must have a matching volumeMount in one of the containers defined in the PodTemplate. Items defined here take precedence over any default claims added by the operator with the same name.
|===



[id="{anchor_prefix}-apm-k8s-elastic-co-v1"]
== apm.k8s.elastic.co/v1
Expand Down
23 changes: 20 additions & 3 deletions pkg/apis/agent/v1alpha1/agent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,21 @@ type AgentSpec struct {
ServiceAccountName string `json:"serviceAccountName,omitempty"`

// DaemonSet specifies the Agent should be deployed as a DaemonSet, and allows providing its spec.
// Cannot be used along with `deployment`.
// Cannot be used along with `deployment` or `statefulSet`.
// +kubebuilder:validation:Optional
DaemonSet *DaemonSetSpec `json:"daemonSet,omitempty"`

// Deployment specifies the Agent should be deployed as a Deployment, and allows providing its spec.
// Cannot be used along with `daemonSet`.
// Cannot be used along with `daemonSet` or `statefulSet`.
// +kubebuilder:validation:Optional
Deployment *DeploymentSpec `json:"deployment,omitempty"`

// RevisionHistoryLimit is the number of revisions to retain to allow rollback in the underlying DaemonSet or Deployment.
// StatefulSet specifies the Agent should be deployed as a StatefulSet, and allows providing its spec.
// Cannot be used along with `daemonSet` or `deployment`.
// +kubebuilder:validation:Optional
StatefulSet *StatefulSetSpec `json:"statefulSet,omitempty"`

// RevisionHistoryLimit is the number of revisions to retain to allow rollback in the underlying DaemonSet or Deployment or StatefulSet.
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"`

// HTTP holds the HTTP layer configuration for the Agent in Fleet mode with Fleet Server enabled.
Expand Down Expand Up @@ -127,6 +132,18 @@ type DeploymentSpec struct {
Strategy appsv1.DeploymentStrategy `json:"strategy,omitempty"`
}

type StatefulSetSpec struct {
// +kubebuilder:pruning:PreserveUnknownFields
PodTemplate corev1.PodTemplateSpec `json:"podTemplate,omitempty"`
Replicas *int32 `json:"replicas,omitempty"`
ServiceName string `json:"serviceName,omitempty"`
// VolumeClaimTemplates is a list of persistent volume claims to be used by each Pod.
// Every claim in this list must have a matching volumeMount in one of the containers defined in the PodTemplate.
// Items defined here take precedence over any default claims added by the operator with the same name.
// +kubebuilder:validation:Optional
VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"`
}

// AgentStatus defines the observed state of the Agent
type AgentStatus struct {
// Version of the stack resource currently running. During version upgrades, multiple versions may run
Expand Down
39 changes: 32 additions & 7 deletions pkg/apis/agent/v1alpha1/validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package v1alpha1
import (
"fmt"
"reflect"
"strings"

"k8s.io/apimachinery/pkg/util/validation/field"

Expand Down Expand Up @@ -70,12 +71,25 @@ func checkPolicyID(a *Agent) field.ErrorList {
}

func checkAtMostOneDeploymentOption(a *Agent) field.ErrorList {
if a.Spec.DaemonSet != nil && a.Spec.Deployment != nil {
msg := "Specify either daemonSet or deployment, not both"
return field.ErrorList{
field.Forbidden(field.NewPath("spec").Child("daemonSet"), msg),
field.Forbidden(field.NewPath("spec").Child("deployment"), msg),
var enabledSpecsNames []string

if a.Spec.DaemonSet != nil {
enabledSpecsNames = append(enabledSpecsNames, "daemonSet")
}
if a.Spec.Deployment != nil {
enabledSpecsNames = append(enabledSpecsNames, "deployment")
}
if a.Spec.StatefulSet != nil {
enabledSpecsNames = append(enabledSpecsNames, "statefulSet")
}

if enabledSpecsLen := len(enabledSpecsNames); enabledSpecsLen > 1 {
msg := fmt.Sprintf("Specify at most one of [%s]", strings.Join(enabledSpecsNames, ", "))
errList := make(field.ErrorList, enabledSpecsLen)
for index, specName := range enabledSpecsNames {
errList[index] = field.Forbidden(field.NewPath("spec").Child(specName), msg)
}
return errList
}

return nil
Expand Down Expand Up @@ -136,9 +150,20 @@ func checkSingleConfigSource(a *Agent) field.ErrorList {
}

func checkSpec(a *Agent) field.ErrorList {
if (a.Spec.DaemonSet == nil && a.Spec.Deployment == nil) || (a.Spec.DaemonSet != nil && a.Spec.Deployment != nil) {
enabledSpecs := 0
if a.Spec.DaemonSet != nil {
enabledSpecs++
}
if a.Spec.Deployment != nil {
enabledSpecs++
}
if a.Spec.StatefulSet != nil {
enabledSpecs++
}

if enabledSpecs != 1 {
return field.ErrorList{
field.Invalid(field.NewPath("spec"), a.Spec, "either daemonset or deployment must be specified"),
field.Invalid(field.NewPath("spec"), a.Spec, "either daemonSet or deployment or statefulSet must be specified"),
}
}
return nil
Expand Down
46 changes: 43 additions & 3 deletions pkg/apis/agent/v1alpha1/validations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func Test_checkSpec(t *testing.T) {
wantErr bool
}{
{
name: "deployment absent, dset present",
name: "deployment absent, statefulSet absent, daemonSet present",
beat: Agent{
Spec: AgentSpec{
DaemonSet: &DaemonSetSpec{},
Expand All @@ -141,14 +141,23 @@ func Test_checkSpec(t *testing.T) {
wantErr: false,
},
{
name: "deployment present, dset absent",
name: "deployment present, statefulSet absent, daemonSet absent",
beat: Agent{
Spec: AgentSpec{
Deployment: &DeploymentSpec{},
},
},
wantErr: false,
},
{
name: "deployment absent, statefulSet present, daemonSet absent",
beat: Agent{
Spec: AgentSpec{
StatefulSet: &StatefulSetSpec{},
},
},
wantErr: false,
},
{
name: "neither present",
beat: Agent{
Expand All @@ -157,7 +166,7 @@ func Test_checkSpec(t *testing.T) {
wantErr: true,
},
{
name: "both present",
name: "both daemonSet and deployment present",
beat: Agent{
Spec: AgentSpec{
Deployment: &DeploymentSpec{},
Expand All @@ -166,6 +175,37 @@ func Test_checkSpec(t *testing.T) {
},
wantErr: true,
},
{
name: "both daemonSet and statefulSet present",
beat: Agent{
Spec: AgentSpec{
DaemonSet: &DaemonSetSpec{},
StatefulSet: &StatefulSetSpec{},
},
},
wantErr: true,
},
{
name: "both statefulSet and deployment present",
beat: Agent{
Spec: AgentSpec{
Deployment: &DeploymentSpec{},
StatefulSet: &StatefulSetSpec{},
},
},
wantErr: true,
},
{
name: "all statefulSet, daemonSet and deployment present",
beat: Agent{
Spec: AgentSpec{
Deployment: &DeploymentSpec{},
StatefulSet: &StatefulSetSpec{},
DaemonSet: &DaemonSetSpec{},
},
},
wantErr: true,
},
}

for _, tc := range tests {
Expand Down
34 changes: 34 additions & 0 deletions pkg/apis/agent/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions pkg/controller/agent/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,14 @@ func (p Params) DynamicWatches() watches.DynamicWatches {

// GetPodTemplate returns the configured pod template for the associated Elastic Agent.
func (p *Params) GetPodTemplate() corev1.PodTemplateSpec {
if p.Agent.Spec.DaemonSet != nil {
switch {
case p.Agent.Spec.DaemonSet != nil:
return p.Agent.Spec.DaemonSet.PodTemplate
case p.Agent.Spec.StatefulSet != nil:
return p.Agent.Spec.StatefulSet.PodTemplate
default:
return p.Agent.Spec.Deployment.PodTemplate
}

return p.Agent.Spec.Deployment.PodTemplate
}

// Logger returns the configured logger for use during reconciliation.
Expand Down
21 changes: 12 additions & 9 deletions pkg/controller/agent/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,15 @@ func buildPodTemplate(params Params, fleetCerts *certificates.CertificatesSecret
}
vols = append(vols, caAssocVols...)

labels := maps.Merge(params.Agent.GetIdentityLabels(), map[string]string{
agentLabels := maps.Merge(params.Agent.GetIdentityLabels(), map[string]string{
VersionLabelName: spec.Version})

annotations := map[string]string{
ConfigHashAnnotationName: fmt.Sprint(configHash.Sum32()),
}

builder = builder.
WithLabels(labels).
WithLabels(agentLabels).
WithAnnotations(annotations).
WithDockerImage(spec.Image, container.ImageRepository(container.AgentImage, spec.Version)).
WithAutomountServiceAccountToken().
Expand Down Expand Up @@ -372,13 +372,16 @@ func applyRelatedEsAssoc(agent agentv1alpha1.Agent, esAssociation commonv1.Assoc
}

func runningAsRoot(agent agentv1alpha1.Agent) bool {
if agent.Spec.DaemonSet != nil {
switch {
case agent.Spec.DaemonSet != nil:
return runningContainerAsRoot(agent.Spec.DaemonSet.PodTemplate)
}
if agent.Spec.Deployment != nil {
case agent.Spec.Deployment != nil:
return runningContainerAsRoot(agent.Spec.Deployment.PodTemplate)
case agent.Spec.StatefulSet != nil:
return runningContainerAsRoot(agent.Spec.StatefulSet.PodTemplate)
default:
return false
}
return false
}

func runningContainerAsRoot(podTemplate corev1.PodTemplateSpec) bool {
Expand All @@ -387,9 +390,9 @@ func runningContainerAsRoot(podTemplate corev1.PodTemplateSpec) bool {
*podTemplate.Spec.SecurityContext.RunAsUser == 0 {
return true
}
for _, container := range podTemplate.Spec.Containers {
if container.SecurityContext != nil && container.SecurityContext.RunAsUser != nil {
if *container.SecurityContext.RunAsUser == 0 {
for _, podContainer := range podTemplate.Spec.Containers {
if podContainer.SecurityContext != nil && podContainer.SecurityContext.RunAsUser != nil {
if *podContainer.SecurityContext.RunAsUser == 0 {
return true
}
}
Expand Down
Loading

0 comments on commit db9ab00

Please sign in to comment.