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
package cloudprovider
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
)
const snapshotMode = 0o600
var ErrNoSnapshot = errors.New("cloudprovider: the instance snapshot does not exist")
var ErrNoSnapshotPath = errors.New(
"cloudprovider: the instance snapshot needs a path. Without one the provider would write an " +
"instance list no cloud controller manager can read, and every Node would read as unknown")
func WriteSnapshot(path string, instances []Instance) error {
if path == "" {
return ErrNoSnapshotPath
}
if instances == nil {
instances = []Instance{}
}
body, err := json.Marshal(instances)
if err != nil {
return fmt.Errorf("cloudprovider: rendering the instance snapshot for %s: %w", path, err)
}
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0o700); err != nil {
return fmt.Errorf("cloudprovider: the directory %s holding the instance snapshot: %w", dir, err)
}
if current, err := os.ReadFile(path); err == nil && string(current) == string(body) {
return nil
}
tmp, err := os.CreateTemp(dir, filepath.Base(path)+".")
if err != nil {
return fmt.Errorf("cloudprovider: a temporary file beside %s: %w", path, err)
}
defer func() { _ = os.Remove(tmp.Name()) }()
if err := tmp.Chmod(snapshotMode); err != nil {
_ = tmp.Close()
return fmt.Errorf("cloudprovider: the mode of %s: %w", tmp.Name(), err)
}
if _, err := tmp.Write(body); err != nil {
_ = tmp.Close()
return fmt.Errorf("cloudprovider: writing %s: %w", tmp.Name(), err)
}
if err := tmp.Close(); err != nil {
return fmt.Errorf("cloudprovider: closing %s: %w", tmp.Name(), err)
}
if err := os.Rename(tmp.Name(), path); err != nil {
return fmt.Errorf("cloudprovider: renaming %s over %s: %w", tmp.Name(), path, err)
}
return nil
}