Uploaded image for project: 'Bitbucket Cloud'
  1. Bitbucket Cloud
  2. BCLOUD-23347

The Windows runner cannot always clean the build directory when symlinks are present

XMLWordPrintable

      Issue Summary

      When symlinks are created in the build directory of a Pipelines build running with a Windows runner, the runner sometimes cannot clean the build directory, and it goes into an unhealth state.

      Steps to Reproduce

      1. Use a yml file like the following with a Windows self-hosted runner:

      pipelines:
        default:
          - step:
              name: 'Build and Test'
              runs-on:
                - self.hosted
                - windows
              script:
                - echo "Your build and test goes here..."
                - mkdir dir1
                - New-Item -ItemType SymbolicLink -Path .\link -Target .\dir1
                - ls
      

      2. Run a Pipelines build with this yml.

      Notes:

      The issue is consistently reproduced when the name of the symlink comes after the name of the directory that the symlink is pointing to in alphabetical order.

      E.g. it is reproducible when the symlink link is pointing to dir1.

      The issue is not reproducible when the symlink alink points to dir1.

      The issue has been reproduced with version 2.6.0 of the Windows Runner.

      Expected Results

      When the build finishes, the runner should be able to clean up the build directory.

      Actual Results

      The runner cannot clean the build directory after the step finishes, and it goes into an unhealthy state.

      The below exception is thrown in runner's log:

      An error occurred whilst tearing down directories.
      java.nio.file.NoSuchFileException: C:\Users\<someuser>\Documents\atlassian-bitbucket-pipelines-runner\bin\..\temp\<runner-uuid>\build\link
              at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
              at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
              at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
              at java.base/sun.nio.fs.WindowsLinkSupport.getFinalPath(WindowsLinkSupport.java:74)
              at java.base/sun.nio.fs.WindowsLinkSupport.getFinalPath(WindowsLinkSupport.java:112)
              at java.base/sun.nio.fs.WindowsFileAttributeViews$Dos.updateAttributes(WindowsFileAttributeViews.java:228)
              at java.base/sun.nio.fs.WindowsFileAttributeViews$Dos.setReadOnly(WindowsFileAttributeViews.java:248)
              at com.atlassian.pipelines.runner.core.directory.windows.WindowsDirectoryImpl$1.visitFile(WindowsDirectoryImpl.java:84)
              at com.atlassian.pipelines.runner.core.directory.windows.WindowsDirectoryImpl$1.visitFile(WindowsDirectoryImpl.java:73)
              at java.base/java.nio.file.Files.walkFileTree(Files.java:2812)
              at java.base/java.nio.file.Files.walkFileTree(Files.java:2883)
              at com.atlassian.pipelines.runner.core.directory.windows.WindowsDirectoryImpl.delete(WindowsDirectoryImpl.java:71)
              at com.atlassian.pipelines.runner.core.runtime.common.NativeTemporaryDirectoryStepRuntimeImpl.lambda$tearDownDirectory$5(NativeTemporaryDirectoryStepRuntimeImpl.java:77)
              at io.reactivex.internal.operators.completable.CompletableFromAction.subscribeActual(CompletableFromAction.java:35)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletableMerge$CompletableMergeSubscriber.onNext(CompletableMerge.java:99)
              at io.reactivex.internal.operators.completable.CompletableMerge$CompletableMergeSubscriber.onNext(CompletableMerge.java:44)
              at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:68)
              at io.reactivex.internal.operators.flowable.FlowableFromIterable$IteratorSubscription.fastPath(FlowableFromIterable.java:178)
              at io.reactivex.internal.operators.flowable.FlowableFromIterable$BaseRangeSubscription.request(FlowableFromIterable.java:122)
              at io.reactivex.internal.subscribers.BasicFuseableSubscriber.request(BasicFuseableSubscriber.java:153)
              at io.reactivex.internal.operators.completable.CompletableMerge$CompletableMergeSubscriber.onSubscribe(CompletableMerge.java:86)
              at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onSubscribe(BasicFuseableSubscriber.java:67)
              at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribe(FlowableFromIterable.java:69)
              at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribeActual(FlowableFromIterable.java:47)
              at io.reactivex.Flowable.subscribe(Flowable.java:14935)
              at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:37)
              at io.reactivex.Flowable.subscribe(Flowable.java:14935)
              at io.reactivex.Flowable.subscribe(Flowable.java:14882)
              at io.reactivex.internal.operators.completable.CompletableMerge.subscribeActual(CompletableMerge.java:41)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletableResumeNext.subscribeActual(CompletableResumeNext.java:41)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.single.SingleDelayWithCompletable.subscribeActual(SingleDelayWithCompletable.java:36)
              at io.reactivex.Single.subscribe(Single.java:3666)
              at io.reactivex.internal.operators.single.SingleResumeNext.subscribeActual(SingleResumeNext.java:39)
              at io.reactivex.Single.subscribe(Single.java:3666)
              at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onSuccess(SingleFlatMap.java:84)
              at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver.onSuccess(SingleFlatMap.java:111)
              at io.reactivex.internal.operators.single.SingleResumeNext$ResumeMainSingleObserver.onSuccess(SingleResumeNext.java:65)
              at io.reactivex.internal.operators.single.SingleResumeNext$ResumeMainSingleObserver.onSuccess(SingleResumeNext.java:65)
              at io.reactivex.internal.observers.ResumeSingleObserver.onSuccess(ResumeSingleObserver.java:46)
              at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:56)
              at io.reactivex.Single.subscribe(Single.java:3666)
              at io.reactivex.internal.operators.single.SingleDelayWithCompletable$OtherObserver.onComplete(SingleDelayWithCompletable.java:69)
              at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$NextObserver.onComplete(CompletableAndThenCompletable.java:99)
              at io.reactivex.internal.operators.completable.CompletableFromRunnable.subscribeActual(CompletableFromRunnable.java:47)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$SourceObserver.onComplete(CompletableAndThenCompletable.java:67)
              at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$NextObserver.onComplete(CompletableAndThenCompletable.java:99)
              at io.reactivex.internal.operators.completable.CompletableDoFinally$DoFinallyObserver.onComplete(CompletableDoFinally.java:78)
              at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
              at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
              at io.reactivex.internal.operators.completable.CompletableMergeIterable$MergeCompletableObserver.onComplete(CompletableMergeIterable.java:132)
              at io.reactivex.internal.operators.completable.CompletableMergeIterable.subscribeActual(CompletableMergeIterable.java:94)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletableDoFinally.subscribeActual(CompletableDoFinally.java:43)
              at io.reactivex.Completable.subscribe(Completable.java:2309)
              at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$SourceObserver.onComplete(CompletableAndThenCompletable.java:67)
              at io.reactivex.internal.operators.completable.CompletableOnErrorComplete$OnError.onComplete(CompletableOnErrorComplete.java:48)
              at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
              at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
              at io.reactivex.internal.operators.completable.CompletableObserveOn$ObserveOnCompletableObserver.run(CompletableObserveOn.java:89)
              at brave.propagation.CurrentTraceContext$1CurrentTraceContextRunnable.run(CurrentTraceContext.java:264)
              at com.atlassian.pipelines.common.trace.rxjava.CopyMdcSchedulerHandler$CopyMdcRunnableAdapter.run(CopyMdcSchedulerHandler.java:74)
              at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:608)
              at brave.propagation.CurrentTraceContext$1CurrentTraceContextRunnable.run(CurrentTraceContext.java:264)
              at com.atlassian.pipelines.common.trace.rxjava.CopyMdcSchedulerHandler$CopyMdcRunnableAdapter.run(CopyMdcSchedulerHandler.java:74)
              at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
              at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
              at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
              at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
              at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
              at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
              at java.base/java.lang.Thread.run(Thread.java:833)
      [2024-08-02 12:15:46,315] Cancelling timeout
      [2024-08-02 12:15:46,320] Updating runner state to "UNHEALTHY".
      [2024-08-02 12:15:46,581] Completing step with result Result{status=FAILED, error=Some(Error{key='runner.working-directory-teardown-error', message='An error occurred while attempting to clean the build folder. Check the runner logs and manually clean up the build folder on the host machine to fix this issue.', arguments={}})}.
      [2024-08-02 12:15:46,590] Runner cannot accept new steps since it's unhealthy.
      [2024-08-02 12:15:46,895] Setting runner state to not executing step.
      

      Workaround

      Delete the symlinks in the after-script of the step. Then, the runner will be able to clean up the build directory during Build teardown.

              Unassigned Unassigned
              tboudale Theodora Boudale (Inactive)
              Votes:
              7 Vote for this issue
              Watchers:
              9 Start watching this issue

                Created:
                Updated: