281 lines
6 KiB
Go
281 lines
6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"slices"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/ghodss/yaml"
|
|
"dario.cat/mergo"
|
|
"github.com/otiai10/copy"
|
|
)
|
|
|
|
type Config struct {
|
|
System SystemConfig
|
|
Networking NetworkingConfig
|
|
Services map[string]ServiceConfig
|
|
}
|
|
|
|
type SystemConfig struct {
|
|
Base string
|
|
Packages []string
|
|
SystemPackages []string `json:"system_packages"`
|
|
}
|
|
|
|
type NetworkingConfig struct {
|
|
}
|
|
|
|
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() {
|
|
// Initialize structs for scripted configurations
|
|
ScriptedInit()
|
|
|
|
// Read user config file
|
|
config, err := ParseConfig("goolinux.json")
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
// Read system "base" config file if it exists
|
|
if config.System.Base != "" {
|
|
fp := "services/" + config.System.Base + ".json"
|
|
|
|
systemConfig, err := ParseConfig(fp)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
// Merge base system config layer
|
|
mergo.Merge(&config, systemConfig)
|
|
}
|
|
|
|
// Parse user services
|
|
for name, service := range config.Services {
|
|
// If no provider specified, try system
|
|
if service.Provider == "" { service.Provider = "system" }
|
|
|
|
// Read service config file
|
|
fp := fmt.Sprintf("services/%s/%s.json",
|
|
name, service.Provider)
|
|
s, err := ParseServiceConfig(fp)
|
|
if err != nil {
|
|
switch err {
|
|
case ErrFileNotFound:
|
|
// 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
|
|
mergo.Merge(&s, ServiceConfig{
|
|
Config: make(map[string]interface{}),
|
|
})
|
|
mergo.Merge(&s, service, mergo.WithOverride)
|
|
config.Services[name] = s
|
|
|
|
|
|
// Scripted actions for the service
|
|
|
|
// 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 != "" {
|
|
port, err := s.GetHostPort()
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
fmt.Println("Error in service", name)
|
|
return
|
|
}
|
|
|
|
err = AddProxy(s.Proxy, port)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
fmt.Println("Error adding proxy for service", name)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle any special case (scripted) configs
|
|
{
|
|
// Expand the proxy list to Services.proxy.Config.scripted_proxy
|
|
config.Services["proxy"].Config["scripted_proxy"] = GetProxy()
|
|
}
|
|
|
|
// Generate service config templates
|
|
for name, s := range config.Services {
|
|
for _, f := range s.ConfigFiles {
|
|
f := strings.Split(f, ":")
|
|
|
|
// Read service config template file
|
|
fp := fmt.Sprintf("services/%s/%s", name, f[0])
|
|
t, err := os.ReadFile(fp)
|
|
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
|
|
{
|
|
var err error
|
|
|
|
err = os.MkdirAll("/config" + "/etc/apk/", 0555)
|
|
if err != nil {
|
|
fmt.Println("Error creating config directory.")
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
slices.Sort(config.System.Packages)
|
|
slices.Sort(config.System.SystemPackages)
|
|
|
|
config.System.Packages = append(
|
|
config.System.SystemPackages,
|
|
config.System.Packages...)
|
|
config.System.Packages = slices.Compact(
|
|
config.System.Packages)
|
|
|
|
p := strings.Join(config.System.Packages, "\n")
|
|
err = os.WriteFile("/config/etc/apk/world",[]byte(p), 0444)
|
|
if err != nil {
|
|
fmt.Println("Error creating package list")
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Copy any mandatory configuration files over before continuing.
|
|
// TODO: This will be replaced with a full copy later.
|
|
{
|
|
err := copy.Copy("/config/etc", "/etc")
|
|
if err != nil { fmt.Println(err) }
|
|
}
|
|
|
|
// Install and remove packages
|
|
{
|
|
fmt.Println("Installing packages")
|
|
args := []string{"add", "-u", "-l"}
|
|
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 updating 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
|
|
}
|
|
}
|
|
}
|