metis/pkg/plan/node_metadata.go

134 lines
3.7 KiB
Go

package plan
import (
"context"
"encoding/json"
"os"
"sort"
"strings"
"metis/pkg/config"
"metis/pkg/secrets"
)
func fetchSecrets(hostname string) *secrets.NodeSecrets {
envSecrets := nodeSecretsFromEnv()
if os.Getenv("VAULT_ADDR") == "" {
return envSecrets
}
cli := secrets.NewFromEnv()
sec, err := cli.FetchNode(context.Background(), hostname)
if err != nil {
return envSecrets
}
return mergeNodeSecrets(sec, envSecrets)
}
func nodeSecretsFromEnv() *secrets.NodeSecrets {
sec := &secrets.NodeSecrets{
SSHPassword: strings.TrimSpace(os.Getenv("METIS_NODE_SSH_PASSWORD")),
SSHPasswordHash: strings.TrimSpace(os.Getenv("METIS_NODE_SSH_PASSWORD_HASH")),
AtlasPassword: strings.TrimSpace(os.Getenv("METIS_NODE_ATLAS_PASSWORD")),
AtlasPasswordHash: strings.TrimSpace(os.Getenv("METIS_NODE_ATLAS_PASSWORD_HASH")),
RootPassword: strings.TrimSpace(os.Getenv("METIS_NODE_ROOT_PASSWORD")),
RootPasswordHash: strings.TrimSpace(os.Getenv("METIS_NODE_ROOT_PASSWORD_HASH")),
}
if sec.SSHPassword == "" && sec.SSHPasswordHash == "" && sec.AtlasPassword == "" && sec.AtlasPasswordHash == "" && sec.RootPassword == "" && sec.RootPasswordHash == "" {
return nil
}
return sec
}
func mergeNodeSecrets(base, override *secrets.NodeSecrets) *secrets.NodeSecrets {
if base == nil {
return override
}
if override == nil {
return base
}
merged := *base
merged.SSHPassword = firstNonEmptyString(override.SSHPassword, base.SSHPassword)
merged.SSHPasswordHash = firstNonEmptyString(override.SSHPasswordHash, base.SSHPasswordHash)
merged.AtlasPassword = firstNonEmptyString(override.AtlasPassword, base.AtlasPassword)
merged.AtlasPasswordHash = firstNonEmptyString(override.AtlasPasswordHash, base.AtlasPasswordHash)
merged.RootPassword = firstNonEmptyString(override.RootPassword, base.RootPassword)
merged.RootPasswordHash = firstNonEmptyString(override.RootPasswordHash, base.RootPasswordHash)
merged.K3sToken = firstNonEmptyString(override.K3sToken, base.K3sToken)
merged.CloudInit = firstNonEmptyString(override.CloudInit, base.CloudInit)
if len(base.Extra) > 0 || len(override.Extra) > 0 {
merged.Extra = map[string]string{}
for key, value := range base.Extra {
merged.Extra[key] = value
}
for key, value := range override.Extra {
merged.Extra[key] = value
}
}
return &merged
}
func applyNodeMetadataEnv(cfg *config.NodeConfig) {
if cfg == nil {
return
}
if labels := parseEnvJSONMap(os.Getenv("METIS_NODE_LABELS_JSON")); len(labels) > 0 {
if cfg.Labels == nil {
cfg.Labels = map[string]string{}
}
for key, value := range labels {
cfg.Labels[key] = value
}
cfg.K3s.Labels = cfg.Labels
}
if taints := parseEnvJSONList(os.Getenv("METIS_NODE_TAINTS_JSON")); len(taints) > 0 {
cfg.Taints = uniqueStrings(append(cfg.Taints, taints...))
cfg.K3s.Taints = append([]string{}, cfg.Taints...)
}
}
func parseEnvJSONMap(raw string) map[string]string {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil
}
var values map[string]string
if err := json.Unmarshal([]byte(raw), &values); err != nil {
return nil
}
return values
}
func parseEnvJSONList(raw string) []string {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil
}
var values []string
if err := json.Unmarshal([]byte(raw), &values); err != nil {
return nil
}
return values
}
func uniqueStrings(values []string) []string {
seen := map[string]struct{}{}
out := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
out = append(out, value)
}
sort.Strings(out)
return out
}
func jsonMarshalIndent(value any) ([]byte, error) {
return json.MarshalIndent(value, "", " ")
}