server.garden privileged automation agent (mirror of https://git.sequentialread.com/forest/rootsystem)
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.9 KiB
173 lines
4.9 KiB
package configuration |
|
|
|
import ( |
|
"encoding/json" |
|
"fmt" |
|
"io/ioutil" |
|
"os" |
|
"os/exec" |
|
"path/filepath" |
|
"runtime" |
|
"syscall" |
|
|
|
errors "git.sequentialread.com/forest/pkg-errors" |
|
) |
|
|
|
type Configuration struct { |
|
Host HostConfiguration |
|
ApplicationModules []string |
|
Terraform TerraformConfiguration |
|
ObjectStorage ObjectStorageConfiguration |
|
Credentials []Credential |
|
} |
|
|
|
type HostConfiguration struct { |
|
Name string |
|
} |
|
|
|
type TerraformConfiguration struct { |
|
GlobalModules []string |
|
LocalModules []string |
|
Variables map[string]string |
|
} |
|
|
|
type ObjectStorageConfiguration struct { |
|
Encryption string |
|
Backends []ObjectStorageBackend |
|
} |
|
|
|
type ObjectStorageBackend struct { |
|
Provider string |
|
Region string |
|
Name string |
|
} |
|
|
|
type Credential struct { |
|
Type string |
|
SSID string |
|
Username string |
|
Password string |
|
} |
|
|
|
const GANDI = "Gandi" |
|
const DIGITALOCEAN = "DigitalOcean" |
|
const DIGITALOCEAN_API_URL = "https://api.digitalocean.com" |
|
const GANDI_API_URL = "https://api.gandi.net" |
|
const AMAZON_S3 = "AmazonS3" |
|
const BACKBLAZE_B2 = "BackblazeB2" |
|
const OBJECT_STORAGE_PASSPHRASE = "ObjectStoragePassphrase" |
|
const APPLICATION_MODULES_PATH = "application-modules" |
|
const GLOBAL_TERRAFORM_PROJECT = "terraform-global" |
|
const LOCAL_TERRAFORM_PROJECT = "terraform-local" |
|
const TERRAFORM_MODULES = "terraform-modules" |
|
const ANSIBLE_ROLES = "ansible-roles" |
|
const ANSIBLE_PLAYBOOK_FILE_NAME = "playbook.yml" |
|
const TERRAFORM_PLAN_FILE_NAME = "terraform-plan-file" |
|
const ANSIBLE_WRAPPER_PATH = "ansible-wrapper" |
|
const DOCKER_SOCKET = "/var/run/docker.sock" |
|
const THRESHOLD_SOCKET = "/var/run/servergarden/threshold/threshold.sock" |
|
const CADDY_SOCKET = "/var/run/servergarden/caddy/caddy.sock" |
|
const CADDY_DATA = "/var/lib/servergarden/caddy/data/" |
|
const TERRAFORM_APPLY_STATUS_UPDATE_INTERVAL_SECONDS = 5 |
|
|
|
func GET_ANSIBLE_WRAPPER_FILES() []string { |
|
return []string{ |
|
"ansible-playbook-wrapper", |
|
"callback_plugins", |
|
"ansible.cfg", |
|
} |
|
} |
|
|
|
const SSH_KEYS_PATH = "ssh" |
|
const TERRAFORM_STATE_SERVER_PORT_NUMBER = 6471 |
|
|
|
func LoadConfiguration() (*Configuration, string, error) { |
|
|
|
if runtime.GOOS == "windows" { |
|
return nil, "", errors.New("windows operating system is not supported.") |
|
} |
|
|
|
workingDirectory, err := os.Getwd() |
|
if err != nil { |
|
return nil, "", errors.Wrap(err, "can't os.Getwd()") |
|
} |
|
executableDirectory, err := getCurrentExecDir() |
|
if err != nil { |
|
return nil, "", errors.Wrap(err, "can't getCurrentExecDir()") |
|
} |
|
|
|
configFileLocation1 := filepath.Join(executableDirectory, "config.json") |
|
configFileLocation2 := filepath.Join(workingDirectory, "config.json") |
|
|
|
configFileLocation := configFileLocation1 |
|
configFileStat, err := os.Stat(configFileLocation) |
|
workingDirectoryToReturn := executableDirectory |
|
if err != nil || !configFileStat.Mode().IsRegular() { |
|
configFileLocation = configFileLocation2 |
|
configFileStat, err = os.Stat(configFileLocation) |
|
workingDirectoryToReturn = workingDirectory |
|
} |
|
if err != nil || !configFileStat.Mode().IsRegular() { |
|
return nil, workingDirectoryToReturn, fmt.Errorf("no config file. checked %s and %s", configFileLocation1, configFileLocation2) |
|
} |
|
|
|
// configFileUid, err := getUID(configFileStat) |
|
// if err != nil { |
|
// return nil, errors.Wrapf(err, "can't getUID() config file %s", configFileLocation) |
|
// } |
|
// if configFileUid != 0 { |
|
// return nil, fmt.Errorf("can't start rootsystem: the config file %s is not owned by root.", configFileLocation) |
|
// } |
|
|
|
// ownerReadWriteOnlyPermissionsOctal := "600" |
|
// configFilePermissionsOctal := fmt.Sprintf("%o", configFileStat.Mode().Perm()) |
|
// if configFilePermissionsOctal != ownerReadWriteOnlyPermissionsOctal { |
|
// return nil, fmt.Errorf( |
|
// "can't start rootsystem: the config file %s had permissions %s. expected %s. %s", |
|
// configFileLocation, |
|
// configFilePermissionsOctal, |
|
// ownerReadWriteOnlyPermissionsOctal, |
|
// "(config file should only be readable and writable by the owner, aka the root user)", |
|
// ) |
|
// } |
|
|
|
jsonBytes, err := ioutil.ReadFile(configFileLocation) |
|
if err != nil { |
|
return nil, workingDirectoryToReturn, errors.Wrap(err, "can't read config file") |
|
} |
|
|
|
var config Configuration |
|
err = json.Unmarshal(jsonBytes, &config) |
|
if err != nil { |
|
return nil, workingDirectoryToReturn, errors.Wrap(err, "can't json.Unmarshal config file") |
|
} |
|
|
|
return &config, workingDirectoryToReturn, nil |
|
} |
|
|
|
func getUID(fileInfo os.FileInfo) (int, error) { |
|
stat, ok := fileInfo.Sys().(*syscall.Stat_t) |
|
if !ok { |
|
return -1, fmt.Errorf("can't cast os.Stat(\"%s\").Sys() to *syscall.Stat_t") |
|
} |
|
|
|
return int(stat.Uid), nil |
|
} |
|
|
|
func getCurrentExecDir() (dir string, err error) { |
|
path, err := exec.LookPath(os.Args[0]) |
|
if err != nil { |
|
fmt.Printf("exec.LookPath(%s) returned %s\n", os.Args[0], err) |
|
return "", err |
|
} |
|
|
|
absPath, err := filepath.Abs(path) |
|
if err != nil { |
|
fmt.Printf("filepath.Abs(%s) returned %s\n", path, err) |
|
return "", err |
|
} |
|
|
|
dir = filepath.Dir(absPath) |
|
|
|
return dir, nil |
|
}
|
|
|