Skip to content

Commit 8978877

Browse files
authored
feat: add support for buildkite metadata upload (#5)
* feat: add support for buildkite metadata upload * update url choosing logic
1 parent a44c508 commit 8978877

File tree

4 files changed

+238
-3
lines changed

4 files changed

+238
-3
lines changed

upload-file/input/input.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,10 @@ func Parse() (*Input, error) {
3737
}
3838
input.FilePath = filePath
3939

40-
securityAgentAPIEndpoint := os.Getenv("INPUT_SECURITY_AGENT_API_ENDPOINT")
41-
if securityAgentAPIEndpoint == "" {
40+
input.SecurityAgentAPIEndpoint = os.Getenv("INPUT_SECURITY_AGENT_API_ENDPOINT")
41+
if input.SecurityAgentAPIEndpoint == "" {
4242
input.SecurityAgentAPIEndpoint = "https://security-agent.ddn.pro.hasura.io/graphql"
4343
}
44-
input.SecurityAgentAPIEndpoint = securityAgentAPIEndpoint
4544

4645
securityAgentAPIKey := os.Getenv("INPUT_SECURITY_AGENT_API_KEY")
4746
if securityAgentAPIKey == "" {

upload-file/input/input_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package input
22

33
import (
4+
"os"
45
"reflect"
56
"testing"
67
)
@@ -87,3 +88,112 @@ func TestParseTags(t *testing.T) {
8788
})
8889
}
8990
}
91+
92+
func TestParseSecurityAgentAPIEndpoint(t *testing.T) {
93+
// Create a temporary test file for the tests
94+
testFile := "test-file.json"
95+
testContent := `{"test": "data"}`
96+
97+
// Clean up function
98+
cleanup := func() {
99+
os.Remove(testFile)
100+
}
101+
defer cleanup()
102+
103+
// Create test file
104+
if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
105+
t.Fatalf("Failed to create test file: %v", err)
106+
}
107+
108+
tests := []struct {
109+
name string
110+
envEndpoint string
111+
envAPIKey string
112+
envFilePath string
113+
expectedEndpoint string
114+
expectError bool
115+
}{
116+
{
117+
name: "uses custom endpoint from environment variable",
118+
envEndpoint: "https://custom-security-agent.example.com/graphql",
119+
envAPIKey: "test-api-key",
120+
envFilePath: testFile,
121+
expectedEndpoint: "https://custom-security-agent.example.com/graphql",
122+
expectError: false,
123+
},
124+
{
125+
name: "uses default endpoint when environment variable is empty",
126+
envEndpoint: "",
127+
envAPIKey: "test-api-key",
128+
envFilePath: testFile,
129+
expectedEndpoint: "https://security-agent.ddn.pro.hasura.io/graphql",
130+
expectError: false,
131+
},
132+
{
133+
name: "uses default endpoint when environment variable is not set",
134+
envEndpoint: "", // Will be unset in test
135+
envAPIKey: "test-api-key",
136+
envFilePath: testFile,
137+
expectedEndpoint: "https://security-agent.ddn.pro.hasura.io/graphql",
138+
expectError: false,
139+
},
140+
}
141+
142+
for _, tt := range tests {
143+
t.Run(tt.name, func(t *testing.T) {
144+
// Save original environment variables
145+
originalEndpoint := os.Getenv("INPUT_SECURITY_AGENT_API_ENDPOINT")
146+
originalAPIKey := os.Getenv("INPUT_SECURITY_AGENT_API_KEY")
147+
originalFilePath := os.Getenv("INPUT_FILE_PATH")
148+
149+
// Clean up environment variables after test
150+
defer func() {
151+
if originalEndpoint != "" {
152+
os.Setenv("INPUT_SECURITY_AGENT_API_ENDPOINT", originalEndpoint)
153+
} else {
154+
os.Unsetenv("INPUT_SECURITY_AGENT_API_ENDPOINT")
155+
}
156+
if originalAPIKey != "" {
157+
os.Setenv("INPUT_SECURITY_AGENT_API_KEY", originalAPIKey)
158+
} else {
159+
os.Unsetenv("INPUT_SECURITY_AGENT_API_KEY")
160+
}
161+
if originalFilePath != "" {
162+
os.Setenv("INPUT_FILE_PATH", originalFilePath)
163+
} else {
164+
os.Unsetenv("INPUT_FILE_PATH")
165+
}
166+
}()
167+
168+
// Set test environment variables
169+
if tt.envEndpoint != "" {
170+
os.Setenv("INPUT_SECURITY_AGENT_API_ENDPOINT", tt.envEndpoint)
171+
} else {
172+
os.Unsetenv("INPUT_SECURITY_AGENT_API_ENDPOINT")
173+
}
174+
os.Setenv("INPUT_SECURITY_AGENT_API_KEY", tt.envAPIKey)
175+
os.Setenv("INPUT_FILE_PATH", tt.envFilePath)
176+
177+
// Call Parse function
178+
result, err := Parse()
179+
180+
// Check error expectation
181+
if tt.expectError && err == nil {
182+
t.Errorf("Expected error but got none")
183+
return
184+
}
185+
if !tt.expectError && err != nil {
186+
t.Errorf("Unexpected error: %v", err)
187+
return
188+
}
189+
190+
// Check endpoint value if no error expected
191+
if !tt.expectError {
192+
if result.SecurityAgentAPIEndpoint != tt.expectedEndpoint {
193+
t.Errorf("SecurityAgentAPIEndpoint = %q, want %q",
194+
result.SecurityAgentAPIEndpoint, tt.expectedEndpoint)
195+
}
196+
}
197+
})
198+
}
199+
}

upload-file/upload/buildkite.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package upload
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"log"
9+
"os"
10+
11+
"github.com/hasura/security-agent-tools/upload-file/input"
12+
)
13+
14+
var (
15+
ErrNotInBuildkite = errors.New("not in Buildkite")
16+
17+
buildkiteEnvVars = []string{
18+
// Build Information
19+
"BUILDKITE_BUILD_ID",
20+
"BUILDKITE_BUILD_NUMBER",
21+
"BUILDKITE_BUILD_URL",
22+
"BUILDKITE_BUILD_CREATOR",
23+
"BUILDKITE_MESSAGE",
24+
"BUILDKITE_PULL_REQUEST",
25+
"BUILDKITE_PULL_REQUEST_BASE_BRANCH",
26+
"BUILDKITE_REBUILT_FROM_BUILD_ID",
27+
28+
// Pipeline and Agent Information
29+
"BUILDKITE_PIPELINE_ID",
30+
"BUILDKITE_PIPELINE_SLUG",
31+
"BUILDKITE_PIPELINE_NAME",
32+
"BUILDKITE_ORGANIZATION_SLUG",
33+
"BUILDKITE_AGENT_ID",
34+
"BUILDKITE_AGENT_NAME",
35+
36+
// Job Information
37+
"BUILDKITE_JOB_ID",
38+
"BUILDKITE_COMMAND",
39+
"BUILDKITE_COMMAND_EXIT_STATUS",
40+
"BUILDKITE_JOB_URL",
41+
"BUILDKITE_STEP_KEY",
42+
43+
// Git and Repository Information
44+
"BUILDKITE_REPO",
45+
"BUILDKITE_COMMIT",
46+
"BUILDKITE_BRANCH",
47+
"BUILDKITE_TAG",
48+
"BUILDKITE_CLEAN_CHECKOUT",
49+
50+
// Other Variables
51+
"BUILDKITE_BUILD_PATH",
52+
"BUILDKITE_ARTIFACT_UPLOAD_DESTINATION",
53+
"BUILDKITE_PLUGINS_PATH",
54+
}
55+
)
56+
57+
func uploadBuildkiteMetadata(ctx context.Context, c *Client, in *input.Input) error {
58+
if os.Getenv("BUILDKITE") != "true" {
59+
return nil
60+
}
61+
62+
type Metadata struct {
63+
ScanReportPath string `json:"scan_report_path"`
64+
Env map[string]string `json:"env"`
65+
Tags map[string]string `json:"tags"`
66+
}
67+
68+
metadata := Metadata{
69+
ScanReportPath: in.Destination,
70+
Env: make(map[string]string),
71+
Tags: in.Tags,
72+
}
73+
74+
for _, envVar := range buildkiteEnvVars {
75+
metadata.Env[envVar] = os.Getenv(envVar)
76+
}
77+
78+
metadataJSON, err := json.Marshal(metadata)
79+
if err != nil {
80+
return fmt.Errorf("failed to marshal metadata: %v", err)
81+
}
82+
metadataFile, err := os.CreateTemp("", "buildkite-metadata.json")
83+
if err != nil {
84+
return fmt.Errorf("failed to create temp metadata file: %v", err)
85+
}
86+
defer os.Remove(metadataFile.Name())
87+
_, err = metadataFile.Write(metadataJSON)
88+
if err != nil {
89+
return fmt.Errorf("failed to write metadata to temp file: %v", err)
90+
}
91+
92+
buildkiteBranch := os.Getenv("BUILDKITE_BRANCH")
93+
buildkiteTag := os.Getenv("BUILDKITE_TAG")
94+
buildkiteCommit := os.Getenv("BUILDKITE_COMMIT")
95+
buildkitePullRequest := os.Getenv("BUILDKITE_PULL_REQUEST")
96+
uploadPath := ""
97+
switch {
98+
case buildkiteBranch != "":
99+
uploadPath = fmt.Sprintf("branches/%s/%s.json", buildkiteBranch, buildkiteCommit)
100+
case buildkiteTag != "":
101+
uploadPath = fmt.Sprintf("tags/%s/%s.json", buildkiteTag, buildkiteCommit)
102+
case buildkitePullRequest != "false":
103+
uploadPath = fmt.Sprintf("pull-requests/%s/%s.json", buildkitePullRequest, buildkiteCommit)
104+
default:
105+
return errors.New("failed to determine upload path. Please set at least one of BUILDKITE_BRANCH, BUILDKITE_TAG, BUILDKITE_PULL_REQUEST env vars")
106+
}
107+
serviceName := in.Tags["service"]
108+
buildkitePipelineSlug := os.Getenv("BUILDKITE_PIPELINE_SLUG")
109+
uploadPath = servicePath(serviceName, fmt.Sprintf("buildkite/%s/%s", buildkitePipelineSlug, uploadPath))
110+
111+
log.Println("Uploading Buildkite metadata")
112+
err = c.UploadFile(ctx, metadataFile.Name(), uploadPath)
113+
if err != nil {
114+
return fmt.Errorf("failed to upload metadata: %v", err)
115+
}
116+
log.Println("Buildkite metadata upload completed successfully")
117+
118+
return nil
119+
}

upload-file/upload/service.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ func ServiceMetadata(ctx context.Context, c *Client, in *input.Input) error {
7777
return err
7878
}
7979

80+
switch err := uploadBuildkiteMetadata(context.Background(), c, in); err {
81+
case ErrNotInBuildkite:
82+
log.Println("Skipping Buildkite metadata upload, as we are not in Buildkite")
83+
default:
84+
return err
85+
}
86+
8087
return nil
8188
}
8289

0 commit comments

Comments
 (0)