github.com/wtsi-hgi/go-softpack-builder@v1.8.1/wr/wr_test.go (about) 1 /******************************************************************************* 2 * Copyright (c) 2023, 2024 Genome Research Ltd. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included 13 * in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 ******************************************************************************/ 23 24 package wr 25 26 import ( 27 "bytes" 28 "crypto/rand" 29 "encoding/json" 30 "fmt" 31 "os" 32 "os/exec" 33 "strings" 34 "testing" 35 "time" 36 37 . "github.com/smartystreets/goconvey/convey" 38 ) 39 40 func TestWR(t *testing.T) { 41 // buildBase would come from our yml config, and envPath and Name 42 // would come from the Definition from the core post to our Server. 43 buildBase := "spack/builds/" 44 envPath := "users/user" 45 envName := "myenv" 46 s3Path := buildBase + envPath + "/" + envName 47 48 Convey("You can generate a wr input", t, func() { 49 const hash = "0110" 50 wrInput, err := SingularityBuildInS3WRInput(s3Path, hash) 51 So(err, ShouldBeNil) 52 So(wrInput, ShouldEqual, `{"cmd": "echo doing build with hash `+hash+`; `+ 53 `if sudo singularity build $TMPDIR/singularity.sif singularity.def &> $TMPDIR/builder.out; then `+ 54 `sudo singularity run $TMPDIR/singularity.sif cat /opt/spack-environment/executables > $TMPDIR/executables && `+ 55 `sudo singularity run $TMPDIR/singularity.sif cat /opt/spack-environment/spack.lock > $TMPDIR/spack.lock && `+ 56 `mv $TMPDIR/singularity.sif $TMPDIR/builder.out $TMPDIR/executables $TMPDIR/spack.lock .; else `+ 57 `mv $TMPDIR/builder.out . && false; fi", `+ 58 `"retries": 0, "rep_grp": "singularity_build-spack/builds/users/user/myenv", "limit_grps": ["s3cache"], `+ 59 `"mounts": [{"Targets": [{"Path":"spack/builds/users/user/myenv","Write":true,"Cache":true}]}]}`) 60 61 var m map[string]any 62 err = json.NewDecoder(strings.NewReader(wrInput)).Decode(&m) 63 So(err, ShouldBeNil) 64 }) 65 66 gsbWR := os.Getenv("GSB_WR_TEST") 67 if gsbWR == "" { 68 SkipConvey("Skipping WR run test, set GSB_WR_TEST to enable", t, func() {}) 69 70 return 71 } 72 73 runner := New("development") 74 runner.memory = "100M" 75 runner.pollDuration = 10 * time.Millisecond 76 77 Convey("You can run a cmd via wr", t, func() { 78 now := time.Now() 79 runArgs, repGrp := uniqueRunArgs("sleep 2s", "") 80 jobID, err := runner.Add(runArgs) 81 So(err, ShouldBeNil) 82 err = runner.WaitForRunning(jobID) 83 So(err, ShouldBeNil) 84 status, err := runner.Wait(jobID) 85 So(err, ShouldBeNil) 86 So(status, ShouldEqual, WRJobStatusComplete) 87 So(time.Since(now), ShouldBeGreaterThan, 2*time.Second) 88 89 statusOut := getWRStatus(runner.deployment, repGrp) 90 So(statusOut, ShouldContainSubstring, `"State":"complete"`) 91 92 jobID2, err := runner.Add(runArgs) 93 So(err, ShouldBeNil) 94 So(jobID2, ShouldEqual, jobID) 95 err = runner.WaitForRunning(jobID) 96 So(err, ShouldBeNil) 97 status, err = runner.Wait(jobID) 98 So(err, ShouldBeNil) 99 So(status, ShouldEqual, WRJobStatusComplete) 100 101 runArgs, _ = uniqueRunArgs("false", "") 102 jobID, err = runner.Add(runArgs) 103 So(err, ShouldBeNil) 104 err = runner.WaitForRunning(jobID) 105 So(err, ShouldBeNil) 106 status, err = runner.Wait(jobID) 107 So(err, ShouldBeNil) 108 So(status, ShouldEqual, WRJobStatusBuried) 109 }) 110 111 Convey("WaitForRunning returns when the build starts running", t, func() { 112 cmd := exec.Command("wr", "limit", "--deployment", runner.deployment, "-g", "limited:0") //nolint:gosec 113 err := cmd.Run() 114 So(err, ShouldBeNil) 115 116 runArgs, _ := uniqueRunArgs("sleep 2s", "limited") 117 jobID, err := runner.Add(runArgs) 118 So(err, ShouldBeNil) 119 120 runningCh := make(chan time.Time) 121 errCh := make(chan error, 1) 122 go func() { 123 errCh <- runner.WaitForRunning(jobID) 124 runningCh <- time.Now() 125 }() 126 127 <-time.After(100 * time.Millisecond) 128 129 startTime := time.Now() 130 cmd = exec.Command("wr", "limit", "--deployment", runner.deployment, "-g", "limited:1") //nolint:gosec 131 err = cmd.Run() 132 So(err, ShouldBeNil) 133 134 status, err := runner.Wait(jobID) 135 So(err, ShouldBeNil) 136 endTime := time.Now() 137 So(status, ShouldEqual, WRJobStatusComplete) 138 139 So(<-errCh, ShouldBeNil) 140 runningTime := <-runningCh 141 So(runningTime, ShouldHappenAfter, startTime) 142 So(runningTime, ShouldHappenBefore, endTime.Add(-1*time.Second)) 143 }) 144 } 145 146 func uniqueRunArgs(cmd, limitGrp string) (string, string) { 147 b := make([]byte, 16) 148 n, err := rand.Read(b) 149 So(n, ShouldEqual, 16) 150 So(err, ShouldBeNil) 151 152 repGrp := fmt.Sprintf("%x", b) 153 154 return `{"cmd":"` + cmd + ` && echo ` + repGrp + `", "rep_grp": "` + repGrp + 155 `", "limit_grps": ["` + limitGrp + `"], "retries": 0}`, repGrp 156 } 157 158 func getWRStatus(deployment, repGrp string) string { 159 cmd := exec.Command("wr", "status", "--deployment", deployment, "-i", repGrp, "-o", "json") 160 161 var stdout, stderr bytes.Buffer 162 cmd.Stdout = &stdout 163 cmd.Stderr = &stderr 164 165 err := cmd.Run() 166 So(err, ShouldBeNil) 167 So(stderr.String(), ShouldBeBlank) 168 169 return stdout.String() 170 }