/
updater.go
136 lines (114 loc) · 3.45 KB
/
updater.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
133
134
135
136
package updater
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/go-logr/logr"
firewallv1 "github.com/metal-stack/firewall-controller/api/v1"
"github.com/metal-stack/v"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/record"
)
const (
binaryLocation = "/usr/local/bin/firewall-controller"
)
// UpdateToSpecVersion updates the firewall-controller binary to the version specified in the firewall spec.
func UpdateToSpecVersion(f firewallv1.Firewall, log logr.Logger, recorder record.EventRecorder) error {
if f.Spec.ControllerVersion == "" {
return nil
}
if f.Spec.ControllerVersion == v.Version {
log.Info("firewall-controller version is already in place", "version", v.Version)
return nil
}
_, err := url.Parse(f.Spec.ControllerURL)
if err != nil {
return err
}
recorder.Eventf(&f, corev1.EventTypeNormal, "Self-Reconcilation", "replacing firewall-controller version %s with version %s", v.Version, f.Spec.ControllerVersion)
binaryReader, checksum, err := FetchBinaryAndChecksum(f.Spec.ControllerURL)
if err != nil {
return fmt.Errorf("could not download binary or checksum for firewall-controller version %s, err: %w", f.Spec.ControllerVersion, err)
}
err = replaceBinary(binaryReader, checksum)
if err != nil {
return fmt.Errorf("could not replace firewall-controller with version %s, err: %w", f.Spec.ControllerVersion, err)
}
recorder.Eventf(&f, corev1.EventTypeNormal, "Self-Reconcilation", "replaced firewall-controller version %s with version %s successfully", v.Version, f.Spec.ControllerVersion)
// after a successful self-reconcilation of the firewall-controller binary we want to get restarted by exiting and letting systemd restart the process.
os.Exit(0)
return nil
}
func FetchBinaryAndChecksum(url string) (io.ReadCloser, string, error) {
checksum, err := slurpFile(url + ".sha256")
if err != nil {
return nil, "", fmt.Errorf("could not slurp checksum file at %s, err: %w", url, err)
}
//nolint:gosec,noctx
resp, err := http.Get(url)
if err != nil {
return nil, "", fmt.Errorf("could not download url %s, err: %w", url, err)
}
return resp.Body, checksum, nil
}
func replaceBinary(binaryReader io.ReadCloser, checksum string) error {
filename, err := copyToTempFile(binaryReader, binaryLocation)
if err != nil {
return err
}
err = validateChecksum(filename, checksum)
if err != nil {
return err
}
if err = os.Rename(filename, binaryLocation); err != nil {
return err
}
return nil
}
func copyToTempFile(binaryReader io.ReadCloser, filename string) (string, error) {
file, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename))
if err != nil {
return "", err
}
_, err = io.Copy(file, binaryReader)
if err != nil {
return "", err
}
defer binaryReader.Close()
err = os.Chmod(file.Name(), 0755)
if err != nil {
return "", err
}
return file.Name(), nil
}
func validateChecksum(filename string, checksum string) error {
bytes, err := os.ReadFile(filename)
if err != nil {
return err
}
hash := sha256.Sum256(bytes)
sum := string(hex.EncodeToString(hash[:]))
if sum != checksum {
return fmt.Errorf("checksum error")
}
return nil
}
func slurpFile(url string) (string, error) {
//nolint:gosec,noctx
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
content, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return strings.Split(string(content), " ")[0], nil
}