From eb50773656ef1df5d3355b904615a9ce2ec1be01 Mon Sep 17 00:00:00 2001 From: Philippe Caseiro Date: Mon, 27 Jun 2022 15:58:37 +0200 Subject: [PATCH] Try 05 --- Makefile | 6 +-- api/main.go | 10 ++-- data/config/test-services.json | 46 +++++++++++++++---- pkg/templater/main.go | 5 -- pkg/templater/packages.go | 73 ++++++++++++++++++++++++++++++ pkg/templater/repositories/apk.go | 57 +++++++++++++++++++++++ pkg/templater/repositories/deb.go | 46 +++++++++++++++++++ pkg/templater/repositories/helm.go | 23 ++++++++++ pkg/templater/repositories/main.go | 8 ++++ pkg/templater/services.go | 26 +++++++---- pkg/templater/system_services.go | 50 ++++++++++++++++++-- pkg/utils/os.go | 27 +++++++++++ 12 files changed, 344 insertions(+), 33 deletions(-) create mode 100644 pkg/templater/packages.go create mode 100644 pkg/templater/repositories/apk.go create mode 100644 pkg/templater/repositories/deb.go create mode 100644 pkg/templater/repositories/helm.go create mode 100644 pkg/templater/repositories/main.go create mode 100644 pkg/utils/os.go diff --git a/Makefile b/Makefile index 631899d..a8b6c47 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ LINT_ARGS ?= ./... DESTDIR ?= "/usr/local" bin: - GOOS=linux go build -o bin/templater-linux cmd/templater/main.go - GOOS=linux go build -o bin/bootstraper-linux cmd/bootstraper/main.go + GOOS=linux CGO_ENABLED=0 go build -o bin/templater-linux cmd/templater/main.go + GOOS=linux CGO_ENABLED=0 go build -o bin/bootstraper-linux cmd/bootstraper/main.go upx bin/templater-linux - upx bin/templaster-server + upx bin/bootstraper-linux install: cp bin/templater-linux $(DESTDIR)/bin/templater diff --git a/api/main.go b/api/main.go index 7c12c62..d0766f1 100644 --- a/api/main.go +++ b/api/main.go @@ -1,9 +1,6 @@ package api import ( - "net/http" - - "forge.cadoles.com/pcaseiro/templatefile/pkg/templater" "github.com/gin-gonic/gin" ) @@ -13,6 +10,12 @@ type Template struct { Config string } +func Generate(c *gin.Context) { + return +} + +/* + func Generate(c *gin.Context) { var template Template @@ -42,3 +45,4 @@ func Generate(c *gin.Context) { } } +*/ diff --git a/data/config/test-services.json b/data/config/test-services.json index 60ae047..a6e4ec3 100644 --- a/data/config/test-services.json +++ b/data/config/test-services.json @@ -1,11 +1,7 @@ { "Name": "LokiStack", "Global": { - "Vars": { - "EnableLoki": true, - "EnableGrafana": false, - "EnablePrometheus" : false - }, + "Vars": {}, "ConfigFiles": [ { "destination": "/etc/hosts", @@ -18,7 +14,6 @@ }, "Services": { "Loki": { - "EnabledBy": { "var": "EnableLoki", "value": true }, "ConfigFiles": [ { "destination": "/etc/loki/loki-local-config.yaml", @@ -28,6 +23,29 @@ "group": "grafana" } ], + "Repositories": { + "Grafana": { + "type": "helm", + "name": "grafana", + "url": "https://grafana.github.io/helm-charts", + "enabled": true + } + }, + "Packages": { + "loki": { + "name": "loki", + "action": "install" + }, + "promtail": { + "name": "loki-promtail", + "action": "install" + }, + "loki-helm": { + "type": "helm", + "name": "loki", + "repo": "grafana/loki-simple-scalable" + } + }, "Vars": { "AuthEnabled": false, "User": "loki", @@ -60,7 +78,6 @@ } }, "Grafana": { - "EnabledBy": { "var": "EnableGrafana", "value": true }, "ConfigFiles": [ { "destination": "/etc/grafana.ini", @@ -70,6 +87,12 @@ "group": "grafana" } ], + "Packages": { + "grafana": { + "name": "grafana", + "action": "install" + } + }, "Vars": { "AuthEnabled": false, "User": "toto", @@ -83,9 +106,12 @@ "shell": "/bin/nologin" } }, - "Daemon": { - "name": "grafana", - "enabled": true + "Daemons": { + "grafana": { + "name": "grafana", + "type": "auto", + "enabled": true + } } } } diff --git a/pkg/templater/main.go b/pkg/templater/main.go index 8e5adb6..b918772 100644 --- a/pkg/templater/main.go +++ b/pkg/templater/main.go @@ -10,11 +10,6 @@ import ( var CacheFilePath = "/var/cache/templater.db" -type SimpleCondition struct { - Var string `json:"var"` - Value bool `json:"value"` -} - type TemplaterConfig struct { Name string `json:"Name"` TemplateDirectory string `json:"TemplateDirectory"` diff --git a/pkg/templater/packages.go b/pkg/templater/packages.go new file mode 100644 index 0000000..c5b6d22 --- /dev/null +++ b/pkg/templater/packages.go @@ -0,0 +1,73 @@ +package templater + +import ( + "fmt" + "runtime" + + "forge.cadoles.com/pcaseiro/templatefile/pkg/utils" +) + +type SystemPackage struct { + Name string `json:"name"` + Type string `json:"type"` + Action string `json:"action"` + OS string `json:"os"` + Distribution string `json:"distribution"` +} + +func (p *SystemPackage) SetDistribution() error { + OSConfig, err := utils.ReadOSRelease() + if err != nil { + return err + } + + p.Distribution = OSConfig["ID_LIKE"] + return nil +} +func (p *SystemPackage) SetOS() error { + p.OS = runtime.GOOS + return nil +} + +func (p *SystemPackage) Manage() error { + var pkErr error + var stdErr []byte + + if p.OS == "" { + if err := p.SetOS(); err != nil { + return err + } + } + + if p.Distribution == "" { + if err := p.SetDistribution(); err != nil { + return err + } + } + + fmt.Printf("Processing %s package\n", p.Name) + switch os := p.Distribution; os { + case "debian", "ubuntu": + _, stdErr, pkErr = utils.RunSystemCommand("apt", "install", "-y", p.Name) + case "alpine": + _, stdErr, pkErr = utils.RunSystemCommand("apk", "add", p.Name) + case "redhat": + _, stdErr, pkErr = utils.RunSystemCommand("yum", "install", "-y", p.Name) + case "arch": + _, stdErr, pkErr = utils.RunSystemCommand("pacman", "-Suy", p.Name) + default: + pkErr = fmt.Errorf("Unsupported OS %s [%s]", p.OS, stdErr) + } + + if pkErr != nil { + var msg string + if len(stdErr) != 0 { + msg = string(stdErr) + } else { + msg = pkErr.Error() + } + return fmt.Errorf("Package %s, os %s, failed with error: %v", p.Name, p.OS, msg) + } + + return nil +} diff --git a/pkg/templater/repositories/apk.go b/pkg/templater/repositories/apk.go new file mode 100644 index 0000000..e8e3e8a --- /dev/null +++ b/pkg/templater/repositories/apk.go @@ -0,0 +1,57 @@ +package templater + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + + "forge.cadoles.com/pcaseiro/templatefile/pkg/utils" +) + +type APKRepository struct { + Name string `json:"name"` + Type string `json:"type"` + URL string `json:"url"` + Enabled bool `json:"enabled"` +} + +func (hr *APKRepository) Add() error { + data := fmt.Sprintf("%s", hr.URL) + file, err := os.OpenFile("/etc/apk/repositories", os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer file.Close() + if _, err := file.WriteString(data); err != nil { + return err + } + return nil +} + +func (hr *APKRepository) Update() error { + if _, stdErr, err := utils.RunSystemCommand("apk", "update"); err != nil { + return fmt.Errorf("%s [%s]", stdErr, err) + } + + return nil +} + +func (hr *APKRepository) Delete() error { + fileBytes, err := ioutil.ReadFile("/etc/apk/repositories") + if err != nil { + return err + } + lines := strings.Split(string(fileBytes), "\n") + for i, line := range lines { + + } + return nil +} +func (hr *APKRepository) Manage() error { + if hr.Enabled { + return hr.Add() + } else { + return hr.Delete() + } +} diff --git a/pkg/templater/repositories/deb.go b/pkg/templater/repositories/deb.go new file mode 100644 index 0000000..d0644c1 --- /dev/null +++ b/pkg/templater/repositories/deb.go @@ -0,0 +1,46 @@ +package templater + +import ( + "fmt" + "os" + + "forge.cadoles.com/pcaseiro/templatefile/pkg/utils" +) + +type DebRepository struct { + Name string `json:"name"` + Type string `json:"type"` + URL string `json:"url"` + Enabled bool `json:"enabled"` +} + +func (hr *DebRepository) Add() error { + //deb http://fr.archive.ubuntu.com/ubuntu/ focal main restricted + + data := fmt.Sprintf("deb %s", hr.URL) + if err := os.WriteFile("/etc/apt/source.list.d", []byte(data)); err != nil { + return err + } + + return nil +} + +func (hr *DebRepository) Update() error { + if _, stdErr, err := utils.RunSystemCommand("apt", "update", "-y"); err != nil { + return fmt.Errorf("%s [%s]", stdErr, err) + } + + return nil +} + +func (hr *DebRepository) Delete() error { + //TODO + return nil +} +func (hr *DebRepository) Manage() error { + if hr.Enabled { + return hr.Add() + } else { + return hr.Delete() + } +} diff --git a/pkg/templater/repositories/helm.go b/pkg/templater/repositories/helm.go new file mode 100644 index 0000000..3d7154e --- /dev/null +++ b/pkg/templater/repositories/helm.go @@ -0,0 +1,23 @@ +package templater + +type HelmRepository struct { + Name string `json:"name"` + Type string `json:"type"` + URL string `json:"url"` + Enabled bool `json:"enabled"` +} + +func (hr *HelmRepository) Add() error { + return nil +} + +func (hr *HelmRepository) Update() error { + return nil +} + +func (hr *HelmRepository) Delete() error { + return nil +} +func (hr *HelmRepository) Manage() error { + return nil +} diff --git a/pkg/templater/repositories/main.go b/pkg/templater/repositories/main.go new file mode 100644 index 0000000..cbe8f39 --- /dev/null +++ b/pkg/templater/repositories/main.go @@ -0,0 +1,8 @@ +package templater + +type PackageRepository interface { // création de L'interface Forme + Manage() error // signature de la méthode Perimetre() + Update() error + Add() error + Delete() error +} diff --git a/pkg/templater/services.go b/pkg/templater/services.go index d31a89b..e2a03a9 100644 --- a/pkg/templater/services.go +++ b/pkg/templater/services.go @@ -7,22 +7,32 @@ import ( ) type Service struct { - EnabledBy SimpleCondition `json:"EnabledBy"` - ConfigFiles []ConfigFile `json:"ConfigFiles"` - Vars map[string]interface{} `json:"Vars"` - Daemon SystemService `json:"Daemon"` - Users map[string]SystemUser `json:"Users"` + ConfigFiles []ConfigFile `json:"ConfigFiles"` + Vars map[string]interface{} `json:"Vars"` + Daemons map[string]SystemService `json:"Daemons"` + Users map[string]SystemUser `json:"Users"` + Packages map[string]SystemPackage `json:"Packages"` } func (s *Service) Manage(templateDir string) error { + // Manage system packages + for _, pack := range s.Packages { + err := pack.Manage() + if err != nil { + return err + } + } + err := processConfigFiles(s.ConfigFiles, s.Vars, templateDir) if err != nil { return fmt.Errorf("ProcessingTemplatesFailed with error: %v", err) } - err = s.Daemon.Manage() - if err != nil { - return fmt.Errorf("Error managing service daemons: %v", err) + for _, daemon := range s.Daemons { + err = daemon.Manage() + if err != nil { + return fmt.Errorf("Error managing service daemons: %v", err) + } } return nil } diff --git a/pkg/templater/system_services.go b/pkg/templater/system_services.go index 7220c74..2e5936c 100644 --- a/pkg/templater/system_services.go +++ b/pkg/templater/system_services.go @@ -11,6 +11,7 @@ type SystemService struct { Name string `json:"name"` Enabled bool `json:"enabled"` Type string `json:"type"` + ToStart bool `json:"start"` } func (sys *SystemService) SetType() { @@ -34,8 +35,16 @@ func (sys *SystemService) SetType() { } } +func (sys *SystemService) Action() error { + if sys.ToStart { + return sys.Start() + } + return nil +} + func (sys *SystemService) Manage() error { - if sys.Type == "" { + // By default if the property sys.ToStart is empty + if sys.Type == "" || sys.Type == "auto" { sys.SetType() } if sys.Enabled { @@ -43,6 +52,10 @@ func (sys *SystemService) Manage() error { if err != nil { return err } + if err = sys.Action(); err != nil { + return err + } + } else { fmt.Printf("Nothing to do for daemon %s\n", sys.Name) } @@ -50,12 +63,38 @@ func (sys *SystemService) Manage() error { } func (sys *SystemService) Start() error { - fmt.Printf("Starting %s\n", sys.Name) + fmt.Printf("Starting system service : %s\n", sys.Name) + if sys.Type == "systemd" { + _, stdErr, err := utils.RunSystemCommand("systemctl", "start", sys.Name) + if err != nil { + return fmt.Errorf("System service %s \n * Start error:\n - %s", sys.Name, stdErr) + } + } else if sys.Type == "openrc" { + _, stdErr, err := utils.RunSystemCommand("service", sys.Name, "stop") + if err != nil { + return fmt.Errorf("System service %s \n * Enable error:\n - %s", sys.Name, stdErr) + } + } else { + return fmt.Errorf("Unsupported service type %s for service %s", sys.Type, sys.Name) + } return nil } func (sys *SystemService) Stop() error { - fmt.Printf("Stoping %s\n", sys.Name) + fmt.Printf("Stoping system service : %s\n", sys.Name) + if sys.Type == "systemd" { + _, stdErr, err := utils.RunSystemCommand("systemctl", "stop", sys.Name) + if err != nil { + return fmt.Errorf("System service %s \n * Stop error:\n - %s", sys.Name, stdErr) + } + } else if sys.Type == "openrc" { + _, stdErr, err := utils.RunSystemCommand("service", sys.Name, "stop") + if err != nil { + return fmt.Errorf("System service %s \n * Enable error:\n - %s", sys.Name, stdErr) + } + } else { + return fmt.Errorf("Unsupported service type %s for service %s", sys.Type, sys.Name) + } return nil } @@ -66,7 +105,10 @@ func (sys *SystemService) Enable() error { return fmt.Errorf("System service %s \n * Enable error:\n - %s", sys.Name, stdErr) } } else if sys.Type == "openrc" { - return nil + _, stdErr, err := utils.RunSystemCommand("rc-update", "add", sys.Name, "default") + if err != nil { + return fmt.Errorf("System service %s \n * Enable error:\n - %s", sys.Name, stdErr) + } } else { return fmt.Errorf("Unsupported service type %s for service %s", sys.Type, sys.Name) } diff --git a/pkg/utils/os.go b/pkg/utils/os.go new file mode 100644 index 0000000..8fe4d64 --- /dev/null +++ b/pkg/utils/os.go @@ -0,0 +1,27 @@ +package utils + +import ( + "fmt" + + ini "gopkg.in/ini.v1" +) + +var osReleaseFile = "/etc/os-release" + +func ReadOSRelease() (map[string]string, error) { + cfg, err := ini.Load(osReleaseFile) + if err != nil { + return nil, fmt.Errorf("Fail to read file: %v ", err) + } + + ConfigParams := make(map[string]string) + ConfigParams["ID"] = cfg.Section("").Key("ID").String() + idLike := cfg.Section("").Key("ID_LIKE").String() + if idLike != "" { + ConfigParams["ID_LIKE"] = idLike + } else { + ConfigParams["ID_LIKE"] = ConfigParams["ID"] + } + + return ConfigParams, nil +}