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  }