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  }