github.com/vshn/k8ify@v1.1.2-0.20240502214202-6c9ed3ef0bf4/pkg/util/configutils.go (about)

     1  package util
     2  
     3  import (
     4  	"fmt"
     5  	core "k8s.io/api/core/v1"
     6  	"maps"
     7  	"os"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/docker/go-units"
    13  	"github.com/sirupsen/logrus"
    14  	"k8s.io/apimachinery/pkg/api/resource"
    15  )
    16  
    17  var (
    18  	reTrue = regexp.MustCompile("(?i)^true|yes|1$")
    19  )
    20  
    21  // SubConfig extracts the keys that start with a given prefix from a given
    22  // config map
    23  //
    24  // If there is a key that is EQUAL to the prefix, the entry identified by
    25  // `defaultKey` will be updated.
    26  //
    27  // If there is a key that is equal to the prefix, AND an entry corresponding to
    28  // `defaultKey`, the behavior is undefined!
    29  func SubConfig(config map[string]string, prefix string, defaultKey string) map[string]string {
    30  	subConfig := make(map[string]string)
    31  	for key, value := range config {
    32  		if key == prefix {
    33  			subConfig[defaultKey] = value
    34  		}
    35  		if strings.HasPrefix(key, prefix+".") && len(key) > (len(prefix)+1) {
    36  			subKey := key[len(prefix)+1:]
    37  			subConfig[subKey] = value
    38  		}
    39  	}
    40  	return subConfig
    41  }
    42  
    43  // ConfigGetInt32 extracts the int32 value from the entry with the given key
    44  //
    45  // Returns `defaultValue` if either the entry does not exist, or is not a
    46  // numeric value.
    47  func ConfigGetInt32(config map[string]string, key string, defaultValue int32) int32 {
    48  	if valStr, ok := config[key]; ok {
    49  		if valInt, err := strconv.Atoi(valStr); err == nil {
    50  			return int32(valInt)
    51  		}
    52  	}
    53  	return defaultValue
    54  }
    55  
    56  // IsTruthy determines whether the given string is a representation of a "true"
    57  // state.
    58  //
    59  // Concretly, it currently tests for "true", "yes" or "1", ignoring character
    60  // cases.
    61  func IsTruthy(s string) bool {
    62  	return reTrue.MatchString(s)
    63  }
    64  
    65  func GetBoolean(labels map[string]string, key string) bool {
    66  	if val, ok := labels[key]; ok {
    67  		return IsTruthy(val)
    68  	}
    69  
    70  	return false
    71  }
    72  
    73  func GetOptional(labels map[string]string, key string) *string {
    74  	if val, ok := labels[key]; ok {
    75  		return &val
    76  	}
    77  
    78  	return nil
    79  }
    80  
    81  // IsSingleton determine whether a resource (according to its labels) should be
    82  // treated as a singleton.
    83  func IsSingleton(labels map[string]string) bool {
    84  	return GetBoolean(labels, "k8ify.singleton")
    85  }
    86  
    87  // IsShared determines whether a volume is shared between replicas
    88  func IsShared(labels map[string]string) bool {
    89  	return GetBoolean(labels, "k8ify.shared")
    90  }
    91  
    92  // StorageClass determines a storage class from a set of labels
    93  func StorageClass(labels map[string]string) *string {
    94  	return GetOptional(labels, "k8ify.storageClass")
    95  }
    96  
    97  func StorageSizeRaw(labels map[string]string) *string {
    98  	return GetOptional(labels, "k8ify.size")
    99  }
   100  
   101  func Converter(labels map[string]string) *string {
   102  	return GetOptional(labels, "k8ify.converter")
   103  }
   104  
   105  func PartOf(labels map[string]string) *string {
   106  	return GetOptional(labels, "k8ify.partOf")
   107  }
   108  
   109  // StorageSize determines the requested storage size for a volume, or a
   110  // fallback value.
   111  func StorageSize(labels map[string]string, fallback string) resource.Quantity {
   112  	quantity := fallback
   113  	if q := StorageSizeRaw(labels); q != nil {
   114  		quantity = *q
   115  	}
   116  
   117  	size, err := units.RAMInBytes(quantity)
   118  	if err != nil {
   119  		logrus.Errorf("Invalid storage size: %q\n", quantity)
   120  		os.Exit(1)
   121  	}
   122  
   123  	return *resource.NewQuantity(size, resource.BinarySI)
   124  }
   125  
   126  func ServiceAccountName(labels map[string]string) string {
   127  	serviceAccountName := GetOptional(labels, "k8ify.serviceAccountName")
   128  	if serviceAccountName == nil {
   129  		return ""
   130  	}
   131  	return *serviceAccountName
   132  }
   133  
   134  func Annotations(labels map[string]string, kind string) map[string]string {
   135  	annotations := SubConfig(labels, "k8ify.annotations", "")
   136  	maps.Copy(annotations, SubConfig(labels, fmt.Sprintf("k8ify.%s.annotations", kind), ""))
   137  	delete(annotations, "")
   138  	return annotations
   139  }
   140  
   141  func ServiceType(labels map[string]string, port int32) core.ServiceType {
   142  	subConfig := SubConfig(labels, fmt.Sprintf("k8ify.exposePlain.%d", port), "")
   143  	if len(subConfig) == 0 {
   144  		return ""
   145  	}
   146  	if serviceType, ok := subConfig["type"]; ok {
   147  		// Go does not offer a way to list values of its "ENUMs", see https://github.com/golang/go/issues/19814
   148  		if serviceType == string(core.ServiceTypeClusterIP) {
   149  			return core.ServiceTypeClusterIP
   150  		}
   151  		if serviceType == string(core.ServiceTypeLoadBalancer) {
   152  			return core.ServiceTypeLoadBalancer
   153  		}
   154  		if serviceType == string(core.ServiceTypeExternalName) {
   155  			return core.ServiceTypeExternalName
   156  		}
   157  		if serviceType == string(core.ServiceTypeNodePort) {
   158  			return core.ServiceTypeNodePort
   159  		}
   160  	}
   161  	return core.ServiceTypeLoadBalancer
   162  }
   163  
   164  func ServiceExternalTrafficPolicy(labels map[string]string, port int32) core.ServiceExternalTrafficPolicy {
   165  	subConfig := SubConfig(labels, fmt.Sprintf("k8ify.exposePlain.%d", port), "")
   166  	if len(subConfig) == 0 {
   167  		return ""
   168  	}
   169  	if serviceType, ok := subConfig["externalTrafficPolicy"]; ok {
   170  		// Go does not offer a way to list values of its "ENUMs", see https://github.com/golang/go/issues/19814
   171  		if serviceType == string(core.ServiceExternalTrafficPolicyCluster) {
   172  			return core.ServiceExternalTrafficPolicyCluster
   173  		}
   174  		if serviceType == string(core.ServiceExternalTrafficPolicyLocal) {
   175  			return core.ServiceExternalTrafficPolicyLocal
   176  		}
   177  	}
   178  	return core.ServiceExternalTrafficPolicyLocal
   179  }
   180  
   181  func ServiceHealthCheckNodePort(labels map[string]string, port int32) int32 {
   182  	subConfig := SubConfig(labels, fmt.Sprintf("k8ify.exposePlain.%d", port), "")
   183  	if len(subConfig) == 0 {
   184  		return 0
   185  	}
   186  	healthCheckNodePort := ConfigGetInt32(subConfig, "healthCheckNodePort", 0)
   187  	if healthCheckNodePort > 65535 || healthCheckNodePort < 0 {
   188  		return 0
   189  	}
   190  	return healthCheckNodePort
   191  }