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

     1  package dockercompose
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"sync"
     9  	"testing"
    10  
    11  	"github.com/compose-spec/compose-go/loader"
    12  
    13  	"github.com/compose-spec/compose-go/types"
    14  
    15  	"github.com/tilt-dev/tilt/internal/container"
    16  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    17  )
    18  
    19  type FakeDCClient struct {
    20  	t   *testing.T
    21  	ctx context.Context
    22  
    23  	mu sync.Mutex
    24  
    25  	ContainerIDDefault   container.ID
    26  	ContainerIDByService map[string]container.ID
    27  	eventJson            chan string
    28  	ConfigOutput         string
    29  	VersionOutput        string
    30  
    31  	upCalls   []UpCall
    32  	downCalls []DownCall
    33  	rmCalls   []RmCall
    34  	DownError error
    35  	RmError   error
    36  	RmOutput  string
    37  	WorkDir   string
    38  }
    39  
    40  var _ DockerComposeClient = &FakeDCClient{}
    41  
    42  // Represents a single call to Up
    43  type UpCall struct {
    44  	Spec        v1alpha1.DockerComposeServiceSpec
    45  	ShouldBuild bool
    46  }
    47  
    48  // Represents a single call to Down
    49  type DownCall struct {
    50  	Proj v1alpha1.DockerComposeProject
    51  }
    52  
    53  type RmCall struct {
    54  	Specs []v1alpha1.DockerComposeServiceSpec
    55  }
    56  
    57  func NewFakeDockerComposeClient(t *testing.T, ctx context.Context) *FakeDCClient {
    58  	return &FakeDCClient{
    59  		t:                    t,
    60  		ctx:                  ctx,
    61  		eventJson:            make(chan string, 100),
    62  		ContainerIDByService: make(map[string]container.ID),
    63  	}
    64  }
    65  
    66  func (c *FakeDCClient) Up(ctx context.Context, spec v1alpha1.DockerComposeServiceSpec,
    67  	shouldBuild bool, stdout, stderr io.Writer) error {
    68  	c.mu.Lock()
    69  	defer c.mu.Unlock()
    70  
    71  	c.upCalls = append(c.upCalls, UpCall{spec, shouldBuild})
    72  	return nil
    73  }
    74  
    75  func (c *FakeDCClient) Down(ctx context.Context, proj v1alpha1.DockerComposeProject, stdout, stderr io.Writer) error {
    76  	c.mu.Lock()
    77  	defer c.mu.Unlock()
    78  
    79  	c.downCalls = append(c.downCalls, DownCall{proj})
    80  	if c.DownError != nil {
    81  		err := c.DownError
    82  		c.DownError = nil
    83  		return err
    84  	}
    85  	return nil
    86  }
    87  
    88  func (c *FakeDCClient) Rm(ctx context.Context, specs []v1alpha1.DockerComposeServiceSpec, stdout, stderr io.Writer) error {
    89  	c.mu.Lock()
    90  	defer c.mu.Unlock()
    91  
    92  	c.rmCalls = append(c.rmCalls, RmCall{specs})
    93  	if c.RmError != nil {
    94  		err := c.RmError
    95  		c.RmError = nil
    96  		return err
    97  	}
    98  
    99  	_, _ = fmt.Fprint(stdout, c.RmOutput)
   100  	return nil
   101  }
   102  
   103  func (c *FakeDCClient) StreamEvents(ctx context.Context, p v1alpha1.DockerComposeProject) (<-chan string, error) {
   104  	events := make(chan string, 10)
   105  	go func() {
   106  		for {
   107  			select {
   108  			case event := <-c.eventJson:
   109  				select {
   110  				case events <- event: // send event to channel (unless it's full)
   111  				default:
   112  					panic(fmt.Sprintf("no room on events channel to send event: '%s'. Something "+
   113  						"is wrong (or you need to increase the buffer).", event))
   114  				}
   115  			case <-ctx.Done():
   116  				return
   117  			}
   118  		}
   119  	}()
   120  
   121  	return events, nil
   122  }
   123  
   124  func (c *FakeDCClient) SendEvent(evt Event) error {
   125  	j, err := json.Marshal(evt)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	c.eventJson <- string(j)
   130  	return nil
   131  }
   132  
   133  func (c *FakeDCClient) Config(_ context.Context, _ []string) (string, error) {
   134  	return c.ConfigOutput, nil
   135  }
   136  
   137  func (c *FakeDCClient) Project(_ context.Context, m v1alpha1.DockerComposeProject) (*types.Project, error) {
   138  	// this is a dummy ProjectOptions that lets us use compose's logic to apply options
   139  	// for consistency, but we have to then pull the data out ourselves since we're calling
   140  	// loader.Load ourselves
   141  	opts, err := composeProjectOptions(m, nil)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	workDir := opts.WorkingDir
   147  	projectName := opts.Name
   148  	if projectName == "" {
   149  		projectName = loader.NormalizeProjectName(workDir)
   150  	}
   151  	if projectName == "" {
   152  		projectName = "fakedc"
   153  	}
   154  
   155  	p, err := loader.Load(types.ConfigDetails{
   156  		WorkingDir: workDir,
   157  		ConfigFiles: []types.ConfigFile{
   158  			{
   159  				Content: []byte(c.ConfigOutput),
   160  			},
   161  		},
   162  		Environment: opts.Environment,
   163  	}, dcLoaderOption(projectName))
   164  	return p, err
   165  }
   166  
   167  func (c *FakeDCClient) ContainerID(ctx context.Context, spec v1alpha1.DockerComposeServiceSpec) (container.ID, error) {
   168  	id, ok := c.ContainerIDByService[spec.Service]
   169  	if ok {
   170  		return id, nil
   171  	}
   172  	return c.ContainerIDDefault, nil
   173  }
   174  
   175  func (c *FakeDCClient) Version(_ context.Context) (string, string, error) {
   176  	if c.VersionOutput != "" {
   177  		return c.VersionOutput, "tilt-fake", nil
   178  	}
   179  	// default to a "known good" version that won't produce warnings
   180  	return "v1.29.2", "tilt-fake", nil
   181  }
   182  
   183  func (c *FakeDCClient) UpCalls() []UpCall {
   184  	c.mu.Lock()
   185  	defer c.mu.Unlock()
   186  	return append([]UpCall{}, c.upCalls...)
   187  }
   188  
   189  func (c *FakeDCClient) DownCalls() []DownCall {
   190  	c.mu.Lock()
   191  	defer c.mu.Unlock()
   192  	return append([]DownCall{}, c.downCalls...)
   193  }
   194  
   195  func (c *FakeDCClient) RmCalls() []RmCall {
   196  	c.mu.Lock()
   197  	defer c.mu.Unlock()
   198  	return append([]RmCall{}, c.rmCalls...)
   199  }