gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+incompatible/network/trace_test.go (about)

     1  package network
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/mock"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"gitlab.com/gitlab-org/gitlab-runner/common"
    17  )
    18  
    19  var (
    20  	jobConfig      = common.RunnerConfig{}
    21  	jobCredentials = &common.JobCredentials{ID: -1}
    22  	jobOutputLimit = common.RunnerConfig{OutputLimit: 1}
    23  
    24  	noTrace *string
    25  )
    26  
    27  func matchJobState(jobInfo common.UpdateJobInfo, id int, state common.JobState, failureReason common.JobFailureReason) bool {
    28  	if jobInfo.ID != id {
    29  		return false
    30  	}
    31  	if jobInfo.State != state {
    32  		return false
    33  	}
    34  	if jobInfo.FailureReason != failureReason {
    35  		return false
    36  	}
    37  	return true
    38  }
    39  
    40  func generateJobInfoMatcher(id int, state common.JobState, failureReason common.JobFailureReason) interface{} {
    41  	return mock.MatchedBy(func(jobInfo common.UpdateJobInfo) bool {
    42  		return matchJobState(jobInfo, id, state, failureReason)
    43  	})
    44  }
    45  
    46  func TestIgnoreStatusChange(t *testing.T) {
    47  	jobInfoMatcher := generateJobInfoMatcher(jobCredentials.ID, common.Success, common.NoneFailure)
    48  
    49  	mockNetwork := new(common.MockNetwork)
    50  	defer mockNetwork.AssertExpectations(t)
    51  
    52  	// expect to receive just one status
    53  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, jobInfoMatcher).
    54  		Return(common.UpdateSucceeded).Once()
    55  
    56  	b := newJobTrace(mockNetwork, jobConfig, jobCredentials)
    57  	b.start()
    58  	b.Success()
    59  	b.Fail(errors.New("test"), "script_failure")
    60  }
    61  
    62  func TestJobAbort(t *testing.T) {
    63  	ctx, cancel := context.WithCancel(context.Background())
    64  	defer cancel()
    65  
    66  	keepAliveUpdateMatcher := generateJobInfoMatcher(jobCredentials.ID, common.Running, common.NoneFailure)
    67  	updateMatcher := generateJobInfoMatcher(jobCredentials.ID, common.Success, common.NoneFailure)
    68  
    69  	mockNetwork := new(common.MockNetwork)
    70  	defer mockNetwork.AssertExpectations(t)
    71  
    72  	// abort while running
    73  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, keepAliveUpdateMatcher).
    74  		Return(common.UpdateAbort).Once()
    75  
    76  	// try to send status at least once more
    77  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, updateMatcher).
    78  		Return(common.UpdateAbort).Once()
    79  
    80  	b := newJobTrace(mockNetwork, jobConfig, jobCredentials)
    81  	b.updateInterval = 0
    82  	b.SetCancelFunc(cancel)
    83  
    84  	b.start()
    85  	assert.NotNil(t, <-ctx.Done(), "should abort the job")
    86  	b.Success()
    87  }
    88  
    89  func TestJobOutputLimit(t *testing.T) {
    90  	traceMessage := "abcde"
    91  
    92  	mockNetwork := new(common.MockNetwork)
    93  	defer mockNetwork.AssertExpectations(t)
    94  
    95  	b := newJobTrace(mockNetwork, jobOutputLimit, jobCredentials)
    96  	// prevent any UpdateJob before `b.Success()` call
    97  	b.updateInterval = 25 * time.Second
    98  
    99  	updateMatcher := generateJobInfoMatcher(jobCredentials.ID, common.Success, common.NoneFailure)
   100  
   101  	receivedTrace := bytes.NewBuffer([]byte{})
   102  	mockNetwork.On("PatchTrace", jobOutputLimit, jobCredentials, mock.Anything, mock.Anything).
   103  		Return(1077, common.UpdateSucceeded).
   104  		Run(func(args mock.Arguments) {
   105  			// the 1077 == len(data)
   106  			data := args.Get(2).([]byte)
   107  			receivedTrace.Write(data)
   108  		})
   109  
   110  	mockNetwork.On("UpdateJob", jobOutputLimit, jobCredentials, updateMatcher).
   111  		Return(common.UpdateSucceeded).Once()
   112  
   113  	b.start()
   114  	// Write 5k to the buffer
   115  	for i := 0; i < 1024; i++ {
   116  		fmt.Fprint(b, traceMessage)
   117  	}
   118  	b.Success()
   119  
   120  	expectedLogLimitExceededMsg := "Job's log exceeded limit of"
   121  
   122  	assert.Contains(t, receivedTrace.String(), traceMessage)
   123  	assert.Contains(t, receivedTrace.String(), expectedLogLimitExceededMsg)
   124  }
   125  
   126  func TestJobMasking(t *testing.T) {
   127  	maskedValues := []string{"masked"}
   128  	traceMessage := "This string should be masked"
   129  	traceMaskedMessage := "This string should be [MASKED]"
   130  
   131  	mockNetwork := new(common.MockNetwork)
   132  	defer mockNetwork.AssertExpectations(t)
   133  
   134  	mockNetwork.On("PatchTrace", mock.Anything, mock.Anything, []byte(traceMaskedMessage), 0).
   135  		Return(len(traceMaskedMessage), common.UpdateSucceeded)
   136  
   137  	mockNetwork.On("UpdateJob", mock.Anything, mock.Anything, mock.Anything).
   138  		Return(common.UpdateSucceeded)
   139  
   140  	jobTrace := newJobTrace(mockNetwork, jobConfig, jobCredentials)
   141  	jobTrace.SetMasked(maskedValues)
   142  	jobTrace.start()
   143  
   144  	_, err := jobTrace.Write([]byte(traceMessage))
   145  	require.NoError(t, err)
   146  	jobTrace.Success()
   147  }
   148  
   149  func TestJobFinishTraceUpdateRetry(t *testing.T) {
   150  	updateMatcher := generateJobInfoMatcher(jobCredentials.ID, common.Success, common.NoneFailure)
   151  
   152  	mockNetwork := new(common.MockNetwork)
   153  	defer mockNetwork.AssertExpectations(t)
   154  
   155  	// accept just 3 bytes
   156  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("My trace send"), 0).
   157  		Return(3, common.UpdateSucceeded).Once()
   158  
   159  	// retry when trying to send next bytes
   160  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("trace send"), 3).
   161  		Return(0, common.UpdateFailed).Once()
   162  
   163  	// accept 6 more bytes
   164  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("trace send"), 3).
   165  		Return(9, common.UpdateSucceeded).Once()
   166  
   167  	// restart most of trace
   168  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("send"), 9).
   169  		Return(6, common.UpdateRangeMismatch).Once()
   170  
   171  	// accept rest of trace
   172  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("ce send"), 6).
   173  		Return(13, common.UpdateSucceeded).Once()
   174  
   175  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, updateMatcher).
   176  		Return(common.UpdateSucceeded).Once()
   177  
   178  	b := newJobTrace(mockNetwork, jobConfig, jobCredentials)
   179  	b.finishRetryInterval = time.Microsecond
   180  
   181  	b.start()
   182  	fmt.Fprint(b, "My trace send")
   183  	b.Success()
   184  }
   185  
   186  func TestJobMaxTracePatchSize(t *testing.T) {
   187  	updateMatcher := generateJobInfoMatcher(jobCredentials.ID, common.Success, common.NoneFailure)
   188  
   189  	mockNetwork := new(common.MockNetwork)
   190  	defer mockNetwork.AssertExpectations(t)
   191  
   192  	// expect just 5 bytes
   193  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("My tr"), 0).
   194  		Return(5, common.UpdateSucceeded).Once()
   195  
   196  	// expect next 5 bytes
   197  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("ace s"), 5).
   198  		Return(10, common.UpdateSucceeded).Once()
   199  
   200  	// expect last 3 bytes
   201  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("end"), 10).
   202  		Return(13, common.UpdateSucceeded).Once()
   203  
   204  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, updateMatcher).
   205  		Return(common.UpdateSucceeded).Once()
   206  
   207  	b := newJobTrace(mockNetwork, jobConfig, jobCredentials)
   208  	b.finishRetryInterval = time.Microsecond
   209  	b.maxTracePatchSize = 5
   210  
   211  	b.start()
   212  	fmt.Fprint(b, "My trace send")
   213  	b.Success()
   214  }
   215  
   216  func TestJobFinishStatusUpdateRetry(t *testing.T) {
   217  	updateMatcher := generateJobInfoMatcher(jobCredentials.ID, common.Success, common.NoneFailure)
   218  
   219  	mockNetwork := new(common.MockNetwork)
   220  	defer mockNetwork.AssertExpectations(t)
   221  
   222  	// fail job 5 times
   223  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, updateMatcher).
   224  		Return(common.UpdateFailed).Times(5)
   225  
   226  	// accept job
   227  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, updateMatcher).
   228  		Return(common.UpdateSucceeded).Once()
   229  
   230  	b := newJobTrace(mockNetwork, jobConfig, jobCredentials)
   231  	b.finishRetryInterval = time.Microsecond
   232  
   233  	b.start()
   234  	b.Success()
   235  }
   236  
   237  func TestJobIncrementalPatchSend(t *testing.T) {
   238  	var wg sync.WaitGroup
   239  
   240  	finalUpdateMatcher := generateJobInfoMatcher(
   241  		jobCredentials.ID, common.Success, common.NoneFailure)
   242  
   243  	mockNetwork := new(common.MockNetwork)
   244  	defer mockNetwork.AssertExpectations(t)
   245  
   246  	// ensure that PatchTrace gets executed first
   247  	wg.Add(1)
   248  	mockNetwork.On("PatchTrace", jobConfig, jobCredentials, []byte("test trace"), 0).
   249  		Return(10, common.UpdateSucceeded).Once().
   250  		Run(func(args mock.Arguments) {
   251  			wg.Done()
   252  		})
   253  
   254  	// wait for the final `UpdateJob` to be executed
   255  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, finalUpdateMatcher).
   256  		Return(common.UpdateSucceeded).Once()
   257  
   258  	b := newJobTrace(mockNetwork, jobConfig, jobCredentials)
   259  	b.updateInterval = time.Millisecond * 10
   260  	b.start()
   261  	fmt.Fprint(b, "test trace")
   262  	wg.Wait()
   263  	b.Success()
   264  }
   265  
   266  func TestJobIncrementalStatusRefresh(t *testing.T) {
   267  	var wg sync.WaitGroup
   268  
   269  	incrementalUpdateMatcher := generateJobInfoMatcher(
   270  		jobCredentials.ID, common.Running, common.NoneFailure)
   271  
   272  	finalUpdateMatcher := generateJobInfoMatcher(
   273  		jobCredentials.ID, common.Success, common.NoneFailure)
   274  
   275  	mockNetwork := new(common.MockNetwork)
   276  	defer mockNetwork.AssertExpectations(t)
   277  
   278  	// ensure that incremental UpdateJob gets executed first
   279  	wg.Add(1)
   280  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, incrementalUpdateMatcher).
   281  		Return(common.UpdateSucceeded).Once().
   282  		Run(func(args mock.Arguments) {
   283  			wg.Done()
   284  		})
   285  
   286  	// wait for the final `UpdateJob` to be executed
   287  	mockNetwork.On("UpdateJob", jobConfig, jobCredentials, finalUpdateMatcher).
   288  		Return(common.UpdateSucceeded).Once()
   289  
   290  	b := newJobTrace(mockNetwork, jobConfig, jobCredentials)
   291  	b.updateInterval = time.Millisecond * 10
   292  
   293  	// Test for: https://gitlab.com/gitlab-org/gitlab-ce/issues/63972
   294  	// 1. lock, to prevent incrementalUpdate to read state
   295  	// 2. inject final state as early as possible
   296  	b.lock.Lock()
   297  	b.start()
   298  	b.state = common.Success
   299  	b.lock.Unlock()
   300  
   301  	wg.Wait()
   302  	b.finish()
   303  }