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 }