~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
57 lines · 1.7 KB Raw History
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
}