/
resource.go
132 lines (120 loc) · 3.2 KB
/
resource.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2023 Cisco Systems, Inc. and its affiliates
// All rights reserved.
package internal
import (
"context"
"crypto/sha256"
b64 "encoding/base64"
"fmt"
"net/url"
"os"
"path"
"path/filepath"
"github.com/rs/zerolog/log"
"github.com/carlmjohnson/requests"
)
// Resource represents an external resource to be downloaded.
type Resource struct {
Urls []string
Integrity string
Tags []string `toml:",omitempty"`
Filename string `toml:",omitempty"`
}
func NewResourceFromUrl(urls []string, algo string, tags []string, filename string) (*Resource, error) {
if len(urls) < 1 {
return nil, fmt.Errorf("empty url list")
}
url := urls[0]
ctx := context.Background()
path, err := GetUrltoTempFile(url, ctx)
if err != nil {
return nil, fmt.Errorf("failed to get url: %s", err)
}
defer os.Remove(path)
integrity, err := getIntegrityFromFile(path, algo)
if err != nil {
return nil, fmt.Errorf("failed to compute ressource integrity: %s", err)
}
return &Resource{Urls: urls, Integrity: integrity, Tags: tags, Filename: filename}, nil
}
// getUrl downloads the given resource and returns the path to it.
func getUrl(u string, fileName string, ctx context.Context) (string, error) {
_, err := url.Parse(u)
if err != nil {
return "", fmt.Errorf("invalid url '%s': %s", u, err)
}
log.Debug().Str("URL", u).Msg("Downloading")
err = requests.
URL(u).
Header("Accept", "*/*").
ToFile(fileName).
Fetch(ctx)
if err != nil {
return "", fmt.Errorf("failed to download '%s': %s", u, err)
}
log.Debug().Str("URL", u).Msg("Downloaded")
return fileName, nil
}
// GetUrlToDir downloads the given resource to the given directory and returns the path to it.
func GetUrlToDir(u string, targetDir string, ctx context.Context) (string, error) {
// create temporary name in the target directory.
h := sha256.New()
h.Write([]byte(u))
fileName := filepath.Join(targetDir, fmt.Sprintf(".%s", b64.StdEncoding.EncodeToString(h.Sum(nil))))
return getUrl(u, fileName, ctx)
}
// GetUrlWithDir downloads the given resource to a temporary file and returns the path to it.
func GetUrltoTempFile(u string, ctx context.Context) (string, error) {
file, err := os.CreateTemp("", "prefix")
if err != nil {
log.Fatal().Err(err)
}
fileName := file.Name()
return getUrl(u, fileName, ctx)
}
func (l *Resource) Download(dir string, mode os.FileMode, ctx context.Context) error {
ok := false
algo, err := getAlgoFromIntegrity(l.Integrity)
if err != nil {
return err
}
for _, u := range l.Urls {
// Download file in the target directory so that the call to
// os.Rename is atomic.
lpath, err := GetUrlToDir(u, dir, ctx)
if err != nil {
break
}
err = checkIntegrityFromFile(lpath, algo, l.Integrity, u)
if err != nil {
return err
}
localName := ""
if l.Filename != "" {
localName = l.Filename
} else {
localName = path.Base(u)
}
resPath := filepath.Join(dir, localName)
err = os.Rename(lpath, resPath)
if err != nil {
return err
}
if mode != NoFileMode {
os.Chmod(resPath, mode.Perm())
}
ok = true
}
if !ok {
return err
}
return nil
}
func (l *Resource) Contains(url string) bool {
for _, u := range l.Urls {
if u == url {
return true
}
}
return false
}