github.com/nektos/act@v0.2.63/pkg/container/docker_run_test.go (about) 1 package container 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "fmt" 8 "io" 9 "net" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/docker/docker/api/types" 15 "github.com/docker/docker/client" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/mock" 18 ) 19 20 func TestDocker(t *testing.T) { 21 ctx := context.Background() 22 client, err := GetDockerClient(ctx) 23 assert.NoError(t, err) 24 defer client.Close() 25 26 dockerBuild := NewDockerBuildExecutor(NewDockerBuildExecutorInput{ 27 ContextDir: "testdata", 28 ImageTag: "envmergetest", 29 }) 30 31 err = dockerBuild(ctx) 32 assert.NoError(t, err) 33 34 cr := &containerReference{ 35 cli: client, 36 input: &NewContainerInput{ 37 Image: "envmergetest", 38 }, 39 } 40 env := map[string]string{ 41 "PATH": "/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin", 42 "RANDOM_VAR": "WITH_VALUE", 43 "ANOTHER_VAR": "", 44 "CONFLICT_VAR": "I_EXIST_IN_MULTIPLE_PLACES", 45 } 46 47 envExecutor := cr.extractFromImageEnv(&env) 48 err = envExecutor(ctx) 49 assert.NoError(t, err) 50 assert.Equal(t, map[string]string{ 51 "PATH": "/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin:/this/path/does/not/exists/anywhere:/this/either", 52 "RANDOM_VAR": "WITH_VALUE", 53 "ANOTHER_VAR": "", 54 "SOME_RANDOM_VAR": "", 55 "ANOTHER_ONE": "BUT_I_HAVE_VALUE", 56 "CONFLICT_VAR": "I_EXIST_IN_MULTIPLE_PLACES", 57 }, env) 58 } 59 60 type mockDockerClient struct { 61 client.APIClient 62 mock.Mock 63 } 64 65 func (m *mockDockerClient) ContainerExecCreate(ctx context.Context, id string, opts types.ExecConfig) (types.IDResponse, error) { 66 args := m.Called(ctx, id, opts) 67 return args.Get(0).(types.IDResponse), args.Error(1) 68 } 69 70 func (m *mockDockerClient) ContainerExecAttach(ctx context.Context, id string, opts types.ExecStartCheck) (types.HijackedResponse, error) { 71 args := m.Called(ctx, id, opts) 72 return args.Get(0).(types.HijackedResponse), args.Error(1) 73 } 74 75 func (m *mockDockerClient) ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error) { 76 args := m.Called(ctx, execID) 77 return args.Get(0).(types.ContainerExecInspect), args.Error(1) 78 } 79 80 func (m *mockDockerClient) CopyToContainer(ctx context.Context, id string, path string, content io.Reader, options types.CopyToContainerOptions) error { 81 args := m.Called(ctx, id, path, content, options) 82 return args.Error(0) 83 } 84 85 type endlessReader struct { 86 io.Reader 87 } 88 89 func (r endlessReader) Read(_ []byte) (n int, err error) { 90 return 1, nil 91 } 92 93 type mockConn struct { 94 net.Conn 95 mock.Mock 96 } 97 98 func (m *mockConn) Write(b []byte) (n int, err error) { 99 args := m.Called(b) 100 return args.Int(0), args.Error(1) 101 } 102 103 func (m *mockConn) Close() (err error) { 104 return nil 105 } 106 107 func TestDockerExecAbort(t *testing.T) { 108 ctx, cancel := context.WithCancel(context.Background()) 109 110 conn := &mockConn{} 111 conn.On("Write", mock.AnythingOfType("[]uint8")).Return(1, nil) 112 113 client := &mockDockerClient{} 114 client.On("ContainerExecCreate", ctx, "123", mock.AnythingOfType("types.ExecConfig")).Return(types.IDResponse{ID: "id"}, nil) 115 client.On("ContainerExecAttach", ctx, "id", mock.AnythingOfType("types.ExecStartCheck")).Return(types.HijackedResponse{ 116 Conn: conn, 117 Reader: bufio.NewReader(endlessReader{}), 118 }, nil) 119 120 cr := &containerReference{ 121 id: "123", 122 cli: client, 123 input: &NewContainerInput{ 124 Image: "image", 125 }, 126 } 127 128 channel := make(chan error) 129 130 go func() { 131 channel <- cr.exec([]string{""}, map[string]string{}, "user", "workdir")(ctx) 132 }() 133 134 time.Sleep(500 * time.Millisecond) 135 136 cancel() 137 138 err := <-channel 139 assert.ErrorIs(t, err, context.Canceled) 140 141 conn.AssertExpectations(t) 142 client.AssertExpectations(t) 143 } 144 145 func TestDockerExecFailure(t *testing.T) { 146 ctx := context.Background() 147 148 conn := &mockConn{} 149 150 client := &mockDockerClient{} 151 client.On("ContainerExecCreate", ctx, "123", mock.AnythingOfType("types.ExecConfig")).Return(types.IDResponse{ID: "id"}, nil) 152 client.On("ContainerExecAttach", ctx, "id", mock.AnythingOfType("types.ExecStartCheck")).Return(types.HijackedResponse{ 153 Conn: conn, 154 Reader: bufio.NewReader(strings.NewReader("output")), 155 }, nil) 156 client.On("ContainerExecInspect", ctx, "id").Return(types.ContainerExecInspect{ 157 ExitCode: 1, 158 }, nil) 159 160 cr := &containerReference{ 161 id: "123", 162 cli: client, 163 input: &NewContainerInput{ 164 Image: "image", 165 }, 166 } 167 168 err := cr.exec([]string{""}, map[string]string{}, "user", "workdir")(ctx) 169 assert.Error(t, err, "exit with `FAILURE`: 1") 170 171 conn.AssertExpectations(t) 172 client.AssertExpectations(t) 173 } 174 175 func TestDockerCopyTarStream(t *testing.T) { 176 ctx := context.Background() 177 178 conn := &mockConn{} 179 180 client := &mockDockerClient{} 181 client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(nil) 182 client.On("CopyToContainer", ctx, "123", "/var/run/act", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(nil) 183 cr := &containerReference{ 184 id: "123", 185 cli: client, 186 input: &NewContainerInput{ 187 Image: "image", 188 }, 189 } 190 191 _ = cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{}) 192 193 conn.AssertExpectations(t) 194 client.AssertExpectations(t) 195 } 196 197 func TestDockerCopyTarStreamErrorInCopyFiles(t *testing.T) { 198 ctx := context.Background() 199 200 conn := &mockConn{} 201 202 merr := fmt.Errorf("Failure") 203 204 client := &mockDockerClient{} 205 client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(merr) 206 client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(merr) 207 cr := &containerReference{ 208 id: "123", 209 cli: client, 210 input: &NewContainerInput{ 211 Image: "image", 212 }, 213 } 214 215 err := cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{}) 216 assert.ErrorIs(t, err, merr) 217 218 conn.AssertExpectations(t) 219 client.AssertExpectations(t) 220 } 221 222 func TestDockerCopyTarStreamErrorInMkdir(t *testing.T) { 223 ctx := context.Background() 224 225 conn := &mockConn{} 226 227 merr := fmt.Errorf("Failure") 228 229 client := &mockDockerClient{} 230 client.On("CopyToContainer", ctx, "123", "/", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(nil) 231 client.On("CopyToContainer", ctx, "123", "/var/run/act", mock.Anything, mock.AnythingOfType("types.CopyToContainerOptions")).Return(merr) 232 cr := &containerReference{ 233 id: "123", 234 cli: client, 235 input: &NewContainerInput{ 236 Image: "image", 237 }, 238 } 239 240 err := cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{}) 241 assert.ErrorIs(t, err, merr) 242 243 conn.AssertExpectations(t) 244 client.AssertExpectations(t) 245 } 246 247 // Type assert containerReference implements ExecutionsEnvironment 248 var _ ExecutionsEnvironment = &containerReference{}