github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/apis/extensions/v1alpha1/addon_types.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1alpha1
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"strings"
    23  
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/version"
    27  
    28  	"github.com/1aal/kubeblocks/pkg/constant"
    29  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    30  )
    31  
    32  // AddonSpec defines the desired state of an add-on.
    33  // +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Helm' ?  has(self.helm) : !has(self.helm)",message="spec.helm is required when spec.type is Helm, and forbidden otherwise"
    34  type AddonSpec struct {
    35  	// Addon description.
    36  	// +optional
    37  	Description string `json:"description,omitempty"`
    38  
    39  	// Add-on type. The valid value is helm.
    40  	// +unionDiscriminator
    41  	// +kubebuilder:validation:Required
    42  	Type AddonType `json:"type"`
    43  
    44  	// Helm installation spec. It's processed only when type=helm.
    45  	// +optional
    46  	Helm *HelmTypeInstallSpec `json:"helm,omitempty"`
    47  
    48  	// Default installation parameters.
    49  	// +kubebuilder:validation:Required
    50  	// +kubebuilder:validation:MinItems=1
    51  	DefaultInstallValues []AddonDefaultInstallSpecItem `json:"defaultInstallValues"`
    52  
    53  	// Installation parameters.
    54  	// +optional
    55  	InstallSpec *AddonInstallSpec `json:"install,omitempty"`
    56  
    57  	// Addon installable spec. It provides selector and auto-install settings.
    58  	// +optional
    59  	Installable *InstallableSpec `json:"installable,omitempty"`
    60  
    61  	// Plugin installation spec.
    62  	// +optional
    63  	CliPlugins []CliPlugin `json:"cliPlugins,omitempty"`
    64  }
    65  
    66  // AddonStatus defines the observed state of an add-on.
    67  type AddonStatus struct {
    68  	// Add-on installation phases. Valid values are Disabled, Enabled, Failed, Enabling, Disabling.
    69  	// +kubebuilder:validation:Enum={Disabled,Enabled,Failed,Enabling,Disabling}
    70  	Phase AddonPhase `json:"phase,omitempty"`
    71  
    72  	// Describes the current state of add-on API installation conditions.
    73  	// +optional
    74  	Conditions []metav1.Condition `json:"conditions,omitempty"`
    75  
    76  	// observedGeneration is the most recent generation observed for this
    77  	// add-on. It corresponds to the add-on's generation, which is
    78  	// updated on mutation by the API Server.
    79  	// +optional
    80  	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
    81  }
    82  
    83  type InstallableSpec struct {
    84  	// Add-on installable selectors. If multiple selectors are provided,
    85  	// all selectors must evaluate to true.
    86  	// +optional
    87  	Selectors []SelectorRequirement `json:"selectors,omitempty"`
    88  
    89  	// autoInstall defines an add-on should be installed automatically.
    90  	// +kubebuilder:default=false
    91  	AutoInstall bool `json:"autoInstall"`
    92  }
    93  
    94  type SelectorRequirement struct {
    95  	// The selector key. Valid values are KubeVersion, KubeGitVersion and KubeProvider.
    96  	// "KubeVersion" the semver expression of Kubernetes versions, i.e., v1.24.
    97  	// "KubeGitVersion" may contain distro. info., i.e., v1.24.4+eks.
    98  	// "KubeProvider" the Kubernetes provider, i.e., aws,gcp,azure,huaweiCloud,tencentCloud etc.
    99  	// +kubebuilder:validation:Required
   100  	Key AddonSelectorKey `json:"key"`
   101  
   102  	// Represents a key's relationship to a set of values.
   103  	// Valid operators are Contains, NotIn, DoesNotContain, MatchRegex, and DoesNoteMatchRegex.
   104  	//
   105  	// Possible enum values:
   106  	// `"Contains"` line contains a string.
   107  	// `"DoesNotContain"` line does not contain a string.
   108  	// `"MatchRegex"` line contains a match to the regular expression.
   109  	// `"DoesNotMatchRegex"` line does not contain a match to the regular expression.
   110  	// +kubebuilder:validation:Required
   111  	Operator LineSelectorOperator `json:"operator"`
   112  
   113  	// An array of string values. It serves as an "OR" expression to the operator.
   114  	// +optional
   115  	Values []string `json:"values,omitempty" protobuf:"bytes,3,rep,name=values"`
   116  }
   117  
   118  // HelmTypeInstallSpec defines the Helm installation spec.
   119  // +kubebuilder:validation:XValidation:rule="self.chartLocationURL.startsWith('file://') ? has(self.chartsImage) : true",message="chartsImage is required when chartLocationURL starts with 'file://'"
   120  type HelmTypeInstallSpec struct {
   121  	// A Helm Chart location URL.
   122  	// +kubebuilder:validation:Required
   123  	ChartLocationURL string `json:"chartLocationURL"`
   124  
   125  	// installOptions defines Helm release installation options.
   126  	// +optional
   127  	InstallOptions HelmInstallOptions `json:"installOptions,omitempty"`
   128  
   129  	// HelmInstallValues defines Helm release installation set values.
   130  	// +optional
   131  	InstallValues HelmInstallValues `json:"installValues,omitempty"`
   132  
   133  	// valuesMapping defines add-on normalized resources parameters mapped to Helm values' keys.
   134  	// +optional
   135  	ValuesMapping HelmValuesMapping `json:"valuesMapping,omitempty"`
   136  
   137  	// chartsImage defines the image of Helm charts.
   138  	// +optional
   139  	ChartsImage string `json:"chartsImage,omitempty"`
   140  
   141  	// chartsPathInImage defines the path of Helm charts in the image. It's used to copy
   142  	// Helm charts from the image to the shared volume.
   143  	// +kubeBuilder:default="/charts"
   144  	// +optional
   145  	ChartsPathInImage string `json:"chartsPathInImage,omitempty"`
   146  }
   147  
   148  type HelmInstallOptions map[string]string
   149  
   150  type HelmInstallValues struct {
   151  	// +optional
   152  	URLs []string `json:"urls,omitempty"`
   153  
   154  	// Selects a key of a ConfigMap item list. The value of ConfigMap can be
   155  	// a JSON or YAML string content. Use a key name with ".json" or ".yaml" or ".yml"
   156  	// extension name to specify a content type.
   157  	// +optional
   158  	ConfigMapRefs []DataObjectKeySelector `json:"configMapRefs,omitempty"`
   159  
   160  	// Selects a key of a Secrets item list. The value of Secrets can be
   161  	// a JSON or YAML string content. Use a key name with ".json" or ".yaml" or ".yml"
   162  	// extension name to specify a content type.
   163  	// +optional
   164  	SecretRefs []DataObjectKeySelector `json:"secretRefs,omitempty"`
   165  
   166  	// Helm install set values. It can specify multiple or separate values with commas(key1=val1,key2=val2).
   167  	// +optional
   168  	SetValues []string `json:"setValues,omitempty"`
   169  
   170  	// Helm install set JSON values. It can specify multiple or separate values with commas(key1=jsonval1,key2=jsonval2).
   171  	// +optional
   172  	SetJSONValues []string `json:"setJSONValues,omitempty"`
   173  }
   174  
   175  type HelmValuesMapping struct {
   176  	HelmValuesMappingItem `json:",inline"`
   177  
   178  	// Helm value mapping items for extra items.
   179  	// +patchMergeKey=name
   180  	// +patchStrategy=merge,retainKeys
   181  	// +listType=map
   182  	// +listMapKey=name
   183  	// +optional
   184  	ExtraItems []HelmValuesMappingExtraItem `json:"extras,omitempty"`
   185  }
   186  
   187  type HelmValuesMappingExtraItem struct {
   188  	HelmValuesMappingItem `json:",inline"`
   189  
   190  	// Name of the item.
   191  	// +kubebuilder:validation:Required
   192  	Name string `json:"name"`
   193  }
   194  
   195  type HelmValueMapType struct {
   196  	// replicaCount sets the replicaCount value mapping key.
   197  	// +optional
   198  	ReplicaCount string `json:"replicaCount,omitempty"`
   199  
   200  	// persistentVolumeEnabled sets the persistent volume enabled mapping key.
   201  	// +optional
   202  	PVEnabled string `json:"persistentVolumeEnabled,omitempty"`
   203  
   204  	// storageClass sets the storageClass mapping key.
   205  	// +optional
   206  	StorageClass string `json:"storageClass,omitempty"`
   207  }
   208  
   209  type HelmJSONValueMapType struct {
   210  	// tolerations sets the toleration mapping key.
   211  	// +optional
   212  	Tolerations string `json:"tolerations,omitempty"`
   213  }
   214  
   215  type HelmValuesMappingItem struct {
   216  	// valueMap define the "key" mapping values. Valid keys are replicaCount,
   217  	// persistentVolumeEnabled, and storageClass. Enum values explained:
   218  	// `"replicaCount"` sets the replicaCount value mapping key.
   219  	// `"persistentVolumeEnabled"` sets the persistent volume enabled mapping key.
   220  	// `"storageClass"` sets the storageClass mapping key.
   221  	// +optional
   222  	HelmValueMap HelmValueMapType `json:"valueMap,omitempty"`
   223  
   224  	// jsonMap defines the "key" mapping values. The valid key is tolerations.
   225  	// Enum values explained:
   226  	// `"tolerations"` sets the toleration mapping key.
   227  	// +optional
   228  	HelmJSONMap HelmJSONValueMapType `json:"jsonMap,omitempty"`
   229  
   230  	// resources sets resources related mapping keys.
   231  	// +optional
   232  	ResourcesMapping *ResourceMappingItem `json:"resources,omitempty"`
   233  }
   234  
   235  type ResourceMappingItem struct {
   236  
   237  	// storage sets the storage size value mapping key.
   238  	// +optional
   239  	Storage string `json:"storage,omitempty"`
   240  
   241  	// cpu sets CPU requests and limits mapping keys.
   242  	// +optional
   243  	CPU *ResourceReqLimItem `json:"cpu,omitempty"`
   244  
   245  	// memory sets Memory requests and limits mapping keys.
   246  	// +optional
   247  	Memory *ResourceReqLimItem `json:"memory,omitempty"`
   248  }
   249  
   250  type CliPlugin struct {
   251  	// Name of the plugin.
   252  	// +kubebuilder:validation:Required
   253  	Name string `json:"name"`
   254  
   255  	// The index repository of the plugin.
   256  	// +kubebuilder:validation:Required
   257  	IndexRepository string `json:"indexRepository"`
   258  
   259  	// The description of the plugin.
   260  	// +optional
   261  	Description string `json:"description,omitempty"`
   262  }
   263  
   264  func (r *ResourceMappingItem) HasStorageMapping() bool {
   265  	return !(r == nil || r.Storage == "")
   266  }
   267  
   268  func (r *ResourceMappingItem) HasCPUReqMapping() bool {
   269  	return !(r == nil || r.CPU == nil || r.CPU.Requests == "")
   270  }
   271  
   272  func (r *ResourceMappingItem) HasMemReqMapping() bool {
   273  	return !(r == nil || r.CPU == nil || r.Memory.Requests == "")
   274  }
   275  
   276  func (r *ResourceMappingItem) HasCPULimMapping() bool {
   277  	return !(r == nil || r.CPU == nil || r.CPU.Limits == "")
   278  }
   279  
   280  func (r *ResourceMappingItem) HasMemLimMapping() bool {
   281  	return !(r == nil || r.CPU == nil || r.Memory.Limits == "")
   282  }
   283  
   284  type ResourceReqLimItem struct {
   285  	// Requests value mapping key.
   286  	// +optional
   287  	Requests string `json:"requests,omitempty"`
   288  
   289  	// Limits value mapping key.
   290  	// +optional
   291  	Limits string `json:"limits,omitempty"`
   292  }
   293  
   294  type DataObjectKeySelector struct {
   295  	// Object name of the referent.
   296  	// +kubebuilder:validation:Required
   297  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
   298  	Name string `json:"name"` // need corev1.LocalObjectReference
   299  
   300  	// The key to select.
   301  	// +kubebuilder:validation:Required
   302  	Key string `json:"key"`
   303  }
   304  
   305  type AddonDefaultInstallSpecItem struct {
   306  	AddonInstallSpec `json:",inline"`
   307  
   308  	// Addon installs parameters selectors by default. If multiple selectors are provided,
   309  	// all selectors must evaluate to true.
   310  	// +optional
   311  	Selectors []SelectorRequirement `json:"selectors,omitempty"`
   312  }
   313  
   314  type AddonInstallSpec struct {
   315  	AddonInstallSpecItem `json:",inline"`
   316  
   317  	// enabled can be set if there are no specific installation attributes to be set.
   318  	// +optional
   319  	Enabled bool `json:"enabled,omitempty"`
   320  
   321  	// Installs spec. for extra items.
   322  	// +patchMergeKey=name
   323  	// +patchStrategy=merge,retainKeys
   324  	// +listType=map
   325  	// +listMapKey=name
   326  	// +optional
   327  	ExtraItems []AddonInstallExtraItem `json:"extras,omitempty"`
   328  }
   329  
   330  func (r *AddonInstallSpec) IsDisabled() bool {
   331  	return r == nil || !r.Enabled
   332  }
   333  
   334  func (r *AddonInstallSpec) HasSetValues() bool {
   335  	if r == nil {
   336  		return false
   337  	}
   338  
   339  	if !r.AddonInstallSpecItem.IsEmpty() {
   340  		return true
   341  	}
   342  	for _, i := range r.ExtraItems {
   343  		if !i.IsEmpty() {
   344  			return true
   345  		}
   346  	}
   347  	return false
   348  }
   349  
   350  type AddonInstallExtraItem struct {
   351  	AddonInstallSpecItem `json:",inline"`
   352  
   353  	// Name of the item.
   354  	// +kubebuilder:validation:Required
   355  	Name string `json:"name"`
   356  }
   357  
   358  type AddonInstallSpecItem struct {
   359  	// Replicas value.
   360  	// +optional
   361  	Replicas *int32 `json:"replicas,omitempty"`
   362  
   363  	// Persistent Volume Enabled value.
   364  	// +optional
   365  	PVEnabled *bool `json:"persistentVolumeEnabled,omitempty"`
   366  
   367  	// Storage class name.
   368  	// +optional
   369  	StorageClass string `json:"storageClass,omitempty"`
   370  
   371  	// Tolerations JSON array string value.
   372  	// +optional
   373  	Tolerations string `json:"tolerations,omitempty"`
   374  
   375  	// Resource requirements.
   376  	// +optional
   377  	Resources ResourceRequirements `json:"resources,omitempty"`
   378  }
   379  
   380  func (r AddonInstallSpecItem) IsEmpty() bool {
   381  	return r.Replicas == nil &&
   382  		r.PVEnabled == nil &&
   383  		r.StorageClass == "" &&
   384  		r.Tolerations == "" &&
   385  		len(r.Resources.Requests) == 0
   386  }
   387  
   388  type ResourceRequirements struct {
   389  	// Limits describes the maximum amount of compute resources allowed.
   390  	// More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/.
   391  	// +optional
   392  	Limits corev1.ResourceList `json:"limits,omitempty"`
   393  	// Requests describes the minimum amount of compute resources required.
   394  	// If Requests is omitted for a container, it defaults to Limits if that is explicitly specified;
   395  	// otherwise, it defaults to an implementation-defined value.
   396  	// More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/.
   397  	// +optional
   398  	Requests corev1.ResourceList `json:"requests,omitempty"`
   399  }
   400  
   401  // +genclient
   402  // +genclient:nonNamespaced
   403  // +k8s:openapi-gen=true
   404  // +kubebuilder:object:root=true
   405  // +kubebuilder:subresource:status
   406  // +kubebuilder:resource:categories={kubeblocks},scope=Cluster
   407  // +kubebuilder:printcolumn:name="TYPE",type="string",JSONPath=".spec.type",description="addon types"
   408  // +kubebuilder:printcolumn:name="STATUS",type="string",JSONPath=".status.phase",description="status phase"
   409  // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
   410  
   411  // Addon is the Schema for the add-ons API.
   412  type Addon struct {
   413  	metav1.TypeMeta   `json:",inline"`
   414  	metav1.ObjectMeta `json:"metadata,omitempty"`
   415  
   416  	Spec   AddonSpec   `json:"spec,omitempty"`
   417  	Status AddonStatus `json:"status,omitempty"`
   418  }
   419  
   420  // +kubebuilder:object:root=true
   421  
   422  // AddonList contains a list of add-ons.
   423  type AddonList struct {
   424  	metav1.TypeMeta `json:",inline"`
   425  	metav1.ListMeta `json:"metadata,omitempty"`
   426  	Items           []Addon `json:"items"`
   427  }
   428  
   429  func init() {
   430  	SchemeBuilder.Register(&Addon{}, &AddonList{})
   431  }
   432  
   433  // GetExtraNames extracts extra items' name.
   434  func (r *Addon) GetExtraNames() []string {
   435  	if r == nil {
   436  		return nil
   437  	}
   438  	switch r.Spec.Type {
   439  	case HelmType:
   440  		if r.Spec.Helm == nil || len(r.Spec.Helm.ValuesMapping.ExtraItems) == 0 {
   441  			return nil
   442  		}
   443  		// r.Spec.DefaultInstallValues has minItem=1 constraint
   444  		names := make([]string, 0, len(r.Spec.Helm.ValuesMapping.ExtraItems))
   445  		for _, i := range r.Spec.Helm.ValuesMapping.ExtraItems {
   446  			names = append(names, i.Name)
   447  		}
   448  		return names
   449  	default:
   450  		return nil
   451  	}
   452  
   453  }
   454  
   455  func buildSelectorStrings(selectors []SelectorRequirement) []string {
   456  	l := len(selectors)
   457  	if l == 0 {
   458  		return nil
   459  	}
   460  	sl := make([]string, 0, l)
   461  	for _, req := range selectors {
   462  		sl = append(sl, req.String())
   463  	}
   464  	return sl
   465  }
   466  
   467  // GetSelectorsStrings extracts selectors to string representations.
   468  func (r AddonDefaultInstallSpecItem) GetSelectorsStrings() []string {
   469  	return buildSelectorStrings(r.Selectors)
   470  }
   471  
   472  // GetSelectorsStrings extracts selectors to string representations.
   473  func (r *InstallableSpec) GetSelectorsStrings() []string {
   474  	if r == nil {
   475  		return nil
   476  	}
   477  	return buildSelectorStrings(r.Selectors)
   478  }
   479  
   480  func (r SelectorRequirement) String() string {
   481  	return fmt.Sprintf("{key=%s,op=%s,values=%v}",
   482  		r.Key, r.Operator, r.Values)
   483  }
   484  
   485  // MatchesFromConfig matches the selector requirement value.
   486  func (r SelectorRequirement) MatchesFromConfig() bool {
   487  	verIf := viper.Get(constant.CfgKeyServerInfo)
   488  	ver, ok := verIf.(version.Info)
   489  	if !ok {
   490  		return false
   491  	}
   492  	var l string
   493  	switch r.Key {
   494  	case KubeGitVersion:
   495  		l = ver.GitVersion
   496  	case KubeVersion:
   497  		l = fmt.Sprintf("%s.%s", ver.Major, ver.Minor)
   498  	case KubeProvider:
   499  		l = viper.GetString(constant.CfgKeyProvider)
   500  	}
   501  	return r.matchesLine(l)
   502  }
   503  
   504  func (r SelectorRequirement) matchesLine(line string) bool {
   505  	processor := func(op bool, predicate func(string) bool) bool {
   506  		if len(r.Values) == 0 {
   507  			return !op
   508  		}
   509  		for _, v := range r.Values {
   510  			m := predicate(v)
   511  			if op && m {
   512  				return true
   513  			} else if !op {
   514  				if m {
   515  					return false
   516  				}
   517  				continue
   518  			}
   519  		}
   520  		return !op
   521  	}
   522  
   523  	containsProcessor := func(op bool) bool {
   524  		return processor(op, func(v string) bool {
   525  			return strings.Contains(line, v)
   526  		})
   527  	}
   528  
   529  	matchRegexProcessor := func(op bool) bool {
   530  		return processor(op, func(v string) bool {
   531  			regex, err := regexp.Compile(v)
   532  			if err != nil {
   533  				return false
   534  			}
   535  			return regex.Match([]byte(line))
   536  		})
   537  	}
   538  
   539  	switch r.Operator {
   540  	case Contains:
   541  		return containsProcessor(true)
   542  	case DoesNotContain:
   543  		return containsProcessor(false)
   544  	case MatchRegex:
   545  		return matchRegexProcessor(true)
   546  	case DoesNotMatchRegex:
   547  		return matchRegexProcessor(false)
   548  	default:
   549  		return false
   550  	}
   551  }
   552  
   553  // GetEnabled provides the Enabled property getter.
   554  func (r *AddonInstallSpec) GetEnabled() bool {
   555  	if r == nil {
   556  		return false
   557  	}
   558  	return r.Enabled
   559  }
   560  
   561  // BuildMergedValues merges values from a AddonInstallSpec and pre-set values.
   562  func (r *HelmTypeInstallSpec) BuildMergedValues(installSpec *AddonInstallSpec) HelmInstallValues {
   563  	if r == nil {
   564  		return HelmInstallValues{}
   565  	}
   566  	installValues := r.InstallValues
   567  	processor := func(installSpecItem AddonInstallSpecItem, valueMapping HelmValuesMappingItem) {
   568  		var pvEnabled *bool
   569  		defer func() {
   570  			if v := valueMapping.HelmValueMap.PVEnabled; v != "" && pvEnabled != nil {
   571  				installValues.SetValues = append(installValues.SetValues,
   572  					fmt.Sprintf("%s=%v", v, *pvEnabled))
   573  			}
   574  		}()
   575  
   576  		if installSpecItem.PVEnabled != nil {
   577  			b := *installSpecItem.PVEnabled
   578  			pvEnabled = &b
   579  		}
   580  
   581  		if installSpecItem.Replicas != nil && *installSpecItem.Replicas >= 0 {
   582  			if v := valueMapping.HelmValueMap.ReplicaCount; v != "" {
   583  				installValues.SetValues = append(installValues.SetValues,
   584  					fmt.Sprintf("%s=%v", v, *installSpecItem.Replicas))
   585  			}
   586  		}
   587  
   588  		if installSpecItem.StorageClass != "" {
   589  			if v := valueMapping.HelmValueMap.StorageClass; v != "" {
   590  				if installSpecItem.StorageClass == "-" {
   591  					installValues.SetValues = append(installValues.SetValues,
   592  						fmt.Sprintf("%s=null", v))
   593  				} else {
   594  					installValues.SetValues = append(installValues.SetValues,
   595  						fmt.Sprintf("%s=%v", v, installSpecItem.StorageClass))
   596  				}
   597  			}
   598  		}
   599  
   600  		if installSpecItem.Tolerations != "" {
   601  			if v := valueMapping.HelmJSONMap.Tolerations; v != "" {
   602  				installValues.SetJSONValues = append(installValues.SetJSONValues,
   603  					fmt.Sprintf("%s=%s", v, installSpecItem.Tolerations))
   604  			}
   605  		}
   606  
   607  		if valueMapping.ResourcesMapping == nil {
   608  			return
   609  		}
   610  
   611  		for k, v := range installSpecItem.Resources.Requests {
   612  			switch k {
   613  			case corev1.ResourceStorage:
   614  				if valueMapping.ResourcesMapping.HasStorageMapping() {
   615  					installValues.SetValues = append(installValues.SetValues,
   616  						fmt.Sprintf("%s=%v", valueMapping.ResourcesMapping.Storage, v.ToUnstructured()))
   617  				}
   618  			case corev1.ResourceCPU:
   619  				if valueMapping.ResourcesMapping.HasCPUReqMapping() {
   620  					installValues.SetValues = append(installValues.SetValues,
   621  						fmt.Sprintf("%s=%v", valueMapping.ResourcesMapping.CPU.Requests, v.ToUnstructured()))
   622  				}
   623  			case corev1.ResourceMemory:
   624  				if valueMapping.ResourcesMapping.HasMemReqMapping() {
   625  					installValues.SetValues = append(installValues.SetValues,
   626  						fmt.Sprintf("%s=%v", valueMapping.ResourcesMapping.Memory.Requests, v.ToUnstructured()))
   627  				}
   628  			}
   629  		}
   630  
   631  		for k, v := range installSpecItem.Resources.Limits {
   632  			switch k {
   633  			case corev1.ResourceCPU:
   634  				if valueMapping.ResourcesMapping.HasCPULimMapping() {
   635  					installValues.SetValues = append(installValues.SetValues,
   636  						fmt.Sprintf("%s=%v", valueMapping.ResourcesMapping.CPU.Limits, v.ToUnstructured()))
   637  				}
   638  			case corev1.ResourceMemory:
   639  				if valueMapping.ResourcesMapping.HasMemLimMapping() {
   640  					installValues.SetValues = append(installValues.SetValues,
   641  						fmt.Sprintf("%s=%v", valueMapping.ResourcesMapping.Memory.Limits, v.ToUnstructured()))
   642  				}
   643  			}
   644  		}
   645  
   646  	}
   647  	processor(installSpec.AddonInstallSpecItem, r.ValuesMapping.HelmValuesMappingItem)
   648  	for _, ei := range installSpec.ExtraItems {
   649  		for _, mei := range r.ValuesMapping.ExtraItems {
   650  			if ei.Name != mei.Name {
   651  				continue
   652  			}
   653  			processor(ei.AddonInstallSpecItem, mei.HelmValuesMappingItem)
   654  			break
   655  		}
   656  	}
   657  	return installValues
   658  }
   659  
   660  // BuildContainerArgs derives helm container args.
   661  func (r *HelmTypeInstallSpec) BuildContainerArgs(helmContainer *corev1.Container, installValues HelmInstallValues) error {
   662  	// Add extra helm installation option flags
   663  	for k, v := range r.InstallOptions {
   664  		helmContainer.Args = append(helmContainer.Args, fmt.Sprintf("--%s", k))
   665  		if v != "" {
   666  			helmContainer.Args = append(helmContainer.Args, v)
   667  		}
   668  	}
   669  
   670  	// Sets values from URL.
   671  	for _, urlValue := range installValues.URLs {
   672  		helmContainer.Args = append(helmContainer.Args, "--values", urlValue)
   673  	}
   674  
   675  	// Sets key1=val1,key2=val2 value.
   676  	if len(installValues.SetValues) > 0 {
   677  		helmContainer.Args = append(helmContainer.Args, "--set",
   678  			strings.Join(installValues.SetValues, ","))
   679  	}
   680  
   681  	// Sets key1=jsonval1,key2=jsonval2 JSON value. It can be applied to multiple.
   682  	for _, v := range installValues.SetJSONValues {
   683  		helmContainer.Args = append(helmContainer.Args, "--set-json", v)
   684  	}
   685  	return nil
   686  }
   687  
   688  // GetSortedDefaultInstallValues returns DefaultInstallValues items with items that have
   689  // a provided selector first.
   690  func (r AddonSpec) GetSortedDefaultInstallValues() []AddonDefaultInstallSpecItem {
   691  	values := make([]AddonDefaultInstallSpecItem, 0, len(r.DefaultInstallValues))
   692  	nvalues := make([]AddonDefaultInstallSpecItem, 0, len(r.DefaultInstallValues))
   693  	for _, i := range r.DefaultInstallValues {
   694  		if len(i.Selectors) > 0 {
   695  			values = append(values, i)
   696  		} else {
   697  			nvalues = append(nvalues, i)
   698  		}
   699  	}
   700  	if len(nvalues) > 0 {
   701  		values = append(values, nvalues...)
   702  	}
   703  	return values
   704  }
   705  
   706  // NewAddonInstallSpecItem creates an initialized AddonInstallSpecItem object.
   707  func NewAddonInstallSpecItem() AddonInstallSpecItem {
   708  	return AddonInstallSpecItem{
   709  		Resources: ResourceRequirements{
   710  			Requests: corev1.ResourceList{},
   711  			Limits:   corev1.ResourceList{},
   712  		},
   713  	}
   714  }