github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/clusterfile/clusterfile.go (about) 1 // Copyright © 2021 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package clusterfile 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "os" 22 "path/filepath" 23 24 "github.com/sealerio/sealer/common" 25 "github.com/sealerio/sealer/pkg/client/k8s" 26 "github.com/sealerio/sealer/pkg/runtime/kubernetes" 27 "github.com/sealerio/sealer/pkg/runtime/kubernetes/kubeadm" 28 v1 "github.com/sealerio/sealer/types/api/v1" 29 v2 "github.com/sealerio/sealer/types/api/v2" 30 utilsos "github.com/sealerio/sealer/utils/os" 31 "github.com/sirupsen/logrus" 32 corev1 "k8s.io/api/core/v1" 33 apierrors "k8s.io/apimachinery/pkg/api/errors" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "sigs.k8s.io/controller-runtime/pkg/client" 36 "sigs.k8s.io/yaml" 37 ) 38 39 const ( 40 ClusterfileConfigMapNamespace = "kube-system" 41 ClusterfileConfigMapName = "sealer-clusterfile" 42 ClusterfileConfigMapDataName = "Clusterfile" 43 ) 44 45 type Interface interface { 46 GetCluster() v2.Cluster 47 SetCluster(v2.Cluster) 48 SetApplication(app v2.Application) 49 GetConfigs() []v1.Config 50 GetPlugins() []v1.Plugin 51 GetApplication() *v2.Application 52 GetKubeadmConfig() *kubeadm.KubeadmConfig 53 SaveAll(opts SaveOptions) error 54 } 55 56 type SaveOptions struct { 57 // if true ,will commit clusterfile to cluster 58 CommitToCluster bool 59 ConfPath string 60 } 61 62 type ClusterFile struct { 63 cluster *v2.Cluster 64 configs []v1.Config 65 kubeadmConfig kubeadm.KubeadmConfig 66 plugins []v1.Plugin 67 app *v2.Application 68 } 69 70 func (c *ClusterFile) GetCluster() v2.Cluster { 71 return *c.cluster 72 } 73 74 func (c *ClusterFile) SetCluster(cluster v2.Cluster) { 75 c.cluster = &cluster 76 } 77 78 func (c *ClusterFile) SetApplication(app v2.Application) { 79 c.app = &app 80 } 81 82 func (c *ClusterFile) GetConfigs() []v1.Config { 83 logrus.Debugf("loaded configs from clusterfile: %+v\n", c.configs) 84 return c.configs 85 } 86 87 func (c *ClusterFile) GetApplication() *v2.Application { 88 logrus.Debugf("loaded application from clusterfile: %+v\n", c.app) 89 return c.app 90 } 91 92 func (c *ClusterFile) GetPlugins() []v1.Plugin { 93 logrus.Debugf("loaded plugins from clusterfile: %+v\n", c.plugins) 94 return c.plugins 95 } 96 97 func (c *ClusterFile) GetKubeadmConfig() *kubeadm.KubeadmConfig { 98 logrus.Debugf("loaded kubeadm config from clusterfile: %+v\n", c.kubeadmConfig) 99 return &c.kubeadmConfig 100 } 101 102 func (c *ClusterFile) SaveAll(opts SaveOptions) error { 103 var ( 104 clusterfileBytes [][]byte 105 appBytes []byte 106 config []byte 107 plugin []byte 108 ) 109 fileName := common.GetDefaultClusterfile() 110 err := os.MkdirAll(filepath.Dir(fileName), os.ModePerm) 111 if err != nil { 112 return fmt.Errorf("failed to mkdir %s: %v", fileName, err) 113 } 114 115 cluster, err := yaml.Marshal(c.cluster) 116 if err != nil { 117 return err 118 } 119 clusterfileBytes = append(clusterfileBytes, cluster) 120 121 if c.app != nil { 122 appBytes, err = yaml.Marshal(c.app) 123 if err != nil { 124 return err 125 } 126 clusterfileBytes = append(clusterfileBytes, appBytes) 127 } 128 129 if len(c.configs) != 0 { 130 for _, cg := range c.configs { 131 config, err = yaml.Marshal(cg) 132 if err != nil { 133 return err 134 } 135 clusterfileBytes = append(clusterfileBytes, config) 136 } 137 } 138 139 if len(c.plugins) != 0 { 140 for _, p := range c.plugins { 141 plugin, err = yaml.Marshal(p) 142 if err != nil { 143 return err 144 } 145 clusterfileBytes = append(clusterfileBytes, plugin) 146 } 147 } 148 149 if len(c.kubeadmConfig.InitConfiguration.TypeMeta.Kind) != 0 { 150 initConfiguration, err := yaml.Marshal(c.kubeadmConfig.InitConfiguration) 151 if err != nil { 152 return err 153 } 154 clusterfileBytes = append(clusterfileBytes, initConfiguration) 155 } 156 157 if len(c.kubeadmConfig.JoinConfiguration.TypeMeta.Kind) != 0 { 158 joinConfiguration, err := yaml.Marshal(c.kubeadmConfig.JoinConfiguration) 159 if err != nil { 160 return err 161 } 162 clusterfileBytes = append(clusterfileBytes, joinConfiguration) 163 } 164 if len(c.kubeadmConfig.ClusterConfiguration.TypeMeta.Kind) != 0 { 165 clusterConfiguration, err := yaml.Marshal(c.kubeadmConfig.ClusterConfiguration) 166 if err != nil { 167 return err 168 } 169 clusterfileBytes = append(clusterfileBytes, clusterConfiguration) 170 } 171 172 if len(c.kubeadmConfig.KubeletConfiguration.TypeMeta.Kind) != 0 { 173 kubeletConfiguration, err := yaml.Marshal(c.kubeadmConfig.KubeletConfiguration) 174 if err != nil { 175 return err 176 } 177 clusterfileBytes = append(clusterfileBytes, kubeletConfiguration) 178 } 179 180 if len(c.kubeadmConfig.KubeProxyConfiguration.TypeMeta.Kind) != 0 { 181 kubeProxyConfiguration, err := yaml.Marshal(c.kubeadmConfig.KubeProxyConfiguration) 182 if err != nil { 183 return err 184 } 185 clusterfileBytes = append(clusterfileBytes, kubeProxyConfiguration) 186 } 187 188 content := bytes.Join(clusterfileBytes, []byte("---\n")) 189 err = utilsos.NewCommonWriter(fileName).WriteFile(content) 190 if err != nil { 191 return fmt.Errorf("failed to save clusterfile to disk:%v", err) 192 } 193 194 if opts.CommitToCluster { 195 return saveToCluster(content, opts.ConfPath) 196 } 197 198 logrus.Info("succeeded in saving clusterfile") 199 return nil 200 } 201 202 func saveToCluster(data []byte, confPath string) error { 203 if confPath == "" { 204 confPath = kubernetes.AdminKubeConfPath 205 } 206 cli, err := kubernetes.GetClientFromConfig(confPath) 207 if err != nil { 208 return fmt.Errorf("failed to new k8s runtime client via adminconf: %v", err) 209 } 210 211 cm := &corev1.ConfigMap{ 212 ObjectMeta: metav1.ObjectMeta{ 213 Name: ClusterfileConfigMapName, 214 Namespace: ClusterfileConfigMapNamespace, 215 }, 216 Data: map[string]string{ClusterfileConfigMapDataName: string(data)}, 217 } 218 219 ctx := context.Background() 220 if err := cli.Create(ctx, cm, &client.CreateOptions{}); err != nil { 221 if !apierrors.IsAlreadyExists(err) { 222 return fmt.Errorf("unable to create configmap: %v", err) 223 } 224 225 if err := cli.Update(ctx, cm, &client.UpdateOptions{}); err != nil { 226 return fmt.Errorf("unable to update configmap: %v", err) 227 } 228 } 229 230 return nil 231 } 232 233 func NewClusterFile(b []byte) (Interface, error) { 234 clusterFile := new(ClusterFile) 235 // use user specified clusterfile 236 if len(b) == 0 { 237 return nil, fmt.Errorf("empty clusterfile") 238 } 239 240 if err := decodeClusterFile(bytes.NewReader(b), clusterFile); err != nil { 241 return nil, fmt.Errorf("failed to load clusterfile: %v", err) 242 } 243 244 return clusterFile, nil 245 } 246 247 func GetActualClusterFile() (Interface, bool, error) { 248 clusterFile := new(ClusterFile) 249 250 // assume that we already have an existed cluster 251 fromCluster, err := getClusterfileFromCluster() 252 if err != nil { 253 logrus.Warn("try to get clusterfile from cluster: ", err) 254 } 255 256 if fromCluster != nil { 257 return fromCluster, true, nil 258 } 259 260 // read local disk clusterfile 261 clusterFileData, err := os.ReadFile(filepath.Clean(common.GetDefaultClusterfile())) 262 if err != nil { 263 return nil, false, err 264 } 265 266 if err := decodeClusterFile(bytes.NewReader(clusterFileData), clusterFile); err != nil { 267 return nil, false, fmt.Errorf("failed to load clusterfile: %v", err) 268 } 269 270 return clusterFile, false, nil 271 } 272 273 func getClusterfileFromCluster() (*ClusterFile, error) { 274 clusterFile := new(ClusterFile) 275 cli, err := k8s.NewK8sClient() 276 if err != nil { 277 return nil, err 278 } 279 280 cm, err := cli.ConfigMap(ClusterfileConfigMapNamespace).Get(context.TODO(), ClusterfileConfigMapName, metav1.GetOptions{}) 281 if err != nil { 282 return nil, err 283 } 284 285 data := cm.Data[ClusterfileConfigMapDataName] 286 if len(data) > 0 { 287 err = decodeClusterFile(bytes.NewReader([]byte(data)), clusterFile) 288 if err != nil { 289 return nil, err 290 } 291 return clusterFile, nil 292 } 293 return nil, fmt.Errorf("failed to get clusterfile from cluster") 294 }