Skip to content

Commit c874621

Browse files
authored
test: Decouple simulator download from external tools, cache downloads (#48)
Signed-off-by: Bartek Mucha <muchzill4@gmail.com>
1 parent 7eada61 commit c874621

File tree

8 files changed

+168
-32
lines changed

8 files changed

+168
-32
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ coverage.out
1010
# binaries
1111
/containerd-shim-remoteproc-v1
1212
/remoteproc-runtime
13+
14+
# test downloads
15+
.downloads/

e2e/remoteproc/simulator.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
package remoteproc
22

33
import (
4+
"archive/tar"
45
"bufio"
6+
"compress/gzip"
7+
"context"
58
"fmt"
69
"io"
10+
"net/http"
11+
"os"
712
"path/filepath"
13+
"runtime"
814
"strings"
15+
"sync"
916
"time"
1017

1118
"github.com/arm/remoteproc-runtime/e2e/limavm"
19+
"github.com/arm/remoteproc-runtime/e2e/repo"
1220
"github.com/arm/remoteproc-runtime/e2e/runner"
1321
)
1422

@@ -123,3 +131,143 @@ func (r *Simulator) DeviceDir() string {
123131
r.rootDir, "sys", "class", "remoteproc", fmt.Sprintf("remoteproc%d", r.index),
124132
)
125133
}
134+
135+
var downloadLocks sync.Map
136+
137+
func DownloadSimulator(ctx context.Context) (string, error) {
138+
const version = "v0.0.8"
139+
arch := runtime.GOARCH
140+
141+
cacheDir := filepath.Join(repo.MustFindRootDir(), ".downloads")
142+
extractDir := filepath.Join(cacheDir, arch, version)
143+
executablePath := filepath.Join(extractDir, "remoteproc-simulator")
144+
145+
if fileExists(executablePath) {
146+
return executablePath, nil
147+
}
148+
149+
lockKey := executablePath
150+
mu, _ := downloadLocks.LoadOrStore(lockKey, &sync.Mutex{})
151+
lock := mu.(*sync.Mutex)
152+
153+
lock.Lock()
154+
defer lock.Unlock()
155+
156+
fmt.Printf("Downloading remoteproc-simulator %s for %s...\n", version, arch)
157+
158+
if err := os.MkdirAll(extractDir, 0o755); err != nil {
159+
return "", fmt.Errorf("failed to create cache directory: %w", err)
160+
}
161+
162+
versionWithoutV := strings.TrimPrefix(version, "v")
163+
assetName := fmt.Sprintf("remoteproc-simulator_%s_linux_%s.tar.gz", versionWithoutV, arch)
164+
assetURL := fmt.Sprintf("https://github.com/arm/remoteproc-simulator/releases/download/%s/%s", version, assetName)
165+
archivePath := filepath.Join(extractDir, assetName)
166+
167+
if err := downloadFile(ctx, assetURL, archivePath); err != nil {
168+
return "", fmt.Errorf("failed to download file: %w", err)
169+
}
170+
171+
if err := extractTarGz(archivePath, extractDir); err != nil {
172+
return "", fmt.Errorf("failed to extract archive: %w", err)
173+
}
174+
175+
_ = os.Remove(archivePath)
176+
177+
if err := os.Chmod(executablePath, 0o755); err != nil {
178+
return "", fmt.Errorf("failed to make file executable: %w", err)
179+
}
180+
181+
return executablePath, nil
182+
}
183+
184+
func downloadFile(ctx context.Context, url, targetPath string) error {
185+
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
186+
if err != nil {
187+
return err
188+
}
189+
190+
client := &http.Client{Timeout: 5 * time.Minute}
191+
resp, err := client.Do(req)
192+
if err != nil {
193+
return err
194+
}
195+
defer func() { _ = resp.Body.Close() }()
196+
197+
if resp.StatusCode != http.StatusOK {
198+
body, _ := io.ReadAll(resp.Body)
199+
return fmt.Errorf("download returned status %d: %s", resp.StatusCode, string(body))
200+
}
201+
202+
tmpPath := targetPath + ".tmp"
203+
out, err := os.Create(tmpPath)
204+
if err != nil {
205+
return err
206+
}
207+
defer func() { _ = out.Close() }()
208+
209+
if _, err := io.Copy(out, resp.Body); err != nil {
210+
_ = os.Remove(tmpPath)
211+
return err
212+
}
213+
214+
if err := out.Close(); err != nil {
215+
_ = os.Remove(tmpPath)
216+
return err
217+
}
218+
219+
return os.Rename(tmpPath, targetPath)
220+
}
221+
222+
func extractTarGz(archivePath, extractDir string) error {
223+
f, err := os.Open(archivePath)
224+
if err != nil {
225+
return err
226+
}
227+
defer func() { _ = f.Close() }()
228+
229+
gzr, err := gzip.NewReader(f)
230+
if err != nil {
231+
return err
232+
}
233+
defer func() { _ = gzr.Close() }()
234+
235+
tr := tar.NewReader(gzr)
236+
237+
for {
238+
header, err := tr.Next()
239+
if err == io.EOF {
240+
break
241+
}
242+
if err != nil {
243+
return err
244+
}
245+
246+
if header.Typeflag == tar.TypeDir {
247+
continue
248+
}
249+
250+
target := filepath.Join(extractDir, header.Name)
251+
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
252+
return err
253+
}
254+
255+
out, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(header.Mode))
256+
if err != nil {
257+
return err
258+
}
259+
260+
if _, err := io.Copy(out, tr); err != nil {
261+
_ = out.Close()
262+
return err
263+
}
264+
_ = out.Close()
265+
}
266+
267+
return nil
268+
}
269+
270+
func fileExists(path string) bool {
271+
_, err := os.Stat(path)
272+
return err == nil
273+
}

e2e/repo/bin.go

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"os"
66
"os/exec"
77
"path/filepath"
8-
"runtime"
98
)
109

1110
func BuildRuntimeBin(binOutDir string, rootPathPrefix string, env map[string]string) (string, error) {
@@ -73,28 +72,3 @@ func BuildBothBins(binOutDir string, rootPathPrefix string, env map[string]strin
7372

7473
return []string{runtime, shim}, nil
7574
}
76-
77-
func GetRemoteprocSimulator(binOutDir string) (string, error) {
78-
const repoDirName = "remoteproc-simulator"
79-
const version = "0.0.8"
80-
81-
artifactURL := fmt.Sprintf(
82-
"https://github.com/arm/remoteproc-simulator/releases/download/v%s/remoteproc-simulator_%s_linux_%s.tar.gz",
83-
version,
84-
version,
85-
runtime.GOARCH,
86-
)
87-
88-
downloader := exec.Command("curl", "-L", "-o", filepath.Join(binOutDir, "simulator.tar.gz"), artifactURL)
89-
if out, err := downloader.CombinedOutput(); err != nil {
90-
return "", fmt.Errorf("failed to download remoteproc-simulator: %s\n%s", err, out)
91-
}
92-
93-
extractor := exec.Command("tar", "-xzf", filepath.Join(binOutDir, "simulator.tar.gz"), "-C", binOutDir)
94-
if out, err := extractor.CombinedOutput(); err != nil {
95-
return "", fmt.Errorf("failed to extract remoteproc-simulator: %s\n%s", err, out)
96-
}
97-
98-
simulatorPath := filepath.Join(binOutDir, repoDirName)
99-
return simulatorPath, nil
100-
}

e2e/runtime_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package e2e
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
67
"os"
@@ -31,9 +32,8 @@ func TestRuntime(t *testing.T) {
3132
installedRuntime, err := vm.InstallBin(runtimeBin)
3233
require.NoError(t, err)
3334

34-
simulatorBin, err := repo.GetRemoteprocSimulator(t.TempDir())
35+
simulatorBin, err := remoteproc.DownloadSimulator(context.Background())
3536
require.NoError(t, err)
36-
3737
installedSimulator, err := vm.InstallBin(simulatorBin)
3838
require.NoError(t, err)
3939

e2e/runtime_via_podman_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package e2e
22

33
import (
4+
"context"
45
"fmt"
56
"path/filepath"
67
"strings"
@@ -27,9 +28,9 @@ func TestPodman(t *testing.T) {
2728

2829
installedRuntimeBin, err := vm.InstallBin(runtimeBin)
2930
require.NoError(t, err)
30-
simulatorBin, err := repo.GetRemoteprocSimulator(t.TempDir())
31-
require.NoError(t, err)
3231

32+
simulatorBin, err := remoteproc.DownloadSimulator(context.Background())
33+
require.NoError(t, err)
3334
installedSimulator, err := vm.InstallBin(simulatorBin)
3435
require.NoError(t, err)
3536

e2e/shim_via_docker_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package e2e
22

33
import (
4+
"context"
45
"fmt"
56
"path/filepath"
67
"strconv"
@@ -31,7 +32,8 @@ func TestDocker(t *testing.T) {
3132
_, err := vm.InstallBin(bin)
3233
require.NoError(t, err)
3334
}
34-
simulatorBin, err := repo.GetRemoteprocSimulator(t.TempDir())
35+
36+
simulatorBin, err := remoteproc.DownloadSimulator(context.Background())
3537
require.NoError(t, err)
3638
installedSimulator, err := vm.InstallBin(simulatorBin)
3739
require.NoError(t, err)

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ require (
3131
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
3232
github.com/google/go-cmp v0.7.0 // indirect
3333
github.com/inconshreveable/mousetrap v1.1.0 // indirect
34+
github.com/kr/pretty v0.1.0 // indirect
3435
github.com/mdlayher/socket v0.5.1 // indirect
3536
github.com/mdlayher/vsock v1.2.1 // indirect
3637
github.com/moby/sys/mountinfo v0.7.2 // indirect
@@ -47,5 +48,6 @@ require (
4748
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect
4849
google.golang.org/grpc v1.76.0 // indirect
4950
google.golang.org/protobuf v1.36.10 // indirect
51+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
5052
gopkg.in/yaml.v3 v3.0.1 // indirect
5153
)

go.sum

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
7878
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
7979
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
8080
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
81+
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
82+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
83+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
84+
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
85+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
8186
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
8287
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
8388
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
@@ -212,8 +217,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
212217
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
213218
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
214219
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
215-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
216220
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
221+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
222+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
217223
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
218224
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
219225
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)