-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhttp_downloader.go
110 lines (89 loc) · 2.55 KB
/
http_downloader.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
// Copyright (c) 2018-2022, R.I. Pienaar and the Choria Project contributors
//
// SPDX-License-Identifier: Apache-2.0
package updater
import (
"compress/bzip2"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
// HTTPDownloader downloads releases from a normal HTTP(S) server
//
// The update directory structure is:
//
// root/<Version>/<OperatingSystem>/<Architecture>/release.json
type HTTPDownloader struct {
cfg *Config
}
// Configure implements Downloader
func (h *HTTPDownloader) Configure(c *Config) error {
h.cfg = c
return nil
}
// FetchSpec implements Downloader
func (h *HTTPDownloader) FetchSpec() (spec *Spec, err error) {
c := h.cfg
specuri := fmt.Sprintf("%s/%s/%s/%s/release.json", c.SourceRepo, c.Version, c.OperatingSystem, c.Architecture)
resp, err := http.Get(specuri)
if err != nil {
return nil, fmt.Errorf("could not download release spec: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("could not fetch release from %s: %s", specuri, resp.Status)
}
specj, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("could not read repo response: %s", err)
}
spec = &Spec{}
err = json.Unmarshal(specj, spec)
if err != nil {
return nil, fmt.Errorf("could not parse spec %s: %s", specuri, err)
}
spec.BinaryURI, err = url.Parse(fmt.Sprintf("%s/%s/%s/%s/%s", c.SourceRepo, c.Version, c.OperatingSystem, c.Architecture, spec.BinaryPath))
if err != nil {
return nil, fmt.Errorf("could not construct full path to the binary: %s", err)
}
return spec, nil
}
// FetchBinary implements Downloader
func (h *HTTPDownloader) FetchBinary(spec *Spec, target string) error {
stat, err := os.Stat(h.cfg.TargetFile)
if err != nil {
return err
}
outf, err := os.Create(target)
if err != nil {
return err
}
defer outf.Close()
h.cfg.Log.Printf("Fetching %s", spec.BinaryURI)
resp, err := http.Get(spec.BinaryURI.String())
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("could not download binary: %s", resp.Status)
}
n, err := io.Copy(outf, bzip2.NewReader(resp.Body))
if err != nil {
return fmt.Errorf("could not save file: %s", err)
}
h.cfg.Log.Printf("Fetched %d bytes from %s", n, spec.BinaryURI.String())
tf := fmt.Sprintf("%s.new", h.cfg.TargetFile)
err = os.Rename(outf.Name(), tf)
if err != nil {
return fmt.Errorf("could not move temporary file to taget: %s", err)
}
err = os.Chmod(tf, stat.Mode())
if err != nil {
return fmt.Errorf("could not set new file modes: %s", err)
}
return nil
}