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 }