Skip to content
Draft
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ require (
github.com/samber/lo v1.52.0
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
golang.org/x/text v0.39.0
gomodules.xyz/jsonpatch/v2 v2.5.0
google.golang.org/protobuf v1.36.11
helm.sh/helm/v3 v3.20.2
k8s.io/kubectl v0.35.3
Expand Down Expand Up @@ -280,7 +281,6 @@ require (
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.47.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
Expand Down
24 changes: 6 additions & 18 deletions internal/controller/datadogagent/experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ import (
"encoding/json"
"fmt"
"maps"
"strings"
"time"

jsonpatch "gomodules.xyz/jsonpatch/v2"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

v2alpha1 "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1"
"github.com/DataDog/datadog-operator/pkg/kubernetes"
)

// ExperimentDefaultTimeout is the duration after which a running experiment is automatically rolled back.
Expand Down Expand Up @@ -347,28 +348,15 @@ func (r *Reconciler) processPromoteSignal(
return true, nil
}

// annotationToJSONPatchPath converts an annotation key to a JSON Patch path
// under /metadata/annotations, escaping "/" as "~1" per RFC 6901.
func annotationToJSONPatchPath(key string) string {
return "/metadata/annotations/" + strings.ReplaceAll(key, "/", "~1")
}

// jsonPatchOp represents a single JSON Patch operation (RFC 6902).
type jsonPatchOp struct {
Op string `json:"op"`
Path string `json:"path"`
Value string `json:"value,omitempty"`
}

// clearExperimentAnnotations removes the experiment signal annotations from the
// DDA using a conditional JSON Patch. The patch asserts the annotation ID matches
// the one we just processed, preventing accidental removal of a newer signal
// written concurrently by the daemon.
func (r *Reconciler) clearExperimentAnnotations(ctx context.Context, instance *v2alpha1.DatadogAgent, expectedID string) error {
ops := []jsonPatchOp{
{Op: "test", Path: annotationToJSONPatchPath(v2alpha1.AnnotationExperimentID), Value: expectedID},
{Op: "remove", Path: annotationToJSONPatchPath(v2alpha1.AnnotationExperimentSignal)},
{Op: "remove", Path: annotationToJSONPatchPath(v2alpha1.AnnotationExperimentID)},
ops := []jsonpatch.Operation{
{Operation: "test", Path: kubernetes.AnnotationJSONPatchPath(v2alpha1.AnnotationExperimentID), Value: expectedID},
{Operation: "remove", Path: kubernetes.AnnotationJSONPatchPath(v2alpha1.AnnotationExperimentSignal)},
{Operation: "remove", Path: kubernetes.AnnotationJSONPatchPath(v2alpha1.AnnotationExperimentID)},
}
patch, err := json.Marshal(ops)
if err != nil {
Expand Down
Loading
Loading