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