zabbix/templates/entrypoints/internal/bootstrap/environment.go
2026-07-15 17:51:41 +09:00

160 lines
4.5 KiB
Go

//go:build windows
// Package bootstrap provides the shared building blocks of the container
// entrypoints: process environment handling, Zabbix configuration file
// updates, logging and the final hand-off to the service binary.
package bootstrap
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
// Environment is a mutable set of environment variables keyed by name.
// The entrypoint modifies it while preparing a service and passes the
// result to the final process.
type Environment map[string]string
// NewEnvironment parses "NAME=value" pairs as returned by os.Environ.
func NewEnvironment(values []string) Environment {
env := make(Environment, len(values))
for _, item := range values {
name, value, found := strings.Cut(item, "=")
if found {
env[name] = value
}
}
return env
}
// List returns the variables as sorted "NAME=value" pairs suitable for exec.
func (env Environment) List() []string {
names := make([]string, 0, len(env))
for name := range env {
names = append(names, name)
}
sort.Strings(names)
values := make([]string, 0, len(names))
for _, name := range names {
values = append(values, name+"="+env[name])
}
return values
}
// ValueOrDefault returns the value of name, or defaultValue when the
// variable is not present. An empty value counts as present; use
// ValueOrDefaultNonEmpty to treat it as missing.
func (env Environment) ValueOrDefault(name, defaultValue string) string {
value, found := env[name]
if !found {
return defaultValue
}
return value
}
// ValueOrDefaultNonEmpty returns the value of name, or defaultValue when
// the variable is missing or empty.
func (env Environment) ValueOrDefaultNonEmpty(name, defaultValue string) string {
if value := env[name]; value != "" {
return value
}
return defaultValue
}
// RequiredHomeDirectory returns the Zabbix home directory,
// verifying that it exists.
func RequiredHomeDirectory(env Environment) (string, error) {
return requiredDirectory(env, "ZABBIX_USER_HOME_DIR")
}
// RequiredDirectories returns the Zabbix home and config directories
// (ZABBIX_USER_HOME_DIR and ZABBIX_CONF_DIR), verifying that both exist.
func RequiredDirectories(env Environment) (homeDir, configDir string, err error) {
homeDir, err = RequiredHomeDirectory(env)
if err != nil {
return "", "", err
}
configDir, err = requiredDirectory(env, "ZABBIX_CONF_DIR")
if err != nil {
return "", "", err
}
return homeDir, configDir, nil
}
// requiredDirectory returns directory from env variable.
func requiredDirectory(env Environment, name string) (string, error) {
directory := env[name]
if directory == "" {
return "", fmt.Errorf("%s must be set", name)
}
info, err := os.Stat(directory)
if err != nil {
return "", fmt.Errorf("access %s directory %s: %w", name, directory, err)
}
if !info.IsDir() {
return "", fmt.Errorf("%s path %s is not a directory", name, directory)
}
return directory, nil
}
// ProcessFileFromEnvironment persists the value of variable into a file
// under directory and points the corresponding "<variable>FILE" variable at
// it. The plain variable is always removed so that secrets do not stay in
// the service environment.
func ProcessFileFromEnvironment(env Environment, directory, variable string) error {
fileVariable := variable + "FILE"
if value := env[variable]; value != "" {
path := filepath.Join(directory, fileVariable)
if err := os.WriteFile(path, []byte(value), 0o600); err != nil {
return fmt.Errorf("write %s: %w", path, err)
}
env[fileVariable] = path
}
delete(env, variable)
return nil
}
// ProcessTLSFiles moves TLS material from the listed variables into
// files under <home>/enc_internal, so that Zabbix reads certificates and
// keys from disk instead of the environment.
func ProcessTLSFiles(env Environment, homeDir string, variables ...string) error {
directory := filepath.Join(homeDir, "enc_internal")
for _, variable := range variables {
if err := ProcessFileFromEnvironment(env, directory, variable); err != nil {
return err
}
}
return nil
}
// ClearPrivateEnv removes variables with the supplied prefixes that the
// service should not inherit. ZBX_CLEAR_ENV=false disables the cleanup.
func ClearPrivateEnv(env Environment, prefixes ...string) {
if env["ZBX_CLEAR_ENV"] == "false" {
return
}
for name := range env {
if hasAnyPrefix(name, prefixes) {
delete(env, name)
}
}
}
func hasAnyPrefix(value string, prefixes []string) bool {
for _, prefix := range prefixes {
if strings.HasPrefix(value, prefix) {
return true
}
}
return false
}