github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/pkg/docker/tracing/traced.go (about) 1 package tracing 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "time" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/container" 11 "github.com/docker/docker/api/types/network" 12 "github.com/docker/docker/client" 13 "github.com/filecoin-project/bacalhau/pkg/system" 14 "github.com/filecoin-project/bacalhau/pkg/telemetry" 15 v1 "github.com/opencontainers/image-spec/specs-go/v1" 16 semconv "go.opentelemetry.io/otel/semconv/v1.17.0" 17 "go.opentelemetry.io/otel/trace" 18 ) 19 20 func NewTracedClient() (TracedClient, error) { 21 c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) 22 if err != nil { 23 return TracedClient{}, err 24 } 25 26 return TracedClient{ 27 client: c, 28 hostname: c.DaemonHost(), 29 }, nil 30 } 31 32 // TracedClient is a client for Docker that also traces requests being made to it. The names of traces are inspired by 33 // the docker CLI, so ContainerRemove becomes `docker.container.rm` which would be `docker container rm` on the 34 // command line. 35 type TracedClient struct { 36 client *client.Client 37 hostname string 38 } 39 40 func (c TracedClient) ContainerCreate( 41 ctx context.Context, 42 config *container.Config, 43 hostConfig *container.HostConfig, 44 networkingConfig *network.NetworkingConfig, 45 platform *v1.Platform, 46 name string, 47 ) (container.ContainerCreateCreatedBody, error) { 48 ctx, span := c.span(ctx, "container.create") 49 defer span.End() 50 51 return telemetry.RecordErrorOnSpanTwo[container.ContainerCreateCreatedBody](span)( 52 c.client.ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, name), 53 ) 54 } 55 56 func (c TracedClient) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) { 57 ctx, span := c.span(ctx, "container.inspect") 58 defer span.End() 59 60 return telemetry.RecordErrorOnSpanTwo[types.ContainerJSON](span)(c.client.ContainerInspect(ctx, containerID)) 61 } 62 63 func (c TracedClient) ContainerList(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) { 64 ctx, span := c.span(ctx, "container.list") 65 defer span.End() 66 67 return telemetry.RecordErrorOnSpanTwo[[]types.Container](span)(c.client.ContainerList(ctx, options)) 68 } 69 70 func (c TracedClient) ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions) (io.ReadCloser, error) { 71 ctx, span := c.span(ctx, "container.logs") 72 // span ends when the io.ReadCloser is closed 73 74 return telemetry.RecordErrorOnSpanReadCloserAndClose(span)(c.client.ContainerLogs(ctx, container, options)) 75 } 76 77 func (c TracedClient) ContainerRemove(ctx context.Context, containerID string, options types.ContainerRemoveOptions) error { 78 ctx, span := c.span(ctx, "container.rm") 79 defer span.End() 80 81 return telemetry.RecordErrorOnSpan(span)(c.client.ContainerRemove(ctx, containerID, options)) 82 } 83 84 func (c TracedClient) ContainerStart(ctx context.Context, id string, options types.ContainerStartOptions) error { 85 ctx, span := c.span(ctx, "container.start") 86 defer span.End() 87 88 return telemetry.RecordErrorOnSpan(span)(c.client.ContainerStart(ctx, id, options)) 89 } 90 91 func (c TracedClient) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error { 92 ctx, span := c.span(ctx, "container.stop") 93 defer span.End() 94 95 return telemetry.RecordErrorOnSpan(span)(c.client.ContainerStop(ctx, containerID, timeout)) 96 } 97 98 func (c TracedClient) ContainerWait( 99 ctx context.Context, 100 containerID string, 101 condition container.WaitCondition, 102 ) (<-chan container.ContainerWaitOKBody, <-chan error) { 103 ctx, span := c.span(ctx, "container.wait") 104 // span ends when one of the channels sends a message 105 106 return telemetry.RecordErrorOnSpanTwoChannels[container.ContainerWaitOKBody](span)(c.client.ContainerWait(ctx, containerID, condition)) 107 } 108 109 func (c TracedClient) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) { 110 ctx, span := c.span(ctx, "container.cp") 111 // span ends when the io.ReadCloser is closed 112 113 return telemetry.RecordErrorOnSpanReadCloserTwoAndClose[types.ContainerPathStat](span)( 114 c.client.CopyFromContainer(ctx, containerID, srcPath), 115 ) 116 } 117 118 func (c TracedClient) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) { 119 ctx, span := c.span(ctx, "image.inspect") 120 defer span.End() 121 122 return telemetry.RecordErrorOnSpanThree[types.ImageInspect, []byte](span)(c.client.ImageInspectWithRaw(ctx, imageID)) 123 } 124 125 func (c TracedClient) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) { 126 ctx, span := c.span(ctx, "image.pull") 127 // span ends when the io.ReadCloser is closed 128 129 // The span won't be annotated with _all_ possible failures as the error returned only covers immediate errors. 130 // Errors occurring while pulling the image are returned within the io.ReadCloser and currently not captured. 131 return telemetry.RecordErrorOnSpanReadCloserAndClose(span)(c.client.ImagePull(ctx, refStr, options)) 132 } 133 134 func (c TracedClient) NetworkConnect(ctx context.Context, networkID, containerID string, config *network.EndpointSettings) error { 135 ctx, span := c.span(ctx, "network.connect") 136 defer span.End() 137 138 return telemetry.RecordErrorOnSpan(span)(c.client.NetworkConnect(ctx, networkID, containerID, config)) 139 } 140 141 func (c TracedClient) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) { 142 ctx, span := c.span(ctx, "network.create") 143 defer span.End() 144 145 return telemetry.RecordErrorOnSpanTwo[types.NetworkCreateResponse](span)(c.client.NetworkCreate(ctx, name, options)) 146 } 147 148 func (c TracedClient) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) { 149 ctx, span := c.span(ctx, "network.list") 150 defer span.End() 151 152 return telemetry.RecordErrorOnSpanTwo[[]types.NetworkResource](span)(c.client.NetworkList(ctx, options)) 153 } 154 155 func (c TracedClient) NetworkInspect( 156 ctx context.Context, 157 networkID string, 158 options types.NetworkInspectOptions, 159 ) (types.NetworkResource, error) { 160 ctx, span := c.span(ctx, "network.inspect") 161 defer span.End() 162 163 return telemetry.RecordErrorOnSpanTwo[types.NetworkResource](span)(c.client.NetworkInspect(ctx, networkID, options)) 164 } 165 166 func (c TracedClient) NetworkRemove(ctx context.Context, networkID string) error { 167 ctx, span := c.span(ctx, "network.rm") 168 defer span.End() 169 170 return telemetry.RecordErrorOnSpan(span)(c.client.NetworkRemove(ctx, networkID)) 171 } 172 173 func (c TracedClient) Info(ctx context.Context) (types.Info, error) { 174 ctx, span := c.span(ctx, "info") 175 defer span.End() 176 177 return telemetry.RecordErrorOnSpanTwo[types.Info](span)(c.client.Info(ctx)) 178 } 179 180 func (c TracedClient) Close() error { 181 return c.client.Close() 182 } 183 184 func (c TracedClient) span(ctx context.Context, name string) (context.Context, trace.Span) { 185 return system.NewSpan( 186 ctx, 187 system.GetTracer(), 188 fmt.Sprintf("docker.%s", name), 189 trace.WithAttributes(semconv.HostName(c.hostname), semconv.PeerService("docker")), 190 trace.WithSpanKind(trace.SpanKindClient), 191 ) 192 }