312 lines
6.8 KiB
Go
312 lines
6.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/hjson/hjson-go/v4"
|
|
"github.com/ghodss/yaml"
|
|
"dario.cat/mergo"
|
|
)
|
|
|
|
type Config struct {
|
|
System SystemConfig
|
|
Networking NetworkingConfig
|
|
Services map[string]ServiceConfig
|
|
}
|
|
|
|
type SystemConfig struct {
|
|
Base string
|
|
Packages []string
|
|
}
|
|
|
|
type NetworkingConfig struct {
|
|
proxy map[int]string
|
|
}
|
|
|
|
type ServiceConfig struct {
|
|
Enable string
|
|
|
|
Config map[string]interface{}
|
|
ExtraConfig string
|
|
|
|
Src string
|
|
Ports []string
|
|
Volumes []string
|
|
Proxy string
|
|
|
|
Provider string
|
|
Packages []string
|
|
ConfigFiles []string
|
|
}
|
|
|
|
func main() {
|
|
var err error
|
|
var config Config
|
|
|
|
// Read user config file
|
|
{
|
|
file, err := os.ReadFile("goolinux.json")
|
|
if err != nil {
|
|
fmt.Println("Cannot read user config file")
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
err = hjson.Unmarshal(file, &config)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Read system "base" config file if it exists
|
|
if config.System.Base != "" {
|
|
var systemConfig Config
|
|
path := "services/" + config.System.Base + ".json"
|
|
|
|
file, err := os.ReadFile(path)
|
|
if err != nil {
|
|
fmt.Println("Cannot read system base config file")
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
err = hjson.Unmarshal(file, &systemConfig)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
// Merge base system config layer
|
|
mergo.Merge(&config, systemConfig, mergo.WithOverride)
|
|
}
|
|
|
|
// Initialize any needed maps in the config
|
|
config.Networking.proxy = make(map[int]string)
|
|
|
|
// Parse user services
|
|
for name, service := range config.Services {
|
|
var b strings.Builder
|
|
s := ServiceConfig{
|
|
Config: make(map[string]interface{}),
|
|
}
|
|
|
|
// If no provider specified, try system
|
|
if service.Provider == "" { service.Provider = "system" }
|
|
|
|
// Read service config file
|
|
fmt.Fprintf(&b, "services/%s/%s.json", name, service.Provider)
|
|
c, err := os.ReadFile(b.String())
|
|
if err != nil {
|
|
switch err := err.(type) {
|
|
case *os.PathError:
|
|
// No system config to merge
|
|
// Rely solely on the user config
|
|
fmt.Println(" Warning: No system config for service: " + name)
|
|
continue
|
|
default:
|
|
fmt.Println("Error reading service config: ", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
{
|
|
// Parse service config
|
|
err = hjson.Unmarshal(c, &s)
|
|
if err != nil {
|
|
fmt.Println("Error parsing config file")
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
mergo.Merge(&s, service, mergo.WithOverride)
|
|
|
|
/*
|
|
// Initialize the config map if it wasn't already
|
|
if s.Config == map[string]interface{} {
|
|
s.Config = make(map[string]interface{})
|
|
}
|
|
*/
|
|
|
|
config.Services[name] = s
|
|
|
|
// Add service packages to the global list
|
|
config.System.Packages = append(config.System.Packages, s.Packages...)
|
|
|
|
// If a proxy definition exists, append it
|
|
if s.Proxy != "" {
|
|
ports := strings.Split(s.Ports[0], ":")
|
|
if len(ports) == 0 {
|
|
fmt.Println("Error: No port defined for proxy to", s.Proxy)
|
|
return
|
|
}
|
|
port, err := strconv.Atoi(ports[0])
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
fmt.Printf("Error: proxy for %s has an invalid port definition: %s\n", s.Proxy, ports)
|
|
return
|
|
}
|
|
|
|
conflict, exists := config.Networking.proxy[port]
|
|
if exists == true {
|
|
fmt.Println("Error: Conflicting proxy settings:")
|
|
fmt.Printf(" %s:%s\n", conflict, port)
|
|
fmt.Printf(" %s:%s\n", s.Proxy, port)
|
|
}
|
|
|
|
config.Networking.proxy[port] = s.Proxy
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle any special case (scripted) configs
|
|
{
|
|
// Expand Networking.proxy to Services.proxy.Config.scripted_proxy
|
|
type proxy struct {
|
|
Domain string
|
|
Port int
|
|
}
|
|
var p []proxy
|
|
for port, domain := range config.Networking.proxy {
|
|
p = append(p, proxy{Domain: domain, Port: port})
|
|
}
|
|
|
|
config.Services["proxy"].Config["scripted_proxy"] = p
|
|
}
|
|
|
|
// Generate service config templates
|
|
for name, s := range config.Services {
|
|
for _, f := range s.ConfigFiles {
|
|
var b strings.Builder
|
|
f := strings.Split(f, ":")
|
|
|
|
// Read service template file
|
|
fmt.Fprintf(&b, "services/%s/%s", name, f[0])
|
|
t, err := os.ReadFile(b.String())
|
|
if err != nil {
|
|
fmt.Println("No template for service: ", err)
|
|
return
|
|
}
|
|
|
|
tmpl, err := template.New(name).Parse(string(t))
|
|
if err != nil {
|
|
fmt.Println("Error reading template")
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
err = os.MkdirAll("/config" + path.Dir(f[1]), 0555)
|
|
if err != nil {
|
|
fmt.Println("Error creating config directory.")
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
fd, err := os.Create("/config" + f[1])
|
|
defer fd.Close()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
fmt.Println("No permission to create config file")
|
|
return
|
|
}
|
|
fd.Chmod(0444)
|
|
|
|
err = tmpl.Execute(fd, s)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert service configs from JSON to YAML
|
|
fmt.Println("Converting docker service configs to YAML")
|
|
servicesPath := "/config/services/docker/"
|
|
dirents, err := os.ReadDir(servicesPath)
|
|
for _, d := range dirents {
|
|
if d.IsDir() == true { continue }
|
|
if strings.HasSuffix(d.Name(), ".json") != true { continue }
|
|
f := servicesPath + d.Name()
|
|
|
|
j, err := os.ReadFile(f)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
y, err := yaml.JSONToYAML(j)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
fd, _ := os.Create(strings.TrimSuffix(f, ".json") + ".yaml")
|
|
defer fd.Close()
|
|
fd.Chmod(0444)
|
|
fd.Write(y)
|
|
}
|
|
|
|
// Parse package list
|
|
|
|
// Add extra packages needed
|
|
// ...
|
|
|
|
// Install packages
|
|
fmt.Println("Installing packages")
|
|
var installString = []string{"add", "--no-interactive", "--no-progress"}
|
|
var testArgs, args []string
|
|
var out []byte
|
|
testArgs = append(installString, "-s")
|
|
testArgs = append(testArgs, config.System.Packages...)
|
|
out, err = exec.Command("/sbin/apk", testArgs...).CombinedOutput()
|
|
if err != nil {
|
|
fmt.Println("===")
|
|
fmt.Println(err)
|
|
fmt.Println(string(out))
|
|
fmt.Println("===")
|
|
fmt.Println("Error preparing packages")
|
|
return
|
|
}
|
|
|
|
args = append(installString, config.System.Packages...)
|
|
out, err = exec.Command("/sbin/apk", args...).CombinedOutput()
|
|
if err != nil {
|
|
fmt.Println("===")
|
|
fmt.Println(err)
|
|
fmt.Println(string(out))
|
|
fmt.Println("===")
|
|
fmt.Println("Error installing packages")
|
|
return
|
|
}
|
|
|
|
// (Re)start services
|
|
|
|
// Handle docker services
|
|
fmt.Println("Starting docker services")
|
|
servicesPath = "/config/services/docker/"
|
|
dirents, err = os.ReadDir(servicesPath)
|
|
for _, d := range dirents {
|
|
if d.IsDir() == true { continue }
|
|
if strings.HasSuffix(d.Name(), ".yaml") != true { continue }
|
|
f := servicesPath + d.Name()
|
|
|
|
fmt.Println(" Starting:", d.Name())
|
|
var cmd = "/usr/bin/podman-compose"
|
|
var args = []string{"-f", f, "up", "-d"}
|
|
out, err := exec.Command(cmd, args...).CombinedOutput()
|
|
if err != nil {
|
|
fmt.Println("===")
|
|
fmt.Println(err)
|
|
fmt.Println(string(out))
|
|
fmt.Println("===")
|
|
fmt.Println("Error starting service:" + d.Name())
|
|
return
|
|
}
|
|
}
|
|
}
|