Skip to content

Commit aa379e0

Browse files
committed
add a test
1 parent 0a39e93 commit aa379e0

File tree

7 files changed

+268
-63
lines changed

7 files changed

+268
-63
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ require (
1010
cdr.dev/slog v1.6.2-0.20230929193652-f0c466fabe10
1111
github.com/coder/coder/v2 v2.6.1-0.20240125021520-d9f5cdda9bae
1212
github.com/google/go-containerregistry v0.14.0
13+
github.com/google/uuid v1.5.0
1314
github.com/jfrog/jfrog-client-go v1.31.6
1415
github.com/spf13/cobra v1.8.0
1516
github.com/stretchr/testify v1.8.4
17+
go.uber.org/mock v0.4.0
1618
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
1719
k8s.io/api v0.29.1
1820
k8s.io/apimachinery v0.29.1
@@ -99,7 +101,6 @@ require (
99101
github.com/google/gofuzz v1.2.0 // indirect
100102
github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c // indirect
101103
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect
102-
github.com/google/uuid v1.5.0 // indirect
103104
github.com/gookit/color v1.5.4 // indirect
104105
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
105106
github.com/hashicorp/errwrap v1.1.0 // indirect

jfrog/client.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ import (
1515
"golang.org/x/xerrors"
1616
)
1717

18-
type Client struct {
18+
type Client interface {
19+
ScanResults(img Image) (ScanResult, error)
20+
}
21+
22+
type client struct {
1923
client *jfroghttpclient.JfrogHttpClient
2024
baseURL string
2125
token string
2226
user string
2327
}
2428

25-
func XRayClient(url, user, token string) (*Client, error) {
29+
func XRayClient(url, user, token string) (Client, error) {
2630
details := auth.NewXrayDetails()
2731
details.SetAccessToken(token)
2832
details.SetUser(user)
@@ -35,7 +39,7 @@ func XRayClient(url, user, token string) (*Client, error) {
3539
if err != nil {
3640
return nil, xerrors.Errorf("new xray manager: %w", err)
3741
}
38-
return &Client{
42+
return &client{
3943
client: mgr.Client(),
4044
baseURL: url,
4145
user: user,
@@ -60,7 +64,7 @@ type SecurityIssues struct {
6064
Total int `json:"total"`
6165
}
6266

63-
func (c *Client) ScanResults(img Image) (ScanResult, error) {
67+
func (c *client) ScanResults(img Image) (ScanResult, error) {
6468
path := fmt.Sprintf("%s/xray/api/v1/packages/%s/versions?search=%s", c.baseURL, img.Package, img.Version)
6569
resp, body, _, err := c.client.SendGet(path, true, &httputils.HttpClientDetails{
6670
User: c.user,

jfrog/doc.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// package jfrog contains an abstraction for interfacing with an JFrog
2+
// artifactory instance.
3+
package jfrog
4+
5+
//go:generate mockgen -destination ./mock.go -package jfrog github.com/coder/xray/jfrog Client

jfrog/mock.go

Lines changed: 54 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

reporter/codermock.go

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

reporter/reporter.go

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import (
44
"context"
55
"fmt"
66

7+
"golang.org/x/xerrors"
8+
9+
"github.com/google/uuid"
10+
711
"github.com/coder/coder/v2/codersdk"
812
"github.com/coder/xray/jfrog"
913

@@ -23,10 +27,11 @@ type K8sReporter struct {
2327
Namespace string
2428
CoderClient CoderClient
2529
Logger slog.Logger
26-
JFrogClient *jfrog.Client
30+
JFrogClient jfrog.Client
31+
ResultsChan chan codersdk.JFrogXrayScan
2732

2833
// Unexported fields are initialized on calls to Init.
29-
podInformer cache.SharedIndexInformer
34+
factory informers.SharedInformerFactory
3035
}
3136

3237
type WorkspaceAgent struct {
@@ -35,14 +40,14 @@ type WorkspaceAgent struct {
3540
}
3641

3742
func (k *K8sReporter) Init(ctx context.Context) error {
38-
podFactory := informers.NewSharedInformerFactoryWithOptions(k.Client, 0, informers.WithNamespace(k.Namespace), informers.WithTweakListOptions(func(lo *v1.ListOptions) {
43+
k.factory = informers.NewSharedInformerFactoryWithOptions(k.Client, 0, informers.WithNamespace(k.Namespace), informers.WithTweakListOptions(func(lo *v1.ListOptions) {
3944
lo.FieldSelector = k.FieldSelector
4045
lo.LabelSelector = k.LabelSelector
4146
}))
4247

43-
k.podInformer = podFactory.Core().V1().Pods().Informer()
48+
podInformer := k.factory.Core().V1().Pods().Informer()
4449

45-
_, err := k.podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
50+
_, err := podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
4651
AddFunc: func(obj interface{}) {
4752
pod, ok := obj.(*corev1.Pod)
4853
if !ok {
@@ -53,63 +58,72 @@ func (k *K8sReporter) Init(ctx context.Context) error {
5358
log := k.Logger.With(
5459
slog.F("pod_name", pod.Name),
5560
)
56-
var isWorkspace bool
5761
for _, container := range pod.Spec.Containers {
58-
var agentToken string
59-
for _, env := range container.Env {
60-
if env.Name != "CODER_AGENT_TOKEN" {
61-
continue
62-
}
63-
isWorkspace = true
64-
agentToken = env.Value
65-
break
66-
}
67-
if agentToken == "" {
68-
continue
69-
}
70-
7162
log = log.With(
7263
slog.F("container_name", container.Name),
7364
slog.F("container_image", container.Image),
7465
)
7566

76-
image, err := jfrog.ParseImage(container.Image)
77-
if err != nil {
78-
log.Error(ctx, "parse image", slog.Error(err))
79-
return
80-
}
67+
scan, err := func() (codersdk.JFrogXrayScan, error) {
68+
var agentToken string
69+
for _, env := range container.Env {
70+
if env.Name != "CODER_AGENT_TOKEN" {
71+
continue
72+
}
73+
agentToken = env.Value
74+
break
75+
}
76+
if agentToken == "" {
77+
return codersdk.JFrogXrayScan{}, nil
78+
}
8179

82-
scan, err := k.JFrogClient.ScanResults(image)
83-
if err != nil {
84-
log.Error(ctx, "fetch scan results", slog.Error(err))
85-
return
86-
}
80+
image, err := jfrog.ParseImage(container.Image)
81+
if err != nil {
82+
return codersdk.JFrogXrayScan{}, xerrors.Errorf("parse image: %w", err)
83+
}
8784

88-
manifest, err := k.CoderClient.AgentManifest(ctx, agentToken)
89-
if err != nil {
90-
log.Error(ctx, "Get agent manifest", slog.Error(err))
91-
return
92-
}
85+
scan, err := k.JFrogClient.ScanResults(image)
86+
if err != nil {
87+
return codersdk.JFrogXrayScan{}, xerrors.Errorf("fetch scan results: %w", err)
88+
}
9389

94-
log = log.With(
95-
slog.F("workspace_id", manifest.WorkspaceID),
96-
slog.F("agent_id", manifest.AgentID),
97-
slog.F("workspace_name", manifest.WorkspaceName),
98-
)
90+
manifest, err := k.CoderClient.AgentManifest(ctx, agentToken)
91+
if err != nil {
92+
return codersdk.JFrogXrayScan{}, xerrors.Errorf("agent manifest: %w", err)
93+
}
9994

100-
err = k.CoderClient.PostJFrogXrayScan(ctx, codersdk.JFrogXrayScan{
101-
WorkspaceID: manifest.WorkspaceID,
102-
AgentID: manifest.AgentID,
103-
Critical: scan.SecurityIssues.Critical,
104-
High: scan.SecurityIssues.High,
105-
})
95+
log = log.With(
96+
slog.F("workspace_id", manifest.WorkspaceID),
97+
slog.F("agent_id", manifest.AgentID),
98+
slog.F("workspace_name", manifest.WorkspaceName),
99+
)
100+
101+
req := codersdk.JFrogXrayScan{
102+
WorkspaceID: manifest.WorkspaceID,
103+
AgentID: manifest.AgentID,
104+
Critical: scan.SecurityIssues.Critical,
105+
High: scan.SecurityIssues.High,
106+
}
107+
err = k.CoderClient.PostJFrogXrayScan(ctx, req)
108+
if err != nil {
109+
return codersdk.JFrogXrayScan{}, xerrors.Errorf("post xray scan: %w", err)
110+
}
111+
112+
return req, nil
113+
}()
106114
if err != nil {
107-
log.Error(ctx, "post xray results", slog.Error(err))
108-
return
115+
log.Error(ctx, "scan agent", slog.Error(err))
116+
break
117+
}
118+
if scan.AgentID != uuid.Nil {
119+
log.Info(ctx, "uploaded agent results!", slog.F("pod_name", pod.Name), slog.F("namespace", pod.Namespace))
120+
if k.ResultsChan != nil {
121+
// This should only be populated during tests
122+
// so it's ok to assume an unbuffered channel is
123+
// going to block until read.
124+
k.ResultsChan <- scan
125+
}
109126
}
110-
}
111-
if isWorkspace {
112-
log.Info(ctx, "uploaded workspace results!", slog.F("pod_name", pod.Name), slog.F("namespace", pod.Namespace))
113127
}
114128
},
115129
})
@@ -120,5 +134,5 @@ func (k *K8sReporter) Init(ctx context.Context) error {
120134
}
121135

122136
func (k *K8sReporter) Start(stop chan struct{}) {
123-
k.podInformer.Run(stop)
137+
k.factory.Start(stop)
124138
}

0 commit comments

Comments
 (0)