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{}