Skip to content

Commit b2d7990

Browse files
committed
intercept calls to cache from build
1 parent aec1feb commit b2d7990

File tree

9 files changed

+222
-205
lines changed

9 files changed

+222
-205
lines changed

cmd/go-cache-plugin/commands.go

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
package main
55

66
import (
7+
"bytes"
78
"context"
9+
"encoding/json"
810
"errors"
911
"fmt"
1012
"github.com/grafana/go-cache-plugin/lib/otel"
@@ -25,22 +27,24 @@ import (
2527
)
2628

2729
var flags struct {
28-
CacheDir string `flag:"cache-dir,default=$GOCACHE_DIR,Local cache directory (required)"`
29-
LogFile string `flag:"log-file,default=trace.log,File used for logs"`
30-
S3Bucket string `flag:"bucket,default=$GOCACHE_S3_BUCKET,S3 bucket name (required if no --local flag provided)"`
31-
S3Region string `flag:"region,default=$GOCACHE_S3_REGION,S3 region"`
32-
S3Endpoint string `flag:"s3-endpoint-url,default=$GOCACHE_S3_ENDPOINT_URL,S3 custom endpoint URL (if unset, use AWS default)"`
33-
S3PathStyle bool `flag:"s3-path-style,default=$GOCACHE_S3_PATH_STYLE,S3 path-style URLs (optional)"`
34-
LocalCache bool `flag:"local,default=false,Runs cache in local mode (no S3)"`
35-
KeyPrefix string `flag:"prefix,default=$GOCACHE_KEY_PREFIX,S3 key prefix (optional)"`
36-
MinUploadSize int64 `flag:"min-upload-size,default=$GOCACHE_MIN_SIZE,Minimum object size to upload to S3 (in bytes)"`
37-
Concurrency int `flag:"c,default=$GOCACHE_CONCURRENCY,Maximum number of concurrent requests"`
38-
S3Concurrency int `flag:"u,default=$GOCACHE_S3_CONCURRENCY,Maximum concurrency for upload to S3"`
39-
PrintMetrics bool `flag:"metrics,default=$GOCACHE_METRICS,Print summary metrics to stderr at exit"`
40-
Expiration time.Duration `flag:"expiry,default=$GOCACHE_EXPIRY,Cache expiration period (optional)"`
41-
Verbose bool `flag:"v,default=$GOCACHE_VERBOSE,Enable verbose logging"`
42-
DebugLog int `flag:"debug,default=$GOCACHE_DEBUG,Enable detailed per-request debug logging (noisy)"`
43-
TracingParams string `flag:"tracing,default=runId:runAttempt:jobName:stepName,Tracing params"`
30+
CacheDir string `flag:"cache-dir,default=$GOCACHE_DIR,Local cache directory (required)"`
31+
LogFile string `flag:"log-file,default=trace.log,File used for logs"`
32+
S3Bucket string `flag:"bucket,default=$GOCACHE_S3_BUCKET,S3 bucket name (required if no --local flag provided)"`
33+
S3Region string `flag:"region,default=$GOCACHE_S3_REGION,S3 region"`
34+
S3Endpoint string `flag:"s3-endpoint-url,default=$GOCACHE_S3_ENDPOINT_URL,S3 custom endpoint URL (if unset, use AWS default)"`
35+
S3PathStyle bool `flag:"s3-path-style,default=$GOCACHE_S3_PATH_STYLE,S3 path-style URLs (optional)"`
36+
LocalCache bool `flag:"local-cache,default=false,Runs in no cache mode (no S3)"`
37+
KeyPrefix string `flag:"prefix,default=$GOCACHE_KEY_PREFIX,S3 key prefix (optional)"`
38+
MinUploadSize int64 `flag:"min-upload-size,default=$GOCACHE_MIN_SIZE,Minimum object size to upload to S3 (in bytes)"`
39+
Concurrency int `flag:"c,default=$GOCACHE_CONCURRENCY,Maximum number of concurrent requests"`
40+
S3Concurrency int `flag:"u,default=$GOCACHE_S3_CONCURRENCY,Maximum concurrency for upload to S3"`
41+
PrintMetrics bool `flag:"metrics,default=$GOCACHE_METRICS,Print summary metrics to stderr at exit"`
42+
Expiration time.Duration `flag:"expiry,default=$GOCACHE_EXPIRY,Cache expiration period (optional)"`
43+
Verbose bool `flag:"v,default=$GOCACHE_VERBOSE,Enable verbose logging"`
44+
DebugLog int `flag:"debug,default=$GOCACHE_DEBUG,Enable detailed per-request debug logging (noisy)"`
45+
TracingEnabled bool `flag:"tracing,default=false,Enable tracing (optional)"`
46+
TracingContext string `flag:"context,default=runId:runAttempt:jobName:stepName:stepNumber,Tracing params"`
47+
OtelCollectorAddress string `flag:"otel-collector,default,OTEL collector address (optional)"`
4448
}
4549

4650
const (
@@ -66,11 +70,12 @@ func runDirect(env *command.Env) error {
6670
}
6771

6872
var serveFlags struct {
69-
Plugin string `flag:"plugin,default=$GOCACHE_PLUGIN,Plugin service addr (or port) (required)"`
70-
HTTP string `flag:"http,default=$GOCACHE_HTTP,HTTP service address ([host]:port)"`
71-
ModProxy bool `flag:"modproxy,default=$GOCACHE_MODPROXY,Enable a Go module proxy (requires --http)"`
72-
RevProxy string `flag:"revproxy,default=$GOCACHE_REVPROXY,Reverse proxy these hosts (comma-separated; requires --http)"`
73-
SumDB string `flag:"sumdb,default=$GOCACHE_SUMDB,SumDB servers to proxy for (comma-separated)"`
73+
Plugin string `flag:"plugin,default=$GOCACHE_PLUGIN,Plugin service addr (or port) (required)"`
74+
HTTP string `flag:"http,default=$GOCACHE_HTTP,HTTP service address ([host]:port)"`
75+
ModProxy bool `flag:"modproxy,default=$GOCACHE_MODPROXY,Enable a Go module proxy (requires --http)"`
76+
ModNoCache bool `flag:"mod-nocache,default=false,Disable the local module cache (requires --modproxy)"`
77+
RevProxy string `flag:"revproxy,default=$GOCACHE_REVPROXY,Reverse proxy these hosts (comma-separated; requires --http)"`
78+
SumDB string `flag:"sumdb,default=$GOCACHE_SUMDB,SumDB servers to proxy for (comma-separated)"`
7479
}
7580

7681
var logger *log.Logger
@@ -83,7 +88,7 @@ func runServe(env *command.Env) error {
8388
return env.Usagef("you must provide a --plugin addr (or port)")
8489
}
8590

86-
otel.Init(context.Background(), otel.Config{Mode: otel.ModeStdout, LogFile: flags.LogFile})
91+
log.Printf("Otel exporter initialized with address: %s", flags.OtelCollectorAddress)
8792
// Initialize the cache server. Unlike a direct server, only close down and
8893
// wait for cache cleanup when the whole process exits.
8994
s, s3c, err := initCacheServer(env)
@@ -176,8 +181,11 @@ func runServe(env *command.Env) error {
176181

177182
// runConnect implements a direct cache proxy by connecting to a remote server.
178183
func runConnect(env *command.Env, plugin string) error {
179-
addr := plugin
180184

185+
ctx := env.Context()
186+
shutdownTracer, reportSpan, err := initTracing(ctx)
187+
188+
addr := plugin
181189
// If the caller has not specified a host/port, then likely this is an older usage which only specifies port
182190
if !strings.Contains(plugin, ":") {
183191
port, err := strconv.Atoi(plugin)
@@ -197,23 +205,61 @@ func runConnect(env *command.Env, plugin string) error {
197205

198206
out := taskgroup.Go(func() error {
199207
defer conn.(*net.TCPConn).CloseWrite() // let the server finish
200-
return copy(conn, os.Stdin)
208+
return copy(conn, os.Stdin, reportSpan)
201209
})
202-
if rerr := copy(os.Stdout, conn); rerr != nil {
210+
if rerr := copy(os.Stdout, conn, reportSpan); rerr != nil {
203211
vprintf("read responses: %v", err)
204212
}
205213
out.Wait()
206214
conn.Close()
207-
vprintf("connection closed (%v elapsed)", time.Since(start))
215+
216+
shutdownTracer(context.Background())
217+
println("@@@@@ - connection closed @@@@@")
218+
println(fmt.Sprintf("connection closed (%v elapsed)", time.Since(start)))
208219
return nil
209220
}
210221

222+
func initTracing(ctx context.Context) (func(context.Context) error, func([]byte), error) {
223+
if !flags.TracingEnabled {
224+
return func(context.Context) error { return nil }, func([]byte) {}, nil
225+
}
226+
227+
var shutdown func(context.Context) error
228+
var err error
229+
if flags.OtelCollectorAddress != "" {
230+
shutdown, err = otel.SetupOtelTraceProvider(ctx, flags.OtelCollectorAddress)
231+
} else if flags.LogFile != "" {
232+
log.Printf("Otel Collector address not specified, starting with the logging reporter, log file: %s", flags.LogFile)
233+
shutdown, err = otel.SetupLoggingProvider(ctx, flags.LogFile)
234+
} else {
235+
log.Printf("please specify either --otel-collector or --log-file to setup tracing or disable tracing")
236+
return nil, nil, errors.New("otel exporter not initialized")
237+
}
238+
if err != nil {
239+
return nil, nil, err
240+
}
241+
242+
tracingContext := otel.NewTracedFromString(flags.TracingContext)
243+
244+
spanner := otel.NewAwesomeSpanner(tracingContext)
245+
246+
spanReporter := func(buffer []byte) {
247+
id, err := parseId(buffer[:])
248+
if err != nil {
249+
log.Printf("failed to parse id from buffer: %v", err)
250+
}
251+
spanner.ProcessId(ctx, id)
252+
}
253+
254+
return shutdown, spanReporter, err
255+
}
256+
211257
// copy emulates the base case of io.Copy, but does not attempt to use the
212258
// io.ReaderFrom or io.WriterTo implementations.
213259
//
214260
// TODO(creachadair): For some reason io.Copy does not work correctly when r is
215261
// a pipe (e.g., stdin) and w is a TCP socket. Figure out why.
216-
func copy(w io.Writer, r io.Reader) error {
262+
func copy(w io.Writer, r io.Reader, reportTrace func([]byte)) error {
217263
var buf [4096]byte
218264
for {
219265
nr, err := r.Read(buf[:])
@@ -223,6 +269,8 @@ func copy(w io.Writer, r io.Reader) error {
223269
} else if nw < nr {
224270
return fmt.Errorf("wrote %d < %d bytes: %w", nw, nr, io.ErrShortWrite)
225271
}
272+
273+
reportTrace(buf[:])
226274
}
227275
if err == io.EOF {
228276
return nil
@@ -231,3 +279,21 @@ func copy(w io.Writer, r io.Reader) error {
231279
}
232280
}
233281
}
282+
283+
func parseId(buf []byte) (string, error) {
284+
map1 := make(map[string]any)
285+
286+
err := json.Unmarshal(buf, &map1)
287+
if err != nil {
288+
slice, _, found := bytes.Cut(buf, []byte{'\n'})
289+
if found {
290+
err2 := json.Unmarshal(slice, &map1)
291+
if err2 != nil {
292+
return "", err
293+
}
294+
}
295+
}
296+
297+
value := map1["ID"]
298+
return fmt.Sprintf("map1 = %v", value), nil
299+
}

cmd/go-cache-plugin/go-cache-plugin.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ When --http is enabled, the following options are available:
7272
7373
This mode bridges stdin/stdout to a cache server (see the "serve" command)
7474
listening on the specified port.`,
75-
7675
Run: command.Adapt(runConnect),
7776
},
7877
command.HelpCommand(helpTopics),

cmd/go-cache-plugin/setup.go

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ import (
1111
"errors"
1212
"expvar"
1313
"fmt"
14-
"github.com/grafana/go-cache-plugin/lib/otel"
15-
"go.opentelemetry.io/auto/sdk"
16-
"go.opentelemetry.io/otel/sdk/trace"
14+
"log"
1715
"net/http"
1816
"os"
1917
"path"
@@ -43,31 +41,24 @@ func initCacheServer(env *command.Env) (*gocache.Server, *s3util.Client, error)
4341
case flags.CacheDir == "":
4442
return nil, nil, env.Usagef("you must provide a --cache-dir")
4543
case flags.LocalCache:
46-
dir, err := cachedir.New(flags.CacheDir)
44+
dirCache, err := cachedir.New(flags.CacheDir)
4745
if err != nil {
4846
return nil, nil, fmt.Errorf("create local cache: %w", err)
4947
}
5048

51-
sdk.TracerProvider()
52-
trace.NewTracerProvider()
53-
cache := &gobuild.LocalCache{
54-
Local: dir,
55-
Tracer: otel.NewTracedFromString(flags.TracingParams),
56-
}
57-
58-
close := cache.Close
49+
cacheClose := func(context.Context) error { return nil }
5950
if flags.Expiration > 0 {
60-
dirClose := dir.Cleanup(flags.Expiration)
61-
close = func(ctx context.Context) error {
62-
return errors.Join(cache.Close(ctx), dirClose(ctx))
51+
dirClose := dirCache.Cleanup(flags.Expiration)
52+
cacheClose = func(ctx context.Context) error {
53+
return errors.Join(dirClose(ctx))
6354
}
6455
}
6556

6657
setMetrics := func(ctx context.Context, m *expvar.Map) {}
6758
s := &gocache.Server{
68-
Get: cache.Get,
69-
Put: cache.Put,
70-
Close: close,
59+
Get: dirCache.Get,
60+
Put: dirCache.Put,
61+
Close: cacheClose,
7162
SetMetrics: setMetrics,
7263
MaxRequests: flags.Concurrency,
7364
Logf: vprintf,
@@ -76,7 +67,7 @@ func initCacheServer(env *command.Env) (*gocache.Server, *s3util.Client, error)
7667
return s, nil, nil
7768

7869
case flags.S3Bucket == "" && !flags.LocalCache:
79-
return nil, nil, env.Usagef("you must provide an S3 --bucket name")
70+
return nil, nil, env.Usagef("you must provide an S3 --bucket name or run with --no-cache")
8071
}
8172
region, err := getBucketRegion(env.Context(), flags.S3Bucket)
8273
if err != nil {
@@ -148,14 +139,18 @@ func initModProxy(env *command.Env, s3c *s3util.Client) (_ http.Handler, cleanup
148139
return nil, nil, env.Usagef("you must set --http to enable --modproxy")
149140
}
150141

151-
modCachePath := filepath.Join(flags.CacheDir, "module")
152-
if err := os.MkdirAll(modCachePath, 0755); err != nil {
153-
return nil, nil, fmt.Errorf("create module cache: %w", err)
142+
if s3c == nil && !flags.LocalCache {
143+
return nil, nil, errors.New("s3 client not configured")
154144
}
155145

156146
var cacher goproxy.Cacher = nil
157-
var metrics func() *expvar.Map
147+
var metrics = func() *expvar.Map { return &expvar.Map{} }
158148
if s3c != nil {
149+
modCachePath := filepath.Join(flags.CacheDir, "module")
150+
if err := os.MkdirAll(modCachePath, 0755); err != nil {
151+
return nil, nil, fmt.Errorf("create module cache: %w", err)
152+
}
153+
159154
cacher := &modproxy.S3Cacher{
160155
Local: modCachePath,
161156
S3Client: s3c,
@@ -166,25 +161,16 @@ func initModProxy(env *command.Env, s3c *s3util.Client) (_ http.Handler, cleanup
166161
}
167162
cleanup = func() { vprintf("close cacher (err=%v)", cacher.Close()) }
168163
metrics = cacher.Metrics
169-
} else {
170-
//cache := modproxy.NewLocalModCacher(modCachePath, logger)
171-
//cache := modproxy.NewNoopModCacher(logger)
172-
//metrics = cache.Metrics
173-
//cacher = cache
174-
//metrics = func() { return &expvar.Map{} }
175-
metrics = func() *expvar.Map { return &expvar.Map{} }
176164
}
165+
177166
proxy := &goproxy.Goproxy{
178-
Fetcher: &modproxy.LoggingGoFetcher{
179-
Delegate: &goproxy.GoFetcher{
180-
// As configured, the fetcher should never shell out to the go
181-
// tool. Specifically, because we set GOPROXY and do not set any
182-
// bypass via GONOPROXY, GOPRIVATE, etc., we will only attempt to
183-
// proxy for the specific server(s) listed in Env.
184-
GoBin: "/bin/false",
185-
Env: []string{"GOPROXY=https://proxy.golang.org"},
186-
},
187-
Tracer: otel.NewTracedFromString(flags.TracingParams),
167+
Fetcher: &goproxy.GoFetcher{
168+
// As configured, the fetcher should never shell out to the go
169+
// tool. Specifically, because we set GOPROXY and do not set any
170+
// bypass via GONOPROXY, GOPRIVATE, etc., we will only attempt to
171+
// proxy for the specific server(s) listed in Env.
172+
GoBin: "/bin/false",
173+
Env: []string{"GOPROXY=https://proxy.golang.org"},
188174
},
189175
Cacher: cacher,
190176
ProxiedSumDBs: []string{"sum.golang.org"}, // default, see below
@@ -341,6 +327,7 @@ func makeHandler(modProxy, revProxy http.Handler) http.HandlerFunc {
341327
return
342328
}
343329
if modProxy != nil && r.Method == http.MethodGet && strings.HasPrefix(path, "/mod/") {
330+
log.Printf("proxying %s %s", r.Method, r.URL.Path)
344331
modProxy.ServeHTTP(w, r)
345332
return
346333
}

lib/gobuild/noop.go

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ package gobuild
22

33
import (
44
"github.com/creachadair/gocache"
5-
"github.com/creachadair/gocache/cachedir"
6-
"github.com/grafana/go-cache-plugin/lib/otel"
7-
"go.opentelemetry.io/otel/attribute"
85
"golang.org/x/net/context"
96
)
107

@@ -14,28 +11,17 @@ type TheCache interface {
1411
Close(ctx context.Context) error
1512
}
1613

17-
type LocalCache struct {
18-
Local *cachedir.Dir
19-
Tracer *otel.TraceMeta
14+
type NoopCache struct {
2015
}
2116

22-
func (l *LocalCache) Get(ctx context.Context, actionID string) (outputID, diskPath string, _ error) {
23-
_, span := l.Tracer.SpanWithContext(ctx, "BUILD-GET", attribute.KeyValue{Key: "action_id", Value: attribute.StringValue(actionID)})
24-
defer func() {
25-
span.End()
26-
}()
27-
28-
return l.Local.Get(ctx, actionID)
17+
func (l *NoopCache) Get(ctx context.Context, actionID string) (outputID, diskPath string, _ error) {
18+
return "", "", nil
2919
}
3020

31-
func (l *LocalCache) Put(ctx context.Context, obj gocache.Object) (diskPath string, _ error) {
32-
_, span := l.Tracer.SpanWithContext(ctx, "BUILD-PUT", attribute.KeyValue{Key: "action_id", Value: attribute.StringValue(obj.ActionID)})
33-
defer func() {
34-
span.End()
35-
}()
36-
return l.Local.Put(ctx, obj)
21+
func (l *NoopCache) Put(ctx context.Context, obj gocache.Object) (diskPath string, _ error) {
22+
return "", nil
3723
}
3824

39-
func (l *LocalCache) Close(ctx context.Context) error {
25+
func (l *NoopCache) Close(ctx context.Context) error {
4026
return nil
4127
}

lib/modproxy/noop.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ func (n *NoopCacher) Put(ctx context.Context, name string, content io.ReadSeeker
2525

2626
type LocalCache struct {
2727
Local goproxy.Cacher
28-
Tracer *otel.TraceMeta
28+
Tracer *otel.TracingContext
2929
}
3030

31-
func NewLocalModCacher(path string, tracer *otel.TraceMeta) *LocalCache {
31+
func NewLocalModCacher(path string, tracer *otel.TracingContext) *LocalCache {
3232
return &LocalCache{Local: goproxy.DirCacher(path), Tracer: tracer}
3333
}
3434

35-
func NewNoopModCacher(tracer *otel.TraceMeta) *LocalCache {
35+
func NewNoopModCacher(tracer *otel.TracingContext) *LocalCache {
3636
return &LocalCache{Local: &NoopCacher{}, Tracer: tracer}
3737
}
3838

@@ -59,7 +59,7 @@ func (l *LocalCache) Metrics() *expvar.Map {
5959

6060
type LoggingGoFetcher struct {
6161
Delegate goproxy.Fetcher
62-
Tracer *otel.TraceMeta
62+
Tracer *otel.TracingContext
6363
}
6464

6565
func (l *LoggingGoFetcher) Query(ctx context.Context, path, query string) (string, time.Time, error) {

0 commit comments

Comments
 (0)