...

Source file src/edge-infra.dev/pkg/edge/linkerd/helm/values/values.go

Documentation: edge-infra.dev/pkg/edge/linkerd/helm/values

     1  package values
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/imdario/mergo"
     8  	"github.com/linkerd/linkerd2/pkg/charts"
     9  	"github.com/linkerd/linkerd2/pkg/charts/static"
    10  	"github.com/linkerd/linkerd2/pkg/k8s"
    11  	"github.com/linkerd/linkerd2/pkg/version"
    12  	"helm.sh/helm/v3/pkg/chart/loader"
    13  	corev1 "k8s.io/api/core/v1"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"sigs.k8s.io/yaml"
    16  )
    17  
    18  const (
    19  	// HelmChartDirCP is the directory name for the linkerd-control-plane chart
    20  	HelmChartDirCP = "linkerd-control-plane"
    21  )
    22  
    23  type (
    24  	// Values contains the top-level elements in the Helm charts
    25  	Values struct {
    26  		ControllerImage              string                 `json:"controllerImage"`
    27  		ControllerReplicas           uint                   `json:"controllerReplicas"`
    28  		ControllerUID                int64                  `json:"controllerUID"`
    29  		ControllerGID                int64                  `json:"controllerGID"`
    30  		EnableH2Upgrade              bool                   `json:"enableH2Upgrade"`
    31  		EnablePodAntiAffinity        bool                   `json:"enablePodAntiAffinity"`
    32  		NodeAffinity                 map[string]interface{} `json:"nodeAffinity"`
    33  		EnablePodDisruptionBudget    bool                   `json:"enablePodDisruptionBudget"`
    34  		DisableIPv6                  bool                   `json:"disableIPv6"`
    35  		Controller                   *Controller            `json:"controller"`
    36  		WebhookFailurePolicy         string                 `json:"webhookFailurePolicy"`
    37  		DeploymentStrategy           map[string]interface{} `json:"deploymentStrategy,omitempty"`
    38  		DisableHeartBeat             bool                   `json:"disableHeartBeat"`
    39  		HeartbeatSchedule            string                 `json:"heartbeatSchedule"`
    40  		Configs                      ConfigJSONs            `json:"configs"`
    41  		ClusterDomain                string                 `json:"clusterDomain"`
    42  		ClusterNetworks              string                 `json:"clusterNetworks"`
    43  		ImagePullPolicy              string                 `json:"imagePullPolicy"`
    44  		RevisionHistoryLimit         int64                  `json:"revisionHistoryLimit"`
    45  		CliVersion                   string                 `json:"cliVersion"`
    46  		ControllerLogLevel           string                 `json:"controllerLogLevel"`
    47  		ControllerLogFormat          string                 `json:"controllerLogFormat"`
    48  		ProxyContainerName           string                 `json:"proxyContainerName"`
    49  		HighAvailability             bool                   `json:"highAvailability"`
    50  		CNIEnabled                   bool                   `json:"cniEnabled"`
    51  		EnableEndpointSlices         bool                   `json:"enableEndpointSlices"`
    52  		ControlPlaneTracing          bool                   `json:"controlPlaneTracing"`
    53  		ControlPlaneTracingNamespace string                 `json:"controlPlaneTracingNamespace"`
    54  		IdentityTrustAnchorsPEM      string                 `json:"identityTrustAnchorsPEM"`
    55  		IdentityTrustDomain          string                 `json:"identityTrustDomain"`
    56  		PrometheusURL                string                 `json:"prometheusUrl"`
    57  		ImagePullSecrets             []map[string]string    `json:"imagePullSecrets"`
    58  		LinkerdVersion               string                 `json:"linkerdVersion"`
    59  
    60  		PodAnnotations    map[string]string `json:"podAnnotations"`
    61  		PodLabels         map[string]string `json:"podLabels"`
    62  		PriorityClassName string            `json:"priorityClassName"`
    63  
    64  		PodMonitor       *PodMonitor       `json:"podMonitor"`
    65  		PolicyController *PolicyController `json:"policyController"`
    66  		Proxy            *Proxy            `json:"proxy"`
    67  		ProxyInit        *ProxyInit        `json:"proxyInit"`
    68  		NetworkValidator *NetworkValidator `json:"networkValidator"`
    69  		Identity         *Identity         `json:"identity"`
    70  		DebugContainer   *DebugContainer   `json:"debugContainer"`
    71  		ProxyInjector    *Webhook          `json:"proxyInjector"`
    72  		ProfileValidator *Webhook          `json:"profileValidator"`
    73  		PolicyValidator  *Webhook          `json:"policyValidator"`
    74  		NodeSelector     map[string]string `json:"nodeSelector"`
    75  		Tolerations      []interface{}     `json:"tolerations"`
    76  
    77  		DestinationResources   *Resources `json:"destinationResources"`
    78  		HeartbeatResources     *Resources `json:"heartbeatResources"`
    79  		IdentityResources      *Resources `json:"identityResources"`
    80  		ProxyInjectorResources *Resources `json:"proxyInjectorResources"`
    81  
    82  		DestinationProxyResources   *Resources `json:"destinationProxyResources"`
    83  		IdentityProxyResources      *Resources `json:"identityProxyResources"`
    84  		ProxyInjectorProxyResources *Resources `json:"proxyInjectorProxyResources"`
    85  	}
    86  
    87  	// ConfigJSONs is the JSON encoding of the Linkerd configuration
    88  	ConfigJSONs struct {
    89  		Global  string `json:"global"`
    90  		Proxy   string `json:"proxy"`
    91  		Install string `json:"install"`
    92  	}
    93  
    94  	// Proxy contains the fields to set the proxy sidecar container
    95  	Proxy struct {
    96  		Capabilities *Capabilities `json:"capabilities"`
    97  		// This should match .Resources.CPU.Limit, but must be a whole number
    98  		Cores                                int64            `json:"cores,omitempty"`
    99  		EnableExternalProfiles               bool             `json:"enableExternalProfiles"`
   100  		Image                                *Image           `json:"image"`
   101  		LogLevel                             string           `json:"logLevel"`
   102  		LogFormat                            string           `json:"logFormat"`
   103  		SAMountPath                          *VolumeMountPath `json:"saMountPath"`
   104  		Ports                                *Ports           `json:"ports"`
   105  		Resources                            *Resources       `json:"resources"`
   106  		UID                                  int64            `json:"uid"`
   107  		GID                                  int64            `json:"gid"`
   108  		WaitBeforeExitSeconds                uint64           `json:"waitBeforeExitSeconds"`
   109  		IsGateway                            bool             `json:"isGateway"`
   110  		IsIngress                            bool             `json:"isIngress"`
   111  		RequireIdentityOnInboundPorts        string           `json:"requireIdentityOnInboundPorts"`
   112  		OutboundConnectTimeout               string           `json:"outboundConnectTimeout"`
   113  		InboundConnectTimeout                string           `json:"inboundConnectTimeout"`
   114  		OutboundDiscoveryCacheUnusedTimeout  string           `json:"outboundDiscoveryCacheUnusedTimeout"`
   115  		InboundDiscoveryCacheUnusedTimeout   string           `json:"inboundDiscoveryCacheUnusedTimeout"`
   116  		DisableOutboundProtocolDetectTimeout bool             `json:"disableOutboundProtocolDetectTimeout"`
   117  		DisableInboundProtocolDetectTimeout  bool             `json:"disableInboundProtocolDetectTimeout"`
   118  		PodInboundPorts                      string           `json:"podInboundPorts"`
   119  		OpaquePorts                          string           `json:"opaquePorts"`
   120  		Await                                bool             `json:"await"`
   121  		DefaultInboundPolicy                 string           `json:"defaultInboundPolicy"`
   122  		AccessLog                            string           `json:"accessLog"`
   123  		ShutdownGracePeriod                  string           `json:"shutdownGracePeriod"`
   124  		NativeSidecar                        bool             `json:"nativeSidecar"`
   125  		StartupProbe                         *StartupProbe    `json:"startupProbe"`
   126  		ReadinessProbe                       *Probe           `json:"readinessProbe"`
   127  		LivenessProbe                        *Probe           `json:"livenessProbe"`
   128  		Control                              *ProxyControl    `json:"control"`
   129  		Inbound                              *ProxyEndpoint   `json:"inbound"`
   130  		Outbound                             *ProxyEndpoint   `json:"outbound"`
   131  		AdditionalEnv                        []corev1.EnvVar  `json:"additionalEnv"`
   132  		ExperimentalEnv                      []corev1.EnvVar  `json:"experimentalEnv"`
   133  	}
   134  
   135  	ProxyControl struct {
   136  		Streams *ProxyControlStreams `json:"streams"`
   137  	}
   138  
   139  	ProxyControlStreams struct {
   140  		InitialTimeout string `json:"initialTimeout"`
   141  		IdleTimeout    string `json:"idleTimeout"`
   142  		Lifetime       string `json:"lifetime"`
   143  	}
   144  
   145  	ProxyEndpoint struct {
   146  		Server *ServerConfig `json:"server"`
   147  	}
   148  
   149  	ServerConfig struct {
   150  		HTTP2 *HTTP2Settings `json:"http2"`
   151  	}
   152  
   153  	HTTP2Settings struct {
   154  		KeepAliveInterval string `json:"keepAliveInterval"`
   155  		KeepAliveTimeout  string `json:"keepAliveTimeout"`
   156  	}
   157  
   158  	// ProxyInit contains the fields to set the proxy-init container
   159  	ProxyInit struct {
   160  		Capabilities         *Capabilities    `json:"capabilities"`
   161  		IgnoreInboundPorts   string           `json:"ignoreInboundPorts"`
   162  		IgnoreOutboundPorts  string           `json:"ignoreOutboundPorts"`
   163  		KubeAPIServerPorts   string           `json:"kubeAPIServerPorts"`
   164  		SkipSubnets          string           `json:"skipSubnets"`
   165  		LogLevel             string           `json:"logLevel"`
   166  		LogFormat            string           `json:"logFormat"`
   167  		Image                *Image           `json:"image"`
   168  		SAMountPath          *VolumeMountPath `json:"saMountPath"`
   169  		XTMountPath          *VolumeMountPath `json:"xtMountPath"`
   170  		Resources            *Resources       `json:"resources"`
   171  		CloseWaitTimeoutSecs int64            `json:"closeWaitTimeoutSecs"`
   172  		Privileged           bool             `json:"privileged"`
   173  		RunAsRoot            bool             `json:"runAsRoot"`
   174  		RunAsUser            int64            `json:"runAsUser"`
   175  		RunAsGroup           int64            `json:"runAsGroup"`
   176  		IptablesMode         string           `json:"iptablesMode"`
   177  	}
   178  
   179  	NetworkValidator struct {
   180  		LogLevel              string `json:"logLevel"`
   181  		LogFormat             string `json:"logFormat"`
   182  		ConnectAddr           string `json:"connectAddr"`
   183  		ListenAddr            string `json:"listenAddr"`
   184  		Timeout               string `json:"timeout"`
   185  		EnableSecurityContext bool   `json:"enableSecurityContext"`
   186  	}
   187  
   188  	// DebugContainer contains the fields to set the debugging sidecar
   189  	DebugContainer struct {
   190  		Image *Image `json:"image"`
   191  	}
   192  
   193  	// PodMonitor contains the fields to configure the Prometheus Operator `PodMonitor`
   194  	PodMonitor struct {
   195  		Enabled        bool                  `json:"enabled"`
   196  		ScrapeInterval string                `json:"scrapeInterval"`
   197  		ScrapeTimeout  string                `json:"scrapeTimeout"`
   198  		Controller     *PodMonitorController `json:"controller"`
   199  		ServiceMirror  *PodMonitorComponent  `json:"serviceMirror"`
   200  		Proxy          *PodMonitorComponent  `json:"proxy"`
   201  	}
   202  
   203  	// PodMonitorController contains the fields to configure the Prometheus Operator `PodMonitor` for the control-plane
   204  	PodMonitorController struct {
   205  		Enabled           bool   `json:"enabled"`
   206  		NamespaceSelector string `json:"namespaceSelector"`
   207  	}
   208  
   209  	// PodMonitorComponent contains the fields to configure the Prometheus Operator `PodMonitor` for other components
   210  	PodMonitorComponent struct {
   211  		Enabled bool `json:"enabled"`
   212  	}
   213  
   214  	// PolicyController contains the fields to configure the policy controller container
   215  	PolicyController struct {
   216  		Image         *Image     `json:"image"`
   217  		Resources     *Resources `json:"resources"`
   218  		LogLevel      string     `json:"logLevel"`
   219  		ProbeNetworks []string   `json:"probeNetworks"`
   220  	}
   221  
   222  	// Image contains the details to define a container image
   223  	Image struct {
   224  		Name       string `json:"name"`
   225  		PullPolicy string `json:"pullPolicy"`
   226  		Version    string `json:"version"`
   227  	}
   228  
   229  	// Ports contains all the port-related setups
   230  	Ports struct {
   231  		Admin    int32 `json:"admin"`
   232  		Control  int32 `json:"control"`
   233  		Inbound  int32 `json:"inbound"`
   234  		Outbound int32 `json:"outbound"`
   235  	}
   236  
   237  	Probe struct {
   238  		InitialDelaySeconds uint `json:"initialDelaySeconds"`
   239  		TimeoutSeconds      uint `json:"timeoutSeconds"`
   240  	}
   241  
   242  	// Constraints wraps the Limit and Request settings for computational resources
   243  	Constraints struct {
   244  		Limit   string `json:"limit"`
   245  		Request string `json:"request"`
   246  	}
   247  
   248  	// Capabilities contains the SecurityContext capabilities to add/drop into the injected
   249  	// containers
   250  	Capabilities struct {
   251  		Add  []string `json:"add"`
   252  		Drop []string `json:"drop"`
   253  	}
   254  
   255  	// VolumeMountPath contains the details for volume mounts
   256  	VolumeMountPath struct {
   257  		Name      string `json:"name"`
   258  		MountPath string `json:"mountPath"`
   259  		ReadOnly  bool   `json:"readOnly"`
   260  	}
   261  
   262  	// Resources represents the computational resources setup for a given container
   263  	Resources struct {
   264  		CPU              Constraints `json:"cpu"`
   265  		Memory           Constraints `json:"memory"`
   266  		EphemeralStorage Constraints `json:"ephemeral-storage"`
   267  	}
   268  
   269  	// StartupProbe represents the initContainer startup probe parameters for the proxy
   270  	StartupProbe struct {
   271  		InitialDelaySeconds uint `json:"initialDelaySeconds"`
   272  		PeriodSeconds       uint `json:"periodSeconds"`
   273  		FailureThreshold    uint `json:"failureThreshold"`
   274  	}
   275  
   276  	// Identity contains the fields to set the identity variables in the proxy
   277  	// sidecar container
   278  	Identity struct {
   279  		ExternalCA                    bool     `json:"externalCA"`
   280  		ServiceAccountTokenProjection bool     `json:"serviceAccountTokenProjection"`
   281  		Issuer                        *Issuer  `json:"issuer"`
   282  		KubeAPI                       *KubeAPI `json:"kubeAPI"`
   283  	}
   284  
   285  	// Issuer has the Helm variables of the identity issuer
   286  	Issuer struct {
   287  		Scheme             string     `json:"scheme"`
   288  		ClockSkewAllowance string     `json:"clockSkewAllowance"`
   289  		IssuanceLifetime   string     `json:"issuanceLifetime"`
   290  		TLS                *IssuerTLS `json:"tls"`
   291  	}
   292  
   293  	// KubeAPI contains the kube-apiserver client config
   294  	KubeAPI struct {
   295  		ClientQPS   float32 `json:"clientQPS"`
   296  		ClientBurst int     `json:"clientBurst"`
   297  	}
   298  
   299  	// Webhook Helm variables for a webhook
   300  	Webhook struct {
   301  		*TLS
   302  		NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector"`
   303  	}
   304  
   305  	// TLS has a pair of PEM-encoded key and certificate variables used in the
   306  	// Helm templates
   307  	TLS struct {
   308  		ExternalSecret     bool   `json:"externalSecret"`
   309  		KeyPEM             string `json:"keyPEM"`
   310  		CrtPEM             string `json:"crtPEM"`
   311  		CaBundle           string `json:"caBundle"`
   312  		InjectCaFrom       string `json:"injectCaFrom"`
   313  		InjectCaFromSecret string `json:"injectCaFromSecret"`
   314  	}
   315  
   316  	// IssuerTLS is a stripped down version of TLS that lacks the integral caBundle.
   317  	// It is tracked separately in the field 'IdentityTrustAnchorsPEM'
   318  	IssuerTLS struct {
   319  		KeyPEM string `json:"keyPEM"`
   320  		CrtPEM string `json:"crtPEM"`
   321  	}
   322  
   323  	Controller struct {
   324  		PodDistruptionBudget *PodDistruptionBudget `json:"podDistruptionBudget"`
   325  	}
   326  
   327  	PodDistruptionBudget struct {
   328  		MaxUnavailable int64 `json:"maxUnavailable"`
   329  	}
   330  
   331  	DestinationController struct {
   332  		MeshedHTTP2ClientProtobuf *MeshedHTTP2ClientProtobuf `json:"meshedHttp2ClientProtobuf"`
   333  	}
   334  
   335  	MeshedHTTP2ClientProtobuf struct {
   336  		KeepAlive *ProtobufKeepAlive `json:"keep_alive"`
   337  	}
   338  
   339  	ProtobufKeepAlive struct {
   340  		Interval  *DurationSeconds `json:"interval"`
   341  		Timeout   *DurationSeconds `json:"timeout"`
   342  		WhileIdle bool             `json:"while_idle"`
   343  	}
   344  
   345  	DurationSeconds struct {
   346  		Seconds int64 `json:"seconds"`
   347  	}
   348  )
   349  
   350  // NewValues returns a new instance of the Values type.
   351  func NewValues() (*Values, error) {
   352  	v, err := readDefaults(HelmChartDirCP + "/values.yaml")
   353  	if err != nil {
   354  		return nil, err
   355  	}
   356  
   357  	v.DebugContainer.Image.Version = version.Version
   358  	v.CliVersion = k8s.CreatedByAnnotationValue()
   359  	v.ProfileValidator.TLS = &TLS{}
   360  	v.ProxyInjector.TLS = &TLS{}
   361  	v.ProxyContainerName = k8s.ProxyContainerName
   362  
   363  	return v, nil
   364  }
   365  
   366  // FromConfigMap converts the data in linkerd-config into
   367  // a Values struct
   368  func FromConfigMap(cm *corev1.ConfigMap) (*Values, error) {
   369  	raw, ok := cm.Data["values"]
   370  	if !ok {
   371  		return nil, errors.New("Linkerd values not found in ConfigMap")
   372  	}
   373  	v := &Values{}
   374  	err := yaml.Unmarshal([]byte(raw), &v)
   375  	return v, err
   376  }
   377  
   378  // MergeHAValues retrieves the default HA values and merges them into the received values
   379  func MergeHAValues(values *Values) error {
   380  	haValues, err := readDefaults(HelmChartDirCP + "/values-ha.yaml")
   381  	if err != nil {
   382  		return err
   383  	}
   384  	*values, err = values.Merge(*haValues)
   385  	return err
   386  }
   387  
   388  // readDefaults read all the default variables from filename.
   389  func readDefaults(filename string) (*Values, error) {
   390  	valuesFile := &loader.BufferedFile{Name: filename}
   391  	if err := charts.ReadFile(static.Templates, "/", valuesFile); err != nil {
   392  		return nil, err
   393  	}
   394  
   395  	var values Values
   396  	err := yaml.Unmarshal(charts.InsertVersion(valuesFile.Data), &values)
   397  
   398  	return &values, err
   399  }
   400  
   401  // Merge merges the non-empty properties of src into v.
   402  // A new Values instance is returned. Neither src nor v are mutated after
   403  // calling Merge.
   404  func (v Values) Merge(src Values) (Values, error) {
   405  	// By default, mergo.Merge doesn't overwrite any existing non-empty values
   406  	// in its first argument. So in HA mode, we are merging values.yaml into
   407  	// values-ha.yaml, instead of the other way round (like Helm). This ensures
   408  	// that all the HA values take precedence.
   409  	if err := mergo.Merge(&src, v); err != nil {
   410  		return Values{}, err
   411  	}
   412  
   413  	return src, nil
   414  }
   415  
   416  // ToMap converts the Values intro a map[string]interface{}
   417  func (v *Values) ToMap() (map[string]interface{}, error) {
   418  	var valuesMap map[string]interface{}
   419  	rawValues, err := yaml.Marshal(v)
   420  	if err != nil {
   421  		return nil, fmt.Errorf("Failed to marshal the values struct: %w", err)
   422  	}
   423  
   424  	err = yaml.Unmarshal(rawValues, &valuesMap)
   425  	if err != nil {
   426  		return nil, fmt.Errorf("Failed to Unmarshal Values into a map: %w", err)
   427  	}
   428  
   429  	return valuesMap, nil
   430  }
   431  
   432  // DeepCopy creates a deep copy of the Values struct by marshalling to yaml and
   433  // then unmarshalling a new struct.
   434  func (v *Values) DeepCopy() (*Values, error) {
   435  	dst := Values{}
   436  	bytes, err := yaml.Marshal(v)
   437  	if err != nil {
   438  		return nil, err
   439  	}
   440  	err = yaml.Unmarshal(bytes, &dst)
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  	return &dst, nil
   445  }
   446  
   447  func (v *Values) String() string {
   448  	bytes, _ := yaml.Marshal(v)
   449  	return string(bytes)
   450  }
   451  

View as plain text