Skip to content

Commit 04414e2

Browse files
Check if a signed URL is specified and use it download tools (#953)
* Wrap v2 tools install function inside tools.Download * Download tools defaulting to the replace behaviour * Improve archive renamer and fix failing tests * Find the correct tool and system when `version=latest` is specified * Reintroduce caching option when downloading tools
1 parent b02967e commit 04414e2

File tree

6 files changed

+142
-211
lines changed

6 files changed

+142
-211
lines changed

Diff for: tools/download.go

+13-157
Original file line numberDiff line numberDiff line change
@@ -16,43 +16,18 @@
1616
package tools
1717

1818
import (
19-
"bytes"
2019
"context"
21-
"crypto/sha256"
22-
"encoding/hex"
23-
"encoding/json"
2420
"errors"
25-
"fmt"
26-
"io"
27-
"net/http"
2821
"os"
2922
"os/exec"
3023
"path/filepath"
3124
"runtime"
3225

26+
"github.com/arduino/arduino-create-agent/gen/tools"
27+
"github.com/arduino/arduino-create-agent/utilities"
3328
"github.com/arduino/arduino-create-agent/v2/pkgs"
34-
"github.com/arduino/go-paths-helper"
35-
"github.com/blang/semver"
36-
"github.com/codeclysm/extract/v3"
3729
)
3830

39-
// public vars to allow override in the tests
40-
var (
41-
OS = runtime.GOOS
42-
Arch = runtime.GOARCH
43-
)
44-
45-
func pathExists(path string) bool {
46-
_, err := os.Stat(path)
47-
if err == nil {
48-
return true
49-
}
50-
if os.IsNotExist(err) {
51-
return false
52-
}
53-
return true
54-
}
55-
5631
// Download will parse the index at the indexURL for the tool to download.
5732
// It will extract it in a folder in .arduino-create, and it will update the
5833
// Installed map.
@@ -70,97 +45,21 @@ func pathExists(path string) bool {
7045
// if it already exists.
7146
func (t *Tools) Download(pack, name, version, behaviour string) error {
7247

73-
body, err := t.index.Read()
74-
if err != nil {
75-
return err
76-
}
77-
78-
var data pkgs.Index
79-
json.Unmarshal(body, &data)
80-
81-
// Find the tool by name
82-
correctTool, correctSystem := findTool(pack, name, version, data)
83-
84-
if correctTool.Name == "" || correctSystem.URL == "" {
85-
t.logger("We couldn't find a tool with the name " + name + " and version " + version + " packaged by " + pack)
86-
return nil
87-
}
88-
89-
key := correctTool.Name + "-" + correctTool.Version
90-
91-
// Check if it already exists
92-
if behaviour == "keep" {
93-
location, ok := t.getMapValue(key)
94-
if ok && pathExists(location) {
95-
// overwrite the default tool with this one
96-
t.setMapValue(correctTool.Name, location)
97-
t.logger("The tool is already present on the system")
98-
return t.writeMap()
99-
}
100-
}
101-
102-
// Download the tool
103-
t.logger("Downloading tool " + name + " from " + correctSystem.URL)
104-
resp, err := http.Get(correctSystem.URL)
48+
tool := pkgs.New(t.index, t.directory.String(), behaviour)
49+
_, err := tool.Install(context.Background(), &tools.ToolPayload{Name: name, Version: version, Packager: pack})
10550
if err != nil {
10651
return err
10752
}
108-
defer resp.Body.Close()
109-
110-
// Read the body
111-
body, err = io.ReadAll(resp.Body)
112-
if err != nil {
113-
return err
114-
}
115-
116-
// Checksum
117-
checksum := sha256.Sum256(body)
118-
checkSumString := "SHA-256:" + hex.EncodeToString(checksum[:sha256.Size])
119-
120-
if checkSumString != correctSystem.Checksum {
121-
return errors.New("checksum doesn't match")
122-
}
123-
124-
tempPath := paths.TempDir()
125-
// Create a temporary dir to extract package
126-
if err := tempPath.MkdirAll(); err != nil {
127-
return fmt.Errorf("creating temp dir for extraction: %s", err)
128-
}
129-
tempDir, err := tempPath.MkTempDir("package-")
130-
if err != nil {
131-
return fmt.Errorf("creating temp dir for extraction: %s", err)
132-
}
133-
defer tempDir.RemoveAll()
13453

135-
t.logger("Unpacking tool " + name)
136-
ctx := context.Background()
137-
reader := bytes.NewReader(body)
138-
// Extract into temp directory
139-
if err := extract.Archive(ctx, reader, tempDir.String(), nil); err != nil {
140-
return fmt.Errorf("extracting archive: %s", err)
141-
}
142-
143-
location := t.directory.Join(pack, correctTool.Name, correctTool.Version)
144-
err = location.RemoveAll()
54+
path := filepath.Join(pack, name, version)
55+
safePath, err := utilities.SafeJoin(t.directory.String(), path)
14556
if err != nil {
14657
return err
14758
}
14859

149-
// Check package content and find package root dir
150-
root, err := findPackageRoot(tempDir)
151-
if err != nil {
152-
return fmt.Errorf("searching package root dir: %s", err)
153-
}
154-
155-
if err := root.Rename(location); err != nil {
156-
if err := root.CopyDirTo(location); err != nil {
157-
return fmt.Errorf("moving extracted archive to destination dir: %s", err)
158-
}
159-
}
160-
16160
// if the tool contains a post_install script, run it: it means it is a tool that needs to install drivers
16261
// AFAIK this is only the case for the windows-driver tool
163-
err = t.installDrivers(location.String())
62+
err = t.installDrivers(safePath)
16463
if err != nil {
16564
return err
16665
}
@@ -169,63 +68,20 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
16968
t.logger("Ensure that the files are executable")
17069

17170
// Update the tool map
172-
t.logger("Updating map with location " + location.String())
173-
174-
t.setMapValue(name, location.String())
175-
t.setMapValue(name+"-"+correctTool.Version, location.String())
176-
return t.writeMap()
177-
}
71+
t.logger("Updating map with location " + safePath)
17872

179-
func findPackageRoot(parent *paths.Path) (*paths.Path, error) {
180-
files, err := parent.ReadDir()
181-
if err != nil {
182-
return nil, fmt.Errorf("reading package root dir: %s", err)
183-
}
184-
files.FilterOutPrefix("__MACOSX")
73+
t.setMapValue(name, safePath)
74+
t.setMapValue(name+"-"+version, safePath)
18575

186-
// if there is only one dir, it is the root dir
187-
if len(files) == 1 && files[0].IsDir() {
188-
return files[0], nil
189-
}
190-
return parent, nil
191-
}
192-
193-
func findTool(pack, name, version string, data pkgs.Index) (pkgs.Tool, pkgs.System) {
194-
var correctTool pkgs.Tool
195-
correctTool.Version = "0.0"
196-
197-
for _, p := range data.Packages {
198-
if p.Name != pack {
199-
continue
200-
}
201-
for _, t := range p.Tools {
202-
if version != "latest" {
203-
if t.Name == name && t.Version == version {
204-
correctTool = t
205-
}
206-
} else {
207-
// Find latest
208-
v1, _ := semver.Make(t.Version)
209-
v2, _ := semver.Make(correctTool.Version)
210-
if t.Name == name && v1.Compare(v2) > 0 {
211-
correctTool = t
212-
}
213-
}
214-
}
215-
}
216-
217-
// Find the url based on system
218-
correctSystem := correctTool.GetFlavourCompatibleWith(OS, Arch)
219-
220-
return correctTool, correctSystem
76+
return nil
22177
}
22278

22379
func (t *Tools) installDrivers(location string) error {
22480
OkPressed := 6
22581
extension := ".bat"
22682
// add .\ to force locality
22783
preamble := ".\\"
228-
if OS != "windows" {
84+
if runtime.GOOS != "windows" {
22985
extension = ".sh"
23086
// add ./ to force locality
23187
preamble = "./"
@@ -237,7 +93,7 @@ func (t *Tools) installDrivers(location string) error {
23793
os.Chdir(location)
23894
t.logger(preamble + "post_install" + extension)
23995
oscmd := exec.Command(preamble + "post_install" + extension)
240-
if OS != "linux" {
96+
if runtime.GOOS != "linux" {
24197
// spawning a shell could be the only way to let the user type his password
24298
TellCommandNotToSpawnShell(oscmd)
24399
}

Diff for: tools/download_test.go

+13-11
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ func TestDownloadCorrectPlatform(t *testing.T) {
4242
{"linux", "arm", "arm-linux-gnueabihf"},
4343
}
4444
defer func() {
45-
OS = runtime.GOOS // restore `runtime.OS`
46-
Arch = runtime.GOARCH // restore `runtime.ARCH`
45+
pkgs.OS = runtime.GOOS // restore `runtime.OS`
46+
pkgs.Arch = runtime.GOARCH // restore `runtime.ARCH`
4747
}()
4848
testIndex := paths.New("testdata", "test_tool_index.json")
4949
buf, err := testIndex.ReadFile()
@@ -54,10 +54,11 @@ func TestDownloadCorrectPlatform(t *testing.T) {
5454
require.NoError(t, err)
5555
for _, tc := range testCases {
5656
t.Run(tc.hostOS+tc.hostArch, func(t *testing.T) {
57-
OS = tc.hostOS // override `runtime.OS` for testing purposes
58-
Arch = tc.hostArch // override `runtime.ARCH` for testing purposes
57+
pkgs.OS = tc.hostOS // override `runtime.OS` for testing purposes
58+
pkgs.Arch = tc.hostArch // override `runtime.ARCH` for testing purposes
5959
// Find the tool by name
60-
correctTool, correctSystem := findTool("arduino-test", "arduino-fwuploader", "2.2.2", data)
60+
correctTool, correctSystem, found := pkgs.FindTool("arduino-test", "arduino-fwuploader", "2.2.2", data)
61+
require.True(t, found)
6162
require.NotNil(t, correctTool)
6263
require.NotNil(t, correctSystem)
6364
require.Equal(t, correctTool.Name, "arduino-fwuploader")
@@ -78,8 +79,8 @@ func TestDownloadFallbackPlatform(t *testing.T) {
7879
{"windows", "amd64", "i686-mingw32"},
7980
}
8081
defer func() {
81-
OS = runtime.GOOS // restore `runtime.OS`
82-
Arch = runtime.GOARCH // restore `runtime.ARCH`
82+
pkgs.OS = runtime.GOOS // restore `runtime.OS`
83+
pkgs.Arch = runtime.GOARCH // restore `runtime.ARCH`
8384
}()
8485
testIndex := paths.New("testdata", "test_tool_index.json")
8586
buf, err := testIndex.ReadFile()
@@ -90,10 +91,11 @@ func TestDownloadFallbackPlatform(t *testing.T) {
9091
require.NoError(t, err)
9192
for _, tc := range testCases {
9293
t.Run(tc.hostOS+tc.hostArch, func(t *testing.T) {
93-
OS = tc.hostOS // override `runtime.OS` for testing purposes
94-
Arch = tc.hostArch // override `runtime.ARCH` for testing purposes
94+
pkgs.OS = tc.hostOS // override `runtime.OS` for testing purposes
95+
pkgs.Arch = tc.hostArch // override `runtime.ARCH` for testing purposes
9596
// Find the tool by name
96-
correctTool, correctSystem := findTool("arduino-test", "arduino-fwuploader", "2.2.0", data)
97+
correctTool, correctSystem, found := pkgs.FindTool("arduino-test", "arduino-fwuploader", "2.2.0", data)
98+
require.True(t, found)
9799
require.NotNil(t, correctTool)
98100
require.NotNil(t, correctSystem)
99101
require.Equal(t, correctTool.Name, "arduino-fwuploader")
@@ -145,7 +147,7 @@ func TestDownload(t *testing.T) {
145147
if filePath.IsDir() {
146148
require.DirExists(t, filePath.String())
147149
} else {
148-
if OS == "windows" {
150+
if runtime.GOOS == "windows" {
149151
require.FileExists(t, filePath.String()+".exe")
150152
} else {
151153
require.FileExists(t, filePath.String())

Diff for: tools/tools.go

-12
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,6 @@ func (t *Tools) getMapValue(key string) (string, bool) {
7878
return value, ok
7979
}
8080

81-
// writeMap() writes installed map to the json file "installed.json"
82-
func (t *Tools) writeMap() error {
83-
t.mutex.RLock()
84-
defer t.mutex.RUnlock()
85-
b, err := json.Marshal(t.installed)
86-
if err != nil {
87-
return err
88-
}
89-
filePath := t.directory.Join("installed.json")
90-
return filePath.WriteFile(b)
91-
}
92-
9381
// readMap() reads the installed map from json file "installed.json"
9482
func (t *Tools) readMap() error {
9583
t.mutex.Lock()

Diff for: v2/http.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func Server(directory string, index *index.Resource) http.Handler {
4040
logAdapter := LogAdapter{Logger: logger}
4141

4242
// Mount tools
43-
toolsSvc := pkgs.New(index, directory)
43+
toolsSvc := pkgs.New(index, directory, "replace")
4444
toolsEndpoints := toolssvc.NewEndpoints(toolsSvc)
4545
toolsServer := toolssvr.New(toolsEndpoints, mux, CustomRequestDecoder, goahttp.ResponseEncoder, errorHandler(logger), nil)
4646
toolssvr.Mount(mux, toolsServer)

0 commit comments

Comments
 (0)