Skip to content
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
5 changes: 0 additions & 5 deletions pkg/controller/utils/metadata/crd_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,6 @@ func NewCRDMetadataForwarder(logger logr.Logger, k8sClient client.Reader, kubern

// Start starts the CRD metadata forwarder
func (cmf *CRDMetadataForwarder) Start() {
if cmf.hostName == "" {
cmf.logger.Error(ErrEmptyHostName, "Could not set host name; not starting metadata forwarder")
return
}

cmf.logger.Info("Starting metadata forwarder")

ticker := time.NewTicker(crdMetadataInterval)
Expand Down
20 changes: 0 additions & 20 deletions pkg/controller/utils/metadata/credential_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ func TestSetupRequestPrerequisites(t *testing.T) {
setupEnv: func() {
os.Setenv(constants.DDAPIKey, "operator-api-key")
os.Setenv(constants.DDAppKey, "operator-app-key")
os.Setenv(constants.DDHostName, "test-hostname")
os.Setenv(constants.DDClusterName, "test-cluster")
},
setupDDA: func() []client.Object {
Expand Down Expand Up @@ -107,7 +106,6 @@ func TestSetupRequestPrerequisites(t *testing.T) {
{
name: "DDA with plaintext API key and default site",
setupEnv: func() {
os.Setenv(constants.DDHostName, "test-hostname")
// No operator credentials
},
setupDDA: func() []client.Object {
Expand Down Expand Up @@ -136,7 +134,6 @@ func TestSetupRequestPrerequisites(t *testing.T) {
{
name: "DDA with API key and custom site",
setupEnv: func() {
os.Setenv(constants.DDHostName, "test-hostname")
// No operator credentials
},
setupDDA: func() []client.Object {
Expand Down Expand Up @@ -166,7 +163,6 @@ func TestSetupRequestPrerequisites(t *testing.T) {
{
name: "DDA with secret reference",
setupEnv: func() {
os.Setenv(constants.DDHostName, "test-hostname")
// No operator credentials
},
setupDDA: func() []client.Object {
Expand Down Expand Up @@ -211,7 +207,6 @@ func TestSetupRequestPrerequisites(t *testing.T) {
{
name: "DDA with encrypted API key",
setupEnv: func() {
os.Setenv(constants.DDHostName, "test-hostname")
// No operator credentials
},
setupDDA: func() []client.Object {
Expand Down Expand Up @@ -243,7 +238,6 @@ func TestSetupRequestPrerequisites(t *testing.T) {
setupEnv: func() {
os.Setenv(constants.DDAPIKey, "operator-api-key")
os.Setenv(constants.DDAppKey, "operator-app-key")
os.Setenv(constants.DDHostName, "test-hostname")
// Note: No DD_CLUSTER_NAME set to trigger fallback
},
setupDDA: func() []client.Object {
Expand All @@ -270,23 +264,9 @@ func TestSetupRequestPrerequisites(t *testing.T) {
wantErr: false,
},
// Error cases
{
name: "missing hostname should fail",
setupEnv: func() {
os.Setenv(constants.DDAPIKey, "operator-api-key")
os.Setenv(constants.DDAppKey, "operator-app-key")
// No DDHostName set
},
setupDDA: func() []client.Object {
return []client.Object{} // No DDA
},
wantClusterName: "", // Not relevant for error case
wantErr: true,
},
{
name: "no credentials anywhere should fail",
setupEnv: func() {
os.Setenv(constants.DDHostName, "test-hostname")
// No operator credentials
},
setupDDA: func() []client.Object {
Expand Down
17 changes: 7 additions & 10 deletions pkg/controller/utils/metadata/helm_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,18 @@ type HelmMetadataForwarder struct {
}

type HelmMetadataPayload struct {
Hostname string `json:"hostname"`
UUID string `json:"uuid"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this require schema or writer change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the dd-go backend creates a schema resource based on the HelmMetadataPayload object HelmMetadata (datadog_operator_helm_metadata) and the schema matches that struct.

Timestamp int64 `json:"timestamp"`
ClusterID string `json:"cluster_id"`
Metadata HelmMetadata `json:"datadog_operator_helm_metadata"`
}

type HelmMetadata struct {
OperatorVersion string `json:"operator_version"`
KubernetesVersion string `json:"kubernetes_version"`
ClusterID string `json:"cluster_id"`
// Shared
OperatorVersion string `json:"operator_version"`
KubernetesVersion string `json:"kubernetes_version"`
ClusterID string `json:"cluster_id"`

ChartName string `json:"chart_name"`
ChartReleaseName string `json:"chart_release_name"`
ChartAppVersion string `json:"chart_app_version"`
Expand Down Expand Up @@ -137,11 +139,6 @@ func getWatchNamespacesForHelm(logger logr.Logger) []string {

// Start starts the helm metadata forwarder
func (hmf *HelmMetadataForwarder) Start() {
if hmf.hostName == "" {
hmf.logger.Error(ErrEmptyHostName, "Could not set host name; not starting metadata forwarder")
return
}

hmf.logger.Info("Starting metadata forwarder")

ticker := time.NewTicker(defaultInterval)
Expand Down Expand Up @@ -252,7 +249,7 @@ func (hmf *HelmMetadataForwarder) buildPayload(release HelmReleaseData, clusterU
}

payload := HelmMetadataPayload{
Hostname: hmf.hostName,
UUID: clusterUID,
Timestamp: now,
ClusterID: clusterUID,
Metadata: helmMetadata,
Expand Down
8 changes: 0 additions & 8 deletions pkg/controller/utils/metadata/helm_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
func Test_HelmMetadataForwarder_getPayload(t *testing.T) {
expectedKubernetesVersion := "v1.28.0"
expectedOperatorVersion := "v1.19.0"
expectedHostname := "test-host"
expectedClusterUID := "test-cluster-uid-123"
expectedReleaseName := "my-release"
expectedNamespace := "default"
Expand All @@ -30,9 +29,6 @@ func Test_HelmMetadataForwarder_getPayload(t *testing.T) {

hmf := NewHelmMetadataForwarder(zap.New(zap.UseDevMode(true)), nil, expectedKubernetesVersion, expectedOperatorVersion, config.NewCredentialManager(fake.NewFakeClient()))

// Set required fields
hmf.hostName = expectedHostname

release := HelmReleaseData{
ReleaseName: expectedReleaseName,
Namespace: expectedNamespace,
Expand Down Expand Up @@ -60,10 +56,6 @@ func Test_HelmMetadataForwarder_getPayload(t *testing.T) {
}

// Validate top-level fields
if hostname, ok := parsed["hostname"].(string); !ok || hostname != expectedHostname {
t.Errorf("buildPayload() hostname = %v, want %v", hostname, expectedHostname)
}

if timestamp, ok := parsed["timestamp"].(float64); !ok || timestamp <= 0 {
t.Errorf("buildPayload() timestamp = %v, want positive number", timestamp)
}
Expand Down
16 changes: 7 additions & 9 deletions pkg/controller/utils/metadata/operator_metadata.go
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not directly related to this PR, but we have a potential data race in func (omf *OperatorMetadataForwarder) GetPayload(clusterUID string).
In OperatorMetadataForwarder.GetPayload(), we use omf.mutex.RLock() (read lock) and then mutate shared state omf.OperatorMetadata.*.

  • Multiple goroutines can hold an RLock() at the same time, so two callers could write to omf.OperatorMetadata concurrently.
  • OperatorMetadata contains ResourceCounts map[string]int with maps not being safe for concurrent access if anyone might write.

A safe approach could be: copy under lock, unlock, then mutate the local copy and marshal

	omf.mutex.RLock()
	// Copy metadata while holding the lock, to avoid data races.
	operatorMetadata := omf.OperatorMetadata
	if omf.OperatorMetadata.ResourceCounts != nil {
		operatorMetadata.ResourceCounts = make(map[string]int, len(omf.OperatorMetadata.ResourceCounts))
		maps.Copy(operatorMetadata.ResourceCounts, omf.OperatorMetadata.ResourceCounts)
	}
	omf.mutex.RUnlock()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in a separate PR: #2481

Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@ type OperatorMetadataForwarder struct {
}

type OperatorMetadataPayload struct {
Hostname string `json:"hostname"`
UUID string `json:"uuid"`
Timestamp int64 `json:"timestamp"`
ClusterID string `json:"cluster_id"`
Metadata OperatorMetadata `json:"datadog_operator_metadata"`
}

type OperatorMetadata struct {
OperatorVersion string `json:"operator_version"`
KubernetesVersion string `json:"kubernetes_version"`
// Shared
OperatorVersion string `json:"operator_version"`
KubernetesVersion string `json:"kubernetes_version"`
ClusterID string `json:"cluster_id"`

InstallMethodTool string `json:"install_method_tool"`
InstallMethodToolVersion string `json:"install_method_tool_version"`
IsLeader bool `json:"is_leader"`
Expand All @@ -57,7 +60,6 @@ type OperatorMetadata struct {
ExtendedDaemonSetEnabled bool `json:"extendeddaemonset_enabled"`
RemoteConfigEnabled bool `json:"remote_config_enabled"`
IntrospectionEnabled bool `json:"introspection_enabled"`
ClusterID string `json:"cluster_id"`
ConfigDDURL string `json:"config_dd_url"`
ConfigDDSite string `json:"config_site"`
ResourceCounts map[string]int `json:"resource_count"`
Expand All @@ -74,10 +76,6 @@ func NewOperatorMetadataForwarder(logger logr.Logger, k8sClient client.Reader, k

// Start starts the operator metadata forwarder
func (omf *OperatorMetadataForwarder) Start() {
if omf.hostName == "" {
omf.logger.Error(ErrEmptyHostName, "Could not set host name; not starting metadata forwarder")
return
}
omf.updateResourceCounts()

omf.logger.Info("Starting metadata forwarder")
Expand Down Expand Up @@ -131,7 +129,7 @@ func (omf *OperatorMetadataForwarder) GetPayload(clusterUID string) []byte {
omf.OperatorMetadata.KubernetesVersion = omf.kubernetesVersion

payload := OperatorMetadataPayload{
Hostname: omf.hostName,
UUID: clusterUID,
Timestamp: now,
ClusterID: clusterUID,
Metadata: omf.OperatorMetadata,
Expand Down
14 changes: 0 additions & 14 deletions pkg/controller/utils/metadata/operator_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ func Test_getURL(t *testing.T) {
os.Setenv("DD_CLUSTER_NAME", "cluster")
os.Setenv("DD_API_KEY", "api-key")
os.Setenv("DD_APP_KEY", "app-key")
os.Setenv("DD_HOSTNAME", "host-name")
},
wantURL: "https://app.datadoghq.com/api/v1/metadata",
},
Expand All @@ -47,7 +46,6 @@ func Test_getURL(t *testing.T) {
os.Setenv("DD_SITE", "datad0g.com")
os.Setenv("DD_API_KEY", "api-key")
os.Setenv("DD_APP_KEY", "app-key")
os.Setenv("DD_HOSTNAME", "host-name")
},
wantURL: "https://app.datad0g.com/api/v1/metadata",
},
Expand All @@ -58,7 +56,6 @@ func Test_getURL(t *testing.T) {
os.Setenv("DD_URL", "https://app.datad0g.com")
os.Setenv("DD_API_KEY", "api-key")
os.Setenv("DD_APP_KEY", "app-key")
os.Setenv("DD_HOSTNAME", "host-name")
},
wantURL: "https://app.datad0g.com/api/v1/metadata",
},
Expand Down Expand Up @@ -116,7 +113,6 @@ func Test_setup(t *testing.T) {
os.Setenv("DD_API_KEY", fakeAPIKeyOperator)
os.Setenv("DD_APP_KEY", fakeAPPKeyDDA)
os.Setenv("DD_CLUSTER_NAME", fakeClusterNameOperator)
os.Setenv("DD_HOSTNAME", "host-name")
},
dda: &v2alpha1.DatadogAgent{},
wantClusterName: "fake_cluster_name_operator",
Expand All @@ -128,7 +124,6 @@ func Test_setup(t *testing.T) {
loadFunc: func() {
os.Clearenv()
os.Setenv("DD_CLUSTER_NAME", fakeClusterNameOperator)
os.Setenv("DD_HOSTNAME", "host-name")

},
dda: &v2alpha1.DatadogAgent{
Expand All @@ -149,7 +144,6 @@ func Test_setup(t *testing.T) {
name: "credentials and site set in DDA",
loadFunc: func() {
os.Clearenv()
os.Setenv("DD_HOSTNAME", "host-name")
},
dda: &v2alpha1.DatadogAgent{
Spec: v2alpha1.DatadogAgentSpec{
Expand Down Expand Up @@ -209,7 +203,6 @@ func Test_GetPayload(t *testing.T) {
expectedKubernetesVersion := "v1.28.0"
expectedOperatorVersion := "v1.19.0"
expectedClusterUID := "test-cluster-uid-12345"
expectedHostname := "test-host"

s := testutils_test.TestScheme()
kubeSystem := &corev1.Namespace{
Expand All @@ -227,9 +220,6 @@ func Test_GetPayload(t *testing.T) {
},
}

// Set hostname in SharedMetadata to simulate it being populated
omf.hostName = expectedHostname

payload := omf.GetPayload(expectedClusterUID)

// Verify payload is valid JSON
Expand All @@ -244,10 +234,6 @@ func Test_GetPayload(t *testing.T) {
}

// Validate top-level fields
if hostname, ok := parsed["hostname"].(string); !ok || hostname != expectedHostname {
t.Errorf("GetPayload() hostname = %v, want %v", hostname, expectedHostname)
}

if timestamp, ok := parsed["timestamp"].(float64); !ok || timestamp <= 0 {
t.Errorf("GetPayload() timestamp = %v, want positive number", timestamp)
}
Expand Down
14 changes: 0 additions & 14 deletions pkg/controller/utils/metadata/shared_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"fmt"
"net/http"
"net/url"
"os"
"time"

"github.com/go-logr/logr"
Expand All @@ -22,7 +21,6 @@ import (

"github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1"
"github.com/DataDog/datadog-operator/pkg/config"
"github.com/DataDog/datadog-operator/pkg/constants"
"github.com/DataDog/datadog-operator/pkg/version"
)

Expand All @@ -38,11 +36,6 @@ const (
defaultURLPath = "api/v1/metadata"
)

var (
// ErrEmptyHostName empty HostName error
ErrEmptyHostName = errors.New("empty host name")
)

// SharedMetadata contains the common metadata shared across all forwarders
type SharedMetadata struct {
k8sClient client.Reader
Expand All @@ -52,7 +45,6 @@ type SharedMetadata struct {
clusterUID string
operatorVersion string
kubernetesVersion string
hostName string
httpClient *http.Client

// Shared credential management
Expand All @@ -66,7 +58,6 @@ func NewSharedMetadata(logger logr.Logger, k8sClient client.Reader, kubernetesVe
logger: logger,
operatorVersion: operatorVersion,
kubernetesVersion: kubernetesVersion,
hostName: os.Getenv(constants.DDHostName),
httpClient: &http.Client{
Timeout: 10 * time.Second,
},
Expand All @@ -75,11 +66,6 @@ func NewSharedMetadata(logger logr.Logger, k8sClient client.Reader, kubernetesVe
}

func (sm *SharedMetadata) createRequest(payload []byte) (*http.Request, error) {
if sm.hostName == "" {
sm.logger.Error(ErrEmptyHostName, "Could not set host name; not starting metadata forwarder")
return nil, ErrEmptyHostName
}

apiKey, requestURL, err := sm.getApiKeyAndURL()
if err != nil {
sm.logger.V(1).Info("Could not get credentials", "error", err)
Expand Down
Loading