Skip to content

Commit 9aead01

Browse files
Merge pull request #75 from DataDog/fix/metrics-submit-require-api-key
fix(metrics): require DD_API_KEY for metrics submit command
2 parents b98cf33 + 87077f7 commit 9aead01

2 files changed

Lines changed: 97 additions & 39 deletions

File tree

cmd/metrics.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,9 +829,21 @@ func runMetricsMetadataUpdate(cmd *cobra.Command, args []string) error {
829829

830830
// runMetricsSubmit executes the metrics submit command
831831
func runMetricsSubmit(cmd *cobra.Command, args []string) error {
832-
client, err := getClient()
832+
// The metrics intake API requires an API key (DD_API_KEY).
833+
// OAuth2 bearer tokens are not supported for metric submission.
834+
if cfg.APIKey == "" {
835+
return fmt.Errorf(
836+
"metrics submit requires a Datadog API key.\n\n" +
837+
"Set the DD_API_KEY environment variable:\n" +
838+
" export DD_API_KEY=\"your-api-key\"\n\n" +
839+
"You can find your API key at https://app.datadoghq.com/organization-settings/api-keys\n\n" +
840+
"Note: OAuth2 authentication (pup auth login) is not supported for metric submission.",
841+
)
842+
}
843+
844+
client, err := apiKeyClientFactory(cfg)
833845
if err != nil {
834-
return err
846+
return fmt.Errorf("failed to create client: %w", err)
835847
}
836848

837849
// Parse timestamp

cmd/metrics_test.go

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func setupMetricsTestClient(t *testing.T) func() {
3939
origClient := ddClient
4040
origCfg := cfg
4141
origFactory := clientFactory
42+
origAPIKeyFactory := apiKeyClientFactory
4243

4344
cfg = &config.Config{
4445
Site: "datadoghq.com",
@@ -47,8 +48,12 @@ func setupMetricsTestClient(t *testing.T) func() {
4748
AutoApprove: false,
4849
}
4950

51+
mockErr := fmt.Errorf("mock client: no real API connection in tests")
5052
clientFactory = func(c *config.Config) (*client.Client, error) {
51-
return nil, fmt.Errorf("mock client: no real API connection in tests")
53+
return nil, mockErr
54+
}
55+
apiKeyClientFactory = func(c *config.Config) (*client.Client, error) {
56+
return nil, mockErr
5257
}
5358

5459
ddClient = nil
@@ -57,6 +62,7 @@ func setupMetricsTestClient(t *testing.T) func() {
5762
ddClient = origClient
5863
cfg = origCfg
5964
clientFactory = origFactory
65+
apiKeyClientFactory = origAPIKeyFactory
6066
}
6167
}
6268

@@ -578,40 +584,44 @@ func TestRunMetricsSubmit(t *testing.T) {
578584
defer cleanup()
579585

580586
tests := []struct {
581-
name string
582-
metricName string
583-
value float64
584-
timestamp string
585-
tags string
586-
metricType string
587-
wantErr bool
587+
name string
588+
metricName string
589+
value float64
590+
timestamp string
591+
tags string
592+
metricType string
593+
wantErr bool
594+
wantErrContains string
588595
}{
589596
{
590-
name: "submit gauge",
591-
metricName: "custom.metric",
592-
value: 123.45,
593-
timestamp: "now",
594-
tags: "env:prod,team:backend",
595-
metricType: "gauge",
596-
wantErr: true, // Mock client error
597+
name: "submit gauge",
598+
metricName: "custom.metric",
599+
value: 123.45,
600+
timestamp: "now",
601+
tags: "env:prod,team:backend",
602+
metricType: "gauge",
603+
wantErr: true, // Mock client error
604+
wantErrContains: "mock client",
597605
},
598606
{
599-
name: "submit count",
600-
metricName: "custom.count",
601-
value: 100,
602-
timestamp: "now",
603-
tags: "",
604-
metricType: "count",
605-
wantErr: true,
607+
name: "submit count",
608+
metricName: "custom.count",
609+
value: 100,
610+
timestamp: "now",
611+
tags: "",
612+
metricType: "count",
613+
wantErr: true,
614+
wantErrContains: "mock client",
606615
},
607616
{
608-
name: "invalid metric type",
609-
metricName: "custom.metric",
610-
value: 123,
611-
timestamp: "now",
612-
tags: "",
613-
metricType: "invalid",
614-
wantErr: true, // Will error on invalid type validation
617+
name: "invalid metric type",
618+
metricName: "custom.metric",
619+
value: 123,
620+
timestamp: "now",
621+
tags: "",
622+
metricType: "invalid",
623+
wantErr: true,
624+
wantErrContains: "mock client",
615625
},
616626
}
617627

@@ -634,19 +644,55 @@ func TestRunMetricsSubmit(t *testing.T) {
634644
t.Errorf("runMetricsSubmit() error = %v, wantErr %v", err, tt.wantErr)
635645
}
636646

637-
// Check for specific error on invalid type
638-
// Note: Invalid type validation happens after client creation,
639-
// but with mock client, client creation fails first
640-
if tt.metricType == "invalid" && err != nil {
641-
// Accept either "invalid metric type" or "mock client" error
642-
if !strings.Contains(err.Error(), "invalid metric type") && !strings.Contains(err.Error(), "mock client") {
643-
t.Errorf("runMetricsSubmit() error = %v, want 'invalid metric type' or 'mock client' error", err)
644-
}
647+
if tt.wantErrContains != "" && err != nil && !strings.Contains(err.Error(), tt.wantErrContains) {
648+
t.Errorf("runMetricsSubmit() error = %v, want error containing %q", err, tt.wantErrContains)
645649
}
646650
})
647651
}
648652
}
649653

654+
func TestRunMetricsSubmit_RequiresAPIKey(t *testing.T) {
655+
origClient := ddClient
656+
origCfg := cfg
657+
origFactory := apiKeyClientFactory
658+
defer func() {
659+
ddClient = origClient
660+
cfg = origCfg
661+
apiKeyClientFactory = origFactory
662+
}()
663+
664+
// Set config with NO API key to trigger the pre-emptive check
665+
cfg = &config.Config{
666+
Site: "datadoghq.com",
667+
APIKey: "",
668+
AppKey: "",
669+
}
670+
ddClient = nil
671+
672+
submitName = "custom.metric"
673+
submitValue = 42.0
674+
submitTimestamp = "now"
675+
submitTags = ""
676+
submitType = "gauge"
677+
submitInterval = 0
678+
679+
var buf bytes.Buffer
680+
outputWriter = &buf
681+
defer func() { outputWriter = os.Stdout }()
682+
683+
err := runMetricsSubmit(metricsSubmitCmd, []string{})
684+
if err == nil {
685+
t.Fatal("expected error when DD_API_KEY is not set")
686+
}
687+
688+
if !strings.Contains(err.Error(), "DD_API_KEY") {
689+
t.Errorf("error should mention DD_API_KEY, got: %v", err)
690+
}
691+
if !strings.Contains(err.Error(), "metrics submit requires") {
692+
t.Errorf("error should explain the requirement, got: %v", err)
693+
}
694+
}
695+
650696
func TestRunMetricsTagsList(t *testing.T) {
651697
cleanup := setupMetricsTestClient(t)
652698
defer cleanup()

0 commit comments

Comments
 (0)