github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/docker/switch.go (about)

     1  package docker
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"sync"
     7  
     8  	"github.com/distribution/reference"
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/api/types/filters"
    11  	"golang.org/x/sync/errgroup"
    12  
    13  	"github.com/tilt-dev/tilt/internal/container"
    14  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    15  	"github.com/tilt-dev/tilt/pkg/model"
    16  )
    17  
    18  // A Cli implementation that lets us switch back and forth between a local
    19  // Docker instance and one that lives in our K8s cluster.
    20  
    21  type switchCli struct {
    22  	localCli   LocalClient
    23  	clusterCli ClusterClient
    24  	orc        model.Orchestrator
    25  	mu         sync.Mutex
    26  }
    27  
    28  var _ Client = &switchCli{}
    29  var _ CompositeClient = &switchCli{}
    30  
    31  func ProvideSwitchCli(clusterCli ClusterClient, localCli LocalClient) CompositeClient {
    32  	return &switchCli{
    33  		localCli:   localCli,
    34  		clusterCli: clusterCli,
    35  		orc:        model.OrchestratorK8s,
    36  	}
    37  }
    38  
    39  var orcKey model.Orchestrator
    40  
    41  // WithOrchestrator returns a Context with the current orchestrator set.
    42  func WithOrchestrator(ctx context.Context, orc model.Orchestrator) context.Context {
    43  	return context.WithValue(ctx, orcKey, orc)
    44  }
    45  
    46  func (c *switchCli) client(ctx context.Context) Client {
    47  	c.mu.Lock()
    48  	defer c.mu.Unlock()
    49  	orc, ok := ctx.Value(orcKey).(model.Orchestrator)
    50  	if ok {
    51  		return c.ForOrchestrator(orc)
    52  	}
    53  	return c.ForOrchestrator(c.orc)
    54  }
    55  
    56  func (c *switchCli) SetOrchestrator(orc model.Orchestrator) {
    57  	c.mu.Lock()
    58  	defer c.mu.Unlock()
    59  	c.orc = orc
    60  }
    61  func (c *switchCli) ForOrchestrator(orc model.Orchestrator) Client {
    62  	if orc == model.OrchestratorK8s {
    63  		return c.clusterCli
    64  	}
    65  	return c.localCli
    66  }
    67  
    68  func (c *switchCli) CheckConnected() error {
    69  	return c.client(context.Background()).CheckConnected()
    70  }
    71  func (c *switchCli) Env() Env {
    72  	return c.client(context.Background()).Env()
    73  }
    74  func (c *switchCli) BuilderVersion(ctx context.Context) (types.BuilderVersion, error) {
    75  	return c.client(ctx).BuilderVersion(ctx)
    76  }
    77  func (c *switchCli) ServerVersion(ctx context.Context) (types.Version, error) {
    78  	return c.client(ctx).ServerVersion(ctx)
    79  }
    80  func (c *switchCli) ContainerLogs(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
    81  	return c.client(ctx).ContainerLogs(ctx, containerID, options)
    82  }
    83  func (c *switchCli) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) {
    84  	return c.client(ctx).ContainerInspect(ctx, containerID)
    85  }
    86  func (c *switchCli) ContainerList(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) {
    87  	return c.client(ctx).ContainerList(ctx, options)
    88  }
    89  func (c *switchCli) ContainerRestartNoWait(ctx context.Context, containerID string) error {
    90  	return c.client(ctx).ContainerRestartNoWait(ctx, containerID)
    91  }
    92  func (c *switchCli) Run(ctx context.Context, opts RunConfig) (RunResult, error) {
    93  	return c.client(ctx).Run(ctx, opts)
    94  }
    95  func (c *switchCli) ExecInContainer(ctx context.Context, cID container.ID, cmd model.Cmd, in io.Reader, out io.Writer) error {
    96  	return c.client(ctx).ExecInContainer(ctx, cID, cmd, in, out)
    97  }
    98  func (c *switchCli) ImagePull(ctx context.Context, ref reference.Named) (reference.Canonical, error) {
    99  	return c.client(ctx).ImagePull(ctx, ref)
   100  }
   101  func (c *switchCli) ImagePush(ctx context.Context, ref reference.NamedTagged) (io.ReadCloser, error) {
   102  	return c.client(ctx).ImagePush(ctx, ref)
   103  }
   104  func (c *switchCli) ImageBuild(ctx context.Context, g *errgroup.Group, buildContext io.Reader, options BuildOptions) (types.ImageBuildResponse, error) {
   105  	return c.client(ctx).ImageBuild(ctx, g, buildContext, options)
   106  }
   107  func (c *switchCli) ImageTag(ctx context.Context, source, target string) error {
   108  	return c.client(ctx).ImageTag(ctx, source, target)
   109  }
   110  func (c *switchCli) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) {
   111  	return c.client(ctx).ImageInspectWithRaw(ctx, imageID)
   112  }
   113  func (c *switchCli) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
   114  	return c.client(ctx).ImageList(ctx, options)
   115  }
   116  func (c *switchCli) ImageRemove(ctx context.Context, imageID string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
   117  	return c.client(ctx).ImageRemove(ctx, imageID, options)
   118  }
   119  func (c *switchCli) NewVersionError(apiRequired, feature string) error {
   120  	return c.client(context.Background()).NewVersionError(apiRequired, feature)
   121  }
   122  func (c *switchCli) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
   123  	return c.client(ctx).BuildCachePrune(ctx, opts)
   124  }
   125  func (c *switchCli) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) {
   126  	return c.client(ctx).ContainersPrune(ctx, pruneFilters)
   127  }
   128  
   129  // CompositeClient
   130  func (c *switchCli) DefaultLocalClient() Client {
   131  	return c.localCli
   132  }
   133  func (c *switchCli) DefaultClusterClient() Client {
   134  	return c.clusterCli
   135  }
   136  func (c *switchCli) ClientFor(cluster v1alpha1.Cluster) Client {
   137  	conn := cluster.Spec.Connection
   138  	if conn.Kubernetes != nil {
   139  		// TODO: pick correct client in multiple cluster situation
   140  		return c.DefaultClusterClient()
   141  	}
   142  	return c.DefaultLocalClient()
   143  }
   144  func (c *switchCli) HasMultipleClients() bool {
   145  	return c.localCli.Env().DaemonHost() != c.clusterCli.Env().DaemonHost()
   146  }