Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EDS Support #591

Merged
merged 2 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions apis/datadoghq/v2alpha1/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

edsdatadoghqv1alpha1 "github.com/DataDog/extendeddaemonset/api/v1alpha1"
)

// DatadogAgentState type representing the deployment state of the different Agent components.
Expand Down Expand Up @@ -181,3 +183,38 @@ func UpdateDaemonSetStatus(ds *appsv1.DaemonSet, dsStatus *DaemonSetStatus, upda
dsStatus.DaemonsetName = ds.ObjectMeta.Name
return dsStatus
}

// UpdateExtendedDaemonSetStatus updates an ExtendedDaemonSet's DaemonSetStatus
func UpdateExtendedDaemonSetStatus(eds *edsdatadoghqv1alpha1.ExtendedDaemonSet, dsStatus *DaemonSetStatus, updateTime *metav1.Time) *DaemonSetStatus {
if dsStatus == nil {
dsStatus = &DaemonSetStatus{}
}
if updateTime != nil {
dsStatus.LastUpdate = updateTime
}
if hash, ok := eds.Annotations[apicommon.MD5AgentDeploymentAnnotationKey]; ok {
dsStatus.CurrentHash = hash
}
dsStatus.Desired = eds.Status.Desired
dsStatus.Current = eds.Status.Current
dsStatus.Ready = eds.Status.Ready
dsStatus.Available = eds.Status.Available
dsStatus.UpToDate = eds.Status.UpToDate

var deploymentState DatadogAgentState
switch {
case eds.Status.Canary != nil:
deploymentState = DatadogAgentStateCanary
case dsStatus.UpToDate != dsStatus.Desired:
deploymentState = DatadogAgentStateUpdating
case dsStatus.Ready == 0:
deploymentState = DatadogAgentStateProgressing
default:
deploymentState = DatadogAgentStateRunning
}

dsStatus.State = fmt.Sprintf("%v", deploymentState)
dsStatus.Status = fmt.Sprintf("%v (%d/%d/%d)", deploymentState, dsStatus.Desired, dsStatus.Ready, dsStatus.UpToDate)
dsStatus.DaemonsetName = eds.ObjectMeta.Name
return dsStatus
}
7 changes: 5 additions & 2 deletions controllers/datadogagent/component/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ func NewExtendedDaemonset(owner metav1.Object, componentKind, componentName, ver

func defaultEDSStrategy() edsv1alpha1.ExtendedDaemonSetSpecStrategy {
return edsv1alpha1.ExtendedDaemonSetSpecStrategy{
Canary: edsv1alpha1.DefaultExtendedDaemonSetSpecStrategyCanary(nil, edsv1alpha1.ExtendedDaemonSetSpecStrategyCanaryValidationModeAuto),
RollingUpdate: *edsv1alpha1.DefaultExtendedDaemonSetSpecStrategyRollingUpdate(nil),
Canary: edsv1alpha1.DefaultExtendedDaemonSetSpecStrategyCanary(
&edsv1alpha1.ExtendedDaemonSetSpecStrategyCanary{},
edsv1alpha1.ExtendedDaemonSetSpecStrategyCanaryValidationModeAuto,
),
RollingUpdate: *edsv1alpha1.DefaultExtendedDaemonSetSpecStrategyRollingUpdate(&edsv1alpha1.ExtendedDaemonSetSpecStrategyRollingUpdate{}),
ReconcileFrequency: &metav1.Duration{
Duration: apicommon.DefaultReconcileFrequency,
},
Expand Down
37 changes: 28 additions & 9 deletions controllers/datadogagent/controller_reconcile_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,33 @@ import (
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

edsv1alpha1 "github.com/DataDog/extendeddaemonset/api/v1alpha1"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func (r *Reconciler) reconcileV2Agent(logger logr.Logger, features []feature.Feature, dda *datadoghqv2alpha1.DatadogAgent, resourcesManager feature.ResourceManagers, newStatus *datadoghqv2alpha1.DatadogAgentStatus, requiredContainers []common.AgentContainerName) (reconcile.Result, error) {
var result reconcile.Result
var err error
var eds *edsv1alpha1.ExtendedDaemonSet
var daemonset *appsv1.DaemonSet
var podManagers feature.PodTemplateManagers

// TODO for now only support Daemonset (not EDS)
if r.options.SupportExtendedDaemonset {
// Start by creating the Default Agent extendeddaemonset
eds = componentagent.NewDefaultAgentExtendedDaemonset(dda, requiredContainers)
podManagers = feature.NewPodTemplateManagers(&eds.Spec.Template)

// Start by creating the Default Cluster-Agent deployment
daemonset := componentagent.NewDefaultAgentDaemonset(dda, requiredContainers)
podManagers := feature.NewPodTemplateManagers(&daemonset.Spec.Template)
// Set Global setting on the default extendeddaemonset
eds.Spec.Template = *override.ApplyGlobalSettings(logger, podManagers, dda, resourcesManager, datadoghqv2alpha1.NodeAgentComponentName)
} else {
// Start by creating the Default Agent daemonset
daemonset = componentagent.NewDefaultAgentDaemonset(dda, requiredContainers)
podManagers = feature.NewPodTemplateManagers(&daemonset.Spec.Template)

// Set Global setting on the default deployment
daemonset.Spec.Template = *override.ApplyGlobalSettings(logger, podManagers, dda, resourcesManager, datadoghqv2alpha1.NodeAgentComponentName)
// Set Global setting on the default daemonset
daemonset.Spec.Template = *override.ApplyGlobalSettings(logger, podManagers, dda, resourcesManager, datadoghqv2alpha1.NodeAgentComponentName)
}

// Apply features changes on the Deployment.Spec.Template
for _, feat := range features {
Expand All @@ -39,7 +50,7 @@ func (r *Reconciler) reconcileV2Agent(logger logr.Logger, features []feature.Fea
}
}

// If Override is define for the cluster-checks-runner component, apply the override on the PodTemplateSpec, it will cascade to container.
// If Override is defined for the node agent component, apply the override on the PodTemplateSpec, it will cascade to container.
if _, ok := dda.Spec.Override[datadoghqv2alpha1.NodeAgentComponentName]; ok {
_, err = override.PodTemplateSpec(podManagers, dda.Spec.Override[datadoghqv2alpha1.NodeAgentComponentName])
if err != nil {
Expand All @@ -48,10 +59,18 @@ func (r *Reconciler) reconcileV2Agent(logger logr.Logger, features []feature.Fea
}

daemonsetLogger := logger.WithValues("component", datadoghqv2alpha1.NodeAgentComponentName)
return r.createOrUpdateDaemonset(daemonsetLogger, dda, daemonset, newStatus, updateStatusV2WithAgent)
if r.options.SupportExtendedDaemonset {
return r.createOrUpdateExtendedDaemonset(daemonsetLogger, dda, eds, newStatus, updateEDSStatusV2WithAgent)
}
return r.createOrUpdateDaemonset(daemonsetLogger, dda, daemonset, newStatus, updateDSStatusV2WithAgent)
}

func updateStatusV2WithAgent(dda *appsv1.DaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateTime metav1.Time, status metav1.ConditionStatus, reason, message string) {
func updateDSStatusV2WithAgent(dda *appsv1.DaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateTime metav1.Time, status metav1.ConditionStatus, reason, message string) {
newStatus.Agent = datadoghqv2alpha1.UpdateDaemonSetStatus(dda, newStatus.Agent, &updateTime)
datadoghqv2alpha1.UpdateDatadogAgentStatusConditions(newStatus, updateTime, datadoghqv2alpha1.AgentReconcileConditionType, status, reason, message, true)
}

func updateEDSStatusV2WithAgent(eds *edsv1alpha1.ExtendedDaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateTime metav1.Time, status metav1.ConditionStatus, reason, message string) {
newStatus.Agent = datadoghqv2alpha1.UpdateExtendedDaemonSetStatus(eds, newStatus.Agent, &updateTime)
datadoghqv2alpha1.UpdateDatadogAgentStatusConditions(newStatus, updateTime, datadoghqv2alpha1.AgentReconcileConditionType, status, reason, message, true)
}
124 changes: 108 additions & 16 deletions controllers/datadogagent/controller_reconcile_v2_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,26 @@ import (
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

edsv1alpha1 "github.com/DataDog/extendeddaemonset/api/v1alpha1"
)

type updateDepStatusComponentFunc func(deployment *appsv1.Deployment, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateTime metav1.Time, status metav1.ConditionStatus, reason, message string)
type updateDSStatusComponentFunc func(daemonset *appsv1.DaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateTime metav1.Time, status metav1.ConditionStatus, reason, message string)
type updateEDSStatusComponentFunc func(eds *edsv1alpha1.ExtendedDaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateTime metav1.Time, status metav1.ConditionStatus, reason, message string)

func (r *Reconciler) createOrUpdateDeployment(parentLogger logr.Logger, dda *datadoghqv2alpha1.DatadogAgent, deployment *appsv1.Deployment, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateStatusFunc updateDepStatusComponentFunc) (reconcile.Result, error) {
logger := parentLogger.WithValues("deployment.Namespace", deployment.Namespace, "deployment.Name", deployment.Name)

var result reconcile.Result
var err error

// Set DatadogAgent instance instance as the owner and controller
// Set DatadogAgent instance as the owner and controller
if err = controllerutil.SetControllerReference(dda, deployment, r.scheme); err != nil {
return reconcile.Result{}, err
}

// From here the PodTemplateSpec should be ready, we can generate the hash that will be use to compare this deployment with the current (if exist).
// From here the PodTemplateSpec should be ready, we can generate the hash that will be used to compare this deployment with the current one (if it exists).
var hash string
hash, err = comparison.SetMD5DatadogAgentGenerationAnnotation(&deployment.ObjectMeta, deployment.Spec)
if err != nil {
Expand All @@ -51,31 +54,31 @@ func (r *Reconciler) createOrUpdateDeployment(parentLogger logr.Logger, dda *dat
}

currentDeployment := &appsv1.Deployment{}
alreadyExist := true
alreadyExists := true
err = r.client.Get(context.TODO(), nsName, currentDeployment)
if err != nil {
if apierrors.IsNotFound(err) {
logger.Info("deployment is not found")
alreadyExist = false
alreadyExists = false
} else {
logger.Error(err, "unexpected error during deployment get")
return reconcile.Result{}, err
}
}

if alreadyExist {
if alreadyExists {
// check if same hash
needUpdate := !comparison.IsSameSpecMD5Hash(hash, currentDeployment.GetAnnotations())
if !needUpdate {
// no need to update to stop here the process
// no need to update hasn't changed
now := metav1.NewTime(time.Now())
updateStatusFunc(currentDeployment, newStatus, now, metav1.ConditionTrue, "deployment_up_to_date", "Deployment up-to-date")
return reconcile.Result{}, nil
}

logger.Info("Updating Deployment")

// TODO: these parameter can be added to the override.PodTemplateSpec. (it exist in v1alpha)
// TODO: these parameters can be added to the override.PodTemplateSpec. (It exists in v1alpha1)
keepAnnotationsFilter := ""
keepLabelsFilter := ""

Expand Down Expand Up @@ -119,12 +122,12 @@ func (r *Reconciler) createOrUpdateDaemonset(parentLogger logr.Logger, dda *data
var result reconcile.Result
var err error

// Set DatadogAgent instance instance as the owner and controller
// Set DatadogAgent instance as the owner and controller
if err = controllerutil.SetControllerReference(dda, daemonset, r.scheme); err != nil {
return reconcile.Result{}, err
}

// From here the PodTemplateSpec should be ready, we can generate the hash that will be use to compare this daemonset with the current (if exist).
// From here the PodTemplateSpec should be ready, we can generate the hash that will be used to compare this daemonset with the current one (if it exists).
var hash string
hash, err = comparison.SetMD5DatadogAgentGenerationAnnotation(&daemonset.ObjectMeta, daemonset.Spec)
if err != nil {
Expand All @@ -138,35 +141,35 @@ func (r *Reconciler) createOrUpdateDaemonset(parentLogger logr.Logger, dda *data
}

currentDaemonset := &appsv1.DaemonSet{}
alreadyExist := true
alreadyExists := true
err = r.client.Get(context.TODO(), nsName, currentDaemonset)
if err != nil {
if apierrors.IsNotFound(err) {
logger.Info("daemonset is not found")
alreadyExist = false
alreadyExists = false
} else {
logger.Error(err, "unexpected error during daemonset get")
return reconcile.Result{}, err
}
}

if alreadyExist {
if alreadyExists {
// check if same hash
needUpdate := !comparison.IsSameSpecMD5Hash(hash, currentDaemonset.GetAnnotations())
if !needUpdate {
// Even if the DaemonSet is still the same, it's status might have
// Even if the DaemonSet is still the same, its status might have
// changed (for example, the number of pods ready). This call is
// needed to keep the agent status updated.
now := metav1.NewTime(time.Now())
newStatus.Agent = datadoghqv2alpha1.UpdateDaemonSetStatus(currentDaemonset, newStatus.Agent, &now)

// no need to update the DaemonSet to stop here the process
// Stop reconcile loop since DaemonSet hasn't changed
return reconcile.Result{}, nil
}

logger.Info("Updating Daemonset")

// TODO: these parameter can be added to the override.PodTemplateSpec. (it exist in v1alpha)
// TODO: these parameters can be added to the override.PodTemplateSpec. (It exists in v1alpha1)
keepAnnotationsFilter := ""
keepLabelsFilter := ""

Expand All @@ -181,7 +184,7 @@ func (r *Reconciler) createOrUpdateDaemonset(parentLogger logr.Logger, dda *data
if err != nil {
return reconcile.Result{}, err
}
event := buildEventInfo(updateDaemonset.Name, updateDaemonset.Namespace, deploymentKind, datadog.UpdateEvent)
event := buildEventInfo(updateDaemonset.Name, updateDaemonset.Namespace, daemonSetKind, datadog.UpdateEvent)
r.recordEvent(dda, event)
updateStatusFunc(updateDaemonset, newStatus, now, metav1.ConditionTrue, "Daemonset_updated", "Daemonset updated")
} else {
Expand All @@ -201,3 +204,92 @@ func (r *Reconciler) createOrUpdateDaemonset(parentLogger logr.Logger, dda *data

return result, err
}

func (r *Reconciler) createOrUpdateExtendedDaemonset(parentLogger logr.Logger, dda *datadoghqv2alpha1.DatadogAgent, eds *edsv1alpha1.ExtendedDaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus, updateStatusFunc updateEDSStatusComponentFunc) (reconcile.Result, error) {
logger := parentLogger.WithValues("ExtendedDaemonSet.Namespace", eds.Namespace, "ExtendedDaemonSet.Name", eds.Name)

var result reconcile.Result
var err error

// Set DatadogAgent instance as the owner and controller
if err = controllerutil.SetControllerReference(dda, eds, r.scheme); err != nil {
return reconcile.Result{}, err
}

// From here the PodTemplateSpec should be ready, we can generate the hash that will be used to compare this extendeddaemonset with the current one (if it exists).
var hash string
hash, err = comparison.SetMD5DatadogAgentGenerationAnnotation(&eds.ObjectMeta, eds.Spec)
if err != nil {
return result, err
}

// Get the current extendeddaemonset and compare
nsName := types.NamespacedName{
Name: eds.GetName(),
Namespace: eds.GetNamespace(),
}

currentEDS := &edsv1alpha1.ExtendedDaemonSet{}
alreadyExists := true
err = r.client.Get(context.TODO(), nsName, currentEDS)
if err != nil {
if apierrors.IsNotFound(err) {
logger.Info("ExtendedDaemonSet is not found")
alreadyExists = false
} else {
logger.Error(err, "unexpected error during ExtendedDaemonSet get")
return reconcile.Result{}, err
}
}

if alreadyExists {
// check if same hash
needUpdate := !comparison.IsSameSpecMD5Hash(hash, currentEDS.GetAnnotations())
if !needUpdate {
// Even if the EDS is still the same, its status might have
// changed (for example, the number of pods ready). This call is
// needed to keep the agent status updated.
now := metav1.NewTime(time.Now())
newStatus.Agent = datadoghqv2alpha1.UpdateExtendedDaemonSetStatus(currentEDS, newStatus.Agent, &now)

// Stop reconcile loop since EDS hasn't changed
return reconcile.Result{}, nil
}

logger.Info("Updating ExtendedDaemonSet")

// TODO: these parameters can be added to the override.PodTemplateSpec. (It exists in v1alpha1)
keepAnnotationsFilter := ""
keepLabelsFilter := ""

// Copy possibly changed fields
updateEDS := eds.DeepCopy()
updateEDS.Spec = *eds.Spec.DeepCopy()
updateEDS.Annotations = mergeAnnotationsLabels(logger, currentEDS.GetAnnotations(), eds.GetAnnotations(), keepAnnotationsFilter)
updateEDS.Labels = mergeAnnotationsLabels(logger, currentEDS.GetLabels(), eds.GetLabels(), keepLabelsFilter)

now := metav1.NewTime(time.Now())
err = kubernetes.UpdateFromObject(context.TODO(), r.client, updateEDS, currentEDS.ObjectMeta)
if err != nil {
return reconcile.Result{}, err
}
event := buildEventInfo(updateEDS.Name, updateEDS.Namespace, extendedDaemonSetKind, datadog.UpdateEvent)
r.recordEvent(dda, event)
updateStatusFunc(updateEDS, newStatus, now, metav1.ConditionTrue, "ExtendedDaemonSet_updated", "ExtendedDaemonSet updated")
} else {
now := metav1.NewTime(time.Now())

err = r.client.Create(context.TODO(), eds)
if err != nil {
updateStatusFunc(nil, newStatus, now, metav1.ConditionFalse, "create_failed", "Unable to create ExtendedDaemonSet")
return reconcile.Result{}, err
}
event := buildEventInfo(eds.Name, eds.Namespace, extendedDaemonSetKind, datadog.CreationEvent)
r.recordEvent(dda, event)
updateStatusFunc(eds, newStatus, now, metav1.ConditionTrue, "create_success", "ExtendedDaemonSet created")
}

logger.Info("Creating ExtendedDaemonSet")

return result, err
}