@@ -4,6 +4,10 @@ import (
4
4
"context"
5
5
"fmt"
6
6
7
+ "golang.org/x/xerrors"
8
+
9
+ "github.com/google/uuid"
10
+
7
11
"github.com/coder/coder/v2/codersdk"
8
12
"github.com/coder/xray/jfrog"
9
13
@@ -23,10 +27,11 @@ type K8sReporter struct {
23
27
Namespace string
24
28
CoderClient CoderClient
25
29
Logger slog.Logger
26
- JFrogClient * jfrog.Client
30
+ JFrogClient jfrog.Client
31
+ ResultsChan chan codersdk.JFrogXrayScan
27
32
28
33
// Unexported fields are initialized on calls to Init.
29
- podInformer cache. SharedIndexInformer
34
+ factory informers. SharedInformerFactory
30
35
}
31
36
32
37
type WorkspaceAgent struct {
@@ -35,14 +40,14 @@ type WorkspaceAgent struct {
35
40
}
36
41
37
42
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 ) {
39
44
lo .FieldSelector = k .FieldSelector
40
45
lo .LabelSelector = k .LabelSelector
41
46
}))
42
47
43
- k . podInformer = podFactory .Core ().V1 ().Pods ().Informer ()
48
+ podInformer := k . factory .Core ().V1 ().Pods ().Informer ()
44
49
45
- _ ,err := k . podInformer .AddEventHandler (cache.ResourceEventHandlerFuncs {
50
+ _ ,err := podInformer .AddEventHandler (cache.ResourceEventHandlerFuncs {
46
51
AddFunc :func (obj interface {}) {
47
52
pod ,ok := obj .(* corev1.Pod )
48
53
if ! ok {
@@ -53,63 +58,72 @@ func (k *K8sReporter) Init(ctx context.Context) error {
53
58
log := k .Logger .With (
54
59
slog .F ("pod_name" ,pod .Name ),
55
60
)
56
- var isWorkspace bool
57
61
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
-
71
62
log = log .With (
72
63
slog .F ("container_name" ,container .Name ),
73
64
slog .F ("container_image" ,container .Image ),
74
65
)
75
66
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
+ }
81
79
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
+ }
87
84
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
+ }
93
89
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
+ }
99
94
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
+ }()
106
114
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
+ }
109
126
}
110
- }
111
- if isWorkspace {
112
- log .Info (ctx ,"uploaded workspace results!" ,slog .F ("pod_name" ,pod .Name ),slog .F ("namespace" ,pod .Namespace ))
113
127
}
114
128
},
115
129
})
@@ -120,5 +134,5 @@ func (k *K8sReporter) Init(ctx context.Context) error {
120
134
}
121
135
122
136
func (k * K8sReporter )Start (stop chan struct {}) {
123
- k .podInformer . Run (stop )
137
+ k .factory . Start (stop )
124
138
}