mirror of
https://github.com/Icinga/docker-icingaweb2.git
synced 2025-10-23 21:11:00 +02:00
It knows best what they have to look like to be interpreted correctly. Especially the proper quoting of special characters is important.
295 lines
6.7 KiB
Go
295 lines
6.7 KiB
Go
// Icinga Web 2 Docker image | (c) 2020 Icinga GmbH | GPLv2+
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
const confDir = "/data/etc/icingaweb2"
|
|
const modsDir = "/usr/share/icingaweb2/modules"
|
|
const dirMode = 0750
|
|
|
|
var enModsDir = path.Join(confDir, "enabledModules")
|
|
|
|
func main() {
|
|
if err := entrypoint(); err != nil {
|
|
logf("crit", "%s", err.Error())
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func entrypoint() error {
|
|
if len(os.Args) < 2 {
|
|
logf("warn", "Nothing to do.")
|
|
return nil
|
|
}
|
|
|
|
if os.Getpid() == 1 {
|
|
logf("info", "Initializing /data as we're the init process")
|
|
|
|
for _, dir := range []string{enModsDir, "/data/var/lib/icingaweb2"} {
|
|
logf("debug", "Creating %#v", dir)
|
|
if errMA := os.MkdirAll(dir, dirMode); errMA != nil {
|
|
return errMA
|
|
}
|
|
}
|
|
|
|
{
|
|
enMod := path.Join(enModsDir, "dockerentrypoint")
|
|
if errSl := os.Symlink("/entrypoint-db-init", enMod); errSl != nil && !os.IsExist(errSl) {
|
|
return errSl
|
|
}
|
|
|
|
defer os.Remove(enMod)
|
|
}
|
|
|
|
logf("debug", "Translating env vars to .ini config")
|
|
|
|
cfgs := map[string]map[string]map[string]string{}
|
|
var enabledModules map[string]struct{} = nil
|
|
passwords := map[string]map[string]string{}
|
|
|
|
EnvVars:
|
|
for _, env := range os.Environ() {
|
|
if kv := strings.SplitN(env, "=", 2); len(kv) == 2 {
|
|
if strings.HasPrefix(kv[0], "icingaweb") {
|
|
if kv[0] = strings.TrimPrefix(kv[0], "icingaweb"); len(kv[0]) > 0 {
|
|
directive := strings.Split(kv[0][1:], kv[0][:1])
|
|
for _, component := range directive {
|
|
if component == "" {
|
|
continue EnvVars
|
|
}
|
|
}
|
|
|
|
if len(directive) == 1 {
|
|
if directive[0] == "enabledModules" {
|
|
if enabledModules == nil {
|
|
enabledModules = map[string]struct{}{}
|
|
}
|
|
|
|
for _, mod := range strings.Split(kv[1], ",") {
|
|
if mod = strings.TrimSpace(mod); mod != "" {
|
|
enabledModules[mod] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
} else if len(directive) >= 3 {
|
|
if len(directive) == 3 && directive[0] == "passwords" {
|
|
users, ok := passwords[directive[1]]
|
|
if !ok {
|
|
users = map[string]string{}
|
|
passwords[directive[1]] = users
|
|
}
|
|
|
|
users[directive[2]] = kv[1]
|
|
} else {
|
|
file := path.Join(directive[:len(directive)-2]...)
|
|
cfg, ok := cfgs[file]
|
|
|
|
if !ok {
|
|
cfg = map[string]map[string]string{}
|
|
cfgs[file] = cfg
|
|
}
|
|
|
|
sectionName := directive[len(directive)-2]
|
|
section, hasSection := cfg[sectionName]
|
|
|
|
if !hasSection {
|
|
section = map[string]string{}
|
|
cfg[sectionName] = section
|
|
}
|
|
|
|
section[directive[len(directive)-1]] = kv[1]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for file, cfg := range cfgs {
|
|
file = path.Join(confDir, file+".ini")
|
|
logf("trace1", "Writing %#v", file)
|
|
|
|
if errMA := os.MkdirAll(path.Dir(file), dirMode); errMA != nil {
|
|
return errMA
|
|
}
|
|
|
|
jsn := &bytes.Buffer{}
|
|
ini := &bytes.Buffer{}
|
|
|
|
if err := json.NewEncoder(jsn).Encode(cfg); err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd := exec.Command("icingacli", "dockerentrypoint", "config", "render")
|
|
cmd.Stdin = jsn
|
|
cmd.Stdout = ini
|
|
cmd.Stderr = os.Stderr
|
|
|
|
if errRn := cmd.Run(); errRn != nil {
|
|
return errRn
|
|
}
|
|
|
|
if errST := os.WriteFile(file, ini.Bytes(), 0o640); errST != nil {
|
|
return errST
|
|
}
|
|
}
|
|
|
|
if enabledModules != nil {
|
|
logf("debug", "Enabling/disabling modules")
|
|
|
|
mods, errRD := ioutil.ReadDir(enModsDir)
|
|
if errRD != nil {
|
|
return errRD
|
|
}
|
|
|
|
enabledModules["dockerentrypoint"] = struct{}{}
|
|
|
|
for _, mod := range mods {
|
|
mod := mod.Name()
|
|
if _, ok := enabledModules[mod]; ok {
|
|
delete(enabledModules, mod)
|
|
} else {
|
|
logf("trace1", "Disabling module %#v", mod)
|
|
if errRm := os.Remove(path.Join(enModsDir, mod)); errRm != nil {
|
|
return errRm
|
|
}
|
|
}
|
|
}
|
|
|
|
for mod := range enabledModules {
|
|
logf("trace1", "Enabling module %#v", mod)
|
|
|
|
errSl := os.Symlink(path.Join(modsDir, mod), path.Join(enModsDir, mod))
|
|
if errSl != nil {
|
|
return errSl
|
|
}
|
|
}
|
|
}
|
|
|
|
if errID := initDb(passwords); errID != nil {
|
|
return errID
|
|
}
|
|
}
|
|
|
|
path := os.Args[1]
|
|
if filepath.Base(path) == path {
|
|
logf("info", "Looking up %#v in $PATH", path)
|
|
|
|
abs, errLP := exec.LookPath(path)
|
|
if errLP != nil {
|
|
return errLP
|
|
}
|
|
|
|
path = abs
|
|
}
|
|
|
|
logf("info", "Running %#v", path)
|
|
return syscall.Exec(path, os.Args[1:], os.Environ())
|
|
}
|
|
|
|
func initDb(passwords map[string]map[string]string) error {
|
|
logf("info", "Checking database resources used as backends")
|
|
|
|
{
|
|
enMod := path.Join(enModsDir, "setup")
|
|
|
|
errSl := os.Symlink(path.Join(modsDir, "setup"), enMod)
|
|
if errSl != nil {
|
|
if le, ok := errSl.(*os.LinkError); !ok || !os.IsNotExist(le.Err) {
|
|
return errSl
|
|
}
|
|
}
|
|
|
|
if errSl == nil {
|
|
defer os.Remove(enMod)
|
|
}
|
|
}
|
|
|
|
var resources []string
|
|
if errIJ := icingacliJson(&resources, "dockerentrypoint", "db", "backends"); errIJ != nil {
|
|
return errIJ
|
|
}
|
|
|
|
for _, resource := range resources {
|
|
logf("debug", "Checking database resource %#v", resource)
|
|
|
|
var initialized uint8
|
|
|
|
errIJ := icingacliJson(&initialized, "dockerentrypoint", "db", "initialized", "--resource="+resource)
|
|
if errIJ != nil {
|
|
return errIJ
|
|
}
|
|
|
|
if initialized == 0 {
|
|
logf("debug", "Importing schema into database resource %#v", resource)
|
|
|
|
cmd := exec.Command("icingacli", "dockerentrypoint", "db", "init", "--resource="+resource)
|
|
cmd.Stdout = os.Stderr
|
|
cmd.Stderr = os.Stderr
|
|
|
|
if errRn := cmd.Run(); errRn != nil {
|
|
return errRn
|
|
}
|
|
}
|
|
}
|
|
|
|
for backend, users := range passwords {
|
|
for name, password := range users {
|
|
logf(
|
|
"info", `Ensuring database authentication backend %#v to have a user %#v with the password "***"`,
|
|
backend, name,
|
|
)
|
|
|
|
cmd := exec.Command("icingacli", "dockerentrypoint", "db", "user", "--backend="+backend, "--name="+name)
|
|
cmd.Stdout = os.Stderr
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Env = append(os.Environ(), "PASSWORD="+password)
|
|
|
|
if errRn := cmd.Run(); errRn != nil {
|
|
return errRn
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func icingacliJson(v interface{}, arg ...string) error {
|
|
cmd := exec.Command("icingacli", arg...)
|
|
var out bytes.Buffer
|
|
|
|
cmd.Stdout = &out
|
|
cmd.Stderr = os.Stderr
|
|
|
|
if errRn := cmd.Run(); errRn != nil {
|
|
return errRn
|
|
}
|
|
|
|
return json.Unmarshal(out.Bytes(), v)
|
|
}
|
|
|
|
var out = bufio.NewWriter(os.Stderr)
|
|
|
|
func logf(severity, format string, a ...interface{}) {
|
|
_, _ = fmt.Fprintf(out, "[%s] ", time.Now().Format("Mon Jan 2 15:04:05.999999999 2006"))
|
|
_, _ = fmt.Fprintf(out, "[docker_entrypoint:%s] [pid %d] DOCKERE: ", severity, os.Getpid())
|
|
_, _ = fmt.Fprintf(out, format, a...)
|
|
|
|
_, _ = fmt.Fprintln(out)
|
|
_ = out.Flush()
|
|
}
|