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, "", " ") }