Skip to content

Commit 7f45d5a

Browse files
committed
Refactor accept file management to its own package
1 parent 22991ac commit 7f45d5a

22 files changed

+892
-991
lines changed

.vscode/launch.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@
7878
"program": "${workspaceFolder}/agent/main.go",
7979
"args": [
8080
"relay",
81-
"-i",
82-
"github",
81+
"-f",
82+
"/Users/shawn/dev/cortex/axon/agent/test/accept.test.json",
8383
"--alias",
8484
"relay2",
8585
"-v"
@@ -91,7 +91,8 @@
9191
"PORT" : "7399",
9292
"GITHUB_API": "api.github.com",
9393
"GITHUB_GRAPHQL": "api.github.com/graphql",
94-
"ENABLE_RELAY_REFLECTOR": "true"
94+
"ENABLE_RELAY_REFLECTOR": "all",
95+
"BROKER_TOKEN": "abc123"
9596
}
9697
},
9798

agent/common/integration.go

Lines changed: 11 additions & 299 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
package common
22

33
import (
4-
"crypto/sha256"
54
_ "embed"
6-
"encoding/hex"
75
"encoding/json"
86
"fmt"
9-
"net/url"
107
"os"
118
"path"
12-
"regexp"
13-
"sort"
149
"strings"
15-
"time"
10+
11+
"github.com/cortexapps/axon/server/snykbroker/acceptfile"
1612
)
1713

1814
type Integration string
@@ -60,7 +56,7 @@ func ParseIntegration(s string) (Integration, error) {
6056
}
6157

6258
func ValidIntegrations() []Integration {
63-
return []Integration{IntegrationGithub, IntegrationJira, IntegrationGitlab, IntegrationBitbucket, IntegrationSonarqube, IntegrationPrometheus}
59+
return []Integration{IntegrationCustom, IntegrationGithub, IntegrationJira, IntegrationGitlab, IntegrationBitbucket, IntegrationSonarqube, IntegrationPrometheus}
6460
}
6561

6662
type IntegrationInfo struct {
@@ -70,284 +66,26 @@ type IntegrationInfo struct {
7066
AcceptFilePath string
7167
}
7268

73-
func (ii IntegrationInfo) AcceptFile(localhostBase string) (string, error) {
74-
75-
// load/locate the accept file then fix it up to have
76-
// an entry to talk to the axon HTTP server itself
77-
// which we use for status, etc
78-
alias := ii.Alias
79-
if len(alias) == 0 {
80-
alias = "default"
81-
}
82-
83-
content, err := ii.getAcceptFileContents()
84-
if err != nil {
85-
return "", err
86-
}
87-
88-
rawFile := []byte(content)
89-
h := sha256.New()
90-
h.Write(rawFile)
91-
expectedPath := path.Join(
92-
os.TempDir(),
93-
"accept-files",
94-
ii.Integration.String(),
95-
alias,
96-
hex.EncodeToString(h.Sum(nil)),
97-
"accept.json",
98-
)
99-
100-
dict := map[string]interface{}{}
101-
err = json.Unmarshal(rawFile, &dict)
102-
if err != nil {
103-
return "", err
104-
}
105-
106-
// we add a section like this to allow the server side
107-
// to hit the axon agent via an HTTP bridge
108-
// "private": [
109-
// {
110-
// "method": "any",
111-
// "path": "/__axon/*",
112-
// "origin": "http://localhost"
113-
// }
114-
// ]
115-
116-
entries, ok := dict["private"].([]interface{})
117-
if !ok {
118-
entries = []interface{}{}
119-
dict["private"] = entries
120-
}
121-
122-
entry := map[string]string{
123-
"method": "any",
124-
"path": "/__axon/*",
125-
"origin": localhostBase,
126-
}
127-
dict["private"] = append([]interface{}{entry}, entries...)
128-
129-
if _, ok := dict["public"]; !ok {
130-
dict["public"] = []interface{}{}
131-
}
132-
133-
json, err := json.Marshal(dict)
134-
if err != nil {
135-
return "", err
136-
}
137-
err = os.MkdirAll(path.Dir(expectedPath), os.ModeDir|os.ModePerm)
138-
if err != nil {
139-
return "", err
140-
}
141-
err = os.WriteFile(expectedPath, json, os.ModePerm)
142-
143-
return expectedPath, err
144-
}
145-
146-
type ValueResolver func() string
69+
func (ii IntegrationInfo) ToAcceptFile() (*acceptfile.AcceptFile, error) {
14770

148-
func StringValueResolver(value string) ValueResolver {
149-
return func() string {
150-
return value
151-
}
152-
}
153-
154-
type ResolverMap map[string]ValueResolver
155-
156-
func NewResolverMapFromMap(m map[string]string) ResolverMap {
157-
rm := make(ResolverMap, len(m))
158-
for key, value := range m {
159-
rm[key] = StringValueResolver(value)
160-
}
161-
return rm
162-
}
163-
164-
func (rm ResolverMap) ToStringMap() map[string]string {
165-
resolved := make(map[string]string, len(rm))
166-
for key, resolver := range rm {
167-
resolved[key] = resolver()
168-
}
169-
return resolved
170-
}
171-
172-
func (rm ResolverMap) Resolve(key string) string {
173-
resolver, ok := rm[key]
174-
if !ok {
175-
return ""
176-
}
177-
return resolver()
178-
}
179-
180-
func EnvValueResolver(envVar string, defaultValue string, capture bool) ValueResolver {
181-
182-
captured := os.ExpandEnv(envVar)
183-
return func() string {
184-
if capture {
185-
return captured
186-
}
187-
if val := os.ExpandEnv(envVar); val != "" {
188-
return val
189-
}
190-
191-
return defaultValue
192-
}
193-
}
194-
195-
func (ii IntegrationInfo) RewriteOrigins(acceptFilePath string, writer func(string, ResolverMap) string) (*AcceptFileInfo, error) {
196-
197-
info, err := newAcceptFileInfo(acceptFilePath, writer)
198-
if err != nil {
71+
if err := ii.Integration.Validate(); err != nil {
19972
return nil, err
20073
}
201-
_, err = info.Rewrite()
202-
if err != nil {
203-
return nil, err
204-
}
205-
return info, nil
206-
}
20774

208-
type AcceptFileInfo struct {
209-
OriginalPath string
210-
RewrittenPath string
211-
Content string
212-
rawContent []byte
213-
Routes []AcceptFileRoute
214-
originRewriter func(uri string, headers ResolverMap) string
215-
}
216-
217-
var IgnoreHosts = []string{
218-
"localhost",
219-
"127.0.0.1",
220-
}
221-
222-
func newAcceptFileInfo(acceptFilePath string, originRewriter func(string, ResolverMap) string) (*AcceptFileInfo, error) {
223-
stat, err := os.Stat(acceptFilePath)
224-
if err != nil {
75+
if _, err := ii.ValidateSubtype(); err != nil {
22576
return nil, err
22677
}
22778

228-
if stat.IsDir() {
229-
return nil, fmt.Errorf("accept file path %q is a directory, expected a file", acceptFilePath)
230-
}
231-
232-
info := &AcceptFileInfo{
233-
OriginalPath: acceptFilePath,
234-
originRewriter: originRewriter,
235-
}
236-
237-
info.rawContent, err = os.ReadFile(acceptFilePath)
79+
content, err := ii.getAcceptFileContents()
23880
if err != nil {
23981
return nil, err
24082
}
83+
file := acceptfile.NewAcceptFile([]byte(content))
24184

242-
return info, nil
243-
}
244-
245-
func (afi *AcceptFileInfo) isIgnoredHost(host string) bool {
246-
for _, ignoreHost := range IgnoreHosts {
247-
if strings.HasPrefix(host, ignoreHost) {
248-
return true
249-
}
250-
}
251-
return false
252-
}
253-
254-
func (afi *AcceptFileInfo) Rewrite() (string, error) {
255-
256-
if afi.Content == "" {
257-
dict := map[string]interface{}{}
258-
err := json.Unmarshal(afi.rawContent, &dict)
259-
if err != nil {
260-
return "", err
261-
}
262-
263-
entries, ok := dict["private"].([]interface{})
264-
if !ok {
265-
return "", nil
266-
}
267-
268-
for _, entry := range entries {
269-
values := entry.(map[string]any)
270-
rawOrigin, ok := values["origin"].(string)
271-
if !ok {
272-
continue
273-
}
274-
275-
origin := os.ExpandEnv(rawOrigin)
276-
277-
parsed, err := url.Parse(origin)
278-
if err != nil {
279-
return "", fmt.Errorf("failed to parse origin %q: %w", origin, err)
280-
}
281-
282-
if afi.isIgnoredHost(parsed.Host) {
283-
continue
284-
}
285-
286-
if parsed.Scheme == "" {
287-
parsed.Scheme = "https"
288-
origin = parsed.String()
289-
}
290-
291-
// Extract headers if present
292-
var headers ResolverMap
293-
if headersInterface, hasHeaders := values["headers"]; hasHeaders {
294-
if headersMap, ok := headersInterface.(map[string]interface{}); ok {
295-
headers = make(ResolverMap, len(headersMap))
296-
for k, v := range headersMap {
297-
if strVal, ok := v.(string); ok {
298-
// Resolve environment variables in header values
299-
headers[k] = EnvValueResolver(strVal, "", true)
300-
}
301-
}
302-
}
303-
}
304-
305-
// rewrite the origin to use the writer function
306-
newOrigin := afi.originRewriter(origin, headers)
307-
if newOrigin != "" {
308-
values["origin"] = newOrigin
309-
}
310-
afi.Routes = append(afi.Routes, AcceptFileRoute{
311-
ResolvedOrigin: origin,
312-
ProxyOrigin: newOrigin,
313-
Headers: headers,
314-
})
315-
}
316-
317-
json, err := json.Marshal(dict)
318-
if err != nil {
319-
return "", err
320-
}
321-
afi.Content = string(json)
322-
323-
stat, err := os.Stat(afi.OriginalPath)
324-
325-
if err != nil {
326-
return "", err
327-
}
328-
newFilePath := path.Join(
329-
os.TempDir(),
330-
"accept-files-written",
331-
fmt.Sprintf("rewrite.%v.%v", time.Now().UnixMilli(), stat.Name()),
332-
)
333-
err = os.MkdirAll(path.Dir(newFilePath), os.ModeDir|os.ModePerm)
334-
if err != nil {
335-
return "", err
336-
}
337-
338-
err = os.WriteFile(newFilePath, json, os.ModePerm)
339-
if err != nil {
340-
return "", err
341-
}
342-
afi.RewrittenPath = newFilePath
85+
if err := file.Validate(); err != nil {
86+
return nil, err
34387
}
344-
return afi.Content, nil
345-
}
346-
347-
type AcceptFileRoute struct {
348-
ResolvedOrigin string
349-
ProxyOrigin string
350-
Headers ResolverMap
88+
return file, nil
35189
}
35290

35391
func (ii IntegrationInfo) getAcceptFileContents() (string, error) {
@@ -478,31 +216,5 @@ func (ii IntegrationInfo) getIntegrationAcceptFile() (string, error) {
478216
if err != nil {
479217
return "", err
480218
}
481-
if err := ii.ensureAcceptFileVars(strContent); err != nil {
482-
return "", err
483-
}
484219
return strContent, nil
485220
}
486-
487-
var reContentVars = regexp.MustCompile(`\$\{(.*?)\}`)
488-
489-
func (ii IntegrationInfo) ensureAcceptFileVars(content string) error {
490-
varMatch := reContentVars.FindAllStringSubmatch(content, -1)
491-
492-
envVars := []string{}
493-
494-
// sort these so they have a stable order
495-
496-
for _, match := range varMatch {
497-
envVars = append(envVars, match[1])
498-
}
499-
500-
sort.Strings(envVars)
501-
502-
for _, envVar := range envVars {
503-
if os.Getenv(envVar) == "" && os.Getenv(envVar+"_POOL") == "" {
504-
return fmt.Errorf("missing required environment variable %q for integration %s", envVar, ii.Integration.String())
505-
}
506-
}
507-
return nil
508-
}

0 commit comments

Comments
 (0)