Update ctl init to support env file
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package initcmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -12,18 +14,28 @@ import (
|
||||
|
||||
"undecided.project/monok8s/pkg/bootstrap"
|
||||
"undecided.project/monok8s/pkg/config"
|
||||
|
||||
types "undecided.project/monok8s/pkg/apis/monok8s/v1alpha1"
|
||||
"undecided.project/monok8s/pkg/templates"
|
||||
)
|
||||
|
||||
func NewCmdInit(_ *genericclioptions.ConfigFlags) *cobra.Command {
|
||||
var configPath string
|
||||
var envFile string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "init [list|STEPSEL]",
|
||||
Short: "Start the bootstrap process for this node",
|
||||
Use: "init [list|STEPSEL] [--config path | --env-file path]",
|
||||
Short: "Bootstrap this node (from config file or env file)",
|
||||
Long: `Run the node bootstrap process.
|
||||
|
||||
You can provide configuration in two ways:
|
||||
|
||||
--config PATH Load MonoKSConfig YAML
|
||||
--env-file PATH Load MKS_* variables from env file and render config
|
||||
|
||||
STEPSEL allows running specific steps instead of the full sequence.
|
||||
It supports:
|
||||
|
||||
Supported formats:
|
||||
|
||||
3 Run step 3
|
||||
1-3 Run steps 1 through 3
|
||||
@@ -33,23 +45,54 @@ It supports:
|
||||
9-10,15 Combine ranges and individual steps
|
||||
`,
|
||||
Example: `
|
||||
ctl init
|
||||
# Run full bootstrap using config file
|
||||
ctl init --config /etc/monok8s/config.yaml
|
||||
|
||||
# Run full bootstrap using env file
|
||||
ctl init --env-file /opt/monok8s/config/cluster.env
|
||||
|
||||
# List steps
|
||||
ctl init list
|
||||
ctl init 1-3
|
||||
ctl init -3
|
||||
ctl init 3-
|
||||
ctl init 1,3,5
|
||||
ctl init 9-10,15
|
||||
|
||||
# Run selected steps
|
||||
ctl init 1-3 --env-file /opt/monok8s/config/cluster.env
|
||||
ctl init 3- --config /etc/monok8s/config.yaml
|
||||
`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
path, err := (config.Loader{}).ResolvePath(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
if strings.TrimSpace(configPath) != "" && strings.TrimSpace(envFile) != "" {
|
||||
return fmt.Errorf("--config and --env-file are mutually exclusive")
|
||||
}
|
||||
cfg, err := (config.Loader{}).Load(path)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if strings.TrimSpace(envFile) != "" {
|
||||
if err := loadEnvFile(envFile); err != nil {
|
||||
return fmt.Errorf("load env file %q: %w", envFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
var cfg *types.MonoKSConfig // or value, depending on your API
|
||||
|
||||
switch {
|
||||
case strings.TrimSpace(envFile) != "":
|
||||
if err := loadEnvFile(envFile); err != nil {
|
||||
return fmt.Errorf("load env file %q: %w", envFile, err)
|
||||
}
|
||||
vals := templates.LoadTemplateValuesFromEnv()
|
||||
rendered := templates.DefaultMonoKSConfig(vals)
|
||||
cfg = &rendered
|
||||
|
||||
default:
|
||||
path, err := (config.Loader{}).ResolvePath(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loaded, err := (config.Loader{}).Load(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg = loaded
|
||||
klog.InfoS("starting init", "config", path, "node", cfg.Spec.NodeName, "envFile", envFile)
|
||||
}
|
||||
|
||||
runner := bootstrap.NewRunner(cfg)
|
||||
@@ -59,18 +102,15 @@ It supports:
|
||||
|
||||
fmt.Fprintln(cmd.OutOrStdout(), "Showing current bootstrap sequence")
|
||||
|
||||
// width = number of digits of max step number
|
||||
width := len(fmt.Sprintf("%d", len(steps)))
|
||||
|
||||
for i, s := range steps {
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "\n %*d. %s\n", width, i+1, s.Name)
|
||||
fmt.Fprintf(cmd.OutOrStdout(), " %s\n", s.Desc)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
klog.InfoS("starting init", "config", path, "node", cfg.Spec.NodeName)
|
||||
|
||||
if len(args) == 0 {
|
||||
return runner.Init(cmd.Context())
|
||||
}
|
||||
@@ -87,9 +127,60 @@ It supports:
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&configPath, "config", "c", "", "path to MonoKSConfig yaml")
|
||||
cmd.Flags().StringVar(&envFile, "env-file", "", "path to env file containing MKS_* variables")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func loadEnvFile(path string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
lineNum := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
lineNum++
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
key, val, ok := strings.Cut(line, "=")
|
||||
if !ok {
|
||||
return fmt.Errorf("line %d: expected KEY=VALUE", lineNum)
|
||||
}
|
||||
|
||||
key = strings.TrimSpace(key)
|
||||
val = strings.TrimSpace(val)
|
||||
|
||||
if key == "" {
|
||||
return fmt.Errorf("line %d: empty variable name", lineNum)
|
||||
}
|
||||
|
||||
// Remove matching single or double quotes around the whole value.
|
||||
if len(val) >= 2 {
|
||||
if (val[0] == '"' && val[len(val)-1] == '"') || (val[0] == '\'' && val[len(val)-1] == '\'') {
|
||||
val = val[1 : len(val)-1]
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Setenv(key, val); err != nil {
|
||||
return fmt.Errorf("line %d: set %q: %w", lineNum, key, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseStepSelection(raw string, max int) (bootstrap.StepSelection, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/json"
|
||||
|
||||
"undecided.project/monok8s/pkg/scheme"
|
||||
tmpl "undecided.project/monok8s/pkg/templates"
|
||||
"undecided.project/monok8s/pkg/templates"
|
||||
)
|
||||
|
||||
func RenderMonoKSConfig() (string, error) {
|
||||
cfg := tmpl.DefaultMonoKSConfig()
|
||||
vals := templates.LoadTemplateValuesFromEnv()
|
||||
cfg := templates.DefaultMonoKSConfig(vals)
|
||||
|
||||
s := runtime.NewScheme()
|
||||
if err := scheme.AddToScheme(s); err != nil {
|
||||
@@ -19,9 +20,7 @@ func RenderMonoKSConfig() (string, error) {
|
||||
}
|
||||
|
||||
serializer := json.NewYAMLSerializer(
|
||||
json.DefaultMetaFactory,
|
||||
s,
|
||||
s,
|
||||
json.DefaultMetaFactory, s, s,
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
@@ -33,7 +32,8 @@ func RenderMonoKSConfig() (string, error) {
|
||||
}
|
||||
|
||||
func RenderOSUpgrade() (string, error) {
|
||||
cfg := tmpl.DefaultOSUpgrade()
|
||||
vals := templates.LoadTemplateValuesFromEnv()
|
||||
cfg := templates.DefaultOSUpgrade(vals)
|
||||
|
||||
s := runtime.NewScheme()
|
||||
if err := scheme.AddToScheme(s); err != nil {
|
||||
@@ -41,9 +41,7 @@ func RenderOSUpgrade() (string, error) {
|
||||
}
|
||||
|
||||
serializer := json.NewYAMLSerializer(
|
||||
json.DefaultMetaFactory,
|
||||
s,
|
||||
s,
|
||||
json.DefaultMetaFactory, s, s,
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
@@ -1,26 +1,13 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "undecided.project/monok8s/pkg/apis/monok8s/v1alpha1"
|
||||
buildinfo "undecided.project/monok8s/pkg/buildinfo"
|
||||
)
|
||||
|
||||
var ValAPIServerEndPoint string = "10.0.0.10:6443"
|
||||
var ValHostname string = "monoks-master-1"
|
||||
var ValBootstrapToken string = "abcd12.ef3456789abcdef0"
|
||||
var ValDiscoveryTokenCACertHash string = "sha256:9f1c2b3a4d5e6f7890abc1234567890abcdef1234567890abcdef1234567890ab"
|
||||
|
||||
func init() {
|
||||
ValBootstrapToken = os.Getenv("HOSTNAME")
|
||||
ValBootstrapToken = os.Getenv("BOOTSTRAP_TOKEN")
|
||||
ValDiscoveryTokenCACertHash = os.Getenv("TOKEN_CACERT_HASH")
|
||||
ValAPIServerEndPoint = os.Getenv("API_SERVER_ENDPOINT")
|
||||
}
|
||||
|
||||
func DefaultMonoKSConfig() types.MonoKSConfig {
|
||||
func DefaultMonoKSConfig(v TemplateValues) types.MonoKSConfig {
|
||||
return types.MonoKSConfig{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "monok8s.io/v1alpha1",
|
||||
@@ -31,66 +18,51 @@ func DefaultMonoKSConfig() types.MonoKSConfig {
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
Spec: types.MonoKSConfigSpec{
|
||||
KubernetesVersion: buildinfo.Version,
|
||||
NodeName: ValHostname,
|
||||
KubernetesVersion: v.KubernetesVersion,
|
||||
NodeName: firstNonEmpty(v.NodeName, v.Hostname),
|
||||
|
||||
ClusterRole: "control-plane",
|
||||
InitControlPlane: true,
|
||||
ClusterRole: clusterRoleFromTemplateValues(v),
|
||||
InitControlPlane: initControlPlaneFromTemplateValues(v),
|
||||
|
||||
ClusterName: "monok8s",
|
||||
ClusterDomain: "cluster.local",
|
||||
ClusterName: v.ClusterName,
|
||||
ClusterDomain: v.ClusterDomain,
|
||||
|
||||
PodSubnet: "10.244.0.0/16",
|
||||
ServiceSubnet: "10.96.0.0/12",
|
||||
PodSubnet: v.PodSubnet,
|
||||
ServiceSubnet: v.ServiceSubnet,
|
||||
|
||||
APIServerAdvertiseAddress: "10.0.0.10",
|
||||
APIServerEndpoint: ValAPIServerEndPoint,
|
||||
APIServerAdvertiseAddress: v.APIServerAdvertiseAddress,
|
||||
APIServerEndpoint: v.APIServerEndpoint,
|
||||
|
||||
// Fake token and hash for placeholder purpose
|
||||
BootstrapToken: ValBootstrapToken,
|
||||
DiscoveryTokenCACertHash: ValDiscoveryTokenCACertHash,
|
||||
BootstrapToken: v.BootstrapToken,
|
||||
DiscoveryTokenCACertHash: v.DiscoveryTokenCACertHash,
|
||||
|
||||
ContainerRuntimeEndpoint: "unix:///var/run/crio/crio.sock",
|
||||
ContainerRuntimeEndpoint: v.ContainerRuntimeEndpoint,
|
||||
CNIPlugin: v.CNIPlugin,
|
||||
|
||||
CNIPlugin: "default",
|
||||
|
||||
AllowSchedulingOnControlPlane: true,
|
||||
SkipImageCheck: false,
|
||||
AllowSchedulingOnControlPlane: v.AllowSchedulingOnControlPlane,
|
||||
SkipImageCheck: v.SkipImageCheck,
|
||||
|
||||
KubeProxyNodePortAddresses: []string{
|
||||
"primary",
|
||||
},
|
||||
|
||||
SubjectAltNames: []string{
|
||||
"10.0.0.10", "localhost", ValHostname,
|
||||
},
|
||||
|
||||
NodeLabels: map[string]string{
|
||||
"monok8s.io/label": "value",
|
||||
},
|
||||
|
||||
NodeAnnotations: map[string]string{
|
||||
"monok8s.io/annotation": "value",
|
||||
},
|
||||
SubjectAltNames: copyStringSlice(v.SubjectAltNames),
|
||||
NodeLabels: copyStringMap(v.NodeLabels),
|
||||
NodeAnnotations: copyStringMap(v.NodeAnnotations),
|
||||
|
||||
Network: types.NetworkSpec{
|
||||
Hostname: "monok8s-worker-1",
|
||||
ManagementIface: "eth1",
|
||||
ManagementCIDR: "10.0.0.10/24",
|
||||
ManagementGW: "10.0.0.1",
|
||||
DNSNameservers: []string{
|
||||
"1.1.1.1",
|
||||
"8.8.8.8",
|
||||
},
|
||||
DNSSearchDomains: []string{
|
||||
"lan",
|
||||
},
|
||||
Hostname: firstNonEmpty(v.Hostname, v.NodeName),
|
||||
ManagementIface: v.MgmtIface,
|
||||
ManagementCIDR: v.MgmtAddress,
|
||||
ManagementGW: v.MgmtGateway,
|
||||
DNSNameservers: copyStringSlice(v.DNSNameservers),
|
||||
DNSSearchDomains: copyStringSlice(v.DNSSearchDomains),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultOSUpgrade() types.OSUpgrade {
|
||||
func DefaultOSUpgrade(v TemplateValues) types.OSUpgrade {
|
||||
return types.OSUpgrade{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "monok8s.io/v1alpha1",
|
||||
@@ -105,9 +77,56 @@ func DefaultOSUpgrade() types.OSUpgrade {
|
||||
ImageURL: "https://example.invalid/images/monok8s-v0.0.1.img.zst",
|
||||
TargetPartition: "B",
|
||||
NodeSelector: []string{
|
||||
ValHostname,
|
||||
firstNonEmpty(v.NodeName, v.Hostname),
|
||||
},
|
||||
Force: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func clusterRoleFromTemplateValues(v TemplateValues) string {
|
||||
switch strings.ToLower(strings.TrimSpace(v.BootstrapMode)) {
|
||||
case "init":
|
||||
return "control-plane"
|
||||
case "join":
|
||||
if strings.EqualFold(strings.TrimSpace(v.JoinKind), "control-plane") {
|
||||
return "control-plane"
|
||||
}
|
||||
return "worker"
|
||||
default:
|
||||
return "control-plane"
|
||||
}
|
||||
}
|
||||
|
||||
func initControlPlaneFromTemplateValues(v TemplateValues) bool {
|
||||
return strings.EqualFold(strings.TrimSpace(v.BootstrapMode), "init")
|
||||
}
|
||||
|
||||
func firstNonEmpty(xs ...string) string {
|
||||
for _, x := range xs {
|
||||
if strings.TrimSpace(x) != "" {
|
||||
return strings.TrimSpace(x)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func copyStringSlice(in []string) []string {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
func copyStringMap(in map[string]string) map[string]string {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]string, len(in))
|
||||
for k, v := range in {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
204
clitools/pkg/templates/values.go
Normal file
204
clitools/pkg/templates/values.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
buildinfo "undecided.project/monok8s/pkg/buildinfo"
|
||||
)
|
||||
|
||||
type TemplateValues struct {
|
||||
Hostname string
|
||||
NodeName string
|
||||
KubernetesVersion string
|
||||
ClusterName string
|
||||
ClusterDomain string
|
||||
PodSubnet string
|
||||
ServiceSubnet string
|
||||
APIServerAdvertiseAddress string
|
||||
APIServerEndpoint string
|
||||
BootstrapToken string
|
||||
DiscoveryTokenCACertHash string
|
||||
ControlPlaneCertKey string
|
||||
ContainerRuntimeEndpoint string
|
||||
CNIPlugin string
|
||||
|
||||
BootstrapMode string // init, join
|
||||
JoinKind string // worker, control-plane
|
||||
|
||||
AllowSchedulingOnControlPlane bool
|
||||
SkipImageCheck bool
|
||||
|
||||
MgmtIface string
|
||||
MgmtAddress string
|
||||
MgmtGateway string
|
||||
DNSNameservers []string
|
||||
DNSSearchDomains []string
|
||||
|
||||
SubjectAltNames []string
|
||||
NodeLabels map[string]string
|
||||
NodeAnnotations map[string]string
|
||||
}
|
||||
|
||||
func defaultTemplateValues() TemplateValues {
|
||||
return TemplateValues{
|
||||
Hostname: "monok8s-master-1",
|
||||
NodeName: "monok8s-master-1",
|
||||
KubernetesVersion: buildinfo.Version,
|
||||
ClusterName: "monok8s",
|
||||
ClusterDomain: "cluster.local",
|
||||
PodSubnet: "10.244.0.0/16",
|
||||
ServiceSubnet: "10.96.0.0/12",
|
||||
APIServerAdvertiseAddress: "10.0.0.10",
|
||||
APIServerEndpoint: "10.0.0.10:6443",
|
||||
BootstrapToken: "abcd12.ef3456789abcdef0",
|
||||
DiscoveryTokenCACertHash: "sha256:9f1c2b3a4d5e6f7890abc1234567890abcdef1234567890abcdef1234567890ab",
|
||||
ControlPlaneCertKey: "",
|
||||
ContainerRuntimeEndpoint: "unix:///var/run/crio/crio.sock",
|
||||
CNIPlugin: "default",
|
||||
|
||||
BootstrapMode: "init",
|
||||
JoinKind: "worker",
|
||||
|
||||
AllowSchedulingOnControlPlane: true,
|
||||
SkipImageCheck: false,
|
||||
|
||||
MgmtIface: "eth1",
|
||||
MgmtAddress: "10.0.0.10/24",
|
||||
MgmtGateway: "10.0.0.1",
|
||||
|
||||
DNSNameservers: []string{"1.1.1.1", "8.8.8.8"},
|
||||
DNSSearchDomains: []string{"lan"},
|
||||
|
||||
SubjectAltNames: []string{"10.0.0.10", "localhost", "monok8s-master-1"},
|
||||
|
||||
NodeLabels: map[string]string{
|
||||
"monok8s.io/label": "value",
|
||||
},
|
||||
NodeAnnotations: map[string]string{
|
||||
"monok8s.io/annotation": "value",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func LoadTemplateValuesFromEnv() TemplateValues {
|
||||
v := defaultTemplateValues()
|
||||
|
||||
v.Hostname = getenvDefault("MKS_HOSTNAME", v.Hostname)
|
||||
v.NodeName = getenvDefault("MKS_NODE_NAME", v.Hostname)
|
||||
|
||||
v.KubernetesVersion = getenvDefault("MKS_KUBERNETES_VERSION", v.KubernetesVersion)
|
||||
v.ClusterName = getenvDefault("MKS_CLUSTER_NAME", v.ClusterName)
|
||||
v.ClusterDomain = getenvDefault("MKS_CLUSTER_DOMAIN", v.ClusterDomain)
|
||||
v.PodSubnet = getenvDefault("MKS_POD_SUBNET", v.PodSubnet)
|
||||
v.ServiceSubnet = getenvDefault("MKS_SERVICE_SUBNET", v.ServiceSubnet)
|
||||
|
||||
v.APIServerAdvertiseAddress = getenvDefault("MKS_APISERVER_ADVERTISE_ADDRESS", v.APIServerAdvertiseAddress)
|
||||
v.APIServerEndpoint = getenvDefault("MKS_API_SERVER_ENDPOINT", v.APIServerEndpoint)
|
||||
|
||||
v.BootstrapToken = getenvDefault("MKS_BOOTSTRAP_TOKEN", v.BootstrapToken)
|
||||
v.DiscoveryTokenCACertHash = getenvDefault("MKS_DISCOVERY_TOKEN_CA_CERT_HASH", v.DiscoveryTokenCACertHash)
|
||||
v.ControlPlaneCertKey = getenvDefault("MKS_CONTROL_PLANE_CERT_KEY", v.ControlPlaneCertKey)
|
||||
|
||||
v.ContainerRuntimeEndpoint = getenvDefault("MKS_CONTAINER_RUNTIME_ENDPOINT", v.ContainerRuntimeEndpoint)
|
||||
v.CNIPlugin = getenvDefault("MKS_CNI_PLUGIN", v.CNIPlugin)
|
||||
|
||||
v.BootstrapMode = getenvDefault("MKS_BOOTSTRAP_MODE", v.BootstrapMode)
|
||||
v.JoinKind = getenvDefault("MKS_JOIN_KIND", v.JoinKind)
|
||||
|
||||
v.AllowSchedulingOnControlPlane = getenvBoolDefault("MKS_ALLOW_SCHEDULING_ON_CONTROL_PLANE", v.AllowSchedulingOnControlPlane)
|
||||
v.SkipImageCheck = getenvBoolDefault("MKS_SKIP_IMAGE_CHECK", v.SkipImageCheck)
|
||||
|
||||
v.MgmtIface = getenvDefault("MKS_MGMT_IFACE", v.MgmtIface)
|
||||
v.MgmtAddress = getenvDefault("MKS_MGMT_ADDRESS", v.MgmtAddress)
|
||||
v.MgmtGateway = getenvDefault("MKS_MGMT_GATEWAY", v.MgmtGateway)
|
||||
|
||||
if xs := splitWhitespaceList(os.Getenv("MKS_DNS_NAMESERVERS")); len(xs) > 0 {
|
||||
v.DNSNameservers = xs
|
||||
}
|
||||
if xs := splitWhitespaceList(os.Getenv("MKS_DNS_SEARCH_DOMAINS")); len(xs) > 0 {
|
||||
v.DNSSearchDomains = xs
|
||||
}
|
||||
if xs := splitCommaList(os.Getenv("MKS_SANS")); len(xs) > 0 {
|
||||
v.SubjectAltNames = xs
|
||||
}
|
||||
if m := parseKeyValueMap(os.Getenv("MKS_NODE_LABELS")); len(m) > 0 {
|
||||
v.NodeLabels = m
|
||||
}
|
||||
if m := parseKeyValueMap(os.Getenv("MKS_NODE_ANNOTATIONS")); len(m) > 0 {
|
||||
v.NodeAnnotations = m
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func getenvDefault(key, def string) string {
|
||||
s := strings.TrimSpace(os.Getenv(key))
|
||||
if s == "" {
|
||||
return def
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func getenvBoolDefault(key string, def bool) bool {
|
||||
s := strings.TrimSpace(strings.ToLower(os.Getenv(key)))
|
||||
if s == "" {
|
||||
return def
|
||||
}
|
||||
switch s {
|
||||
case "1", "true", "yes", "y", "on":
|
||||
return true
|
||||
case "0", "false", "no", "n", "off":
|
||||
return false
|
||||
default:
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
func splitCommaList(s string) []string {
|
||||
if strings.TrimSpace(s) == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(s, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func splitWhitespaceList(s string) []string {
|
||||
if strings.TrimSpace(s) == "" {
|
||||
return nil
|
||||
}
|
||||
return strings.Fields(s)
|
||||
}
|
||||
|
||||
func parseKeyValueMap(s string) map[string]string {
|
||||
out := map[string]string{}
|
||||
if strings.TrimSpace(s) == "" {
|
||||
return out
|
||||
}
|
||||
|
||||
for _, item := range strings.Split(s, ",") {
|
||||
item = strings.TrimSpace(item)
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
k, val, ok := strings.Cut(item, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
k = strings.TrimSpace(k)
|
||||
val = strings.TrimSpace(val)
|
||||
if k == "" {
|
||||
continue
|
||||
}
|
||||
out[k] = val
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user