github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/cluster/config_wrapper.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package cluster 21 22 import ( 23 corev1 "k8s.io/api/core/v1" 24 "sigs.k8s.io/controller-runtime/pkg/client" 25 26 "github.com/1aal/kubeblocks/pkg/configuration/core" 27 28 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 29 "github.com/1aal/kubeblocks/pkg/cli/cluster" 30 "github.com/1aal/kubeblocks/pkg/cli/create" 31 "github.com/1aal/kubeblocks/pkg/cli/types" 32 "github.com/1aal/kubeblocks/pkg/cli/util" 33 cfgutil "github.com/1aal/kubeblocks/pkg/configuration/util" 34 ) 35 36 type configWrapper struct { 37 create.CreateOptions 38 39 clusterName string 40 updatedParams map[string]*string 41 42 // autofill field 43 componentName string 44 configSpecName string 45 configFileKey string 46 47 configTemplateSpec appsv1alpha1.ComponentConfigSpec 48 49 clusterObj *appsv1alpha1.Cluster 50 clusterDefObj *appsv1alpha1.ClusterDefinition 51 clusterVerObj *appsv1alpha1.ClusterVersion 52 } 53 54 func (w *configWrapper) ConfigTemplateSpec() *appsv1alpha1.ComponentConfigSpec { 55 return &w.configTemplateSpec 56 } 57 58 func (w *configWrapper) ConfigSpecName() string { 59 return w.configSpecName 60 } 61 62 func (w *configWrapper) ComponentName() string { 63 return w.componentName 64 } 65 66 func (w *configWrapper) ConfigFile() string { 67 return w.configFileKey 68 } 69 70 // AutoFillRequiredParam auto fills required param. 71 func (w *configWrapper) AutoFillRequiredParam() error { 72 if err := w.fillComponent(); err != nil { 73 return err 74 } 75 if err := w.fillConfigSpec(); err != nil { 76 return err 77 } 78 return w.fillConfigFile() 79 } 80 81 // ValidateRequiredParam validates required param. 82 func (w *configWrapper) ValidateRequiredParam(forceReplace bool) error { 83 // step1: check existence of component. 84 if w.clusterObj.Spec.GetComponentByName(w.componentName) == nil { 85 return makeComponentNotExistErr(w.clusterName, w.componentName) 86 } 87 88 // step2: check existence of configmap 89 cmObj := corev1.ConfigMap{} 90 cmKey := client.ObjectKey{ 91 Name: core.GetComponentCfgName(w.clusterName, w.componentName, w.configSpecName), 92 Namespace: w.Namespace, 93 } 94 if err := util.GetResourceObjectFromGVR(types.ConfigmapGVR(), cmKey, w.Dynamic, &cmObj); err != nil { 95 return err 96 } 97 98 // step3: check existence of config file 99 if _, ok := cmObj.Data[w.configFileKey]; !ok { 100 return makeNotFoundConfigFileErr(w.configFileKey, w.configSpecName, cfgutil.ToSet(cmObj.Data).AsSlice()) 101 } 102 103 if !forceReplace && !core.IsSupportConfigFileReconfigure(w.configTemplateSpec, w.configFileKey) { 104 return makeNotSupportConfigFileUpdateErr(w.configFileKey, w.configTemplateSpec) 105 } 106 return nil 107 } 108 109 func (w *configWrapper) fillComponent() error { 110 if w.componentName != "" { 111 return nil 112 } 113 componentNames, err := util.GetComponentsFromResource(w.clusterObj.Spec.ComponentSpecs, w.clusterDefObj) 114 if err != nil { 115 return err 116 } 117 if len(componentNames) != 1 { 118 return core.MakeError(multiComponentsErrorMessage) 119 } 120 w.componentName = componentNames[0] 121 return nil 122 } 123 124 func (w *configWrapper) fillConfigSpec() error { 125 foundConfigSpec := func(configSpecs []appsv1alpha1.ComponentConfigSpec, name string) *appsv1alpha1.ComponentConfigSpec { 126 for _, configSpec := range configSpecs { 127 if configSpec.Name == name { 128 w.configTemplateSpec = configSpec 129 return &configSpec 130 } 131 } 132 return nil 133 } 134 135 var vComponents []appsv1alpha1.ClusterComponentVersion 136 var cComponents = w.clusterObj.Spec.ComponentSpecs 137 var dComponents = w.clusterDefObj.Spec.ComponentDefs 138 139 if w.clusterVerObj != nil { 140 vComponents = w.clusterVerObj.Spec.ComponentVersions 141 } 142 143 configSpecs, err := util.GetConfigTemplateListWithResource(cComponents, dComponents, vComponents, w.componentName, w.configSpecName == "") 144 if err != nil { 145 return err 146 } 147 if len(configSpecs) == 0 { 148 return makeNotFoundTemplateErr(w.clusterName, w.componentName) 149 } 150 151 if w.configSpecName != "" { 152 if foundConfigSpec(configSpecs, w.configSpecName) == nil { 153 return makeConfigSpecNotExistErr(w.clusterName, w.componentName, w.configSpecName) 154 } 155 return nil 156 } 157 158 w.configTemplateSpec = configSpecs[0] 159 if len(configSpecs) == 1 { 160 w.configSpecName = configSpecs[0].Name 161 return nil 162 } 163 164 supportUpdatedTpl := make([]appsv1alpha1.ComponentConfigSpec, 0) 165 for _, configSpec := range configSpecs { 166 if ok, err := util.IsSupportReconfigureParams(configSpec, w.updatedParams, w.Dynamic); err == nil && ok { 167 supportUpdatedTpl = append(supportUpdatedTpl, configSpec) 168 } 169 } 170 if len(supportUpdatedTpl) == 1 { 171 w.configTemplateSpec = configSpecs[0] 172 w.configSpecName = supportUpdatedTpl[0].Name 173 return nil 174 } 175 return core.MakeError(multiConfigTemplateErrorMessage) 176 } 177 178 func (w *configWrapper) fillConfigFile() error { 179 if w.configFileKey != "" { 180 return nil 181 } 182 183 if w.configTemplateSpec.TemplateRef == "" { 184 return makeNotFoundTemplateErr(w.clusterName, w.componentName) 185 } 186 187 cmObj := corev1.ConfigMap{} 188 cmKey := client.ObjectKey{ 189 Name: core.GetComponentCfgName(w.clusterName, w.componentName, w.configSpecName), 190 Namespace: w.Namespace, 191 } 192 if err := util.GetResourceObjectFromGVR(types.ConfigmapGVR(), cmKey, w.Dynamic, &cmObj); err != nil { 193 return err 194 } 195 if len(cmObj.Data) == 0 { 196 return core.MakeError("not supported reconfiguring because there is no config file.") 197 } 198 199 keys := w.filterForReconfiguring(cmObj.Data) 200 if len(keys) == 1 { 201 w.configFileKey = keys[0] 202 return nil 203 } 204 return core.MakeError(multiConfigFileErrorMessage) 205 } 206 207 func (w *configWrapper) filterForReconfiguring(data map[string]string) []string { 208 keys := make([]string, 0, len(data)) 209 for configFileKey := range data { 210 if core.IsSupportConfigFileReconfigure(w.configTemplateSpec, configFileKey) { 211 keys = append(keys, configFileKey) 212 } 213 } 214 return keys 215 } 216 217 func newConfigWrapper(baseOptions create.CreateOptions, clusterName, componentName, configSpec, configKey string, params map[string]*string) (*configWrapper, error) { 218 var ( 219 err error 220 clusterObj *appsv1alpha1.Cluster 221 clusterDefObj *appsv1alpha1.ClusterDefinition 222 ) 223 224 if clusterObj, err = cluster.GetClusterByName(baseOptions.Dynamic, clusterName, baseOptions.Namespace); err != nil { 225 return nil, err 226 } 227 if clusterDefObj, err = cluster.GetClusterDefByName(baseOptions.Dynamic, clusterObj.Spec.ClusterDefRef); err != nil { 228 return nil, err 229 } 230 231 w := &configWrapper{ 232 CreateOptions: baseOptions, 233 clusterObj: clusterObj, 234 clusterDefObj: clusterDefObj, 235 clusterName: clusterName, 236 237 componentName: componentName, 238 configSpecName: configSpec, 239 configFileKey: configKey, 240 updatedParams: params, 241 } 242 243 if w.clusterObj.Spec.ClusterVersionRef == "" { 244 return w, err 245 } 246 247 clusterVerObj := &appsv1alpha1.ClusterVersion{} 248 if err := util.GetResourceObjectFromGVR(types.ClusterVersionGVR(), client.ObjectKey{ 249 Namespace: "", 250 Name: w.clusterObj.Spec.ClusterVersionRef, 251 }, w.Dynamic, clusterVerObj); err != nil { 252 return nil, err 253 } 254 255 w.clusterVerObj = clusterVerObj 256 return w, nil 257 }