diff --git a/README.en.md b/README.en.md index 341b2e7d16e948d7b52fdf1b112f8a634cb94c95..c76de628801085c13976dd0a3809e4f973aea0ef 100644 --- a/README.en.md +++ b/README.en.md @@ -3,11 +3,7 @@ ## Introduction -NKD (NestOS Kubernetes Deployer) is a solution designed for deploying and maintaining Kubernetes clusters on NestOS. It is designed to simplify the process of deploying and upgrading clusters by providing a range of services outside the cluster, including deployment, updates, and configuration management of infrastructure and core components of Kubernetes. NKD is designed to provide a more convenient cluster operation experience, allowing users to easily complete complex management tasks, thereby improving the overall efficiency of deployment and maintenance. - -#### Support Platforms - -It supports multiple deployment platforms, and NKD dynamically creates the required IaaS resources by connecting infrastructure providers according to the needs of the cluster, and currently supports OpenStack and libvirt platforms. +NKD (NestOS Kubernetes Deployer) is a cluster deployment and operation tool developed by the NestOS team for container cloud scenarios. It covers a series of functions such as infrastructure and Kubernetes core component deployment, update, and configuration management, providing users with a one-stop solution. It supports customization of multiple container runtimes including crio, iSulad, docker, and containerd, and is compatible with multiple platform deployments, ensuring that users can easily handle various complex deployment requirements. In addition, NKD has the ability to create cluster self-signed certificates and supports deploying multiple versions of Kubernetes clusters, covering various scenarios that may be encountered in actual use. ## Software architecture diff --git a/README.md b/README.md index 5ece96d983085bd121a3c731a16055276283efca..27604dabd24d84a098b09cc3ad7032bd0a86dcc8 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,7 @@ ![ignition_design_2](/docs/logo/nkd-logo.png) ## 介绍 -NKD(NestOS Kubernetes Deployer)是专为在NestOS上部署和维护Kubernetes集群而打造的解决方案。其旨在通过在集群外提供一系列服务,涵盖了基础设施和Kubernetes核心组件的部署、更新和配置管理等,从而简化了集群部署和升级的流程。NKD的设计目标在于提供更为便捷的集群操作体验,使得用户能够轻松完成复杂的管理任务,从而提高整体部署和维护的效率。 - -#### 支持平台 -支持多种部署平台,NKD根据集群需求,连接基础设施提供商动态创建所需的IaaS资源,目前支持OpenStack和libvirt平台。 +NKD(NestOS Kubernetes Deployer)是NestOS团队面向容器云场景开发的集群部署运维工具。涵盖了基础设施和Kubernetes核心组件的部署、更新和配置管理等一系列功能,为用户提供了一站式的解决方案。支持自定义多种容器运行时(包括crio、iSulad、docker和containerd),并且兼容多种平台部署,确保用户能够轻松应对各种复杂的部署需求。此外,NKD具备集群自签名证书创建的能力,并支持部署多种版本的Kubernetes集群,从而覆盖实际使用中可能遇到的各种场景。 ## 软件架构 详细内容请见[软件架构说明](docs/zh/overall_design.md) diff --git a/cmd/command/log.go b/cmd/command/log.go new file mode 100644 index 0000000000000000000000000000000000000000..9112d4f52850dffc9a919646d3ed9771f7664023 --- /dev/null +++ b/cmd/command/log.go @@ -0,0 +1,126 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package command + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" + "time" + + "github.com/natefinch/lumberjack" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + logFilePrefix = "logs/nkd-" + logFileSuffix = ".log" + maxLogSize = 10 // 每个日志文件的最大大小(MB) + maxBackups = 10 // 保留旧日志文件的最大个数 + maxAgeInDays = 30 // 保留旧日志文件的最大天数 +) + +var LogLevel string + +type loggerHook struct { + file io.Writer + formatter logrus.Formatter + level logrus.Level +} + +func NewloggerHook(file io.Writer, level logrus.Level, formatter logrus.Formatter) *loggerHook { + return &loggerHook{ + file: file, + level: level, + formatter: formatter, + } +} + +// Levels 返回允许记录的日志级别列表 +func (h *loggerHook) Levels() []logrus.Level { + var levels []logrus.Level + for _, level := range logrus.AllLevels { + if level <= h.level { + levels = append(levels, level) + } + } + + return levels +} + +// Fire 实现钩子的 Fire 方法,用于记录日志 +func (h *loggerHook) Fire(entry *logrus.Entry) error { + orig := entry.Message + defer func() { entry.Message = orig }() + + msgs := strings.Split(orig, "\n") + for _, msg := range msgs { + entry.Message = msg + line, err := h.formatter.Format(entry) + if err != nil { + return err + } + if _, err := h.file.Write(line); err != nil { + return err + } + } + + return nil +} + +// 设置日志文件的基本配置,包括创建日志目录,打开日志文件、设置日志格式等 +func SetuploggerHook(baseDir string) func() { + if err := os.MkdirAll(baseDir, 0755); err != nil { + logrus.Fatal(errors.Wrap(err, "failed to create base directory for logs")) + } + + logfilePath := filepath.Join(baseDir, generateLogFileName()) + logfile := &lumberjack.Logger{ + Filename: logfilePath, + MaxSize: maxLogSize, + MaxBackups: maxBackups, + MaxAge: maxAgeInDays, + Compress: true, + LocalTime: true, + } + + orgHooks := logrus.LevelHooks{} + for k, v := range logrus.StandardLogger().Hooks { + orgHooks[k] = v + } + logrus.AddHook(NewloggerHook(logfile, logrus.TraceLevel, &logrus.TextFormatter{ + DisableColors: true, + DisableTimestamp: false, + FullTimestamp: true, + DisableLevelTruncation: false, + })) + logrus.SetLevel(logrus.TraceLevel) + + return func() { + logfile.Close() + logrus.StandardLogger().ReplaceHooks(orgHooks) + } +} + +func generateLogFileName() string { + currentTime := time.Now() + dateString := currentTime.Format("2006-01-02") + return fmt.Sprintf("%s%s%s", logFilePrefix, dateString, logFileSuffix) +} diff --git a/cmd/command/opts/opts.go b/cmd/command/opts/opts.go index 810427c74801f157d940b7c0f202718a6026e0a2..56920440db7ef88796f377fd287757c9ce725491 100644 --- a/cmd/command/opts/opts.go +++ b/cmd/command/opts/opts.go @@ -18,6 +18,10 @@ package opts var Opts OptionsList +var RootOpts struct { + LogLevel string +} + type OptionsList struct { RootOptDir string Arch string @@ -25,6 +29,7 @@ type OptionsList struct { KubeConfigFile string NKD NKDConfig InfraPlatform + OSImage ClusterID string Platform string @@ -44,13 +49,15 @@ type OptionsList struct { KubernetesAPIVersion uint Token string CertificateKey string + PreHookScript string + PostHookYaml string NetWork NetworkConfig Housekeeper } type NKDConfig struct { - Log_Level string + LogLevel string BootstrapUrl } @@ -59,30 +66,51 @@ type BootstrapUrl struct { BootstrapIgnPort string } +type OSImage struct { + Type string +} + type InfraPlatform struct { OpenStack Libvirt + PXE + IPXE } type OpenStack struct { - UserName string - Password string - Tenant_Name string - Auth_URL string - Region string - Internal_Network string - External_Network string - Glance_Name string - Availability_Zone string + UserName string + Password string + TenantName string + AuthURL string + Region string + InternalNetwork string + ExternalNetwork string + GlanceName string + AvailabilityZone string } type Libvirt struct { URI string - OSImage string + OSPath string CIDR string Gateway string } +type PXE struct { + IP string + HTTPServerPort string + HTTPRootDir string + TFTPServerPort string + TFTPRootDir string +} + +type IPXE struct { + IP string + Port string + FilePath string + OSInstallTreePath string +} + type MasterConfig struct { Hostname []string CPU uint diff --git a/cmd/command/setup_opts.go b/cmd/command/setup_opts.go index 166ecb7542be7824db2357da70ad777911ec9492..bc0205060188816d29633284593717d7f9fe08f0 100644 --- a/cmd/command/setup_opts.go +++ b/cmd/command/setup_opts.go @@ -25,9 +25,38 @@ import ( func SetupDeployCmdOpts(deployCmd *cobra.Command) { flags := deployCmd.Flags() flags.StringVarP(&opts.Opts.ClusterConfigFile, "file", "f", "", "Location of the cluster deploy config file") - flags.StringVarP(&opts.Opts.ClusterID, "cluster-id", "", "", "Unique identifier for the cluster") + flags.StringVarP(&opts.Opts.ClusterID, "clusterID", "", "", "Unique identifier for the cluster") flags.StringVar(&opts.Opts.Arch, "arch", "", "Architecture for Kubernetes cluster deployment (e.g., amd64 or arm64)") - flags.StringVarP(&opts.Opts.Platform, "platform", "", "", "Infrastructure platform for deploying the cluster (supports 'libvirt' or 'openstack')") + flags.StringVarP(&opts.Opts.Platform, "platform", "", "", "Infrastructure platform for deploying the cluster (supports 'libvirt' 'openstack' 'pxe' 'ipxe')") + + // libvirt + flags.StringVarP(&opts.Opts.InfraPlatform.Libvirt.URI, "libvirt-uri", "", "", "URI for libvirt (default: qemu:///system)") + flags.StringVarP(&opts.Opts.InfraPlatform.Libvirt.OSPath, "libvirt-osPath", "", "", "OS path for libvirt") + flags.StringVarP(&opts.Opts.InfraPlatform.Libvirt.CIDR, "libvirt-cidr", "", "", "CIDR for libvirt (default: 192.168.132.0/24)") + flags.StringVarP(&opts.Opts.InfraPlatform.Libvirt.Gateway, "libvirt-gateway", "", "", "Gateway for libvirt (default: 192.168.132.1)") + + // openstack + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.UserName, "openstack-username", "", "", "UserName for openstack (default: admin)") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.Password, "openstack-password", "", "", "Password for openstack") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.TenantName, "openstack-tenantName", "", "", "TenantName for openstack (default: admin)") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.AuthURL, "openstack-authURL", "", "", "AuthURL for openstack (default: http://controller:5000/v3)") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.Region, "openstack-region", "", "", "Region for openstack (default: RegionOne)") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.InternalNetwork, "openstack-internalNetwork", "", "", "InternalNetwork for openstack") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.ExternalNetwork, "openstack-externalNetwork", "", "", "ExternalNetwork for openstack") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.GlanceName, "openstack-glanceName", "", "", "GlanceName for openstack") + flags.StringVarP(&opts.Opts.InfraPlatform.OpenStack.AvailabilityZone, "openstack-availabilityZone", "", "", "AvailabilityZone for openstack (default: nova)") + + // pxe + flags.StringVarP(&opts.Opts.InfraPlatform.PXE.IP, "pxe-ip", "", "", "IP address of local machine for PXE") + flags.StringVarP(&opts.Opts.InfraPlatform.PXE.HTTPRootDir, "pxe-httpRootDir", "", "", "Root directory of HTTP server for PXE (default: /var/www/html/)") + flags.StringVarP(&opts.Opts.InfraPlatform.PXE.TFTPRootDir, "pxe-tftpRootDir", "", "", "Root directory of TFTP server for PXE (default: /var/lib/tftpboot/)") + + // ipxe + flags.StringVarP(&opts.Opts.InfraPlatform.IPXE.IP, "ipxe-ip", "", "", "IP address of local machine for iPXE") + flags.StringVarP(&opts.Opts.InfraPlatform.IPXE.FilePath, "ipxe-filePath", "", "", "Path of config file for iPXE") + flags.StringVarP(&opts.Opts.InfraPlatform.IPXE.OSInstallTreePath, "ipxe-osInstallTreePath", "", "", "Path of OS install tree for iPXE. (default: /var/www/html/)") + + flags.StringVarP(&opts.Opts.OSImage.Type, "os-type", "", "", "Operating system type for Kubernetes cluster deployment (e.g., nestos or generalos)") flags.StringVarP(&opts.Opts.UserName, "username", "", "", "User name for node login") flags.StringVarP(&opts.Opts.Password, "password", "", "", "Password for node login") flags.StringVarP(&opts.Opts.SSHKey, "sshkey", "", "", "SSH key file path used for node authentication (default: ~/.ssh/id_rsa.pub)") @@ -41,7 +70,7 @@ func SetupDeployCmdOpts(deployCmd *cobra.Command) { flags.UintVar(&opts.Opts.Worker.RAM, "worker-ram", 0, "RAM allocation for worker nodes (units: MB)") flags.UintVar(&opts.Opts.Worker.Disk, "worker-disk", 0, "Disk size allocation for worker nodes (units: GB)") flags.StringArrayVarP(&opts.Opts.Worker.IP, "worker-ips", "", []string{}, "IP addresses of worker nodes (e.g., --worker-ips [worker-ip-01] --worker-ips [worker-ip-02] ...)") - flags.StringVarP(&opts.Opts.Runtime, "runtime", "", "", "Container runtime type (docker, isulad or crio)") + flags.StringVarP(&opts.Opts.Runtime, "runtime", "", "", "Container runtime type (docker, isulad, containerd or crio)") flags.StringVarP(&opts.Opts.ImageRegistry, "image-registry", "", "", "Registry address for Kubernetes component container images") flags.StringVarP(&opts.Opts.PauseImage, "pause-image", "", "", "Image for the pause container (e.g., pause:TAG)") flags.StringVarP(&opts.Opts.ReleaseImageUrl, "release-image-url", "", "", "URL of the NestOS container image containing Kubernetes component") @@ -61,6 +90,8 @@ func SetupDeployCmdOpts(deployCmd *cobra.Command) { flags.BoolVarP(&opts.Opts.DeployHousekeeper, "deploy-housekeeper", "", false, "Deploy the Housekeeper Operator. (default: false)") flags.StringVarP(&opts.Opts.NKD.BootstrapIgnHost, "bootstrap-ign-host", "", "", "Ignition service address (domain name or IP)") flags.StringVarP(&opts.Opts.NKD.BootstrapIgnPort, "bootstrap-ign-port", "", "", "Ignition service port (default: 9080)") + flags.StringVarP(&opts.Opts.PreHookScript, "prehook-script", "", "", "Specify a script file or directory to execute before cluster deployment as hooks") + flags.StringVarP(&opts.Opts.PostHookYaml, "posthook-yaml", "", "", "Specify a YAML file or directory to apply after cluster deployment using 'kubectl apply'") } func SetupDestroyCmdOpts(destroyCmd *cobra.Command) { @@ -87,4 +118,5 @@ func SetupExtendCmdOpts(extendCmd *cobra.Command) { func SetupTemplateCmdOpts(templateCmd *cobra.Command) { flags := templateCmd.Flags() flags.StringVarP(&opts.Opts.ClusterConfigFile, "output", "o", "", "Generates a default configuration template at the specified location") + flags.StringVarP(&opts.Opts.Platform, "platform", "", "", "Infrastructure platform for deploying the cluster (supports 'libvirt' 'openstack' 'pxe' 'ipxe')") } diff --git a/cmd/deploy.go b/cmd/deploy.go index 17f3ca1afa0c69589094e867254dabce0d0c576a..618941c1a8c3b2e4a39528dc167c74d8763b359d 100755 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -17,6 +17,7 @@ package cmd import ( "context" + "errors" "fmt" "io" "nestos-kubernetes-deployer/cmd/command" @@ -25,10 +26,13 @@ import ( "nestos-kubernetes-deployer/pkg/cert" "nestos-kubernetes-deployer/pkg/configmanager" "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/asset/infraasset" + "nestos-kubernetes-deployer/pkg/constants" "nestos-kubernetes-deployer/pkg/httpserver" - "nestos-kubernetes-deployer/pkg/ignition/machine" "nestos-kubernetes-deployer/pkg/infra" "nestos-kubernetes-deployer/pkg/kubeclient" + "nestos-kubernetes-deployer/pkg/osmanager" + "nestos-kubernetes-deployer/pkg/tftpserver" "nestos-kubernetes-deployer/pkg/utils" "net/http" "os" @@ -62,6 +66,9 @@ const ( ) func runDeployCmd(cmd *cobra.Command, args []string) error { + cleanup := command.SetuploggerHook(opts.Opts.RootOptDir) + defer cleanup() + if err := validateDeployConfig(); err != nil { return err } @@ -72,15 +79,12 @@ func runDeployCmd(cmd *cobra.Command, args []string) error { return err } - if err := deployCluster(config); err != nil { - logrus.Errorf("Failed to deploy %s cluster: %v", clusterID, err) - return err - } - if err := configmanager.Persist(); err != nil { - logrus.Errorf("Failed to persist the cluster asset: %v", err) + if err := createCluster(config); err != nil { + logrus.Errorf("Failed to create cluster: %v", err) return err } + logrus.Info("Cluster deployment completed successfully!") logrus.Infof("To access 'cluster-id:%s' cluster using 'kubectl', run 'export KUBECONFIG=%s'", clusterID, config.AdminKubeConfig) return nil } @@ -116,52 +120,146 @@ func getClusterConfig(options *opts.OptionsList) (*asset.ClusterAsset, error) { return config, nil } -// startHttpService initializes the HTTP file service, adds files to the cache, and starts the service. -func startHttpService(conf *asset.ClusterAsset) (*httpserver.HttpFileService, error) { - fileService := httpserver.NewFileService(configmanager.GetBootstrapIgnPort()) +func createCluster(conf *asset.ClusterAsset) error { + httpService := httpserver.NewHTTPService(configmanager.GetBootstrapIgnPort()) + defer httpService.Stop() - // Ignition files are divided into three types: - // control plane ignition files for initializing the cluster, - // master ignition files for master node joining the cluster, - // and worker ignition files for worker node joining the cluster. - if len(conf.Master) > 0 { - fileService.AddFileToCache(machine.ControlplaneIgnFilename, conf.Master[0].CreateIgnContent) - } - if len(conf.Master) > 1 { - fileService.AddFileToCache(machine.MasterIgnFilename, conf.Master[1].CreateIgnContent) - } - if len(conf.Worker) > 0 { - fileService.AddFileToCache(machine.WorkerIgnFilename, conf.Worker[0].CreateIgnContent) + osMgr := osmanager.NewOSManager(conf) + if err := osMgr.GenerateOSConfig(); err != nil { + logrus.Errorf("Error generating OS config: %v", err) + return err } - // Start the HTTP file service - if err := fileService.Start(); err != nil { - return nil, fmt.Errorf("error starting file service: %v", err) + if osMgr.IsNestOS() { + if err := addIgnitionFiles(httpService, conf); err != nil { + return err + } } + if osMgr.IsGeneralOS() && len(conf.Master) > 0 { + certs, _ := cert.CertsToBytes(conf.Master[0].Certs) + if err := httpService.AddFileToCache(constants.CertsFiles, certs); err != nil { + return err + } - return fileService, nil -} + if len(conf.Kubernetes.RpmPackagePath) > 0 { + httpService.PackageDir = conf.Kubernetes.RpmPackagePath + } -func deployCluster(conf *asset.ClusterAsset) error { - if err := generateDeployConfig(conf); err != nil { - logrus.Errorf("Failed to get cluster deploy config: %v", err) - return err + if strings.ToLower(conf.Platform) == "pxe" || strings.ToLower(conf.Platform) == "ipxe" { + if err := addKickstartFiles(httpService, conf); err != nil { + return fmt.Errorf("error adding kickstart file to cache: %v", err) + } + } } - // Start HTTP service - fileService, err := startHttpService(conf) - if err != nil { + if err := configmanager.Persist(); err != nil { + logrus.Errorf("Failed to persist the cluster asset: %v", err) return err } - defer fileService.Stop() - if err := createCluster(conf); err != nil { - logrus.Errorf("Failed to create cluster: %v", err) + p := infra.InfraPlatform{} + switch strings.ToLower(conf.Platform) { + case "libvirt": + httpserver.StartHTTPService(httpService) + + libvirtMaster := &infra.Libvirt{ + PersistDir: configmanager.GetPersistDir(), + ClusterID: conf.ClusterID, + Node: "master", + Count: uint(len(conf.Master)), + } + + p.SetInfra(libvirtMaster) + if err := p.Deploy(); err != nil { + logrus.Errorf("Failed to deploy master nodes:%v", err) + return err + } + + libvirtWorker := &infra.Libvirt{ + PersistDir: configmanager.GetPersistDir(), + ClusterID: conf.ClusterID, + Node: "worker", + Count: uint(len(conf.Master)), + } + p.SetInfra(libvirtWorker) + if err := p.Deploy(); err != nil { + logrus.Errorf("Failed to deploy worker nodes:%v", err) + return err + } + case "openstack": + httpserver.StartHTTPService(httpService) + + openstackMaster := &infra.OpenStack{ + PersistDir: configmanager.GetPersistDir(), + ClusterID: conf.ClusterID, + Node: "master", + Count: uint(len(conf.Master)), + } + p.SetInfra(openstackMaster) + if err := p.Deploy(); err != nil { + logrus.Errorf("Failed to deploy master nodes:%v", err) + return err + } + + openstackWorker := &infra.OpenStack{ + PersistDir: configmanager.GetPersistDir(), + ClusterID: conf.ClusterID, + Node: "worker", + Count: uint(len(conf.Master)), + } + p.SetInfra(openstackWorker) + if err := p.Deploy(); err != nil { + logrus.Errorf("Failed to deploy worker nodes:%v", err) + return err + } + case "pxe": + pxeConfig := conf.InfraPlatform.(*infraasset.PXEAsset) + httpService.Port = pxeConfig.HTTPServerPort + httpService.DirPath = pxeConfig.HTTPRootDir + httpserver.StartHTTPService(httpService) + + tftpService := tftpserver.NewTFTPService(pxeConfig.IP, pxeConfig.TFTPServerPort, pxeConfig.TFTPRootDir) + go func() { + select { + case <-httpService.Ch: + logrus.Info("tftp server stop") + tftpService.Stop() + return + } + }() + go func() { + if err := tftpService.Start(); err != nil { + logrus.Errorf("error starting http service: %v", err) + return + } + }() + defer tftpService.Stop() + + case "ipxe": + ipxeConfig := conf.InfraPlatform.(*infraasset.IPXEAsset) + httpService.Port = ipxeConfig.Port + httpService.DirPath = ipxeConfig.OSInstallTreePath + fileContent, err := os.ReadFile(ipxeConfig.FilePath) + if err != nil { + return err + } + if err := httpService.AddFileToCache(constants.IPXECfg, fileContent); err != nil { + return fmt.Errorf("error adding ipxe config file to cache: %v", err) + } + httpserver.StartHTTPService(httpService) + + default: + return errors.New("unsupported platform") + } + + if err := clusterCreatePost(conf); err != nil { return err } + return nil +} - configPath := conf.Kubernetes.AdminKubeConfig - kubeClient, err := kubeclient.CreateClient(configPath) +func clusterCreatePost(conf *asset.ClusterAsset) error { + kubeClient, err := kubeclient.CreateClient(conf.Kubernetes.AdminKubeConfig) if err != nil { logrus.Errorf("Failed to create kubernetes client %v", err) return err @@ -171,10 +269,11 @@ func deployCluster(conf *asset.ClusterAsset) error { logrus.Errorf("Failed while waiting for Kubernetes API to be ready: %v", err) return err } + // Set kubeconfig environment variable + os.Setenv("KUBECONFIG", conf.Kubernetes.AdminKubeConfig) - os.Setenv("KUBECONFIG", configPath) // set kubeconfig environment variable - // apply network plugin - if err := applyNetworkPlugin(conf.Network.Plugin); err != nil { + // Apply network plugin + if err := applyNetworkPlugin(conf.Network.Plugin, conf.IsNestOS); err != nil { logrus.Errorf("Failed to apply network plugin: %v", err) return err } @@ -182,105 +281,18 @@ func deployCluster(conf *asset.ClusterAsset) error { if conf.Housekeeper.DeployHousekeeper { logrus.Info("Starting deployment of Housekeeper...") - if err := deployHousekeeper(conf.Housekeeper, configPath); err != nil { + if err := deployHousekeeper(conf.Housekeeper, conf.Kubernetes.AdminKubeConfig); err != nil { logrus.Errorf("Failed to deploy operator: %v", err) return err } logrus.Info("Housekeeper deployment completed successfully.") } + // Wait for pods to be ready if err := waitForPodsReady(kubeClient); err != nil { logrus.Errorf("Failed while waiting for pods to be in 'Ready' state: %v", err) return err } - logrus.Info("Cluster deployment completed successfully!") - return nil -} - -func generateDeployConfig(conf *asset.ClusterAsset) error { - if err := generateCerts(conf); err != nil { - logrus.Errorf("Error generating certificate files: %v", err) - return err - } - - if err := generateIgnition(conf); err != nil { - logrus.Errorf("Error generating ignition files: %v", err) - return err - } - - if err := generateTF(conf); err != nil { - logrus.Errorf("Error generating terraform files: %v", err) - return err - } - - return nil -} - -func generateCerts(conf *asset.ClusterAsset) error { - cg := cert.NewCertGenerator(conf.Cluster_ID, &conf.Master[0]) - err := cg.GenerateAllFiles() - if err != nil { - logrus.Errorf("Error generating all certs files: %v", err) - return err - } - conf.CaCertHash = cg.CaCertHash - return nil -} - -func generateIgnition(conf *asset.ClusterAsset) error { - - hostport := configmanager.GetBootstrapIgnHost() + ":" + configmanager.GetBootstrapIgnPort() - - master := &machine.Master{ - ClusterAsset: conf, - Bootstrap_baseurl: hostport, - } - if err := master.GenerateFiles(); err != nil { - logrus.Errorf("Failed to generate master ignition file: %v", err) - return err - } - - worker := &machine.Worker{ - ClusterAsset: conf, - Bootstrap_baseurl: hostport, - } - if err := worker.GenerateFiles(); err != nil { - logrus.Errorf("Failed to generate worker ignition file: %v", err) - return err - } - - return nil -} - -func generateTF(conf *asset.ClusterAsset) error { - // generate master.tf - var master infra.Infra - if err := master.Generate(conf, "master"); err != nil { - logrus.Errorf("Failed to generate master terraform file") - return err - } - // generate worker.tf - var worker infra.Infra - if err := worker.Generate(conf, "worker"); err != nil { - logrus.Errorf("Failed to generate worker terraform file") - return err - } - return nil -} - -func createCluster(conf *asset.ClusterAsset) error { - persistDir := configmanager.GetPersistDir() - masterInfra := infra.InstanceCluster(persistDir, conf.Cluster_ID, "master", uint(len(conf.Master))) - if err := masterInfra.Deploy(); err != nil { - logrus.Errorf("Failed to deploy master nodes:%v", err) - return err - } - workerInfra := infra.InstanceCluster(persistDir, conf.Cluster_ID, "worker", uint(len(conf.Worker))) - if err := workerInfra.Deploy(); err != nil { - logrus.Errorf("Failed to deploy worker nodes:%v", err) - return err - } - return nil } @@ -335,7 +347,7 @@ func waitForPodsReady(client *kubernetes.Clientset) error { } if allReady { - logrus.Infof("All Pods in namespace %s are in Ready state", namespace) + // logrus.Infof("All Pods in namespace %s are in Ready state", namespace) return true, nil } return false, nil @@ -383,7 +395,7 @@ func deployHousekeeper(tmplData interface{}, kubeconfig string) error { return nil } -func applyNetworkPlugin(pluginConfigPath string) error { +func applyNetworkPlugin(pluginConfigPath string, isNestOS bool) error { var content []byte var err error @@ -413,7 +425,7 @@ func applyNetworkPlugin(pluginConfigPath string) error { // "/usr/libexec/kubernetes/kubelet-plugins",而 FlexVolume 的目录必须是可写入的, // 该功能特性才能正常工作,为了解决这个问题将/usr目录修改为可写目录/opt. // Check if the content contains "/usr/libexec/kubernetes/kubelet-plugins" - if strings.Contains(string(content), "/usr/libexec/kubernetes/kubelet-plugins") { + if isNestOS && strings.Contains(string(content), "/usr/libexec/kubernetes/kubelet-plugins") { content = []byte(strings.ReplaceAll(string(content), "/usr/libexec/kubernetes/kubelet-plugins", "/opt/libexec/kubernetes/kubelet-plugins")) @@ -443,3 +455,51 @@ func applyNetworkPlugin(pluginConfigPath string) error { return nil } + +func addIgnitionFiles(httpService *httpserver.HTTPService, conf *asset.ClusterAsset) error { + // Ignition files are divided into three types: + // control plane ignition files for initializing the cluster, + // master ignition files for master node joining the cluster, + // and worker ignition files for worker node joining the cluster. + + // Only one master node + if err := httpService.AddFileToCache(constants.ControlplaneIgn, conf.BootConfig.Controlplane.Content); err != nil { + return fmt.Errorf("error adding control plane ignition file to cache: %v", err) + } + + // multiple master nodes + if len(conf.Master) > 1 { + if err := httpService.AddFileToCache(constants.MasterIgn, conf.BootConfig.Master.Content); err != nil { + return fmt.Errorf("error adding master ignition file to cache: %v", err) + } + } + + if err := httpService.AddFileToCache(constants.WorkerIgn, conf.BootConfig.Worker.Content); err != nil { + return fmt.Errorf("error adding worker ignition file to cache: %v", err) + } + + return nil +} + +func addKickstartFiles(httpService *httpserver.HTTPService, conf *asset.ClusterAsset) error { + // Only one master node + if err := httpService.AddFileToCache(conf.Master[0].Hostname+constants.KickstartSuffix, conf.BootConfig.Controlplane.Content); err != nil { + return fmt.Errorf("error adding control plane kickstart file to cache: %v", err) + } + + // multiple master nodes + n := len(conf.Master) + if n > 1 { + for i := 1; i < n; i++ { + if err := httpService.AddFileToCache(conf.Master[i].Hostname+constants.KickstartSuffix, conf.BootConfig.KickstartMaster[i-1].Content); err != nil { + return fmt.Errorf("error adding master kickstart file to cache: %v", err) + } + } + } + + if err := httpService.AddFileToCache(constants.Worker+constants.KickstartSuffix, conf.BootConfig.Worker.Content); err != nil { + return fmt.Errorf("error adding worker kickstart file to cache: %v", err) + } + + return nil +} diff --git a/cmd/destroy.go b/cmd/destroy.go index ade2fba3b812a1e737d7c8c25e252905f3e26b11..d73538e3ffb1a8cf4a623876725d19fcc12a75c3 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -16,10 +16,13 @@ limitations under the License. package cmd import ( + "fmt" "nestos-kubernetes-deployer/cmd/command" "nestos-kubernetes-deployer/cmd/command/opts" "nestos-kubernetes-deployer/pkg/configmanager" + "nestos-kubernetes-deployer/pkg/configmanager/asset/infraasset" "nestos-kubernetes-deployer/pkg/infra" + "strings" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -37,6 +40,9 @@ func NewDestroyCommand() *cobra.Command { } func runDestroyCmd(cmd *cobra.Command, args []string) error { + cleanup := command.SetuploggerHook(opts.Opts.RootOptDir) + defer cleanup() + clusterID, err := cmd.Flags().GetString("cluster-id") if err != nil { logrus.Errorf("Failed to get cluster id: %v", err) @@ -44,22 +50,84 @@ func runDestroyCmd(cmd *cobra.Command, args []string) error { } if clusterID == "" { logrus.Errorf("cluster-id is not provided: %v", err) + return err } if err := configmanager.Initial(&opts.Opts); err != nil { logrus.Errorf("Failed to initialize configuration parameters: %v", err) return err } - persistDir := configmanager.GetPersistDir() - workerInfra := infra.InstanceCluster(persistDir, clusterID, "worker", 0) - if err := workerInfra.Destroy(); err != nil { - logrus.Errorf("Failed to perform the destroy worker nodes:%v", err) + clusterConfig, err := configmanager.GetClusterConfig(clusterID) + if err != nil { + logrus.Errorf("Failed to get cluster config using the cluster id: %v", err) return err } - masterInfra := infra.InstanceCluster(persistDir, clusterID, "master", 0) - if err := masterInfra.Destroy(); err != nil { - logrus.Errorf("Failed to perform the destroy master nodes:%v", err) + + var infrastructure infra.Infrastructure + switch strings.ToLower(clusterConfig.Platform) { + case "libvirt": + persistDir := configmanager.GetPersistDir() + + infrastructure = &infra.Libvirt{ + PersistDir: persistDir, + ClusterID: clusterID, + Node: "worker", + Count: 0, + } + if err := infrastructure.Destroy(); err != nil { + logrus.Errorf("Failed to destroy worker nodes:%v", err) + return err + } + + infrastructure = &infra.Libvirt{ + PersistDir: persistDir, + ClusterID: clusterID, + Node: "master", + Count: 0, + } + if err := infrastructure.Destroy(); err != nil { + logrus.Errorf("Failed to destroy master nodes:%v", err) + return err + } + case "openstack": + persistDir := configmanager.GetPersistDir() + + infrastructure = &infra.OpenStack{ + PersistDir: persistDir, + ClusterID: clusterID, + Node: "worker", + Count: 0, + } + if err := infrastructure.Destroy(); err != nil { + logrus.Errorf("Failed to destroy worker nodes:%v", err) + return err + } + + infrastructure = &infra.OpenStack{ + PersistDir: persistDir, + ClusterID: clusterID, + Node: "master", + Count: 0, + } + if err := infrastructure.Destroy(); err != nil { + logrus.Errorf("Failed to destroy master nodes:%v", err) + return err + } + case "pxe": + logrus.Println("If necessary, manually destroy the config for the PXE server:\n", + "1. Stop dhcpd service\n", + fmt.Sprintf("2. Delete http root dir: %s\n", clusterConfig.InfraPlatform.(*infraasset.PXEAsset).HTTPRootDir), + fmt.Sprintf("3. Delete tftp root dir: %s", clusterConfig.InfraPlatform.(*infraasset.PXEAsset).TFTPRootDir), + ) + case "ipxe": + logrus.Println("If necessary, manually destroy the config for the iPXE server:\n", + "1. Stop dhcpd service\n", + fmt.Sprintf("2. Delete ipxe config: %s\n", clusterConfig.InfraPlatform.(*infraasset.IPXEAsset).FilePath), + fmt.Sprintf("3. Delete OS install tree: %s", clusterConfig.InfraPlatform.(*infraasset.IPXEAsset).OSInstallTreePath), + ) + default: + logrus.Errorf("unsupported platform") return err } diff --git a/cmd/extend.go b/cmd/extend.go index d07371096d12d4eee0680c429f08f9f2ba933265..8410ae71f7e08ae63d2a88fe7231fb7b8640c340 100755 --- a/cmd/extend.go +++ b/cmd/extend.go @@ -17,22 +17,27 @@ package cmd import ( "context" + "errors" "fmt" "nestos-kubernetes-deployer/cmd/command" "nestos-kubernetes-deployer/cmd/command/opts" "nestos-kubernetes-deployer/pkg/configmanager" "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/asset/infraasset" + "nestos-kubernetes-deployer/pkg/constants" "nestos-kubernetes-deployer/pkg/httpserver" - "nestos-kubernetes-deployer/pkg/ignition/machine" "nestos-kubernetes-deployer/pkg/infra" "nestos-kubernetes-deployer/pkg/kubeclient" + "nestos-kubernetes-deployer/pkg/osmanager" + "nestos-kubernetes-deployer/pkg/terraform" + "nestos-kubernetes-deployer/pkg/tftpserver" "os" + "strings" "time" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - - corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -49,6 +54,9 @@ func NewExtendCommand() *cobra.Command { } func runExtendCmd(cmd *cobra.Command, args []string) error { + cleanup := command.SetuploggerHook(opts.Opts.RootOptDir) + defer cleanup() + clusterID, err := cmd.Flags().GetString("cluster-id") if err != nil { logrus.Errorf("Failed to get cluster-id: %v", err) @@ -58,12 +66,6 @@ func runExtendCmd(cmd *cobra.Command, args []string) error { logrus.Errorf("cluster-id is not provided: %v", err) } - num, err := cmd.Flags().GetUint("num") - if err != nil { - logrus.Errorf("Failed to get the number of extended nodes: %v", err) - return err - } - if err := configmanager.Initial(&opts.Opts); err != nil { logrus.Errorf("Failed to initialize configuration parameters: %v", err) return err @@ -74,33 +76,150 @@ func runExtendCmd(cmd *cobra.Command, args []string) error { logrus.Errorf("Failed to get cluster config using the cluster id: %v", err) return err } - newHostnames := extendArray(clusterConfig, int(num)) - fileService := httpserver.NewFileService(configmanager.GetBootstrapIgnPort()) - defer fileService.Stop() + num, err := cmd.Flags().GetUint("num") + if err != nil { + platform := strings.ToLower(clusterConfig.Platform) + if platform != "pxe" && platform != "ipxe" { + logrus.Errorf("Failed to get the number of extended nodes: %v", err) + return err + } + } - if err := extendCluster(clusterConfig, fileService); err != nil { + if err := extendCluster(clusterConfig, num); err != nil { logrus.Errorf("Failed to extend %s cluster: %v", clusterID, err) return err } - if err := configmanager.Persist(); err != nil { - logrus.Errorf("Failed to persist the cluster asset: %v", err) + + logrus.Infof("The cluster nodes are extended successfully") + + return nil +} + +func extendCluster(conf *asset.ClusterAsset, num uint) error { + httpService := httpserver.NewHTTPService(configmanager.GetBootstrapIgnPort()) + defer httpService.Stop() + + data, err := os.ReadFile(conf.BootConfig.Worker.Path) + if err != nil { + logrus.Errorf("error reading boot config file: %v", err) return err } - logrus.Infof("Waiting for cluster extend nodes to be ready...") - if err := checkNodesReady(clusterConfig, newHostnames); err != nil { - return err + osMgr := osmanager.NewOSManager(conf) + if osMgr.IsNestOS() { + httpService.AddFileToCache(constants.WorkerIgn, data) + } + if osMgr.IsGeneralOS() { + if strings.ToLower(conf.Platform) == "pxe" || strings.ToLower(conf.Platform) == "ipxe" { + httpService.AddFileToCache(constants.Worker+constants.KickstartSuffix, data) + } } - logrus.Infof("The cluster id:%s node is extended successfully", clusterID) + httpService.AddFileToCache(constants.WorkerIgn, data) + + p := infra.InfraPlatform{} + switch strings.ToLower(conf.Platform) { + case "libvirt": + httpserver.StartHTTPService(httpService) + + if err := extendArray(conf, int(num)); err != nil { + return err + } + + // regenerate worker.tf + var worker terraform.Infra + if err := worker.Generate(conf, "worker"); err != nil { + logrus.Errorf("Failed to generate worker terraform file") + return err + } + + libvirtWorker := &infra.Libvirt{ + PersistDir: configmanager.GetPersistDir(), + ClusterID: conf.ClusterID, + Node: "worker", + Count: uint(len(conf.Worker)), + } + p.SetInfra(libvirtWorker) + if err := p.Extend(); err != nil { + logrus.Errorf("Failed to extend worker nodes:%v", err) + return err + } + case "openstack": + httpserver.StartHTTPService(httpService) + + if err := extendArray(conf, int(num)); err != nil { + return err + } + + // regenerate worker.tf + var worker terraform.Infra + if err := worker.Generate(conf, "worker"); err != nil { + logrus.Errorf("Failed to generate worker terraform file") + return err + } + + openstackWorker := &infra.OpenStack{ + PersistDir: configmanager.GetPersistDir(), + ClusterID: conf.ClusterID, + Node: "worker", + Count: uint(len(conf.Worker)), + } + p.SetInfra(openstackWorker) + if err := p.Extend(); err != nil { + logrus.Errorf("Failed to extend worker nodes:%v", err) + return err + } + case "pxe": + pxeConfig := conf.InfraPlatform.(*infraasset.PXEAsset) + httpService.Port = pxeConfig.HTTPServerPort + httpService.DirPath = pxeConfig.HTTPRootDir + httpserver.StartHTTPService(httpService) + + tftpService := tftpserver.NewTFTPService(pxeConfig.IP, pxeConfig.TFTPServerPort, pxeConfig.TFTPRootDir) + go func() { + select { + case <-httpService.Ch: + logrus.Info("tftp server stop") + tftpService.Stop() + return + } + }() + go func() { + if err := tftpService.Start(); err != nil { + logrus.Errorf("error starting http service: %v", err) + return + } + }() + defer tftpService.Stop() + case "ipxe": + ipxeConfig := conf.InfraPlatform.(*infraasset.IPXEAsset) + httpService.Port = ipxeConfig.Port + httpService.DirPath = ipxeConfig.OSInstallTreePath + fileContent, err := os.ReadFile(ipxeConfig.FilePath) + if err != nil { + return err + } + httpService.AddFileToCache(constants.IPXECfg, fileContent) + httpserver.StartHTTPService(httpService) + + default: + return errors.New("unsupported platform") + } + + if err := checkNodesReady(context.Background(), conf, int(num)); err != nil { + return err + } return nil } -func extendArray(c *asset.ClusterAsset, count int) []string { +func extendArray(c *asset.ClusterAsset, count int) error { + if count <= 0 { + return fmt.Errorf("the number of nodes to be extended should be greater than 0") + } + num := len(c.Worker) - var newHostnames []string for i := 0; i < count; i++ { hostname := fmt.Sprintf("k8s-worker%02d", num+i+1) c.Worker = append(c.Worker, asset.NodeAsset{ @@ -111,45 +230,66 @@ func extendArray(c *asset.ClusterAsset, count int) []string { RAM: c.Worker[i].RAM, Disk: c.Worker[i].Disk, }, - Ignitions: c.Worker[i].Ignitions, }) - newHostnames = append(newHostnames, hostname) } - return newHostnames -} -func extendCluster(conf *asset.ClusterAsset, fileService *httpserver.HttpFileService) error { - data, err := os.ReadFile(conf.Worker[0].CreateIgnPath) - if err != nil { - logrus.Errorf("error reading Ignition file: %v", err) + if err := configmanager.Persist(); err != nil { + logrus.Errorf("Failed to persist the extended cluster asset: %v", err) return err } - fileService.AddFileToCache(machine.WorkerIgnFilename, data) - if err := fileService.Start(); err != nil { - logrus.Errorf("error starting file service: %v", err) + return nil +} + +// checkNodesReady waits for all nodes to be ready +func checkNodesReady(ctx context.Context, conf *asset.ClusterAsset, num int) error { + clientset, err := kubeclient.CreateClient(conf.Kubernetes.AdminKubeConfig) + if err != nil { + logrus.Errorf("error creating Kubernetes client: %v", err) return err } - // regenerate worker.tf - var worker infra.Infra - if err := worker.Generate(conf, "worker"); err != nil { - logrus.Errorf("Failed to generate worker terraform file") + // Get the current number of ready nodes + readyNodesCount, err := getReadyNodesCount(ctx, clientset) + if err != nil { + logrus.Errorf("error getting current ready nodes count: %v", err) return err } + allNodeNums := readyNodesCount + num - persistDir := configmanager.GetPersistDir() - workerInfra := infra.InstanceCluster(persistDir, conf.Cluster_ID, "worker", uint(len(conf.Worker))) - if err := workerInfra.Deploy(); err != nil { - logrus.Errorf("Failed to deploy worker nodes:%v", err) + // Wait for nodes to be ready + timeout := 30 * time.Minute + err = waitForMinimumReadyNodes(ctx, clientset, allNodeNums, timeout) + if err != nil { + logrus.Errorf("error waiting for nodes to be ready: %v", err) return err } return nil } -// waitUntilNodesReady waits until all nodes are ready within a given timeout -func waitUntilNodesReady(ctx context.Context, clientset *kubernetes.Clientset, nodeNames []string, timeout time.Duration) error { +// getReadyNodesCount returns the number of ready nodes in the cluster +func getReadyNodesCount(ctx context.Context, clientset *kubernetes.Clientset) (int, error) { + nodeList, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return 0, fmt.Errorf("failed to list nodes: %v", err) + } + + readyNodesCount := 0 + for _, node := range nodeList.Items { + for _, condition := range node.Status.Conditions { + if condition.Type == v1.NodeReady && condition.Status == v1.ConditionTrue { + readyNodesCount++ + break + } + } + } + + return readyNodesCount, nil +} + +func waitForMinimumReadyNodes(ctx context.Context, clientset *kubernetes.Clientset, requiredReadyNodes int, timeout time.Duration) error { + logrus.Infof("Waiting for cluster extend nodes to be ready...") ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() @@ -159,42 +299,14 @@ func waitUntilNodesReady(ctx context.Context, clientset *kubernetes.Clientset, n case <-ctx.Done(): return ctx.Err() default: - allNodesReady := true - for _, nodeName := range nodeNames { - node, err := clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) - if err != nil { - allNodesReady = false - break - } - for _, condition := range node.Status.Conditions { - if condition.Type == corev1.NodeReady && condition.Status != corev1.ConditionTrue { - allNodesReady = false - break - } - } + readyNodeCount, err := getReadyNodesCount(ctx, clientset) + if err != nil { + return err } - if allNodesReady { + + if readyNodeCount >= requiredReadyNodes { return nil } } } } - -// checkNodesReady waits for all nodes to be ready -func checkNodesReady(conf *asset.ClusterAsset, nodeNames []string) error { - clientset, err := kubeclient.CreateClient(conf.Kubernetes.AdminKubeConfig) - if err != nil { - logrus.Errorf("error creating Kubernetes client: %v", err) - return err - } - - // Wait for nodes to be ready - timeout := 30 * time.Minute - err = waitUntilNodesReady(context.Background(), clientset, nodeNames, timeout) - if err != nil { - logrus.Errorf("error waiting for nodes to be ready: %v", err) - return err - } - - return nil -} diff --git a/cmd/template.go b/cmd/template.go index cefd99a751ce15568b0f715a4f91448ba2a804c3..e29849a684504a4fdcfbf1283c5e1ccc5a5fedbb 100644 --- a/cmd/template.go +++ b/cmd/template.go @@ -17,12 +17,15 @@ limitations under the License. package cmd import ( + "errors" "nestos-kubernetes-deployer/cmd/command" "nestos-kubernetes-deployer/cmd/command/opts" "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/asset/infraasset" "nestos-kubernetes-deployer/pkg/utils" "os" "runtime" + "strings" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -46,27 +49,64 @@ func createTemplate(cmd *cobra.Command, args []string) error { if opts.Opts.Arch != "" { arch = opts.Opts.Arch } - conf, err := asset.GetDefaultClusterConfig(arch) + + conf, err := asset.GetDefaultClusterConfig(arch, opts.Opts.Platform) if err != nil { return err } + conf.InfraPlatform = getDefaultInfraAsset(strings.ToLower(conf.Platform)) + data, err := yaml.Marshal(conf) if err != nil { logrus.Errorf("Faild to marshal template config: %v", err) return err } - file, err := cmd.Flags().GetString("file") - if err != nil { - logrus.Errorf("Failed to create template file: %v", err) - return err + + output, _ := cmd.Flags().GetString("output") + if output == "" { + output = "." } - if file == "" { - file = "./template.yaml" + if !strings.HasSuffix(output, "/") { + output = output + "/" } - if err := os.WriteFile(file, data, utils.DeployConfigFileMode); err != nil { + + if err := os.WriteFile(output+"template.yaml", data, utils.DeployConfigFileMode); err != nil { logrus.Errorf("Faild to write template config file: %v", err) return err } return nil } + +func getDefaultInfraAsset(platform string) interface{} { + switch platform { + case "libvirt": + return infraasset.LibvirtAsset{ + URI: "qemu:///system", + CIDR: "192.168.132.0/24", + Gateway: "192.168.132.1", + } + case "openstack": + return infraasset.OpenStackAsset{ + UserName: "admin", + TenantName: "admin", + AuthURL: "http://controller:5000/v3", + Region: "RegionOne", + AvailabilityZone: "nova", + } + case "pxe": + return infraasset.PXEAsset{ + HTTPServerPort: "9080", + HTTPRootDir: "/var/www/html/", + TFTPServerPort: "69", + TFTPRootDir: "/var/lib/tftpboot/", + } + case "ipxe": + return infraasset.IPXEAsset{ + Port: "9080", + OSInstallTreePath: "/var/www/html/", + } + default: + return errors.New("unsupported platform") + } +} diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 2075df25195d162c44dc79cebd90fa504bb89d3a..6c9ced9205eff300474b678ccedb7af628ae413f 100755 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -51,16 +51,11 @@ func getFlagString(cmd *cobra.Command, flagName string) string { func runUpgradeCmd(cmd *cobra.Command, args []string) error { clusterId := getFlagString(cmd, "cluster-id") - kubeVersion := getFlagString(cmd, "kube-version") imageURL := getFlagString(cmd, "imageurl") if clusterId == "" { return errors.New("cluster-id is required") } - if kubeVersion == "" { - return errors.New("kube-version is required") - } - if imageURL == "" { return errors.New("imageurl is required") } @@ -78,7 +73,6 @@ func runUpgradeCmd(cmd *cobra.Command, args []string) error { if err := upgradeCluster(clusterConfig); err != nil { return err } - return nil } @@ -98,7 +92,7 @@ spec: maxUnavailable: %d `, clusterConfig.Housekeeper.OSImageURL, clusterConfig.Housekeeper.KubeVersion, clusterConfig.Housekeeper.EvictPodForce, clusterConfig.Housekeeper.MaxUnavailable) - adminconfig := filepath.Join(configmanager.GetPersistDir(), clusterConfig.Cluster_ID, "admin.config") + adminconfig := filepath.Join(configmanager.GetPersistDir(), clusterConfig.ClusterID, "admin.config") if err := kubeclient.ApplyHousekeeperCR(yamlData, adminconfig); err != nil { logrus.Errorf("Failed to deploy Custom Resource: %v", err) return err diff --git a/cmd/version.go b/cmd/version.go index a507d5eb9ae2253bb8ee6e77741f81c084185d1d..42e619b4da7da718e16edeb2614787d8b038b7a5 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -25,7 +25,7 @@ import ( func NewVersionCommand() *cobra.Command { var ( - version = "0.2.2" + version = "0.3.0" arch = fmt.Sprint(runtime.GOOS, "/", runtime.GOARCH) ) diff --git a/data/assets_test.go b/data/assets_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8996af8ede28b7cf043e6b6fda6021f34e291987 --- /dev/null +++ b/data/assets_test.go @@ -0,0 +1,14 @@ +package data_test + +import ( + "nestos-kubernetes-deployer/data" + "testing" +) + +func TestOpenFile(t *testing.T) { + file, err := data.Assets.Open("/bootconfig/systemd/init-cluster.service") + if err != nil { + t.Errorf("Failed to open file: %v", err) + } + defer file.Close() +} diff --git a/data/assets_vfsdata.go b/data/assets_vfsdata.go index 4416421fe8172655d4146b449c25b1b56d8715a6..05eb76da64ca3ee00959c535c7500fe0c631d163 100644 --- a/data/assets_vfsdata.go +++ b/data/assets_vfsdata.go @@ -20,557 +20,409 @@ var Assets = func() http.FileSystem { fs := vfsgen۰FS{ "/": &vfsgen۰DirInfo{ name: "/", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/housekeeper": &vfsgen۰DirInfo{ - name: "housekeeper", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - }, - "/housekeeper/1housekeeper.io_updates.yaml": &vfsgen۰CompressedFileInfo{ - name: "1housekeeper.io_updates.yaml", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 570829819, time.UTC), - uncompressedSize: 2294, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xb4\x54\x4d\x93\xe3\x44\x0c\xbd\xe7\x57\xa8\x8a\xc3\x5e\x88\xc3\xc0\x65\xf1\x6d\x6b\x80\xaa\x14\xcb\x32\x35\x99\xd9\xbb\xec\x56\x9c\x26\xed\xee\x46\x52\xa7\x76\xa0\xf8\xef\x54\xb7\x9d\x89\xed\xd9\xf9\xe0\x80\x6f\x96\xd4\x7a\x92\x9e\x9e\x30\xda\xcf\xc4\x62\x83\xaf\x01\xa3\xa5\x2f\x4a\x3e\xff\x49\x75\x7c\x2f\x95\x0d\x9b\xd3\xd5\xea\x68\xbd\xa9\xe1\x3a\x89\x86\xfe\x96\x24\x24\x6e\xe9\x27\xda\x5b\x6f\xd5\x06\xbf\xea\x49\xd1\xa0\x62\xbd\x02\x40\xef\x83\x62\x36\x4b\xfe\x05\x68\x83\x57\x0e\xce\x11\xaf\x3b\xf2\xd5\x31\x35\xd4\x24\xeb\x0c\x71\x49\x7e\x86\x3e\x7d\x57\xfd\x58\x7d\xbf\x02\x68\x99\xca\xf3\x3b\xdb\x93\x28\xf6\xb1\x06\x9f\x9c\x5b\x01\x78\xec\xa9\x86\x14\x0d\x2a\x49\x75\x08\x49\xe8\x48\x14\x4b\xa2\x95\x44\x6a\x33\x60\xc7\x21\xc5\x1a\x16\xde\xe1\xf1\x58\xd1\xd0\xcd\x7d\xc9\x53\x0c\xce\x8a\xfe\x3a\x31\x7e\xb4\xa2\xc5\x11\x5d\x62\x74\x8f\x98\xc5\x26\xd6\x77\xc9\x21\x9f\xad\x2b\x00\x69\x43\xa4\x1a\x3e\x65\x88\x88\x2d\x99\x15\xc0\xd8\x58\x81\x5c\x8f\xa5\x9f\xae\xd0\xc5\x03\x5e\x0d\x79\xda\x03\xf5\x38\x54\x04\x10\x22\xf9\x0f\x37\xdb\xcf\x3f\xec\x66\x66\x00\x43\xd2\xb2\x8d\x5a\x86\x34\x94\x07\x56\x40\x0f\x04\x43\x28\xec\x03\x97\xdf\xb1\x48\xf8\x70\xb3\x7d\x7c\x1d\x39\x44\x62\xb5\xe7\xd6\x87\x6f\x42\xf9\xc4\xba\xc0\x7a\x97\xcb\x19\xa2\xc0\x64\xae\x69\x40\x1d\x1b\x23\x33\x76\x00\x61\x0f\x7a\xb0\x02\x4c\x91\x49\xc8\x0f\xec\xcf\x12\x43\x0e\x42\x0f\xa1\xf9\x83\x5a\xad\x60\x47\x9c\xd3\x80\x1c\x42\x72\x26\xaf\xc8\x89\x58\x81\xa9\x0d\x9d\xb7\x7f\x3d\xe6\x16\xd0\x50\x40\x5d\xee\x4c\x17\x39\xad\x57\x62\x8f\x0e\x4e\xe8\x12\x7d\x0b\xe8\x0d\xf4\xf8\x00\x4c\x19\x05\x92\x9f\xe4\x2b\x21\x52\xc1\x6f\x81\x09\xac\xdf\x87\x1a\x0e\xaa\x51\xea\xcd\xa6\xb3\x7a\x5e\xf5\x36\xf4\x7d\xf2\x56\x1f\x36\x65\x6b\x6d\x93\x34\xb0\x6c\x0c\x9d\xc8\x6d\xc4\x76\x6b\xe4\xf6\x60\x95\x5a\x4d\x4c\x1b\x8c\x76\x5d\x4a\xf7\x65\xdd\xab\xde\x7c\xc3\xa3\x38\xe4\xdd\xac\x56\x7d\xc8\xfb\x21\xca\xd6\x77\x13\x47\x59\xc4\x17\x18\xc8\x3b\x99\xc9\xc6\xf1\xe9\xd0\xc5\x65\xd0\xd9\x94\xa7\x73\xfb\xf3\xee\x0e\xce\xd0\x85\x8c\xe5\xf4\xcb\xdc\x2f\x0f\xe5\x42\x41\x1e\x98\xf5\x7b\xe2\x81\xc4\x3d\x87\xbe\xe4\x24\x6f\x62\xb0\x5e\xcb\x4f\xeb\x2c\xf9\xe5\xf8\x25\x35\xbd\xd5\xcc\xfb\x9f\x89\x44\x33\x57\x15\x5c\x17\xfd\x43\x73\x5e\x47\x53\xc1\xd6\xc3\x35\xf6\xe4\xae\x51\xe8\x7f\x27\x20\x4f\x5a\xd6\x79\xb0\x6f\xa3\x60\x7a\xba\x96\xc1\xc3\xd4\x26\x8e\xf3\x8d\x79\x86\xaf\x41\x9d\xbb\x48\xed\x4c\x30\x86\xc4\x72\x5e\x69\xcd\xda\x0d\xfb\xe9\xe5\x79\x59\xa7\x65\x47\x52\x43\x5f\x15\xeb\xd3\x75\xb9\xbb\xa8\x13\x92\x90\xc9\xe2\x49\xb1\x63\x34\x04\xc7\xf7\x8b\x79\xbc\x30\x93\xb2\x31\xb2\xed\xb1\xa3\xfb\xdb\x8f\x6f\x41\xb5\x39\x16\x12\xbb\x27\xb8\xbf\xef\xfe\x13\x2c\x9d\x6c\xab\x37\xc1\xfc\x12\xb8\xa5\xd7\x90\xb7\x7b\x50\xce\xc2\xdf\xe7\xe8\xe1\x6d\x99\x78\x0c\xe6\x39\xd4\x26\x04\x47\xb8\xbc\x4e\x3d\x7e\xb9\xf7\x78\x42\xeb\xb0\x71\xaf\xe2\x7e\x4a\x7d\x43\x9c\x99\xf4\xc1\x14\x92\x51\x01\x99\xa0\xa1\xac\xc8\xb1\x75\x03\x38\x54\x23\xd8\x13\xa8\xed\xe9\xb9\x9a\xf2\x1d\xeb\x88\x67\xde\x2c\xaa\xbc\x35\xf3\x5a\xd6\xd3\x75\x58\x78\x2e\x8c\x2d\x1c\xb3\x99\x2e\x7c\xf3\xc6\xdf\xa4\x01\x45\x4d\xf2\xba\x0a\x4a\xd8\x4c\x07\xa1\x91\x7c\x73\x5e\x16\xc2\x57\x51\x9f\x18\x87\x44\x75\xa1\x7f\x30\x68\x60\xec\x68\x6a\x49\xcd\xe3\x31\x3e\x57\x3b\xd6\x0e\x7f\xff\xb3\xfa\x37\x00\x00\xff\xff\x66\xcf\xb8\xbd\xf6\x08\x00\x00"), + "/bootconfig": &vfsgen۰DirInfo{ + name: "bootconfig", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/housekeeper/2namespace.yaml": &vfsgen۰FileInfo{ - name: "2namespace.yaml", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 570829819, time.UTC), - content: []byte("\x61\x70\x69\x56\x65\x72\x73\x69\x6f\x6e\x3a\x20\x76\x31\x0a\x6b\x69\x6e\x64\x3a\x20\x4e\x61\x6d\x65\x73\x70\x61\x63\x65\x0a\x6d\x65\x74\x61\x64\x61\x74\x61\x3a\x0a\x20\x20\x6e\x61\x6d\x65\x3a\x20\x68\x6f\x75\x73\x65\x6b\x65\x65\x70\x65\x72\x2d\x73\x79\x73\x74\x65\x6d"), + "/bootconfig/files": &vfsgen۰DirInfo{ + name: "files", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), }, - "/housekeeper/3role.yaml": &vfsgen۰CompressedFileInfo{ - name: "3role.yaml", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 773, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x92\x41\x6b\xfb\x30\x0c\xc5\xef\xfe\x14\xa2\xf7\xa4\xfc\x6f\x7f\x72\xdd\x61\xf7\x31\x76\x57\xe3\xb7\x56\xd4\xb1\x8d\x64\x77\xd0\x4f\x3f\xea\x66\x30\x92\x6e\x8c\x9c\xfc\x90\xd1\xef\x3d\xd9\xe2\x2c\x6f\x50\x93\x14\x07\xd2\x03\x8f\x3d\xd7\x72\x4a\x2a\x57\x2e\x92\x62\x7f\xfe\x6f\xbd\xa4\xfd\xe5\x9f\x3b\x4b\xf4\x03\x3d\x85\x6a\x05\xfa\x92\x02\xdc\x84\xc2\x9e\x0b\x0f\x8e\x68\x54\xb4\x86\x57\x99\x60\x85\xa7\x3c\x50\xac\x21\x38\xa2\xc8\x13\x06\xaa\xd9\x73\x41\x37\x71\xe4\x23\xb4\xd3\x5b\xbf\xd6\x00\x1b\x5c\x47\x9c\xe5\x59\x53\xcd\x76\x23\x75\x74\x4a\xd5\x70\x06\x32\xb4\x97\xe4\x88\x14\x96\xaa\x8e\x98\xef\xef\x2c\x73\x44\x17\xe8\x61\x2e\xb6\x00\x68\xd2\x23\x60\x96\x47\x94\x76\x06\xb1\xbb\xc8\x5c\xc6\xd3\x37\x4a\x93\x1f\xad\xb8\x35\xc7\xfe\x5d\x22\x07\xb9\x42\x17\x91\x66\x87\xcd\x5c\x2b\x5c\xea\x82\xf9\x35\xd0\x6a\x8e\x95\xcb\x6e\xb7\x26\xc7\xe4\xf1\x03\x70\xd3\x0b\x3d\xf2\xc8\xc9\xff\x66\xf1\x67\xc6\x1e\x17\x19\x6f\x2b\xf5\xf0\x9f\x57\x18\xce\xd9\xd6\x20\xcf\x98\x52\x34\x94\x45\xa4\xc5\x8a\x7c\x06\x00\x00\xff\xff\xe4\x20\x3c\x4b\x05\x03\x00\x00"), + "/bootconfig/files/etc": &vfsgen۰DirInfo{ + name: "etc", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/housekeeper/4role_binding.yaml": &vfsgen۰CompressedFileInfo{ - name: "4role_binding.yaml", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 286, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x8d\xbd\x4a\x03\x41\x14\x85\xfb\x79\x8a\x79\x81\x5d\xb1\x93\xe9\xd4\xc2\x3e\x82\xfd\xdd\x99\x93\xe4\xba\xbb\x73\x87\xfb\x13\xd0\xa7\x97\x40\xb4\x11\xd2\x9d\x03\xdf\xc7\x47\x83\x3f\xa0\xc6\xd2\x4b\xd6\x85\xea\x4c\xe1\x67\x51\xfe\x26\x67\xe9\xf3\xfa\x64\x33\xcb\xc3\xe5\x31\xad\xdc\x5b\xc9\xaf\x5b\x98\x43\x0f\xb2\xe1\x85\x7b\xe3\x7e\x4a\x3b\x9c\x1a\x39\x95\x94\x73\xa7\x1d\x25\xc7\x68\xe4\x98\x76\xea\x74\x82\x4e\x2a\x1b\x96\x1b\x7c\xdd\x07\x1c\xaf\x2c\x0d\x7e\x53\x89\x71\xa7\x9b\x72\xfe\x97\xbd\x57\x49\x16\xcb\x27\xaa\x5b\x49\xd3\xcd\x7c\x87\x5e\xb8\xe2\xb9\x56\x89\xee\x7f\x72\xc3\x91\x62\xfb\xfd\x36\xa8\xa2\xe4\xb3\x84\x61\x05\x06\x74\xb2\x2f\x73\xec\x3f\x01\x00\x00\xff\xff\x54\x83\xe7\x45\x1e\x01\x00\x00"), + "/bootconfig/files/etc/docker": &vfsgen۰DirInfo{ + name: "docker", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), }, - "/housekeeper/5deployment.yaml.template": &vfsgen۰CompressedFileInfo{ - name: "5deployment.yaml.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 985, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x94\x92\x49\x6b\x1c\x41\x0c\x85\xef\xf3\x2b\x44\xdf\x7b\x96\xe4\x56\xb7\x90\x98\x10\x48\xc8\x80\x49\xee\x72\xf5\xcb\xb8\x18\xd5\x12\xa9\xda\x76\x63\xfc\xdf\x43\xdb\xb3\x74\x1b\x82\x3d\x3a\xaa\x9e\x3e\x49\xa5\xc7\x25\xfc\x86\x5a\xc8\xc9\x11\x97\x62\xab\xbb\xcd\x62\x1f\x52\xe7\xe8\x0b\x8a\xe4\x21\x22\xd5\x45\x44\xe5\x8e\x2b\xbb\x05\x51\xe2\x08\x47\xb7\xb9\x37\xec\x81\x02\x6d\x73\x81\x72\xcd\xda\x46\x4e\xbc\x83\x1e\x44\x56\xd8\xbf\x52\xda\x60\x15\x71\x41\x24\x7c\x03\xb1\x11\x47\xe4\x73\xaa\x9a\xa5\x2d\xc2\xe9\x2d\xb2\x15\xf8\xb1\xca\x20\xf0\x35\xeb\x0b\x21\x72\xf5\xb7\xdf\x27\xc8\xcb\xa0\x44\x8a\x22\xc1\xb3\x39\xda\x2c\x88\x2a\x62\x11\xae\x38\xc0\x27\xbb\x8f\x21\xb3\x3e\x97\x76\x22\x3a\xae\x70\xac\xe5\x90\xa0\x27\x5e\x4b\x3e\xc7\xc8\xa9\x3b\x37\x68\x69\xf5\x26\x74\x8c\x10\x79\x07\x47\x8f\x8f\xcb\x9f\x07\xc9\xb7\x31\xf3\x4b\xe5\xe9\x69\x2e\xda\xf6\x22\xdb\x2c\xc1\x0f\x8e\x3e\xc9\x3d\x0f\x76\x7a\x7f\xd7\x75\x5f\xc2\xe0\x7b\x0d\x75\xf8\x9c\x53\xc5\x43\x3d\x0f\x4c\xc4\x22\xf9\x7e\xab\xe1\x2e\x08\x76\xb8\x32\xcf\xc2\xf5\xd9\x62\x7f\x58\x0c\x27\xa5\xc2\x72\xaf\x1e\x36\x2d\x96\x10\x43\x9d\x65\x88\x7c\xe9\x1d\x6d\xd6\xeb\x38\xcb\x46\xc4\xac\x83\xa3\x8f\xeb\x1f\x61\xf2\xa0\xf8\xdb\xc3\x2e\x43\x7c\x38\x23\x2a\x34\x86\xf4\x3c\xef\x57\x65\x8f\x2d\x34\xe4\xee\x1a\x3e\xa7\x6e\x74\xc8\xfa\xa0\x4b\xb9\xc3\xf5\xcc\x87\xc7\x6c\xab\x59\xb0\xdc\xf7\x37\xd0\x84\x0a\x5b\x86\xbc\x7a\x65\x93\xa6\x39\x76\xcb\x32\xfe\x6f\xc8\xc9\xa6\x27\xdf\x63\x70\xd4\xfc\x0f\x16\xd9\x2a\xb4\x99\x6c\x72\xbc\x92\xa3\xe6\xea\x21\x58\xb5\xe6\x5f\x00\x00\x00\xff\xff\x47\x90\x58\x37\xd9\x03\x00\x00"), - }, - "/housekeeper/6daemonset.yaml.template": &vfsgen۰CompressedFileInfo{ - name: "6daemonset.yaml.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 1138, + "/bootconfig/files/etc/docker/daemon.json.template": &vfsgen۰CompressedFileInfo{ + name: "daemon.json.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), + uncompressedSize: 171, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xac\x93\x41\x6b\x1b\x31\x10\x85\xef\xfe\x15\x83\xef\xeb\x25\x57\xdd\x42\x93\x42\xa1\x71\x4d\x43\x7b\x2d\x93\xdd\x67\xaf\xd8\x91\x46\x48\xda\x4d\x4d\xc8\x7f\x2f\x8a\x5d\x7b\xd7\x14\xe2\x40\x75\x9c\x79\xf3\xcd\x3c\x31\xc3\xc1\xfe\x44\x4c\x56\xbd\x21\x0e\x21\xd5\xe3\xcd\xa2\xb7\xbe\x35\x74\xc7\x70\xea\x1f\x91\x17\x0e\x99\x5b\xce\x6c\x16\x44\x9e\x1d\x0c\x75\x3a\x24\xf4\x40\x40\xac\x1a\xf5\x39\xaa\x08\x62\xe5\xd8\xf3\x0e\xf1\x28\x4b\x81\x9b\x0b\x6d\xda\xa7\x0c\xb7\x20\x12\x7e\x82\xa4\x02\x24\x3a\x02\xaa\x20\xec\xdf\x67\xa7\x80\xa6\xd4\x25\x08\x9a\xac\xf1\xc0\x70\x9c\x9b\xee\xeb\x04\xfa\x51\x2c\x51\x86\x0b\xc2\x19\x47\xe0\xc4\x73\x79\x32\x63\x7f\x9c\x4e\xf4\x77\xf0\xf2\xb2\x0a\x22\x67\xab\x7e\x82\xac\xa8\xc7\xde\xd0\xd2\x6b\x8b\x2a\xaa\x60\xd5\x0f\x4f\x88\x1e\x19\x69\x65\xb5\x76\x9c\x32\xe2\xf2\xa4\x27\xd2\x50\x28\x1a\x0d\x2d\xef\x7f\xdb\x94\xd3\x34\x89\xed\x16\x4d\x36\xb4\x5c\xeb\x63\xd3\xa1\x1d\x04\xcb\xab\x7b\x3d\x6b\xec\xff\x4f\xaf\xf2\x1b\x6c\x3d\xe2\xd9\x69\x75\xed\x16\x9d\xfe\xda\x39\xf6\xad\x99\x34\xac\xa8\xbe\xb6\xda\x3a\xde\xc1\xd0\xcb\xcb\xea\xd3\x49\xf5\xa5\xc4\x7e\x44\x79\x7d\xbd\xd0\x6d\x06\x91\x8d\x8a\x6d\xf6\x86\x6e\xe5\x99\xf7\xe9\x2c\x18\x55\x06\x87\x07\x1d\x7c\x4e\xf3\x59\x0e\x76\x86\xb0\x8b\xdc\xa2\x6a\xdf\x0e\x67\x22\x20\x72\xa5\x68\xc3\xb9\x33\x54\x8f\x1c\x6b\xdf\xb7\xe7\x3c\xfc\xf8\x2f\xdc\xfa\xdb\xdd\xfd\xaf\xf5\xed\xc3\xfd\x8c\x34\xb2\x0c\xf8\x1c\xd5\x99\x59\x98\xb6\x16\xd2\x7e\xc7\xf6\x22\x4c\xd3\xfb\x1e\x6f\x2e\x92\x6f\x45\x87\xb1\xca\x76\xae\xca\x36\xac\xd9\x61\x31\x35\x3c\xdb\xd0\x77\x9c\x76\x9a\x0e\x36\x67\x8d\xc2\xcc\xf8\x9f\x00\x00\x00\xff\xff\x09\x7d\x30\x1a\x72\x04\x00\x00"), - }, - "/ignition": &vfsgen۰DirInfo{ - name: "ignition", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x5c\xcb\xb1\x0a\xc2\x30\x10\x80\xe1\xbd\x4f\x11\x6e\x6e\x52\x5c\x0b\x3e\x82\x8b\xab\x74\x90\xe4\xac\x87\xb4\x09\x97\xb3\x58\x8e\x7b\x77\x29\x95\x0e\xce\xff\xff\x69\xe3\x1c\xe0\x07\xa3\xcf\x45\x2a\xf4\xee\x06\xf3\x5d\x68\xc1\x10\x47\xce\xef\x92\x98\x16\xe4\x73\x5d\xab\xe0\x94\x60\x68\xb7\x9f\x71\xa4\x2a\xbc\xfa\x89\x98\x33\xef\xec\x29\x52\x6a\xdf\x75\x47\x3c\x85\x94\xe3\x0b\x39\x50\x06\x55\x7a\xb8\x70\xfd\xa5\xcb\xce\x9c\x37\x6b\x0f\xa7\xfa\xdf\xcd\x40\x15\xe7\xb4\x7d\x43\x63\xdf\x00\x00\x00\xff\xff\xe7\x13\xeb\x18\xab\x00\x00\x00"), }, - "/ignition/controlplane": &vfsgen۰DirInfo{ - name: "controlplane", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - }, - "/ignition/controlplane/files": &vfsgen۰DirInfo{ - name: "files", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - }, - "/ignition/controlplane/files/etc": &vfsgen۰DirInfo{ - name: "etc", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - }, - "/ignition/controlplane/files/etc/hosts.template": &vfsgen۰CompressedFileInfo{ + "/bootconfig/files/etc/hosts.template": &vfsgen۰CompressedFileInfo{ name: "hosts.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), uncompressedSize: 167, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x32\x34\x32\xd7\x33\xd0\x33\xd0\x33\x54\x50\x50\xc8\xc9\x4f\x4e\xcc\xc9\xc8\x2f\x2e\x41\xb0\xf4\xc0\xac\x94\xfc\xdc\xc4\xcc\x3c\x84\xa8\x09\x12\x13\x59\x85\x09\x97\x95\x15\xc8\x20\x08\x20\xda\x38\x33\x24\x26\xb2\x0a\x33\xae\xea\x6a\x3d\x8f\xe2\xcc\x82\xda\x5a\x40\x00\x00\x00\xff\xff\x0b\x57\x23\x96\xa7\x00\x00\x00"), }, - "/ignition/controlplane/files/etc/isulad": &vfsgen۰DirInfo{ + "/bootconfig/files/etc/isulad": &vfsgen۰DirInfo{ name: "isulad", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), }, - "/ignition/controlplane/files/etc/isulad/daemon.json.template": &vfsgen۰CompressedFileInfo{ + "/bootconfig/files/etc/isulad/daemon.json.template": &vfsgen۰CompressedFileInfo{ name: "daemon.json.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 1174, + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), + uncompressedSize: 1124, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x54\xcb\xae\xd3\x3c\x10\xde\x9f\xa7\x88\xbc\xfe\x9d\xf4\x2f\x12\x8b\x4a\x67\x83\xc4\x02\xb1\x00\x75\x8b\xd0\x91\x6b\x4f\xd2\x21\x8e\x6d\x8d\xed\xd0\x50\xe5\xdd\x91\x5d\x27\x6d\x03\x62\x57\xcd\x77\xe9\x37\x97\xf8\xfa\x52\x55\x55\xc5\xe0\x02\x92\x5b\x17\x3c\x3b\x54\xdf\x98\x11\x01\x47\xa8\x65\x47\x36\x3a\x45\x38\x02\xbd\xfa\xc9\x07\x18\x14\xfb\xfe\xdf\x4d\x91\x31\x76\xa8\x18\xfa\xa8\x05\x2b\x55\x05\xad\x88\x3a\x70\x8a\x26\xe0\x00\x09\xd7\x92\xd8\xaa\x11\xee\x9c\x6a\xcd\x28\xa8\xd1\x78\x6a\xb2\x58\x2d\xb8\x0f\x22\xc0\x8a\x53\x34\x1b\x1c\x4c\x87\x66\x6b\xaa\x6d\xc7\x35\x8c\xa0\x53\xfd\xe3\xf1\xf8\xe5\xb8\x20\x0e\x55\x8b\xfa\x6f\x86\xb5\x43\xf5\xa8\x2f\x9d\xdf\x86\xb1\x56\x93\x98\x0f\x56\x65\x87\xdd\xfb\xdd\xae\x48\x56\x82\x13\xe1\x1f\xed\x64\xda\x20\x2e\x7c\x09\xf1\xff\x16\xf0\xf8\x2b\x03\xef\x76\x9f\x3f\xb0\x0c\xcd\x0f\xa1\x6e\x83\x4f\xb8\x0f\xca\xc6\xb0\x04\x96\xd6\x04\x81\x06\x88\x6b\xdb\x3d\xa7\xbe\x4b\x7e\x78\x6b\x6e\x7f\xfc\xe4\x7b\xb6\xb6\xe7\xde\x81\xcc\xb1\x21\xc8\xa6\x6c\xac\x44\x6f\x12\xc1\x2f\xc5\x3a\xb9\x3c\x2c\x87\x02\x4f\x5b\x4d\x59\x0e\x15\xdb\x0f\x77\xc8\x92\xe8\xe0\x21\xb1\x1d\x81\xb4\x98\xf6\x5b\xc6\x72\x62\xf7\xc8\x0b\xb3\x4e\x3f\x08\x15\xbc\xf5\x40\x06\xf4\x9b\x3c\x83\xec\x5f\x03\xc5\xd2\xc2\x72\x78\x04\x1d\xfa\x40\x13\x1f\x90\xc8\xd2\xc6\x4e\x59\xd9\x03\xd5\x68\x9f\x45\x68\x3c\xc8\x48\xc0\x8b\x1a\x61\xa3\xbb\x5e\xeb\x4f\x83\xe8\xe0\x58\xdc\xe7\xf9\xd9\xc0\x59\xc5\xbd\x30\xea\x64\x2f\x1c\x13\x31\x75\xf9\xa7\xa8\xb9\x5e\xeb\xaf\x22\x7a\xc8\xf5\x79\x5e\xfa\x2f\x5f\x54\x1c\x84\xef\xf3\x46\x73\x9a\x15\x85\xf0\xd3\x52\xcf\x9d\x8e\x1d\x9a\x84\x4b\x83\xeb\xba\x0d\xf2\x13\x1a\xae\x30\x4f\xb6\xb1\x2e\x34\xd2\x60\x73\x42\xf3\x48\x91\xd6\xb4\x2b\x27\x2d\x36\x71\x0c\x84\x7a\x3d\xf4\x1c\x9b\x6b\x31\x01\xf1\x3c\x5c\x76\xa8\x5a\xa1\x3d\x14\x3c\x7a\xe0\x0a\x24\x4d\x2e\x80\xe2\x3d\x4c\xec\x50\xa5\xf1\x6f\x47\xe8\x7b\x74\x7c\x04\xc2\x76\xe2\x60\x5a\x4b\x12\x36\x4e\x92\x70\x79\x01\x36\x9f\x55\x2f\x82\xc8\x8f\x86\xad\xd7\x33\x56\x75\xaa\xd6\xe3\xbe\x9c\xea\xcb\xfc\xf2\x3b\x00\x00\xff\xff\x2e\x9c\xa7\x42\x96\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x54\x3d\xcf\xd3\x30\x10\xde\xfb\x2b\xa2\xcc\x38\x29\x45\x62\xa8\xc4\x82\xc4\x80\x18\x40\x5d\x11\x7a\xe5\xda\x97\xf4\x88\xe3\xb3\xce\x76\x78\x43\x95\xff\x8e\xec\x26\x69\x1b\x10\x5b\x75\xcf\x47\x9f\xfb\x88\xaf\xbb\xa2\x28\x8a\xb2\x65\x8a\xae\x3c\x16\x25\xfa\x68\x64\xf9\xe6\x56\xd5\xd0\xc8\x68\x82\xe0\x68\x03\xf6\x90\x70\xa3\x78\x41\x5b\x96\xee\x92\x6a\xf5\x20\xb9\x36\x78\xae\xb3\x58\x2f\xb8\x0f\x32\xc0\x8a\x73\xb4\x1b\x1c\x6c\x8b\x76\x6b\x6a\xa8\x15\x06\x06\x30\xa9\xfe\xe9\x74\xfa\x7a\x5a\x10\x87\xba\x41\xf3\x2f\xc3\xca\xa1\x7e\xd4\x93\x0b\xbe\x3c\x16\xb7\xd6\xd6\x6a\x12\x8b\x9e\x74\x76\xd8\xbf\xdf\xef\x67\xc9\x4a\x70\x32\xfc\xa7\x9d\x4c\xeb\xe5\xab\x58\x42\xbc\xdd\x02\x1e\x7f\x67\xe0\xdd\xfe\xcb\xc7\x32\x43\xd3\x43\x28\xcd\x38\x00\x27\xdc\x07\x4d\x31\x2c\x81\x15\xd9\x20\xd1\x02\x0b\x43\xed\x73\xea\xbb\xe4\xa7\x27\x7b\xfb\xe3\x27\xdf\x0b\x51\x27\xbc\x03\x95\x63\x43\x50\xf5\xbc\xb1\x39\x7a\x9d\x08\x7e\x29\x56\xc9\xe5\x61\x39\x1c\x44\xda\x6a\xca\x72\x2c\xca\x43\x7f\x87\x88\x65\x0b\x0f\x89\x69\x00\x36\x72\x3c\x6c\x19\xf3\xa0\xbf\xdf\x23\x2f\xcc\x2a\xfd\x60\xd4\xf0\xd2\x01\x5b\x30\x2f\xea\x02\xaa\xfb\x10\x38\xce\x2d\xfc\x98\xad\x18\x5a\xf4\x81\x47\xd1\x23\x33\xf1\xc6\x4e\x93\xea\x80\x2b\xa4\x67\x11\x5a\x0f\x2a\x32\x88\x59\x8d\xb0\xd1\x5d\xaf\xd5\xe7\x5e\xb6\x70\x9a\xdd\xa7\xe9\xd9\xc0\x91\x16\x5e\x5a\x7d\xa6\x57\x81\x89\x98\xba\xfc\x5b\x54\x5f\xaf\xd5\x37\x19\x3d\xe4\xfa\x34\x2d\xfd\x5b\x19\x70\x80\x2a\xf6\xd2\x77\x79\xa3\x39\xcd\x8a\x42\xf8\x45\xdc\x09\x67\x62\x8b\x36\xe1\xca\xe2\xba\x6e\x8b\xe2\x8c\x56\x68\xcc\x93\xad\xc9\x85\x5a\x59\xac\xcf\x68\x1f\x29\x8a\x6c\xb3\x72\xd2\x62\x13\xc7\x42\xa8\xd6\x43\xcf\xb1\x85\x91\x23\xb0\xc8\xc3\x2d\x8f\x45\x23\x8d\x87\x19\x8f\x1e\x84\x06\xc5\xa3\x0b\xa0\x45\x07\x63\x79\x2c\xd2\xf8\xb7\x23\xf4\x1d\x3a\x31\x00\x63\x33\x0a\xb0\x0d\xb1\x82\x8d\x93\x62\x5c\x5e\x80\xcd\x67\xd5\xc9\x20\xf3\xa3\x41\xd5\x7a\xc6\xba\x4a\xd5\x6a\x38\xcc\xa7\xba\x9b\x76\x7f\x02\x00\x00\xff\xff\x68\x18\xbf\x42\x64\x04\x00\x00"), }, - "/ignition/controlplane/files/etc/nkd": &vfsgen۰DirInfo{ - name: "nkd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + "/bootconfig/files/etc/nkdfiles": &vfsgen۰DirInfo{ + name: "nkdfiles", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), }, - "/ignition/controlplane/files/etc/nkd/init-config.yaml.template": &vfsgen۰CompressedFileInfo{ + "/bootconfig/files/etc/nkdfiles/init-config.yaml.template": &vfsgen۰CompressedFileInfo{ name: "init-config.yaml.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), uncompressedSize: 849, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xac\x51\x41\x8f\x9b\x3c\x10\xbd\xf3\x2b\x2c\xee\xc0\xea\xd3\x77\xa8\x7c\x8b\xd2\x3d\x44\xdb\xae\xa2\xa4\xed\xdd\x81\x89\x77\x84\x99\x41\xe3\x21\xcd\x0a\xf1\xdf\x2b\x03\xc9\x6e\x8e\x95\x7a\x02\x3f\xbf\x37\xf3\xde\xb3\xeb\xf1\x17\x48\x44\x26\x6b\xda\xe1\x04\xae\xe9\xca\xf6\x4b\x2c\x91\xab\x71\x2c\x5f\x16\x64\x73\x27\x4d\x53\x76\x62\xd6\xa8\xe2\xfa\x1f\xdc\x02\x45\x9b\x15\xc6\x0b\x0f\x7d\xb4\x99\x31\x85\x89\xef\x51\xa1\xb3\x77\x56\x0f\x12\xed\x3a\xd9\x36\x70\x76\x43\xd0\x82\xb8\x81\x42\x93\x3e\x33\x66\xfe\x5a\x33\x8e\xe5\x3c\x71\x9a\x12\xa6\xc1\x9a\xff\xfe\x7f\x7b\xea\x9e\x62\x66\xcc\x10\x9d\x87\xdb\x02\xf4\x84\xe4\xe7\x7f\x37\xe8\x1b\x90\x62\xed\x14\x99\xb2\x16\xa9\xb1\x66\x47\xa8\x5b\xa6\x33\xfa\x41\x16\x3c\xad\x3b\x80\xc7\x64\x28\x01\x69\x50\x2d\x78\xe4\xba\x05\x9d\x37\x6f\x6f\xa7\x79\x3b\xb9\x0e\x66\xf8\x95\x1b\x78\x75\x1d\xcc\x68\x0a\x11\x40\x9f\xaf\x2a\x6e\x23\x7e\xb6\x63\xcc\x85\xc3\xd0\x41\xd1\x87\xc1\x23\x15\x0d\x8a\x35\x79\xc5\xbd\x56\x01\x4f\x70\x85\xba\x4a\x32\x21\x50\x88\xd5\x3a\x61\x25\xc7\x6a\xd1\x56\x33\x2d\xcf\x6a\x10\xc5\x73\xca\x02\x2f\xf0\xbe\xd8\x7a\x80\xa6\x29\x2b\x8a\x22\xfb\xfb\x27\x5b\x7a\xd9\x86\x21\x2a\xc8\x63\x35\x35\x93\x0a\x87\x00\xf2\xdd\x91\xf3\x20\x29\x15\x3c\x46\x3c\x07\xb8\x16\xff\x2e\xe7\x07\xf1\x1e\x63\xf5\xfd\xe1\x18\x3b\xe7\xe1\x00\x3d\x47\x54\x96\xa5\x8c\xdd\x82\xcd\xcf\x98\xba\x58\xad\xef\x83\x23\x78\xa6\xa6\x67\x24\xb5\x26\x1f\xc7\x72\xb3\xdf\x1d\x41\x2e\x20\x3f\x0f\xdf\xa6\x29\xcf\x08\xf4\x37\x4b\x8b\xe4\x53\xa0\x08\x72\xc1\x1a\x8e\xc3\x89\x60\x15\x1c\x3f\x43\x49\x61\x4c\xcf\xcd\x67\xc6\xfe\x76\x5c\x6e\x1b\x8a\x5f\xb9\x73\x48\xd6\xe4\xf5\xd2\x6b\x19\xb8\x76\x21\xff\x13\x00\x00\xff\xff\x05\xcc\x21\x0f\x51\x03\x00\x00"), }, - "/ignition/controlplane/files/etc/nkd/node-pivot.sh.template": &vfsgen۰CompressedFileInfo{ + "/bootconfig/files/etc/nkdfiles/node-pivot.sh.template": &vfsgen۰CompressedFileInfo{ name: "node-pivot.sh.template", - modTime: time.Date(2024, 3, 18, 8, 58, 14, 95122884, time.UTC), - uncompressedSize: 2294, + modTime: time.Date(2024, 6, 13, 2, 8, 56, 897199049, time.UTC), + uncompressedSize: 12900, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\xdb\x6e\xdb\x46\x10\x7d\xe7\x57\x4c\x29\x23\x69\x83\x2c\x99\xbc\xba\x70\xfa\xd0\xba\x40\x80\xa0\x28\x64\x18\x28\x60\x29\xc2\x6a\x39\xa4\x06\x26\x77\xe9\xdd\xa5\x23\x41\xd6\xbf\x17\x7b\x91\x44\xd2\xb2\xe5\xd6\x0f\xbe\x8c\xcf\x9c\x39\x73\x5d\x4d\x7e\xca\x97\x24\x73\xb3\x4a\x92\x09\xfc\xd9\x49\x61\x49\x49\xb0\x0a\x1a\x2e\x79\x85\x60\x50\x3f\x92\x40\x30\x96\x6b\xdb\xb5\xc0\x65\x01\x28\xf9\xb2\x46\x50\x12\x96\x4a\xd9\x24\x20\x17\x11\xf9\xf3\x2f\xb0\x4d\x00\x60\xef\xb9\x90\xbc\xc1\xab\xf4\xe2\x73\xea\xad\x54\x82\xd9\x18\x8b\x8d\xb0\x35\x90\x61\x5c\x58\x7a\x44\x60\xec\xa1\x23\xb4\x90\x5e\xf4\xdd\xd2\x5f\xc1\xae\x50\x7a\x47\xf7\x85\x62\xa5\x46\x10\x20\x03\xbc\xd6\xc8\x8b\x0d\xe8\x4e\x4a\x92\x55\x08\x84\xb5\xc1\x73\x8e\x52\xd9\xbd\xd3\xc7\x90\x21\xc9\x2a\xcb\xb2\xf4\xe0\x38\x90\xeb\x11\x63\x89\xf0\xee\x5d\x0f\x12\x4b\xf3\x7a\x1a\x2f\x29\xda\x2b\x00\xd3\x09\x81\xc6\xf4\x74\x0c\xb2\x39\xfa\xdf\x86\x70\x56\x45\x6d\x03\xbe\x9e\xbb\xf7\x58\x93\x85\xcf\x07\x53\x49\x49\xfc\xb1\x73\xad\xff\x7d\x85\xe2\xde\xa7\x1b\x1b\x8e\x6b\x32\xd6\xf8\x7e\xc7\x51\x20\x9b\x08\x87\x5a\x70\x59\x2c\xfe\x7f\xd3\x6b\x32\x96\x75\x92\x2c\x2b\xa9\x46\x03\x4f\x50\x69\x6c\x81\x3d\x8c\xca\x91\xc5\x3f\xc6\xd5\x1b\x46\x1e\x97\xfa\x6d\xbd\xdf\x3b\x17\x0a\xc3\x14\xf8\x6c\x3f\x82\xb9\xa7\xb6\xed\xcf\x40\x28\xcf\x4b\x69\x43\xba\xdd\x66\xd3\x4e\x5a\x6a\x70\xb7\x4b\x5f\xc1\xad\x54\x67\xf0\x1e\xb1\x45\xcd\x0a\x8e\x8d\x92\xa9\xaf\xba\x92\x25\x55\x9d\x46\x97\x22\x08\x4d\x0a\x84\x92\x96\x93\x44\xed\x26\xd3\x11\x27\x54\xc2\x1d\xb0\x12\xd2\x1c\xad\xc8\x1d\xc8\x7f\xcb\x84\x92\x65\x0a\xf3\x5e\x79\x3c\x72\xa8\x09\xae\x20\x75\xe8\x21\x30\x82\x0f\x85\x9f\xdd\x39\xcc\x2c\xa3\x86\x57\x38\x9b\xa7\x70\x22\xd4\x89\x21\xee\x53\x7c\xbf\xbb\xbb\x34\x2d\x17\x78\x39\x9f\x7f\x68\x79\x67\x70\xe1\xd9\x9c\x80\x37\xd2\x85\x01\x2a\x80\x11\xbc\x37\x4f\xdf\x87\x24\xd9\x87\xa7\x11\xeb\x76\x9b\x7d\x75\xbf\x4f\xb1\x22\x63\xf5\x66\xb7\xcb\xb7\xdb\xec\x6f\x07\xf2\xf6\xdd\x2e\x7d\x7a\x7f\x2a\xf4\x70\x2d\xc6\x9b\xd5\x57\x91\x8f\x0a\x93\x73\xf8\xef\x22\xce\x6b\x88\xbb\xf8\xca\xa2\x7b\x19\x41\xc5\x3c\x85\x2f\x5f\xce\xa7\xe5\xdd\x86\x62\x67\x6f\x50\x3b\x4b\xcf\xd2\xf7\xd4\x1e\xb7\x5a\x63\xb8\x40\x0e\xbe\xdf\x9c\x92\xdc\x8c\xff\x41\xc6\x1f\xa9\x9b\xeb\x6f\x24\xbb\x75\x12\x94\x05\xab\x3b\x76\xd1\xee\x57\xee\xd0\xfd\xc9\xcd\xf5\xb7\xaf\x7f\xdd\xfe\x73\x85\xb2\x54\x5a\x90\xac\x0e\x96\x22\xf0\x15\x93\x2a\x16\xd6\x60\xed\x08\x72\xe1\x97\x29\x31\x68\x83\x13\xc2\xa7\xc1\x65\x9b\x62\x8d\x3c\x26\x7a\x3b\x75\xaf\x0f\x60\xd3\xda\x4d\x5c\x30\x19\x36\x67\x08\x72\x1b\xd4\x5f\x9c\x09\x5c\xaf\x51\x74\x16\x41\xe3\x92\xc7\x56\xe9\xb6\x61\xca\x58\x8d\x7b\x2b\x30\x86\xeb\x16\x35\x35\x28\x2d\xaf\x21\xfc\x93\x75\xf2\x11\x35\x95\x84\x05\xf3\x2d\xb9\x2c\x94\xb8\x47\x7d\x99\xe7\xa7\x02\x03\x63\xcb\x4d\xcb\x8d\x61\x85\xa6\x47\xd4\x31\xfe\x21\x1d\x77\x31\x62\xb8\x1f\xdc\xec\xdf\x8c\xb2\xab\x8f\xa7\xe0\xe2\x37\x60\xf8\x00\x9f\x9e\xed\x7e\xe8\xc1\x34\x78\xab\x16\x35\xf7\xcf\xbe\x50\x4d\x5b\xa3\xc5\xa2\xc7\x56\x6f\x32\x98\xa2\x7b\xe8\x5d\xb3\x5c\xd0\xd0\xf4\xc1\x33\xd9\x9f\x03\xff\x99\xe0\x85\x2b\xfc\x2c\x62\xc9\xa9\xc6\x22\x83\x1b\x4f\x00\x3f\xa8\xae\xfd\x3d\x5e\x62\x64\xc2\xe2\x78\x89\x93\x03\xe3\x9e\xed\x74\x43\x8f\x77\xfc\x79\x6b\xb2\xd4\x8d\xe5\xbf\x01\x00\x00\xff\xff\xd0\xd1\x38\x68\xf6\x08\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xbc\x3b\xdb\x72\xdb\xb8\x92\xef\xfc\x8a\x1e\x5a\x1b\x5b\x9e\x21\x95\x6c\x6d\xed\x83\x4f\x94\xd9\x2a\x8f\x73\xd6\x7b\xe6\x24\x2e\x3b\x79\xb2\x1c\x15\x4c\x36\x25\x8c\x28\x80\x01\x40\xd9\x5e\xc7\xff\xbe\x05\x80\x20\xc1\x8b\x2e\x4e\x6d\x1d\x3f\xc4\x26\xd0\x37\x34\xfa\x06\xa0\x73\xf4\xcb\xe4\x9e\xb2\xc9\x3d\x91\xcb\x20\xc0\x47\x4c\xe0\xc3\x07\x98\x6c\x88\x98\xe4\x7c\x31\x61\xab\x74\xce\x78\x8a\xf3\x82\x6e\xb8\x8a\x73\xbe\x80\x7f\xff\xf0\xe6\x5d\x10\x1c\xc1\x17\x5c\x17\x39\x51\x08\x1b\x22\x28\xb9\xcf\x51\x06\x4b\xce\x57\xf3\x8c\xe6\x28\xe7\x05\x51\xcb\x69\xf8\xfc\x1c\xff\x37\xe7\xab\x8f\x7a\xe8\x8a\xa8\xe5\xcb\x4b\x18\x88\x92\x29\xba\x46\x33\x79\x6d\xff\xd6\xc3\x74\x4d\x16\x38\x17\xb8\xa0\x52\x89\x27\x33\x7b\xa9\x87\xae\xab\x11\x0d\x53\x90\x52\xe2\xdc\x40\x1a\x80\x2b\xfd\x6d\xa0\x0c\x61\xcc\x91\xb8\xf9\x79\x29\x72\xcb\xc2\x8e\x1a\xa8\xaf\xd7\xb9\x06\x4c\x50\x28\x59\x03\x9c\xeb\xaf\xaf\x22\xb7\x0c\x92\x95\x46\xce\xa9\x54\xd3\x93\xe7\x67\x41\xd8\x02\x21\xbe\xb2\xc3\x7f\x52\xa9\x5e\x5e\x34\xce\xcb\x4b\x08\xcf\xcf\xc8\xd2\x97\x97\x71\xe0\x64\x9e\xaf\xa9\x10\x5c\xc8\x8a\xad\x1d\xfc\xa7\x1d\xd3\xc4\x83\x23\xf8\x58\xb2\x44\x51\xce\x40\x71\x58\x13\x46\x16\x08\x12\xc5\x86\x26\x28\x03\xfb\x3d\xaf\xbe\x4f\xc6\xf0\x1c\x00\x00\xe4\x3c\x21\xb9\x83\x9a\x33\xa2\x35\x37\x7a\x17\x9a\x39\x9a\x81\x7c\x92\x0a\xd7\x89\xca\x81\xca\x88\x24\x8a\x6e\x10\xa2\xe8\x7b\x49\x51\x41\x38\xf2\xd1\xc2\xbf\x81\x5a\x22\x33\x88\xfa\x07\x93\x25\xef\x80\x00\x95\x40\x72\x81\x24\x7d\x02\x51\x32\x46\xd9\xc2\x32\xc2\x5c\xe2\x3e\x44\xc6\x95\x43\xfa\x0d\xa4\x22\x42\x51\xb6\x88\xe3\x38\xac\x11\x5b\xe2\x1a\x88\xae\x88\xf0\xe6\x8d\x07\x82\x4c\x9b\xd5\x9e\x65\x6c\x93\xc8\x49\x00\xb2\x4c\x12\x94\xd2\x93\xa3\xb5\x9a\x06\xff\xab\x65\xa7\x78\x25\x1b\x17\x4e\x82\x16\x65\x8f\x90\xc1\x7d\xa4\x0a\xde\xd5\x43\x19\x0d\xaa\x5f\x2f\xdd\x0d\x4f\x96\x98\xac\x80\xb0\xb4\xa2\x5f\x11\x0d\xcc\xf8\x9c\xb0\x74\x6e\xc6\x7f\xde\x02\xb4\xd5\x46\x25\xa3\x2a\x32\x3e\x08\x3f\x60\x21\xb0\x80\xe8\x7b\x47\x37\x71\xf5\xd1\x55\x65\xdb\x02\xbb\x7a\x3f\xcc\x10\x1c\x72\xca\xd1\x9a\x04\x3e\x52\xa9\x7e\x03\xb9\xa2\x45\xe1\x1b\xc4\x90\x86\x52\x2a\x8d\xbe\x33\x2a\xf0\x81\xe4\xb9\x5e\x1e\x55\xc7\xb2\xda\x87\x34\xa8\x00\xe6\x0e\xa0\xd6\xd1\x11\x9c\x1b\xed\xd2\xcc\x43\x6e\xf0\x0e\x70\x16\x87\x96\x0e\xba\xc9\x47\x8f\x68\x6d\xe4\x56\x9a\xae\x95\x6f\x33\x77\x5e\x34\x3c\xda\x76\xde\x5d\x76\xba\xd5\xc4\x6b\x31\x2a\x94\xd4\x59\x77\x56\xe6\xf9\xd3\x7e\x13\xff\x48\xa8\x46\x1a\x50\xf5\x41\x56\x3d\xb0\xfb\xbe\x62\xbc\x08\xb0\x63\x93\x75\x8e\x29\x15\x82\x4e\x16\x60\x0c\x35\xa8\x86\xe6\x7a\xc8\x8c\x74\x6c\x3f\xa5\x02\x13\xc5\x75\x5a\xf0\x0c\xff\x16\x7e\x81\x28\x85\x70\x54\x4f\x87\x70\xd7\x51\x9d\x40\x55\x0a\x06\x4e\x18\xdf\x9f\x96\x98\xe7\x36\x59\x4d\x4f\x7c\x1a\x93\xd3\x71\xc3\x61\xf4\x7c\xe4\x01\xde\xfe\xd7\xdd\x0b\x44\xf8\x1d\xde\xf6\x18\x59\x6d\x7c\xe2\x76\x45\x90\xf1\x92\xa5\x40\x99\x5d\x65\x4d\xfc\x0c\x3c\x46\x1d\x21\x5b\x32\x66\x5c\x18\x4a\x9a\x44\x38\x7a\xee\xc8\x10\xfe\x0d\x52\xee\xdb\xd9\x2d\x44\x19\x84\x23\x0d\xd0\xd7\x41\x23\xde\x85\xd1\xb3\x09\x8a\x89\xa0\x85\x3a\x03\x8b\xd2\x02\x8d\x1d\xa1\xee\xee\xa7\x9c\xe1\x40\x54\xe3\x2c\xa3\x8b\x52\x20\x9c\x5f\x5f\x46\x9f\xa1\x4a\xef\x41\x3d\x3e\x4f\x04\xe5\xf3\x6a\xb8\xb3\xb1\x16\xc8\x2c\x6c\x1a\x4e\x50\x25\x13\x0d\x6c\xfe\x89\xf5\x5c\x18\x74\xbd\x5b\x2d\x1d\x23\x8b\x6b\xb5\x64\x82\x8c\x6c\x19\x86\xd6\x87\x47\x3e\xdc\xb2\x63\x7d\x5a\xda\x8a\xcd\xf6\x9d\x41\x8b\x40\x30\xe0\x1b\x6e\xbb\x68\xd6\x84\xda\xd9\xad\x16\x7f\x16\x9b\x32\x64\x76\x17\x76\xe4\xe8\x48\x71\x04\x97\x76\x51\x39\x65\x08\x0f\x54\x2d\xe1\xd8\x2b\x73\x8e\x4d\x20\x65\xc7\x75\x1c\x25\x69\x0a\x54\x55\x16\x95\x3f\xf9\x46\xf0\x4b\x23\xc3\x37\x8f\xc4\x1e\x01\xf4\x8f\xc4\x14\x22\x0a\xc7\x93\x6f\x1d\xe1\x27\x04\x3c\x4a\x30\x85\xf0\x38\x1c\xb5\x2b\xb5\xc9\xc8\xe7\x75\x1c\x1e\x77\xd8\x6d\x0f\x49\x7b\x97\x6e\x77\xf5\x37\x10\x58\xe4\x24\x41\xa0\x4a\xc2\x86\xe4\x25\x0e\x0a\x2f\x7f\x7c\x6b\xcb\x1a\x9f\xfe\x78\xad\xf0\x3f\xb6\x4a\xbf\x35\x02\x46\x08\xa1\x51\x9a\xd5\xd9\xdd\x8c\xb5\x99\xce\x76\xf2\x9c\x85\xa1\xae\xb7\x07\x98\x7a\xa6\xd5\x64\x0a\x81\xb6\x74\xd0\xfc\x06\xcd\xf9\xc6\x4b\x07\x0e\x1a\x53\xeb\x31\x5b\x33\x78\x93\x15\x1c\x7d\x0f\xbe\x6f\xf1\xfb\x43\x40\xb5\xd2\x5e\x0c\xa8\xc6\xf7\x07\x01\xce\x14\xa1\x0c\x85\x9c\x54\x38\x14\x65\x15\x10\xea\x14\xeb\x5b\xfc\x05\x1c\x7f\x9b\xc9\xd3\x93\xa3\xdf\x67\xf2\x74\x5c\xb2\xef\x25\xc9\x69\x46\x31\x8d\x24\x12\x91\x2c\xa3\x86\xcc\x4c\x9e\x4e\x8f\xf7\xf9\xe4\x1f\x98\x69\x9b\xdc\x49\xc8\x16\x27\x6d\xff\xd4\x35\xa3\xab\x59\x12\xbe\x5e\x23\xd3\xda\xe7\xa5\xea\x68\x7c\x37\xe1\x29\xdc\xce\xc2\x94\x27\x2b\x14\x31\xe5\xb3\xf0\x2e\x84\x1f\x20\xcb\x94\x83\x42\x84\x88\x74\x23\xdb\x87\x49\x8a\x9b\x09\x2b\xf3\xbc\x6b\x39\xb7\x10\xe9\xfc\xd1\x3d\xa1\xf4\x83\x61\x6b\x27\xb4\xfa\x91\xa9\xe9\xe8\x24\x21\x0a\xde\xbf\xbf\xf8\xfc\x71\x7e\x7e\x7d\xf9\x79\x7e\x7d\xf1\xf7\xe0\xf6\xd6\x51\xbb\xbb\x0b\x0a\x81\x19\x7d\xd4\xae\x55\x8b\x1b\x06\x9a\x96\x31\x8d\xf6\xb0\x87\x19\x5b\x39\xee\xee\x5a\xb0\x7d\x31\x83\x16\x67\x27\xeb\xb8\x5f\x6a\x55\x95\x68\x5b\xfe\xd7\x69\xad\x9b\x12\x9c\x30\xb5\x75\x5b\x39\x13\xbe\x2e\x72\x54\x98\xee\x2b\xf8\x76\xb8\xea\x6b\xdc\xf5\xa0\x62\x6e\x8b\xdb\xee\x3b\x9e\x94\x45\x4a\x14\xce\x57\xe5\x3d\xe6\xa8\xe6\x76\xa1\x1d\xe7\xf4\x27\xe7\x29\x15\x95\x87\xda\x75\xa6\xd5\xef\x49\x05\xe5\x0e\x16\x71\xa5\x1d\x4b\x82\xe1\x83\x41\x9f\x86\xa3\x2e\xb5\xc9\xbb\xb7\x91\x1e\x23\xe9\xda\x73\x6f\x8b\xc6\xf3\x74\x2b\x5a\x1b\x27\x68\x57\x41\x8e\xdf\x40\x35\xb8\xb6\x10\x8e\x74\xa3\xaa\xf5\xa6\x85\xe8\x83\xd4\x30\x5e\xc1\x4e\x70\xcd\x59\x24\x30\xe7\x24\x1d\x98\x77\xfb\x51\xc9\x1d\x74\x0c\xec\x1f\x76\xb8\x63\x59\x76\x37\x06\x2b\xfa\x81\x98\x7d\xa1\x3d\xe4\x6c\x35\x48\xa9\x5d\xc6\xc4\x3b\xe2\xf8\xeb\x6b\x39\x17\x99\xd3\x5e\x45\xd7\x4c\xb9\x72\x2a\xc5\x8c\x94\xb9\x82\x0f\xd0\x8e\xea\xe9\xc4\x02\xc4\x8a\xaf\xad\xf3\x49\xc2\xd2\x7b\xfe\xe8\xae\x76\x76\x66\xe9\x56\x06\xa8\x92\x7f\x28\x8f\xbe\xcd\x4e\x66\xf2\xb4\x45\x48\x87\xfa\x99\x3c\x9d\x8d\xe3\xd3\xd9\xe8\x68\xf6\x6e\x16\x8e\xda\xf3\xe1\x51\xb8\x4b\xb4\xc1\x0c\xdb\xa2\x00\x19\xc5\xdc\xd4\xf9\x3b\xc8\xbc\x72\x67\x1b\xb7\xb6\x78\xf0\x7a\x8e\xf1\x21\x25\x44\x8d\xf9\x9a\x42\xa2\xc1\x7a\x45\x35\xd1\x45\x7a\x55\x49\xe1\x99\x55\x1d\x92\xab\xfc\xb0\xc5\x2e\x5f\x5d\x65\xb4\x74\xd7\xb6\xaf\xff\xc7\x04\xfa\xf9\xd3\x97\x56\x1a\xbb\x2d\xf2\x72\x41\x99\x8c\x43\x7b\xd8\xa9\x64\x89\x17\xa2\x48\xe2\xcd\xbb\x38\x11\x34\x8c\x3b\x19\x53\xc6\x5e\x46\xbd\xf3\x02\x3d\xb2\xb4\xe0\x94\x29\x5d\x3b\x0c\x88\x7b\x17\x0c\x8a\x30\x1e\xcc\x61\x87\x1d\x9f\xbc\x7c\xd9\x0f\x3e\x3e\x6a\xe7\x2a\x28\x6e\xd5\xd4\x1d\xfd\x29\x5c\x17\x76\x8b\x46\x27\xeb\x95\xfe\x6a\x44\x34\xa9\x5c\xfb\x7b\x38\xe9\x6a\x65\x22\xc0\x24\x73\xa9\x52\xca\xba\xe7\x1d\x78\xff\xfe\xfd\x40\x6d\xa0\x8b\xee\x9a\x9d\x1f\xe5\x35\x1b\x93\x0e\x9a\xd9\xad\x47\x1a\xb3\xfe\x1d\x16\xba\xbd\x78\xf0\x8a\xcd\xeb\x9e\x9b\xd4\x57\x83\x7e\x65\x61\x96\x7f\xb8\x1b\xbf\xda\x95\x5f\x55\x69\x0c\x21\xbf\xf2\x36\x54\xa0\x0e\x6e\x89\xa0\x7a\x39\xfd\x04\x16\x58\x80\xb9\x05\x18\x2e\x4e\x06\xaf\x0f\x12\x95\xc7\x4f\xa4\xe5\xc8\x47\x70\x5e\x71\xeb\xb3\x31\xa9\xcb\x2f\x73\xcf\xbf\xfc\x09\xbd\x13\x59\x95\xec\x22\xe7\x67\x67\xba\x86\xa7\x8f\x67\x93\xc9\x44\x94\xac\x13\x4a\x9c\x27\x4b\x9e\xac\xaa\x87\x8d\x9f\x41\xd4\x0c\x79\xa9\xce\xe0\x6d\x90\xe2\x7d\xb9\x38\x83\x8c\xe8\xfd\x69\xe4\xec\xdd\x8e\x0c\xd4\x01\x54\x56\xaa\x6e\xe7\x9f\x4e\xcd\x74\xc0\x4d\xc9\xd6\x9d\x1a\xa4\x0f\x44\xb5\xa2\xc0\x21\xf9\x6e\x9f\x49\x6c\xa1\x39\x64\x5d\x05\x8a\x8c\x8b\x35\x7c\xbe\xf9\x22\x10\x41\xe0\x3d\x91\x18\x54\xa3\x73\xfb\xd9\x31\x26\xf3\x1e\xd4\xba\x5e\x8c\xfe\x17\xc2\x51\x29\xf2\x6d\x1a\xe9\x3d\x38\x99\x2b\xe7\x75\xa1\x9e\x9a\xfb\x6e\x10\xc5\x3a\xe2\x52\x35\x52\x0c\x69\x82\x66\x7d\x38\x88\x22\x7c\x2c\x50\x50\x7d\x8a\x24\x39\x84\x76\x36\x2a\xd9\x06\x85\x3d\x3a\x1a\xc6\x67\x36\x23\x9c\x4d\x26\x56\xd6\x28\xba\x7f\x2a\x88\x94\x51\x2a\xe8\x06\xc5\xd6\xb0\x70\x6d\xb9\xf0\x02\xbb\xd1\xa9\x5d\xa8\xc0\x35\xde\x73\x6e\x6e\x0d\xd5\x12\xab\x10\xd4\xba\xf1\xee\x16\xbf\x1a\x7c\x5f\x48\xe9\x71\xcf\x8c\x19\xc4\x70\x63\x08\xc1\x03\xcd\x73\x93\x34\xee\xb1\xa2\x88\xe9\x4f\xbf\xbc\x64\xa8\x92\xa5\x7d\x79\x21\x1b\x84\x04\x85\xa2\x19\x4d\x88\x42\x19\x98\x39\xfb\xfa\x42\x36\x38\xf7\xe7\xba\xd1\xc6\x3c\x1b\x36\x67\x20\x5d\x7c\x0b\x86\x0a\xe5\xa4\x58\x51\x3d\xe4\xe2\xfa\x7a\x95\x52\x01\x51\xa1\x1d\xcb\x21\xf9\x47\x1b\x81\xb2\xe0\x4c\xea\x2c\x97\x68\xb3\x89\x64\x0d\xa9\xb7\x70\xdc\x31\x41\x07\xbe\xcd\x0e\xed\x59\x00\x1a\x47\xb2\xeb\xfd\x9f\x9b\xcf\x9f\x20\x25\x8a\xc4\x3b\xef\x2e\xad\x48\x05\x51\x4b\xa8\x92\xa3\x19\x7e\x58\x6a\x8f\xbb\xfc\x78\x33\x05\x81\x24\x85\x48\x98\x7b\xba\xd6\xcd\xb3\x9b\xf0\x91\xab\x74\xab\x61\x1b\xb6\x4d\x41\xd4\x9c\xd4\xdd\x11\x5d\xdb\xc1\x7f\xfe\x07\x44\xe9\x78\xe0\x38\xdf\xe4\x6a\xcd\x23\xac\xef\xa1\xe1\x3d\xbc\x77\xb4\x1a\xf5\xfc\x80\xbf\xbe\x6b\x79\x8e\xe3\xdb\x3b\xf8\x01\xe1\xec\x24\xbe\x22\x6a\x39\x86\xd9\x49\x7c\x6e\x89\x8d\xc3\xe3\xf1\xc0\xc3\x93\xbd\xef\xd1\xf6\x4d\x99\x54\x24\xcf\x51\xaf\xca\xbc\xfa\x4a\x23\x61\x0a\x1a\x72\x89\x80\x6c\x43\x05\x67\xda\x29\x03\x8b\x36\xaf\x51\xe6\x0e\xa5\x63\x39\x6e\x78\x7a\xe2\x6b\x84\x29\x41\x92\x55\xa4\x38\xcf\x65\x33\x2e\x68\x67\x44\xf2\x84\xa8\x96\x26\x4d\x96\x60\xa8\x1e\xb8\x58\x51\xb6\x88\xaa\x42\xb2\x89\x26\x85\xb2\xef\xf1\x6e\xe0\xaf\xef\xf5\x9f\x0f\x0b\xb4\xc4\xac\xb6\x9f\x9f\x69\x06\xf1\xa5\xfc\xc3\xc4\x10\x88\x5e\x5e\xcc\xb0\x13\xf8\xd7\xe9\x89\x8d\x2e\x11\xb2\x05\x65\xe8\x90\x90\xa5\x35\xac\x23\x71\x29\xcb\x9c\xa4\x43\x24\xe8\x8d\x9e\xd9\x85\x7b\x2e\x28\x1f\xc2\xd4\xda\xe0\x3b\x11\x9b\x9a\x69\x08\xbd\x9e\x1d\xa2\x51\x99\xcf\xb3\x43\x30\x2f\x2b\xbb\x9e\x6d\xab\x7d\x86\x14\x0b\x64\xa9\xb6\x75\x87\xea\xbd\xe1\x56\x40\xf3\x1a\xa8\x6f\x14\x96\xf1\xa5\x05\xd4\x71\xb5\x4f\xb0\x8e\xaf\x6d\x03\x9a\x2b\xee\x18\x4c\x4f\xc6\x2e\xfd\x5b\x09\x9f\xca\xb5\x83\xab\xe3\x87\x79\xc0\xf2\x9b\x1a\xec\x0b\x16\x43\x78\x0b\x6f\xde\x78\x8b\xb7\x93\x6f\xef\x5e\x42\xf8\x65\x0a\x61\x08\x77\xc3\xa1\xe6\xab\xd4\xf2\xd6\xe7\xae\xd4\x71\x34\x6f\xcf\x67\x8d\xc3\x0f\x08\xfc\xeb\xf4\xa4\xcb\x4f\x2b\x7c\xdc\x0a\x46\x47\x76\x2d\xa2\xe8\xaf\xc5\x9c\xc4\x9e\x9f\xe3\xeb\x62\x5d\xf5\x63\x9c\x97\xa6\x6b\xa3\x7f\xcd\x53\xac\x4d\x77\xc7\x00\x70\x0d\x43\x44\xb2\xa4\x0a\x13\x55\x0a\x1d\x86\x4b\xf3\x78\x1d\xad\xc7\x2d\x22\x4e\x58\xdb\xcc\x62\x82\x3e\x5b\xa5\xe6\x19\x6e\xe2\x56\x38\x19\xf9\xa4\xc2\xde\x41\x2a\xd5\x11\xaa\x43\x6b\xf8\x34\xe5\x65\x8d\x1e\xc2\xe0\x99\xe9\x08\x3e\x9a\x40\xef\xef\x01\x64\x82\xaf\x4d\xa0\x2a\x04\xdf\xd0\x14\x53\xf8\x7a\xfd\x67\x77\x5f\x6c\x83\x8b\x9f\x7d\x2a\x9d\x85\x75\xd3\x00\xbf\x82\xe3\xa5\xc0\x6c\x1a\xce\xfe\x71\xfb\x2d\xbc\xfb\xf5\xb8\x9e\xba\x80\xe3\x59\x2c\x8a\xf5\xe8\x78\x1c\xb4\xd6\x5b\x9d\x96\x7d\x26\x03\x96\xd4\x58\xd3\x1f\xfc\x81\xe5\x9c\xa4\xda\xa6\xae\xaf\xfe\xd9\x44\x5c\xb3\x88\x5a\xa6\x16\x66\xc6\xeb\xc8\x6c\x8a\x40\xdb\x7e\xc2\xa0\xc5\xb5\x95\xa5\xba\x6b\xb7\x85\xde\x73\x45\xfd\xdf\x26\x2f\x93\x51\x97\x60\xb8\x15\xd9\x9a\x42\x6f\x83\x86\x48\xf4\x68\x34\xe5\xf6\x7e\x63\x68\x2b\xaa\xb2\xe1\xba\x39\xc7\xbe\x8c\xc5\x70\xe3\xaa\xcc\xb4\xd2\x64\xdc\x97\xbc\x57\x80\x79\xe2\xe8\x6c\x00\xd1\xd5\xa0\x89\x36\x52\x1a\xbb\x68\xae\xcb\x4d\x07\xd8\x0e\x91\xfb\x62\xf7\x94\x53\xcb\xbb\xbd\x83\xa1\xfb\x93\x2c\xd7\x3c\x85\x5f\x1f\xbb\xda\xdb\x8a\xb0\x2d\x04\xb5\xb0\xc7\xc3\x4a\xdf\xa6\xb2\xc1\x4e\x8a\x6a\x29\xb5\x1b\xf6\x56\xbb\x63\x55\x9d\x1a\xb6\x65\xe8\x34\xd8\x33\x64\x1e\xe4\xf7\xd4\xd9\x9f\x78\xc7\xb3\x4c\x63\x82\x3e\x48\xf5\xbc\xab\x29\xa0\xab\xbc\x42\xb3\x56\xbc\x30\x39\xd0\xb7\x13\x3b\x28\xd0\x9e\x79\x06\x93\x8e\xaf\x7d\xbf\x7b\x62\x57\x76\x71\xd7\xc6\x87\xa7\x96\xe9\xc9\xe8\x64\x6b\x2d\x36\x1e\xf7\x3a\x2b\x1c\x65\xdb\x5c\xb1\x45\xd4\x5e\x93\x85\xb9\x81\xd1\x59\xd6\xd5\x01\xd1\x53\x63\x8c\x87\x38\xc8\xb0\x53\xd4\x15\xe7\xcf\x37\xf3\x38\x81\xba\x16\x78\xd0\x81\xc9\xb5\x75\xf4\x4b\x18\x7d\xc0\x99\xdf\x53\x46\x04\xed\x95\xb4\x6e\xd8\x2b\x69\xc3\xea\x55\x26\x6c\x8f\xe4\xa8\x3a\x23\x89\xaa\xac\x6e\x5c\xef\x88\x21\xf7\x54\x6d\x88\xa3\xdd\xdf\x05\xfb\xe8\x9b\xf0\xf5\x5a\xdb\x62\xb4\x81\x70\x74\xa2\xab\x73\x13\x53\xc2\x91\xa5\x12\x8e\x43\x78\xd3\xec\xc6\xf6\x7e\x41\xc7\x54\xc2\x9a\x4a\x69\x5a\xba\x68\x53\x94\x51\x05\xa5\xb1\xc7\xa7\x72\xdd\x3d\xec\x0e\x58\xc2\xa4\x94\xc2\x74\xd2\xd6\x62\x40\xd0\x0b\xfe\xa3\xdf\xab\xe2\x6b\x5b\xc8\xdf\xb6\xb3\x15\xcd\xa1\xf0\xde\x8f\x20\x83\xe1\xab\x5a\xf3\x90\xba\xf6\x5b\x60\x27\xf8\xec\x6c\x08\x72\x9d\x65\xf2\x81\x14\x75\xc3\x9e\xfe\x18\x6a\xd6\xd3\x85\x4a\x29\x51\xc0\x92\x48\x90\x65\x96\xd1\x84\x9a\x3a\x58\xd0\x0d\xcd\x71\x81\xd2\x0f\x2b\x17\x5f\x2f\xff\x70\x0a\x1c\x8e\x20\x5f\x96\x54\x42\xe6\xa4\x59\x97\xd2\x5e\x20\x94\x0c\x88\x04\xc1\xb9\xf2\x76\x65\xe0\x2c\x5c\x95\x25\xae\xaf\xcf\xac\xa1\x2e\xc5\xf5\x07\xcf\x32\x88\x48\xef\xde\x4d\x4f\x69\x3b\x72\x6d\x79\xbe\xd0\xbf\xef\x8e\x79\x37\x1a\x75\x57\x3b\xdf\xce\x2b\x33\x5f\xd9\xf1\xc1\xaf\x2b\x0e\xeb\xe6\xe2\x4f\xca\xca\xc7\x66\x97\x30\xd7\xdf\xf5\x46\xfd\x0b\xd4\x5e\xf5\x5b\x74\x04\xb2\x7d\x3d\x0a\x59\xc6\x45\x82\xf0\xd6\xdb\x9c\x0a\x64\xab\xca\xcc\x62\x2f\x0e\xe8\x31\xd4\x91\xa6\xd3\xb0\x6e\x9b\xb4\xcf\xb5\xc3\x69\xc3\xcc\x76\xb4\xa6\xf6\x5a\x51\x83\xc0\x9c\x49\xb9\xd0\xc7\xd2\xbf\x23\x43\x41\xf2\xcf\x37\xe6\xb0\x79\xc8\xe1\x70\x5f\xf8\x0d\xfc\x13\xaf\xe0\xf9\x55\x4e\x18\x1a\xea\x3b\x2e\xb0\x82\xe6\xc0\xdb\x3b\x69\x0f\x3e\xfe\x7b\x08\xbd\x36\xec\xce\x1b\x7f\xd0\x7f\x8b\x90\xa8\xa2\x15\x0a\x86\x79\x54\x10\x41\x6a\xc8\x86\x68\x70\x04\x37\x06\x94\xa1\xde\x33\x1d\x7d\xeb\xfe\xf7\x2d\x6d\xd0\x7a\x9b\xaa\xdb\xf7\x03\xb6\xa7\x17\x74\xf6\x62\x6c\xf1\x81\xa0\xaf\xb1\xe1\x8e\xc6\x6d\x4d\x4e\xfe\xaa\xfb\xb7\x15\x6d\x72\xbd\x47\xf5\xc1\xc7\x8f\xdd\xcf\x9d\x6d\x2d\x5f\x0d\xdd\x7f\x9b\x4c\xcb\x50\x2a\xde\x98\xd3\x27\x94\xaa\x32\xd3\xf6\xed\xb8\xb9\x58\xeb\xdc\x6f\x87\x3e\x93\xd6\x1d\xb8\xbd\x8a\x8e\xec\xff\x12\x71\x3b\xe7\x2e\x94\xc3\xff\x0b\x00\x00\xff\xff\xe8\x62\xa8\x36\x64\x32\x00\x00"), }, - "/ignition/controlplane/files/etc/sysctl.d": &vfsgen۰DirInfo{ + "/bootconfig/files/etc/sysctl.d": &vfsgen۰DirInfo{ name: "sysctl.d", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/ignition/controlplane/files/etc/sysctl.d/kubernetes.conf": &vfsgen۰CompressedFileInfo{ + "/bootconfig/files/etc/sysctl.d/kubernetes.conf": &vfsgen۰CompressedFileInfo{ name: "kubernetes.conf", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 97, + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 96, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xca\x4b\x2d\xd1\x4b\x2a\xca\x4c\x49\x4f\x85\x52\xba\x79\x69\xba\xc9\x89\x39\x39\xba\x99\x05\x25\x89\x49\x39\xa9\xc5\xb6\x86\x5c\xf8\x14\x99\xa1\xa8\xca\x2c\x28\x33\xd1\xcb\x2c\x88\x4f\xcb\x2f\x2a\x4f\x2c\x4a\xb1\x35\xe4\x02\x04\x00\x00\xff\xff\x6d\xd4\xf2\x72\x61\x00\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xca\x4b\x2d\xd1\x4b\x2a\xca\x4c\x49\x4f\x85\x52\xba\x79\x69\xba\xc9\x89\x39\x39\xba\x99\x05\x25\x89\x49\x39\xa9\xc5\xb6\x86\x5c\xf8\x14\x99\xa1\xa8\xca\x2c\x28\x33\xd1\xcb\x2c\x88\x4f\xcb\x2f\x2a\x4f\x2c\x4a\xb1\x35\x04\x04\x00\x00\xff\xff\xea\x98\xd9\xfa\x60\x00\x00\x00"), }, - "/ignition/controlplane/files/etc/systemd": &vfsgen۰DirInfo{ + "/bootconfig/files/etc/systemd": &vfsgen۰DirInfo{ name: "systemd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/ignition/controlplane/files/etc/systemd/system": &vfsgen۰DirInfo{ + "/bootconfig/files/etc/systemd/system": &vfsgen۰DirInfo{ name: "system", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/ignition/controlplane/files/etc/systemd/system/kubelet.service.d": &vfsgen۰DirInfo{ + "/bootconfig/files/etc/systemd/system/kubelet.service.d": &vfsgen۰DirInfo{ name: "kubelet.service.d", - modTime: time.Date(2024, 3, 18, 8, 5, 54, 843936879, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/ignition/controlplane/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template": &vfsgen۰CompressedFileInfo{ + "/bootconfig/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template": &vfsgen۰CompressedFileInfo{ name: "10-kubeadm.conf.template", - modTime: time.Date(2024, 3, 18, 8, 28, 8, 557474880, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), uncompressedSize: 1086, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x53\xd1\x4e\xdb\x50\x0c\x7d\xef\x57\x58\x01\x89\x87\x91\x54\xbc\x4e\xca\x43\xb7\x15\x34\xb1\x01\x2a\xa0\x4d\x9a\x26\xe4\x24\x4e\x6a\xb8\xbd\x2e\xbe\x4e\xa0\x42\xfc\xfb\x74\x13\x5a\x8a\x56\x78\x8a\xe3\xf8\x9e\x73\xae\xcf\xc9\x1e\x9c\x89\xd1\x67\xb8\x9a\x73\x80\x4a\x65\xc9\x1e\xc4\xbb\x15\x3c\x88\xde\x05\x78\x60\x9b\xc3\x5d\x5b\x10\x56\x0b\x40\x5f\xf5\xb5\x23\x83\xee\x28\x3b\x3a\xfa\x34\xfa\x73\x49\xda\x71\x49\x7f\x47\x53\xdf\xb1\x8a\x5f\x90\xb7\x3c\x39\xbd\xfe\x32\xfd\x31\xbd\xba\x89\xcf\xaf\xe7\x67\xc7\xdf\x4f\x6e\x26\xb3\x93\xcb\x3c\x4d\x0b\x11\x0b\xa6\xb8\x4c\x23\x52\x29\xbe\xe6\x26\x1f\x93\x95\xe3\xf8\xae\x9e\x8c\xc2\xf8\xed\x90\x23\xcb\xe2\x20\xa4\x1f\x9d\xd9\x9e\x4c\x76\xab\x79\xab\x64\x8d\xd3\xa1\x8e\x1d\x17\x6b\x80\xf1\xd0\xcf\x56\xb8\x70\xc9\xe8\xe9\x09\xb8\x06\xba\x87\x6c\xd6\x7a\xe3\x05\x41\x52\x2a\x4b\x02\xcf\xcf\xef\x70\x9c\xcc\xce\xaf\x2f\x7e\xfd\xdc\xb0\x34\x2a\xed\x32\xa4\x4b\xd2\xf4\x5e\x42\x5e\xa3\x0b\x04\x69\x4a\xbe\x16\x2d\x29\xf5\x52\x51\x8a\xce\x49\x89\x86\x85\xa3\xfc\xe0\xa0\x67\x25\x5f\x45\x8e\xbd\xc1\x18\x0e\x80\x50\xb3\x23\xb0\x39\x1a\x24\x6b\x47\xd8\xb3\x25\xbd\x2f\x9b\xd6\xad\xb0\x4f\xa0\x21\x4f\x8a\x46\x01\xd0\x40\x07\xe9\x87\xb0\x94\x65\xeb\xd0\xd8\x37\x60\x73\x82\x6d\x97\x26\xdf\x06\xc9\xd0\xa1\x72\x14\x02\xd5\xca\xe3\x82\x4b\x74\x6e\xb5\x7d\xd5\x63\x76\x94\xa7\xff\x6d\xed\x85\x3e\xad\x1d\x36\x21\x23\xdf\xbd\x23\x3d\xf2\xb6\x81\x14\x4a\xf4\xb1\x80\x5a\x14\xa4\x23\x55\xae\x28\x80\xd4\xfd\xc4\x3a\x65\xa8\x4d\x00\x8c\x08\x0e\x83\x81\x52\x10\xb5\x0c\x2e\x94\x6a\x52\x2c\xdc\xea\xf0\x15\x30\xcc\xa5\x75\x55\xac\x47\x7b\x7d\x37\x3b\x93\x8a\x66\xd4\x70\x8c\x92\xb1\xf8\xec\x74\x80\x9d\x3e\x9a\xe2\x24\x42\x4b\x71\x4b\xa5\x01\xfb\xfe\xc0\xe0\x7c\x3b\x0c\xf7\x9a\x03\xb0\x0f\x46\x58\x65\x9b\x65\x4d\x7f\x5f\xcd\x26\xc3\xaa\x5e\x18\x0b\x82\x20\xad\x96\x54\x41\xad\xb2\x00\x8b\xd7\x8e\xa7\xb3\x1d\x7b\x8b\xa9\x0d\xab\x30\x50\xad\xb7\x37\x9a\x3e\x52\x79\x69\xa8\x96\x6f\x95\xe3\x36\xe8\xb8\x60\xbf\x9e\x82\xfd\x77\x7e\xab\xd7\x0f\x3b\x9b\x6f\xec\xdd\xdf\x71\x8f\x0f\x42\xbe\xbf\x33\xd7\x9b\x7c\xfe\x0b\x00\x00\xff\xff\x21\x0d\x51\xd3\x3e\x04\x00\x00"), }, - "/ignition/controlplane/systemd": &vfsgen۰DirInfo{ + "/bootconfig/systemd": &vfsgen۰DirInfo{ name: "systemd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/ignition/controlplane/systemd/init-cluster.service": &vfsgen۰CompressedFileInfo{ + "/bootconfig/systemd/init-cluster.service": &vfsgen۰CompressedFileInfo{ name: "init-cluster.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 510, + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 515, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x90\x3d\x6f\xe3\x30\x0c\x86\x77\xfd\x0a\x26\x43\x36\xd9\x77\xc3\x4d\x07\x0f\xf7\x91\xe1\xb6\x20\xc1\xa1\x83\xe1\x81\x91\x69\x9b\x88\x2c\xb9\x22\x95\x26\xff\xbe\xb0\x9b\xa4\x40\x87\x0e\x1d\x45\x82\xcf\xfb\xe8\xad\xff\x07\xd6\xc6\xfc\x25\x71\x89\x27\xe5\x18\x2a\x0e\xac\x70\xca\x47\x4a\x81\x94\x04\x9c\xcf\xa2\x94\xcc\x9e\x9e\x33\x27\x92\x4a\x48\xed\x69\xde\x7a\x3b\x61\xc2\x42\x28\x9d\xd9\x11\x24\xf2\x84\x42\x96\x47\xec\xc9\x4e\x7c\x8e\x7a\xdf\x99\x5f\x9d\x52\xfa\xd2\xe5\x9f\x18\x5a\x9e\xc5\x76\xa8\xc3\xf6\xc2\xa2\x52\xad\xca\x33\xa6\xd2\xc7\xbe\x9c\x65\xed\xcd\xb0\x10\xc5\x71\x32\xa6\x3e\xbc\x9d\x36\x66\x7b\x21\x77\x50\x4c\xba\x4b\x54\x95\x47\x0e\xe5\x11\x65\x00\xeb\x60\xfd\x32\xb0\x27\xa8\x61\x05\xb6\x83\x07\x2e\xc4\xf6\x91\x3f\xc3\xa0\xf9\x09\x6d\x04\xf1\x44\x13\x7c\xff\x36\x3f\x02\xad\xdf\xb9\x1f\xa0\x73\x6b\xd8\x8e\xb0\x54\x68\xad\x8b\xa1\xe3\xbe\x2a\x49\x5d\x19\x4e\xed\x4d\x76\x19\x16\x57\x1c\x3d\x58\x9b\x27\x1f\xb1\xb5\x8e\x92\x0a\x6c\x36\xb0\xf0\x34\x66\x37\xc0\x27\x7f\x5c\x9b\x3d\xc9\x92\x1f\x83\xed\x90\x7d\x4e\x74\x1f\x1d\xc8\x55\x3f\xc4\x98\xfa\x5f\x10\x45\xef\x1b\xf3\x84\x41\xa9\xfd\x7d\xad\xc6\xec\x95\x6d\x16\x4a\x85\x62\xea\x49\x5f\x03\x00\x00\xff\xff\x6d\x6f\x26\x69\xfe\x01\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x90\xbd\x6e\xe3\x30\x10\x84\x7b\x3e\xc5\xda\x85\x3b\x4a\x77\xc5\x55\x07\x15\xf7\xe3\xe2\x3a\xc3\xc6\x21\x85\xa0\x62\x4d\xad\xa4\x85\x29\x52\xe1\x2e\x1d\xfb\xed\x03\x29\xb6\x03\xa4\x48\x91\x92\x1c\xcc\xc7\x8f\x53\xff\x0f\xac\x8d\xf9\x4b\xe2\x12\x4f\xca\x31\x54\x1c\x58\xe1\x94\x8f\x94\x02\x29\x09\x38\x9f\x45\x29\x99\x3d\x3d\x67\x4e\x24\x95\x90\xda\xd3\x9c\x7a\x3b\x61\xc2\x42\x28\x9d\xd9\x11\x24\xf2\x84\x42\x96\x47\xec\xc9\x4e\x7c\x8e\x7a\xcf\xcc\xaf\x4e\x29\x7d\xa9\xf9\x27\x86\x96\x67\xb1\x1d\xea\xb0\xbd\xb0\xa8\x54\xab\xf2\x8c\xa9\xf4\xb1\x2f\x67\x59\x7b\x33\x2c\x44\x71\x9c\x8c\xa9\x0f\x6f\xd5\xc6\x6c\x2f\xe4\x0e\x8a\x49\x77\x89\xaa\xf2\xc8\xa1\x3c\xa2\x0c\x60\x1d\xac\x5f\x06\xf6\x04\x35\xac\xc0\x76\xf0\xc0\x85\xd8\x3e\xde\x9f\x61\xd0\xfc\x84\x36\x82\x78\xa2\x09\xbe\x7f\x9b\x0f\x81\xd6\xef\xdc\x0f\xd0\x79\x35\x6c\x47\x58\x26\xb4\xd6\xc5\xd0\x71\x5f\x95\xa4\xae\x0c\xa7\xb6\x63\x4f\x72\x33\x5e\x92\xe2\x8a\xa3\x07\x6b\xf3\xe4\x23\xb6\xd6\x51\x52\x81\xcd\x06\x16\xa8\xc6\xec\x06\xf8\xe4\xa3\x6b\xb3\x27\x59\x24\x62\xb0\x1d\xb2\xcf\x89\xee\x57\x07\x72\xd5\x0f\x31\xa6\xfe\x17\x44\xd1\xfb\xc6\x3c\x61\x50\x6a\x7f\x5f\xab\x31\x7b\x65\x9b\x85\x52\xa1\x98\x7a\xd2\xd7\x00\x00\x00\xff\xff\x5b\x74\x1f\x12\x03\x02\x00\x00"), }, - "/ignition/controlplane/systemd/kubelet.service": &vfsgen۰CompressedFileInfo{ - name: "kubelet.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 325, + "/bootconfig/systemd/join-master.service.template": &vfsgen۰CompressedFileInfo{ + name: "join-master.service.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 625, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\x31\x6e\xf3\x30\x0c\x46\x77\x9e\xc2\x17\xb0\x95\x7f\x0d\xa0\x21\x7f\x93\x21\x68\x51\x14\x75\x8b\x0e\x86\x07\x59\x66\x6d\xc2\x32\x65\x88\x94\x93\xdc\xbe\xa8\xd1\x74\xeb\xfc\x1e\x89\xef\x35\xef\x4c\xda\xc2\x11\xc5\x27\x5a\x94\x22\xdb\x29\x77\x18\x50\xf7\xc5\xdb\x88\xc5\x63\xee\x30\x31\x2a\x4a\xf1\x1c\x7b\x2c\x0e\x03\xb2\xc2\x31\xfa\x3c\x23\xab\xdb\x0e\x46\xd5\x45\xf6\xc6\x4c\xbf\x6e\x45\xd1\xf4\xd1\x8b\x81\x0f\xc7\x2a\x96\x51\x2f\x31\x4d\x65\xe4\x40\x8c\x95\xba\x34\xa0\xc2\xe1\x53\x31\xfd\xc1\x1e\x22\xf7\xf4\xfd\xfd\xc5\xe9\x78\xba\x92\xa8\x58\xb3\xba\x64\x42\x1c\x0c\xc7\x1e\xcb\x85\xd6\xa8\x95\xa8\x9b\x17\x80\xa6\xc6\xb4\x92\xc7\x16\x4e\x57\xf4\xb5\xba\xa4\xd6\x64\x49\xa6\x23\x36\x3f\x41\xf0\x8a\xb2\x01\x17\x2e\xee\x26\xb0\x59\x4f\x34\x93\x9e\x59\x31\xad\x2e\xd8\xdd\xdd\xa9\xd1\xdb\x7f\x3b\x80\xe6\xcc\xa2\x2e\x84\x76\x0b\xc1\xfe\xff\xcd\xce\x39\x28\x95\x59\x30\xdd\xb7\xc2\x57\x00\x00\x00\xff\xff\xcb\xcf\xb3\x78\x45\x01\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x50\xb1\x8e\x13\x31\x10\xed\xfd\x15\x73\x29\xae\x73\x02\x05\x15\xda\xe2\x08\x27\x71\x82\x22\x4a\x38\x51\x44\x29\x26\xde\x97\xac\x59\xaf\xbd\x8c\x67\xc3\x45\x51\xfe\x1d\x79\x57\xe1\x24\x24\x9a\xeb\xfc\xde\x9b\xf7\xfc\x66\xb6\xcf\xd1\xeb\xce\x7c\x46\x76\xe2\x7b\xf5\x29\x56\x1d\x67\x85\x50\x4c\x35\xe8\x67\xf2\x91\xb4\x01\xb9\x30\x14\xd6\xac\xf1\x6b\xf0\x82\x5c\x65\xa8\x6d\x21\x11\xc1\xf6\x2c\x3c\xcf\x90\x93\x77\x20\x41\x00\x67\x58\xdf\xf1\x11\xb6\xf7\xa7\xa4\x37\xcd\x3c\x1c\x14\xf2\x26\xe7\x32\xc5\xda\x97\x76\x2b\xd6\xe6\xf1\xc5\x67\xcd\xd5\xdd\xe2\xc4\xb2\x08\xe9\xb8\x28\x2d\xed\x54\x7b\x9e\x95\xbb\xde\x98\xed\x66\x72\xee\xcc\xe3\x0b\xdc\x46\x59\x74\x25\xa8\x16\x7b\x1f\x17\x7b\xce\x0d\x59\x47\xb3\xdf\x8d\x0f\xa0\x2d\xdd\x91\x3d\xd0\xdf\xb4\xb2\xf8\xed\xfb\x12\x46\xbb\x8f\x54\x27\xca\x01\xe8\xe9\xfd\xbb\x02\x22\x66\xaf\xb9\xff\x84\xb6\xc3\x1e\x5c\x77\xd3\xe9\x2e\x97\xf9\xc3\xea\xa9\x74\x81\x3c\xaf\xbf\x5d\xaf\x64\xad\xa6\x16\xa3\xf2\xbd\x3c\x46\xaa\xf6\xd9\xa5\x13\xe4\x3c\x89\xd6\xb1\x75\x10\xb5\x4d\x09\xbd\x5c\xe6\x4b\x5e\x42\xf4\x0b\xe7\x66\x1c\x77\x29\xaa\xa4\x60\xfb\xc0\x11\x05\x43\xd4\x1f\xbc\x63\x85\x6d\x71\x1e\x1d\xaf\xd4\x57\x9c\x27\x97\x78\x9b\x93\x6b\xa1\x55\x19\x10\xbf\x19\xc1\xf5\x4a\x74\x7f\x4f\x9a\x06\xd7\xd0\xff\x4f\x3a\x33\x6b\xe4\x71\xdd\x14\xed\x81\x7d\x18\x04\x37\x6a\x03\x57\x7d\xc8\xc6\x6c\x9f\x62\x56\x0e\x61\x67\x7e\x70\x54\xd4\x9f\xce\x55\x37\x04\xf5\x76\xc8\x90\xb9\xb2\x1c\xa1\x7f\x02\x00\x00\xff\xff\xba\x44\x08\x20\x71\x02\x00\x00"), }, - "/ignition/controlplane/systemd/release-image-pivot.service": &vfsgen۰CompressedFileInfo{ - name: "release-image-pivot.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 324, + "/bootconfig/systemd/join-worker.service.template": &vfsgen۰CompressedFileInfo{ + name: "join-worker.service.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 571, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\xbd\x6e\xc3\x30\x0c\x06\x77\x3d\x05\x9b\x21\x9b\xa3\xa9\xa3\x86\xfe\x64\xe8\x16\xd4\x28\x3a\x18\x1e\x18\x99\xb1\x09\xcb\x94\x21\xd2\x6e\xfa\xf6\x85\x03\x64\xe8\x90\x95\xc4\x77\x87\x6b\xbe\x84\xad\x75\xef\xa4\xb1\xf0\x6c\x9c\x25\x9c\x78\xcd\x06\x92\x3b\x02\xcb\x60\x03\x81\x8c\x1d\x14\x4a\x84\x4a\xc0\x13\xf6\xe4\xbe\x51\x4c\x83\x90\xfd\xe4\x32\x56\x59\x12\x0b\x1d\x0c\x4b\x4f\xe6\x5e\x2e\x46\xe5\xc1\xef\x2d\x4b\xc7\x9b\xe5\x84\x36\x1c\xaf\xac\xa6\xe1\xc9\xaf\x58\x7c\xca\xbd\xdf\x9c\xd5\xbc\xe9\x0f\x6a\x38\xcd\xce\x35\x35\x95\x95\x23\xb5\xee\x78\xa5\x58\x1b\x16\x0b\xfe\xcc\xe2\xcf\xa8\x03\x54\x11\x76\x9e\x2c\x7a\x19\xbb\x7f\xe3\x01\xf6\x7b\xb0\xbc\xc4\x01\x1e\xc2\x77\xce\x7d\x92\xde\x88\x59\xaa\x0b\x72\x5a\x0a\xdd\x4f\x35\xc5\xf0\xac\xce\x35\x1f\xa2\x86\x29\xb5\xb7\x62\xea\x5e\x7f\xc3\xb4\x24\xe3\x6a\x51\x2a\xf7\xa8\xbf\x00\x00\x00\xff\xff\xea\x51\xb4\x1e\x44\x01\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x90\xb1\x6e\xdb\x30\x10\x86\x77\x3e\xc5\xc5\x43\x36\x4a\xed\xd0\xa9\xd0\x90\xba\x01\x1a\xa0\x83\x61\x37\xe8\x60\x78\xa0\xa9\xdf\x16\x2b\x9a\x54\xef\x4e\x4e\x02\xc3\xef\x5e\x50\x82\x5b\xa0\x40\x97\x6e\xbc\xfb\x79\x1f\x3f\xde\xf6\x39\x05\xdd\x99\xcf\x10\xcf\x61\xd0\x90\x53\xf3\x92\xb9\x07\x53\xca\x2d\xe8\x47\x0e\x89\xb4\x03\xf9\x38\x8a\x82\xcd\x1a\x3f\xc7\xc0\x90\x46\xa0\xb6\x07\x27\x44\x3b\x38\x76\x95\x80\xcf\xc1\x83\x18\x11\x4e\x60\xc3\xc9\x1d\x61\x87\x70\xce\x7a\xcb\xcc\xc3\x41\xc1\xff\x35\xb9\xcc\xa9\x0d\xc5\x6e\xe5\xb4\x7b\x7c\x0d\xa2\xd2\xdc\xd5\x67\xc7\x75\xcc\xc7\xba\x58\xda\x59\xbb\x12\x75\xa7\xc1\x98\xed\x66\x9e\xdc\x99\xc7\x57\xf8\x8d\x3a\xd6\x15\xa3\xa9\xf7\x21\xd5\x7b\x27\x1d\x59\x4f\x8b\x97\x2e\x44\xd0\x96\xee\xc8\x1e\xe8\x37\xad\x7c\xfc\xf6\x7c\x81\xd1\xee\x23\xb5\x99\x24\x02\x03\xbd\x7f\x57\x8a\x84\xc5\x1f\xee\x5f\xd0\x7e\xdc\xc3\xb5\xa7\x79\x75\x97\x4b\xf5\xb0\x7a\x2a\x2e\xe0\xe7\xf5\xd7\xeb\x95\xac\xd5\xdc\x63\x4a\xbe\x95\xc3\xd4\x6a\x83\xf8\x7c\x06\xbf\xcd\xa1\xf5\xce\x7a\xb0\xda\xae\x40\x2f\x97\x6a\xe9\x96\x60\xfd\xe2\xa4\x9b\xae\x7b\x0e\x56\xb2\xef\xa1\x4d\x09\x39\x6c\xa6\xe2\x7a\x25\xba\xbf\x27\xcd\xa3\xef\xe8\xdf\xcb\x59\x98\x35\x64\x12\xcf\xc9\x1e\x5c\x88\x23\xe3\xd6\xda\xc0\x37\x1f\xc4\x98\xed\x53\x12\x75\x31\xee\xcc\x77\x97\x14\xed\xa7\xb7\xe6\x34\x46\x0d\x76\x14\x70\xa5\x8e\x8f\xd0\x5f\x01\x00\x00\xff\xff\xc1\x5b\x96\x23\x3b\x02\x00\x00"), }, - "/ignition/controlplane/systemd/set-kernel-para.service": &vfsgen۰CompressedFileInfo{ - name: "set-kernel-para.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 571829823, time.UTC), - uncompressedSize: 286, + "/bootconfig/systemd/kubelet.service": &vfsgen۰CompressedFileInfo{ + name: "kubelet.service", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 323, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x8e\x4d\x4e\xc3\x30\x10\x85\xf7\x73\x0a\x5f\x20\xc9\x09\xbc\x00\xd1\x05\x62\xd7\x82\x58\x44\x11\x72\x9c\x97\x32\xaa\x63\x9b\x99\x49\xd5\xdc\x1e\x41\x24\xc4\xaa\xbb\xf7\x2b\x7d\xfd\x5b\x66\x1b\xe8\x09\x1a\x85\xab\x71\xc9\x5e\x61\xee\x02\xc9\x48\xae\x06\x09\x6e\x2e\xe2\x5e\xd6\xf1\x27\x31\x28\x1d\xf1\xb5\xb2\x40\xbd\x20\x21\x28\x1a\x5e\xc2\x19\x4d\xe5\x6b\xb1\x56\x21\x57\x8e\xa0\x87\xd9\x20\x77\x17\xd4\x9f\x76\x35\xd0\xeb\x56\xe1\x4b\x86\x7e\x16\xa3\x23\x96\xc0\xf9\xf7\x7f\xb8\xb1\xf9\x0d\x4a\x87\x1b\xe2\xc9\x82\x98\x5f\xca\x54\xa5\x8c\x70\xa3\x7c\x64\xd8\xcc\xc9\x20\xff\x7a\xdd\x34\x5a\x72\x4d\x75\x1d\x2c\x76\xbb\x6d\xa7\xee\xf2\xc7\xdf\xc6\x92\x67\xa2\xfe\x39\xab\x85\x94\x06\x7a\x0f\xd9\x30\x3d\x6e\x7e\x59\x93\x71\xb3\x2a\xa4\xb5\x20\x67\x18\xd1\x77\x00\x00\x00\xff\xff\x73\x48\x6e\xcb\x1e\x01\x00\x00"), - }, - "/ignition/master": &vfsgen۰DirInfo{ - name: "master", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\xc1\x4e\xeb\x30\x10\x45\xf7\xfe\x8a\xfc\x40\xeb\xbe\x6d\x25\x2f\xfa\x68\x17\x15\x08\x21\x0a\x62\x51\x65\xe1\x38\x43\x32\x8a\x33\x8e\x3c\xd7\x69\xfb\xf7\x88\xa8\xb0\x63\x7d\xce\x8c\xee\x39\xbf\x0b\xa3\x36\x7b\xd2\x90\x79\x02\x27\x71\x43\x69\x28\x12\xb6\xd5\x5b\x4f\xd5\x63\x69\x28\x0b\x81\xb4\x7a\x4e\x2d\x55\xbb\x8e\x04\x66\x9f\x42\x19\x49\xe0\x97\x83\x1e\x98\x74\x6b\xed\xf0\xeb\xae\x39\xd9\x36\x05\xb5\xe6\xc3\x0b\xd4\x09\xe1\x92\xf2\xb0\x4a\x12\x59\x68\x0d\x9f\x3b\x82\xd9\x7d\x82\xf2\x1f\xec\x21\x49\xcb\xdf\xdf\x5f\x3c\xfa\xc3\x95\x15\xea\xec\xec\xb3\x8d\xa9\xb3\x92\x5a\x5a\x4d\x3c\x27\xac\x15\x7e\x9c\x8c\x39\x9f\x28\xcf\x1c\xa8\x36\x87\x2b\x85\x13\x7c\x86\xb3\x45\xb3\x6d\x58\xec\x3d\xc8\xbc\x92\x2e\xc0\xc7\x8b\xbf\xa9\x59\xac\x27\x1e\x19\x47\x01\xe5\xd9\x47\xb7\xf9\x71\x4e\x14\xdc\xbf\x8d\x31\xe7\xa3\x28\x7c\x8c\xf5\x12\x42\xed\xff\x9b\x1b\x4b\x04\xaf\x8a\x52\xbe\x6f\xfd\x0a\x00\x00\xff\xff\x99\x17\x34\x68\x43\x01\x00\x00"), }, - "/ignition/master/files": &vfsgen۰DirInfo{ - name: "files", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - }, - "/ignition/master/files/etc": &vfsgen۰DirInfo{ - name: "etc", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - }, - "/ignition/master/files/etc/hosts.template": &vfsgen۰CompressedFileInfo{ - name: "hosts.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 167, + "/bootconfig/systemd/release-image-pivot.service": &vfsgen۰CompressedFileInfo{ + name: "release-image-pivot.service", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 328, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x32\x34\x32\xd7\x33\xd0\x33\xd0\x33\x54\x50\x50\xc8\xc9\x4f\x4e\xcc\xc9\xc8\x2f\x2e\x41\xb0\xf4\xc0\xac\x94\xfc\xdc\xc4\xcc\x3c\x84\xa8\x09\x12\x13\x59\x85\x09\x97\x95\x15\xc8\x20\x08\x20\xda\x38\x33\x24\x26\xb2\x0a\x33\xae\xea\x6a\x3d\x8f\xe2\xcc\x82\xda\x5a\x40\x00\x00\x00\xff\xff\x0b\x57\x23\x96\xa7\x00\x00\x00"), - }, - "/ignition/master/files/etc/isulad": &vfsgen۰DirInfo{ - name: "isulad", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\xcf\xaf\x6e\xc3\x40\x0c\xc7\x71\x7e\x4f\xe1\x15\x94\xa5\x87\x06\x0f\xec\x4f\xc1\x58\xb5\x6a\x1a\x88\x02\xdc\x8b\x93\x58\xb9\xf8\xa2\xb3\x93\x75\x6f\x3f\xa5\xda\xc0\x40\xa9\x2d\xfd\x3e\xfa\xd6\x1f\xc2\xd6\xb8\x57\xd2\x58\x78\x36\xce\x12\x4e\xbc\x66\x03\xc9\x2d\x81\x65\xb0\x81\x40\xc6\x16\x0a\x25\x42\x25\xe0\x09\x7b\x72\x9f\x28\xa6\x41\xc8\xbe\x72\x19\xab\x2c\x89\x85\x0e\x86\xa5\x27\x73\x4f\x9d\x51\xb9\xf3\x7b\xc9\xd2\xf2\xa6\x9c\xd0\x86\xe3\x95\xd5\x34\x3c\xf8\x15\x8b\x4f\xb9\xf7\x9b\x59\xcd\x1b\x7f\x50\xc3\x69\x76\xae\x3e\x53\x59\x39\x52\xe3\x8e\x57\x8a\x67\xc3\x62\xc1\x5f\x58\xfc\x05\x75\x80\x2a\xc2\xce\x93\x45\x2f\x63\xdb\x71\x22\xfd\xb7\x30\xc0\x7e\x0f\x96\x97\x38\xc0\x5d\x61\xe7\xdc\x3b\xe9\x6d\x36\x4b\xd5\x21\xa7\xa5\xd0\xdf\xe9\x4c\x31\x3c\xaa\x73\xf5\x9b\xa8\x61\x4a\xcd\x2d\x9b\xda\xe7\xef\x30\x2d\xc9\xb8\x5a\x94\xca\x6f\xd9\x4f\x00\x00\x00\xff\xff\xff\x91\xbf\xed\x48\x01\x00\x00"), }, - "/ignition/master/files/etc/isulad/daemon.json.template": &vfsgen۰CompressedFileInfo{ - name: "daemon.json.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 1174, + "/bootconfig/systemd/set-kernel-para.service": &vfsgen۰CompressedFileInfo{ + name: "set-kernel-para.service", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 284, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x54\xcb\xae\xd3\x3c\x10\xde\x9f\xa7\x88\xbc\xfe\x9d\xf4\x2f\x12\x8b\x4a\x67\x83\xc4\x02\xb1\x00\x75\x8b\xd0\x91\x6b\x4f\xd2\x21\x8e\x6d\x8d\xed\xd0\x50\xe5\xdd\x91\x5d\x27\x6d\x03\x62\x57\xcd\x77\xe9\x37\x97\xf8\xfa\x52\x55\x55\xc5\xe0\x02\x92\x5b\x17\x3c\x3b\x54\xdf\x98\x11\x01\x47\xa8\x65\x47\x36\x3a\x45\x38\x02\xbd\xfa\xc9\x07\x18\x14\xfb\xfe\xdf\x4d\x91\x31\x76\xa8\x18\xfa\xa8\x05\x2b\x55\x05\xad\x88\x3a\x70\x8a\x26\xe0\x00\x09\xd7\x92\xd8\xaa\x11\xee\x9c\x6a\xcd\x28\xa8\xd1\x78\x6a\xb2\x58\x2d\xb8\x0f\x22\xc0\x8a\x53\x34\x1b\x1c\x4c\x87\x66\x6b\xaa\x6d\xc7\x35\x8c\xa0\x53\xfd\xe3\xf1\xf8\xe5\xb8\x20\x0e\x55\x8b\xfa\x6f\x86\xb5\x43\xf5\xa8\x2f\x9d\xdf\x86\xb1\x56\x93\x98\x0f\x56\x65\x87\xdd\xfb\xdd\xae\x48\x56\x82\x13\xe1\x1f\xed\x64\xda\x20\x2e\x7c\x09\xf1\xff\x16\xf0\xf8\x2b\x03\xef\x76\x9f\x3f\xb0\x0c\xcd\x0f\xa1\x6e\x83\x4f\xb8\x0f\xca\xc6\xb0\x04\x96\xd6\x04\x81\x06\x88\x6b\xdb\x3d\xa7\xbe\x4b\x7e\x78\x6b\x6e\x7f\xfc\xe4\x7b\xb6\xb6\xe7\xde\x81\xcc\xb1\x21\xc8\xa6\x6c\xac\x44\x6f\x12\xc1\x2f\xc5\x3a\xb9\x3c\x2c\x87\x02\x4f\x5b\x4d\x59\x0e\x15\xdb\x0f\x77\xc8\x92\xe8\xe0\x21\xb1\x1d\x81\xb4\x98\xf6\x5b\xc6\x72\x62\xf7\xc8\x0b\xb3\x4e\x3f\x08\x15\xbc\xf5\x40\x06\xf4\x9b\x3c\x83\xec\x5f\x03\xc5\xd2\xc2\x72\x78\x04\x1d\xfa\x40\x13\x1f\x90\xc8\xd2\xc6\x4e\x59\xd9\x03\xd5\x68\x9f\x45\x68\x3c\xc8\x48\xc0\x8b\x1a\x61\xa3\xbb\x5e\xeb\x4f\x83\xe8\xe0\x58\xdc\xe7\xf9\xd9\xc0\x59\xc5\xbd\x30\xea\x64\x2f\x1c\x13\x31\x75\xf9\xa7\xa8\xb9\x5e\xeb\xaf\x22\x7a\xc8\xf5\x79\x5e\xfa\x2f\x5f\x54\x1c\x84\xef\xf3\x46\x73\x9a\x15\x85\xf0\xd3\x52\xcf\x9d\x8e\x1d\x9a\x84\x4b\x83\xeb\xba\x0d\xf2\x13\x1a\xae\x30\x4f\xb6\xb1\x2e\x34\xd2\x60\x73\x42\xf3\x48\x91\xd6\xb4\x2b\x27\x2d\x36\x71\x0c\x84\x7a\x3d\xf4\x1c\x9b\x6b\x31\x01\xf1\x3c\x5c\x76\xa8\x5a\xa1\x3d\x14\x3c\x7a\xe0\x0a\x24\x4d\x2e\x80\xe2\x3d\x4c\xec\x50\xa5\xf1\x6f\x47\xe8\x7b\x74\x7c\x04\xc2\x76\xe2\x60\x5a\x4b\x12\x36\x4e\x92\x70\x79\x01\x36\x9f\x55\x2f\x82\xc8\x8f\x86\xad\xd7\x33\x56\x75\xaa\xd6\xe3\xbe\x9c\xea\xcb\xfc\xf2\x3b\x00\x00\xff\xff\x2e\x9c\xa7\x42\x96\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x8e\x4d\x4e\xc3\x30\x10\x85\xf7\x73\x0a\x5f\x20\xc9\x09\xbc\x00\xd1\x05\x62\xd7\x82\x58\x44\x11\x72\x9c\x97\x32\xaa\x63\x9b\x99\x49\xd5\xdc\x1e\x41\x24\xc4\xaa\xbb\xf7\x2b\x7d\xfd\x5b\x66\x1b\xe8\x09\x1a\x85\xab\x71\xc9\x5e\x61\xee\x02\xc9\x48\xae\x06\x09\x6e\x2e\xe2\x5e\xd6\xf1\x27\x31\x28\x1d\xf1\xb5\xb2\x40\xbd\x20\x21\x28\x1a\x5e\xc2\x19\x4d\xe5\x6b\xb1\x56\x21\x57\x8e\xa0\x87\xd9\x20\x77\x17\xd4\x9f\x76\x35\xd0\xeb\x56\xe1\x4b\x86\x7e\x16\xa3\x23\x96\xc0\xf9\xf7\x7f\xb8\xb1\xf9\x0d\x4a\x87\x1b\xe2\xc9\x82\x98\x5f\xca\x54\xa5\x8c\x70\xa3\x7c\x64\xd8\xcc\xc9\x20\xff\x7a\xdd\x34\x5a\x72\x4d\x75\x1d\x2c\x76\xbb\x6d\xa7\xee\xf2\xc7\xdf\xc6\x92\x67\xa2\xfe\x39\xab\x85\x94\x06\x7a\x0f\xd9\x30\x3d\x6e\x7e\x59\x93\x71\xb3\x2a\xa4\xb5\x20\x67\xd8\x77\x00\x00\x00\xff\xff\x2c\xec\xe5\x9e\x1c\x01\x00\x00"), }, - "/ignition/master/files/etc/nkd": &vfsgen۰DirInfo{ - name: "nkd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + "/housekeeper": &vfsgen۰DirInfo{ + name: "housekeeper", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/ignition/master/files/etc/nkd/node-pivot.sh.template": &vfsgen۰CompressedFileInfo{ - name: "node-pivot.sh.template", - modTime: time.Date(2024, 3, 18, 9, 13, 23, 723590344, time.UTC), + "/housekeeper/1housekeeper.io_updates.yaml": &vfsgen۰CompressedFileInfo{ + name: "1housekeeper.io_updates.yaml", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), uncompressedSize: 2294, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\xdb\x6e\xdb\x46\x10\x7d\xe7\x57\x4c\x29\x23\x69\x83\x2c\x99\xbc\xba\x70\xfa\xd0\xba\x40\x80\xa0\x28\x64\x18\x28\x60\x29\xc2\x6a\x39\xa4\x06\x26\x77\xe9\xdd\xa5\x23\x41\xd6\xbf\x17\x7b\x91\x44\xd2\xb2\xe5\xd6\x0f\xbe\x8c\xcf\x9c\x39\x73\x5d\x4d\x7e\xca\x97\x24\x73\xb3\x4a\x92\x09\xfc\xd9\x49\x61\x49\x49\xb0\x0a\x1a\x2e\x79\x85\x60\x50\x3f\x92\x40\x30\x96\x6b\xdb\xb5\xc0\x65\x01\x28\xf9\xb2\x46\x50\x12\x96\x4a\xd9\x24\x20\x17\x11\xf9\xf3\x2f\xb0\x4d\x00\x60\xef\xb9\x90\xbc\xc1\xab\xf4\xe2\x73\xea\xad\x54\x82\xd9\x18\x8b\x8d\xb0\x35\x90\x61\x5c\x58\x7a\x44\x60\xec\xa1\x23\xb4\x90\x5e\xf4\xdd\xd2\x5f\xc1\xae\x50\x7a\x47\xf7\x85\x62\xa5\x46\x10\x20\x03\xbc\xd6\xc8\x8b\x0d\xe8\x4e\x4a\x92\x55\x08\x84\xb5\xc1\x73\x8e\x52\xd9\xbd\xd3\xc7\x90\x21\xc9\x2a\xcb\xb2\xf4\xe0\x38\x90\xeb\x11\x63\x89\xf0\xee\x5d\x0f\x12\x4b\xf3\x7a\x1a\x2f\x29\xda\x2b\x00\xd3\x09\x81\xc6\xf4\x74\x0c\xb2\x39\xfa\xdf\x86\x70\x56\x45\x6d\x03\xbe\x9e\xbb\xf7\x58\x93\x85\xcf\x07\x53\x49\x49\xfc\xb1\x73\xad\xff\x7d\x85\xe2\xde\xa7\x1b\x1b\x8e\x6b\x32\xd6\xf8\x7e\xc7\x51\x20\x9b\x08\x87\x5a\x70\x59\x2c\xfe\x7f\xd3\x6b\x32\x96\x75\x92\x2c\x2b\xa9\x46\x03\x4f\x50\x69\x6c\x81\x3d\x8c\xca\x91\xc5\x3f\xc6\xd5\x1b\x46\x1e\x97\xfa\x6d\xbd\xdf\x3b\x17\x0a\xc3\x14\xf8\x6c\x3f\x82\xb9\xa7\xb6\xed\xcf\x40\x28\xcf\x4b\x69\x43\xba\xdd\x66\xd3\x4e\x5a\x6a\x70\xb7\x4b\x5f\xc1\xad\x54\x67\xf0\x1e\xb1\x45\xcd\x0a\x8e\x8d\x92\xa9\xaf\xba\x92\x25\x55\x9d\x46\x97\x22\x08\x4d\x0a\x84\x92\x96\x93\x44\xed\x26\xd3\x11\x27\x54\xc2\x1d\xb0\x12\xd2\x1c\xad\xc8\x1d\xc8\x7f\xcb\x84\x92\x65\x0a\xf3\x5e\x79\x3c\x72\xa8\x09\xae\x20\x75\xe8\x21\x30\x82\x0f\x85\x9f\xdd\x39\xcc\x2c\xa3\x86\x57\x38\x9b\xa7\x70\x22\xd4\x89\x21\xee\x53\x7c\xbf\xbb\xbb\x34\x2d\x17\x78\x39\x9f\x7f\x68\x79\x67\x70\xe1\xd9\x9c\x80\x37\xd2\x85\x01\x2a\x80\x11\xbc\x37\x4f\xdf\x87\x24\xd9\x87\xa7\x11\xeb\x76\x9b\x7d\x75\xbf\x4f\xb1\x22\x63\xf5\x66\xb7\xcb\xb7\xdb\xec\x6f\x07\xf2\xf6\xdd\x2e\x7d\x7a\x7f\x2a\xf4\x70\x2d\xc6\x9b\xd5\x57\x91\x8f\x0a\x93\x73\xf8\xef\x22\xce\x6b\x88\xbb\xf8\xca\xa2\x7b\x19\x41\xc5\x3c\x85\x2f\x5f\xce\xa7\xe5\xdd\x86\x62\x67\x6f\x50\x3b\x4b\xcf\xd2\xf7\xd4\x1e\xb7\x5a\x63\xb8\x40\x0e\xbe\xdf\x9c\x92\xdc\x8c\xff\x41\xc6\x1f\xa9\x9b\xeb\x6f\x24\xbb\x75\x12\x94\x05\xab\x3b\x76\xd1\xee\x57\xee\xd0\xfd\xc9\xcd\xf5\xb7\xaf\x7f\xdd\xfe\x73\x85\xb2\x54\x5a\x90\xac\x0e\x96\x22\xf0\x15\x93\x2a\x16\xd6\x60\xed\x08\x72\xe1\x97\x29\x31\x68\x83\x13\xc2\xa7\xc1\x65\x9b\x62\x8d\x3c\x26\x7a\x3b\x75\xaf\x0f\x60\xd3\xda\x4d\x5c\x30\x19\x36\x67\x08\x72\x1b\xd4\x5f\x9c\x09\x5c\xaf\x51\x74\x16\x41\xe3\x92\xc7\x56\xe9\xb6\x61\xca\x58\x8d\x7b\x2b\x30\x86\xeb\x16\x35\x35\x28\x2d\xaf\x21\xfc\x93\x75\xf2\x11\x35\x95\x84\x05\xf3\x2d\xb9\x2c\x94\xb8\x47\x7d\x99\xe7\xa7\x02\x03\x63\xcb\x4d\xcb\x8d\x61\x85\xa6\x47\xd4\x31\xfe\x21\x1d\x77\x31\x62\xb8\x1f\xdc\xec\xdf\x8c\xb2\xab\x8f\xa7\xe0\xe2\x37\x60\xf8\x00\x9f\x9e\xed\x7e\xe8\xc1\x34\x78\xab\x16\x35\xf7\xcf\xbe\x50\x4d\x5b\xa3\xc5\xa2\xc7\x56\x6f\x32\x98\xa2\x7b\xe8\x5d\xb3\x5c\xd0\xd0\xf4\xc1\x33\xd9\x9f\x03\xff\x99\xe0\x85\x2b\xfc\x2c\x62\xc9\xa9\xc6\x22\x83\x1b\x4f\x00\x3f\xa8\xae\xfd\x3d\x5e\x62\x64\xc2\xe2\x78\x89\x93\x03\xe3\x9e\xed\x74\x43\x8f\x77\xfc\x79\x6b\xb2\xd4\x8d\xe5\xbf\x01\x00\x00\xff\xff\xd0\xd1\x38\x68\xf6\x08\x00\x00"), - }, - "/ignition/master/files/etc/sysctl.d": &vfsgen۰DirInfo{ - name: "sysctl.d", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - }, - "/ignition/master/files/etc/sysctl.d/kubernetes.conf": &vfsgen۰CompressedFileInfo{ - name: "kubernetes.conf", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 97, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xca\x4b\x2d\xd1\x4b\x2a\xca\x4c\x49\x4f\x85\x52\xba\x79\x69\xba\xc9\x89\x39\x39\xba\x99\x05\x25\x89\x49\x39\xa9\xc5\xb6\x86\x5c\xf8\x14\x99\xa1\xa8\xca\x2c\x28\x33\xd1\xcb\x2c\x88\x4f\xcb\x2f\x2a\x4f\x2c\x4a\xb1\x35\xe4\x02\x04\x00\x00\xff\xff\x6d\xd4\xf2\x72\x61\x00\x00\x00"), - }, - "/ignition/master/files/etc/systemd": &vfsgen۰DirInfo{ - name: "systemd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - }, - "/ignition/master/files/etc/systemd/system": &vfsgen۰DirInfo{ - name: "system", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - }, - "/ignition/master/files/etc/systemd/system/kubelet.service.d": &vfsgen۰DirInfo{ - name: "kubelet.service.d", - modTime: time.Date(2024, 3, 18, 8, 2, 2, 908807863, time.UTC), - }, - "/ignition/master/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template": &vfsgen۰CompressedFileInfo{ - name: "10-kubeadm.conf.template", - modTime: time.Date(2024, 3, 18, 8, 27, 54, 991469945, time.UTC), - uncompressedSize: 1086, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x53\xd1\x4e\xdb\x50\x0c\x7d\xef\x57\x58\x01\x89\x87\x91\x54\xbc\x4e\xca\x43\xb7\x15\x34\xb1\x01\x2a\xa0\x4d\x9a\x26\xe4\x24\x4e\x6a\xb8\xbd\x2e\xbe\x4e\xa0\x42\xfc\xfb\x74\x13\x5a\x8a\x56\x78\x8a\xe3\xf8\x9e\x73\xae\xcf\xc9\x1e\x9c\x89\xd1\x67\xb8\x9a\x73\x80\x4a\x65\xc9\x1e\xc4\xbb\x15\x3c\x88\xde\x05\x78\x60\x9b\xc3\x5d\x5b\x10\x56\x0b\x40\x5f\xf5\xb5\x23\x83\xee\x28\x3b\x3a\xfa\x34\xfa\x73\x49\xda\x71\x49\x7f\x47\x53\xdf\xb1\x8a\x5f\x90\xb7\x3c\x39\xbd\xfe\x32\xfd\x31\xbd\xba\x89\xcf\xaf\xe7\x67\xc7\xdf\x4f\x6e\x26\xb3\x93\xcb\x3c\x4d\x0b\x11\x0b\xa6\xb8\x4c\x23\x52\x29\xbe\xe6\x26\x1f\x93\x95\xe3\xf8\xae\x9e\x8c\xc2\xf8\xed\x90\x23\xcb\xe2\x20\xa4\x1f\x9d\xd9\x9e\x4c\x76\xab\x79\xab\x64\x8d\xd3\xa1\x8e\x1d\x17\x6b\x80\xf1\xd0\xcf\x56\xb8\x70\xc9\xe8\xe9\x09\xb8\x06\xba\x87\x6c\xd6\x7a\xe3\x05\x41\x52\x2a\x4b\x02\xcf\xcf\xef\x70\x9c\xcc\xce\xaf\x2f\x7e\xfd\xdc\xb0\x34\x2a\xed\x32\xa4\x4b\xd2\xf4\x5e\x42\x5e\xa3\x0b\x04\x69\x4a\xbe\x16\x2d\x29\xf5\x52\x51\x8a\xce\x49\x89\x86\x85\xa3\xfc\xe0\xa0\x67\x25\x5f\x45\x8e\xbd\xc1\x18\x0e\x80\x50\xb3\x23\xb0\x39\x1a\x24\x6b\x47\xd8\xb3\x25\xbd\x2f\x9b\xd6\xad\xb0\x4f\xa0\x21\x4f\x8a\x46\x01\xd0\x40\x07\xe9\x87\xb0\x94\x65\xeb\xd0\xd8\x37\x60\x73\x82\x6d\x97\x26\xdf\x06\xc9\xd0\xa1\x72\x14\x02\xd5\xca\xe3\x82\x4b\x74\x6e\xb5\x7d\xd5\x63\x76\x94\xa7\xff\x6d\xed\x85\x3e\xad\x1d\x36\x21\x23\xdf\xbd\x23\x3d\xf2\xb6\x81\x14\x4a\xf4\xb1\x80\x5a\x14\xa4\x23\x55\xae\x28\x80\xd4\xfd\xc4\x3a\x65\xa8\x4d\x00\x8c\x08\x0e\x83\x81\x52\x10\xb5\x0c\x2e\x94\x6a\x52\x2c\xdc\xea\xf0\x15\x30\xcc\xa5\x75\x55\xac\x47\x7b\x7d\x37\x3b\x93\x8a\x66\xd4\x70\x8c\x92\xb1\xf8\xec\x74\x80\x9d\x3e\x9a\xe2\x24\x42\x4b\x71\x4b\xa5\x01\xfb\xfe\xc0\xe0\x7c\x3b\x0c\xf7\x9a\x03\xb0\x0f\x46\x58\x65\x9b\x65\x4d\x7f\x5f\xcd\x26\xc3\xaa\x5e\x18\x0b\x82\x20\xad\x96\x54\x41\xad\xb2\x00\x8b\xd7\x8e\xa7\xb3\x1d\x7b\x8b\xa9\x0d\xab\x30\x50\xad\xb7\x37\x9a\x3e\x52\x79\x69\xa8\x96\x6f\x95\xe3\x36\xe8\xb8\x60\xbf\x9e\x82\xfd\x77\x7e\xab\xd7\x0f\x3b\x9b\x6f\xec\xdd\xdf\x71\x8f\x0f\x42\xbe\xbf\x33\xd7\x9b\x7c\xfe\x0b\x00\x00\xff\xff\x21\x0d\x51\xd3\x3e\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xb4\x54\x4d\x93\xe3\x44\x0c\xbd\xe7\x57\xa8\x8a\xc3\x5e\x88\xc3\xc0\x65\xf1\x6d\x6b\x80\xaa\x14\xcb\x32\x35\x99\xd9\xbb\xec\x56\x9c\x26\xed\xee\x46\x52\xa7\x76\xa0\xf8\xef\x54\xb7\x9d\x89\xed\xd9\xf9\xe0\x80\x6f\x96\xd4\x7a\x92\x9e\x9e\x30\xda\xcf\xc4\x62\x83\xaf\x01\xa3\xa5\x2f\x4a\x3e\xff\x49\x75\x7c\x2f\x95\x0d\x9b\xd3\xd5\xea\x68\xbd\xa9\xe1\x3a\x89\x86\xfe\x96\x24\x24\x6e\xe9\x27\xda\x5b\x6f\xd5\x06\xbf\xea\x49\xd1\xa0\x62\xbd\x02\x40\xef\x83\x62\x36\x4b\xfe\x05\x68\x83\x57\x0e\xce\x11\xaf\x3b\xf2\xd5\x31\x35\xd4\x24\xeb\x0c\x71\x49\x7e\x86\x3e\x7d\x57\xfd\x58\x7d\xbf\x02\x68\x99\xca\xf3\x3b\xdb\x93\x28\xf6\xb1\x06\x9f\x9c\x5b\x01\x78\xec\xa9\x86\x14\x0d\x2a\x49\x75\x08\x49\xe8\x48\x14\x4b\xa2\x95\x44\x6a\x33\x60\xc7\x21\xc5\x1a\x16\xde\xe1\xf1\x58\xd1\xd0\xcd\x7d\xc9\x53\x0c\xce\x8a\xfe\x3a\x31\x7e\xb4\xa2\xc5\x11\x5d\x62\x74\x8f\x98\xc5\x26\xd6\x77\xc9\x21\x9f\xad\x2b\x00\x69\x43\xa4\x1a\x3e\x65\x88\x88\x2d\x99\x15\xc0\xd8\x58\x81\x5c\x8f\xa5\x9f\xae\xd0\xc5\x03\x5e\x0d\x79\xda\x03\xf5\x38\x54\x04\x10\x22\xf9\x0f\x37\xdb\xcf\x3f\xec\x66\x66\x00\x43\xd2\xb2\x8d\x5a\x86\x34\x94\x07\x56\x40\x0f\x04\x43\x28\xec\x03\x97\xdf\xb1\x48\xf8\x70\xb3\x7d\x7c\x1d\x39\x44\x62\xb5\xe7\xd6\x87\x6f\x42\xf9\xc4\xba\xc0\x7a\x97\xcb\x19\xa2\xc0\x64\xae\x69\x40\x1d\x1b\x23\x33\x76\x00\x61\x0f\x7a\xb0\x02\x4c\x91\x49\xc8\x0f\xec\xcf\x12\x43\x0e\x42\x0f\xa1\xf9\x83\x5a\xad\x60\x47\x9c\xd3\x80\x1c\x42\x72\x26\xaf\xc8\x89\x58\x81\xa9\x0d\x9d\xb7\x7f\x3d\xe6\x16\xd0\x50\x40\x5d\xee\x4c\x17\x39\xad\x57\x62\x8f\x0e\x4e\xe8\x12\x7d\x0b\xe8\x0d\xf4\xf8\x00\x4c\x19\x05\x92\x9f\xe4\x2b\x21\x52\xc1\x6f\x81\x09\xac\xdf\x87\x1a\x0e\xaa\x51\xea\xcd\xa6\xb3\x7a\x5e\xf5\x36\xf4\x7d\xf2\x56\x1f\x36\x65\x6b\x6d\x93\x34\xb0\x6c\x0c\x9d\xc8\x6d\xc4\x76\x6b\xe4\xf6\x60\x95\x5a\x4d\x4c\x1b\x8c\x76\x5d\x4a\xf7\x65\xdd\xab\xde\x7c\xc3\xa3\x38\xe4\xdd\xac\x56\x7d\xc8\xfb\x21\xca\xd6\x77\x13\x47\x59\xc4\x17\x18\xc8\x3b\x99\xc9\xc6\xf1\xe9\xd0\xc5\x65\xd0\xd9\x94\xa7\x73\xfb\xf3\xee\x0e\xce\xd0\x85\x8c\xe5\xf4\xcb\xdc\x2f\x0f\xe5\x42\x41\x1e\x98\xf5\x7b\xe2\x81\xc4\x3d\x87\xbe\xe4\x24\x6f\x62\xb0\x5e\xcb\x4f\xeb\x2c\xf9\xe5\xf8\x25\x35\xbd\xd5\xcc\xfb\x9f\x89\x44\x33\x57\x15\x5c\x17\xfd\x43\x73\x5e\x47\x53\xc1\xd6\xc3\x35\xf6\xe4\xae\x51\xe8\x7f\x27\x20\x4f\x5a\xd6\x79\xb0\x6f\xa3\x60\x7a\xba\x96\xc1\xc3\xd4\x26\x8e\xf3\x8d\x79\x86\xaf\x41\x9d\xbb\x48\xed\x4c\x30\x86\xc4\x72\x5e\x69\xcd\xda\x0d\xfb\xe9\xe5\x79\x59\xa7\x65\x47\x52\x43\x5f\x15\xeb\xd3\x75\xb9\xbb\xa8\x13\x92\x90\xc9\xe2\x49\xb1\x63\x34\x04\xc7\xf7\x8b\x79\xbc\x30\x93\xb2\x31\xb2\xed\xb1\xa3\xfb\xdb\x8f\x6f\x41\xb5\x39\x16\x12\xbb\x27\xb8\xbf\xef\xfe\x13\x2c\x9d\x6c\xab\x37\xc1\xfc\x12\xb8\xa5\xd7\x90\xb7\x7b\x50\xce\xc2\xdf\xe7\xe8\xe1\x6d\x99\x78\x0c\xe6\x39\xd4\x26\x04\x47\xb8\xbc\x4e\x3d\x7e\xb9\xf7\x78\x42\xeb\xb0\x71\xaf\xe2\x7e\x4a\x7d\x43\x9c\x99\xf4\xc1\x14\x92\x51\x01\x99\xa0\xa1\xac\xc8\xb1\x75\x03\x38\x54\x23\xd8\x13\xa8\xed\xe9\xb9\x9a\xf2\x1d\xeb\x88\x67\xde\x2c\xaa\xbc\x35\xf3\x5a\xd6\xd3\x75\x58\x78\x2e\x8c\x2d\x1c\xb3\x99\x2e\x7c\xf3\xc6\xdf\xa4\x01\x45\x4d\xf2\xba\x0a\x4a\xd8\x4c\x07\xa1\x91\x7c\x73\x5e\x16\xc2\x57\x51\x9f\x18\x87\x44\x75\xa1\x7f\x30\x68\x60\xec\x68\x6a\x49\xcd\xe3\x31\x3e\x57\x3b\xd6\x0e\x7f\xff\xb3\xfa\x37\x00\x00\xff\xff\x66\xcf\xb8\xbd\xf6\x08\x00\x00"), }, - "/ignition/master/systemd": &vfsgen۰DirInfo{ - name: "systemd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + "/housekeeper/2namespace.yaml": &vfsgen۰FileInfo{ + name: "2namespace.yaml", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + content: []byte("\x61\x70\x69\x56\x65\x72\x73\x69\x6f\x6e\x3a\x20\x76\x31\x0a\x6b\x69\x6e\x64\x3a\x20\x4e\x61\x6d\x65\x73\x70\x61\x63\x65\x0a\x6d\x65\x74\x61\x64\x61\x74\x61\x3a\x0a\x20\x20\x6e\x61\x6d\x65\x3a\x20\x68\x6f\x75\x73\x65\x6b\x65\x65\x70\x65\x72\x2d\x73\x79\x73\x74\x65\x6d"), }, - "/ignition/master/systemd/join-master.service.template": &vfsgen۰CompressedFileInfo{ - name: "join-master.service.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 625, + "/housekeeper/3role.yaml": &vfsgen۰CompressedFileInfo{ + name: "3role.yaml", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 773, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x50\xb1\x8e\x13\x31\x10\xed\xfd\x15\x73\x29\xae\x73\x02\x05\x15\xda\xe2\x08\x27\x71\x82\x22\x4a\x38\x51\x44\x29\x26\xde\x97\xac\x59\xaf\xbd\x8c\x67\xc3\x45\x51\xfe\x1d\x79\x57\xe1\x24\x24\x9a\xeb\xfc\xde\x9b\xf7\xfc\x66\xb6\xcf\xd1\xeb\xce\x7c\x46\x76\xe2\x7b\xf5\x29\x56\x1d\x67\x85\x50\x4c\x35\xe8\x67\xf2\x91\xb4\x01\xb9\x30\x14\xd6\xac\xf1\x6b\xf0\x82\x5c\x65\xa8\x6d\x21\x11\xc1\xf6\x2c\x3c\xcf\x90\x93\x77\x20\x41\x00\x67\x58\xdf\xf1\x11\xb6\xf7\xa7\xa4\x37\xcd\x3c\x1c\x14\xf2\x26\xe7\x32\xc5\xda\x97\x76\x2b\xd6\xe6\xf1\xc5\x67\xcd\xd5\xdd\xe2\xc4\xb2\x08\xe9\xb8\x28\x2d\xed\x54\x7b\x9e\x95\xbb\xde\x98\xed\x66\x72\xee\xcc\xe3\x0b\xdc\x46\x59\x74\x25\xa8\x16\x7b\x1f\x17\x7b\xce\x0d\x59\x47\xb3\xdf\x8d\x0f\xa0\x2d\xdd\x91\x3d\xd0\xdf\xb4\xb2\xf8\xed\xfb\x12\x46\xbb\x8f\x54\x27\xca\x01\xe8\xe9\xfd\xbb\x02\x22\x66\xaf\xb9\xff\x84\xb6\xc3\x1e\x5c\x77\xd3\xe9\x2e\x97\xf9\xc3\xea\xa9\x74\x81\x3c\xaf\xbf\x5d\xaf\x64\xad\xa6\x16\xa3\xf2\xbd\x3c\x46\xaa\xf6\xd9\xa5\x13\xe4\x3c\x89\xd6\xb1\x75\x10\xb5\x4d\x09\xbd\x5c\xe6\x4b\x5e\x42\xf4\x0b\xe7\x66\x1c\x77\x29\xaa\xa4\x60\xfb\xc0\x11\x05\x43\xd4\x1f\xbc\x63\x85\x6d\x71\x1e\x1d\xaf\xd4\x57\x9c\x27\x97\x78\x9b\x93\x6b\xa1\x55\x19\x10\xbf\x19\xc1\xf5\x4a\x74\x7f\x4f\x9a\x06\xd7\xd0\xff\x4f\x3a\x33\x6b\xe4\x71\xdd\x14\xed\x81\x7d\x18\x04\x37\x6a\x03\x57\x7d\xc8\xc6\x6c\x9f\x62\x56\x0e\x61\x67\x7e\x70\x54\xd4\x9f\xce\x55\x37\x04\xf5\x76\xc8\x90\xb9\xb2\x1c\xa1\x7f\x02\x00\x00\xff\xff\xba\x44\x08\x20\x71\x02\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x92\x41\x6b\xfb\x30\x0c\xc5\xef\xfe\x14\xa2\xf7\xa4\xfc\x6f\x7f\x72\xdd\x61\xf7\x31\x76\x57\xe3\xb7\x56\xd4\xb1\x8d\x64\x77\xd0\x4f\x3f\xea\x66\x30\x92\x6e\x8c\x9c\xfc\x90\xd1\xef\x3d\xd9\xe2\x2c\x6f\x50\x93\x14\x07\xd2\x03\x8f\x3d\xd7\x72\x4a\x2a\x57\x2e\x92\x62\x7f\xfe\x6f\xbd\xa4\xfd\xe5\x9f\x3b\x4b\xf4\x03\x3d\x85\x6a\x05\xfa\x92\x02\xdc\x84\xc2\x9e\x0b\x0f\x8e\x68\x54\xb4\x86\x57\x99\x60\x85\xa7\x3c\x50\xac\x21\x38\xa2\xc8\x13\x06\xaa\xd9\x73\x41\x37\x71\xe4\x23\xb4\xd3\x5b\xbf\xd6\x00\x1b\x5c\x47\x9c\xe5\x59\x53\xcd\x76\x23\x75\x74\x4a\xd5\x70\x06\x32\xb4\x97\xe4\x88\x14\x96\xaa\x8e\x98\xef\xef\x2c\x73\x44\x17\xe8\x61\x2e\xb6\x00\x68\xd2\x23\x60\x96\x47\x94\x76\x06\xb1\xbb\xc8\x5c\xc6\xd3\x37\x4a\x93\x1f\xad\xb8\x35\xc7\xfe\x5d\x22\x07\xb9\x42\x17\x91\x66\x87\xcd\x5c\x2b\x5c\xea\x82\xf9\x35\xd0\x6a\x8e\x95\xcb\x6e\xb7\x26\xc7\xe4\xf1\x03\x70\xd3\x0b\x3d\xf2\xc8\xc9\xff\x66\xf1\x67\xc6\x1e\x17\x19\x6f\x2b\xf5\xf0\x9f\x57\x18\xce\xd9\xd6\x20\xcf\x98\x52\x34\x94\x45\xa4\xc5\x8a\x7c\x06\x00\x00\xff\xff\xe4\x20\x3c\x4b\x05\x03\x00\x00"), }, - "/ignition/master/systemd/kubelet.service": &vfsgen۰CompressedFileInfo{ - name: "kubelet.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 325, + "/housekeeper/4role_binding.yaml": &vfsgen۰CompressedFileInfo{ + name: "4role_binding.yaml", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 286, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\x31\x6e\xf3\x30\x0c\x46\x77\x9e\xc2\x17\xb0\x95\x7f\x0d\xa0\x21\x7f\x93\x21\x68\x51\x14\x75\x8b\x0e\x86\x07\x59\x66\x6d\xc2\x32\x65\x88\x94\x93\xdc\xbe\xa8\xd1\x74\xeb\xfc\x1e\x89\xef\x35\xef\x4c\xda\xc2\x11\xc5\x27\x5a\x94\x22\xdb\x29\x77\x18\x50\xf7\xc5\xdb\x88\xc5\x63\xee\x30\x31\x2a\x4a\xf1\x1c\x7b\x2c\x0e\x03\xb2\xc2\x31\xfa\x3c\x23\xab\xdb\x0e\x46\xd5\x45\xf6\xc6\x4c\xbf\x6e\x45\xd1\xf4\xd1\x8b\x81\x0f\xc7\x2a\x96\x51\x2f\x31\x4d\x65\xe4\x40\x8c\x95\xba\x34\xa0\xc2\xe1\x53\x31\xfd\xc1\x1e\x22\xf7\xf4\xfd\xfd\xc5\xe9\x78\xba\x92\xa8\x58\xb3\xba\x64\x42\x1c\x0c\xc7\x1e\xcb\x85\xd6\xa8\x95\xa8\x9b\x17\x80\xa6\xc6\xb4\x92\xc7\x16\x4e\x57\xf4\xb5\xba\xa4\xd6\x64\x49\xa6\x23\x36\x3f\x41\xf0\x8a\xb2\x01\x17\x2e\xee\x26\xb0\x59\x4f\x34\x93\x9e\x59\x31\xad\x2e\xd8\xdd\xdd\xa9\xd1\xdb\x7f\x3b\x80\xe6\xcc\xa2\x2e\x84\x76\x0b\xc1\xfe\xff\xcd\xce\x39\x28\x95\x59\x30\xdd\xb7\xc2\x57\x00\x00\x00\xff\xff\xcb\xcf\xb3\x78\x45\x01\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x8d\xbd\x4a\x03\x41\x14\x85\xfb\x79\x8a\x79\x81\x5d\xb1\x93\xe9\xd4\xc2\x3e\x82\xfd\xdd\x99\x93\xe4\xba\xbb\x73\x87\xfb\x13\xd0\xa7\x97\x40\xb4\x11\xd2\x9d\x03\xdf\xc7\x47\x83\x3f\xa0\xc6\xd2\x4b\xd6\x85\xea\x4c\xe1\x67\x51\xfe\x26\x67\xe9\xf3\xfa\x64\x33\xcb\xc3\xe5\x31\xad\xdc\x5b\xc9\xaf\x5b\x98\x43\x0f\xb2\xe1\x85\x7b\xe3\x7e\x4a\x3b\x9c\x1a\x39\x95\x94\x73\xa7\x1d\x25\xc7\x68\xe4\x98\x76\xea\x74\x82\x4e\x2a\x1b\x96\x1b\x7c\xdd\x07\x1c\xaf\x2c\x0d\x7e\x53\x89\x71\xa7\x9b\x72\xfe\x97\xbd\x57\x49\x16\xcb\x27\xaa\x5b\x49\xd3\xcd\x7c\x87\x5e\xb8\xe2\xb9\x56\x89\xee\x7f\x72\xc3\x91\x62\xfb\xfd\x36\xa8\xa2\xe4\xb3\x84\x61\x05\x06\x74\xb2\x2f\x73\xec\x3f\x01\x00\x00\xff\xff\x54\x83\xe7\x45\x1e\x01\x00\x00"), }, - "/ignition/master/systemd/release-image-pivot.service": &vfsgen۰CompressedFileInfo{ - name: "release-image-pivot.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 324, + "/housekeeper/5deployment.yaml.template": &vfsgen۰CompressedFileInfo{ + name: "5deployment.yaml.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 985, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\xbd\x6e\xc3\x30\x0c\x06\x77\x3d\x05\x9b\x21\x9b\xa3\xa9\xa3\x86\xfe\x64\xe8\x16\xd4\x28\x3a\x18\x1e\x18\x99\xb1\x09\xcb\x94\x21\xd2\x6e\xfa\xf6\x85\x03\x64\xe8\x90\x95\xc4\x77\x87\x6b\xbe\x84\xad\x75\xef\xa4\xb1\xf0\x6c\x9c\x25\x9c\x78\xcd\x06\x92\x3b\x02\xcb\x60\x03\x81\x8c\x1d\x14\x4a\x84\x4a\xc0\x13\xf6\xe4\xbe\x51\x4c\x83\x90\xfd\xe4\x32\x56\x59\x12\x0b\x1d\x0c\x4b\x4f\xe6\x5e\x2e\x46\xe5\xc1\xef\x2d\x4b\xc7\x9b\xe5\x84\x36\x1c\xaf\xac\xa6\xe1\xc9\xaf\x58\x7c\xca\xbd\xdf\x9c\xd5\xbc\xe9\x0f\x6a\x38\xcd\xce\x35\x35\x95\x95\x23\xb5\xee\x78\xa5\x58\x1b\x16\x0b\xfe\xcc\xe2\xcf\xa8\x03\x54\x11\x76\x9e\x2c\x7a\x19\xbb\x7f\xe3\x01\xf6\x7b\xb0\xbc\xc4\x01\x1e\xc2\x77\xce\x7d\x92\xde\x88\x59\xaa\x0b\x72\x5a\x0a\xdd\x4f\x35\xc5\xf0\xac\xce\x35\x1f\xa2\x86\x29\xb5\xb7\x62\xea\x5e\x7f\xc3\xb4\x24\xe3\x6a\x51\x2a\xf7\xa8\xbf\x00\x00\x00\xff\xff\xea\x51\xb4\x1e\x44\x01\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x94\x92\x49\x6b\x1c\x41\x0c\x85\xef\xf3\x2b\x44\xdf\x7b\x96\xe4\x56\xb7\x90\x98\x10\x48\xc8\x80\x49\xee\x72\xf5\xcb\xb8\x18\xd5\x12\xa9\xda\x76\x63\xfc\xdf\x43\xdb\xb3\x74\x1b\x82\x3d\x3a\xaa\x9e\x3e\x49\xa5\xc7\x25\xfc\x86\x5a\xc8\xc9\x11\x97\x62\xab\xbb\xcd\x62\x1f\x52\xe7\xe8\x0b\x8a\xe4\x21\x22\xd5\x45\x44\xe5\x8e\x2b\xbb\x05\x51\xe2\x08\x47\xb7\xb9\x37\xec\x81\x02\x6d\x73\x81\x72\xcd\xda\x46\x4e\xbc\x83\x1e\x44\x56\xd8\xbf\x52\xda\x60\x15\x71\x41\x24\x7c\x03\xb1\x11\x47\xe4\x73\xaa\x9a\xa5\x2d\xc2\xe9\x2d\xb2\x15\xf8\xb1\xca\x20\xf0\x35\xeb\x0b\x21\x72\xf5\xb7\xdf\x27\xc8\xcb\xa0\x44\x8a\x22\xc1\xb3\x39\xda\x2c\x88\x2a\x62\x11\xae\x38\xc0\x27\xbb\x8f\x21\xb3\x3e\x97\x76\x22\x3a\xae\x70\xac\xe5\x90\xa0\x27\x5e\x4b\x3e\xc7\xc8\xa9\x3b\x37\x68\x69\xf5\x26\x74\x8c\x10\x79\x07\x47\x8f\x8f\xcb\x9f\x07\xc9\xb7\x31\xf3\x4b\xe5\xe9\x69\x2e\xda\xf6\x22\xdb\x2c\xc1\x0f\x8e\x3e\xc9\x3d\x0f\x76\x7a\x7f\xd7\x75\x5f\xc2\xe0\x7b\x0d\x75\xf8\x9c\x53\xc5\x43\x3d\x0f\x4c\xc4\x22\xf9\x7e\xab\xe1\x2e\x08\x76\xb8\x32\xcf\xc2\xf5\xd9\x62\x7f\x58\x0c\x27\xa5\xc2\x72\xaf\x1e\x36\x2d\x96\x10\x43\x9d\x65\x88\x7c\xe9\x1d\x6d\xd6\xeb\x38\xcb\x46\xc4\xac\x83\xa3\x8f\xeb\x1f\x61\xf2\xa0\xf8\xdb\xc3\x2e\x43\x7c\x38\x23\x2a\x34\x86\xf4\x3c\xef\x57\x65\x8f\x2d\x34\xe4\xee\x1a\x3e\xa7\x6e\x74\xc8\xfa\xa0\x4b\xb9\xc3\xf5\xcc\x87\xc7\x6c\xab\x59\xb0\xdc\xf7\x37\xd0\x84\x0a\x5b\x86\xbc\x7a\x65\x93\xa6\x39\x76\xcb\x32\xfe\x6f\xc8\xc9\xa6\x27\xdf\x63\x70\xd4\xfc\x0f\x16\xd9\x2a\xb4\x99\x6c\x72\xbc\x92\xa3\xe6\xea\x21\x58\xb5\xe6\x5f\x00\x00\x00\xff\xff\x47\x90\x58\x37\xd9\x03\x00\x00"), }, - "/ignition/master/systemd/set-kernel-para.service": &vfsgen۰CompressedFileInfo{ - name: "set-kernel-para.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 286, + "/housekeeper/6daemonset.yaml.template": &vfsgen۰CompressedFileInfo{ + name: "6daemonset.yaml.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 1138, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x8e\x4d\x4e\xc3\x30\x10\x85\xf7\x73\x0a\x5f\x20\xc9\x09\xbc\x00\xd1\x05\x62\xd7\x82\x58\x44\x11\x72\x9c\x97\x32\xaa\x63\x9b\x99\x49\xd5\xdc\x1e\x41\x24\xc4\xaa\xbb\xf7\x2b\x7d\xfd\x5b\x66\x1b\xe8\x09\x1a\x85\xab\x71\xc9\x5e\x61\xee\x02\xc9\x48\xae\x06\x09\x6e\x2e\xe2\x5e\xd6\xf1\x27\x31\x28\x1d\xf1\xb5\xb2\x40\xbd\x20\x21\x28\x1a\x5e\xc2\x19\x4d\xe5\x6b\xb1\x56\x21\x57\x8e\xa0\x87\xd9\x20\x77\x17\xd4\x9f\x76\x35\xd0\xeb\x56\xe1\x4b\x86\x7e\x16\xa3\x23\x96\xc0\xf9\xf7\x7f\xb8\xb1\xf9\x0d\x4a\x87\x1b\xe2\xc9\x82\x98\x5f\xca\x54\xa5\x8c\x70\xa3\x7c\x64\xd8\xcc\xc9\x20\xff\x7a\xdd\x34\x5a\x72\x4d\x75\x1d\x2c\x76\xbb\x6d\xa7\xee\xf2\xc7\xdf\xc6\x92\x67\xa2\xfe\x39\xab\x85\x94\x06\x7a\x0f\xd9\x30\x3d\x6e\x7e\x59\x93\x71\xb3\x2a\xa4\xb5\x20\x67\x18\xd1\x77\x00\x00\x00\xff\xff\x73\x48\x6e\xcb\x1e\x01\x00\x00"), - }, - "/ignition/worker": &vfsgen۰DirInfo{ - name: "worker", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - }, - "/ignition/worker/files": &vfsgen۰DirInfo{ - name: "files", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - }, - "/ignition/worker/files/etc": &vfsgen۰DirInfo{ - name: "etc", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xac\x93\x41\x6b\x1b\x31\x10\x85\xef\xfe\x15\x83\xef\xeb\x25\x57\xdd\x42\x93\x42\xa1\x71\x4d\x43\x7b\x2d\x93\xdd\x67\xaf\xd8\x91\x46\x48\xda\x4d\x4d\xc8\x7f\x2f\x8a\x5d\x7b\xd7\x14\xe2\x40\x75\x9c\x79\xf3\xcd\x3c\x31\xc3\xc1\xfe\x44\x4c\x56\xbd\x21\x0e\x21\xd5\xe3\xcd\xa2\xb7\xbe\x35\x74\xc7\x70\xea\x1f\x91\x17\x0e\x99\x5b\xce\x6c\x16\x44\x9e\x1d\x0c\x75\x3a\x24\xf4\x40\x40\xac\x1a\xf5\x39\xaa\x08\x62\xe5\xd8\xf3\x0e\xf1\x28\x4b\x81\x9b\x0b\x6d\xda\xa7\x0c\xb7\x20\x12\x7e\x82\xa4\x02\x24\x3a\x02\xaa\x20\xec\xdf\x67\xa7\x80\xa6\xd4\x25\x08\x9a\xac\xf1\xc0\x70\x9c\x9b\xee\xeb\x04\xfa\x51\x2c\x51\x86\x0b\xc2\x19\x47\xe0\xc4\x73\x79\x32\x63\x7f\x9c\x4e\xf4\x77\xf0\xf2\xb2\x0a\x22\x67\xab\x7e\x82\xac\xa8\xc7\xde\xd0\xd2\x6b\x8b\x2a\xaa\x60\xd5\x0f\x4f\x88\x1e\x19\x69\x65\xb5\x76\x9c\x32\xe2\xf2\xa4\x27\xd2\x50\x28\x1a\x0d\x2d\xef\x7f\xdb\x94\xd3\x34\x89\xed\x16\x4d\x36\xb4\x5c\xeb\x63\xd3\xa1\x1d\x04\xcb\xab\x7b\x3d\x6b\xec\xff\x4f\xaf\xf2\x1b\x6c\x3d\xe2\xd9\x69\x75\xed\x16\x9d\xfe\xda\x39\xf6\xad\x99\x34\xac\xa8\xbe\xb6\xda\x3a\xde\xc1\xd0\xcb\xcb\xea\xd3\x49\xf5\xa5\xc4\x7e\x44\x79\x7d\xbd\xd0\x6d\x06\x91\x8d\x8a\x6d\xf6\x86\x6e\xe5\x99\xf7\xe9\x2c\x18\x55\x06\x87\x07\x1d\x7c\x4e\xf3\x59\x0e\x76\x86\xb0\x8b\xdc\xa2\x6a\xdf\x0e\x67\x22\x20\x72\xa5\x68\xc3\xb9\x33\x54\x8f\x1c\x6b\xdf\xb7\xe7\x3c\xfc\xf8\x2f\xdc\xfa\xdb\xdd\xfd\xaf\xf5\xed\xc3\xfd\x8c\x34\xb2\x0c\xf8\x1c\xd5\x99\x59\x98\xb6\x16\xd2\x7e\xc7\xf6\x22\x4c\xd3\xfb\x1e\x6f\x2e\x92\x6f\x45\x87\xb1\xca\x76\xae\xca\x36\xac\xd9\x61\x31\x35\x3c\xdb\xd0\x77\x9c\x76\x9a\x0e\x36\x67\x8d\xc2\xcc\xf8\x9f\x00\x00\x00\xff\xff\x09\x7d\x30\x1a\x72\x04\x00\x00"), }, - "/ignition/worker/files/etc/hosts.template": &vfsgen۰CompressedFileInfo{ - name: "hosts.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 167, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x32\x34\x32\xd7\x33\xd0\x33\xd0\x33\x54\x50\x50\xc8\xc9\x4f\x4e\xcc\xc9\xc8\x2f\x2e\x41\xb0\xf4\xc0\xac\x94\xfc\xdc\xc4\xcc\x3c\x84\xa8\x09\x12\x13\x59\x85\x09\x97\x95\x15\xc8\x20\x08\x20\xda\x38\x33\x24\x26\xb2\x0a\x33\xae\xea\x6a\x3d\x8f\xe2\xcc\x82\xda\x5a\x40\x00\x00\x00\xff\xff\x0b\x57\x23\x96\xa7\x00\x00\x00"), + "/kickstart": &vfsgen۰DirInfo{ + name: "kickstart", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/ignition/worker/files/etc/isulad": &vfsgen۰DirInfo{ - name: "isulad", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + "/kickstart/controlplane": &vfsgen۰DirInfo{ + name: "controlplane", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), }, - "/ignition/worker/files/etc/isulad/daemon.json.template": &vfsgen۰CompressedFileInfo{ - name: "daemon.json.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 1174, + "/kickstart/controlplane/kickstart.cfg.template": &vfsgen۰CompressedFileInfo{ + name: "kickstart.cfg.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 898199063, time.UTC), + uncompressedSize: 965, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x54\xcb\xae\xd3\x3c\x10\xde\x9f\xa7\x88\xbc\xfe\x9d\xf4\x2f\x12\x8b\x4a\x67\x83\xc4\x02\xb1\x00\x75\x8b\xd0\x91\x6b\x4f\xd2\x21\x8e\x6d\x8d\xed\xd0\x50\xe5\xdd\x91\x5d\x27\x6d\x03\x62\x57\xcd\x77\xe9\x37\x97\xf8\xfa\x52\x55\x55\xc5\xe0\x02\x92\x5b\x17\x3c\x3b\x54\xdf\x98\x11\x01\x47\xa8\x65\x47\x36\x3a\x45\x38\x02\xbd\xfa\xc9\x07\x18\x14\xfb\xfe\xdf\x4d\x91\x31\x76\xa8\x18\xfa\xa8\x05\x2b\x55\x05\xad\x88\x3a\x70\x8a\x26\xe0\x00\x09\xd7\x92\xd8\xaa\x11\xee\x9c\x6a\xcd\x28\xa8\xd1\x78\x6a\xb2\x58\x2d\xb8\x0f\x22\xc0\x8a\x53\x34\x1b\x1c\x4c\x87\x66\x6b\xaa\x6d\xc7\x35\x8c\xa0\x53\xfd\xe3\xf1\xf8\xe5\xb8\x20\x0e\x55\x8b\xfa\x6f\x86\xb5\x43\xf5\xa8\x2f\x9d\xdf\x86\xb1\x56\x93\x98\x0f\x56\x65\x87\xdd\xfb\xdd\xae\x48\x56\x82\x13\xe1\x1f\xed\x64\xda\x20\x2e\x7c\x09\xf1\xff\x16\xf0\xf8\x2b\x03\xef\x76\x9f\x3f\xb0\x0c\xcd\x0f\xa1\x6e\x83\x4f\xb8\x0f\xca\xc6\xb0\x04\x96\xd6\x04\x81\x06\x88\x6b\xdb\x3d\xa7\xbe\x4b\x7e\x78\x6b\x6e\x7f\xfc\xe4\x7b\xb6\xb6\xe7\xde\x81\xcc\xb1\x21\xc8\xa6\x6c\xac\x44\x6f\x12\xc1\x2f\xc5\x3a\xb9\x3c\x2c\x87\x02\x4f\x5b\x4d\x59\x0e\x15\xdb\x0f\x77\xc8\x92\xe8\xe0\x21\xb1\x1d\x81\xb4\x98\xf6\x5b\xc6\x72\x62\xf7\xc8\x0b\xb3\x4e\x3f\x08\x15\xbc\xf5\x40\x06\xf4\x9b\x3c\x83\xec\x5f\x03\xc5\xd2\xc2\x72\x78\x04\x1d\xfa\x40\x13\x1f\x90\xc8\xd2\xc6\x4e\x59\xd9\x03\xd5\x68\x9f\x45\x68\x3c\xc8\x48\xc0\x8b\x1a\x61\xa3\xbb\x5e\xeb\x4f\x83\xe8\xe0\x58\xdc\xe7\xf9\xd9\xc0\x59\xc5\xbd\x30\xea\x64\x2f\x1c\x13\x31\x75\xf9\xa7\xa8\xb9\x5e\xeb\xaf\x22\x7a\xc8\xf5\x79\x5e\xfa\x2f\x5f\x54\x1c\x84\xef\xf3\x46\x73\x9a\x15\x85\xf0\xd3\x52\xcf\x9d\x8e\x1d\x9a\x84\x4b\x83\xeb\xba\x0d\xf2\x13\x1a\xae\x30\x4f\xb6\xb1\x2e\x34\xd2\x60\x73\x42\xf3\x48\x91\xd6\xb4\x2b\x27\x2d\x36\x71\x0c\x84\x7a\x3d\xf4\x1c\x9b\x6b\x31\x01\xf1\x3c\x5c\x76\xa8\x5a\xa1\x3d\x14\x3c\x7a\xe0\x0a\x24\x4d\x2e\x80\xe2\x3d\x4c\xec\x50\xa5\xf1\x6f\x47\xe8\x7b\x74\x7c\x04\xc2\x76\xe2\x60\x5a\x4b\x12\x36\x4e\x92\x70\x79\x01\x36\x9f\x55\x2f\x82\xc8\x8f\x86\xad\xd7\x33\x56\x75\xaa\xd6\xe3\xbe\x9c\xea\xcb\xfc\xf2\x3b\x00\x00\xff\xff\x2e\x9c\xa7\x42\x96\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x6c\x50\x4d\x6f\xe5\x36\x0c\xbc\xeb\x57\x10\x31\x82\x9c\x64\xf7\x50\x14\xbd\x18\x68\x90\x0f\x34\x68\x1b\x04\x4d\xd3\xdd\xd3\x2e\xf4\x24\x3e\x5b\xb0\x4c\x1a\x12\xed\x17\xc7\x78\xff\x7d\x21\xc7\xf9\xc4\x9e\x3c\x1a\x72\xc6\x9c\x29\x26\x8c\xc9\x33\xd5\x97\x57\xff\x5f\xfd\xad\x7c\x43\x1c\xd1\xf9\xd4\x81\xd6\x4c\x61\xd6\x63\xc2\x3a\x39\xa3\xcc\x28\x3c\x98\x28\xa0\xb5\xcc\x03\xd6\x61\xea\x55\x01\x77\x26\x8a\x17\xcf\x04\x36\xa0\x89\x9e\x1a\xf0\xb4\xe7\xd8\x9b\x4c\xaa\x95\xdc\x54\xc4\x84\xa0\xb5\x27\x2f\xc1\xec\x30\xa8\x02\x1e\x12\x42\x13\xcd\xd0\x7a\x6b\x02\x78\x4a\x62\x42\x50\xaf\x8c\x2a\xe0\x2f\x9c\x77\x6c\xa2\x83\x60\x66\x1e\x25\xa9\xee\x85\xd0\x7a\xb2\x1d\xce\xbd\x19\x6a\x4b\xa0\xf5\xe3\xb6\x51\x9f\x59\x3a\x53\x05\xdc\xcf\x49\xb0\x87\x60\xa8\x19\x4d\x83\x2a\x03\x78\x6a\xbf\x5f\xdc\x96\x0f\xff\x5d\xeb\xdf\x95\x2a\xe0\x16\xe5\xc0\xb1\xfb\x70\x32\x6d\x1c\x68\xbd\x63\x96\x21\xb2\x70\xed\x5a\x3b\x80\xd6\x0e\x27\x6f\xb1\x46\x1a\x7e\x4d\xbf\xe4\x2c\xc3\xf4\x5b\x9d\x8b\x01\xad\x8d\x15\x3f\x19\xc1\xf7\x06\x2d\x27\x21\xd3\x63\xbd\x2c\xe5\x9f\x1b\x3e\x1e\x55\x01\xff\x32\x0b\x0c\x26\xa5\x03\x47\xa7\x62\xfe\xcf\x21\xfb\x25\x1b\xe7\x41\xd0\xc1\xb2\x94\x77\xdb\xf8\x59\x30\x12\x48\x8b\x70\x8f\x32\x0e\x70\xde\x20\x09\x30\xc1\xde\xc7\x24\x90\xef\x54\x2b\xcc\x08\xb4\x46\x32\xbb\x80\xaa\x80\x4b\x06\x62\x01\xcb\xb4\xf7\xcd\x18\x71\xf5\xf8\x0a\x5f\x3c\x39\x3e\x6c\x15\xa9\xd4\xf9\xe1\xf1\xad\xb1\x84\x31\xa7\x4c\xea\x05\xe4\xe0\x3e\x65\x47\x57\x9f\xd8\x36\x32\xcd\xee\xe4\x6d\x5f\x7c\x8f\x4f\x4c\xa8\x5e\x00\x9c\x27\x6f\xaa\xfb\xd6\x50\xd3\x1a\x0f\x5a\x8f\x62\x95\x3a\x1d\x8c\xed\x4c\x83\x49\xfd\xf1\xad\xf7\xe4\x7b\x13\x34\xd2\xe4\x23\x53\x8f\x24\x4a\x9d\x22\xb9\xbc\xc6\x29\x47\x08\xdc\xd4\xd5\x64\x62\x15\xb8\xa9\xba\xa4\x33\x5d\x06\x6e\x54\xdf\x39\x1f\x41\x0f\x50\xa1\xd8\x8a\x3a\xb7\xf7\x01\x53\xd5\x32\x77\xcf\xe8\xd3\x46\x5a\xaf\x74\xdb\xb7\xea\xc6\x1d\x06\x94\x72\x0b\x57\x3a\xa5\x96\xc5\xef\xa1\xbc\x49\x97\x6c\x3b\x8c\xa0\x8f\xc7\x4f\x16\x6e\x1d\xa8\x65\x41\x72\xeb\xf8\x55\x72\x93\xc6\x60\xdc\x4f\x24\x7e\x1d\x7c\x94\x44\x43\x0d\x42\x79\x9d\xaf\x3c\x1e\xd5\xb2\x94\x17\x4c\x82\x24\xdb\x23\x17\x86\xff\xb0\x5b\x9f\x48\xee\x83\xea\xb9\x6c\xb7\x09\xdf\xad\xe4\xda\x7e\x04\x00\x00\xff\xff\xef\xff\xc4\x92\xc5\x03\x00\x00"), }, - "/ignition/worker/files/etc/nkd": &vfsgen۰DirInfo{ - name: "nkd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + "/kickstart/master": &vfsgen۰DirInfo{ + name: "master", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/ignition/worker/files/etc/nkd/node-pivot.sh.template": &vfsgen۰CompressedFileInfo{ - name: "node-pivot.sh.template", - modTime: time.Date(2024, 3, 18, 9, 13, 34, 991596548, time.UTC), - uncompressedSize: 2294, + "/kickstart/master/kickstart.cfg.template": &vfsgen۰CompressedFileInfo{ + name: "kickstart.cfg.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), + uncompressedSize: 965, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\xdb\x6e\xdb\x46\x10\x7d\xe7\x57\x4c\x29\x23\x69\x83\x2c\x99\xbc\xba\x70\xfa\xd0\xba\x40\x80\xa0\x28\x64\x18\x28\x60\x29\xc2\x6a\x39\xa4\x06\x26\x77\xe9\xdd\xa5\x23\x41\xd6\xbf\x17\x7b\x91\x44\xd2\xb2\xe5\xd6\x0f\xbe\x8c\xcf\x9c\x39\x73\x5d\x4d\x7e\xca\x97\x24\x73\xb3\x4a\x92\x09\xfc\xd9\x49\x61\x49\x49\xb0\x0a\x1a\x2e\x79\x85\x60\x50\x3f\x92\x40\x30\x96\x6b\xdb\xb5\xc0\x65\x01\x28\xf9\xb2\x46\x50\x12\x96\x4a\xd9\x24\x20\x17\x11\xf9\xf3\x2f\xb0\x4d\x00\x60\xef\xb9\x90\xbc\xc1\xab\xf4\xe2\x73\xea\xad\x54\x82\xd9\x18\x8b\x8d\xb0\x35\x90\x61\x5c\x58\x7a\x44\x60\xec\xa1\x23\xb4\x90\x5e\xf4\xdd\xd2\x5f\xc1\xae\x50\x7a\x47\xf7\x85\x62\xa5\x46\x10\x20\x03\xbc\xd6\xc8\x8b\x0d\xe8\x4e\x4a\x92\x55\x08\x84\xb5\xc1\x73\x8e\x52\xd9\xbd\xd3\xc7\x90\x21\xc9\x2a\xcb\xb2\xf4\xe0\x38\x90\xeb\x11\x63\x89\xf0\xee\x5d\x0f\x12\x4b\xf3\x7a\x1a\x2f\x29\xda\x2b\x00\xd3\x09\x81\xc6\xf4\x74\x0c\xb2\x39\xfa\xdf\x86\x70\x56\x45\x6d\x03\xbe\x9e\xbb\xf7\x58\x93\x85\xcf\x07\x53\x49\x49\xfc\xb1\x73\xad\xff\x7d\x85\xe2\xde\xa7\x1b\x1b\x8e\x6b\x32\xd6\xf8\x7e\xc7\x51\x20\x9b\x08\x87\x5a\x70\x59\x2c\xfe\x7f\xd3\x6b\x32\x96\x75\x92\x2c\x2b\xa9\x46\x03\x4f\x50\x69\x6c\x81\x3d\x8c\xca\x91\xc5\x3f\xc6\xd5\x1b\x46\x1e\x97\xfa\x6d\xbd\xdf\x3b\x17\x0a\xc3\x14\xf8\x6c\x3f\x82\xb9\xa7\xb6\xed\xcf\x40\x28\xcf\x4b\x69\x43\xba\xdd\x66\xd3\x4e\x5a\x6a\x70\xb7\x4b\x5f\xc1\xad\x54\x67\xf0\x1e\xb1\x45\xcd\x0a\x8e\x8d\x92\xa9\xaf\xba\x92\x25\x55\x9d\x46\x97\x22\x08\x4d\x0a\x84\x92\x96\x93\x44\xed\x26\xd3\x11\x27\x54\xc2\x1d\xb0\x12\xd2\x1c\xad\xc8\x1d\xc8\x7f\xcb\x84\x92\x65\x0a\xf3\x5e\x79\x3c\x72\xa8\x09\xae\x20\x75\xe8\x21\x30\x82\x0f\x85\x9f\xdd\x39\xcc\x2c\xa3\x86\x57\x38\x9b\xa7\x70\x22\xd4\x89\x21\xee\x53\x7c\xbf\xbb\xbb\x34\x2d\x17\x78\x39\x9f\x7f\x68\x79\x67\x70\xe1\xd9\x9c\x80\x37\xd2\x85\x01\x2a\x80\x11\xbc\x37\x4f\xdf\x87\x24\xd9\x87\xa7\x11\xeb\x76\x9b\x7d\x75\xbf\x4f\xb1\x22\x63\xf5\x66\xb7\xcb\xb7\xdb\xec\x6f\x07\xf2\xf6\xdd\x2e\x7d\x7a\x7f\x2a\xf4\x70\x2d\xc6\x9b\xd5\x57\x91\x8f\x0a\x93\x73\xf8\xef\x22\xce\x6b\x88\xbb\xf8\xca\xa2\x7b\x19\x41\xc5\x3c\x85\x2f\x5f\xce\xa7\xe5\xdd\x86\x62\x67\x6f\x50\x3b\x4b\xcf\xd2\xf7\xd4\x1e\xb7\x5a\x63\xb8\x40\x0e\xbe\xdf\x9c\x92\xdc\x8c\xff\x41\xc6\x1f\xa9\x9b\xeb\x6f\x24\xbb\x75\x12\x94\x05\xab\x3b\x76\xd1\xee\x57\xee\xd0\xfd\xc9\xcd\xf5\xb7\xaf\x7f\xdd\xfe\x73\x85\xb2\x54\x5a\x90\xac\x0e\x96\x22\xf0\x15\x93\x2a\x16\xd6\x60\xed\x08\x72\xe1\x97\x29\x31\x68\x83\x13\xc2\xa7\xc1\x65\x9b\x62\x8d\x3c\x26\x7a\x3b\x75\xaf\x0f\x60\xd3\xda\x4d\x5c\x30\x19\x36\x67\x08\x72\x1b\xd4\x5f\x9c\x09\x5c\xaf\x51\x74\x16\x41\xe3\x92\xc7\x56\xe9\xb6\x61\xca\x58\x8d\x7b\x2b\x30\x86\xeb\x16\x35\x35\x28\x2d\xaf\x21\xfc\x93\x75\xf2\x11\x35\x95\x84\x05\xf3\x2d\xb9\x2c\x94\xb8\x47\x7d\x99\xe7\xa7\x02\x03\x63\xcb\x4d\xcb\x8d\x61\x85\xa6\x47\xd4\x31\xfe\x21\x1d\x77\x31\x62\xb8\x1f\xdc\xec\xdf\x8c\xb2\xab\x8f\xa7\xe0\xe2\x37\x60\xf8\x00\x9f\x9e\xed\x7e\xe8\xc1\x34\x78\xab\x16\x35\xf7\xcf\xbe\x50\x4d\x5b\xa3\xc5\xa2\xc7\x56\x6f\x32\x98\xa2\x7b\xe8\x5d\xb3\x5c\xd0\xd0\xf4\xc1\x33\xd9\x9f\x03\xff\x99\xe0\x85\x2b\xfc\x2c\x62\xc9\xa9\xc6\x22\x83\x1b\x4f\x00\x3f\xa8\xae\xfd\x3d\x5e\x62\x64\xc2\xe2\x78\x89\x93\x03\xe3\x9e\xed\x74\x43\x8f\x77\xfc\x79\x6b\xb2\xd4\x8d\xe5\xbf\x01\x00\x00\xff\xff\xd0\xd1\x38\x68\xf6\x08\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x6c\x50\x4d\x6f\xe5\x36\x0c\xbc\xeb\x57\x10\x31\x82\x9c\x64\xf7\x50\x14\xbd\x18\x68\x90\x0f\x34\x68\x1b\x04\x4d\xd3\xdd\xd3\x2e\xf4\x24\x3e\x5b\xb0\x4c\x1a\x12\xed\x17\xc7\x78\xff\x7d\x21\xc7\xf9\xc4\x9e\x3c\x1a\x72\xc6\x9c\x29\x26\x8c\xc9\x33\xd5\x97\x57\xff\x5f\xfd\xad\x7c\x43\x1c\xd1\xf9\xd4\x81\xd6\x4c\x61\xd6\x63\xc2\x3a\x39\xa3\xcc\x28\x3c\x98\x28\xa0\xb5\xcc\x03\xd6\x61\xea\x55\x01\x77\x26\x8a\x17\xcf\x04\x36\xa0\x89\x9e\x1a\xf0\xb4\xe7\xd8\x9b\x4c\xaa\x95\xdc\x54\xc4\x84\xa0\xb5\x27\x2f\xc1\xec\x30\xa8\x02\x1e\x12\x42\x13\xcd\xd0\x7a\x6b\x02\x78\x4a\x62\x42\x50\xaf\x8c\x2a\xe0\x2f\x9c\x77\x6c\xa2\x83\x60\x66\x1e\x25\xa9\xee\x85\xd0\x7a\xb2\x1d\xce\xbd\x19\x6a\x4b\xa0\xf5\xe3\xb6\x51\x9f\x59\x3a\x53\x05\xdc\xcf\x49\xb0\x87\x60\xa8\x19\x4d\x83\x2a\x03\x78\x6a\xbf\x5f\xdc\x96\x0f\xff\x5d\xeb\xdf\x95\x2a\xe0\x16\xe5\xc0\xb1\xfb\x70\x32\x6d\x1c\x68\xbd\x63\x96\x21\xb2\x70\xed\x5a\x3b\x80\xd6\x0e\x27\x6f\xb1\x46\x1a\x7e\x4d\xbf\xe4\x2c\xc3\xf4\x5b\x9d\x8b\x01\xad\x8d\x15\x3f\x19\xc1\xf7\x06\x2d\x27\x21\xd3\x63\xbd\x2c\xe5\x9f\x1b\x3e\x1e\x55\x01\xff\x32\x0b\x0c\x26\xa5\x03\x47\xa7\x62\xfe\xcf\x21\xfb\x25\x1b\xe7\x41\xd0\xc1\xb2\x94\x77\xdb\xf8\x59\x30\x12\x48\x8b\x70\x8f\x32\x0e\x70\xde\x20\x09\x30\xc1\xde\xc7\x24\x90\xef\x54\x2b\xcc\x08\xb4\x46\x32\xbb\x80\xaa\x80\x4b\x06\x62\x01\xcb\xb4\xf7\xcd\x18\x71\xf5\xf8\x0a\x5f\x3c\x39\x3e\x6c\x15\xa9\xd4\xf9\xe1\xf1\xad\xb1\x84\x31\xa7\x4c\xea\x05\xe4\xe0\x3e\x65\x47\x57\x9f\xd8\x36\x32\xcd\xee\xe4\x6d\x5f\x7c\x8f\x4f\x4c\xa8\x5e\x00\x9c\x27\x6f\xaa\xfb\xd6\x50\xd3\x1a\x0f\x5a\x8f\x62\x95\x3a\x1d\x8c\xed\x4c\x83\x49\xfd\xf1\xad\xf7\xe4\x7b\x13\x34\xd2\xe4\x23\x53\x8f\x24\x4a\x9d\x22\xb9\xbc\xc6\x29\x47\x08\xdc\xd4\xd5\x64\x62\x15\xb8\xa9\xba\xa4\x33\x5d\x06\x6e\x54\xdf\x39\x1f\x41\x0f\x50\xa1\xd8\x8a\x3a\xb7\xf7\x01\x53\xd5\x32\x77\xcf\xe8\xd3\x46\x5a\xaf\x74\xdb\xb7\xea\xc6\x1d\x06\x94\x72\x0b\x57\x3a\xa5\x96\xc5\xef\xa1\xbc\x49\x97\x6c\x3b\x8c\xa0\x8f\xc7\x4f\x16\x6e\x1d\xa8\x65\x41\x72\xeb\xf8\x55\x72\x93\xc6\x60\xdc\x4f\x24\x7e\x1d\x7c\x94\x44\x43\x0d\x42\x79\x9d\xaf\x3c\x1e\xd5\xb2\x94\x17\x4c\x82\x24\xdb\x23\x17\x86\xff\xb0\x5b\x9f\x48\xee\x83\xea\xb9\x6c\xb7\x09\xdf\xad\xe4\xda\x7e\x04\x00\x00\xff\xff\xef\xff\xc4\x92\xc5\x03\x00\x00"), }, - "/ignition/worker/files/etc/sysctl.d": &vfsgen۰DirInfo{ - name: "sysctl.d", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), + "/kickstart/worker": &vfsgen۰DirInfo{ + name: "worker", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/ignition/worker/files/etc/sysctl.d/kubernetes.conf": &vfsgen۰CompressedFileInfo{ - name: "kubernetes.conf", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 572829827, time.UTC), - uncompressedSize: 97, + "/kickstart/worker/kickstart.cfg.template": &vfsgen۰CompressedFileInfo{ + name: "kickstart.cfg.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), + uncompressedSize: 1136, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xca\x4b\x2d\xd1\x4b\x2a\xca\x4c\x49\x4f\x85\x52\xba\x79\x69\xba\xc9\x89\x39\x39\xba\x99\x05\x25\x89\x49\x39\xa9\xc5\xb6\x86\x5c\xf8\x14\x99\xa1\xa8\xca\x2c\x28\x33\xd1\xcb\x2c\x88\x4f\xcb\x2f\x2a\x4f\x2c\x4a\xb1\x35\xe4\x02\x04\x00\x00\xff\xff\x6d\xd4\xf2\x72\x61\x00\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x6c\x52\xc1\x8e\xdb\x36\x10\xbd\xcf\x57\x0c\x6c\x07\xbb\x7b\xa0\xdd\x16\x45\x91\x8b\x8a\x06\xd9\x04\x08\xda\x06\x41\xb7\x69\x7b\x6a\x40\x93\x63\x89\x10\x35\x23\x90\x23\xef\x3a\x8a\xfe\xbd\xa0\x56\x76\xba\x8b\x9c\xf4\xf4\xe6\xbd\xc7\x99\x21\xd7\x47\x4a\x39\x08\x57\xb7\x6f\xfe\x7a\xf3\x1b\x84\x9a\x25\x91\x0f\xb9\x45\x63\x84\xe3\xc9\x0c\x99\xaa\xec\x2d\xd8\x41\xa5\xb7\x49\xd1\x18\x3d\xf5\x54\xc5\x63\x07\x6b\xfc\x60\x93\x06\x0d\xc2\xe8\x22\xd9\x14\xb8\xc6\xc0\x07\x49\x9d\x2d\x24\xcc\xe4\xe2\x62\x61\x42\x63\x02\x07\x8d\x76\x4f\x11\xd6\xf8\x31\x13\xd6\xc9\xf6\x4d\x70\x36\x62\xe0\xac\x36\x46\xb8\x30\xb0\xc6\x5f\xe9\xb4\x17\x9b\x3c\x46\x7b\x92\x41\x33\xb4\x67\xc2\x98\xa3\x6b\xe9\xd4\xd9\xbe\x72\x8c\xc6\x3c\x2c\x8a\xea\xca\xf1\x15\xac\xf1\xee\x94\x95\x3a\x8c\x96\xeb\xc1\xd6\x04\x05\xe0\xe7\xe6\xd3\xeb\xf7\xdb\x8f\x7f\xbe\x35\x2f\x01\xd6\xf8\x9e\xf4\x5e\x52\xfb\xa4\x65\x5e\x38\x34\x66\x2f\xa2\x7d\x12\x95\xca\x37\xae\x47\x63\x3c\x1d\x83\xa3\x8a\xb8\xff\x31\x7f\x57\x66\xe9\x8f\x3f\x55\x65\x31\x68\x8c\x75\x1a\x8e\x56\x09\xd6\xf8\x87\x88\x62\x6f\x73\xbe\x97\xe4\x21\x95\x94\xfb\xa2\xce\x2e\x9d\x7a\x25\x8f\xe3\xb8\xfd\xb0\x94\xa7\xa9\x18\x06\x46\x6d\x08\xef\x48\x87\x1e\x5f\xd5\xc4\x8a\xc2\x78\x08\x29\x2b\x96\x2e\x60\x86\x05\xa1\x31\xc4\x76\x1f\xcb\x39\xb7\x82\x2c\x8a\x4e\xf8\x10\xea\x21\xd1\x9c\xf1\x0f\xfe\x1d\xd8\xcb\xfd\xb2\x00\xc8\x6d\xe8\x1f\xbe\xee\x23\x53\x2a\x33\x64\x38\x83\x32\x56\xc8\x25\xd1\x57\x2b\xd7\x24\xe1\x93\x5f\x7d\xd5\x6b\xe8\xe8\xb3\x30\xc1\x19\xe0\xab\x1c\xec\xee\xae\xb1\x5c\x37\x36\xa0\x31\x83\x3a\x80\x17\xbd\x75\xad\xad\x29\xc3\x2f\xff\x76\x81\x43\x67\xa3\x21\x3e\x86\x24\xdc\x11\x2b\xc0\x0b\x62\x5f\x64\x92\xcb\x08\x51\xea\x6a\x77\xb4\x69\x17\xa5\xde\xb5\xd9\x14\x7a\x1b\xa5\x2e\xe7\x92\xce\x73\x34\x92\x95\x6d\x47\xd0\x27\x3a\x84\x87\x6a\xd5\xbe\xcc\x66\x05\xc9\xb2\x97\xee\x53\xd6\xf2\xd6\xaa\xcd\xf5\x30\x04\x5f\x13\xe3\x17\xd4\x84\xc6\xe3\x95\xb9\xc2\x2f\xd8\x90\xf5\x68\x1c\x6e\xae\xaf\xbf\xff\x01\x0d\x6e\xc6\xf5\x63\xcc\x74\x73\x73\x03\xe7\xe8\x6a\xb5\x19\x17\x7a\x33\x3e\x09\x9e\x56\x40\xae\x11\xdc\x9c\xa5\xf8\x33\xee\x48\xdd\xee\xd2\xd5\x19\x38\x8d\x98\x49\xcd\x45\x78\xb1\x00\x74\xad\x0f\x09\x4d\xff\x68\xe5\xd6\x1f\x42\xa4\xbc\x6b\x44\xda\x47\xf4\x4c\x91\xe7\x95\xfb\xe5\xbb\x6b\x87\x3d\x45\xd2\xed\x72\x53\x5b\x0f\x30\x8e\xe1\x80\xdb\x77\xf9\x56\x5c\x4b\x09\xcd\x34\x3d\x8b\xf0\x73\x01\xc6\x91\xd8\xcf\xe5\x8b\xe5\x5d\x1e\xa2\xf5\xdf\xb0\x84\xb9\xf0\xd4\x92\x2c\xd7\x84\xdb\xb7\xa5\xcb\x69\x82\x71\xdc\xbe\x16\x56\x62\x5d\x7e\xca\xed\xd3\xef\xe2\xe7\x5f\x62\xff\xc4\xf5\xf8\x72\xfc\x62\xfc\x9f\xa4\xbc\x81\xff\x02\x00\x00\xff\xff\xe6\xa2\xf2\x8b\x70\x04\x00\x00"), }, - "/ignition/worker/files/etc/systemd": &vfsgen۰DirInfo{ - name: "systemd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), + "/terraform": &vfsgen۰DirInfo{ + name: "terraform", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/ignition/worker/files/etc/systemd/system": &vfsgen۰DirInfo{ - name: "system", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), + "/terraform/generalos": &vfsgen۰DirInfo{ + name: "generalos", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/ignition/worker/files/etc/systemd/system/kubelet.service.d": &vfsgen۰DirInfo{ - name: "kubelet.service.d", - modTime: time.Date(2024, 3, 18, 8, 6, 16, 850949118, time.UTC), + "/terraform/generalos/libvirt": &vfsgen۰DirInfo{ + name: "libvirt", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/ignition/worker/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template": &vfsgen۰CompressedFileInfo{ - name: "10-kubeadm.conf.template", - modTime: time.Date(2024, 3, 18, 8, 27, 59, 406471550, time.UTC), - uncompressedSize: 1086, + "/terraform/generalos/libvirt/master.tf.template": &vfsgen۰CompressedFileInfo{ + name: "master.tf.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), + uncompressedSize: 3400, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x53\xd1\x4e\xdb\x50\x0c\x7d\xef\x57\x58\x01\x89\x87\x91\x54\xbc\x4e\xca\x43\xb7\x15\x34\xb1\x01\x2a\xa0\x4d\x9a\x26\xe4\x24\x4e\x6a\xb8\xbd\x2e\xbe\x4e\xa0\x42\xfc\xfb\x74\x13\x5a\x8a\x56\x78\x8a\xe3\xf8\x9e\x73\xae\xcf\xc9\x1e\x9c\x89\xd1\x67\xb8\x9a\x73\x80\x4a\x65\xc9\x1e\xc4\xbb\x15\x3c\x88\xde\x05\x78\x60\x9b\xc3\x5d\x5b\x10\x56\x0b\x40\x5f\xf5\xb5\x23\x83\xee\x28\x3b\x3a\xfa\x34\xfa\x73\x49\xda\x71\x49\x7f\x47\x53\xdf\xb1\x8a\x5f\x90\xb7\x3c\x39\xbd\xfe\x32\xfd\x31\xbd\xba\x89\xcf\xaf\xe7\x67\xc7\xdf\x4f\x6e\x26\xb3\x93\xcb\x3c\x4d\x0b\x11\x0b\xa6\xb8\x4c\x23\x52\x29\xbe\xe6\x26\x1f\x93\x95\xe3\xf8\xae\x9e\x8c\xc2\xf8\xed\x90\x23\xcb\xe2\x20\xa4\x1f\x9d\xd9\x9e\x4c\x76\xab\x79\xab\x64\x8d\xd3\xa1\x8e\x1d\x17\x6b\x80\xf1\xd0\xcf\x56\xb8\x70\xc9\xe8\xe9\x09\xb8\x06\xba\x87\x6c\xd6\x7a\xe3\x05\x41\x52\x2a\x4b\x02\xcf\xcf\xef\x70\x9c\xcc\xce\xaf\x2f\x7e\xfd\xdc\xb0\x34\x2a\xed\x32\xa4\x4b\xd2\xf4\x5e\x42\x5e\xa3\x0b\x04\x69\x4a\xbe\x16\x2d\x29\xf5\x52\x51\x8a\xce\x49\x89\x86\x85\xa3\xfc\xe0\xa0\x67\x25\x5f\x45\x8e\xbd\xc1\x18\x0e\x80\x50\xb3\x23\xb0\x39\x1a\x24\x6b\x47\xd8\xb3\x25\xbd\x2f\x9b\xd6\xad\xb0\x4f\xa0\x21\x4f\x8a\x46\x01\xd0\x40\x07\xe9\x87\xb0\x94\x65\xeb\xd0\xd8\x37\x60\x73\x82\x6d\x97\x26\xdf\x06\xc9\xd0\xa1\x72\x14\x02\xd5\xca\xe3\x82\x4b\x74\x6e\xb5\x7d\xd5\x63\x76\x94\xa7\xff\x6d\xed\x85\x3e\xad\x1d\x36\x21\x23\xdf\xbd\x23\x3d\xf2\xb6\x81\x14\x4a\xf4\xb1\x80\x5a\x14\xa4\x23\x55\xae\x28\x80\xd4\xfd\xc4\x3a\x65\xa8\x4d\x00\x8c\x08\x0e\x83\x81\x52\x10\xb5\x0c\x2e\x94\x6a\x52\x2c\xdc\xea\xf0\x15\x30\xcc\xa5\x75\x55\xac\x47\x7b\x7d\x37\x3b\x93\x8a\x66\xd4\x70\x8c\x92\xb1\xf8\xec\x74\x80\x9d\x3e\x9a\xe2\x24\x42\x4b\x71\x4b\xa5\x01\xfb\xfe\xc0\xe0\x7c\x3b\x0c\xf7\x9a\x03\xb0\x0f\x46\x58\x65\x9b\x65\x4d\x7f\x5f\xcd\x26\xc3\xaa\x5e\x18\x0b\x82\x20\xad\x96\x54\x41\xad\xb2\x00\x8b\xd7\x8e\xa7\xb3\x1d\x7b\x8b\xa9\x0d\xab\x30\x50\xad\xb7\x37\x9a\x3e\x52\x79\x69\xa8\x96\x6f\x95\xe3\x36\xe8\xb8\x60\xbf\x9e\x82\xfd\x77\x7e\xab\xd7\x0f\x3b\x9b\x6f\xec\xdd\xdf\x71\x8f\x0f\x42\xbe\xbf\x33\xd7\x9b\x7c\xfe\x0b\x00\x00\xff\xff\x21\x0d\x51\xd3\x3e\x04\x00\x00"), - }, - "/ignition/worker/systemd": &vfsgen۰DirInfo{ - name: "systemd", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x57\xdf\x6b\xe4\x36\x10\x7e\xf7\x5f\x31\x15\x7d\xb8\x0b\x8d\x92\x96\xa3\x85\xc2\x52\xda\x04\xda\x3c\x1c\x0d\x69\xef\xe9\x08\x46\xb1\x67\x77\x45\x64\xc9\xd5\x0f\xa7\xdb\xc5\xff\x7b\x91\x2c\xf9\xd7\xda\xb9\xdc\x1e\x5c\x64\xcb\x33\xdf\x7c\x9a\xd1\x37\xd2\x5a\xd4\x9a\x6d\x95\xae\xe0\x98\x01\x68\xfc\xc7\x71\x8d\x65\x5e\x6b\xd5\xf0\x12\xb5\x09\xd3\x00\x82\x3f\x35\x5c\x5b\xd8\xc4\x77\x00\xa3\x9c\x2e\x10\x36\x40\xca\x8a\x15\x0d\x2f\x98\xbe\x8a\x56\x24\x9a\x34\xa8\x0d\x57\xd2\xdb\x5c\xd3\x9f\xe8\x8f\xdd\x7c\x9b\xf9\xff\x6d\x96\xa5\x18\x40\x92\x5f\x00\x77\x9a\x7b\x8f\xe3\x91\xde\x0b\x66\x3d\x35\xfa\xe9\xe1\xae\x6d\x89\xf7\x69\x98\xe6\xec\x49\x20\x90\x42\x38\x63\x51\xe7\xbc\xec\xdc\xec\xa1\x46\x0f\xbf\x01\x63\x35\x97\xbb\x0c\xa0\xc4\x2d\x73\xc2\x46\xb4\x9b\xce\xe1\xee\xf6\x04\x8a\x4b\x63\x99\x2c\x30\x2f\x94\x93\xf6\x8d\x70\x1f\x99\x47\xa3\x37\xde\x65\x1d\x71\xaf\x8c\x95\xac\xc2\x39\xa8\xe0\xc6\xbe\xeb\x90\xdf\x4f\xa0\x07\xe4\x3f\xa2\x6b\xdb\xae\x60\xf3\xfa\x0c\xd4\xbb\xfb\x39\x5e\x9f\xc8\xba\xf9\x90\x17\xbc\xd4\x6f\x4c\x40\x5f\x9d\x9b\xbb\xdb\x87\xf5\xf2\x78\xd4\x1d\xb3\xf8\xc2\x0e\x5f\x0b\xfc\x7b\xe7\xf6\x4a\xbd\x6a\x77\x46\x0a\x6e\xee\x3f\xad\xe6\x54\xb3\xea\x0c\xc4\x87\x5f\x3f\xae\x22\x96\xdc\x3c\x9f\x01\x79\xcb\xcd\xf3\x2a\x66\x21\x94\x2b\xb9\xe4\xf6\x0c\xe0\xdf\x94\xb2\x37\x4a\x6e\xf9\xae\x83\xd7\x18\x95\x9c\x54\x98\xd7\x4a\x09\x02\xa4\x1b\x3c\xbe\xdf\x85\xbe\x36\xdf\x1e\x1b\xa6\xe9\x20\xbc\xf6\x32\xd8\x24\x06\xbe\x15\x70\xed\x5f\x6b\x66\xf7\xfe\xf5\xaa\xe9\x9a\x42\x6a\x0c\x57\xbc\x62\x3b\x34\x57\x27\x40\x64\x99\x49\xa3\x84\xf3\xda\x21\xe9\xa1\x67\x03\xcb\x7c\xa2\x9d\xa7\xa0\x94\x88\x29\x19\x56\x45\xc3\x1f\x0f\x90\x8d\x1b\xd8\x78\xcf\xfd\xf9\xd7\x9d\xe7\xd8\x7e\x91\xd2\x50\xd7\xd0\x35\xa0\xff\xb7\x01\xcf\x6a\xda\x53\x06\xda\x83\x55\xa4\x7f\xd2\x2a\x3e\x07\x0f\xca\x65\x89\xff\x3e\xb6\x97\x21\x4e\x06\xf0\xc4\x0c\xc6\xe8\x39\x2f\x47\x0b\xeb\xe6\x68\x1c\x78\x39\x2c\x7e\x88\xb5\x9e\x04\xfe\xdf\x94\xd5\x84\x92\x8f\x3d\xa1\x03\x17\xf0\xfd\xf5\x0f\x1f\xa6\xc3\x72\xa2\xfa\x3d\x1a\x15\x40\x66\x9b\x76\xc8\xda\x17\x13\xf6\xd6\x5c\x51\x6e\x14\x99\x2c\xff\x95\x95\x3b\x83\x3a\x2f\x99\x65\xb0\x01\x3f\x50\x8b\x55\x2d\x98\xc5\x7c\xcb\x05\xd2\xfe\x33\xbd\xa0\x1a\x65\x89\x1a\xcb\x49\x34\xbf\xea\xe0\x4e\x26\x8e\x04\x48\xef\x3a\x5b\xe8\xca\x3a\x93\x3b\x6c\xfa\x47\x0f\xf4\x6e\x6a\x9c\x92\x37\xe1\xf0\x1d\x1c\x21\xa5\x62\x0e\xbf\x98\x22\x68\xdf\x2f\x57\x4b\xa2\x7d\x51\xda\x97\xa9\x7f\x3a\x2e\xd7\x60\x2c\x37\x89\xe1\xac\xaf\x54\x39\x58\x49\x16\xe6\x4a\x55\x31\x2e\xd7\x3c\xa9\x50\x05\x0b\x9d\x83\x95\xa5\x46\x63\xd0\xc0\x06\x3e\x4f\xac\xd2\x71\xf4\x98\x79\xb8\x7d\x51\xc7\xab\x07\x4a\xdf\x0d\xbd\x02\xac\x76\x18\x2e\x13\xde\x40\x9a\xd9\xf7\x10\x3a\x9a\x00\x84\x80\xb9\x92\xe2\x30\x73\xd4\xca\x59\x4c\xbe\x3e\xde\x50\xaa\x13\x2a\xc1\x26\x1e\x66\x4b\x36\xf1\x53\x82\x66\xce\x2a\x63\x59\xb8\x36\x85\x90\x8b\xa9\xef\x32\x45\x80\xec\x50\xa2\x66\x42\x99\xc5\xb6\xf2\xc6\xbe\xf2\xa6\x6d\xe0\xd1\x6b\x17\xd7\x1c\xaa\xb7\x01\xe2\x2d\x2f\x6b\x66\x8c\xdd\x6b\xe5\x76\x7b\x92\x75\x77\xb5\xc6\x9b\xbe\xc2\xa3\x76\x73\xec\x0a\x2b\xa5\x0f\xab\x1e\x9a\x55\x27\x6c\xd2\xf6\x3e\xe9\x58\xd3\x36\x42\xfb\x57\x7a\x41\x79\x79\x12\x98\x15\x7b\x2e\x71\xdc\x64\xc3\xc1\x17\x66\xff\x3e\xd4\xa1\xad\x8f\xeb\x92\xcc\xe2\x86\x48\x07\xe9\xc8\xff\xb9\xa9\x48\xd8\x5f\xdc\x3c\xc7\x84\xbd\xd2\x84\x03\xc7\x25\x66\x61\x3f\x44\x69\xe5\x5c\x5a\xd4\x5b\x56\x60\x04\x4c\xf3\xfd\xc9\x36\x53\x25\xed\xc7\xae\x75\xc1\x20\xfa\xaf\xaa\xf9\x58\x6d\x4b\x7e\xbc\x9e\x36\x8b\x6f\xbc\x9c\x9d\x10\x04\x7e\xe9\xb4\xb9\x66\xf9\x08\x3f\x83\x37\x0c\x31\x5e\x18\xb7\xf9\x56\xe9\x5c\x20\x33\x38\x53\xdb\x4e\xb3\x7a\xcf\x8b\xa4\xb7\x71\xc2\x37\x40\x1a\x59\x90\xf8\x73\xc3\x58\x94\x79\xba\x56\x44\xda\x24\xa1\x14\x4a\x1a\x25\x70\x19\xa4\xb6\x87\x0e\xc4\x32\xbd\x43\xdf\xf9\x83\x00\xc9\x35\x49\x3f\x3c\x94\xb3\xb5\xb3\x40\xbc\x66\x3b\xad\x35\x4c\x38\x1c\x25\xbe\xd3\x24\xed\x15\x49\x2f\xe8\x49\xf1\xe8\x35\xed\xd3\x99\xb5\xff\x07\x00\x00\xff\xff\x02\xa3\xa5\xb2\x48\x0d\x00\x00"), }, - "/ignition/worker/systemd/join-worker.service.template": &vfsgen۰CompressedFileInfo{ - name: "join-worker.service.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 572, + "/terraform/generalos/libvirt/worker.tf.template": &vfsgen۰CompressedFileInfo{ + name: "worker.tf.template", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), + uncompressedSize: 2699, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x90\xb1\x6e\xdb\x30\x10\x86\x77\x3e\xc5\xc5\x43\x36\x4a\xed\xd0\xa9\xd0\x90\xba\x01\x1a\xa0\x83\x61\x37\xe8\x60\x78\xa0\xa9\xdf\x16\x2b\x9a\x54\xef\x4e\x4e\x02\xc3\xef\x5e\x50\x82\x5b\xa0\x40\x97\x6e\xbc\xfb\x79\x1f\x3f\xde\xf6\x39\x05\xdd\x99\xcf\x10\xcf\x61\xd0\x90\x53\xf3\x92\xb9\x07\x53\xca\x2d\xe8\x47\x0e\x89\xb4\x03\xf9\x38\x8a\x82\xcd\x1a\x3f\xc7\xc0\x90\x46\xa0\xb6\x07\x27\x44\x3b\x38\x76\x95\x80\xcf\xc1\x83\x18\x11\x4e\x60\xc3\xc9\x1d\x61\x87\x70\xce\x7a\xcb\xcc\xc3\x41\xc1\xff\x35\xb9\xcc\xa9\x0d\xc5\x6e\xe5\xb4\x7b\x7c\x0d\xa2\xd2\xdc\xd5\x67\xc7\x75\xcc\xc7\xba\x58\xda\x59\xbb\x12\x75\xa7\xc1\x98\xed\x66\x9e\xdc\x99\xc7\x57\xf8\x8d\x3a\xd6\x15\xa3\xa9\xf7\x21\xd5\x7b\x27\x1d\x59\x4f\x8b\x97\x2e\x44\xd0\x96\xee\xc8\x1e\xe8\x37\xad\x7c\xfc\xf6\x7c\x81\xd1\xee\x23\xb5\x99\x24\x02\x03\xbd\x7f\x57\x8a\x84\xc5\x1f\xee\x5f\xd0\x7e\xdc\xc3\xb5\xa7\x79\x75\x97\x4b\xf5\xb0\x7a\x2a\x2e\xe0\xe7\xf5\xd7\xeb\x95\xac\xd5\xdc\x63\x4a\xbe\x95\xc3\xd4\x6a\x83\xf8\x7c\x06\xbf\xcd\xa1\xf5\xce\x7a\xb0\xda\xae\x40\x2f\x97\x6a\xe9\x96\x60\xfd\xe2\xa4\x9b\xae\x7b\x0e\x56\xb2\xef\xa1\x4d\x09\x39\x6c\xa6\xe2\x7a\x25\xba\xbf\x27\xcd\xa3\xef\xe8\xdf\xcb\x59\x98\x35\x64\x12\xcf\xc9\x1e\x5c\x88\x23\xe3\xd6\xda\xc0\x37\x1f\xc4\x98\xed\x53\x12\x75\x31\xee\xcc\x77\x97\x14\xed\xa7\xb7\xe6\x34\x46\x0d\x76\x14\x70\xa5\x8e\x8f\x50\xf3\x2b\x00\x00\xff\xff\xee\x62\x97\xde\x3c\x02\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\x51\x8f\xdc\x34\x10\x7e\xcf\xaf\x18\x2c\x1e\xda\x13\x67\x0e\x54\x81\x84\x14\x21\xb8\x3e\x70\x0f\x15\xa7\x42\xc5\x43\x55\x45\xbe\x78\x76\xd7\x5a\xc7\x0e\x63\x3b\xe5\x58\xe5\xbf\x23\x27\x4e\x36\xd9\x4d\x96\xeb\x9e\x74\xda\xc4\x9e\xef\x9b\xcf\x9f\x67\x1c\x7b\x24\x12\x1b\x4b\x15\x1c\x32\x00\xc2\xbf\x83\x22\x94\x45\x4d\xb6\x51\x12\xc9\x75\xc3\x00\x5a\x3d\x35\x8a\x3c\xe4\xe9\x1d\xc0\xd9\x40\x25\x42\x0e\x4c\x56\xa2\x6c\x54\x29\xe8\xdb\x14\xc5\x52\x48\x83\xe4\x94\x35\x31\xe6\x8e\xff\xc8\x7f\xe8\xc7\xdb\x2c\xfe\xb7\x59\x36\xe4\x00\x36\xe0\x3a\xf2\x40\x2a\x22\x0e\x07\xfe\xa8\x85\x8f\xd2\xf8\x87\xf7\x0f\x6d\xcb\x22\xa6\x11\xa4\xc4\x93\x46\x60\xa5\x0e\xce\x23\x15\x4a\xf6\x30\xff\x5c\x63\xa4\xcf\xc1\x79\x52\x66\x9b\x01\x48\xdc\x88\xa0\x7d\x62\xbb\xef\x01\x0f\x6f\xcf\xa8\x94\x71\x5e\x98\x12\x8b\xd2\x06\x93\x54\xcc\xb1\x7f\x59\xda\x23\xf1\xfb\x38\xbf\x0e\xdf\x59\xe7\x8d\xa8\xf0\x54\x90\x56\xce\xbf\xea\x55\xbd\x9e\x51\x1f\x99\x7f\x4b\xd0\xb6\x5d\xe1\x56\xf5\x15\xac\x0f\x8f\xab\x7c\x65\x1d\xae\x20\xbc\x7f\xfc\xb0\xca\x48\xa2\xba\x82\xf1\xfd\x2f\xef\x56\x19\xa5\x72\xfb\x2b\x28\xdf\x2a\xb7\x5f\x5f\xb7\xb6\x41\x2a\xa3\xfc\x15\xc4\xbf\x5a\xeb\xef\xad\xd9\xa8\x6d\x4f\x4f\x98\x9a\x60\x28\xe0\xa2\xb1\x3a\xc4\xfd\x67\xc3\x43\xcc\x11\xf7\xb5\x4b\xc1\xbe\x3e\x34\x82\xf8\xb1\x72\xdb\xdb\x14\x97\x01\xd4\xd6\xea\xb5\xa8\x38\x17\x63\x8e\x3d\x37\xed\x8e\xdf\xff\x78\xa8\xc4\x16\x53\x5d\x5e\x10\x75\xf4\xb3\x2b\x74\x98\xfc\xe5\x10\x73\xce\x1b\xe1\x28\x7d\x1a\x97\xe4\x9d\x15\xfd\xc7\x0e\xc3\x95\x91\xf8\xcf\xa7\xf6\xb6\xcb\x95\x01\x3c\x09\x87\x49\x41\xd1\xb1\xbd\xcc\x86\x85\x8c\x8b\x86\xa8\x7f\x4f\xf5\xcd\xc4\x45\x15\x33\x61\x70\x03\xdf\xdd\x7d\xff\x66\xfe\xb3\x6c\xdb\x58\x29\xa9\x0e\xd9\x49\xe9\x1c\x3d\xfc\x5f\xf3\x5e\xea\x1a\x57\xce\xce\x4d\xb8\xb8\xfa\xe0\x90\x0a\x29\xbc\x80\x1c\xe2\x0f\xf7\x58\xd5\x5a\x78\x2c\x36\x4a\x23\x1f\xa7\xf9\x0d\x27\x34\x12\x09\xe5\x2c\x5f\x5c\x77\x07\x67\x33\x20\x03\x36\x42\x4f\x96\xba\xb2\xd2\x01\x0e\xf9\xf8\x18\x89\x5e\xcd\x83\x07\xfb\x66\x1a\xbe\x81\x03\x0c\x66\x9c\xd2\x2f\x9a\x04\xed\xeb\xe5\xfd\x92\xb6\x12\xca\x30\x60\x5b\x34\x48\x42\x5b\xb7\x5c\xeb\x2f\x2c\xf5\x97\x68\x89\xec\x75\x48\x9f\xc4\xca\xca\xae\xba\x63\xe4\x6d\x2d\x9c\xf3\x3b\xb2\x61\xbb\x63\x59\xff\xcd\x6b\x62\xe8\x05\x1d\x75\x38\xe5\xae\xb0\xb2\xf4\xbc\x8a\x20\x51\x9d\xa9\x19\x3c\x1e\x11\xcb\xd5\xcc\xc7\x57\x7e\xc3\x95\x3c\x4b\x2c\xca\x9d\x32\x38\xed\xc1\xc3\x81\xbf\xeb\x47\xff\x7c\xae\xbb\xb3\x06\x40\x04\x6f\x9d\x17\x74\xcc\xe6\x29\xe0\xe4\x54\x9d\xe0\xf7\x4d\xc5\xb2\x78\xae\x2a\xb7\x4f\x86\xa5\x53\x41\xc9\x89\xcc\x7e\x8c\x77\x1a\x97\x94\xb5\x91\xc3\xa0\xff\x6c\x69\x5f\x28\xe3\x91\x36\xa2\xc4\x44\x38\x8c\x5f\x3a\x70\x0d\xa6\xeb\xc9\x58\x74\x5f\xb4\xdd\x00\x42\x4a\x42\xe7\xd0\x2d\xe2\x54\x3d\x2f\xd6\xaf\x72\x60\x26\x68\xcd\xe0\x67\xf8\x78\x29\xf2\x13\xfc\x04\x31\xb0\xcb\xf1\x59\x28\x5f\x6c\x2c\x15\x1a\x85\xc3\xa3\xaf\xdd\xea\xb7\x24\xea\x9d\x2a\x87\x9b\xd9\xd4\xeb\x1c\x58\x63\x4a\x96\x6e\x6c\xce\xa3\x29\xba\xe9\x1c\x58\x92\xcd\x06\x96\xd2\x1a\x67\x35\x2e\x93\xd4\xfe\xb9\x27\xf1\x82\xb6\xe8\x8b\xda\x76\x57\x3f\x76\xc7\x86\xbb\x9b\x0d\xbe\x0e\x1e\x98\xaa\x9b\x37\x7d\x9b\x35\x42\x07\x9c\xec\x64\xdf\x8e\x7c\x6c\x46\x7e\xc3\xcf\xf6\x8d\xdf\xf1\xd1\xce\xac\xfd\x2f\x00\x00\xff\xff\xfb\x83\xbf\x12\x8b\x0a\x00\x00"), }, - "/ignition/worker/systemd/kubelet.service": &vfsgen۰CompressedFileInfo{ - name: "kubelet.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 325, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\x31\x6e\xf3\x30\x0c\x46\x77\x9e\xc2\x17\xb0\x95\x7f\x0d\xa0\x21\x7f\x93\x21\x68\x51\x14\x75\x8b\x0e\x86\x07\x59\x66\x6d\xc2\x32\x65\x88\x94\x93\xdc\xbe\xa8\xd1\x74\xeb\xfc\x1e\x89\xef\x35\xef\x4c\xda\xc2\x11\xc5\x27\x5a\x94\x22\xdb\x29\x77\x18\x50\xf7\xc5\xdb\x88\xc5\x63\xee\x30\x31\x2a\x4a\xf1\x1c\x7b\x2c\x0e\x03\xb2\xc2\x31\xfa\x3c\x23\xab\xdb\x0e\x46\xd5\x45\xf6\xc6\x4c\xbf\x6e\x45\xd1\xf4\xd1\x8b\x81\x0f\xc7\x2a\x96\x51\x2f\x31\x4d\x65\xe4\x40\x8c\x95\xba\x34\xa0\xc2\xe1\x53\x31\xfd\xc1\x1e\x22\xf7\xf4\xfd\xfd\xc5\xe9\x78\xba\x92\xa8\x58\xb3\xba\x64\x42\x1c\x0c\xc7\x1e\xcb\x85\xd6\xa8\x95\xa8\x9b\x17\x80\xa6\xc6\xb4\x92\xc7\x16\x4e\x57\xf4\xb5\xba\xa4\xd6\x64\x49\xa6\x23\x36\x3f\x41\xf0\x8a\xb2\x01\x17\x2e\xee\x26\xb0\x59\x4f\x34\x93\x9e\x59\x31\xad\x2e\xd8\xdd\xdd\xa9\xd1\xdb\x7f\x3b\x80\xe6\xcc\xa2\x2e\x84\x76\x0b\xc1\xfe\xff\xcd\xce\x39\x28\x95\x59\x30\xdd\xb7\xc2\x57\x00\x00\x00\xff\xff\xcb\xcf\xb3\x78\x45\x01\x00\x00"), + "/terraform/generalos/openstack": &vfsgen۰DirInfo{ + name: "openstack", + modTime: time.Date(2024, 6, 13, 2, 31, 23, 701127080, time.UTC), }, - "/ignition/worker/systemd/release-image-pivot.service": &vfsgen۰CompressedFileInfo{ - name: "release-image-pivot.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 325, + "/terraform/generalos/openstack/master.tf.template": &vfsgen۰CompressedFileInfo{ + name: "master.tf.template", + modTime: time.Date(2024, 6, 13, 2, 43, 36, 979516247, time.UTC), + uncompressedSize: 4738, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x74\x8f\xbd\x6e\xc3\x30\x0c\x06\x77\x3d\x05\x9b\x21\x9b\xa3\xa9\xa3\x86\xfe\x64\xe8\x16\xd4\x28\x3a\x18\x1e\x18\x99\xb1\x09\xcb\x94\x21\xd2\x6e\xfa\xf6\x85\x03\x64\xe8\x90\x95\xc4\x77\x87\x6b\xbe\x84\xad\x75\xef\xa4\xb1\xf0\x6c\x9c\x25\x9c\x78\xcd\x06\x92\x3b\x02\xcb\x60\x03\x81\x8c\x1d\x14\x4a\x84\x4a\xc0\x13\xf6\xe4\xbe\x51\x4c\x83\x90\xfd\xe4\x32\x56\x59\x12\x0b\x1d\x0c\x4b\x4f\xe6\x5e\x2e\x46\xe5\xc1\xef\x2d\x4b\xc7\x9b\xe5\x84\x36\x1c\xaf\xac\xa6\xe1\xc9\xaf\x58\x7c\xca\xbd\xdf\x9c\xd5\xbc\xe9\x0f\x6a\x38\xcd\xce\x35\x35\x95\x95\x23\xb5\xee\x78\xa5\x58\x1b\x16\x0b\xfe\xcc\xe2\xcf\xa8\x03\x54\x11\x76\x9e\x2c\x7a\x19\xbb\x7f\xe3\x01\xf6\x7b\xb0\xbc\xc4\x01\x1e\xc2\x77\xce\x7d\x92\xde\x88\x59\xaa\x0b\x72\x5a\x0a\xdd\x4f\x35\xc5\xf0\xac\xce\x35\x1f\xa2\x86\x29\xb5\xb7\x62\xea\x5e\x7f\xc3\xb4\x24\xe3\x6a\x51\x2a\xf7\x28\xf7\x17\x00\x00\xff\xff\xa4\xb9\x16\x72\x45\x01\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xcc\x58\x5f\x6f\xdb\x36\x10\x7f\xd7\xa7\xb8\x09\x7b\x68\x8b\x45\x55\xec\x34\xc9\x06\x04\x43\x96\x0e\x5b\x80\xb5\x08\x82\xe5\x65\xc5\x20\x30\xd2\xc9\x21\x4c\x89\x1c\xff\xa8\x69\x03\x7d\xf7\x81\x22\x25\x5b\xb2\x14\x3b\x6e\x02\x14\x45\x01\xea\x78\xf7\xfb\xdd\x1d\x79\xbc\x73\x34\x4a\x49\x72\x2e\x0b\x78\x08\x00\x24\xfe\x67\xa8\xc4\x2c\x11\x92\x57\x34\x43\xa9\x1a\x31\x00\x17\x58\x2a\x4d\xd2\x25\x9c\x79\x09\x80\xe2\x46\xa6\x08\x67\x10\x76\x20\x07\xad\xdd\x41\x67\xf0\xb6\x5b\x85\xde\xae\x42\xa9\x28\x2f\xad\xe1\x2c\x8a\xa3\xd8\xc9\xeb\xc0\xfe\xaf\x83\xa0\x85\x80\x70\x65\xd9\x70\x1a\x85\x32\x29\x49\x81\x00\xd6\xf6\xe1\x21\xba\x62\x44\x5b\xda\xe8\x46\xa1\xb4\x3b\x75\x6d\xc1\x04\x51\xea\x33\x97\x19\x6c\x2a\x5e\xf9\x2d\xa7\xa8\xb1\x24\xa5\x76\x98\x03\xc5\xbf\x9b\xad\x8f\x1d\x26\x31\xfa\x2e\x31\x92\x8d\x60\x9e\x1b\x7d\x77\x73\xfd\x97\xd3\x93\xb8\xb0\xb1\xc1\x88\xde\x75\xb3\x65\xd5\xea\x20\xa8\x88\xa4\xe4\x96\x21\x84\x29\x33\x4a\xa3\x4c\x68\xe6\xc2\xd4\x5f\x04\x3a\x6b\xa5\x25\x2d\x17\x01\x40\x86\x39\x31\x4c\x7b\xc0\x0b\x67\x70\xf9\x7e\x03\x8a\xda\x74\x95\x29\x26\x29\x37\xa5\xde\x11\xee\x03\xb1\x68\xd1\x85\x35\x99\x46\xbc\xe3\x4a\xdb\x3c\x0d\x41\x19\x55\xfa\x95\x43\x7e\xdd\x83\x5e\x21\xff\xe9\x4d\xeb\x7a\xca\x5b\x61\xf6\x80\xbd\xb8\xba\x99\x44\x94\xa4\xd8\x03\xf1\xfa\xfc\xc3\x24\x62\x46\xd5\x72\x0f\xc8\xf7\x54\x2d\x27\x31\xb9\xa2\x05\x59\xe0\x8e\xe7\xd4\xdd\xa3\x3f\x98\xb5\x6e\xaf\x66\x0f\x9a\x54\x84\x32\x72\x4b\x19\xd5\x5f\x92\xaf\xbc\x7c\x32\xf6\xf9\x1a\xc0\x3f\xbc\xc4\xe9\x0b\x61\xab\x31\x23\x9a\xec\x91\x94\xdf\x38\xd7\x17\xbc\xcc\xe9\x62\x33\x35\xda\x16\x32\x4b\x4a\xd4\x4f\x75\xfd\xd2\xdb\x7e\x44\xfd\x99\xcb\xe5\xb4\xe7\x54\xec\xe1\xf3\xe5\xd5\xd0\x57\xbc\xdf\xdf\xd7\xdf\xef\xc7\x7c\x95\xe8\x9f\xd4\xd5\xcb\x97\xa4\xbc\x10\x46\x63\x92\x33\x52\x71\x99\x54\xb3\x10\x42\xb7\x76\x9c\x4d\xa5\xfb\xd7\xa6\x22\x32\xea\x3f\x01\x01\x80\x7f\x30\x37\x15\xda\x8a\xfe\xd4\x68\x46\xb4\xcc\xf0\xfe\xdf\x00\xa0\x4a\x85\x51\xa3\x88\xc2\x0c\x75\x25\x29\x60\x14\x5c\x92\x62\xa8\x6b\x2b\x68\x54\xd7\x6e\x0c\x95\xa9\x4a\x84\xb9\x65\x34\x6d\xda\x8b\x34\x38\x99\xa0\x5b\xc6\xd3\xa5\xd2\x5c\x92\x05\x26\x15\x67\xa6\xc0\xa4\x9a\x87\x10\xba\xf5\x7a\x96\x1e\xcd\xd0\x8e\xd9\x51\xf4\x2b\xee\x10\xc1\x96\xd3\x54\x98\x2e\x24\x37\xc2\x9d\x67\xfb\xe5\x7c\x5d\x1d\x58\xd3\x41\x7e\x7c\xb0\x54\xab\x26\x51\x1f\x14\xcd\x8d\x0c\x9b\xdb\xa5\x52\x49\x85\xf6\xdd\xb4\xc5\x81\x9c\x4b\x58\x9e\x2a\x68\x35\xed\x49\x19\x86\xbe\x73\xe7\x92\x17\x89\xe0\x52\x37\x04\xb3\x59\x23\xd4\xbc\x15\xad\x09\xa9\xb0\x83\x80\xe6\x29\x67\xcd\x39\xa4\xc2\xf5\xea\x94\x66\x72\xcd\xc5\x38\x6a\xfe\xbd\x6d\x3a\x79\xfd\x28\xdb\xc1\xe1\x08\x9b\x17\x0e\xd8\x68\x5a\x7c\x33\xdd\x69\x3c\x42\xe7\x85\xcf\x1f\xdc\xd1\xd1\x7c\x84\xae\x95\x3e\x3f\xdf\x6c\x7e\xf2\xf3\xd8\xe1\xcd\x5f\x2c\xc2\xc3\x51\xc2\x56\xfa\xfc\x7c\xc7\xe3\x29\x3d\x7e\xb9\x9c\x1e\xc6\xb3\xa3\xd3\xb1\x18\x3b\xf9\x8b\x70\xbe\x8b\xc7\x39\xdf\xbd\xd4\x49\xce\xe3\x38\x1e\xe3\x9c\xcf\x4e\x8e\x4f\xbe\x17\x4e\x93\xed\xc4\xb9\xe5\xb1\xed\x9e\xe9\xe6\xb1\x6d\xbf\x86\xed\x13\x60\xbc\x93\x6d\xf6\xd1\x29\xcd\xa9\x96\xd1\x0c\x79\x49\xcf\x7e\x60\xe9\x07\xc1\x00\xc0\xb7\xf9\x7e\x0f\xd8\xad\x31\x61\x6a\xa4\x1d\xfb\x9a\x16\xa0\x9c\xe5\xa7\x47\x5b\x4f\xd4\xae\x23\x0b\x68\x41\x36\xe6\xc7\x96\x7e\x63\xa3\xfd\x3d\x66\x47\xc0\xf5\x8c\x68\x2c\x04\x23\x1a\x73\xca\xf0\x55\xcf\xf1\x76\x60\xec\x39\xfe\x13\x3c\x40\x1b\xd1\x4e\x91\x42\xfd\xda\xde\xb1\xd2\x0d\x4e\xfe\x9a\x8d\xa5\x6b\x35\x9c\xb9\x9b\x48\xef\x31\x4b\xa8\x48\xaa\xa3\x21\x0f\x15\x7d\x86\x1f\xce\x20\x2c\x0d\x63\x21\xfc\xfa\xb8\xe2\x2f\x60\xd5\x1e\xbb\x82\xde\x4d\x5a\x2e\x92\x9c\x71\xa2\x69\xb9\xa0\xa2\x1d\xe2\xec\xb7\xd8\x61\x3e\x11\x9c\xb3\x36\xae\xf5\xa1\x73\xfb\xc8\xd8\x31\x12\xa5\x78\x4a\x89\xf6\x35\x90\xaf\x8b\x36\x0b\x61\xc2\x8f\x16\x2f\xa1\x02\xce\x60\x7b\x90\x91\x0f\x31\x7a\x13\x91\x2c\x93\xa8\xd4\x46\x65\x74\x99\xcd\x7a\x88\x23\x95\xdb\xb9\x13\xbd\x89\x68\xf6\xa4\x61\xcb\x0f\x85\x44\x6b\x92\xde\xb9\x04\xf4\x44\x3b\x27\xe0\x99\xdc\x05\xf0\xf4\x34\x6b\xb8\xb6\xcd\xb2\x91\x5b\x8d\x07\xce\x8d\x16\x46\xaf\xff\xba\x29\x73\xee\x22\xaa\x08\x33\xd8\xfd\xbd\xa6\xab\x88\xc1\xe9\x6d\x71\xde\x1f\x6e\x14\x47\x6b\x15\xe4\x2a\xea\x9b\xaf\x83\xab\x9c\xff\x03\x00\x00\xff\xff\x81\x9a\x0c\xab\x82\x12\x00\x00"), }, - "/ignition/worker/systemd/set-kernel-para.service": &vfsgen۰CompressedFileInfo{ - name: "set-kernel-para.service", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 286, + "/terraform/generalos/openstack/worker.tf.template": &vfsgen۰CompressedFileInfo{ + name: "worker.tf.template", + modTime: time.Date(2024, 6, 13, 2, 44, 1, 220820433, time.UTC), + uncompressedSize: 4719, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\x8e\x4d\x4e\xc3\x30\x10\x85\xf7\x73\x0a\x5f\x20\xc9\x09\xbc\x00\xd1\x05\x62\xd7\x82\x58\x44\x11\x72\x9c\x97\x32\xaa\x63\x9b\x99\x49\xd5\xdc\x1e\x41\x24\xc4\xaa\xbb\xf7\x2b\x7d\xfd\x5b\x66\x1b\xe8\x09\x1a\x85\xab\x71\xc9\x5e\x61\xee\x02\xc9\x48\xae\x06\x09\x6e\x2e\xe2\x5e\xd6\xf1\x27\x31\x28\x1d\xf1\xb5\xb2\x40\xbd\x20\x21\x28\x1a\x5e\xc2\x19\x4d\xe5\x6b\xb1\x56\x21\x57\x8e\xa0\x87\xd9\x20\x77\x17\xd4\x9f\x76\x35\xd0\xeb\x56\xe1\x4b\x86\x7e\x16\xa3\x23\x96\xc0\xf9\xf7\x7f\xb8\xb1\xf9\x0d\x4a\x87\x1b\xe2\xc9\x82\x98\x5f\xca\x54\xa5\x8c\x70\xa3\x7c\x64\xd8\xcc\xc9\x20\xff\x7a\xdd\x34\x5a\x72\x4d\x75\x1d\x2c\x76\xbb\x6d\xa7\xee\xf2\xc7\xdf\xc6\x92\x67\xa2\xfe\x39\xab\x85\x94\x06\x7a\x0f\xd9\x30\x3d\x6e\x7e\x59\x93\x71\xb3\x2a\xa4\xb5\x20\x67\x18\xd1\x77\x00\x00\x00\xff\xff\x73\x48\x6e\xcb\x1e\x01\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xcc\x58\x6f\x6b\xe4\x36\x13\x7f\xef\x4f\x31\x8f\x79\x5e\xdc\x1d\x8d\xcf\xd9\xcd\x25\x69\x21\x94\x34\x57\xda\x40\x7b\x84\xd0\x50\xe8\x51\x8c\x62\x8f\x37\x62\x65\x4b\xd5\x1f\x27\x77\xc1\xdf\xbd\xc8\x92\xbd\x6b\xaf\x9d\xdd\xec\x25\x50\x8e\x03\x79\x34\xf3\xfb\xcd\x8c\x34\x9a\xd9\x68\x94\x92\xe4\x5c\x16\xf0\x18\x00\x48\xfc\xc7\x50\x89\x59\x22\x24\xaf\x68\x86\x52\x35\x62\x00\x2e\xb0\x54\x9a\xa4\x4b\x38\xf3\x12\x00\xc5\x8d\x4c\x11\xce\x20\xec\x40\x0e\x5a\xbb\x83\xce\xe0\x7d\xb7\x0a\xbd\x5d\x85\x52\x51\x5e\x5a\xc3\x59\x14\x47\xb1\x93\xd7\x81\xfd\x5f\x07\x41\x0b\x01\xe1\xca\xb2\xe1\x34\x0a\x65\x52\x92\x02\x01\xac\xed\xe3\x63\x74\xc5\x88\xb6\xb4\xd1\x8d\x42\x69\x77\xea\xda\x82\x09\xa2\xd4\x3d\x97\x19\x6c\x2a\x5e\xf9\x2d\xa7\xa8\xb1\x24\xa5\x76\x98\x03\xc5\x3f\x9a\xad\x4f\x1d\x26\x31\xfa\x2e\x31\x92\x8d\x60\x9e\x1b\x7d\x77\x73\xfd\x9b\xd3\x93\xb8\xb0\xb1\xc1\x88\xde\x75\xb3\x65\xd5\xea\x20\xa8\x88\xa4\xe4\x96\x21\x84\x29\x33\x4a\xa3\x4c\x68\xe6\xc2\xd4\x5f\x04\x3a\x6b\xa5\x25\x2d\x17\x01\x40\x86\x39\x31\x4c\x7b\xc0\x0b\x67\x70\xf9\x71\x03\x8a\xda\x74\x95\x29\x26\x29\x37\xa5\x76\x70\x7d\xdb\x3f\xb9\x5c\xa2\x8c\x2e\xec\xfe\xb4\xf9\x1d\x57\xda\x26\x65\xe8\x10\xa3\x4a\xbf\x71\x5e\xbd\xed\x41\xaf\x90\x7f\xf5\xa6\x75\x3d\xe5\x9a\x30\x7b\xc0\x5e\x5c\xdd\x4c\x22\x4a\x52\xec\x81\x78\x7d\xfe\xfb\x24\x62\x46\xd5\x72\x0f\xc8\x8f\x54\x2d\x27\x31\xb9\xa2\x05\x59\xe0\x8e\x67\xdc\x5d\x9a\x5f\x98\xb5\x6e\xef\x61\x0f\x9a\x54\x84\x32\x72\x4b\x19\xd5\x5f\x92\xaf\xbc\x7c\x36\xf6\xf9\x1a\xc0\x5f\xbc\xc4\xe9\x0b\x61\x4b\x2f\x23\x9a\xec\x91\x94\x9f\x38\xd7\x17\xbc\xcc\xe9\x62\x33\x35\xda\x56\x2d\x4b\x4a\xd4\xcf\x75\xfd\xd2\xdb\x7e\x42\x7d\xcf\xe5\x72\xda\x73\x2a\xf6\xf0\xf9\xf2\x6a\xe8\x2b\x3e\xec\xef\xeb\xcf\x0f\x63\xbe\x4a\xf4\xef\xe7\xea\x99\x4b\x52\x5e\x08\xa3\x31\xc9\x19\xa9\xb8\x4c\xaa\x59\x08\xa1\x5b\x3b\xce\xa6\xac\xfd\xd3\x52\x11\x19\xf5\xeb\x3d\x00\xf0\xaf\xe3\xa6\x42\x5b\xd1\x9f\x1b\xcd\x88\x96\x19\x3e\xfc\x1d\x00\x54\xa9\x30\x6a\x14\x51\x98\xa1\xae\x24\x05\x8c\x82\x4b\x52\x0c\x75\x6d\x05\x8d\xea\xda\x8d\xa1\x32\x55\x89\x30\xb7\x8c\xa6\x4d\x2f\x91\x06\x27\x13\x74\xcb\x78\xba\x54\x9a\x4b\xb2\xc0\xa4\xe2\xcc\x14\x98\x54\xf3\x10\x42\xb7\x5e\xcf\xd2\x93\x19\xda\x31\x3b\x8a\x7e\xc5\x1d\x22\xd8\x72\x9a\x0a\xd3\x85\xe4\x46\xb8\xf3\x6c\xbf\x9c\xaf\xab\x03\x6b\xda\xc5\xff\x1f\x2d\xd5\xaa\x23\xd4\x07\xf7\xcd\x8d\x0c\x9b\xdb\xa5\x52\x49\x85\xf6\xad\xb3\xc5\x81\x9c\x4b\x58\x9e\x2a\x68\x35\xed\x49\x19\x86\xbe\x4d\xe7\x92\x17\x89\xe0\x52\x37\x04\xb3\x59\x23\xd4\xbc\x15\xad\x09\xa9\xb0\x5d\x5f\xf3\x94\xb3\xe6\x1c\x52\xe1\x1a\x73\x4a\x33\xb9\xe6\x62\x1c\x35\xff\xde\x37\x6d\xbb\x7e\x92\xed\xe0\x70\x84\xcd\x0b\x07\x6c\x34\x2d\xbe\x99\xee\x34\x1e\xa1\xf3\xc2\x97\x0f\xee\xe8\x68\x3e\x42\xd7\x4a\x5f\x9e\x6f\x36\x3f\xf9\x7e\xec\xf0\xe6\xaf\x16\xe1\xe1\x28\x61\x2b\x7d\x79\xbe\xe3\xf1\x94\x1e\xbf\x5e\x4e\x0f\xe3\xd9\xd1\xe9\x58\x8c\x9d\xfc\x55\x38\x3f\xc4\xe3\x9c\x1f\x5e\xeb\x24\xe7\x71\x1c\x8f\x71\xce\x67\x27\xc7\x27\xff\x15\x4e\x93\xed\xc4\xb9\xe5\xb1\xed\x9e\xe9\xe6\xb1\x6d\xbf\x86\xed\x13\x60\xbc\x93\x6d\xf6\xd1\x29\xcd\xa9\x96\xd1\x0c\x79\x49\xcf\x7e\x60\xe9\x07\xc1\x00\xc0\xb7\xf9\x7e\x0f\xd8\xad\x31\x61\x6a\xa4\x1d\xfb\x9a\x16\xa0\x9c\xe5\xe7\x27\x5b\x4f\xd4\xae\x23\x0b\x68\x41\x36\xe6\xc7\x96\x7e\x63\xa3\xfd\xf1\x65\x47\xc0\xf5\x8c\x68\x2c\x04\x23\x1a\x73\xca\xf0\x4d\xcf\xf1\x76\x60\xec\x39\xfe\x1d\x3c\x42\x1b\xd1\x4e\x91\x42\xfd\xd6\xde\xb1\xd2\x0d\x4e\xfe\x9a\x8d\xa5\x6b\x35\x9c\xb9\x9b\x48\x1f\x30\x4b\xa8\x48\xaa\xa3\x21\x0f\x15\x7d\x86\xff\x9d\x41\x58\x1a\xc6\x42\xf8\xf1\x69\xc5\x1f\xc0\xaa\x3d\x75\x05\xbd\x9b\xb4\x5c\x24\x39\xe3\x44\xd3\x72\x41\x45\x3b\xc4\xd9\x6f\xb1\xc3\x7c\x22\x38\x67\x6d\x5c\xeb\x43\xe7\xf6\x91\xb1\x63\x24\x4a\xf1\x94\x12\xed\x6b\x20\x5f\x17\x6d\x16\xc2\x84\x1f\x2d\x5e\x42\x05\x9c\xc1\xf6\x20\x23\x1f\x62\xf4\x2e\x22\x59\x26\x51\xa9\x8d\xca\xe8\x32\x9b\xf5\x10\x47\x2a\xb7\x73\x27\x7a\x17\xd1\xec\x59\xc3\x96\x1f\x0a\x89\xd6\x24\xbd\x73\x09\xe8\x89\x76\x4e\xc0\x0b\xb9\x0b\xe0\xe9\x69\xd6\x70\x6d\x9b\x65\x23\xb7\x1a\x0f\x9c\x1b\x2d\x8c\x5e\xff\x75\x53\xe6\xdc\x45\x54\x11\x66\xb0\xfb\xe3\x4c\x57\x11\x83\xd3\xdb\xe2\xbc\x3f\xdc\x28\x8e\xd6\x2a\xc8\x55\xd4\x37\x5f\x07\x57\x39\xff\x06\x00\x00\xff\xff\xf0\x12\x5c\x27\x6f\x12\x00\x00"), }, - "/terraform": &vfsgen۰DirInfo{ - name: "terraform", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), + "/terraform/nestos": &vfsgen۰DirInfo{ + name: "nestos", + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/terraform/libvirt": &vfsgen۰DirInfo{ + "/terraform/nestos/libvirt": &vfsgen۰DirInfo{ name: "libvirt", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/terraform/libvirt/master.tf.template": &vfsgen۰CompressedFileInfo{ + "/terraform/nestos/libvirt/master.tf.template": &vfsgen۰CompressedFileInfo{ name: "master.tf.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 3232, + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), + uncompressedSize: 3229, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\xdf\x6b\xe4\x36\x10\x7e\xf7\x5f\x31\x15\x7d\xb8\x0b\x8d\x93\x96\xa3\x85\xc2\x52\x4a\x02\xed\x3e\x1c\x0d\x69\xef\xe9\x08\x46\xb1\x67\xbd\x62\x6d\xc9\x95\x64\xa7\xdb\xc5\xff\xfb\x31\xb2\xe4\x1f\xbb\xf6\xde\xde\x06\xb2\xb6\xe5\x99\x6f\x46\xdf\xcc\x37\xb2\x45\xad\xf9\x46\xe9\x12\x0e\x11\x80\xc6\x7f\x6b\xa1\x31\x4b\x2a\xad\x1a\x91\xa1\x36\x6e\x19\xa0\x10\xaf\x8d\xd0\x16\x56\xfe\x19\xc0\xa8\x5a\xa7\x08\x2b\x60\x59\xc9\xd3\x46\xa4\x5c\xdf\x79\x2b\xe6\x4d\x1a\xd4\x46\x28\x49\x36\xf7\xf1\x2f\xf1\xcf\xdd\x7a\x1b\xd1\x7f\x1b\x45\x21\x06\xb0\xe0\xe7\xc0\x6b\x2d\xc8\xe3\x70\x88\x9f\x0a\x6e\x29\xb5\xf8\xd3\xf3\xba\x6d\x19\xf9\x34\x5c\x0b\xfe\x5a\x20\xb0\xb4\xa8\x8d\x45\x9d\x88\xac\x73\xb3\xfb\x0a\x09\x7e\x05\xc6\x6a\x21\xf3\x08\x20\xc3\x0d\xaf\x0b\xeb\xd1\x1e\x3a\x87\xf5\xe3\x09\x94\x90\xc6\x72\x99\x62\x92\xaa\x5a\xda\x0b\xe1\x3e\x72\x42\x8b\x1f\xc8\x65\x19\x71\xab\x8c\x95\xbc\xc4\x63\xd0\x42\x18\xfb\xae\x43\x7e\x3f\x81\x1e\x90\xff\xf4\xae\x6d\xbb\x80\x2d\xaa\x2b\x50\xd7\x4f\xc7\x78\x3d\x91\x55\xf3\x21\x49\x45\xa6\x2f\x24\xa0\xaf\xce\xc3\xfa\xf1\x79\xb9\x3c\x84\x9a\x73\x8b\x6f\x7c\xff\xad\xc0\x7f\x74\x6e\x67\xea\x55\xd5\x57\x50\xf0\xf0\xf4\x69\x91\x53\xcd\xcb\x2b\x10\x9f\x7f\xff\xb8\x88\x98\x09\xb3\xbb\x02\xf2\x51\x98\xdd\x72\xe5\x73\x79\x4d\xe9\x73\x99\x3c\x71\xbb\xed\x60\x35\x7a\x05\x07\xf5\x25\x95\x52\x05\x03\xd6\x5d\x08\x9d\xba\x8f\x6a\xf2\xfd\xa1\xe1\x3a\x1e\x04\xd7\xde\x3a\x9b\x10\x9f\x46\x80\xd0\xf4\x58\x71\xbb\xa5\xc7\xbb\xa6\x1b\x06\x61\x20\xdc\x89\x92\xe7\x68\xee\x4e\x80\xd8\x7c\x26\x8d\x2a\x6a\xd2\x0c\x0b\x37\x7d\x36\x30\x9f\x8f\xb7\xa3\x14\x94\x2a\x3c\x21\xc3\xae\x62\xf7\x43\x00\xd1\x78\x70\x8d\x7b\xed\xaf\xbf\xd7\x94\xa3\x27\xe8\x6b\x79\x0d\x45\x75\x23\x03\xfa\xbf\x15\x50\x6a\xd3\x81\x32\xe4\x3e\x58\xf9\x3d\x9c\xcc\x89\xcf\xce\x23\x16\x32\xc3\xff\x5e\xda\x5b\x17\x27\x02\x78\xe5\x06\x7d\xf4\x44\x64\xa3\xdd\x75\x6b\xb1\xbf\x88\x6c\x60\x60\x88\xb5\xcc\x84\xf8\x7f\x9a\xd5\x24\x25\x8a\x3d\x49\x07\x6e\xe0\xc7\xfb\x9f\x3e\x4c\x2f\xf3\x44\x89\x5c\x0a\x2b\x94\x64\xc0\x86\xdb\x31\x5d\x5f\xe1\xe9\x62\x82\x7a\xf4\xd1\xc6\xcf\xec\x38\x55\xd2\xa2\x24\x59\x58\x2c\xab\x82\x5b\xdc\x88\x02\xdf\x4d\x22\x89\x5c\x4e\x82\xfc\x00\x07\x08\xd1\x8f\xf3\x9e\xcd\x0a\xda\xf7\xf3\xac\x48\xb4\x6f\x4a\xef\x18\xb0\xfe\xee\x30\x69\x8e\xd9\xde\x96\xe8\x0e\xd4\x52\x65\x83\x95\xe4\x6e\x2d\x53\x25\x17\x72\xc9\x33\x2e\x54\xca\x9d\x4c\x79\x96\x69\x34\x06\x0d\xac\xe0\xf3\xc4\x2a\xcc\xfc\x97\x88\xe0\xb6\x69\xe5\xcf\x77\x94\x34\x72\xa8\xd3\xac\xae\xd1\x9d\xd8\x64\x20\xcd\xd1\x7b\x17\xda\x9b\x00\xb8\x80\x89\x92\xc5\xfe\xc8\x51\xab\xda\x62\xf0\xa5\x78\x43\xbf\x9d\xa4\xe2\x6c\xfc\x89\x31\x67\xe3\x5f\x05\x68\x5e\x5b\x65\x2c\x77\xdf\x26\x2e\xe4\x2c\xf5\x1d\x53\x8e\x79\x63\x95\x99\xd5\xee\x85\xe2\xbd\xa8\x07\x08\xbd\xaa\xfd\x86\x5d\xe9\x56\xc0\xc8\xf2\xb6\xe2\xc6\xd8\xad\x56\x75\xbe\x65\x51\xf7\x35\xd4\x90\xe9\x99\x3c\xaa\xfa\x18\xbb\xc4\x52\xe9\xfd\xa2\x87\xe6\xe5\x49\x36\x4a\xa3\x32\xbd\x30\x47\x22\x09\x4b\x71\x7f\x73\x13\x8b\xec\x24\x22\x4f\xb7\x42\xe2\x78\x84\xb9\x93\xc5\xad\xfe\xb3\xaf\x90\x86\xe6\xb8\x1a\xc1\xcc\xb7\x41\x38\xa9\x46\xfe\xbb\xa6\x64\xae\xab\x84\xd9\x79\xa6\xce\x8c\x38\xb2\x9a\xcd\xcc\x75\x81\x17\x54\x22\xa4\x45\xbd\xe1\x29\x7a\xc0\xb0\xde\x1f\x1e\x47\x5a\x8c\xfb\x6b\x37\x20\x60\x90\xfa\x37\x15\x7b\xac\xb1\x39\x3f\x51\x4d\x47\xc4\x77\x24\xe2\xba\x28\x18\xfc\xd6\x29\x72\xc9\xf2\x05\x7e\x05\x32\x74\x31\xde\xb8\xb0\xc9\x46\xe9\xa4\x40\x6e\xf0\x48\x63\xb9\xe6\xd5\x56\xa4\x41\x65\x63\xc2\x57\xc0\x1a\x99\x32\xff\x25\x6f\x2c\xca\x24\x9c\xdc\x3e\x6d\x16\x50\x52\x25\x8d\x2a\x70\x1e\xa4\xb2\xfb\x0e\xc4\x72\x9d\x23\xcd\x57\x27\x3b\x76\xcf\xc2\x37\xbd\xaa\x6d\x55\x5b\x60\xa4\xd4\x4e\x64\x0d\x2f\x6a\x1c\x11\xdf\x29\x31\xee\x74\x18\xdf\xc4\x27\x95\x8b\xef\xe3\x9e\xcb\xa8\xfd\x12\x00\x00\xff\xff\xdd\xf0\xef\x6a\xa0\x0c\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\xdf\x6f\xe3\x36\x0c\x7e\xf7\x5f\xc1\x09\x7b\xb8\x2b\x56\xb7\x1b\x0e\x1b\x30\x20\x18\xb6\x14\xd8\xf2\x70\x58\xd1\xed\x9e\x0e\x85\xa1\xda\x8c\x43\xc4\x96\x3c\x49\x76\x97\x05\xfe\xdf\x07\xc9\x92\x7f\x24\x76\x2f\x97\x02\x8d\x6d\x99\xfc\xf8\x89\xe4\x47\xd9\xa0\x52\x7c\x2b\x55\x09\xc7\x08\x40\xe1\x3f\x35\x29\xcc\x92\x4a\xc9\x86\x32\x54\xda\x2d\x03\x14\xf4\xd2\x90\x32\xb0\xf2\xcf\x00\x5a\xd6\x2a\x45\x58\x01\xcb\x4a\x9e\x36\x94\x72\x75\xe7\xad\x98\x37\x69\x50\x69\x92\xc2\xda\xdc\xc7\x3f\xc5\x3f\x76\xeb\x6d\x64\xff\xdb\x28\x0a\x31\x80\x05\x3f\x07\x5e\x2b\xb2\x1e\xc7\x63\xfc\x58\x70\x63\xa9\xc5\x9f\x9e\x36\x6d\xcb\xac\x4f\xc3\x15\xf1\x97\x02\x81\xa5\x45\xad\x0d\xaa\x84\xb2\xce\xcd\x1c\x2a\xb4\xf0\x2b\xd0\x46\x91\xc8\x23\x80\x0c\xb7\xbc\x2e\x8c\x47\x5b\x77\x0e\x9b\x87\x33\x28\x12\xda\x70\x91\x62\x92\xca\x5a\x98\x0b\xe1\x3e\x72\x8b\x16\xaf\xad\xcb\x32\xe2\x4e\x6a\x23\x78\x89\xa7\xa0\x05\x69\xf3\xae\x43\x7e\x3f\x81\x1e\x90\xff\xf0\xae\x6d\xbb\x80\x4d\xd5\x15\xa8\x9b\xc7\x53\xbc\x3e\x91\x55\xf3\x21\x49\x29\x53\x17\x26\xa0\xaf\xce\x7a\xf3\xf0\xb4\x5c\x1e\x8b\x9a\x73\x83\xaf\xfc\xf0\xb5\xc0\xbf\x77\x6e\x6f\xd4\xab\xaa\xaf\x48\xc1\xfa\xf1\xd3\x62\x4e\x15\x2f\xaf\x40\x7c\xfa\xf5\xe3\x22\x62\x46\x7a\x7f\x05\xe4\x03\xe9\xfd\x72\xe5\x73\x71\x05\xe4\x6f\x52\x9a\xb5\x14\x5b\xca\x3b\x60\x85\x5e\xc3\x41\x7f\x49\x25\x65\xc1\x80\x75\x17\x8b\x6f\xfb\xcf\x56\xe5\xdb\x63\xc3\x55\x3c\x48\xae\xbd\x75\x36\x81\x81\x1d\x02\xa4\xec\x63\xc5\xcd\xce\x3e\xde\x35\xdd\x38\x08\x23\xe1\x8e\x4a\x9e\xa3\xbe\x3b\x03\x62\xf3\x4c\x1a\x59\xd4\x56\x35\x2c\xdc\xf4\x6c\x60\x9e\x8f\xb7\xb3\x14\xa4\x2c\x7c\x4a\x86\x5d\xc5\xee\xc7\x02\x44\xe3\xd1\x35\xee\xb6\x3f\xff\xda\x58\x8e\xed\x17\x29\x0d\x15\x75\xf3\x02\xfa\xbf\x15\x58\x56\xd3\x69\x32\xd0\x1e\xac\x3c\xfd\xb3\x21\xf1\xd9\x79\xc4\x24\x32\xfc\xf7\xb9\xbd\x75\x71\x22\x80\x17\xae\xd1\x47\x4f\x28\x1b\x6d\xac\x5b\x8b\xfd\x85\xb2\x61\xf3\x43\xac\xe5\x24\xd0\x7f\x53\x56\x13\x4a\x36\xf6\x84\x0e\xdc\xc0\xf7\xf7\x3f\x7c\x98\x5e\xe6\x13\x45\xb9\x20\x43\x52\x30\x60\xc3\xed\x38\x5d\x5f\xc8\xd3\xc5\x09\xea\xd1\x47\x1b\x7f\x63\xc7\xa9\x14\x06\x85\xd5\x84\xc1\xb2\x2a\xb8\xc1\x2d\x15\xf8\x6e\x12\x89\x72\x31\x09\xf2\x1d\x1c\x21\x44\x3f\xe5\x3d\xcb\x0a\xda\xf7\xf3\x59\x11\x68\x5e\xa5\xda\x33\x60\xfd\xdd\x71\xd2\x1c\xb3\x6d\x2d\xd0\x9d\xa6\xa5\xcc\x06\x2b\xc1\xdd\x5a\x26\x4b\x4e\x62\xc9\x33\x2e\x64\xca\x9d\x42\x79\x96\x29\xd4\x1a\x35\xac\xe0\xf3\xc4\x2a\x0c\xfc\xe7\xc8\xc2\xed\xd2\xca\x1f\xee\x28\xec\xbc\xb1\x9d\x66\x54\x8d\xee\xb8\xb6\x06\x42\x9f\xbc\x77\xa1\xbd\x09\x80\x0b\x98\x48\x51\x1c\x4e\x1c\x95\xac\x0d\x06\x5f\x1b\x6f\xe8\xb7\x33\x2a\xce\xc6\x1f\x17\x73\x36\xfe\x55\x80\xe6\xb5\x91\xda\x70\xf7\x61\xe2\x42\xce\xa6\xbe\xcb\x94\xcb\xbc\x36\x52\xcf\x6a\xf7\x42\xf1\x5e\xd4\x03\x16\xbd\xaa\xfd\x86\x5d\xe9\x56\xc0\xac\xe5\x6d\xc5\xb5\x36\x3b\x25\xeb\x7c\xc7\xa2\xee\x53\xa8\xb1\xa6\x6f\xf0\xa8\xea\x53\xec\x12\x4b\xa9\x0e\x8b\x1e\x8a\x97\x67\x6c\xa4\x42\xa9\x7b\x61\x8e\x44\x12\x96\xe2\xfe\xe6\x26\xa6\xec\x2c\x22\x4f\x77\x24\x70\x3c\xc2\xdc\xb1\xe2\x56\xff\x3e\x54\x6e\x68\x8e\xab\x11\xcc\x7c\x1b\x84\x63\x6a\xe4\xbf\x6f\x4a\xe6\xba\x8a\xf4\xde\x67\xea\x8d\x11\x67\xad\x66\x99\xb9\x2e\xf0\x82\x4a\x48\x18\x54\x5b\x9e\xa2\x07\x0c\xeb\xfd\xb9\x71\xa2\xc5\xb8\xbf\x76\x03\x02\x06\xa9\x7f\x55\xb1\xc7\x1a\x9b\xf3\xa3\x6a\x3a\x22\xbe\xb1\x22\xae\x8b\x82\xc1\x2f\x9d\x22\x97\x2c\x9f\xe1\x67\xb0\x86\x2e\xc6\x2b\x27\x93\x6c\xa5\x4a\x0a\xe4\x1a\x4f\x34\x96\x2b\x5e\xed\x28\x0d\x2a\x1b\x27\x7c\x05\xac\x11\x29\xf3\x9f\xf1\xda\xa0\x48\xc2\xa1\xed\x69\xb3\x80\x92\x4a\xa1\x65\x81\xf3\x20\x95\x39\x74\x20\x86\xab\x1c\xed\x7c\x75\xb2\x63\xf7\x2c\x7c\xd0\xcb\xda\x54\xb5\x01\x66\x95\xda\x89\xac\xe1\x45\x8d\xa3\xc4\x77\x4a\x8c\x3b\x1d\xc6\x37\xf1\x59\xe5\xe2\xfb\xb8\xcf\x65\xd4\xfe\x1f\x00\x00\xff\xff\x8e\xbd\x3a\xb4\x9d\x0c\x00\x00"), }, - "/terraform/libvirt/worker.tf.template": &vfsgen۰CompressedFileInfo{ + "/terraform/nestos/libvirt/worker.tf.template": &vfsgen۰CompressedFileInfo{ name: "worker.tf.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 2531, + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), + uncompressedSize: 2528, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\x51\x8b\xe3\x36\x10\x7e\xf7\xaf\x98\x8a\x3e\xdc\x2d\x5d\x75\x5b\x8e\x16\x0a\xa1\x94\xbd\x87\xee\xc3\xd1\xe5\xda\xa3\x0f\xc7\x61\xb4\xf6\xc4\x19\x62\x4b\xea\x48\xf2\x75\x1b\xfc\xdf\x8b\x6c\x39\xb1\x37\xf6\xde\x5e\x02\x21\x8e\x3c\xf3\xcd\xa7\x4f\xdf\x48\xf2\xc8\xac\xb6\x86\x1b\x38\x64\x00\x8c\xff\x04\x62\x2c\x73\xcb\xa6\xa5\x12\xd9\xf5\xc3\x00\x35\x3d\xb4\xc4\x1e\x36\xe9\x3f\x80\x33\x81\x0b\x84\x0d\x88\xb2\x51\x45\x4b\x85\xe2\xef\x53\x94\x48\x21\x2d\xb2\x23\xa3\x63\xcc\x8d\xfc\x59\xfe\x34\x8c\x77\x59\xfc\x76\x59\x36\xd6\x00\x31\xe6\xf5\xe0\x81\x29\x66\x1c\x0e\xf2\xbe\x56\x3e\x52\x93\x1f\xde\xdf\x75\x9d\x88\x39\xad\x62\x52\x0f\x35\x82\x28\xea\xe0\x3c\x72\x4e\xe5\x90\xe6\x1f\x2d\x46\xf8\x0d\x38\xcf\xa4\xab\x0c\xa0\xc4\xad\x0a\xb5\x4f\x68\xb7\x43\xc2\xdd\xdb\x33\x28\xd2\xce\x2b\x5d\x60\x5e\x98\xa0\x13\x8b\x79\xee\xdf\x86\xf7\xc8\xf2\x36\xbe\x5f\x4f\xdf\x19\xe7\xb5\x6a\xf0\x29\xa1\x9a\x9c\x7f\x35\xb0\x7a\x3d\x83\x3e\x21\xff\x9e\x52\xbb\x6e\x05\x9b\xec\x05\xa8\x77\xf7\xab\x78\x85\x0d\x17\x00\xde\xde\x7f\x58\x45\x64\xd5\x5c\x80\xf8\xfe\xb7\x77\xab\x88\x25\xb9\xfd\x05\x90\x6f\xc9\xed\xd7\x75\xac\xf4\x25\x42\x56\x3a\xbf\x57\x7e\x37\xc0\x32\x26\xf3\x8f\xc6\xcd\x5b\x53\x87\xb8\xee\x62\x7c\x88\x15\xe2\x7a\xf6\x05\xc4\xb7\x87\x56\xb1\x3c\x39\xb6\xbb\x4e\x71\x19\x80\x35\xa6\x5e\x8b\x8a\xef\x62\xcc\xa9\xd7\xa6\x5d\xf1\xc7\x9f\x77\x8d\xaa\x30\x11\x13\x5f\x60\x76\x12\xb3\x77\x39\x4c\x3e\x1b\x88\x85\xe7\x5d\x70\xe2\x3f\x8d\x4b\x1c\xcf\x1c\xff\xb1\xcf\x91\xa4\x4b\xfc\xf7\x53\x77\xdd\xd7\xca\x00\x1e\x94\xc3\xc4\x20\xef\xd1\x5e\xa6\xc5\x42\xc5\x45\x55\xe8\xbf\xa7\xfc\x66\xe4\x22\x8b\x19\x31\xb8\x82\x1f\x6e\x7e\x7c\x33\xff\x59\x96\x8d\x2a\x4d\x9e\x8c\x16\x20\x4e\x8f\x53\xf1\xbe\xa0\xd9\x8b\xa5\x3a\xa2\x4f\xa6\xff\xec\xac\x0b\xa3\x3d\xea\x68\x4f\x8f\x8d\xad\x95\xc7\x2d\xd5\xf8\x6a\x56\x8b\x2a\x3d\x2b\xf3\x1d\x1c\x60\xac\xff\x94\xf9\x22\x2f\xe8\x5e\x2f\xeb\x52\x9a\x46\x51\x54\x45\xa3\xf3\xc6\x2d\x1b\xea\x85\x7e\x7a\x09\x91\x88\x6e\x43\x3a\x74\x1a\x53\xf6\x16\x8a\x91\xd7\x56\x39\xe7\x77\x6c\x42\xb5\x13\xd9\x70\xaa\xb4\x31\xf4\x19\x1e\x36\x3c\xc5\x6e\xb0\x31\xfc\xb8\x9a\xc1\xaa\x39\x63\x63\x18\x8d\x3b\xfa\xa3\xdf\x3e\xe6\x96\x91\xc7\x87\x2b\x49\xe5\x59\x45\x55\xec\x48\xe3\xd4\xe1\x87\x83\x7c\x37\x8c\xfe\xf5\x68\x31\x76\x32\x80\x0a\xde\x38\xaf\xd8\x1f\xc3\x3c\x07\x9c\x6c\x5b\x93\xfc\x7d\xdb\x88\x2c\x6e\x5c\xe4\xf6\x49\xa9\xd4\x73\x54\x4e\xf8\x0d\x63\x32\x46\x2d\x32\xeb\x22\x86\x46\xff\xd9\xf0\x3e\x27\xed\x91\xb7\xaa\xc0\x04\x38\x8e\x3f\xb7\xa7\x69\x4c\x27\xff\xd1\x6a\x5f\xb5\xce\x00\xaa\x2c\x19\x9d\x43\xb7\x98\x47\x76\x6e\xd1\x6f\x36\x20\x74\xa8\x6b\x01\xbf\xc2\xc7\xe7\x22\x3f\xc1\x2f\x10\x03\xfb\x1a\x9f\x15\xf9\x7c\x6b\x38\xaf\x51\x39\x3c\xe9\xda\xcf\xbe\x62\x65\x77\x54\x8c\x97\x9e\xa9\xd6\x1b\x10\xad\x2e\x44\xba\x0c\x39\x8f\x3a\xef\x5f\x6f\x40\x24\xda\x62\x44\x29\x8c\x76\xa6\xc6\x65\x10\xeb\x1f\x07\x10\xaf\xb8\x42\x9f\x5b\xd3\xdf\xaa\xc4\x8d\x18\xaf\x45\x26\x78\x1b\x3c\x08\xb2\xed\x9b\xa1\xbf\x5a\x55\x07\x9c\xac\xe4\xd0\x84\x72\x68\x41\x79\x25\xcf\x16\x4d\xde\xc8\xa3\x96\x59\xf7\x7f\x00\x00\x00\xff\xff\x1e\x1d\x37\x93\xe3\x09\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x56\x51\x8f\xe3\x34\x10\x7e\xcf\xaf\x18\x2c\x1e\xee\x56\xac\x59\xd0\x09\x24\xa4\x0a\xc1\xde\x03\xfb\x70\x62\x75\x70\xe2\xe1\x74\x8a\xbc\xc9\x34\x1d\x35\xb1\xcd\xd8\xce\xb1\x54\xf9\xef\xc8\x89\xd3\x26\xdb\xb4\xf4\x5a\xa9\x6a\xea\xcc\x7c\xf3\xf9\xf3\x37\xb6\x3d\x32\xab\xb5\xe1\x06\x76\x19\x00\xe3\xdf\x81\x18\xcb\xdc\xb2\x69\xa9\x44\x76\xfd\x30\x40\x4d\x4f\x2d\xb1\x87\x55\xfa\x0f\xe0\x4c\xe0\x02\x61\x05\xa2\x6c\x54\xd1\x52\xa1\xf8\xdb\x14\x25\x52\x48\x8b\xec\xc8\xe8\x18\x73\x27\x7f\x94\x3f\x0c\xe3\x5d\x16\xbf\x5d\x96\x8d\x35\x40\x8c\x79\x3d\x78\x60\x8a\x19\xbb\x9d\x7c\xac\x95\x8f\xd4\xe4\x87\xf7\x0f\x5d\x27\x62\x4e\xab\x98\xd4\x53\x8d\x20\x8a\x3a\x38\x8f\x9c\x53\x39\xa4\xf9\x67\x8b\x11\x7e\x05\xce\x33\xe9\x2a\x03\x28\x71\xad\x42\xed\x13\xda\xfd\x90\xf0\xf0\xf6\x08\x8a\xb4\xf3\x4a\x17\x98\x17\x26\xe8\xc4\x62\x9e\xfb\x97\xe1\x2d\xb2\xbc\x8f\xef\x4f\xa7\x6f\x8c\xf3\x5a\x35\xf8\x92\x50\x4d\xce\xbf\x1a\x58\xbd\x9e\x41\x1f\x90\x7f\x4b\xa9\x5d\x77\x02\x9b\xec\x15\xa8\x0f\x8f\x27\xf1\x0a\x1b\xae\x00\xbc\x7f\xfc\x70\x12\x91\x55\x73\x05\xe2\xfb\x5f\xde\x9d\x44\x2c\xc9\x6d\xaf\x80\x7c\x4b\x6e\x7b\x5a\xc7\x4a\x5f\x01\xf9\xab\x31\xfe\xde\xe8\x35\x55\x03\x30\x63\xb2\xff\x68\xdd\xbc\x35\x75\x88\x2b\x2f\xc6\x87\x58\x23\xae\x68\x5f\x42\x7c\xbd\x6b\x15\xcb\x83\x67\xbb\xdb\x14\x97\x01\x58\x63\xea\x53\x51\xf1\x5d\x8c\x39\x74\xdb\xb4\x2f\x7e\xff\xe3\xa1\x51\x15\x26\x47\x9e\x21\x75\x50\xb2\xb7\x38\x4c\x3e\x2b\x88\x35\xe7\x2d\x70\xa0\x3e\x8d\x4b\xf4\x8e\xec\xfe\xb1\xcf\x91\xa4\x4b\xfc\xe7\x53\x77\xdb\xd7\xca\x00\x9e\x94\xc3\xc4\x20\xef\xd1\x2e\x93\x61\xa1\xe2\xa2\x20\xf4\xef\x4b\x7e\x33\x72\x91\xc5\x8c\x18\xdc\xc0\x77\x77\xdf\xbf\x99\xff\x2c\xcb\x46\x95\x26\x4f\x46\x0b\x10\x87\xc7\xa9\x78\xff\xa3\xd9\xc5\x52\xed\xd1\x27\xd3\x3f\x3b\xeb\xc2\x68\x8f\x3a\x7a\xd3\x63\x63\x6b\xe5\x71\x4d\x35\xbe\x9a\xd5\xa2\x4a\xcf\xca\x7c\x03\x3b\x18\xeb\xbf\x64\xbe\xc8\x0b\xba\xd7\xcb\xba\x94\xa6\x51\x14\x55\xd1\xe8\xbc\x71\xcb\x86\xba\xd0\x4f\x97\x10\x89\xe8\x36\xa4\x13\xa7\x31\x65\x6f\xa1\x18\x79\x6b\x95\x73\x7e\xc3\x26\x54\x1b\x91\x0d\x47\x4a\x1b\x43\xcf\xf0\xb0\xe1\x25\x76\x83\x8d\xe1\xe7\x93\x19\xac\x9a\x23\x36\x86\xd1\xb8\xbd\x3f\xfa\xbd\x63\x6e\x19\xb9\x7f\xb8\x91\x54\x1e\x55\x54\xc5\x86\x34\x4e\x1d\xbe\xdb\xc9\x77\xc3\xe8\x9f\xcf\xb6\xef\x64\x00\x15\xbc\x71\x5e\xb1\xdf\x87\x79\x0e\x38\xd9\xb3\x26\xf9\xdb\xb6\x11\x59\xdc\xb5\xc8\x6d\x93\x52\xa9\xe7\xa8\x9c\xf0\x1b\xc6\x64\x8c\x5a\x64\xd6\x45\x0c\x8d\xfe\xb3\xe1\x6d\x4e\xda\x23\xaf\x55\x81\x09\x70\x1c\x3f\xb7\x9d\x69\x4c\xc7\xfe\xde\x6a\x5f\xb4\xce\x00\xaa\x2c\x19\x9d\x43\xb7\x98\x47\x76\x6e\xd1\xaf\x56\x20\x74\xa8\x6b\x01\x3f\xc3\xc7\x73\x91\x9f\xe0\x27\x88\x81\x7d\x8d\xcf\x8a\x7c\xbe\x36\x9c\xd7\xa8\x1c\x1e\x74\xed\x67\x5f\xb1\xb2\x1b\x2a\xc6\x1b\xcf\x54\xeb\x15\x88\x56\x17\x22\xdd\x84\x9c\x47\x9d\xf7\xaf\x57\x20\x12\x6d\x31\xa2\x14\x46\x3b\x53\xe3\x32\x88\xf5\xcf\x03\x88\x57\x5c\xa1\xcf\xad\xe9\xaf\x54\xe2\x4e\x8c\x77\x22\x13\xbc\x0d\x1e\x04\xd9\xf6\xcd\xd0\x5f\xad\xaa\x03\x4e\x56\x72\x68\x42\x39\xb4\xa0\xbc\x91\x47\x8b\x26\xef\xe4\x5e\xcb\xac\xfb\x2f\x00\x00\xff\xff\x56\x79\xfb\xa1\xe0\x09\x00\x00"), }, - "/terraform/openstack": &vfsgen۰DirInfo{ + "/terraform/nestos/openstack": &vfsgen۰DirInfo{ name: "openstack", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), + modTime: time.Date(2024, 6, 13, 2, 8, 56, 899199076, time.UTC), }, - "/terraform/openstack/master.tf.template": &vfsgen۰CompressedFileInfo{ + "/terraform/nestos/openstack/master.tf.template": &vfsgen۰CompressedFileInfo{ name: "master.tf.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 4743, + modTime: time.Date(2024, 6, 13, 2, 44, 1, 221820446, time.UTC), + uncompressedSize: 4738, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xcc\x58\x6d\x6f\xdb\x36\x10\xfe\xae\x5f\x71\x13\xf6\xa1\x2d\x16\xd5\xb1\xf3\xb6\x01\xc1\x10\xa4\xc3\x16\x60\x2d\x82\x60\xf9\xb2\x62\x20\x18\x89\x76\x08\x53\x22\xc7\x17\x35\xad\xe1\xff\x3e\x50\x24\x65\x4b\xa6\x6c\xc7\x4d\x80\xa1\x28\x40\x1d\xef\x9e\xe7\xee\xc8\xe3\x9d\xa3\x89\x94\x78\xca\x65\x09\x8b\x04\x40\x92\x7f\x0d\x95\xa4\x40\x42\xf2\x9a\x16\x44\xaa\x46\x0c\xc0\x05\xa9\x94\xc6\xf9\x1c\x2e\xbd\x04\x40\x71\x23\x73\x02\x97\x90\xb6\x20\x47\xc1\xee\xa8\x35\x78\xdf\xae\x52\x6f\x57\x13\xa9\x28\xaf\xac\xe1\x71\x76\x3a\xce\x8e\xdd\xc6\x32\xb1\xff\x97\x49\x12\x30\x20\x5d\x99\x36\xa4\x46\x11\x89\x2a\x5c\x12\x00\x6b\xbc\x58\x64\xb7\x0c\x6b\xcb\x9b\xdd\x2b\x22\xed\xce\x72\x69\xc1\x04\x56\xea\x0b\x97\x05\x6c\x2a\xde\xfa\x2d\xa7\xa8\x49\x85\x2b\xed\x30\x7b\x8a\x7f\xb9\xad\x4f\x2d\x28\x36\xfa\x11\x19\xc9\x22\xa0\x57\x76\xeb\xfe\xee\x4f\xa7\x28\xc9\xcc\x86\x07\x11\xc5\xbb\x66\xcb\xaa\x2d\x93\xa4\xc6\x92\xe2\x07\x46\x20\xcd\x99\x51\x9a\x48\x44\x0b\x17\xa8\xfe\x2a\x88\xb3\x56\x5a\xd2\x6a\x96\x00\x14\x64\x8a\x0d\xd3\x1e\xf0\xda\x19\xdc\x7c\xd8\x80\xa2\x36\x61\x55\x4e\x50\xce\x4d\xa5\xf7\x84\xfb\x88\x2d\x5a\x76\x6d\x4d\x86\x11\x1f\xb9\xd2\x36\x53\x7d\x50\x46\x95\x7e\xe3\x90\xdf\x76\xa0\x57\xc8\x7f\x78\xd3\xe5\x72\xc8\x5b\x61\x0e\x80\xbd\xbe\xbd\x1f\x44\x94\xb8\x3c\x00\xf1\xee\xea\xe3\x20\x62\x41\xd5\xfc\x00\xc8\x0f\x54\xcd\x07\x31\xb9\xa2\x25\x9e\x91\x3d\xcf\xa9\xbd\x47\xbf\xb3\xc6\x3a\x5c\xce\x0e\x36\xae\x31\x65\xf8\x81\x32\xaa\xbf\xa2\x6f\xbc\x7a\x36\xf8\xd5\x3a\xc0\xdf\xbc\x22\xc3\x57\xc2\x56\x64\x81\x35\x3e\x20\x2d\x37\xb3\x0a\xdd\x62\xfd\xb8\x99\x1a\x6d\x4b\x99\xa1\x8a\xe8\xe7\x7a\x7e\x13\x6c\x3f\x11\xfd\x85\xcb\xf9\xb0\xe3\x54\x1c\xe2\xf2\x6d\xdf\x59\xf2\x74\xb8\xb3\xbf\x3d\x45\x9d\x95\xc4\xbf\xab\xab\xd7\x0f\xe5\xbc\x14\x46\x13\x34\x65\xb8\xe6\x12\xd5\xe3\x14\x52\xb7\x76\xa4\x4d\xad\xfb\xf7\xa6\xc6\x32\xeb\x3e\x02\x09\x80\x7f\x34\x37\x15\x42\x4d\x7f\x6e\x34\x33\x5a\x15\xe4\xe9\x9f\x04\xa0\xce\x85\x51\x51\x44\x61\xfa\xba\x12\x97\x10\x05\x97\xb8\xec\xeb\xda\x1a\x8a\xea\xda\x8d\xbe\x32\x55\x48\x98\x07\x46\xf3\xa6\xc7\x48\x43\x06\x13\xf4\xc0\x78\x3e\x57\x9a\x4b\x3c\x23\xa8\xe6\xcc\x94\x04\xd5\x93\x14\x52\xb7\x5e\xcf\xd2\xd6\x0c\xed\x99\x1d\x45\xbf\x91\x3d\x22\xd8\x71\x9a\x8a\xe4\x33\xc9\x8d\x70\xe7\x19\xbe\x9c\xaf\xab\x03\x6b\x7a\xc8\x8f\x0b\x4b\xb5\x6a\x13\xcb\xa3\xb2\xb9\x92\x69\x73\xbd\x54\x2e\xa9\xd0\xbe\xa5\x06\x1c\x98\x72\x09\xf3\x0b\x05\x41\xd3\x9e\x94\x61\xc4\xb7\xef\xa9\xe4\x25\x12\x5c\xea\x86\x60\x3c\x6e\x84\x9a\x07\xd1\x9a\x90\x0a\x3b\x0d\x68\x9e\x73\xd6\x9c\x43\x2e\x5c\xbf\xce\x69\x21\xd7\x5c\x1c\x65\xcd\xbf\xf7\xa3\xb4\xe9\xe3\xdb\xd8\x8e\x8e\x23\x6c\x5e\xd8\x63\xa3\x79\xf9\xdd\x74\x17\xa3\x08\x9d\x17\xbe\x7c\x70\x27\x27\x93\x08\x5d\x90\xbe\x3c\xdf\x78\x72\xfe\x73\xec\xf0\x26\xaf\x16\xe1\x71\x94\x30\x48\x5f\x9e\xef\x2c\x9e\xd2\xb3\xd7\xcb\xe9\xf1\x68\x7c\x72\x11\x8b\xb1\x95\xbf\x0a\xe7\xe9\x28\xce\x79\xfa\x5a\x27\x39\x19\x8d\x46\x31\xce\xc9\xf8\xfc\xec\xfc\xff\xc2\x69\x8a\xbd\x38\x77\x3c\xb6\xed\x33\xdd\x3c\xb6\xe1\xab\xdf\x3e\x01\xe2\x9d\x6c\xb3\x8f\x0e\x69\x0e\xb5\x8c\x66\xcc\x43\x1d\xfb\x9e\xa5\x1f\x05\x13\x00\xdf\xe6\xbb\x3d\x60\xbf\xc6\x44\x72\x23\xed\xd8\xd6\xb4\x00\xe5\x2c\x3f\x6f\x6d\x3d\x59\x58\x67\x16\xd0\x82\x6c\x0c\x90\x81\x7e\x63\x23\xfc\x26\xb3\x23\xe0\x7a\x46\x34\x29\x05\xc3\x9a\x4c\x29\x23\x6f\x3a\x8e\x87\x81\xb1\xe3\xf8\x4f\xb0\x80\x10\xd1\x5e\x91\xc2\xf2\xad\xbd\x63\x95\x1b\x9c\xfc\x35\x8b\xa5\x6b\x35\x9d\xb9\x9b\x48\x9f\x48\x81\xa8\x40\xf5\x49\x9f\x87\x8a\x2e\xc3\x0f\x97\x90\x56\x86\xb1\x14\x7e\xdd\xae\xf8\x0b\x58\xb5\x6d\x57\xd0\xbb\x49\xab\x19\x9a\x32\x8e\x35\xad\x66\x54\x84\x21\xce\x7e\x8b\x3d\xe6\x13\xc1\x39\x0b\x71\xad\x4f\x9d\xbb\x47\xc6\x96\x11\x2b\xc5\x73\x8a\xb5\xaf\x81\xe9\xba\x68\xb3\x10\x06\xfc\x08\x78\x88\x0a\xb8\x84\xdd\x41\x66\x3e\xc4\xec\x5d\x86\x8b\x42\x12\xa5\x36\x2a\xa3\xcd\x6c\xd1\x41\x8c\x54\x6e\xeb\x4e\xf6\x2e\xa3\xc5\xb3\x86\x2d\x3f\x14\x62\xad\x71\xfe\xe8\x12\xd0\x11\xed\x9d\x80\x17\x72\x17\xc0\xd3\xd3\xa2\xe1\xda\x35\xcb\x66\x6e\x15\x0f\x9c\x1b\x2d\x8c\x5e\xff\x79\x53\x4d\xb9\x8b\xa8\xc6\xcc\x90\xf6\x8f\x36\x6d\x45\xf4\x4e\x6f\x87\xf3\xfe\x70\xb3\x51\xb6\x56\x41\xae\xa2\xbe\xfb\x3a\xb8\xca\xf9\x2f\x00\x00\xff\xff\x2c\x2f\x41\x1e\x87\x12\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xcc\x58\x5f\x6f\xdb\x36\x10\x7f\xd7\xa7\xb8\x09\x7b\x68\x8b\x45\x55\xec\x34\xc9\x06\x04\x43\x96\x0e\x5b\x80\xb5\x08\x82\xe5\x65\xc5\x20\x30\xd2\xc9\x21\x4c\x89\x1c\xff\xa8\x69\x03\x7d\xf7\x81\x22\x25\x5b\xb2\x14\x3b\x6e\x02\x14\x45\x01\xea\x78\xf7\xfb\xdd\x1d\x79\xbc\x73\x34\x4a\x49\x72\x2e\x0b\x78\x08\x00\x24\xfe\x67\xa8\xc4\x2c\x11\x92\x57\x34\x43\xa9\x1a\x31\x00\x17\x58\x2a\x4d\xd2\x25\x9c\x79\x09\x80\xe2\x46\xa6\x08\x67\x10\x76\x20\x07\xad\xdd\x41\x67\xf0\xb6\x5b\x85\xde\xae\x42\xa9\x28\x2f\xad\xe1\x2c\x8a\xa3\xd8\xc9\xeb\xc0\xfe\xaf\x83\xa0\x85\x80\x70\x65\xd9\x70\x1a\x85\x32\x29\x49\x81\x00\xd6\xf6\xe1\x21\xba\x62\x44\x5b\xda\xe8\x46\xa1\xb4\x3b\x75\x6d\xc1\x04\x51\xea\x33\x97\x19\x6c\x2a\x5e\xf9\x2d\xa7\xa8\xb1\x24\xa5\x76\x98\x03\xc5\xbf\x9b\xad\x8f\x1d\x26\x31\xfa\x2e\x31\x92\x8d\x60\x9e\x1b\x7d\x77\x73\xfd\x97\xd3\x93\xb8\xb0\xb1\xc1\x88\xde\x75\xb3\x65\xd5\xea\x20\xa8\x88\xa4\xe4\x96\x21\x84\x29\x33\x4a\xa3\x4c\x68\xe6\xc2\xd4\x5f\x04\x3a\x6b\xa5\x25\x2d\x17\x01\x40\x86\x39\x31\x4c\x7b\xc0\x0b\x67\x70\xf9\x7e\x03\x8a\xda\x74\x95\x29\x26\x29\x37\xa5\xde\x11\xee\x03\xb1\x68\xd1\x85\x35\x99\x46\xbc\xe3\x4a\xdb\x3c\x0d\x41\x19\x55\xfa\x95\x43\x7e\xdd\x83\x5e\x21\xff\xe9\x4d\xeb\x7a\xca\x5b\x61\xf6\x80\xbd\xb8\xba\x99\x44\x94\xa4\xd8\x03\xf1\xfa\xfc\xc3\x24\x62\x46\xd5\x72\x0f\xc8\xf7\x54\x2d\x27\x31\xb9\xa2\x05\x59\xe0\x8e\xe7\xd4\xdd\xa3\x3f\x98\xb5\x6e\xaf\x66\x0f\x9a\x54\x84\x32\x72\x4b\x19\xd5\x5f\x92\xaf\xbc\x7c\x32\xf6\xf9\x1a\xc0\x3f\xbc\xc4\xe9\x0b\x61\xab\x31\x23\x9a\xec\x91\x94\xdf\x38\xd7\x17\xbc\xcc\xe9\x62\x33\x35\xda\x16\x32\x4b\x4a\xd4\x4f\x75\xfd\xd2\xdb\x7e\x44\xfd\x99\xcb\xe5\xb4\xe7\x54\xec\xe1\xf3\xe5\xd5\xd0\x57\xbc\xdf\xdf\xd7\xdf\xef\xc7\x7c\x95\xe8\x9f\xd4\xd5\xcb\x97\xa4\xbc\x10\x46\x63\x92\x33\x52\x71\x99\x54\xb3\x10\x42\xb7\x76\x9c\x4d\xa5\xfb\xd7\xa6\x22\x32\xea\x3f\x01\x01\x80\x7f\x30\x37\x15\xda\x8a\xfe\xd4\x68\x46\xb4\xcc\xf0\xfe\xdf\x00\xa0\x4a\x85\x51\xa3\x88\xc2\x0c\x75\x25\x29\x60\x14\x5c\x92\x62\xa8\x6b\x2b\x68\x54\xd7\x6e\x0c\x95\xa9\x4a\x84\xb9\x65\x34\x6d\xda\x8b\x34\x38\x99\xa0\x5b\xc6\xd3\xa5\xd2\x5c\x92\x05\x26\x15\x67\xa6\xc0\xa4\x9a\x87\x10\xba\xf5\x7a\x96\x1e\xcd\xd0\x8e\xd9\x51\xf4\x2b\xee\x10\xc1\x96\xd3\x54\x98\x2e\x24\x37\xc2\x9d\x67\xfb\xe5\x7c\x5d\x1d\x58\xd3\x41\x7e\x7c\xb0\x54\xab\x26\x51\x1f\x14\xcd\x8d\x0c\x9b\xdb\xa5\x52\x49\x85\xf6\xdd\xb4\xc5\x81\x9c\x4b\x58\x9e\x2a\x68\x35\xed\x49\x19\x86\xbe\x73\xe7\x92\x17\x89\xe0\x52\x37\x04\xb3\x59\x23\xd4\xbc\x15\xad\x09\xa9\xb0\x83\x80\xe6\x29\x67\xcd\x39\xa4\xc2\xf5\xea\x94\x66\x72\xcd\xc5\x38\x6a\xfe\xbd\x6d\x3a\x79\xfd\x28\xdb\xc1\xe1\x08\x9b\x17\x0e\xd8\x68\x5a\x7c\x33\xdd\x69\x3c\x42\xe7\x85\xcf\x1f\xdc\xd1\xd1\x7c\x84\xae\x95\x3e\x3f\xdf\x6c\x7e\xf2\xf3\xd8\xe1\xcd\x5f\x2c\xc2\xc3\x51\xc2\x56\xfa\xfc\x7c\xc7\xe3\x29\x3d\x7e\xb9\x9c\x1e\xc6\xb3\xa3\xd3\xb1\x18\x3b\xf9\x8b\x70\xbe\x8b\xc7\x39\xdf\xbd\xd4\x49\xce\xe3\x38\x1e\xe3\x9c\xcf\x4e\x8e\x4f\xbe\x17\x4e\x93\xed\xc4\xb9\xe5\xb1\xed\x9e\xe9\xe6\xb1\x6d\xbf\x86\xed\x13\x60\xbc\x93\x6d\xf6\xd1\x29\xcd\xa9\x96\xd1\x0c\x79\x49\xcf\x7e\x60\xe9\x07\xc1\x00\xc0\xb7\xf9\x7e\x0f\xd8\xad\x31\x61\x6a\xa4\x1d\xfb\x9a\x16\xa0\x9c\xe5\xa7\x47\x5b\x4f\xd4\xae\x23\x0b\x68\x41\x36\xe6\xc7\x96\x7e\x63\xa3\xfd\x3d\x66\x47\xc0\xf5\x8c\x68\x2c\x04\x23\x1a\x73\xca\xf0\x55\xcf\xf1\x76\x60\xec\x39\xfe\x13\x3c\x40\x1b\xd1\x4e\x91\x42\xfd\xda\xde\xb1\xd2\x0d\x4e\xfe\x9a\x8d\xa5\x6b\x35\x9c\xb9\x9b\x48\xef\x31\x4b\xa8\x48\xaa\xa3\x21\x0f\x15\x7d\x86\x1f\xce\x20\x2c\x0d\x63\x21\xfc\xfa\xb8\xe2\x2f\x60\xd5\x1e\xbb\x82\xde\x4d\x5a\x2e\x92\x9c\x71\xa2\x69\xb9\xa0\xa2\x1d\xe2\xec\xb7\xd8\x61\x3e\x11\x9c\xb3\x36\xae\xf5\xa1\x73\xfb\xc8\xd8\x31\x12\xa5\x78\x4a\x89\xf6\x35\x90\xaf\x8b\x36\x0b\x61\xc2\x8f\x16\x2f\xa1\x02\xce\x60\x7b\x90\x91\x0f\x31\x7a\x13\x91\x2c\x93\xa8\xd4\x46\x65\x74\x99\xcd\x7a\x88\x23\x95\xdb\xb9\x13\xbd\x89\x68\xf6\xa4\x61\xcb\x0f\x85\x44\x6b\x92\xde\xb9\x04\xf4\x44\x3b\x27\xe0\x99\xdc\x05\xf0\xf4\x34\x6b\xb8\xb6\xcd\xb2\x91\x5b\x8d\x07\xce\x8d\x16\x46\xaf\xff\xba\x29\x73\xee\x22\xaa\x08\x33\xd8\xfd\xbd\xa6\xab\x88\xc1\xe9\x6d\x71\xde\x1f\x6e\x14\x47\x6b\x15\xe4\x2a\xea\x9b\xaf\x83\xab\x9c\xff\x03\x00\x00\xff\xff\x81\x9a\x0c\xab\x82\x12\x00\x00"), }, - "/terraform/openstack/worker.tf.template": &vfsgen۰CompressedFileInfo{ + "/terraform/nestos/openstack/worker.tf.template": &vfsgen۰CompressedFileInfo{ name: "worker.tf.template", - modTime: time.Date(2024, 3, 15, 2, 41, 38, 573829831, time.UTC), - uncompressedSize: 4724, + modTime: time.Date(2024, 6, 13, 2, 44, 1, 221820446, time.UTC), + uncompressedSize: 4719, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xcc\x58\x6d\x6b\x23\x37\x10\xfe\xee\x5f\x31\x5d\xfa\xe1\xee\x68\xf6\x1c\x3b\x97\xa4\x85\x50\x42\xae\xb4\x81\xf6\x08\xa1\xa1\xd0\xa3\x08\x65\x77\xec\x08\x6b\x57\xaa\x5e\x36\xb9\x0b\xfe\xef\x45\x2b\x69\xed\x5d\xef\xc6\x8e\x2f\x81\x72\x1c\x68\x47\x33\xcf\x33\x33\xd2\x68\xc6\x31\xa8\x14\x9d\x09\x55\xc0\xe3\x08\x40\xe1\xbf\x96\x29\xcc\x89\x54\xa2\x62\x39\x2a\x5d\x8b\x01\x84\xc4\x52\x1b\x9a\x2d\xe0\x2c\x48\x00\xb4\xb0\x2a\x43\x38\x83\xa4\x01\x39\x88\x76\x07\x8d\xc1\xfb\x66\x95\x04\xbb\x0a\x95\x66\xa2\x74\x86\x87\xe9\x87\x49\x7a\xe8\x37\x96\x23\xf7\x7f\x39\x1a\x45\x0c\x48\x56\xa6\x35\xa9\xd5\xa8\x48\x49\x0b\x04\x70\xc6\x8f\x8f\xe9\x15\xa7\xc6\xf1\xa6\x37\x1a\x95\xdb\x59\x2e\x1d\x98\xa4\x5a\xdf\x0b\x95\xc3\xa6\xe2\x55\xd8\xf2\x8a\x06\x4b\x5a\x1a\x8f\xd9\x51\xfc\xd3\x6f\x7d\x6a\x40\xa9\x35\x77\xc4\x2a\xde\x03\x7a\xee\xb6\x6e\xae\x7f\xf7\x8a\x0a\xe7\x2e\x3c\xe8\x51\xbc\xae\xb7\x9c\xda\x72\x34\xaa\xa8\x62\xf4\x96\x23\x24\x19\xb7\xda\xa0\x22\x2c\xf7\x81\x9a\x2f\x12\xbd\xb5\x36\x8a\x95\xf3\x11\x40\x8e\x33\x6a\xb9\x09\x80\x17\xde\xe0\xf2\xe3\x06\x14\x73\x09\x2b\x33\x24\x99\xb0\xa5\xf1\x70\x6d\xdb\xbf\x84\x5a\xa0\x4a\x2f\xdc\xfe\xb0\xf9\x9d\xd0\xc6\xa5\xa5\xeb\x10\x67\xda\xbc\xf1\x5e\xbd\x6d\x41\xaf\x90\x7f\x0b\xa6\xcb\xe5\x90\x6b\xd2\xee\x01\x7b\x71\x75\x33\x88\xa8\x68\xb1\x07\xe2\xf5\xf9\x1f\x83\x88\x39\xd3\x8b\x3d\x20\x3f\x32\xbd\x18\xc4\x14\x9a\x15\x74\x8e\x3b\x9e\x71\x73\x69\x7e\xe5\xb5\x75\xbc\x89\x2d\x6c\x5a\x51\xc6\xe9\x2d\xe3\xcc\x7c\x21\x5f\x45\xf9\x6c\xf0\xf3\x75\x80\xbf\x45\x89\xc3\x57\xc2\x95\x5f\x4e\x0d\xdd\x23\x2d\x97\xf3\x92\x5c\x51\x73\xb7\x99\x1a\xe3\xea\x96\x93\x12\xcd\x73\x3d\xbf\x8c\xb6\x9f\xd0\xdc\x0b\xb5\x18\x76\x9c\xc9\x7d\x5c\xbe\xea\x3a\x8b\x0f\xfb\x3b\xfb\xcb\x43\xaf\xb3\x0a\xc3\x23\xba\x7a\xea\x48\x26\x0a\x69\x0d\x92\x19\xa7\x95\x50\xa4\x9a\x24\x90\xf8\xb5\x27\xad\x0b\x3b\x3c\x2e\x15\x55\x69\xbb\xe2\x47\x00\xe1\x85\xdc\x54\x88\x35\xfd\xb9\xd6\x4c\x59\x99\xe3\xc3\x3f\x23\x80\x2a\x93\x56\xf7\x22\x4a\xdb\xd5\x55\xb4\x80\x5e\x70\x45\x8b\xae\xae\xab\xa1\x5e\x5d\xb7\xd1\x55\x66\x9a\x48\x7b\xcb\x59\x56\x37\x14\x65\x71\x30\x41\xb7\x5c\x64\x0b\x6d\x84\xa2\x73\x24\x95\xe0\xb6\x40\x52\x4d\x13\x48\xfc\x7a\x3d\x4b\x4f\x66\x68\xc7\xec\x68\xf6\x15\x77\x88\x60\xcb\x69\x6a\xcc\xe6\x4a\x58\xe9\xcf\x33\x7e\x79\x5f\x57\x07\x56\x37\x8c\xef\x1f\x1d\xd5\xaa\x27\x2c\x0f\xee\xeb\x2b\x99\xd4\xd7\x4b\x67\x8a\x49\x13\xfa\x67\xc4\x81\x99\x50\xb0\x38\xd5\x10\x35\xdd\x49\x59\x8e\xa1\x57\xcf\x94\x28\x88\x14\xca\xd4\x04\x93\x49\x2d\x34\x22\x8a\xd6\x84\x4c\xba\xd6\x6f\x44\x26\x78\x7d\x0e\x99\xf4\xcd\x39\x63\xb9\x5a\x73\x71\x9c\xd6\xff\xde\x8f\x93\xba\x69\x3f\xc5\x76\x70\xd8\xc3\x16\x84\x1d\x36\x96\x15\xdf\x4c\x77\x3a\xee\xa1\x0b\xc2\x97\x0f\xee\xe8\x68\xda\x43\x17\xa5\x2f\xcf\x37\x99\x9e\xfc\xd8\x77\x78\xd3\x57\x8b\xf0\xb0\x97\x30\x4a\x5f\x9e\xef\xb8\x3f\xa5\xc7\xaf\x97\xd3\xc3\xf1\xe4\xe8\xb4\x2f\xc6\x46\xfe\x2a\x9c\x1f\xc6\xfd\x9c\x1f\x5e\xeb\x24\xa7\xe3\xf1\xb8\x8f\x73\x3a\x39\x39\x3e\xf9\xbf\x70\xda\x7c\x27\xce\x2d\x8f\x6d\xf3\x4c\xd7\x8f\x6d\xfc\xea\xb6\x4f\x80\xfe\x4e\xb6\xd9\x47\x87\x34\x87\x5a\x46\x3d\xe6\x91\x96\x7d\xc7\x32\x8c\x82\x23\x80\xd0\xe6\xdb\x3d\x60\xb7\xc6\x84\x99\x55\x6e\x6c\xab\x5b\x80\xf6\x96\x9f\x9f\x6c\x3d\x69\x5c\xa7\x0e\xd0\x81\x6c\x0c\x90\x91\x7e\x63\x23\xfe\x00\x73\x23\xe0\x7a\x46\x0c\x16\x92\x53\x83\x33\xc6\xf1\x4d\xcb\xf1\x38\x30\xb6\x1c\xff\x01\x1e\x21\x46\xb4\x53\xa4\xb0\x7c\xeb\xee\x58\xe9\x07\xa7\x70\xcd\xfa\xd2\xb5\x9a\xce\xfc\x4d\x64\x0f\x98\x13\x26\x49\x75\xd4\xe5\x61\xb2\xcd\xf0\xdd\x19\x24\xa5\xe5\x3c\x81\x9f\x9f\x56\xfc\x09\x9c\xda\x53\x57\x30\xb8\xc9\xca\x39\x99\x71\x41\x0d\x2b\xe7\x4c\xc6\x21\xce\x7d\xcb\x1d\xe6\x13\x29\x04\x8f\x71\xad\x4f\x9d\xdb\x47\xc6\x86\x91\x6a\x2d\x32\x46\x4d\xa8\x81\xd9\xba\x68\xb3\x10\x06\xfc\x88\x78\x84\x49\x38\x83\xed\x41\xa6\x21\xc4\xf4\x5d\x4a\xf3\x5c\xa1\xd6\x1b\x95\xd1\x64\x36\x6f\x21\xf6\x54\x6e\xe3\x4e\xfa\x2e\x65\xf9\xb3\x86\xad\x30\x14\x52\x63\x68\x76\xe7\x13\xd0\x12\xed\x9c\x80\x17\x72\x17\x20\xd0\xb3\xbc\xe6\xda\x36\xcb\xa6\x7e\xd5\x1f\xb8\xb0\x46\x5a\xb3\xfe\xf3\xa6\x9c\x09\x1f\x51\x45\xb9\xc5\xe6\x2f\x34\x4d\x45\x74\x4e\x6f\x8b\xf3\xe1\x70\xd3\x71\xba\x56\x41\xbe\xa2\xbe\xf9\x3a\xf8\xca\xf9\x2f\x00\x00\xff\xff\x03\xd8\xc9\xd7\x74\x12\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xcc\x58\x6f\x6b\xe4\x36\x13\x7f\xef\x4f\x31\x8f\x79\x5e\xdc\x1d\x8d\xcf\xd9\xcd\x25\x69\x21\x94\x34\x57\xda\x40\x7b\x84\xd0\x50\xe8\x51\x8c\x62\x8f\x37\x62\x65\x4b\xd5\x1f\x27\x77\xc1\xdf\xbd\xc8\x92\xbd\x6b\xaf\x9d\xdd\xec\x25\x50\x8e\x03\x79\x34\xf3\xfb\xcd\x8c\x34\x9a\xd9\x68\x94\x92\xe4\x5c\x16\xf0\x18\x00\x48\xfc\xc7\x50\x89\x59\x22\x24\xaf\x68\x86\x52\x35\x62\x00\x2e\xb0\x54\x9a\xa4\x4b\x38\xf3\x12\x00\xc5\x8d\x4c\x11\xce\x20\xec\x40\x0e\x5a\xbb\x83\xce\xe0\x7d\xb7\x0a\xbd\x5d\x85\x52\x51\x5e\x5a\xc3\x59\x14\x47\xb1\x93\xd7\x81\xfd\x5f\x07\x41\x0b\x01\xe1\xca\xb2\xe1\x34\x0a\x65\x52\x92\x02\x01\xac\xed\xe3\x63\x74\xc5\x88\xb6\xb4\xd1\x8d\x42\x69\x77\xea\xda\x82\x09\xa2\xd4\x3d\x97\x19\x6c\x2a\x5e\xf9\x2d\xa7\xa8\xb1\x24\xa5\x76\x98\x03\xc5\x3f\x9a\xad\x4f\x1d\x26\x31\xfa\x2e\x31\x92\x8d\x60\x9e\x1b\x7d\x77\x73\xfd\x9b\xd3\x93\xb8\xb0\xb1\xc1\x88\xde\x75\xb3\x65\xd5\xea\x20\xa8\x88\xa4\xe4\x96\x21\x84\x29\x33\x4a\xa3\x4c\x68\xe6\xc2\xd4\x5f\x04\x3a\x6b\xa5\x25\x2d\x17\x01\x40\x86\x39\x31\x4c\x7b\xc0\x0b\x67\x70\xf9\x71\x03\x8a\xda\x74\x95\x29\x26\x29\x37\xa5\x76\x70\x7d\xdb\x3f\xb9\x5c\xa2\x8c\x2e\xec\xfe\xb4\xf9\x1d\x57\xda\x26\x65\xe8\x10\xa3\x4a\xbf\x71\x5e\xbd\xed\x41\xaf\x90\x7f\xf5\xa6\x75\x3d\xe5\x9a\x30\x7b\xc0\x5e\x5c\xdd\x4c\x22\x4a\x52\xec\x81\x78\x7d\xfe\xfb\x24\x62\x46\xd5\x72\x0f\xc8\x8f\x54\x2d\x27\x31\xb9\xa2\x05\x59\xe0\x8e\x67\xdc\x5d\x9a\x5f\x98\xb5\x6e\xef\x61\x0f\x9a\x54\x84\x32\x72\x4b\x19\xd5\x5f\x92\xaf\xbc\x7c\x36\xf6\xf9\x1a\xc0\x5f\xbc\xc4\xe9\x0b\x61\x4b\x2f\x23\x9a\xec\x91\x94\x9f\x38\xd7\x17\xbc\xcc\xe9\x62\x33\x35\xda\x56\x2d\x4b\x4a\xd4\xcf\x75\xfd\xd2\xdb\x7e\x42\x7d\xcf\xe5\x72\xda\x73\x2a\xf6\xf0\xf9\xf2\x6a\xe8\x2b\x3e\xec\xef\xeb\xcf\x0f\x63\xbe\x4a\xf4\xef\xe7\xea\x99\x4b\x52\x5e\x08\xa3\x31\xc9\x19\xa9\xb8\x4c\xaa\x59\x08\xa1\x5b\x3b\xce\xa6\xac\xfd\xd3\x52\x11\x19\xf5\xeb\x3d\x00\xf0\xaf\xe3\xa6\x42\x5b\xd1\x9f\x1b\xcd\x88\x96\x19\x3e\xfc\x1d\x00\x54\xa9\x30\x6a\x14\x51\x98\xa1\xae\x24\x05\x8c\x82\x4b\x52\x0c\x75\x6d\x05\x8d\xea\xda\x8d\xa1\x32\x55\x89\x30\xb7\x8c\xa6\x4d\x2f\x91\x06\x27\x13\x74\xcb\x78\xba\x54\x9a\x4b\xb2\xc0\xa4\xe2\xcc\x14\x98\x54\xf3\x10\x42\xb7\x5e\xcf\xd2\x93\x19\xda\x31\x3b\x8a\x7e\xc5\x1d\x22\xd8\x72\x9a\x0a\xd3\x85\xe4\x46\xb8\xf3\x6c\xbf\x9c\xaf\xab\x03\x6b\xda\xc5\xff\x1f\x2d\xd5\xaa\x23\xd4\x07\xf7\xcd\x8d\x0c\x9b\xdb\xa5\x52\x49\x85\xf6\xad\xb3\xc5\x81\x9c\x4b\x58\x9e\x2a\x68\x35\xed\x49\x19\x86\xbe\x4d\xe7\x92\x17\x89\xe0\x52\x37\x04\xb3\x59\x23\xd4\xbc\x15\xad\x09\xa9\xb0\x5d\x5f\xf3\x94\xb3\xe6\x1c\x52\xe1\x1a\x73\x4a\x33\xb9\xe6\x62\x1c\x35\xff\xde\x37\x6d\xbb\x7e\x92\xed\xe0\x70\x84\xcd\x0b\x07\x6c\x34\x2d\xbe\x99\xee\x34\x1e\xa1\xf3\xc2\x97\x0f\xee\xe8\x68\x3e\x42\xd7\x4a\x5f\x9e\x6f\x36\x3f\xf9\x7e\xec\xf0\xe6\xaf\x16\xe1\xe1\x28\x61\x2b\x7d\x79\xbe\xe3\xf1\x94\x1e\xbf\x5e\x4e\x0f\xe3\xd9\xd1\xe9\x58\x8c\x9d\xfc\x55\x38\x3f\xc4\xe3\x9c\x1f\x5e\xeb\x24\xe7\x71\x1c\x8f\x71\xce\x67\x27\xc7\x27\xff\x15\x4e\x93\xed\xc4\xb9\xe5\xb1\xed\x9e\xe9\xe6\xb1\x6d\xbf\x86\xed\x13\x60\xbc\x93\x6d\xf6\xd1\x29\xcd\xa9\x96\xd1\x0c\x79\x49\xcf\x7e\x60\xe9\x07\xc1\x00\xc0\xb7\xf9\x7e\x0f\xd8\xad\x31\x61\x6a\xa4\x1d\xfb\x9a\x16\xa0\x9c\xe5\xe7\x27\x5b\x4f\xd4\xae\x23\x0b\x68\x41\x36\xe6\xc7\x96\x7e\x63\xa3\xfd\xf1\x65\x47\xc0\xf5\x8c\x68\x2c\x04\x23\x1a\x73\xca\xf0\x4d\xcf\xf1\x76\x60\xec\x39\xfe\x1d\x3c\x42\x1b\xd1\x4e\x91\x42\xfd\xd6\xde\xb1\xd2\x0d\x4e\xfe\x9a\x8d\xa5\x6b\x35\x9c\xb9\x9b\x48\x1f\x30\x4b\xa8\x48\xaa\xa3\x21\x0f\x15\x7d\x86\xff\x9d\x41\x58\x1a\xc6\x42\xf8\xf1\x69\xc5\x1f\xc0\xaa\x3d\x75\x05\xbd\x9b\xb4\x5c\x24\x39\xe3\x44\xd3\x72\x41\x45\x3b\xc4\xd9\x6f\xb1\xc3\x7c\x22\x38\x67\x6d\x5c\xeb\x43\xe7\xf6\x91\xb1\x63\x24\x4a\xf1\x94\x12\xed\x6b\x20\x5f\x17\x6d\x16\xc2\x84\x1f\x2d\x5e\x42\x05\x9c\xc1\xf6\x20\x23\x1f\x62\xf4\x2e\x22\x59\x26\x51\xa9\x8d\xca\xe8\x32\x9b\xf5\x10\x47\x2a\xb7\x73\x27\x7a\x17\xd1\xec\x59\xc3\x96\x1f\x0a\x89\xd6\x24\xbd\x73\x09\xe8\x89\x76\x4e\xc0\x0b\xb9\x0b\xe0\xe9\x69\xd6\x70\x6d\x9b\x65\x23\xb7\x1a\x0f\x9c\x1b\x2d\x8c\x5e\xff\x75\x53\xe6\xdc\x45\x54\x11\x66\xb0\xfb\xe3\x4c\x57\x11\x83\xd3\xdb\xe2\xbc\x3f\xdc\x28\x8e\xd6\x2a\xc8\x55\xd4\x37\x5f\x07\x57\x39\xff\x06\x00\x00\xff\xff\xf0\x12\x5c\x27\x6f\x12\x00\x00"), }, } fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig"].(os.FileInfo), fs["/housekeeper"].(os.FileInfo), - fs["/ignition"].(os.FileInfo), + fs["/kickstart"].(os.FileInfo), fs["/terraform"].(os.FileInfo), } - fs["/housekeeper"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/housekeeper/1housekeeper.io_updates.yaml"].(os.FileInfo), - fs["/housekeeper/2namespace.yaml"].(os.FileInfo), - fs["/housekeeper/3role.yaml"].(os.FileInfo), - fs["/housekeeper/4role_binding.yaml"].(os.FileInfo), - fs["/housekeeper/5deployment.yaml.template"].(os.FileInfo), - fs["/housekeeper/6daemonset.yaml.template"].(os.FileInfo), - } - fs["/ignition"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane"].(os.FileInfo), - fs["/ignition/master"].(os.FileInfo), - fs["/ignition/worker"].(os.FileInfo), - } - fs["/ignition/controlplane"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files"].(os.FileInfo), - fs["/ignition/controlplane/systemd"].(os.FileInfo), - } - fs["/ignition/controlplane/files"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc"].(os.FileInfo), - } - fs["/ignition/controlplane/files/etc"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc/hosts.template"].(os.FileInfo), - fs["/ignition/controlplane/files/etc/isulad"].(os.FileInfo), - fs["/ignition/controlplane/files/etc/nkd"].(os.FileInfo), - fs["/ignition/controlplane/files/etc/sysctl.d"].(os.FileInfo), - fs["/ignition/controlplane/files/etc/systemd"].(os.FileInfo), - } - fs["/ignition/controlplane/files/etc/isulad"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc/isulad/daemon.json.template"].(os.FileInfo), - } - fs["/ignition/controlplane/files/etc/nkd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc/nkd/init-config.yaml.template"].(os.FileInfo), - fs["/ignition/controlplane/files/etc/nkd/node-pivot.sh.template"].(os.FileInfo), - } - fs["/ignition/controlplane/files/etc/sysctl.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc/sysctl.d/kubernetes.conf"].(os.FileInfo), - } - fs["/ignition/controlplane/files/etc/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc/systemd/system"].(os.FileInfo), - } - fs["/ignition/controlplane/files/etc/systemd/system"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc/systemd/system/kubelet.service.d"].(os.FileInfo), - } - fs["/ignition/controlplane/files/etc/systemd/system/kubelet.service.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template"].(os.FileInfo), + fs["/bootconfig"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files"].(os.FileInfo), + fs["/bootconfig/systemd"].(os.FileInfo), } - fs["/ignition/controlplane/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/controlplane/systemd/init-cluster.service"].(os.FileInfo), - fs["/ignition/controlplane/systemd/kubelet.service"].(os.FileInfo), - fs["/ignition/controlplane/systemd/release-image-pivot.service"].(os.FileInfo), - fs["/ignition/controlplane/systemd/set-kernel-para.service"].(os.FileInfo), + fs["/bootconfig/files"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc"].(os.FileInfo), } - fs["/ignition/master"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files"].(os.FileInfo), - fs["/ignition/master/systemd"].(os.FileInfo), + fs["/bootconfig/files/etc"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/docker"].(os.FileInfo), + fs["/bootconfig/files/etc/hosts.template"].(os.FileInfo), + fs["/bootconfig/files/etc/isulad"].(os.FileInfo), + fs["/bootconfig/files/etc/nkdfiles"].(os.FileInfo), + fs["/bootconfig/files/etc/sysctl.d"].(os.FileInfo), + fs["/bootconfig/files/etc/systemd"].(os.FileInfo), } - fs["/ignition/master/files"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc"].(os.FileInfo), + fs["/bootconfig/files/etc/docker"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/docker/daemon.json.template"].(os.FileInfo), } - fs["/ignition/master/files/etc"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc/hosts.template"].(os.FileInfo), - fs["/ignition/master/files/etc/isulad"].(os.FileInfo), - fs["/ignition/master/files/etc/nkd"].(os.FileInfo), - fs["/ignition/master/files/etc/sysctl.d"].(os.FileInfo), - fs["/ignition/master/files/etc/systemd"].(os.FileInfo), + fs["/bootconfig/files/etc/isulad"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/isulad/daemon.json.template"].(os.FileInfo), } - fs["/ignition/master/files/etc/isulad"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc/isulad/daemon.json.template"].(os.FileInfo), + fs["/bootconfig/files/etc/nkdfiles"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/nkdfiles/init-config.yaml.template"].(os.FileInfo), + fs["/bootconfig/files/etc/nkdfiles/node-pivot.sh.template"].(os.FileInfo), } - fs["/ignition/master/files/etc/nkd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc/nkd/node-pivot.sh.template"].(os.FileInfo), + fs["/bootconfig/files/etc/sysctl.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/sysctl.d/kubernetes.conf"].(os.FileInfo), } - fs["/ignition/master/files/etc/sysctl.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc/sysctl.d/kubernetes.conf"].(os.FileInfo), + fs["/bootconfig/files/etc/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/systemd/system"].(os.FileInfo), } - fs["/ignition/master/files/etc/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc/systemd/system"].(os.FileInfo), + fs["/bootconfig/files/etc/systemd/system"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/systemd/system/kubelet.service.d"].(os.FileInfo), } - fs["/ignition/master/files/etc/systemd/system"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc/systemd/system/kubelet.service.d"].(os.FileInfo), + fs["/bootconfig/files/etc/systemd/system/kubelet.service.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template"].(os.FileInfo), } - fs["/ignition/master/files/etc/systemd/system/kubelet.service.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template"].(os.FileInfo), + fs["/bootconfig/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/bootconfig/systemd/init-cluster.service"].(os.FileInfo), + fs["/bootconfig/systemd/join-master.service.template"].(os.FileInfo), + fs["/bootconfig/systemd/join-worker.service.template"].(os.FileInfo), + fs["/bootconfig/systemd/kubelet.service"].(os.FileInfo), + fs["/bootconfig/systemd/release-image-pivot.service"].(os.FileInfo), + fs["/bootconfig/systemd/set-kernel-para.service"].(os.FileInfo), } - fs["/ignition/master/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/master/systemd/join-master.service.template"].(os.FileInfo), - fs["/ignition/master/systemd/kubelet.service"].(os.FileInfo), - fs["/ignition/master/systemd/release-image-pivot.service"].(os.FileInfo), - fs["/ignition/master/systemd/set-kernel-para.service"].(os.FileInfo), - } - fs["/ignition/worker"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files"].(os.FileInfo), - fs["/ignition/worker/systemd"].(os.FileInfo), - } - fs["/ignition/worker/files"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc"].(os.FileInfo), + fs["/housekeeper"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/housekeeper/1housekeeper.io_updates.yaml"].(os.FileInfo), + fs["/housekeeper/2namespace.yaml"].(os.FileInfo), + fs["/housekeeper/3role.yaml"].(os.FileInfo), + fs["/housekeeper/4role_binding.yaml"].(os.FileInfo), + fs["/housekeeper/5deployment.yaml.template"].(os.FileInfo), + fs["/housekeeper/6daemonset.yaml.template"].(os.FileInfo), } - fs["/ignition/worker/files/etc"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc/hosts.template"].(os.FileInfo), - fs["/ignition/worker/files/etc/isulad"].(os.FileInfo), - fs["/ignition/worker/files/etc/nkd"].(os.FileInfo), - fs["/ignition/worker/files/etc/sysctl.d"].(os.FileInfo), - fs["/ignition/worker/files/etc/systemd"].(os.FileInfo), + fs["/kickstart"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/kickstart/controlplane"].(os.FileInfo), + fs["/kickstart/master"].(os.FileInfo), + fs["/kickstart/worker"].(os.FileInfo), } - fs["/ignition/worker/files/etc/isulad"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc/isulad/daemon.json.template"].(os.FileInfo), + fs["/kickstart/controlplane"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/kickstart/controlplane/kickstart.cfg.template"].(os.FileInfo), } - fs["/ignition/worker/files/etc/nkd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc/nkd/node-pivot.sh.template"].(os.FileInfo), + fs["/kickstart/master"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/kickstart/master/kickstart.cfg.template"].(os.FileInfo), } - fs["/ignition/worker/files/etc/sysctl.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc/sysctl.d/kubernetes.conf"].(os.FileInfo), + fs["/kickstart/worker"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/kickstart/worker/kickstart.cfg.template"].(os.FileInfo), } - fs["/ignition/worker/files/etc/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc/systemd/system"].(os.FileInfo), + fs["/terraform"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/terraform/generalos"].(os.FileInfo), + fs["/terraform/nestos"].(os.FileInfo), } - fs["/ignition/worker/files/etc/systemd/system"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc/systemd/system/kubelet.service.d"].(os.FileInfo), + fs["/terraform/generalos"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/terraform/generalos/libvirt"].(os.FileInfo), + fs["/terraform/generalos/openstack"].(os.FileInfo), } - fs["/ignition/worker/files/etc/systemd/system/kubelet.service.d"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template"].(os.FileInfo), + fs["/terraform/generalos/libvirt"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/terraform/generalos/libvirt/master.tf.template"].(os.FileInfo), + fs["/terraform/generalos/libvirt/worker.tf.template"].(os.FileInfo), } - fs["/ignition/worker/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/ignition/worker/systemd/join-worker.service.template"].(os.FileInfo), - fs["/ignition/worker/systemd/kubelet.service"].(os.FileInfo), - fs["/ignition/worker/systemd/release-image-pivot.service"].(os.FileInfo), - fs["/ignition/worker/systemd/set-kernel-para.service"].(os.FileInfo), + fs["/terraform/generalos/openstack"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/terraform/generalos/openstack/master.tf.template"].(os.FileInfo), + fs["/terraform/generalos/openstack/worker.tf.template"].(os.FileInfo), } - fs["/terraform"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/terraform/libvirt"].(os.FileInfo), - fs["/terraform/openstack"].(os.FileInfo), + fs["/terraform/nestos"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/terraform/nestos/libvirt"].(os.FileInfo), + fs["/terraform/nestos/openstack"].(os.FileInfo), } - fs["/terraform/libvirt"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/terraform/libvirt/master.tf.template"].(os.FileInfo), - fs["/terraform/libvirt/worker.tf.template"].(os.FileInfo), + fs["/terraform/nestos/libvirt"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/terraform/nestos/libvirt/master.tf.template"].(os.FileInfo), + fs["/terraform/nestos/libvirt/worker.tf.template"].(os.FileInfo), } - fs["/terraform/openstack"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/terraform/openstack/master.tf.template"].(os.FileInfo), - fs["/terraform/openstack/worker.tf.template"].(os.FileInfo), + fs["/terraform/nestos/openstack"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ + fs["/terraform/nestos/openstack/master.tf.template"].(os.FileInfo), + fs["/terraform/nestos/openstack/worker.tf.template"].(os.FileInfo), } return fs diff --git a/data/data/bootconfig/files/etc/docker/daemon.json.template b/data/data/bootconfig/files/etc/docker/daemon.json.template new file mode 100644 index 0000000000000000000000000000000000000000..acd738bc96d67c85b280149cab5e19a47331ec18 --- /dev/null +++ b/data/data/bootconfig/files/etc/docker/daemon.json.template @@ -0,0 +1,4 @@ +{ + "exec-opts": ["native.cgroupdriver=systemd"], + "registry-mirrors": ["https://registry-1.docker.io"{{if .RegistryMirrors -}},"https://{{.RegistryMirrors}}"{{end -}}] +} \ No newline at end of file diff --git a/data/data/ignition/controlplane/files/etc/hosts.template b/data/data/bootconfig/files/etc/hosts.template similarity index 100% rename from data/data/ignition/controlplane/files/etc/hosts.template rename to data/data/bootconfig/files/etc/hosts.template diff --git a/data/data/ignition/controlplane/files/etc/isulad/daemon.json.template b/data/data/bootconfig/files/etc/isulad/daemon.json.template similarity index 95% rename from data/data/ignition/controlplane/files/etc/isulad/daemon.json.template rename to data/data/bootconfig/files/etc/isulad/daemon.json.template index 43a21088be8504fc63159ddfa4205684afdba6a9..c4adab4619f57d51be2fd62bbceb4ea84407d1f3 100644 --- a/data/data/ignition/controlplane/files/etc/isulad/daemon.json.template +++ b/data/data/bootconfig/files/etc/isulad/daemon.json.template @@ -1,5 +1,4 @@ { - "exec-opts": ["native.cgroupdriver=systemd"], "group": "isula", "default-runtime": "lcr", "graph": "/var/lib/isulad", diff --git a/data/data/ignition/controlplane/files/etc/nkd/init-config.yaml.template b/data/data/bootconfig/files/etc/nkdfiles/init-config.yaml.template similarity index 100% rename from data/data/ignition/controlplane/files/etc/nkd/init-config.yaml.template rename to data/data/bootconfig/files/etc/nkdfiles/init-config.yaml.template diff --git a/data/data/bootconfig/files/etc/nkdfiles/node-pivot.sh.template b/data/data/bootconfig/files/etc/nkdfiles/node-pivot.sh.template new file mode 100644 index 0000000000000000000000000000000000000000..fc4de7257d2a5bb791e11abc7cac4b0fe1e65f2f --- /dev/null +++ b/data/data/bootconfig/files/etc/nkdfiles/node-pivot.sh.template @@ -0,0 +1,450 @@ +#!/bin/bash + +exec >> /var/log/nkd_node_pivot.log 2>&1 + +# Template variables +hook_files_path="{{.HookFilesPath}}" +runtime="{{.Runtime}}" +image_registry="{{.ImageRegistry}}" +pause_image="{{.PauseImage}}" +release_image_url="{{.ReleaseImageURl}}" +certs_url="{{.CertsUrl}}" +package_list=({{range .PackageList}}"{{.}}" {{end}}) +registry_mirrors="{{.RegistryMirrors}}" + +# Function to manage services +manage_service() { + local service_name="$1" + if systemctl is-active --quiet "$service_name"; then + echo "$service_name is already running" + else + echo "$service_name is not running, starting..." + if systemctl start "$service_name" && systemctl enable "$service_name"; then + echo "$service_name starting success." + else + echo "Unable to start or enable $service_name." + exit 1 + fi + fi +} + +# Function to check and start service +check_and_start_service() { + local service_name="$1" + if systemctl list-unit-files | grep -q "$service_name.service"; then + manage_service "$service_name" + else + echo "$service_name service does not exist, skipping..." + fi +} + +# Function to disable firewall if it's enabled +disable_firewall() { + # Check if firewall is enabled + if systemctl is-active --quiet firewalld; then + echo "Firewall is running, disabling..." + + if systemctl stop firewalld && systemctl disable firewalld; then + echo "Firewall disabled successfully." + else + echo "Failed to disable firewall." + exit 1 + fi + else + echo "Firewall is not running." + fi +} + +# Function to execute hook files +execute_hookfiles() { + local directory="$1" + if [ ! -d "$directory" ]; then + return + fi + + local shell_files=("$directory"/*) + if [ ${#shell_files[@]} -eq 0 ]; then + echo "No files found in hook directory: $directory" + return + fi + + for file in "${shell_files[@]}"; do + if [ -f "$file" ]; then + echo "Executing script: $file" + . "$file" + fi + done +} + +# Function to configure CRI-O runtime +configure_crio_runtime() { + local config_file="/etc/crio/crio.conf" + + # Check if the CRI-O config file exists + if [ ! -f "$config_file" ]; then + echo "CRI-O config file not found: $config_file" + exit 1 + fi + + if grep -q "\[crio\.image\]" "$config_file"; then + # If the line with 'pause_image' doesn't exist, add it directly + if ! grep -q "^pause_image" "$config_file"; then + sed -i '/^\[crio\.image\]/a pause_image = "'"$image_registry/$pause_image"'"' "$config_file" + else + # If the line with 'pause_image' exists, replace its value + sed -i 's|^pause_image = .*|pause_image = "'"$image_registry/$pause_image"'"|' "$config_file" + fi + else + echo -e "[crio.image]\npause_image = \"$image_registry/$pause_image\"" >> "$config_file" + fi + + if systemctl restart crio; then + echo "Successfully restarted CRI-O" + else + echo "Failed to restart CRI-O" + exit 1 + fi +} + +# Function to configure CRI-O registry +configure_crio_registry() { + local config_file="/etc/containers/registries.conf" + + if ! grep -qE '^\s*(#?\s*)unqualified-search-registries\s*=' "$config_file"; then + # Define unqualified-search-registries if it doesn't exist or if it's commented out + echo "unqualified-search-registries = [\"docker.io\"]" | sudo tee -a "$config_file" >/dev/null + fi + + if [ -n "$registry_mirrors" ]; then + local config_content=$(cat </dev/null + echo "CRI-O registry configuration completed" + + if systemctl restart crio; then + echo "Successfully restarted CRI-O" + else + echo "Failed to restart CRI-O" + exit 1 + fi + fi +} + +update_kubelet_config() { + local kubelet_conf_dir="/etc/systemd/system/kubelet.service.d" + local new_conf="$kubelet_conf_dir/10-kubeadm.conf" + local old_conf="$kubelet_conf_dir/kubeadm.conf" + + if [ -f "$new_conf" ]; then + rm -f "$old_conf" + mv "$new_conf" "$old_conf" + + systemctl daemon-reload + systemctl restart kubelet + + echo "Kubelet configuration updated successfully." + else + echo "Error:kubelet configuration file not found." + exit 1 + fi +} + + +# Function to configure CRI-O runtime +configure_containerd_runtime() { + containerd config default > /etc/containerd/config.toml + sandbox_image="$image_registry/$pause_image" + + if sed -i "s#^\(\s*sandbox_image\s*=\s*\).*\$#\1\"$sandbox_image\"#" /etc/containerd/config.toml; then + echo "sandbox_image field in /etc/containerd/config.toml updated successfully." + else + echo "Failed to update sandbox_image field in /etc/containerd/config.toml." + fi + + if systemctl restart containerd; then + echo "Successfully restarted containerd" + else + echo "Failed to restart containerd" + exit 1 + fi +} + +# Function to configure containerd registry mirrors +configure_containerd_registry() { + local config_file="/etc/containerd/config.toml" + + if [ -n "$registry_mirrors" ]; then + local config_content=$(cat < "$temp_file" + + sudo mv "$temp_file" "$config_file" + echo "containerd registry mirrors configuration completed" + + # Restart containerd service + if sudo systemctl restart containerd; then + echo "Successfully restarted containerd" + else + echo "Failed to restart containerd" + exit 1 + fi + fi +} + +# Function to create crictl configuration file +create_crictl_config() { + local config_file="/etc/crictl.yaml" + + # Create configuration file + cat < "$config_file" +runtime-endpoint: "unix:///run/containerd/containerd.sock" +image-endpoint: "unix:///run/containerd/containerd.sock" +timeout: 0 +debug: false +EOF_CRICTL + + # Check if configuration file is created successfully + if [ -f "$config_file" ]; then + echo "crictl configuration file created successfully at $config_file." + else + echo "Failed to create crictl configuration file at $config_file." + fi +} + +# Function to perform OSTree rebase +perform_rebase() { + local url="$1" + if [ -z "$url" ]; then + echo "release_image_url is empty, skipping rpm-ostree rebase." + else + if rpm-ostree rebase --experimental "ostree-unverified-image:docker://$url" --bypass-driver; then + echo "Rebase operation completed successfully. Rebooting the system..." + systemctl reboot + else + echo "Rebase operation failed. System will not be rebooted." + exit 1 + fi + fi +} + +# Function to fetch and save certificates +fetch_and_save_certificates() { + local certs_dir="/etc/kubernetes/pki/etcd" + + mkdir -p "$certs_dir" + local response=$(curl -s "$certs_url") + if [ -z "$response" ]; then + echo "Error: Failed to fetch JSON data." + exit 1 + fi + + local path content + while IFS= read -r line; do + read -r path content <<< "$line" + content=$(echo "$content" | base64 -d) + echo "$content" > "$path" + done < <(echo "$response" | jq -r '.[] | "\(.Path) \(.Content)"') +} + +# Function to define the installer packages based on the environment +define_installer_packages() { + local packages=( + conntrack-tools + cri-tools + socat + containernetworking-plugins + iptables + jq + wget + ) + {{if .IsDocker -}} + packages+=(docker-engine) + {{end -}} + {{if .IsIsulad -}} + packages+=(iSulad) + {{end -}} + {{if .IsCrio -}} + packages+=(cri-o) + {{end -}} + {{if .IsContainerd -}} + packages+=(containerd) + {{end -}} + echo "${packages[@]}" +} + +# Function to check and install dependent packages +check_and_install_dependent_packages() { + echo "Installing dependent packages..." + local packages_to_install=() + + # check yum package + if [[ ${#package_list[@]} -ne 0 && "${package_list[0]}" != "" ]]; then + echo "Using configured package list:" + packages_to_install+=("${package_list[@]}") + fi + + #check rpm package + if [ -n "{{.RpmPackageCurl}}" ]; then + rpm_url={{.RpmPackageCurl}} + architecture=$(uname -m) + rpm_package_path="/etc/nkdfiles/packages/$architecture" + if [ ! -d "$rpm_package_path" ]; then + mkdir -p "$rpm_package_path" + fi + + # Fetch package list from the provided URL + package_list=$(curl -s "$rpm_url" | grep -oP 'href="\K[^"]+' | grep -E '\.rpm$') + + if [[ -n "$package_list" ]]; then + echo "Downloading RPM packages from $rpm_url" + for package_filename in $package_list; do + package_url="${rpm_url%/}/$package_filename" + package_path="$rpm_package_path/$package_filename" + + if [ -f "$package_path" ]; then + echo "Package already exists. Skipping download." + else + if wget -P "$rpm_package_path" "$package_url" >/dev/null 2>&1; then + echo "Package $package_filename downloaded successfully." + chmod +x "$package_path" + packages_to_install+=("$package_path") + else + echo "Failed to download package $package_filename." + exit 1 + fi + fi + done + else + echo "No RPM packages found at $rpm_url" + fi + fi + + # if package_list and rpm_package_list are empty + if [[ ${#packages_to_install[@]} -eq 0 ]]; then + echo "Using default package list:" + packages_to_install=($(define_installer_packages)) + fi + + for package in "${packages_to_install[@]}"; do + if sudo yum install -y "$package" >/dev/null 2>&1; then + echo "Package $package installed successfully." + else + echo "Failed to install package $package." + exit 1 + fi + done +} + +check_and_install_kube_binaries() { + local binaries=( + "kubeadm" + "kubelet" + "kubectl" + ) + for binary in "${binaries[@]}"; do + if ! command -v "$(basename "$binary")" &>/dev/null; then + echo "$binary is missing, installing it using yum." + sudo yum install -y /usr/bin/"$binary" + if [ $? -ne 0 ]; then + echo "Failed to install $binary." + exit 1 + else + echo "$(basename "$binary") installed successfully." + fi + fi + done +} + +# Function to disable swap +disable_swap() { + # Check if the user has sufficient privileges + if [[ $EUID -ne 0 ]]; then + echo "This function must be run as root" + exit 1 + fi + + echo "Disabling swap..." + swapoff -a + + # Check if swap is disabled + if [[ $? -eq 0 ]]; then + echo "Swap disabled successfully." + else + echo "Failed to disable swap." + exit 1 + fi +} + +# Function to disable SELinux +disable_selinux() { + if [[ $EUID -ne 0 ]]; then + echo "This function must be run as root" + exit 1 + fi + # Disable SELinux + setenforce 0 + echo "SELinux disabled successfully." +} + +# Execute hook files +execute_hookfiles "${hook_files_path}" + +# Call the function to disable firewall +disable_firewall + +{{if or .IsGeneralOS -}} +check_and_install_dependent_packages +check_and_install_kube_binaries +{{if .IsControlPlane -}} +fetch_and_save_certificates +{{end -}} +{{if .IsCrio -}} +update_kubelet_config +{{end -}} +systemctl enable kubelet.service +systemctl restart set-kernel-para.service +{{end -}} + +# Start necessary services +check_and_start_service "${runtime}" + +# Call the function to disable swap +disable_swap + +# Call the function to disable SELinux +disable_selinux + +{{if .IsCrio -}} +configure_crio_runtime +configure_crio_registry +{{end -}} + +{{if .IsContainerd}} +configure_containerd_runtime +create_crictl_config +configure_containerd_registry +{{end -}} + +# Perform OSTree rebase for nestos +{{if .IsNestOS -}} +perform_rebase "$release_image_url" +{{end -}} + +echo "release-image-pivot.service complete" \ No newline at end of file diff --git a/data/data/ignition/master/files/etc/sysctl.d/kubernetes.conf b/data/data/bootconfig/files/etc/sysctl.d/kubernetes.conf similarity index 77% rename from data/data/ignition/master/files/etc/sysctl.d/kubernetes.conf rename to data/data/bootconfig/files/etc/sysctl.d/kubernetes.conf index cadba01da1e0c01c2964780977205ec1418d48dc..fd63d576131c080c0bde08375881ac36ba35c359 100644 --- a/data/data/ignition/master/files/etc/sysctl.d/kubernetes.conf +++ b/data/data/bootconfig/files/etc/sysctl.d/kubernetes.conf @@ -1,3 +1,3 @@ net.bridge.bridge-nf-call-iptables=1 net.bridge.bridge-nf-call-ip6tables=1 -net.ipv4.ip_forward=1 +net.ipv4.ip_forward=1 \ No newline at end of file diff --git a/data/data/ignition/controlplane/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template b/data/data/bootconfig/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template similarity index 100% rename from data/data/ignition/controlplane/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template rename to data/data/bootconfig/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template diff --git a/data/data/ignition/controlplane/systemd/init-cluster.service b/data/data/bootconfig/systemd/init-cluster.service similarity index 73% rename from data/data/ignition/controlplane/systemd/init-cluster.service rename to data/data/bootconfig/systemd/init-cluster.service index 829b275af487186d8693c2736a5746cc2be7b672..9dc884f601d756cdc88ae67019938dd01cafa2fd 100644 --- a/data/data/ignition/controlplane/systemd/init-cluster.service +++ b/data/data/bootconfig/systemd/init-cluster.service @@ -6,7 +6,7 @@ ConditionPathExists=!/var/log/init-cluster.stamp [Service] ExecStartPre=/bin/bash -c "while [ ! -f /var/log/node-pivot.stamp ]; do sleep 10; done" -ExecStart=/bin/bash -c "kubeadm init --config=/etc/nkd/init-config.yaml --upload-certs && /bin/touch /var/log/init-cluster.stamp" +ExecStart=/bin/bash -c "kubeadm init --config=/etc/nkdfiles/init-config.yaml --upload-certs && /bin/touch /var/log/init-cluster.stamp" Restart=on-failure RestartSec=5s diff --git a/data/data/ignition/master/systemd/join-master.service.template b/data/data/bootconfig/systemd/join-master.service.template similarity index 100% rename from data/data/ignition/master/systemd/join-master.service.template rename to data/data/bootconfig/systemd/join-master.service.template diff --git a/data/data/ignition/worker/systemd/join-worker.service.template b/data/data/bootconfig/systemd/join-worker.service.template similarity index 95% rename from data/data/ignition/worker/systemd/join-worker.service.template rename to data/data/bootconfig/systemd/join-worker.service.template index ccc8bd9f9d9de26ccf5882866af2e105341f787d..01e94f09f4c74e0e407d13309344a2bc2e420cb5 100644 --- a/data/data/ignition/worker/systemd/join-worker.service.template +++ b/data/data/bootconfig/systemd/join-worker.service.template @@ -11,4 +11,4 @@ Restart=on-failure RestartSec=5s [Install] -WantedBy=multi-user.target +WantedBy=multi-user.target \ No newline at end of file diff --git a/data/data/ignition/worker/systemd/kubelet.service b/data/data/bootconfig/systemd/kubelet.service similarity index 91% rename from data/data/ignition/worker/systemd/kubelet.service rename to data/data/bootconfig/systemd/kubelet.service index b8ff7f2b7706dc80e7246ab3c7e5e7ae3ea87ad3..9890eb270017f129b6a47610e43d50e78b308d4b 100644 --- a/data/data/ignition/worker/systemd/kubelet.service +++ b/data/data/bootconfig/systemd/kubelet.service @@ -12,5 +12,4 @@ StartLimitInterval=0 RestartSec=10 [Install] -WantedBy=multi-user.target - +WantedBy=multi-user.target \ No newline at end of file diff --git a/data/data/ignition/controlplane/systemd/release-image-pivot.service b/data/data/bootconfig/systemd/release-image-pivot.service similarity index 65% rename from data/data/ignition/controlplane/systemd/release-image-pivot.service rename to data/data/bootconfig/systemd/release-image-pivot.service index 275ffe0affd18b35bf08f05f0f2eadcea6a516bf..d563fe913ec0097fe1195f7dfdf7ba0912426c35 100644 --- a/data/data/ignition/controlplane/systemd/release-image-pivot.service +++ b/data/data/bootconfig/systemd/release-image-pivot.service @@ -5,10 +5,10 @@ After=network-online.target ConditionPathExists=!/var/log/node-pivot.stamp [Service] -ExecStart=/bin/bash -c "/etc/nkd/node-pivot.sh && touch /var/log/node-pivot.stamp" +ExecStart=/bin/bash -c "/etc/nkdfiles/node-pivot.sh && touch /var/log/node-pivot.stamp" Restart=on-failure RestartSec=5s [Install] -WantedBy=multi-user.target +WantedBy=multi-user.target \ No newline at end of file diff --git a/data/data/ignition/worker/systemd/set-kernel-para.service b/data/data/bootconfig/systemd/set-kernel-para.service similarity index 90% rename from data/data/ignition/worker/systemd/set-kernel-para.service rename to data/data/bootconfig/systemd/set-kernel-para.service index e56bda35f943cdda2f347dc2bada792c9bffab62..220eb0b7e9a7aae94e10f8435c35b9e9f2e4b041 100644 --- a/data/data/ignition/worker/systemd/set-kernel-para.service +++ b/data/data/bootconfig/systemd/set-kernel-para.service @@ -10,5 +10,4 @@ ExecStart=modprobe br_netfilter ExecStart=sysctl -p /etc/sysctl.d/kubernetes.conf [Install] -WantedBy=multi-user.target - +WantedBy=multi-user.target \ No newline at end of file diff --git a/data/data/ignition/controlplane/files/etc/nkd/node-pivot.sh.template b/data/data/ignition/controlplane/files/etc/nkd/node-pivot.sh.template deleted file mode 100644 index b9eea04e82a7fa203085308e085a3fc0e9fc0283..0000000000000000000000000000000000000000 --- a/data/data/ignition/controlplane/files/etc/nkd/node-pivot.sh.template +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh - -# Function to manage service startup and enable on boot -manage_service() { - service_name="$1" - if systemctl is-active --quiet "$service_name"; then - echo "$service_name is already running" - else - echo "$service_name is not running, starting..." - if systemctl start "$service_name" && systemctl enable "$service_name"; then - echo "$service_name starting success." - else - echo "Unable to start $service_name." - exit 1 - fi - fi -} - -# Check if service exists and manage it -check_and_manage_service() { - service_name="$1" - if systemctl list-unit-files | grep -q "$service_name.service"; then - manage_service "$service_name" - else - echo "$service_name service does not exist, skipping..." - fi -} - -check_and_manage_service "{{.Runtime}}" -check_and_manage_service "housekeeper-daemon" - -# Configure the crio container runtime -if [ -f "/etc/crio/crio.conf" ]; then - if [ "{{.Runtime}}" = "crio" ]; then - if grep -q "\[crio\.image\]" /etc/crio/crio.conf; then - if grep -q "^[[:space:]]*pause_image = " /etc/crio/crio.conf; then - sed -i 's|^pause_image = .*|pause_image = "{{.ImageRegistry}}/{{.PauseImage}}"|' /etc/crio/crio.conf - else - sed -i '/\[crio\.image\]/a pause_image = "{{.ImageRegistry}}/{{.PauseImage}}"' /etc/crio/crio.conf - fi - else - echo "[crio.image]" >> /etc/crio/crio.conf - echo "pause_image = \"{{.ImageRegistry}}/{{.PauseImage}}\"" >> /etc/crio/crio.conf - fi - systemctl restart crio - fi -fi - -# Disable SELinux -echo "Disabling SELinux..." -sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config -setenforce 0 - -# Check if ReleaseImageURl is empty -if [ -n "{{.ReleaseImageURl}}" ]; then - # Execute rebase - rpm-ostree rebase --experimental ostree-unverified-image:docker://{{.ReleaseImageURl}} --bypass-driver - # Check if the rebase was successful - if [ $? -eq 0 ]; then - echo "Rebase operation completed successfully. Rebooting the system..." - systemctl reboot - else - echo "Rebase operation failed. System will not be rebooted." - fi - -else - echo "ReleaseImageURl is empty, skipping rpm-ostree rebase." -fi diff --git a/data/data/ignition/controlplane/files/etc/sysctl.d/kubernetes.conf b/data/data/ignition/controlplane/files/etc/sysctl.d/kubernetes.conf deleted file mode 100644 index cadba01da1e0c01c2964780977205ec1418d48dc..0000000000000000000000000000000000000000 --- a/data/data/ignition/controlplane/files/etc/sysctl.d/kubernetes.conf +++ /dev/null @@ -1,3 +0,0 @@ -net.bridge.bridge-nf-call-iptables=1 -net.bridge.bridge-nf-call-ip6tables=1 -net.ipv4.ip_forward=1 diff --git a/data/data/ignition/controlplane/systemd/kubelet.service b/data/data/ignition/controlplane/systemd/kubelet.service deleted file mode 100644 index b8ff7f2b7706dc80e7246ab3c7e5e7ae3ea87ad3..0000000000000000000000000000000000000000 --- a/data/data/ignition/controlplane/systemd/kubelet.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=kubelet: The Kubernetes Node Agent -Documentation=https://kubernetes.io/docs/ -Wants=network-online.target -After=network-online.target -ConditionPathExists=/var/log/node-pivot.stamp - -[Service] -ExecStart=/usr/bin/kubelet -Restart=always -StartLimitInterval=0 -RestartSec=10 - -[Install] -WantedBy=multi-user.target - diff --git a/data/data/ignition/controlplane/systemd/set-kernel-para.service b/data/data/ignition/controlplane/systemd/set-kernel-para.service deleted file mode 100644 index e56bda35f943cdda2f347dc2bada792c9bffab62..0000000000000000000000000000000000000000 --- a/data/data/ignition/controlplane/systemd/set-kernel-para.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=set kernel para for Kubernetes -Requires=release-image-pivot.service -After=release-image-pivot.service - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=modprobe br_netfilter -ExecStart=sysctl -p /etc/sysctl.d/kubernetes.conf - -[Install] -WantedBy=multi-user.target - diff --git a/data/data/ignition/master/files/etc/hosts.template b/data/data/ignition/master/files/etc/hosts.template deleted file mode 100644 index bdb6bb4bac215dc26c7219eb4c74f4908936db7e..0000000000000000000000000000000000000000 --- a/data/data/ignition/master/files/etc/hosts.template +++ /dev/null @@ -1,3 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 -{{.Hsip}} \ No newline at end of file diff --git a/data/data/ignition/master/files/etc/isulad/daemon.json.template b/data/data/ignition/master/files/etc/isulad/daemon.json.template deleted file mode 100644 index 43a21088be8504fc63159ddfa4205684afdba6a9..0000000000000000000000000000000000000000 --- a/data/data/ignition/master/files/etc/isulad/daemon.json.template +++ /dev/null @@ -1,43 +0,0 @@ -{ - "exec-opts": ["native.cgroupdriver=systemd"], - "group": "isula", - "default-runtime": "lcr", - "graph": "/var/lib/isulad", - "state": "/var/run/isulad", - "engine": "lcr", - "log-level": "ERROR", - "pidfile": "/var/run/isulad.pid", - "log-opts": { - "log-file-mode": "0600", - "log-path": "/var/lib/isulad", - "max-file": "1", - "max-size": "30KB" - }, - "log-driver": "stdout", - "container-log": { - "driver": "json-file" - }, - "hook-spec": "/etc/default/isulad/hooks/default.json", - "start-timeout": "2m", - "storage-driver": "overlay2", - "storage-opts": [ - "overlay2.override_kernel_check=true" - ], - "registry-mirrors": [ - "docker.io" - ], - "insecure-registries": [ - "{{.ImageRegistry}}" - ], - "pod-sandbox-image": "{{.ImageRegistry}}/{{.PauseImage}}", - "native.umask": "secure", - "network-plugin": "cni", - "cni-bin-dir": "/opt/cni/bin", - "cni-conf-dir": "/etc/cni/net.d", - "image-layer-check": false, - "use-decrypted-key": true, - "insecure-skip-verify-enforce": false, - "cri-runtimes": { - "kata": "io.containerd.kata.v2" - } -} diff --git a/data/data/ignition/master/files/etc/nkd/node-pivot.sh.template b/data/data/ignition/master/files/etc/nkd/node-pivot.sh.template deleted file mode 100644 index b9eea04e82a7fa203085308e085a3fc0e9fc0283..0000000000000000000000000000000000000000 --- a/data/data/ignition/master/files/etc/nkd/node-pivot.sh.template +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh - -# Function to manage service startup and enable on boot -manage_service() { - service_name="$1" - if systemctl is-active --quiet "$service_name"; then - echo "$service_name is already running" - else - echo "$service_name is not running, starting..." - if systemctl start "$service_name" && systemctl enable "$service_name"; then - echo "$service_name starting success." - else - echo "Unable to start $service_name." - exit 1 - fi - fi -} - -# Check if service exists and manage it -check_and_manage_service() { - service_name="$1" - if systemctl list-unit-files | grep -q "$service_name.service"; then - manage_service "$service_name" - else - echo "$service_name service does not exist, skipping..." - fi -} - -check_and_manage_service "{{.Runtime}}" -check_and_manage_service "housekeeper-daemon" - -# Configure the crio container runtime -if [ -f "/etc/crio/crio.conf" ]; then - if [ "{{.Runtime}}" = "crio" ]; then - if grep -q "\[crio\.image\]" /etc/crio/crio.conf; then - if grep -q "^[[:space:]]*pause_image = " /etc/crio/crio.conf; then - sed -i 's|^pause_image = .*|pause_image = "{{.ImageRegistry}}/{{.PauseImage}}"|' /etc/crio/crio.conf - else - sed -i '/\[crio\.image\]/a pause_image = "{{.ImageRegistry}}/{{.PauseImage}}"' /etc/crio/crio.conf - fi - else - echo "[crio.image]" >> /etc/crio/crio.conf - echo "pause_image = \"{{.ImageRegistry}}/{{.PauseImage}}\"" >> /etc/crio/crio.conf - fi - systemctl restart crio - fi -fi - -# Disable SELinux -echo "Disabling SELinux..." -sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config -setenforce 0 - -# Check if ReleaseImageURl is empty -if [ -n "{{.ReleaseImageURl}}" ]; then - # Execute rebase - rpm-ostree rebase --experimental ostree-unverified-image:docker://{{.ReleaseImageURl}} --bypass-driver - # Check if the rebase was successful - if [ $? -eq 0 ]; then - echo "Rebase operation completed successfully. Rebooting the system..." - systemctl reboot - else - echo "Rebase operation failed. System will not be rebooted." - fi - -else - echo "ReleaseImageURl is empty, skipping rpm-ostree rebase." -fi diff --git a/data/data/ignition/master/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template b/data/data/ignition/master/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template deleted file mode 100644 index febcd36a57f3e4536edfce5f87cf3b4c09c6c90b..0000000000000000000000000000000000000000 --- a/data/data/ignition/master/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template +++ /dev/null @@ -1,14 +0,0 @@ -# Note: This dropin only works with kubeadm and kubelet v1.11+ -[Service] -Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" -Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" -{{ if eq .Runtime "crio" }} -Environment="KUBELET_CGROUPWM_ARGS=--cgroups-per-qos=false --enforce-node-allocatable=''" -{{ end }} -# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically -EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env -# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use -# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file. -EnvironmentFile=-/etc/sysconfig/kubelet -ExecStart= -ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS {{ if eq .Runtime "crio" }}$KUBELET_CGROUPWM_ARGS{{ end }} \ No newline at end of file diff --git a/data/data/ignition/master/systemd/kubelet.service b/data/data/ignition/master/systemd/kubelet.service deleted file mode 100644 index b8ff7f2b7706dc80e7246ab3c7e5e7ae3ea87ad3..0000000000000000000000000000000000000000 --- a/data/data/ignition/master/systemd/kubelet.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=kubelet: The Kubernetes Node Agent -Documentation=https://kubernetes.io/docs/ -Wants=network-online.target -After=network-online.target -ConditionPathExists=/var/log/node-pivot.stamp - -[Service] -ExecStart=/usr/bin/kubelet -Restart=always -StartLimitInterval=0 -RestartSec=10 - -[Install] -WantedBy=multi-user.target - diff --git a/data/data/ignition/master/systemd/release-image-pivot.service b/data/data/ignition/master/systemd/release-image-pivot.service deleted file mode 100644 index 275ffe0affd18b35bf08f05f0f2eadcea6a516bf..0000000000000000000000000000000000000000 --- a/data/data/ignition/master/systemd/release-image-pivot.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Pivot node to the nkd release image -Wants=network-online.target -After=network-online.target -ConditionPathExists=!/var/log/node-pivot.stamp - -[Service] -ExecStart=/bin/bash -c "/etc/nkd/node-pivot.sh && touch /var/log/node-pivot.stamp" - -Restart=on-failure -RestartSec=5s - -[Install] -WantedBy=multi-user.target diff --git a/data/data/ignition/master/systemd/set-kernel-para.service b/data/data/ignition/master/systemd/set-kernel-para.service deleted file mode 100644 index e56bda35f943cdda2f347dc2bada792c9bffab62..0000000000000000000000000000000000000000 --- a/data/data/ignition/master/systemd/set-kernel-para.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=set kernel para for Kubernetes -Requires=release-image-pivot.service -After=release-image-pivot.service - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=modprobe br_netfilter -ExecStart=sysctl -p /etc/sysctl.d/kubernetes.conf - -[Install] -WantedBy=multi-user.target - diff --git a/data/data/ignition/worker/files/etc/hosts.template b/data/data/ignition/worker/files/etc/hosts.template deleted file mode 100644 index bdb6bb4bac215dc26c7219eb4c74f4908936db7e..0000000000000000000000000000000000000000 --- a/data/data/ignition/worker/files/etc/hosts.template +++ /dev/null @@ -1,3 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 -{{.Hsip}} \ No newline at end of file diff --git a/data/data/ignition/worker/files/etc/isulad/daemon.json.template b/data/data/ignition/worker/files/etc/isulad/daemon.json.template deleted file mode 100644 index 43a21088be8504fc63159ddfa4205684afdba6a9..0000000000000000000000000000000000000000 --- a/data/data/ignition/worker/files/etc/isulad/daemon.json.template +++ /dev/null @@ -1,43 +0,0 @@ -{ - "exec-opts": ["native.cgroupdriver=systemd"], - "group": "isula", - "default-runtime": "lcr", - "graph": "/var/lib/isulad", - "state": "/var/run/isulad", - "engine": "lcr", - "log-level": "ERROR", - "pidfile": "/var/run/isulad.pid", - "log-opts": { - "log-file-mode": "0600", - "log-path": "/var/lib/isulad", - "max-file": "1", - "max-size": "30KB" - }, - "log-driver": "stdout", - "container-log": { - "driver": "json-file" - }, - "hook-spec": "/etc/default/isulad/hooks/default.json", - "start-timeout": "2m", - "storage-driver": "overlay2", - "storage-opts": [ - "overlay2.override_kernel_check=true" - ], - "registry-mirrors": [ - "docker.io" - ], - "insecure-registries": [ - "{{.ImageRegistry}}" - ], - "pod-sandbox-image": "{{.ImageRegistry}}/{{.PauseImage}}", - "native.umask": "secure", - "network-plugin": "cni", - "cni-bin-dir": "/opt/cni/bin", - "cni-conf-dir": "/etc/cni/net.d", - "image-layer-check": false, - "use-decrypted-key": true, - "insecure-skip-verify-enforce": false, - "cri-runtimes": { - "kata": "io.containerd.kata.v2" - } -} diff --git a/data/data/ignition/worker/files/etc/nkd/node-pivot.sh.template b/data/data/ignition/worker/files/etc/nkd/node-pivot.sh.template deleted file mode 100644 index b9eea04e82a7fa203085308e085a3fc0e9fc0283..0000000000000000000000000000000000000000 --- a/data/data/ignition/worker/files/etc/nkd/node-pivot.sh.template +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh - -# Function to manage service startup and enable on boot -manage_service() { - service_name="$1" - if systemctl is-active --quiet "$service_name"; then - echo "$service_name is already running" - else - echo "$service_name is not running, starting..." - if systemctl start "$service_name" && systemctl enable "$service_name"; then - echo "$service_name starting success." - else - echo "Unable to start $service_name." - exit 1 - fi - fi -} - -# Check if service exists and manage it -check_and_manage_service() { - service_name="$1" - if systemctl list-unit-files | grep -q "$service_name.service"; then - manage_service "$service_name" - else - echo "$service_name service does not exist, skipping..." - fi -} - -check_and_manage_service "{{.Runtime}}" -check_and_manage_service "housekeeper-daemon" - -# Configure the crio container runtime -if [ -f "/etc/crio/crio.conf" ]; then - if [ "{{.Runtime}}" = "crio" ]; then - if grep -q "\[crio\.image\]" /etc/crio/crio.conf; then - if grep -q "^[[:space:]]*pause_image = " /etc/crio/crio.conf; then - sed -i 's|^pause_image = .*|pause_image = "{{.ImageRegistry}}/{{.PauseImage}}"|' /etc/crio/crio.conf - else - sed -i '/\[crio\.image\]/a pause_image = "{{.ImageRegistry}}/{{.PauseImage}}"' /etc/crio/crio.conf - fi - else - echo "[crio.image]" >> /etc/crio/crio.conf - echo "pause_image = \"{{.ImageRegistry}}/{{.PauseImage}}\"" >> /etc/crio/crio.conf - fi - systemctl restart crio - fi -fi - -# Disable SELinux -echo "Disabling SELinux..." -sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config -setenforce 0 - -# Check if ReleaseImageURl is empty -if [ -n "{{.ReleaseImageURl}}" ]; then - # Execute rebase - rpm-ostree rebase --experimental ostree-unverified-image:docker://{{.ReleaseImageURl}} --bypass-driver - # Check if the rebase was successful - if [ $? -eq 0 ]; then - echo "Rebase operation completed successfully. Rebooting the system..." - systemctl reboot - else - echo "Rebase operation failed. System will not be rebooted." - fi - -else - echo "ReleaseImageURl is empty, skipping rpm-ostree rebase." -fi diff --git a/data/data/ignition/worker/files/etc/sysctl.d/kubernetes.conf b/data/data/ignition/worker/files/etc/sysctl.d/kubernetes.conf deleted file mode 100644 index cadba01da1e0c01c2964780977205ec1418d48dc..0000000000000000000000000000000000000000 --- a/data/data/ignition/worker/files/etc/sysctl.d/kubernetes.conf +++ /dev/null @@ -1,3 +0,0 @@ -net.bridge.bridge-nf-call-iptables=1 -net.bridge.bridge-nf-call-ip6tables=1 -net.ipv4.ip_forward=1 diff --git a/data/data/ignition/worker/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template b/data/data/ignition/worker/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template deleted file mode 100644 index febcd36a57f3e4536edfce5f87cf3b4c09c6c90b..0000000000000000000000000000000000000000 --- a/data/data/ignition/worker/files/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template +++ /dev/null @@ -1,14 +0,0 @@ -# Note: This dropin only works with kubeadm and kubelet v1.11+ -[Service] -Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" -Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" -{{ if eq .Runtime "crio" }} -Environment="KUBELET_CGROUPWM_ARGS=--cgroups-per-qos=false --enforce-node-allocatable=''" -{{ end }} -# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically -EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env -# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use -# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file. -EnvironmentFile=-/etc/sysconfig/kubelet -ExecStart= -ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS {{ if eq .Runtime "crio" }}$KUBELET_CGROUPWM_ARGS{{ end }} \ No newline at end of file diff --git a/data/data/ignition/worker/systemd/release-image-pivot.service b/data/data/ignition/worker/systemd/release-image-pivot.service deleted file mode 100644 index 385b05e7a6405b9ef6ea9aaeecead27a5d38a1e7..0000000000000000000000000000000000000000 --- a/data/data/ignition/worker/systemd/release-image-pivot.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Pivot node to the nkd release image -Wants=network-online.target -After=network-online.target -ConditionPathExists=!/var/log/node-pivot.stamp - -[Service] -ExecStart=/bin/bash -c "/etc/nkd/node-pivot.sh && touch /var/log/node-pivot.stamp" - -Restart=on-failure -RestartSec=5s - -[Install] -WantedBy=multi-user.target - diff --git a/data/data/kickstart/controlplane/kickstart.cfg.template b/data/data/kickstart/controlplane/kickstart.cfg.template new file mode 100644 index 0000000000000000000000000000000000000000..6cdea8e70c45dcb97e648053e1f3538f147af67c --- /dev/null +++ b/data/data/kickstart/controlplane/kickstart.cfg.template @@ -0,0 +1,52 @@ +#version=DEVEL +ignoredisk --only-use=sda +autopart --type=lvm +# Partition clearing information +clearpart --none --initlabel +# Use graphical install +graphical +# Keyboard layouts +keyboard --vckeymap=cn --xlayouts='cn' +# System language +lang zh_CN.UTF-8 + +# Network information +network --bootproto=dhcp --device=enp4s0 --ipv6=auto --activate +network --hostname={{.Hostname}} +# Root password +rootpw --iscrypted {{.Password}} +# Run the Setup Agent on first boot +firstboot --enable +# Do not configure the X Window System +skipx +# System services +services --disabled="chronyd" +# System timezone +timezone Asia/Shanghai --utc + +%packages +@^minimal-environment + +%end + +%post --log=/var/log/ks-post.log +mkdir -p /etc/nkdfiles/hookfiles/ +mkdir -p /etc/systemd/system/kubelet.service.d + +{{if .IsDocker -}} +mkdir -p /etc/docker +{{end -}} + +{{if .IsIsulad -}} +mkdir -p /etc/isulad +{{end -}} + +{{range .Files}} +{{.Content}} +{{.ChangeMod}} +{{end}} + +{{range .Systemds}} +{{.}} +{{end}} +%end \ No newline at end of file diff --git a/data/data/kickstart/master/kickstart.cfg.template b/data/data/kickstart/master/kickstart.cfg.template new file mode 100644 index 0000000000000000000000000000000000000000..6cdea8e70c45dcb97e648053e1f3538f147af67c --- /dev/null +++ b/data/data/kickstart/master/kickstart.cfg.template @@ -0,0 +1,52 @@ +#version=DEVEL +ignoredisk --only-use=sda +autopart --type=lvm +# Partition clearing information +clearpart --none --initlabel +# Use graphical install +graphical +# Keyboard layouts +keyboard --vckeymap=cn --xlayouts='cn' +# System language +lang zh_CN.UTF-8 + +# Network information +network --bootproto=dhcp --device=enp4s0 --ipv6=auto --activate +network --hostname={{.Hostname}} +# Root password +rootpw --iscrypted {{.Password}} +# Run the Setup Agent on first boot +firstboot --enable +# Do not configure the X Window System +skipx +# System services +services --disabled="chronyd" +# System timezone +timezone Asia/Shanghai --utc + +%packages +@^minimal-environment + +%end + +%post --log=/var/log/ks-post.log +mkdir -p /etc/nkdfiles/hookfiles/ +mkdir -p /etc/systemd/system/kubelet.service.d + +{{if .IsDocker -}} +mkdir -p /etc/docker +{{end -}} + +{{if .IsIsulad -}} +mkdir -p /etc/isulad +{{end -}} + +{{range .Files}} +{{.Content}} +{{.ChangeMod}} +{{end}} + +{{range .Systemds}} +{{.}} +{{end}} +%end \ No newline at end of file diff --git a/data/data/kickstart/worker/kickstart.cfg.template b/data/data/kickstart/worker/kickstart.cfg.template new file mode 100644 index 0000000000000000000000000000000000000000..9d87fa30b5310545f5f7bee0044defad89efe836 --- /dev/null +++ b/data/data/kickstart/worker/kickstart.cfg.template @@ -0,0 +1,58 @@ +#version=DEVEL +ignoredisk --only-use=sda +autopart --type=lvm +# Partition clearing information +clearpart --none --initlabel +# Use graphical install +graphical +# Keyboard layouts +keyboard --vckeymap=cn --xlayouts='cn' +# System language +lang zh_CN.UTF-8 + +# Network information +network --bootproto=dhcp --device=enp4s0 --ipv6=auto --activate +# Root password +rootpw --iscrypted {{.Password}} +# Run the Setup Agent on first boot +firstboot --enable +# Do not configure the X Window System +skipx +# System services +services --disabled="chronyd" +# System timezone +timezone Asia/Shanghai --utc + +%packages +@^minimal-environment + +%end + +%post --log=/var/log/ks-post.log +# Set the hostname +prefix="k8s-" +random_string=$(uuidgen | tr -d '-' | head -c $((12 - ${#prefix}))) +hostname="${prefix}${random_string}" +echo $hostname > /etc/hostname +hostnamectl set-hostname $hostname + +mkdir -p /etc/nkdfiles/hookfiles/ +mkdir -p /etc/systemd/system/kubelet.service.d + +{{if .IsDocker -}} +mkdir -p /etc/docker +{{end -}} + +{{if .IsIsulad -}} +mkdir -p /etc/isulad +{{end -}} + +{{range .Files}} +{{.Content}} +{{.ChangeMod}} +{{end}} + +{{range .Systemds}} +{{.}} +{{end}} +%end \ No newline at end of file diff --git a/data/data/terraform/generalos/libvirt/master.tf.template b/data/data/terraform/generalos/libvirt/master.tf.template new file mode 100644 index 0000000000000000000000000000000000000000..a3eb23e0ad4556b76257a0d6c29b712793c6070d --- /dev/null +++ b/data/data/terraform/generalos/libvirt/master.tf.template @@ -0,0 +1,156 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.7.6" + } + } +} + +provider "libvirt" { + uri = "{{.Platform.URI}}" +} + +variable "cluster_id" { + type = string + default = "{{.ClusterID}}" +} + +variable "instance_count" { + type = string + default = "{{.Master.Count}}" +} + +variable "instance_hostname" { + type = list(string) + default = {{.Master.Hostname}} +} + +variable "instance_ip" { + type = list(string) + default = {{.Master.IP}} +} + +variable "cluster_ipv4_cidr" { + type = string + default = "{{.Platform.CIDR}}" +} + +variable "cluster_ipv4_gateway" { + type = string + default = "{{.Platform.Gateway}}" +} + +variable "instance_cpu" { + type = list(string) + default = {{.Master.CPU}} +} + +variable "instance_ram" { + type = list(string) + default = {{.Master.RAM}} +} + +variable "instance_disk" { + type = list(string) + default = {{.Master.Disk}} +} + +variable "instance_cloudinit" { + type = list(string) + default = {{.Master.BootConfig}} +} + +resource "libvirt_pool" "pool" { + name = "${var.cluster_id}-pool" + type = "dir" + path = "/var/lib/libvirt/images/${var.cluster_id}" +} + +resource "libvirt_volume" "volume" { + name = "${var.cluster_id}-volume" + pool = libvirt_pool.pool.name + source = "{{.Platform.OSImage}}" +} + +resource "libvirt_volume" "disk" { + count = var.instance_count + name = "${var.instance_hostname[count.index]}-disk" + base_volume_id = libvirt_volume.volume.id + pool = libvirt_pool.pool.name + size = var.instance_disk[count.index] * 1024 * 1024 * 1024 +} + +resource "libvirt_cloudinit_disk" "cloudinit" { + count = var.instance_count + name = "${var.instance_hostname[count.index]}.iso" + pool = libvirt_pool.pool.name + user_data = data.template_file.user_data.*.rendered[count.index] +} + +data "template_file" "user_data" { + count = var.instance_count + template = templatefile(var.instance_cloudinit[count.index], { hostname = var.instance_hostname[count.index] }) +} + +resource "libvirt_network" "network" { + name = "${var.cluster_id}-net" + mode = "nat" + domain = "${var.cluster_id}.local" + addresses = [var.cluster_ipv4_cidr] + + dhcp { + enabled = true + } + + dns { + enabled = true + local_only = true + } + + routes { + cidr = var.cluster_ipv4_cidr + gateway = var.cluster_ipv4_gateway + } + + autostart = true +} + +resource "libvirt_domain" "generalos" { + count = var.instance_count + name = var.instance_hostname[count.index] + cpu { + mode = "host-passthrough" + } + vcpu = var.instance_cpu[count.index] + memory = var.instance_ram[count.index] + cloudinit = libvirt_cloudinit_disk.cloudinit.*.id[count.index] + machine = "{{.MachineType}}" + autostart = true + type = "kvm" + + disk { + volume_id = libvirt_volume.disk.*.id[count.index] + } + + network_interface { + network_name = libvirt_network.network.name + hostname = var.instance_hostname[count.index] + addresses = var.instance_ip[count.index] != "null" ? [var.instance_ip[count.index]] : null + wait_for_lease = true + } + + graphics { + type = "vnc" + listen_type = "address" + } + + console { + type = "pty" + target_port = "0" + } +} + +output "ipv4" { + value = libvirt_domain.generalos.*.network_interface.0.addresses +} \ No newline at end of file diff --git a/data/data/terraform/generalos/libvirt/worker.tf.template b/data/data/terraform/generalos/libvirt/worker.tf.template new file mode 100644 index 0000000000000000000000000000000000000000..b2e962278cabae172ac1db8f5a1ce0ac6c663aee --- /dev/null +++ b/data/data/terraform/generalos/libvirt/worker.tf.template @@ -0,0 +1,116 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.7.6" + } + } +} + +provider "libvirt" { + uri = "{{.Platform.URI}}" +} + +variable "cluster_id" { + type = string + default = "{{.ClusterID}}" +} + +variable "instance_count" { + default = "{{.Worker.Count}}" +} + +variable "instance_hostname" { + type = list(string) + default = {{.Worker.Hostname}} +} + +variable "instance_ip" { + type = list(string) + default = {{.Worker.IP}} +} + +variable "instance_cpu" { + type = list(string) + default = {{.Worker.CPU}} +} + +variable "instance_ram" { + type = list(string) + default = {{.Worker.RAM}} +} + +variable "instance_disk" { + type = list(string) + default = {{.Worker.Disk}} +} + +variable "instance_cloudinit" { + type = list(string) + default = {{.Worker.BootConfig}} +} + +resource "libvirt_volume" "volume" { + name = "${var.cluster_id}-volume" + pool = "${var.cluster_id}-pool" + source = "{{.Platform.OSImage}}" +} + +resource "libvirt_volume" "disk" { + count = var.instance_count + name = "${var.instance_hostname[count.index]}-disk" + base_volume_name = "${var.cluster_id}-volume" + pool = "${var.cluster_id}-pool" + size = var.instance_disk[count.index] * 1024 * 1024 * 1024 +} + +resource "libvirt_cloudinit_disk" "cloudinit" { + count = var.instance_count + name = "${var.instance_hostname[count.index]}.iso" + pool = "${var.cluster_id}-pool" + user_data = data.template_file.user_data.*.rendered[count.index] +} + +data "template_file" "user_data" { + count = var.instance_count + template = templatefile(var.instance_cloudinit[count.index], { hostname = var.instance_hostname[count.index] }) +} + +resource "libvirt_domain" "generalos" { + count = var.instance_count + name = var.instance_hostname[count.index] + cpu { + mode = "host-passthrough" + } + vcpu = var.instance_cpu[count.index] + memory = var.instance_ram[count.index] + cloudinit = libvirt_cloudinit_disk.cloudinit.*.id[count.index] + machine = "{{.MachineType}}" + autostart = true + type = "kvm" + + disk { + volume_id = libvirt_volume.disk.*.id[count.index] + } + + network_interface { + network_name = "${var.cluster_id}-net" + hostname = var.instance_hostname[count.index] + addresses = var.instance_ip[count.index] != "null" ? [var.instance_ip[count.index]] : null + wait_for_lease = true + } + + graphics { + type = "vnc" + listen_type = "address" + } + + console { + type = "pty" + target_port = "0" + } +} + +output "ipv4" { + value = libvirt_domain.generalos.*.network_interface.0.addresses +} \ No newline at end of file diff --git a/data/data/terraform/openstack/master.tf.template b/data/data/terraform/generalos/openstack/master.tf.template similarity index 93% rename from data/data/terraform/openstack/master.tf.template rename to data/data/terraform/generalos/openstack/master.tf.template index f3a61535cc5044d989c772f811a0b382b24c8cdc..1ce2e85538b71c8d6dcdf380f70dd0df0ebc7e34 100644 --- a/data/data/terraform/openstack/master.tf.template +++ b/data/data/terraform/generalos/openstack/master.tf.template @@ -2,7 +2,7 @@ terraform { required_providers { openstack = { source = "terraform-provider-openstack/openstack" - version = "1.52.1" + version = "2.0.0" } } } @@ -10,8 +10,8 @@ terraform { provider "openstack" { user_name = "{{.Platform.Username}}" password = "{{.Platform.Password}}" - tenant_name = "{{.Platform.Tenant_Name}}" - auth_url = "{{.Platform.Auth_URL}}" + tenant_name = "{{.Platform.TenantName}}" + auth_url = "{{.Platform.AuthURL}}" region = "{{.Platform.Region}}" } @@ -47,22 +47,22 @@ variable "instance_disk" { variable "instance_osimage" { type = string - default = "{{.Platform.Glance_Name}}" + default = "{{.Platform.GlanceName}}" } variable "availability_zone" { type = string - default = "{{.Platform.Availability_Zone}}" + default = "{{.Platform.AvailabilityZone}}" } variable "instance_userdata" { type = list(string) - default = {{.Master.Ign_Path}} + default = {{.Master.BootConfig}} } variable "internal_net" { type = string - default = "{{.Platform.Internal_Network}}" + default = "{{.Platform.InternalNetwork}}" } variable "instance_ip" { @@ -72,7 +72,7 @@ variable "instance_ip" { variable "external_net" { type = string - default = "{{.Platform.External_Network}}" + default = "{{.Platform.ExternalNetwork}}" } resource "openstack_compute_flavor_v2" "flavor" { diff --git a/data/data/terraform/openstack/worker.tf.template b/data/data/terraform/generalos/openstack/worker.tf.template similarity index 93% rename from data/data/terraform/openstack/worker.tf.template rename to data/data/terraform/generalos/openstack/worker.tf.template index 2c6c2b77cde174158d972300cfbf30487ffefc2e..408ac115601b574ebfd667c173f49d1308a3499f 100644 --- a/data/data/terraform/openstack/worker.tf.template +++ b/data/data/terraform/generalos/openstack/worker.tf.template @@ -2,7 +2,7 @@ terraform { required_providers { openstack = { source = "terraform-provider-openstack/openstack" - version = "1.52.1" + version = "2.0.0" } } } @@ -10,8 +10,8 @@ terraform { provider "openstack" { user_name = "{{.Platform.Username}}" password = "{{.Platform.Password}}" - tenant_name = "{{.Platform.Tenant_Name}}" - auth_url = "{{.Platform.Auth_URL}}" + tenant_name = "{{.Platform.TenantName}}" + auth_url = "{{.Platform.AuthURL}}" region = "{{.Platform.Region}}" } @@ -46,22 +46,22 @@ variable "instance_disk" { variable "instance_osimage" { type = string - default = "{{.Platform.Glance_Name}}" + default = "{{.Platform.GlanceName}}" } variable "availability_zone" { type = string - default = "{{.Platform.Availability_Zone}}" + default = "{{.Platform.AvailabilityZone}}" } variable "instance_userdata" { type = list(string) - default = {{.Worker.Ign_Path}} + default = {{.Worker.BootConfig}} } variable "internal_net" { type = string - default = "{{.Platform.Internal_Network}}" + default = "{{.Platform.InternalNetwork}}" } variable "instance_ip" { @@ -71,7 +71,7 @@ variable "instance_ip" { variable "external_net" { type = string - default = "{{.Platform.External_Network}}" + default = "{{.Platform.ExternalNetwork}}" } resource "openstack_compute_flavor_v2" "flavor" { diff --git a/data/data/terraform/libvirt/master.tf.template b/data/data/terraform/nestos/libvirt/master.tf.template similarity index 97% rename from data/data/terraform/libvirt/master.tf.template rename to data/data/terraform/nestos/libvirt/master.tf.template index 6d9cc0b18d36c0e4beca1e1a71369d63d689a26a..2f514ee4b3064a00a23a4a6826e5229d3752a10d 100644 --- a/data/data/terraform/libvirt/master.tf.template +++ b/data/data/terraform/nestos/libvirt/master.tf.template @@ -58,7 +58,7 @@ variable "instance_disk" { variable "instance_ign" { type = list(string) - default = {{.Master.Ign_Path}} + default = {{.Master.BootConfig}} } resource "libvirt_pool" "pool" { @@ -70,7 +70,7 @@ resource "libvirt_pool" "pool" { resource "libvirt_volume" "volume" { name = "${var.cluster_id}-volume" pool = libvirt_pool.pool.name - source = "{{.Platform.OSImage_Path}}" + source = "{{.Platform.OSImage}}" } resource "libvirt_volume" "disk" { diff --git a/data/data/terraform/libvirt/worker.tf.template b/data/data/terraform/nestos/libvirt/worker.tf.template similarity index 97% rename from data/data/terraform/libvirt/worker.tf.template rename to data/data/terraform/nestos/libvirt/worker.tf.template index 6a85f6d4286f520e0e3914288ee2cd41e186148a..d56edce0df600e8f57b01327e2477808cea39ee6 100644 --- a/data/data/terraform/libvirt/worker.tf.template +++ b/data/data/terraform/nestos/libvirt/worker.tf.template @@ -47,13 +47,13 @@ variable "instance_disk" { variable "instance_ign" { type = list(string) - default = {{.Worker.Ign_Path}} + default = {{.Worker.BootConfig}} } resource "libvirt_volume" "volume" { name = "${var.cluster_id}-volume" pool = "${var.cluster_id}-pool" - source = "{{.Platform.OSImage_Path}}" + source = "{{.Platform.OSImage}}" } resource "libvirt_volume" "disk" { diff --git a/data/data/terraform/nestos/openstack/master.tf.template b/data/data/terraform/nestos/openstack/master.tf.template new file mode 100644 index 0000000000000000000000000000000000000000..1ce2e85538b71c8d6dcdf380f70dd0df0ebc7e34 --- /dev/null +++ b/data/data/terraform/nestos/openstack/master.tf.template @@ -0,0 +1,212 @@ +terraform { + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + version = "2.0.0" + } + } +} + +provider "openstack" { + user_name = "{{.Platform.Username}}" + password = "{{.Platform.Password}}" + tenant_name = "{{.Platform.TenantName}}" + auth_url = "{{.Platform.AuthURL}}" + region = "{{.Platform.Region}}" +} + +variable "cluster_id" { + type = string + default = "{{.ClusterID}}" +} + +variable "instance_count" { + type = string + default = "{{.Master.Count}}" +} + +variable "instance_hostname" { + type = list(string) + default = {{.Master.Hostname}} +} + +variable "instance_cpu" { + type = list(string) + default = {{.Master.CPU}} +} + +variable "instance_ram" { + type = list(string) + default = {{.Master.RAM}} +} + +variable "instance_disk" { + type = list(string) + default = {{.Master.Disk}} +} + +variable "instance_osimage" { + type = string + default = "{{.Platform.GlanceName}}" +} + +variable "availability_zone" { + type = string + default = "{{.Platform.AvailabilityZone}}" +} + +variable "instance_userdata" { + type = list(string) + default = {{.Master.BootConfig}} +} + +variable "internal_net" { + type = string + default = "{{.Platform.InternalNetwork}}" +} + +variable "instance_ip" { + type = list(string) + default = {{.Master.IP}} +} + +variable "external_net" { + type = string + default = "{{.Platform.ExternalNetwork}}" +} + +resource "openstack_compute_flavor_v2" "flavor" { + count = var.instance_count + name = var.instance_hostname[count.index] + vcpus = var.instance_cpu[count.index] + ram = var.instance_ram[count.index] + disk = var.instance_disk[count.index] + is_public = "true" +} + +resource "openstack_blockstorage_volume_v3" "volume" { + count = var.instance_count + name = var.instance_hostname[count.index] + size = var.instance_disk[count.index] +} + +resource "openstack_compute_secgroup_v2" "secgroup" { + name = "${var.cluster_id}-master" + description = "secgroup for k8s master" + + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = -1 + to_port = -1 + ip_protocol = "icmp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 80 + to_port = 80 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 443 + to_port = 443 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 2379 + to_port = 2380 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 179 + to_port = 179 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 6443 + to_port = 6443 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 10248 + to_port = 10248 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 10250 + to_port = 10250 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 30000 + to_port = 32767 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 30000 + to_port = 32767 + ip_protocol = "udp" + cidr = "0.0.0.0/0" + } +} + +resource "openstack_compute_instance_v2" "instance" { + count = var.instance_count + name = var.instance_hostname[count.index] + image_name = var.instance_osimage + flavor_name = var.instance_hostname[count.index] + security_groups = [openstack_compute_secgroup_v2.secgroup.name] + availability_zone = var.availability_zone + user_data = templatefile(var.instance_userdata[count.index], { hostname = var.instance_hostname[count.index] }) + + network { + name = var.internal_net + fixed_ip_v4 = var.instance_ip[count.index] != "null" ? var.instance_ip[count.index] : null + } +} + +resource "openstack_networking_floatingip_v2" "floatip" { + count = var.instance_count + pool = var.external_net +} + +resource "openstack_compute_floatingip_associate_v2" "fip_associate" { + count = var.instance_count + floating_ip = openstack_networking_floatingip_v2.floatip.*.address[count.index] + instance_id = openstack_compute_instance_v2.instance.*.id[count.index] +} + +resource "openstack_compute_volume_attach_v2" "volume_attach" { + count = var.instance_count + instance_id = openstack_compute_instance_v2.instance.*.id[count.index] + volume_id = openstack_blockstorage_volume_v3.volume.*.id[count.index] +} + +output "instance_info" { + value = { + internal_ip = openstack_compute_instance_v2.instance.*.network.0.fixed_ip_v4 + floating_ip = openstack_networking_floatingip_v2.floatip.*.address + } +} \ No newline at end of file diff --git a/data/data/terraform/nestos/openstack/worker.tf.template b/data/data/terraform/nestos/openstack/worker.tf.template new file mode 100644 index 0000000000000000000000000000000000000000..408ac115601b574ebfd667c173f49d1308a3499f --- /dev/null +++ b/data/data/terraform/nestos/openstack/worker.tf.template @@ -0,0 +1,211 @@ +terraform { + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + version = "2.0.0" + } + } +} + +provider "openstack" { + user_name = "{{.Platform.Username}}" + password = "{{.Platform.Password}}" + tenant_name = "{{.Platform.TenantName}}" + auth_url = "{{.Platform.AuthURL}}" + region = "{{.Platform.Region}}" +} + +variable "cluster_id" { + type = string + default = "{{.ClusterID}}" +} + +variable "instance_count" { + default = "{{.Worker.Count}}" +} + +variable "instance_hostname" { + type = list(string) + default = {{.Worker.Hostname}} +} + +variable "instance_cpu" { + type = list(string) + default = {{.Worker.CPU}} +} + +variable "instance_ram" { + type = list(string) + default = {{.Worker.RAM}} +} + +variable "instance_disk" { + type = list(string) + default = {{.Worker.Disk}} +} + +variable "instance_osimage" { + type = string + default = "{{.Platform.GlanceName}}" +} + +variable "availability_zone" { + type = string + default = "{{.Platform.AvailabilityZone}}" +} + +variable "instance_userdata" { + type = list(string) + default = {{.Worker.BootConfig}} +} + +variable "internal_net" { + type = string + default = "{{.Platform.InternalNetwork}}" +} + +variable "instance_ip" { + type = list(string) + default = {{.Worker.IP}} +} + +variable "external_net" { + type = string + default = "{{.Platform.ExternalNetwork}}" +} + +resource "openstack_compute_flavor_v2" "flavor" { + count = var.instance_count + name = var.instance_hostname[count.index] + vcpus = var.instance_cpu[count.index] + ram = var.instance_ram[count.index] + disk = var.instance_disk[count.index] + is_public = "true" +} + +resource "openstack_blockstorage_volume_v3" "volume" { + count = var.instance_count + name = var.instance_hostname[count.index] + size = var.instance_disk[count.index] +} + +resource "openstack_compute_secgroup_v2" "secgroup" { + name = "${var.cluster_id}-worker" + description = "secgroup for k8s worker" + + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = -1 + to_port = -1 + ip_protocol = "icmp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 80 + to_port = 80 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 443 + to_port = 443 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 2379 + to_port = 2380 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 179 + to_port = 179 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 6443 + to_port = 6443 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 10248 + to_port = 10248 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 10250 + to_port = 10250 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 30000 + to_port = 32767 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + + rule { + from_port = 30000 + to_port = 32767 + ip_protocol = "udp" + cidr = "0.0.0.0/0" + } +} + +resource "openstack_compute_instance_v2" "instance" { + count = var.instance_count + name = var.instance_hostname[count.index] + image_name = var.instance_osimage + flavor_name = var.instance_hostname[count.index] + security_groups = [openstack_compute_secgroup_v2.secgroup.name] + availability_zone = var.availability_zone + user_data = templatefile(var.instance_userdata[count.index], { hostname = var.instance_hostname[count.index] }) + + network { + name = var.internal_net + fixed_ip_v4 = var.instance_ip[count.index] != "null" ? var.instance_ip[count.index] : null + } +} + +resource "openstack_networking_floatingip_v2" "floatip" { + count = var.instance_count + pool = var.external_net +} + +resource "openstack_compute_floatingip_associate_v2" "fip_associate" { + count = var.instance_count + floating_ip = openstack_networking_floatingip_v2.floatip.*.address[count.index] + instance_id = openstack_compute_instance_v2.instance.*.id[count.index] +} + +resource "openstack_compute_volume_attach_v2" "volume_attach" { + count = var.instance_count + instance_id = openstack_compute_instance_v2.instance.*.id[count.index] + volume_id = openstack_blockstorage_volume_v3.volume.*.id[count.index] +} + +output "instance_info" { + value = { + internal_ip = openstack_compute_instance_v2.instance.*.network.0.fixed_ip_v4 + floating_ip = openstack_networking_floatingip_v2.floatip.*.address + } +} \ No newline at end of file diff --git a/docs/en/config_file_desc.md b/docs/en/config_file_desc.md index ab71c49ca9162226be4e60823b981e70f4c151b1..49e13e61986d0d53b0a4680d123b575f8232dbae 100644 --- a/docs/en/config_file_desc.md +++ b/docs/en/config_file_desc.md @@ -1,15 +1,12 @@ # Cluster config file description -For the NestOS image download address, see[website](https://nestos.openeuler.org/) ``` shell cluster_id: cluster # cluster name architecture: amd64 # deploy cluster architecture, support amd64 or arm64 -platform: libvirt # deployment platform is libvirt -infraplatform - uri: qemu:///system - osimage: https://nestos.org.cn/nestos20230928/nestos-for-container/x86_64/NestOS-For-Container-22.03-LTS-SP2.20230928.0-qemu.{arch}.qcow2 # image URL,support amd64 or arm64 - cidr: 192.168.132.0/24 - gateway: 192.168.132.1 +platform: libvirt # deployment platform is libvirt、openstack、pxe + # Parameters need to be set according to different deployment platforms +osImage: + type: # Specify the type of operating system, such as nestos or generalos. username: root # Specify the username for ssh login password: $1$yoursalt$UGhjCXAJKpWWpeN8xsF.c/ # Specify the password for ssh login sshkey: "/root/.ssh/id_rsa.pub" # The storage path of the ssh-key file @@ -27,21 +24,24 @@ worker: # worker config ram: 8192 disk: 50 ip: "" # If the worker node IP address is not set, it will be automatically assigned by dhcp and will be empty by default. -runtime: isulad # support docker、isulad、crio +runtime: isulad # support docker、isulad、containerd and crio kubernetes: - kubernetes-version: "v1.23.10" + kubernetes-version: "v1.29.1" kubernetes-apiversion: "v1beta3" # support v1beta3、v1beta2、v1beta1 apiserver-endpoint: "192.168.132.11:6443" - image-registry: "k8s.gcr.io" - pause-image: "pause:3.6" - release-image-url: "hub.oepkgs.net/nestos/nestos:22.03-LTS-SP2.20230928.0-{arch}-k8s-v1.23.10" + image-registry: "registry.k8s.io" # The image repository address used during Kubeadm initialization + registryMirror: "" # The mirror site address of the image repository used when downloading the container image + pause-image: "pause:3.9" + release-image-url: "" token: "" # automatically generated by default adminkubeconfig: /etc/nkd/cluster/admin.config # path of admin.conf certificatekey: "" # The key used to decrypt the certificate in the downloaded Secret when adding a new control plane node + packageList: # List of RPM package names that need to be installed in the cluster environment + rpmPackagePath: "" # Path to the RPM package files that need to be installed in the cluster environment network: service-subnet: "10.96.0.0/16" pod-subnet: "10.244.0.0/16" - plugin: https://projectcalico.docs.tigera.io/archive/v3.22/manifests/calico.yaml # network plugin + plugin: "" # network plugin housekeeper: # housekeeper deployhousekeeper: false operatorimageurl: "hub.oepkgs.net/nestos/housekeeper/{arch}/housekeeper-operator-manager:{tag}" # housekeeper-operator image URL @@ -57,6 +57,16 @@ certasset: # Configure user-defined cer sakey: "" ``` +Specify deployment platform configuration parameters for libvirt as an example: +``` shell +platform: libvirt # Deployment platform is libvirt +infraPlatform + uri: qemu:///system + osPath: # Specify the operating system image address for deploying cluster machines, supporting architectures x86_64 or aarch64 + cidr: 192.168.132.0/24 # Routing address + gateway: 192.168.132.1 # Gateway address +``` + To set the deployment platform to openstack, you need to reset the "infraplatform" field configuration parameters. ``` shell platform: openstack @@ -70,4 +80,40 @@ infraplatform external_network: glance_name: # qcow2 image availability_zone: # default nova -``` \ No newline at end of file +``` + +Specify deployment platform configuration parameters for PXE as an example: +``` shell +platform: pxe # Deployment platform is PXE +infraPlatform + ip: # IP address of the HTTP server + httpServerPort: "9080" # Port number of the HTTP server + httpRootDir: /var/www/html/ # Root directory of the HTTP server + tftpServerPort: "69" # Port number of the TFTP server + tftpRootDir: /var/lib/tftpboot/ # Root directory of the TFTP server +``` + +## Image Download Links + +- NestOS image download, please visit the [official website](https://nestos.openeuler.org/), and download the NestOS For Container version. +- For OpenEuler image download, please visit the [official website](https://www.openeuler.org/). + +## Password Cipher Generation Methods: +- When specifying the underlying operating system of the cluster as nestos, a cipher password needs to be used. Here's the generation method: + ``` shell + openssl passwd -1 -salt yoursalt + Password: qwer1234!@#$ + $1$yoursalt$UGhjCXAJKpWWpeN8xsF.c/ + ``` + +- When deploying the platform as pxe, a cipher password needs to be used. + ``` shell + # python3 + Python 3.7.9 (default, Mar 2 2021, 02:43:11) + [GCC 7.3.0] on linux + Type "help", "copyright", "credits" or "license" for more information. + >>> import crypt + >>> passwd = crypt.crypt("myPasswd") + >>> print (passwd) + $6$sH1qri2n14V1VCv/$fWnV3rPv95gWHJ3wZu6o0bBGy.SnllSw4a2HuoP45jXfI9fCrwe60AULO/0aXS7dWTSwvwdqqY4yFhwUdJcb.0 + ``` \ No newline at end of file diff --git a/docs/en/figures/config_manager_design.jpg b/docs/en/figures/config_manager_design.jpg index 1e83bbf989b363fd47b25b732182adce4f318a9f..06bd46e3140ba4479d5e5465355bbb7f30465882 100644 Binary files a/docs/en/figures/config_manager_design.jpg and b/docs/en/figures/config_manager_design.jpg differ diff --git a/docs/en/figures/detailed_design.jpg b/docs/en/figures/detailed_design.jpg index b29d9e71ddeb74dfc0a9e7b6bf2b1e87b7070c34..b30dc233105f528ac7439c85c4be044bb1983130 100644 Binary files a/docs/en/figures/detailed_design.jpg and b/docs/en/figures/detailed_design.jpg differ diff --git a/docs/en/figures/ignition_design.jpg b/docs/en/figures/ignition_design.jpg deleted file mode 100644 index f5b15d19934411c6650f657285983782a0cf1317..0000000000000000000000000000000000000000 Binary files a/docs/en/figures/ignition_design.jpg and /dev/null differ diff --git a/docs/en/figures/ignition_design_1.jpg b/docs/en/figures/ignition_design_1.jpg deleted file mode 100644 index 984d52135301a1ae1537f7bd87988f4914305a8a..0000000000000000000000000000000000000000 Binary files a/docs/en/figures/ignition_design_1.jpg and /dev/null differ diff --git a/docs/en/figures/ignition_design_2.jpg b/docs/en/figures/ignition_design_2.jpg deleted file mode 100644 index 8526c63c0ac37027251888230845d1fb0ae2cda1..0000000000000000000000000000000000000000 Binary files a/docs/en/figures/ignition_design_2.jpg and /dev/null differ diff --git a/docs/en/figures/ignition_design_3.jpg b/docs/en/figures/ignition_design_3.jpg deleted file mode 100644 index 73ee548b5905e731c1145596043c12e4fd5a6f7b..0000000000000000000000000000000000000000 Binary files a/docs/en/figures/ignition_design_3.jpg and /dev/null differ diff --git a/docs/en/globalconfig_file_desc.md b/docs/en/globalconfig_file_desc.md index de45dc96a8b68089a3b22df9a8a130c68a0b1e0c..e4117d5767ddcbb52598b694b7301ec4c1756cca 100644 --- a/docs/en/globalconfig_file_desc.md +++ b/docs/en/globalconfig_file_desc.md @@ -3,6 +3,6 @@ ``` shell persistdir: /etc/nkd # File storage path, including global configuration files, cluster configuration files, certificate files, etc. Default path: /etc/nkd bootstrapurl: - bootstrap_ign_host: "" # Ignition service address (domain name or IP, usually NKD operating environment) - bootstrap_ign_port: "9080" # Ignition service port (default 9080, you need to open the firewall port yourself) + bootstrapIgnHost: "" # Ignition service address (domain name or IP, usually NKD operating environment) + bootstrapIgnPort: "9080" # Ignition service port (default 9080, you need to open the firewall port yourself) ``` \ No newline at end of file diff --git a/docs/en/ignition_design.md b/docs/en/ignition_design.md index 878bca953318193b8f5334fac63d4e99812eb7d5..f9dcf8428c3ef6a8271b78f741afcb4dd6d58d44 100644 --- a/docs/en/ignition_design.md +++ b/docs/en/ignition_design.md @@ -1,23 +1,10 @@ -# Ignition +# Provide configuration +After the cluster infrastructure is successfully created, the operating system will enter the startup process. During this process, the ignition file will automatically obtain the required configuration information and write the configuration files such as user information, cluster certificates, cluster deployment services, etc. to the disk of the node machine. Subsequently, the systemd service will start to ensure that the system can run stably according to the preset rules and configurations. After the "release-image-pivot.service" service starts normally, it will build the required environment for cluster deployment. After the environment configuration is completed, the K8S cluster deployment service will officially start, which marks the official start of cluster deployment work. The entire process will continue until the service automatically shuts down after the cluster deployment is completed. NKD currently supports multiple ignition files including Ignition, cloud-init, and Kickstart files. -Ignition is a utility used by NestOS during the initramfs phase to manipulate disks. This includes tasks such as creating users, adding trusted SSH keys, writing files (regular files, systemd services, etc.), and configuring networks. Upon the first boot, Ignition reads its configuration and applies it. Ignition utilizes JSON configuration files to represent the set of changes to be made. The format of this configuration is detailed in the specification [here](https://coreos.github.io/ignition/specs/). +## Ignition +Ignition is a utility used by immutable infrastructure operating systems such as NestOS and Fedora CoreOS to operate disks during the initramfs phase, including creating users, adding trusted SSH keys, writing regular files, systemd services, network configurations, and more. Upon first boot, Ignition reads its configuration and applies it. Ignition uses a JSON configuration file to represent the set of changes to be made. The format of this configuration is detailed in the [specification](https://coreos.github.io/ignition/specs/). -## Provide configuration -The Ignition file (controlplane.ign) required to generate and deploy the cluster is approximately 90KB in size, containing declarations for systemd services, certificates, and other necessities for cluster create. When deploying the cluster on the OpenStack platform, the Nova user data limit is 64KB, making direct infrastructure deployment impossible. To address this issue, NKD has created a smaller Ignition file (*-merge.ign) to serve as a bootstrapping configuration file for creating the infrastructure. The main Ignition file will be loaded into memory accessible via HTTP service, enabling the smaller Ignition file to be automatically loaded during the system boot phase. - -When the infrastructure of the cluster nodes is successfully created, the operating system is in the bootstrapping phase. At this point, Ignition retrieves configuration information and writes configuration files (including users, cluster certificates, cluster deployment services, etc.) to the disks of the node machines, and then starts systemd services. Once the "release-image-pivot.service" service is started successfully, the node machines switch the file system to a customized version of NestOS based on the rpm-ostree mechanism. Afterward, the cluster creation service is executed, continuing until the completion of cluster creation, at which point the service is shut down. - -### ControlPlane Node -The configuration information of the control plane node's Ignition file is as shown in the image: -![ignition_design_1](/docs/en/figures/ignition_design_1.jpg) - -### Master Node -The configuration information of the master node's Ignition file is as shown in the image: -![ignition_design_2](/docs/en/figures/ignition_design_2.jpg) - -### Worker Node -The configuration information of the worker node's Ignition file is as shown in the image: -![ignition_design_3](/docs/en/figures/ignition_design_3.jpg) +The Ignition controlplane.ign file required for deploying a cluster generated by NKD is approximately 90KB, which declares the key components required during the cluster deployment process, including necessary systemd services and cluster certificates. However, when deploying a cluster on the OpenStack platform, due to the Nova user data limit of 64KB, it is not possible to directly create instances. To solve this problem, NKD created a stripped-down version of Ignition*-merge.ign file as a bootstrap configuration file when creating instances. At the same time, NKD loaded the main Ignition file controlplane.ign into a memory storage accessible through HTTP services. The generated Ignition file directory structure is as follows: ``` shell @@ -48,4 +35,15 @@ Where: "version": "3.2.0" } } -``` \ No newline at end of file +``` + +## cloud-init +Cloud-init is an open source tool developed specifically for initializing virtual machine instances in cloud computing environments. During cluster deployment using NKD, the underlying operating system is selected as a general-purpose operating system. When deploying a cluster on a virtualization platform, a cloud-init file is generated, which configures the environment and cluster deployment services required for cluster deployment, such as: + +- Configure the host name +- Install software packages on the instance +- Configure cluster environment +- Run the cluster installation script + +## Kickstart +During the cluster deployment process using NKD, when the underlying operating system is selected as a general-purpose operating system and the cluster deployment is performed based on the PXE pre-boot execution environment platform, a kickstart file will be generated, which configures the environment and cluster deployment services required for cluster deployment, enabling a self-service installation method. diff --git a/docs/en/manual.md b/docs/en/manual.md index 7d8ddc27fb1d2d9104af5e0fba60d748a8ed51ea..ad6094d10743d48fc64ce8947028f7f36a441fe3 100644 --- a/docs/en/manual.md +++ b/docs/en/manual.md @@ -7,13 +7,13 @@ * Installation of the tofu software package ``` shell # Install amd64 version - $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.0-rc1/tofu_1.6.0-rc1_amd64.rpm - $ rpm -ivh tofu_1.6.0-rc1_amd64.rpm + $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.2/tofu_1.6.2_amd64.rpm + $ rpm -ivh tofu_1.6.2_amd64.rpm ``` ``` shell # Install arm64 version - $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.0-rc1/tofu_1.6.0-rc1_arm64.rpm - $ rpm -ivh tofu_1.6.0-rc1_arm64.rpm + $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.2/tofu_1.6.2_arm64.rpm + $ rpm -ivh tofu_1.6.2_arm64.rpm ``` * Install NKD @@ -31,11 +31,13 @@ Deploying clusters on the libvirt platform requires pre-installation of the libv ### openstack Deploying clusters on the OpenStack platform requires pre-setup of the OpenStack environment. +### PXE + ## Compilation and Installation * Compilation Environment: Linux x86_64/aarch64 * The following software packages are required for compilation: - * golang >= 1.17 + * golang >= 1.21 * git ``` shell $ sudo yum install golang git @@ -106,22 +108,44 @@ Supports deploying the cluster using application configuration parameters, in ad -f, --file string Location of the cluster deploy config file -h, --help help for deploy --image-registry string Registry address for Kubernetes component container images + --ipxe-filePath string Path of config file for iPXE + --ipxe-ip string IP address of local machine for iPXE + --ipxe-osInstallTreePath string Path of OS install tree for iPXE. (default: /var/www/html/) --kubernetes-apiversion uint Sets the Kubernetes API version. Acceptable reference values: - 1 for Kubernetes versions < v1.15.0, - 2 for Kubernetes versions >= v1.15.0 && < v1.22.0, - 3 for Kubernetes versions >= v1.22.0 --kubeversion string Version of Kubernetes to deploy + --libvirt-cidr string CIDR for libvirt (default: 192.168.132.0/24) + --libvirt-gateway string Gateway for libvirt (default: 192.168.132.1) + --libvirt-osPath string OS path for libvirt + --libvirt-uri string URI for libvirt (default: qemu:///system) --master-cpu uint CPU allocation for master nodes (units: cores) --master-disk uint Disk size allocation for master nodes (units: GB) --master-hostname stringArray Hostnames of master nodes (e.g., --master-hostname [master-01] --master-hostname [master-02] ...) --master-ips stringArray IP addresses of master nodes (e.g., --master-ips [master-ip-01] --master-ips [master-ip-02] ...) --master-ram uint RAM allocation for master nodes (units: MB) --network-plugin-url string The deployment yaml URL of the network plugin + --openstack-authURL string AuthURL for openstack (default: http://controller:5000/v3) + --openstack-availabilityZone string AvailabilityZone for openstack (default: nova) + --openstack-externalNetwork string ExternalNetwork for openstack + --openstack-glanceName string GlanceName for openstack + --openstack-internalNetwork string InternalNetwork for openstack + --openstack-password string Password for openstack + --openstack-region string Region for openstack (default: RegionOne) + --openstack-tenantName string TenantName for openstack (default: admin) + --openstack-username string UserName for openstack (default: admin) --operator-image-url string URL of the container image for the housekeeper operator component + --os-type string Operating system type for Kubernetes cluster deployment (e.g., nestos or generalos) --password string Password for node login --pause-image string Image for the pause container (e.g., pause:TAG) --platform string Infrastructure platform for deploying the cluster (supports 'libvirt' or 'openstack') --pod-subnet string Subnet used for Kubernetes Pods. (default: 10.244.0.0/16) + --posthook-yaml string Specify a YAML file or directory to apply after cluster deployment using 'kubectl apply' + --prehook-script string Specify a script file or directory to execute before cluster deployment as hooks + --pxe-httpRootDir string Root directory of HTTP server for PXE (default: /var/www/html/) + --pxe-ip string IP address of local machine for PXE + --pxe-tftpRootDir string Root directory of TFTP server for PXE (default: /var/lib/tftpboot/) --release-image-url string URL of the NestOS container image containing Kubernetes component --runtime string Container runtime type (docker, isulad or crio) --service-subnet string Subnet used by Kubernetes services. (default: 10.96.0.0/16) @@ -134,7 +158,7 @@ Supports deploying the cluster using application configuration parameters, in ad --worker-ips stringArray IP addresses of worker nodes (e.g., --worker-ips [worker-ip-01] --worker-ips [worker-ip-02] ...) --worker-ram uint RAM allocation for worker nodes (units: MB) # Deploying the cluster with optional application configuration parameters - $ nkd deploy --platform [platform] --master-ips [master-ip-01] --master-ips [master-ip-02] --master-hostname [master-hostname-01] --master-hostname [master-hostname-02] --master-cpu [master-cpu-cores] --worker-hostname [worker-hostname-01] --worker-disk [worker-disk-size] + $ nkd deploy --platform [platform] --master-ips [master-ip-01] --master-ips [master-ip-02] --master-hostname [master-hostname-01] --master-hostname [master-hostname-02] --master-cpu [master-cpu-cores] --worker-hostname [worker-hostname-01] --worker-disk [worker-disk-size] ... ``` ## Deployment Process Demonstration @@ -157,21 +181,22 @@ Deploying the Cluster with Application Configuration Files ``` dockerfile FROM nestos_base_image COPY kube* /usr/bin/ + COPY crictl /usr/bin/ RUN ostree container commit ``` Note: Users need to customize building deployment images before deploying the cluster. ## Create Cluster - - Deploy the cluster using default configurations without adding any parameters. The default platform is libvirt, and it creates one master node and one worker node - ``` shell - $ nkd deploy - ``` - Deploy the cluster with optional parameters. Example command: ``` shell - $ nkd deploy --master-ips 192.168.132.11 --master-ips 192.168.132.12 --master-hostname k8s-master01 --master-hostname k8s-master02 --master-cpu 8 --worker-hostname k8s-worker01 --worker-disk 50 + $ nkd deploy --master-ips 192.168.132.11 --master-ips 192.168.132.12 --master-hostname k8s-master01 --master-hostname k8s-master02 --master-cpu 8 --worker-hostname k8s-worker01 --worker-disk 50 ... ``` - Additionally, for more fine-grained configurations, you can deploy the cluster using a cluster configuration file. See configuration management for details. ``` shell $ nkd deploy -f cluster_config.yaml ``` + +## troubleshooting + +The logs of NKD are stored in the directory /etc/nkd/logs by default, which facilitates effective troubleshooting in case of any issues encountered during the infrastructure creation process. \ No newline at end of file diff --git a/docs/en/overall_design.md b/docs/en/overall_design.md index 083fd6e45ca484ee91f6dcab82b68ad7f8bbc4e9..5ffe8b9dd6690c4d6aaa9754748c6efcbb6c7aad 100644 --- a/docs/en/overall_design.md +++ b/docs/en/overall_design.md @@ -41,9 +41,7 @@ NKD relies on certificates for the creation of cluster nodes and access to resou ![certmanager_design](/docs/en/figures/certmanager_design.jpg) ### Ignition module design -NKD utilizes the Ignition mechanism to inject dynamic configurations required after system deployment when creating infrastructure. For more details, please refer to the [design document](./ignition_design.md)。It also supports converting user configurations to ignition files via command line arguments or configuration files.After the deployment completes the operating system boot, the node automatically completes the cluster creation during the operating system boot phase through the Ignition mechanism, without manual intervention. The Ignition file creation process for each cluster node is shown below: - -![ignition_design](/docs/en/figures/ignition_design.jpg) +When creating the infrastructure, NKD needs to pass the dynamic configuration required after system deployment through the ignition mechanism to support user deployment of Kubernetes resources. The ignition mechanism can convert user-written configuration files into configuration files used during machine boot. For immutable infrastructure operating systems, the ignition module will generate Ignition files. If the underlying operating system is a general-purpose operating system, a cloudinit file will be generated when deploying the cluster on a virtualization platform, while a kickstart file will be generated when deploying the cluster on a bare metal platform. For more details, see the [design document](./ignition_design.md). ### housekeeper module design During the cluster deployment phase, users can choose whether to deploy housekeeper. diff --git a/docs/zh/config_file_desc.md b/docs/zh/config_file_desc.md index 1cef48e31ca94db651c4d6b2982c345c4743885d..976c0629e4a7593342ba4459d3d8955708ac0669 100644 --- a/docs/zh/config_file_desc.md +++ b/docs/zh/config_file_desc.md @@ -1,18 +1,16 @@ # 集群配置文件说明 -NestOS镜像下载地址见[官网](https://nestos.openeuler.org/) ``` shell -cluster_id: cluster # 集群名称 +clusterID: cluster # 集群名称 architecture: amd64 # 部署集群的机器架构,支持amd64或者arm64 -platform: libvirt # 部署平台为libvirt -infraplatform - uri: qemu:///system - osimage: https://nestos.org.cn/nestos20230928/nestos-for-container/x86_64/NestOS-For-Container-22.03-LTS-SP2.20230928.0-qemu.{arch}.qcow2 # 指定部署集群机器的操作系统镜像地址,支持架构x86_64或者aarch64 - cidr: 192.168.132.0/24 # 路由地址 - gateway: 192.168.132.1 # 网关地址 +platform: libvirt # 部署平台为libvirt、openstack、pxe +infraPlatform # 指定基础设施平台类型 + # 需要根据不同的部署平台设置参数 +osImage: + type: # 指定操作系统类型,例如nestos、generalos username: root # 指定 ssh 登录所配置节点的用户名 -password: $1$yoursalt$UGhjCXAJKpWWpeN8xsF.c/ # 指定 ssh 登录所配置节点的密码 -sshkey: "/root/.ssh/id_rsa.pub" # ssh 免密登录的密钥存储文件的路径 +password: # 指定 ssh 登录所配置节点的密码 +sshKey: "/root/.ssh/id_rsa.pub" # ssh 免密登录的密钥存储文件的路径 master: # 配置master节点的列表 - hostname: k8s-master01 # 该节点的名称 hardwareinfo: # 该节点配置的硬件资源信息 @@ -26,48 +24,99 @@ worker: # 配置worker节点的列 cpu: 4 ram: 8192 disk: 50 - ip: "" # 如果不设置worker节点IP地址,则由dhcp自动分配,默认为空 -runtime: isulad # 指定容器运行时类型,目前支持 docker、isulad和crio -kubernetes: # 集群相关配置列表 - kubernetes-version: "v1.23.10" # 部署集群的版本 - kubernetes-apiversion: "v1beta3" # 指定kubeadm配置文件格式的版本,目前支持 v1beta3、v1beta2、v1beta1 - apiserver-endpoint: "192.168.132.11:6443" # 对外暴露的APISERVER服务的地址或域名 - image-registry: "k8s.gcr.io" # 下载容器镜像时使用的镜像仓库的mirror站点地址 - pause-image: "pause:3.6" # 容器运行时的pause容器的容器镜像名称 - release-image-url: "hub.oepkgs.net/nestos/nestos:22.03-LTS-SP2.20230928.0-{arch}-k8s-v1.23.10" # 包含K8S二进制组件的NestOS发布镜像的地址,支持架构x86_64或者aarch64 - token: "" # 启动引导过程中使用的令牌,默认自动生成 - adminkubeconfig: /etc/nkd/cluster/admin.config # 集群管理员配置文件admin.conf的路径 - certificatekey: "" # 添加新的控制面节点时用来解密所下载的Secret中的证书的秘钥 - network: # k8s集群网络配置 - service-subnet: "10.96.0.0/16" # k8s创建的service的IP地址网段 - pod-subnet: "10.244.0.0/16" # k8s集群网络的IP地址网段 - plugin: https://projectcalico.docs.tigera.io/archive/v3.22/manifests/calico.yaml # 网络插件 + ip: "" # 如果不设置worker节点IP地址,则由dhcp自动分配,默认为空 +runtime: isulad # 指定容器运行时类型,目前支持 docker、isulad、containerd和crio +kubernetes: # 集群相关配置列表 + kubernetesVersion: "v1.29.1" # 部署集群的版本 + kubernetesApiversion: "v1beta3" # 指定kubeadm配置文件格式的版本,目前支持 v1beta3、v1beta2、v1beta1 + apiserverEndpoint: "192.168.132.11:6443" # 对外暴露的APISERVER服务的地址或域名 + imageRegistry: "registry.k8s.io" # Kubeadm初始化时使用的镜像仓库地址 + registryMirror: "" # 下载容器镜像时,使用的镜像仓库的 mirror 站点地址 + pauseImage: "pause:3.9" # 容器运行时的pause容器的容器镜像名称 + releaseImageUrl: "" # 包含K8S二进制组件的NestOS发布镜像的地址,支持架构x86_64或者aarch64 + token: "" # 启动引导过程中使用的令牌,默认自动生成 + adminKubeconfig: /etc/nkd/cluster/admin.config # 集群管理员配置文件admin.conf的路径 + certificateKey: "" # 添加新的控制面节点时用来解密所下载的Secret中的证书的秘钥 + packageList: # 集群环境中需要安装的RPM软件包名称列表 + rpmPackagePath: "" # 集群环境中需要安装的RPM软件包文件路径 + network: # k8s集群网络配置 + serviceSubnet: "10.96.0.0/16" # k8s创建的service的IP地址网段 + podSubnet: "10.244.0.0/16" # k8s集群网络的IP地址网段 + plugin: "" # 网络插件 housekeeper: # housekeeper相关配置列表 - deployhousekeeper: false # 是否部署housekeeper - operatorimageurl: "hub.oepkgs.net/nestos/housekeeper/{arch}/housekeeper-operator-manager:{tag}" # housekeeper-operator镜像的地址,支持架构amd64或者arm64 - controllerimageurl: "hub.oepkgs.net/nestos/housekeeper/{arch}/housekeeper-controller-manager:{tag}" # housekeeper-controller镜像的地址,支持架构amd64或者arm64 -certasset: # 配置外部证书文件路径列表,默认自动生成 - rootcacertpath: "" - rootcakeypath: "" - etcdcacertpath: "" - etcdcakeypath: "" - frontproxycacertpath: "" - frontproxycakeypath: "" - sapub: "" - sakey: "" + deployHousekeeper: false # 是否部署housekeeper + operatorImageURL: "hub.oepkgs.net/nestos/housekeeper/{arch}/housekeeper-operator-manager:{tag}" # housekeeper-operator镜像的地址,支持架构amd64或者arm64 + controllerImageURL: "hub.oepkgs.net/nestos/housekeeper/{arch}/housekeeper-controller-manager:{tag}" # housekeeper-controller镜像的地址,支持架构amd64或者arm64 +certAsset: # 配置外部证书文件路径列表,默认自动生成 + rootCACertPath: "" + rootCAKeyPath: "" + etcdCACertPath: "" + etcdCAKeyPath: "" + frontProxyCACertPath: "" + frontProxyCAKeyPath: "" + saPub: "" + saKey: "" +``` + +指定部署平台为libvirt配置参数示例: +``` shell +platform: libvirt # 部署平台为libvirt +infraPlatform + uri: qemu:///system + osPath: # 指定部署集群机器的操作系统镜像地址,支持架构x86_64或者aarch64 + cidr: 192.168.132.0/24 # 路由地址 + gateway: 192.168.132.1 # 网关地址 ``` -设置部署平台为openstack,需要重新设置“infraplatform”字段配置参数 +指定部署平台为openstack配置参数示例: ``` shell -platform: openstack # 部署平台为openstack -infraplatform - username: # openstack用户名,需要有创建资源权限 - password: # openstack登录密码,用于登录openstack平台 - tenant_name: # openstack租户名,用户所属的合集,例如:admin - auth_url: # openstack鉴权地址,例如:http://{ip}:{port}/v3 - region: # openstack地区,用于资源隔离,例如:RegionOne - internal_network: # openstack内部网络名称,用户自定义内部网络名称 - external_network: # openstack外部网络名称,用户自定义外部网络名称 - glance_name: # 创建openstack实例的qcow2镜像 - availability_zone: # 可用域,默认nova -``` \ No newline at end of file +platform: openstack # 部署平台为openstack +infraPlatform + username: # openstack用户名,需要有创建资源权限 + password: # openstack登录密码,用于登录openstack平台 + tenantName: # openstack租户名,用户所属的合集,例如:admin + authURL: # openstack鉴权地址,例如:http://{ip}:{port}/v3 + region: # openstack地区,用于资源隔离,例如:RegionOne + internalNetwork: # openstack内部网络名称,用户自定义内部网络名称 + externalNetwork: # openstack外部网络名称,用户自定义外部网络名称 + glanceName: # 创建openstack实例的qcow2镜像 + availabilityZone: # 可用域,默认nova +``` + +指定部署平台为pxe时配置参数示例: +``` shell +platform: pxe # 部署平台为pxe +infraPlatform + ip: # http服务器的ip地址 + httpServerPort: "9080" # http服务器的端口号 + httpRootDir: /var/www/html/ # 设置 HTTP 服务器的根目录 + tftpServerPort: "69" # TFTP服务器端口号 + tftpRootDir: /var/lib/tftpboot/ # TFTP服务器的根目录 +``` + +## 镜像下载地址 + +- NestOS镜像下载地址见[官网](https://nestos.openeuler.org/),需下载NestOS For Container版本 +- Openeuler镜像下载地址见[官网](https://www.openeuler.org/) + +## 密码密文生成方式: + +- 指定集群底层操作系统为nestos时需使用密文密码,其生成方式: + ``` shell + openssl passwd -1 -salt yoursalt + Password: qwer1234!@#$ + $1$yoursalt$UGhjCXAJKpWWpeN8xsF.c/ + ``` + +- 部署平台为pxe时需使用密文密码,其生成方式: + ``` shell + # python3 + Python 3.7.9 (default, Mar 2 2021, 02:43:11) + [GCC 7.3.0] on linux + Type "help", "copyright", "credits" or "license" for more information. + >>> import crypt + >>> passwd = crypt.crypt("myPasswd") + >>> print (passwd) + $6$sH1qri2n14V1VCv/$fWnV3rPv95gWHJ3wZu6o0bBGy.SnllSw4a2HuoP45jXfI9fCrwe60AULO/0aXS7dWTSwvwdqqY4yFhwUdJcb.0 + ``` + diff --git a/docs/zh/figures/config_manager_design.jpg b/docs/zh/figures/config_manager_design.jpg index 1e83bbf989b363fd47b25b732182adce4f318a9f..06bd46e3140ba4479d5e5465355bbb7f30465882 100644 Binary files a/docs/zh/figures/config_manager_design.jpg and b/docs/zh/figures/config_manager_design.jpg differ diff --git a/docs/zh/figures/detailed_design.jpg b/docs/zh/figures/detailed_design.jpg index b29d9e71ddeb74dfc0a9e7b6bf2b1e87b7070c34..997ddb16c82f13af294e934ff76c54dab5599105 100644 Binary files a/docs/zh/figures/detailed_design.jpg and b/docs/zh/figures/detailed_design.jpg differ diff --git a/docs/zh/figures/ignition_design.jpg b/docs/zh/figures/ignition_design.jpg deleted file mode 100644 index f5b15d19934411c6650f657285983782a0cf1317..0000000000000000000000000000000000000000 Binary files a/docs/zh/figures/ignition_design.jpg and /dev/null differ diff --git a/docs/zh/figures/ignition_design_1.jpg b/docs/zh/figures/ignition_design_1.jpg deleted file mode 100644 index 68705cf56eb3740a790bf3264022ad04d5fde7c4..0000000000000000000000000000000000000000 Binary files a/docs/zh/figures/ignition_design_1.jpg and /dev/null differ diff --git a/docs/zh/figures/ignition_design_2.jpg b/docs/zh/figures/ignition_design_2.jpg deleted file mode 100644 index c8894ad2540f266d752b8afabcdc1c67811cd375..0000000000000000000000000000000000000000 Binary files a/docs/zh/figures/ignition_design_2.jpg and /dev/null differ diff --git a/docs/zh/figures/ignition_design_3.jpg b/docs/zh/figures/ignition_design_3.jpg deleted file mode 100644 index e2e157f4b626fab9b88bea9c4378dfa4154de5b8..0000000000000000000000000000000000000000 Binary files a/docs/zh/figures/ignition_design_3.jpg and /dev/null differ diff --git a/docs/zh/globalconfig_file_desc.md b/docs/zh/globalconfig_file_desc.md index 152cee835272e4157b49f10dba4dd57a80cf03ce..7cb8681593dc00522241babc280df73e01850127 100644 --- a/docs/zh/globalconfig_file_desc.md +++ b/docs/zh/globalconfig_file_desc.md @@ -3,6 +3,6 @@ ``` shell persistdir: /etc/nkd # 文件存储路径,包括全局配置文件、集群配置文件以及证书文件等。默认路径:/etc/nkd bootstrapurl: - bootstrap_ign_host: "" # 点火服务地址(域名或ip,一般为NKD运行环境) - bootstrap_ign_port: "9080" # 点火服务端口(默认9080,需自行开放防火墙端口) + bootstrapIgnHost: "" # 点火服务地址(域名或ip,一般为NKD运行环境) + bootstrapIgnPort: "9080" # 点火服务端口(默认9080,需自行开放防火墙端口) ``` \ No newline at end of file diff --git a/docs/zh/ignition_design.md b/docs/zh/ignition_design.md index f85edd00a400a04829b9287102928d415b3fab87..eb01fed23ecbf9664bdfa988e8cf7e0f7df626c0 100644 --- a/docs/zh/ignition_design.md +++ b/docs/zh/ignition_design.md @@ -1,23 +1,10 @@ -# Ignition +# 点火设计 +当集群基础设施顺利完成创建后,操作系统随即进入启动流程。在此过程中,点火文件会自动获取所需的配置信息,并将配置文件(如用户信息、集群证书、集群部署服务等)写入到节点机器的磁盘中。随后,systemd服务将启动,确保系统能够按照预设的规则和配置稳定运行。当"release-image-pivot.service"服务正常启动后,将会为集群部署搭建所需的环境。在环境配置完成后,K8S集群部署服务将正式启动,这标志着集群部署工作的正式开始。整个过程将持续进行,直到集群部署完成后服务自动关闭。NKD目前支持多种点火文件包括Ignition、cloud-init和Kickstart文件。 -Ignition是NestOS在initramfs期间用来操作磁盘的实用程序。其中包括创建用户、添加受信的SSH密钥、写入文件(常规文件、systemd服务...)、网络配置等。首次启动时,Ignition读取其配置并应用该配置。Ignition使用JSON配置文件来表示要进行的更改集。此配置的格式在[规范](https://coreos.github.io/ignition/specs/)中有详细说明。 +## Ignition +Ignition是不可变基础设施操作系统(如NestOS、Fedora CoreOS)在initramfs期间用来操作磁盘的实用程序,这包括创建用户、添加受信的SSH密钥、写入文件(常规文件、systemd服务...)、网络配置等。首次启动时,Ignition读取其配置并应用该配置。Ignition使用JSON配置文件来表示要进行的更改集。此配置的格式在[规范](https://coreos.github.io/ignition/specs/)中有详细说明。 -## 提供配置 -生成部署集群所需的Ignition(controlplane.ign)文件约为90KB,文件中声明了集群部署所需的systemd服务、证书等。在OpenStack平台上部署集群时,由于Nova用户数据限制为64KB,因此无法直接部署基础设施。为了解决这个问题,NKD创建了一个较小的Ignition(*-merge.ign)文件,作为创建基础设施时的引导配置文件。而主要的Ignition文件将会加载到可以通过 HTTP 服务访问的内存中,使较小的Ignition文件在系统引导阶自动加载主要的Ignition文件。 - -当集群节点的基础设施正常创建后,操作系统处于引导阶段,Ignition将获取配置信息并将配置文件(用户、集群证书、集群部署服务等)写入到节点机器的磁盘中,然后启动systemd服务。当"release-image-pivot.service"服务正常启动后,节点机器通过rpm-ostree机制将文件系统切换为基于NestOS的kubernetes定制版本,之后启动K8S集群部署服务,开始正式的集群部署任务,直到集群部署完成后服务关闭。 - -### ControlPlane Node -controlplane节点的Ignition文件配置信息如图: -![ignition_design_1](/docs/zh/figures/ignition_design_1.jpg) - -### Master Node -Master节点的Ignition文件配置信息如图: -![ignition_design_2](/docs/zh/figures/ignition_design_2.jpg) - -### Worker Node -Worker节点的Ignition文件配置信息如图: -![ignition_design_3](/docs/zh/figures/ignition_design_3.jpg) +NKD生成部署集群所需的Ignition(controlplane.ign)文件约为90KB,该文件声明了集群部署过程中所需的关键组件,包括必要的systemd服务和集群证书等。然而,当在OpenStack平台上部署集群时,由于Nova用户数据限制为64KB,因此无法直接创建实例。为了解决这个问题,NKD创建了一个精简版的Ignition(*-merge.ign)文件,作为创建实例时的引导配置文件。同时,NKD将主要的Ignition文件(controlplane.ign)加载到了一个可通过HTTP服务访问的内存存储中。 生成的Ignition文件目录结构如下: ``` shell @@ -48,4 +35,15 @@ $ tree "version": "3.2.0" } } -``` \ No newline at end of file +``` + +## cloud-init +cloud-init是专为云计算环境中虚拟机实例初始化而开发的一款开源工具。在使用NKD进行集群部署过程中,选择底层操作系统为通用操作系统(例如:openeuler),在虚拟化平台上进行集群部署时将生成cloud-init文件,该文件中配置了集群部署所需的环境和集群部署服务,例如: + +- 配置主机名 +- 在实例上安装软件包 +- 配置集群环境 +- 运行集群安装脚本 + +## Kickstart +在使用NKD进行集群部署的过程中,当选择底层操作系统为通用操作系统,并基于PXE(预启动执行环境)平台上进行集群部署时将生成kickstart文件,该文件中配置了集群部署所需的环境和集群部署服务,实现了一种无人值守的安装方式。 \ No newline at end of file diff --git a/docs/zh/manual.md b/docs/zh/manual.md index 748499665d61ca9e8b07c2b3bc1b04991fcfe3df..09cf80ee91c8b133a4bdd821a162425639358498 100644 --- a/docs/zh/manual.md +++ b/docs/zh/manual.md @@ -7,13 +7,13 @@ * 安装tofu软件包 ``` shell # 安装amd64版本 - $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.0-rc1/tofu_1.6.0-rc1_amd64.rpm - $ rpm -ivh tofu_1.6.0-rc1_amd64.rpm + $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.2/tofu_1.6.2_amd64.rpm + $ rpm -ivh tofu_1.6.2_amd64.rpm ``` ``` shell # 安装arm64版本 - $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.0-rc1/tofu_1.6.0-rc1_arm64.rpm - $ rpm -ivh tofu_1.6.0-rc1_arm64.rpm + $ wget https://github.com/opentofu/opentofu/releases/download/v1.6.2/tofu_1.6.2_arm64.rpm + $ rpm -ivh tofu_1.6.2_arm64.rpm ``` * 安装NKD @@ -26,16 +26,18 @@ ## 支持平台 ### libvirt -libvirt平台部署集群,需要提前安装libvirt虚拟化环境 +在libvirt平台部署集群时,需要提前安装libvirt虚拟化环境 -### openstack -openstack平台部署集群,需要提前搭建好openstack环境 +### OpenStack +在OpenStack平台部署集群时,需要提前搭建好OpenStack环境 + +### PXE ## 编译安装 * 编译环境:Linux x86_64/aarch64 * 进行编译需要以下软件包: - * golang >= 1.17 + * golang >= 1.21 * git ``` shell $ sudo yum install golang git @@ -56,8 +58,8 @@ openstack平台部署集群,需要提前搭建好openstack环境 #### 点火服务配置参数: NKD部署集群过程中集群节点需要访问NKD提供的点火服务,通过以下全局配置参数对点火服务进行配置: -* bootstrap_ign_host:点火服务地址(域名或ip,一般为NKD运行环境) -* bootstrap_ign_port:点火服务端口(默认9080,需自行开放防火墙端口) +* bootstrapIgnHost:点火服务地址(域名或ip,一般为NKD运行环境) +* bootstrapIgnPort:点火服务端口(默认9080,需自行开放防火墙端口) 为适配多网卡环境,点火服务真实监听地址为0.0.0.0。 * 简单网络环境下,部署集群节点可直接访问NKD服务,"bootstrap_ign_host"参数项可以为空,此时NKD会探测路由表默认最高优先级的IP地址作为访问点火服务URL的host; @@ -76,7 +78,7 @@ NKD部署集群过程中集群节点需要访问NKD提供的点火服务,通 $ nkd template -f cluster_config.yaml # 应用配置文件部署集群 - $ nkd deploy -f cluster_config.yaml + $ nkd deploy -f cluster_config.yaml # 销毁指定集群 $ nkd destroy --cluster-id [your-cluster-id] @@ -96,45 +98,70 @@ NKD部署集群过程中集群节点需要访问NKD提供的点火服务,通 除了应用配置文件部署集群外,支持应用配置项参数部署集群 ``` shell $ nkd deploy --help - --arch string 部署集群的机器架构(例如,amd64或者arm64) - --bootstrap-ign-host string 指定点火服务地址(域名或者IP地址) - --bootstrap-ign-port string 指定点火服务端口(默认:9080) - --certificateKey string 用于在加入新的Master节点后,从 secret 下载的证书进行解密的密钥。 - (证书密钥是一个十六进制编码的字符串,是一个大小为 32 字节的 AES 密钥) - --cluster-id string 指定集群的唯一标识符 - --controller-image-url string 指定Housekeeper控制器组件的容器镜像地址 - --deploy-housekeeper 是否部署Housekeeper Operator,默认false - -f, --file string 指定集群部署配置文件的位置 - --image-registry string 指定用于拉取Kubernetes组件容器镜像的地址 - --kubernetes-apiversion uint 指定Kubernetes API版本。可接受的参考数值为: - - 1 用于Kubernetes版本 < v1.15.0; - - 2 用于Kubernetes版本 >= v1.15.0 && < v1.22.0; - - 3 用于Kubernetes版本 >= v1.22.0; - --kubeversion string 指定要部署的Kubernetes版本 - --master-cpu uint 设置主节点的CPU(单位:核) - --master-disk uint 设置主节点磁盘大小(单位:GB) - --master-hostname stringArray 设置主节点主机名 - --master-ips stringArray 设置主节点IP地址 - --master-ram uint 设置主节点的RAM(单位:MB) - --network-plugin-url 部署网络插件yaml的URL - --operator-image-url string 指定Housekeeper Operator组件的容器镜像地址 - --password string 指定 ssh 登录所配置节点的密码 - --pause-image string 指定pause容器的镜像 - --platform string 选择用于部署集群的基础设施平台(支持libvirt或者openstack平台) - --pod-subnet string 指定Kubernetes Pod的子网(默认:10.244.0.0/16) - --release-image-url string 指定包含Kubernetes组件的NestOS容器镜像的URL,仅支持qcow2格式 - --runtime string 指定容器运行时类型(docker、isulad 或 crio) - --service-subnet string 指定Kubernetes服务的子网(默认:"10.96.0.0/16") - --sshkey string ssh 免密登录的密钥存储文件的路径(默认:~/.ssh/id_rsa.pub) - --token string 用于验证从控制平面获取的集群信息,非控制平面节点用于加入集群 - --username string 需要部署 k8s 集群的机器的 ssh 登录用户名 - --worker-cpu uint 设置工作节点的CPU(单位:核心) - --worker-disk uint 设置工作节点磁盘大小(单位:GB) - --worker-hostname stringArray 设置工作节点主机名 - --worker-ips stringArray 设置工作节点IP地址 - --worker-ram uint 设置工作节点的RAM(单位:MB) + --arch string 部署集群的机器架构(例如,amd64或者arm64) + --bootstrap-ign-host string 指定点火服务地址(域名或者IP地址) + --bootstrap-ign-port string 指定点火服务端口(默认:9080) + --certificateKey string 用于在加入新的Master节点后,从 secret 下载的证书进行解密的密钥。 + (证书密钥是一个十六进制编码的字符串,是一个大小为 32 字节的 AES 密钥) + --clusterID string 指定集群的唯一标识符 + --controller-image-url string 指定Housekeeper控制器组件的容器镜像地址 + --deploy-housekeeper 是否部署Housekeeper Operator,默认false + -f, --file string 指定集群部署配置文件的位置 + --image-registry string 指定用于拉取Kubernetes组件容器镜像的地址 + --ipxe-filePath string ipxe配置文件路径 + --ipxe-osInstallTreePath string ipxe所需操作系统安装树路径 (默认: /var/www/html/) + --kubernetes-apiversion uint 指定Kubernetes API版本。可接受的参考数值为: + - 1 用于Kubernetes版本 < v1.15.0; + - 2 用于Kubernetes版本 >= v1.15.0 && < v1.22.0; + - 3 用于Kubernetes版本 >= v1.22.0; + --kubeversion string 指定要部署的Kubernetes版本 + --libvirt-cidr string 用于libvirt平台的CIDR (默认: 192.168.132.0/24) + --libvirt-gateway string 用于libvirt平台的网关 (默认: 192.168.132.1) + --libvirt-osPath string libvirt 平台下的操作系统路径 + --libvirt-uri string 用于libvirt的URI (默认: qemu:///system) + --master-cpu uint 设置主节点的CPU(单位:核) + --master-disk uint 设置主节点磁盘大小(单位:GB) + --master-hostname stringArray 设置主节点主机名 + --master-ips stringArray 设置主节点IP地址 + --master-ram uint 设置主节点的RAM(单位:MB) + --network-plugin-url 部署网络插件yaml的URL + --openstack-authURL string OpenStack的鉴权地址 (默认: http://controller:5000/v3) + --openstack-availabilityZone string OpenStack的可用域 (默认: nova) + --openstack-externalNetwork string OpenStack的外部网络 + --openstack-glanceName string OpenStack的镜像名称 + --openstack-internalNetwork string OpenStack的内部网络 + --openstack-password string OpenStack的密码 + --openstack-region string OpenStack的地区(默认: RegionOne) + --openstack-tenantName string OpenStack的租户名称(默认: admin) + --openstack-username string OpenStack的用户名(默认: admin) + --operator-image-url string 指定Housekeeper Operator组件的容器镜像地址 + --os-type string 指定集群节点的操作系统类型(例如:nestos、openeuler) + --password string 指定 ssh 登录所配置节点的密码 + --pause-image string 指定pause容器的镜像 + --platform string 选择用于部署集群的基础设施平台(支持libvirt或者openstack平台) + --pod-subnet string 指定Kubernetes Pod的子网(默认:10.244.0.0/16) + --posthook-yaml string 指定一个 YAML 文件或目录,在集群部署后使用 'kubectl apply' 应用 + --prehook-script string 指定一个脚本文件或目录,在集群部署前执行 + --pxe-httpRootDir string PXE平台下 HTTP 服务器的根目录 (默认: /var/www/html/) + --pxe-ip string PXE本地服务器的IP地址 + --pxe-tftpRootDir string PXE平台下TFTP服务器的根目录 (默认: /var/lib/tftpboot/) + --release-image-url string 指定包含Kubernetes组件的NestOS容器镜像的URL,仅支持qcow2格式 + --runtime string 指定容器运行时类型(docker、isulad 或 crio) + --service-subnet string 指定Kubernetes服务的子网(默认:"10.96.0.0/16") + --sshkey string ssh 免密登录的密钥存储文件的路径(默认:~/.ssh/id_rsa.pub) + --token string 用于验证从控制平面获取的集群信息,非控制平面节点用于加入集群 + --username string 需要部署 k8s 集群的机器的 ssh 登录用户名 + --worker-cpu uint 设置工作节点的CPU(单位:核心) + --worker-disk uint 设置工作节点磁盘大小(单位:GB) + --worker-hostname stringArray 设置工作节点主机名 + --worker-ips stringArray 设置工作节点IP地址 + --worker-ram uint 设置工作节点的RAM(单位:MB) + 全局参数: + --dir string 文件生成目录 (默认 "/etc/nkd") + --log-level string 日志级别 (例如 "debug | info | warn | error") (默认 "info") + # 应用可选配置项参数部署集群 - $ nkd deploy --platform [platform] --master-ips [master-ip-01] --master-ips [master-ip-02] --master-hostname [master-hostname-01] --master-hostname [master-hostname-02] --master-cpu [master-cpu-cores] --worker-hostname [worker-hostname-01] --worker-disk [worker-disk-size] + $ nkd deploy --platform [platform] --master-ips [master-ip-01] --master-ips [master-ip-02] --master-hostname [master-hostname-01] --master-hostname [master-hostname-02] --master-cpu [master-cpu-cores] --worker-hostname [worker-hostname-01] --worker-disk [worker-disk-size] ... ``` ## 部署过程展示 @@ -150,28 +177,29 @@ NKD部署集群过程中集群节点需要访问NKD提供的点火服务,通 * NestOS容器镜像支持利用Dockerfile在原来的基础上构建新的容器镜像 * 制作注意事项 * 请确保已安装docker。 - * 基础镜像需从NestOS官网下载最新版本容器镜像。 - * 制作部署镜像,需提前下载相对应版本的kubeadm、kubelet、crictl二进制文件并复制到/usr/bin目录。 + * 基础镜像需从NestOS官网下载最新版本容器镜像,官网镜像未包含kubernetes相关二进制组件 + * 制作部署镜像,需提前下载相对应版本的kubeadm、kubelet、crictl二进制文件并拷贝到/usr/bin目录。 * 软件包的安装需要使用rpm-ostree命令。 * Dockerfiles示例如下 ``` dockerfile FROM nestos_base_image COPY kube* /usr/bin/ + COPY crictl /usr/bin/ RUN ostree container commit ``` -备注:部署集群前用户需要自定义构建部署镜像 +备注:如果集群底层操作系统选择NestOS,用户在部署集群前需要自定义构建部署镜像。 ## 部署集群 - - 不添加任何配置项,通过默认配置部署集群。默认选择libvirt平台,并创建1个master节点、1个worker节点 - ``` shell - $ nkd deploy - ``` - 添加可选参数项部署集群,命令示例: ``` shell - $ nkd deploy --master-ips 192.168.132.11 --master-ips 192.168.132.12 --master-hostname k8s-master01 --master-hostname k8s-master02 --master-cpu 8 --worker-hostname k8s-worker01 --worker-disk 50 + $ nkd deploy --master-ips 192.168.132.11 --master-ips 192.168.132.12 --master-hostname k8s-master01 --master-hostname k8s-master02 --master-cpu 8 --worker-hostname k8s-worker01 --worker-disk 50 ... ``` - 此外更精细化的配置,可以通过集群配置文件部署集群,详情见配置管理。 ``` shell $ nkd deploy -f cluster_config.yaml ``` + +## 故障排查 + +NKD的日志默认存放在/etc/nkd/logs目录下,以便在基础设施创建过程中遇到问题能够有效地进行排查 \ No newline at end of file diff --git a/docs/zh/overall_design.md b/docs/zh/overall_design.md index 483f02fe892734be4dec08cce9397010a087901f..f660df002e20816d6db3e40ec39cb83401e2c431 100644 --- a/docs/zh/overall_design.md +++ b/docs/zh/overall_design.md @@ -22,26 +22,23 @@ NKD模块交互关系图 ![detailed_design](/docs/zh/figures/detailed_design.jpg) -### config-manager模块设计 +### 配置管理模块设计 NKD部署集群提供了不同的应用配置方式,以方便不同的用户使用这款部署工具。 - 体验部署一个基础集群,仅部署一个master和worker节点的小型集群,配置项参数使用默认配置,这样可以直接执行部署命令,且不用添加任何配置项; - 更精细化的配置各项参数,通过应用配置文件部署高可用集群; - 更灵活方便的配置集群,通过添加命令行参数部署高可用集群。 -命令行参数的优先级最高,配置文件次之。如果部署集群同时应用了配置文件和命令行参数,在配置参数项相同时,命令行参数会将配置文件内容覆盖。 -如果用户没有配置参数,NKD会自动生成该项参数或者使用默认配置,例如集群证书、Ignition文件等。config-manager模块会纳管集群的所有配置项参数,并存储在磁盘中。NKD部署集群依赖项如图: +命令行参数的优先级最高,其次是配置文件。当部署集群时,如果同时应用了配置文件和命令行参数,并且两者存在相同的配置项,则命令行参数会覆盖配置文件中的内容。如果用户没有提供配置参数,NKD会自动生成该参数或使用默认配置。配置管理模块负责管理集群的所有配置项,并将其存储在磁盘中。NKD部署集群的依赖关系如下图所示: ![config_manager_design](/docs/zh/figures/config_manager_design.jpg) -### cert-manager模块设计 -集群节点的创建、资源的访问都依赖证书,NKD在集群外创建证书并本地存储ca证书和admin.conf文件,更详细内容见[设计文档](./certmanager_design.md),创建完成的证书通过Ignition文件写入到节点机器。证书创建流程如图: +### 证书模块设计 +集群节点的创建、资源的访问都依赖证书,NKD在集群外创建证书并本地存储ca证书和admin.conf文件,更详细内容见[设计文档](./certmanager_design.md)。证书创建流程如图: ![certmanager_design](/docs/zh/figures/certmanager_design.jpg) -### Ignition模块设计 -NKD在创建基础设施时,需要通过ignition点火机制传入系统部署后所需的动态配置,详细内容见[设计文档](./ignition_design.md)。并且支持通过命令行参数或配置文件将用户配置转换为ignition文件。节点在部署完成操作系统引导后,通过Ignition机制在操作系统引导阶段自动完成集群创建,无需手动干预。集群各节点的Ignition文件创建流程如图: -![ignition_design](/docs/zh/figures/ignition_design.jpg) +### 点火模块设计 +在创建基础设施时,NKD 需要通过点火机制传入系统部署后所需的动态配置,以支持用户部署Kubernetes资源。点火机制能够将用户编写的配置文件转化为机器引导时的配置文件。对于不可变基础设施的操作系统,点火模块将生成Ignition文件。如果底层操作系统为通用操作系统,则在虚拟化平台部署集群时会生成cloud-init 文件,而在裸金属平台部署集群时会生成kickstart文件。详细内容见[设计文档](./ignition_design.md)。 ### housekeeper模块设计 在集群部署阶段,用户可以选择是否部署housekeeper -详细内容见[设计文档](./housekeeper_design.md) - +详细内容见[设计文档](./housekeeper_design.md) \ No newline at end of file diff --git a/go.mod b/go.mod index eb3f81a113b2ee47127ce64bc8d4255dd39637a9..b722b21f057dfca9f4fb80529718a92e8136b064 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,14 @@ require ( github.com/clarketm/json v1.17.1 github.com/coreos/ignition/v2 v2.14.0 github.com/hashicorp/terraform-exec v0.17.2 + github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/pin/tftp v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/vincent-petithory/dataurl v1.0.0 + golang.org/x/term v0.10.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.24.0 k8s.io/apimachinery v0.24.0 @@ -51,12 +54,12 @@ require ( golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sys v0.10.0 // indirect - golang.org/x/term v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect diff --git a/go.sum b/go.sum index c19ef79aceaacc4f7e6888bf358602180d2fcccc..8453a7cc5c0c7fb29d6ecba2c91d6042968c5f9e 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,7 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= @@ -104,6 +105,7 @@ github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -266,17 +268,23 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pin/tftp v2.1.0+incompatible h1:Yng4J7jv6lOc6IF4XoB5mnd3P7ZrF60XQq+my3FAMus= github.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -567,6 +575,7 @@ golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -683,6 +692,9 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/nkd.go b/nkd.go index 8f9c3b7fae1a866ca5a1c5123c5c2cb4a4be8242..483ff8e5215563b346faa7e7ede76a8df71d47dc 100755 --- a/nkd.go +++ b/nkd.go @@ -16,10 +16,15 @@ limitations under the License. package main import ( + "io" "nestos-kubernetes-deployer/cmd" + "nestos-kubernetes-deployer/cmd/command" "nestos-kubernetes-deployer/cmd/command/opts" + "os" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" + terminal "golang.org/x/term" ) func main() { @@ -43,9 +48,29 @@ func main() { func newRootCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "nkd", - Short: "Creates Kubernetes Clusters", + Use: "nkd", + Short: "Creates Kubernetes Clusters", + PersistentPreRun: runRootCmd, } + cmd.PersistentFlags().StringVar(&opts.Opts.RootOptDir, "dir", "/etc/nkd", "Assets directory") + cmd.PersistentFlags().StringVar(&opts.RootOpts.LogLevel, "log-level", "info", "log level (e.g. \"debug | info | warn | error\")") return cmd } + +func runRootCmd(cmd *cobra.Command, args []string) { + logrus.SetOutput(io.Discard) + + level, err := logrus.ParseLevel(opts.RootOpts.LogLevel) + if err != nil { + level = logrus.InfoLevel + } + + fmt := &logrus.TextFormatter{ + ForceColors: terminal.IsTerminal(int(os.Stderr.Fd())), + DisableTimestamp: true, + DisableLevelTruncation: true, + DisableQuote: true, + } + logrus.AddHook(command.NewloggerHook(os.Stderr, level, fmt)) +} diff --git a/pkg/api/api.go b/pkg/api/api.go new file mode 100644 index 0000000000000000000000000000000000000000..3b99dcd85ef0e5d94fb49d11814031ebf68f4e8c --- /dev/null +++ b/pkg/api/api.go @@ -0,0 +1,31 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +type OperatingSystem interface { + GenerateResourceFiles() error +} + +type BootConfigAPI interface { + GenerateBootConfig() error +} + +// Runtime 接口定义了获取运行时相关信息的方法。 +type Runtime interface { + // GetRuntimeCriSocket 返回与运行时相关的 CRI (Container Runtime Interface) 套接字地址 + GetRuntimeCriSocket() string +} diff --git a/pkg/bufferedprinter/bufferedprinter.go b/pkg/bufferedprinter/bufferedprinter.go new file mode 100644 index 0000000000000000000000000000000000000000..db54146c448bc0343cba6324c2f06662e81a66d1 --- /dev/null +++ b/pkg/bufferedprinter/bufferedprinter.go @@ -0,0 +1,83 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bufferedprinter + +import ( + "bufio" + "bytes" + "strings" + "sync" +) + +type print func(args ...interface{}) + +type bufferedPrinter struct { + buf bytes.Buffer + print print + + sync.Mutex + once sync.Once +} + +func New(print print) *bufferedPrinter { + return &bufferedPrinter{ + print: print, + } +} + +func (bp *bufferedPrinter) Write(p []byte) (int, error) { + bp.Lock() + defer bp.Unlock() + + n, err := bp.buf.Write(p) + if err != nil { + return n, err + } + + scanner := bufio.NewScanner(&bp.buf) + for scanner.Scan() { + bp.print(scanner.Text()) + } + if err := scanner.Err(); err != nil { + return n, err + } + + return n, nil +} + +func (bp *bufferedPrinter) Close() error { + bp.once.Do(func() { + bp.Lock() + defer bp.Unlock() + + line := bp.buf.String() + if len(line) > 0 { + bp.print(line) + } + }) + + return nil +} + +func TrimLastNewline(args ...interface{}) []interface{} { + if len(args) > 0 { + if lastArg, ok := args[len(args)-1].(string); ok { + args[len(args)-1] = strings.TrimRight(lastArg, "\n") + } + } + return args +} diff --git a/pkg/cert/GenerateAllFiles.go b/pkg/cert/GenerateAllFiles.go index ec3530630ec360dfd0f23b7a4eb1f4792f0aa105..458fbb8632f08e39355172b934e9f06e600e3c02 100644 --- a/pkg/cert/GenerateAllFiles.go +++ b/pkg/cert/GenerateAllFiles.go @@ -75,8 +75,8 @@ func (cg *CertGenerator) GenerateAllFiles() error { /* **********生成root CA 证书和密钥********** */ - rootCACert, err := GenerateAllCA(clusterconfig.CertAsset.RootCaCertPath, - clusterconfig.CertAsset.RootCaKeyPath, "kubernetes", []string{"kubernetes"}) + rootCACert, err := GenerateAllCA(clusterconfig.CertAsset.RootCACertPath, + clusterconfig.CertAsset.RootCAKeyPath, "kubernetes", []string{"kubernetes"}) if err != nil { logrus.Errorf("Error generating root CA:%v", err) return err @@ -84,8 +84,8 @@ func (cg *CertGenerator) GenerateAllFiles() error { /*如果用户没有提供自定义路径,则将ca保存在以下目录; 如果用户提供了自定义路径,也保存一份在以下路径,并反存到配置文件中*/ - clusterconfig.CertAsset.RootCaCertPath = globalconfig.PersistDir + "/" + clusterID + "/pki/ca.crt" - clusterconfig.CertAsset.RootCaKeyPath = globalconfig.PersistDir + "/" + clusterID + "/pki/ca.key" + clusterconfig.CertAsset.RootCACertPath = globalconfig.PersistDir + "/" + clusterID + "/pki/ca.crt" + clusterconfig.CertAsset.RootCAKeyPath = globalconfig.PersistDir + "/" + clusterID + "/pki/ca.key" //保存root CA证书和密钥到宿主机 err = SaveFileToLocal(globalconfig.PersistDir+"/"+clusterID+"/pki/ca.crt", rootCACert.CertRaw) @@ -119,8 +119,8 @@ func (cg *CertGenerator) GenerateAllFiles() error { /* **********生成etcd CA 证书和密钥********** */ - etcdCACert, err := GenerateAllCA(clusterconfig.CertAsset.EtcdCaCertPath, - clusterconfig.CertAsset.EtcdCaKeyPath, "etcd-ca", []string{"etcd-ca"}) + etcdCACert, err := GenerateAllCA(clusterconfig.CertAsset.EtcdCACertPath, + clusterconfig.CertAsset.EtcdCAKeyPath, "etcd-ca", []string{"etcd-ca"}) if err != nil { logrus.Errorf("Error generating etcd CA:%v", err) return err @@ -128,8 +128,8 @@ func (cg *CertGenerator) GenerateAllFiles() error { /*如果用户没有提供自定义路径,则将ca保存在以下目录; 如果用户提供了自定义路径,也保存一份在以下路径,并反存到配置文件中*/ - clusterconfig.CertAsset.EtcdCaCertPath = globalconfig.PersistDir + "/" + clusterID + "/pki/etcd/ca.crt" - clusterconfig.CertAsset.EtcdCaKeyPath = globalconfig.PersistDir + "/" + clusterID + "/pki/etcd/ca.key" + clusterconfig.CertAsset.EtcdCACertPath = globalconfig.PersistDir + "/" + clusterID + "/pki/etcd/ca.crt" + clusterconfig.CertAsset.EtcdCAKeyPath = globalconfig.PersistDir + "/" + clusterID + "/pki/etcd/ca.key" //保存etcd-ca和密钥到宿主机 err = SaveFileToLocal(globalconfig.PersistDir+"/"+clusterID+"/pki/etcd/ca.crt", etcdCACert.CertRaw) @@ -158,8 +158,8 @@ func (cg *CertGenerator) GenerateAllFiles() error { /* **********生成front-proxy CA 证书和密钥********** */ - frontProxyCACert, err := GenerateAllCA(clusterconfig.CertAsset.FrontProxyCaCertPath, - clusterconfig.CertAsset.FrontProxyCaKeyPath, "front-proxy-ca", []string{"front-proxy-ca"}) + frontProxyCACert, err := GenerateAllCA(clusterconfig.CertAsset.FrontProxyCACertPath, + clusterconfig.CertAsset.FrontProxyCAKeyPath, "front-proxy-ca", []string{"front-proxy-ca"}) if err != nil { logrus.Errorf("Error generating front-proxy CA:%v", err) return err @@ -167,8 +167,8 @@ func (cg *CertGenerator) GenerateAllFiles() error { /*如果用户没有提供自定义路径,则将ca保存在以下目录; 如果用户提供了自定义路径,也保存一份在以下路径,并反存到配置文件中*/ - clusterconfig.CertAsset.FrontProxyCaCertPath = globalconfig.PersistDir + "/" + clusterID + "/pki/front-proxy-ca.crt" - clusterconfig.CertAsset.FrontProxyCaKeyPath = globalconfig.PersistDir + "/" + clusterID + "/pki/front-proxy-ca.key" + clusterconfig.CertAsset.FrontProxyCACertPath = globalconfig.PersistDir + "/" + clusterID + "/pki/front-proxy-ca.crt" + clusterconfig.CertAsset.FrontProxyCAKeyPath = globalconfig.PersistDir + "/" + clusterID + "/pki/front-proxy-ca.key" //保存front-proxy-ca和密钥到宿主机 err = SaveFileToLocal(globalconfig.PersistDir+"/"+clusterID+"/pki/front-proxy-ca.crt", frontProxyCACert.CertRaw) @@ -205,8 +205,8 @@ func (cg *CertGenerator) GenerateAllFiles() error { /*如果用户没有提供自定义路径,则将密钥对保存在以下目录; 如果用户提供了自定义路径,也保存一份在以下路径,并反存到配置文件中*/ - clusterconfig.CertAsset.SaKey = globalconfig.PersistDir + "/pki/sa.key" - clusterconfig.CertAsset.SaPub = globalconfig.PersistDir + "/pki/sa.pub" + clusterconfig.CertAsset.SAKey = globalconfig.PersistDir + "/pki/sa.key" + clusterconfig.CertAsset.SAPub = globalconfig.PersistDir + "/pki/sa.pub" //保存密钥对到宿主机 err = SaveFileToLocal(globalconfig.PersistDir+"/"+clusterID+"/pki/sa.key", sakeypair.PrivateKeyPEM) diff --git a/pkg/cert/tools.go b/pkg/cert/tools.go index b3eb430e2a7b91a9f87c56dd5c27f56171a207fd..89d497e041e2adb80ab5511e18f32b9e5e4d2726 100644 --- a/pkg/cert/tools.go +++ b/pkg/cert/tools.go @@ -21,8 +21,10 @@ import ( "crypto/rsa" "crypto/sha256" "crypto/x509" + "encoding/json" "encoding/pem" "fmt" + "nestos-kubernetes-deployer/pkg/utils" "os" "path/filepath" @@ -109,7 +111,7 @@ func SaveFileToLocal(savepath string, file []byte) error { return err } - logrus.Infof("Successfully saved %s", savepath) + // logrus.Infof("Successfully saved %s", savepath) return nil } @@ -137,3 +139,11 @@ func GenerateCACertHashes(certData []byte) (string, error) { return caCertHashes, nil } + +func CertsToBytes(certs []utils.StorageContent) ([]byte, error) { + jsonData, err := json.Marshal(certs) + if err != nil { + return nil, err + } + return jsonData, nil +} diff --git a/pkg/configmanager/asset/certasset.go b/pkg/configmanager/asset/certasset.go index 40efe4338dd9f610155d5594e942a538c1e3d699..849b31bd667970c1a15dd4681ce8e9a88ef69150 100644 --- a/pkg/configmanager/asset/certasset.go +++ b/pkg/configmanager/asset/certasset.go @@ -18,12 +18,12 @@ package asset //接受用户自定义的各类ca证书路径 type CertAsset struct { - RootCaCertPath string - RootCaKeyPath string - EtcdCaCertPath string - EtcdCaKeyPath string - FrontProxyCaCertPath string - FrontProxyCaKeyPath string - SaPub string - SaKey string + RootCACertPath string `yaml:"rootCACertPath"` + RootCAKeyPath string `yaml:"rootCAKeyPath"` + EtcdCACertPath string `yaml:"etcdCACertPath"` + EtcdCAKeyPath string `yaml:"etcdCAKeyPath"` + FrontProxyCACertPath string `yaml:"frontProxyCACertPath"` + FrontProxyCAKeyPath string `yaml:"frontProxyCAKeyPath"` + SAPub string `yaml:"saPub"` + SAKey string `yaml:"saKey"` } diff --git a/pkg/configmanager/asset/clusterasset.go b/pkg/configmanager/asset/clusterasset.go index 5a02d9db07279bfff3fba4213b1dd07d282124a6..8cc65e419758a9a20a7375de3f248ea741dc7d1e 100644 --- a/pkg/configmanager/asset/clusterasset.go +++ b/pkg/configmanager/asset/clusterasset.go @@ -22,6 +22,7 @@ import ( "nestos-kubernetes-deployer/cmd/command/opts" "nestos-kubernetes-deployer/pkg/utils" "os" + "strings" "time" "github.com/pkg/errors" @@ -31,7 +32,7 @@ import ( // Sets a value of the string type, using the parameter value if the command line argument exists, // otherwise using the default value. -func setStringValue(target *string, value string, defaultValue string) { +func SetStringValue(target *string, value string, defaultValue string) { if value != "" { *target = value } else if *target == "" { @@ -40,7 +41,7 @@ func setStringValue(target *string, value string, defaultValue string) { } // Check whether the value is provided -func checkStringValue(target *string, value string, paramName string) error { +func CheckStringValue(target *string, value string, paramName string) error { if value != "" { *target = value } else if *target == "" { @@ -61,7 +62,7 @@ func setUIntValue(target *uint, value uint, defaultValue uint) { } // Generate token -func generateToken() string { +func GenerateToken() string { // Generate a character set for lowercase letters and numbers. charset := "abcdefghijklmnopqrstuvwxyz0123456789" charsetLength := len(charset) @@ -98,7 +99,7 @@ func setMasterConfigs(mc []NodeAsset, opts *opts.MasterConfig) []NodeAsset { } } else { confs = append(confs, mc...) - for i, _ := range opts.IP { + for i := range opts.IP { if i < len(mc) { confs[i].IP = opts.IP[i] confs[i].Hostname = opts.Hostname[i] @@ -127,7 +128,7 @@ func setWorkerHostname(wc []NodeAsset, opts *opts.WorkerConfig) []NodeAsset { } } else { confs = append(confs, wc...) - for i, _ := range opts.Hostname { + for i := range opts.Hostname { if i < len(wc) { confs[i].Hostname = opts.Hostname[i] continue @@ -149,61 +150,93 @@ func setWorkerHostname(wc []NodeAsset, opts *opts.WorkerConfig) []NodeAsset { // ========== Structure method ========== type ClusterAsset struct { - Cluster_ID string - Architecture string - Platform string - InfraPlatform - UserName string - Password string - SSHKey string - Master []NodeAsset - Worker []NodeAsset - Runtime string `yaml:"runtime"` //后续考虑增加os层面的配置管理,并将runtime放入OS层面的配置中 + ClusterID string `yaml:"clusterID"` + Architecture string + Platform string + InfraPlatform interface{} `yaml:"infraPlatform"` + OSImage `yaml:"osImage"` + UserName string `yaml:"username"` + Password string + SSHKey string `yaml:"sshKey"` + Master []NodeAsset `yaml:"master,omitempty"` + Worker []NodeAsset `yaml:"worker,omitempty"` + BootConfig NodeType `yaml:"bootConfig,omitempty"` + Runtime string `yaml:"runtime,omitempty"` //后续考虑增加os层面的配置管理,并将runtime放入OS层面的配置中 Kubernetes - Housekeeper - CertAsset + Housekeeper `json:"housekeeper" yaml:"-"` //不对housekeeper字段配置 + CertAsset `yaml:"certAsset,omitempty"` + HookConf `yaml:"hooks,omitempty"` } -type InfraPlatform interface { +type NodeType struct { + Controlplane BootFile `yaml:"controlplane,omitempty"` + Master BootFile `yaml:"master,omitempty"` + Worker BootFile `yaml:"worker,omitempty"` + + KickstartMaster []BootFile `yaml:"-"` } -type Kubernetes struct { - KubernetesVersion string `yaml:"kubernetes-version"` - KubernetesAPIVersion string `yaml:"kubernetes-apiversion"` - ApiServerEndpoint string `yaml:"apiserver-endpoint"` - ImageRegistry string `yaml:"image-registry"` - PauseImage string `yaml:"pause-image"` - ReleaseImageURL string `yaml:"release-image-url"` - Token string - AdminKubeConfig string - CertificateKey string - CaCertHash string `json:"-" yaml:"-"` +type BootFile struct { + Content []byte `json:"content" yaml:"-"` + Path string `json:"path"` +} + +type HookConf struct { + PreHookScript string `yaml:"preHookScript,omitempty"` + PostHookYaml string `yaml:"postHookYaml,omitempty"` + ShellFiles []ShellFile `yaml:"-"` + PostHookFiles []string `yaml:"-"` +} + +type ShellFile struct { + Name string `json:"name" yaml:"-"` + Mode int `json:"mode" yaml:"-"` + Content []byte `json:"content" yaml:"-"` +} + +type OSImage struct { + Type string + IsNestOS bool `json:"isNestOS" yaml:"-"` + IsGeneralOS bool `json:"isGeneralOS" yaml:"-"` +} +type Kubernetes struct { + KubernetesVersion string `yaml:"kubernetesVersion"` + KubernetesAPIVersion string `yaml:"kubernetesApiVersion"` + ApiServerEndpoint string `yaml:"apiserverEndpoint"` + ImageRegistry string `yaml:"imageRegistry"` + RegistryMirror string `json:"registryMirror" yaml:"registryMirror,omitempty"` + PauseImage string `yaml:"pauseImage"` + ReleaseImageURL string `json:"releaseImageURL" yaml:"releaseImageURL,omitempty"` + Token string `json:"token" yaml:"token,omitempty"` + AdminKubeConfig string `yaml:"adminKubeconfig"` + CertificateKey string `yaml:"certificateKey"` + CaCertHash string `json:"-" yaml:"-"` + PackageList []string `json:"packageList" yaml:"packageList,omitempty"` + RpmPackagePath string `json:"rpmPackagePath" yaml:"rpmPackagePath,omitempty"` Network } type Network struct { - ServiceSubnet string `yaml:"service-subnet"` - PodSubnet string `yaml:"pod-subnet"` - Plugin string + ServiceSubnet string `yaml:"serviceSubnet"` + PodSubnet string `yaml:"podSubnet"` + Plugin string `yaml:"plugin"` } type Housekeeper struct { - DeployHousekeeper bool - OperatorImageUrl string - ControllerImageUrl string + DeployHousekeeper bool `json:"deployHousekeeper,omitempty"` + OperatorImageURL string `json:"operatorImageURL,omitempty"` + ControllerImageURL string `json:"controllerImageURL,omitempty"` KubeVersion string `json:"-" yaml:"-"` EvictPodForce bool `json:"-" yaml:"-"` MaxUnavailable uint `json:"-" yaml:"-"` OSImageURL string `json:"-" yaml:"-"` } -func (clusterAsset *ClusterAsset) InitClusterAsset(infraAsset InfraAsset, opts *opts.OptionsList) (*ClusterAsset, error) { +func (clusterAsset *ClusterAsset) InitClusterAsset(opts *opts.OptionsList) (*ClusterAsset, error) { // bind info // infra platform - clusterAsset.InfraPlatform = infraAsset - - cf, err := GetDefaultClusterConfig(clusterAsset.Architecture) + cf, err := GetDefaultClusterConfig(clusterAsset.Architecture, strings.ToLower(clusterAsset.Platform)) if err != nil { return nil, err } @@ -219,17 +252,17 @@ func (clusterAsset *ClusterAsset) InitClusterAsset(infraAsset InfraAsset, opts * clusterAsset.Master = setMasterConfigs(clusterAsset.Master, &opts.Master) } if opts.Master.CPU != 0 { - for i, _ := range clusterAsset.Master { + for i := range clusterAsset.Master { clusterAsset.Master[i].HardwareInfo.CPU = opts.Master.CPU } } if opts.Master.RAM != 0 { - for i, _ := range clusterAsset.Master { + for i := range clusterAsset.Master { clusterAsset.Master[i].HardwareInfo.RAM = opts.Master.RAM } } if opts.Master.Disk != 0 { - for i, _ := range clusterAsset.Master { + for i := range clusterAsset.Master { clusterAsset.Master[i].HardwareInfo.Disk = opts.Master.Disk } } @@ -247,59 +280,68 @@ func (clusterAsset *ClusterAsset) InitClusterAsset(infraAsset InfraAsset, opts * if len(opts.Worker.Hostname) != len(opts.Worker.IP) { return nil, fmt.Errorf("the number of configuration parameters worker hostname and ip should be the same") } - for i, _ := range opts.Worker.IP { + for i := range opts.Worker.IP { clusterAsset.Worker[i].IP = opts.Worker.IP[i] } } if opts.Worker.CPU != 0 { - for i, _ := range clusterAsset.Worker { + for i := range clusterAsset.Worker { clusterAsset.Worker[i].HardwareInfo.CPU = opts.Worker.CPU } } if opts.Worker.RAM != 0 { - for i, _ := range clusterAsset.Worker { + for i := range clusterAsset.Worker { clusterAsset.Worker[i].HardwareInfo.RAM = opts.Worker.RAM } } if opts.Worker.Disk != 0 { - for i, _ := range clusterAsset.Worker { + for i := range clusterAsset.Worker { clusterAsset.Worker[i].HardwareInfo.Disk = opts.Worker.Disk } } // cluster info - setStringValue(&clusterAsset.Cluster_ID, opts.ClusterID, cf.Cluster_ID) - setStringValue(&clusterAsset.UserName, opts.UserName, cf.UserName) - setStringValue(&clusterAsset.Password, opts.Password, cf.Password) - setStringValue(&clusterAsset.SSHKey, opts.SSHKey, cf.SSHKey) - setStringValue(&clusterAsset.Kubernetes.KubernetesVersion, opts.KubeVersion, cf.KubernetesVersion) - setStringValue(&clusterAsset.Runtime, opts.Runtime, cf.Runtime) - setStringValue(&clusterAsset.Kubernetes.ApiServerEndpoint, opts.ApiServerEndpoint, cf.ApiServerEndpoint) - setStringValue(&clusterAsset.Kubernetes.ImageRegistry, opts.ImageRegistry, cf.ImageRegistry) - setStringValue(&clusterAsset.Kubernetes.PauseImage, opts.PauseImage, cf.PauseImage) - setStringValue(&clusterAsset.Kubernetes.ReleaseImageURL, opts.ReleaseImageUrl, cf.ReleaseImageURL) - setStringValue(&clusterAsset.Kubernetes.CertificateKey, opts.CertificateKey, opts.CertificateKey) - setStringValue(&clusterAsset.Kubernetes.Token, opts.Token, cf.Token) - setStringValue(&clusterAsset.Kubernetes.Network.ServiceSubnet, opts.NetWork.ServiceSubnet, cf.ServiceSubnet) - setStringValue(&clusterAsset.Kubernetes.Network.PodSubnet, opts.NetWork.PodSubnet, cf.Network.PodSubnet) - setStringValue(&clusterAsset.Kubernetes.Network.Plugin, opts.NetWork.Plugin, cf.Network.Plugin) + SetStringValue(&clusterAsset.ClusterID, opts.ClusterID, cf.ClusterID) + SetStringValue(&clusterAsset.UserName, opts.UserName, cf.UserName) + SetStringValue(&clusterAsset.Password, opts.Password, cf.Password) + SetStringValue(&clusterAsset.SSHKey, opts.SSHKey, cf.SSHKey) + SetStringValue(&clusterAsset.Kubernetes.KubernetesVersion, opts.KubeVersion, cf.KubernetesVersion) + SetStringValue(&clusterAsset.Runtime, opts.Runtime, cf.Runtime) + SetStringValue(&clusterAsset.Kubernetes.ApiServerEndpoint, opts.ApiServerEndpoint, clusterAsset.Master[0].IP+":6443") + SetStringValue(&clusterAsset.Kubernetes.ImageRegistry, opts.ImageRegistry, cf.ImageRegistry) + SetStringValue(&clusterAsset.Kubernetes.PauseImage, opts.PauseImage, cf.PauseImage) + SetStringValue(&clusterAsset.Kubernetes.ReleaseImageURL, opts.ReleaseImageUrl, cf.ReleaseImageURL) + SetStringValue(&clusterAsset.Kubernetes.CertificateKey, opts.CertificateKey, opts.CertificateKey) + SetStringValue(&clusterAsset.Kubernetes.Token, opts.Token, cf.Token) + SetStringValue(&clusterAsset.Kubernetes.Network.ServiceSubnet, opts.NetWork.ServiceSubnet, cf.ServiceSubnet) + SetStringValue(&clusterAsset.Kubernetes.Network.PodSubnet, opts.NetWork.PodSubnet, cf.Network.PodSubnet) + SetStringValue(&clusterAsset.Kubernetes.Network.Plugin, opts.NetWork.Plugin, cf.Network.Plugin) + SetStringValue(&clusterAsset.PreHookScript, opts.PreHookScript, "") + SetStringValue(&clusterAsset.PostHookYaml, opts.PostHookYaml, "") + SetStringValue(&clusterAsset.OSImage.Type, opts.OSImage.Type, "") + apiVersion, err := utils.GetKubernetesApiVersion(opts.KubernetesAPIVersion) if err != nil { logrus.Errorf("Error getting kubernetes api version: %v\n", err) return nil, err } - setStringValue(&clusterAsset.Kubernetes.KubernetesAPIVersion, apiVersion, cf.KubernetesAPIVersion) + SetStringValue(&clusterAsset.Kubernetes.KubernetesAPIVersion, apiVersion, cf.KubernetesAPIVersion) if clusterAsset.Housekeeper.DeployHousekeeper || opts.Housekeeper.DeployHousekeeper { - setStringValue(&clusterAsset.Housekeeper.OperatorImageUrl, opts.Housekeeper.OperatorImageUrl, cf.OperatorImageUrl) - setStringValue(&clusterAsset.Housekeeper.ControllerImageUrl, opts.Housekeeper.ControllerImageUrl, cf.ControllerImageUrl) - setStringValue(&clusterAsset.Housekeeper.KubeVersion, opts.Housekeeper.KubeVersion, "") - setStringValue(&clusterAsset.Housekeeper.OSImageURL, opts.Housekeeper.OSImageURL, "") + SetStringValue(&clusterAsset.Housekeeper.OperatorImageURL, opts.Housekeeper.OperatorImageUrl, cf.OperatorImageURL) + SetStringValue(&clusterAsset.Housekeeper.ControllerImageURL, opts.Housekeeper.ControllerImageUrl, cf.ControllerImageURL) + SetStringValue(&clusterAsset.Housekeeper.KubeVersion, opts.Housekeeper.KubeVersion, "") + SetStringValue(&clusterAsset.Housekeeper.OSImageURL, opts.Housekeeper.OSImageURL, "") setUIntValue(&clusterAsset.Housekeeper.MaxUnavailable, opts.Housekeeper.MaxUnavailable, cf.MaxUnavailable) clusterAsset.Housekeeper.EvictPodForce = opts.Housekeeper.EvictPodForce } + if err := GetCmdHooks(&clusterAsset.HookConf); err != nil { + logrus.Errorf("error in initializing cluster hooks config: %v", err) + return nil, err + } + return clusterAsset, nil } @@ -324,71 +366,88 @@ func (clusterAsset *ClusterAsset) Persist(dir string) error { return nil } -func GetDefaultClusterConfig(arch string) (*ClusterAsset, error) { +func GetDefaultClusterConfig(arch string, platform string) (*ClusterAsset, error) { var ( - OperatorImageUrl string - ControllerImageUrl string + OperatorImageURL string + ControllerImageURL string ) switch arch { case "amd64", "x86_64": - OperatorImageUrl = "hub.oepkgs.net/nestos/housekeeper/amd64/housekeeper-operator-manager:0.1.0" - ControllerImageUrl = "hub.oepkgs.net/nestos/housekeeper/amd64/housekeeper-controller-manager:0.1.0" + OperatorImageURL = "hub.oepkgs.net/nestos/housekeeper/amd64/housekeeper-operator-manager:0.1.0" + ControllerImageURL = "hub.oepkgs.net/nestos/housekeeper/amd64/housekeeper-controller-manager:0.1.0" case "arm64", "aarch64": - OperatorImageUrl = "hub.oepkgs.net/nestos/housekeeper/arm64/housekeeper-operator-manager:0.1.0" - ControllerImageUrl = "hub.oepkgs.net/nestos/housekeeper/arm64/housekeeper-controller-manager:0.1.0" + OperatorImageURL = "hub.oepkgs.net/nestos/housekeeper/arm64/housekeeper-operator-manager:0.1.0" + ControllerImageURL = "hub.oepkgs.net/nestos/housekeeper/arm64/housekeeper-controller-manager:0.1.0" default: return nil, errors.New("unsupported architecture") } - return &ClusterAsset{ - Cluster_ID: "cluster", - Architecture: arch, - Platform: "libvirt", - UserName: "root", - Password: "$1$yoursalt$UGhjCXAJKpWWpeN8xsF.c/", - SSHKey: utils.GetDefaultPubKeyPath(), - Master: []NodeAsset{ + clusterAsset := &ClusterAsset{} + clusterAsset.ClusterID = "cluster" + clusterAsset.Architecture = arch + clusterAsset.Platform = platform + clusterAsset.UserName = "root" + clusterAsset.SSHKey = utils.GetDefaultPubKeyPath() + + switch platform { + case "libvirt", "openstack": + clusterAsset.Master = []NodeAsset{ { Hostname: "k8s-master01", + IP: "", HardwareInfo: HardwareInfo{ CPU: 4, RAM: 8192, Disk: 50, }, - IP: "192.168.132.11", }, - }, - Worker: []NodeAsset{ + } + clusterAsset.Worker = []NodeAsset{ { Hostname: "k8s-worker01", + IP: "", HardwareInfo: HardwareInfo{ CPU: 4, RAM: 8192, Disk: 50, }, - IP: "", }, - }, - Runtime: "isulad", - Kubernetes: Kubernetes{ - KubernetesVersion: "v1.23.10", - KubernetesAPIVersion: "v1beta3", - ApiServerEndpoint: utils.GetApiServerEndpoint("192.168.132.11"), - ImageRegistry: "k8s.gcr.io", - PauseImage: "pause:3.6", - ReleaseImageURL: "", - Token: generateToken(), - CertificateKey: "a301c9c55596c54c5d4c7173aa1e3b6fd304130b0c703bb23149c0c69f94b8e0", - Network: Network{ - ServiceSubnet: "10.96.0.0/16", - PodSubnet: "10.244.0.0/16", - Plugin: "https://projectcalico.docs.tigera.io/archive/v3.22/manifests/calico.yaml", + } + clusterAsset.Password = "$1$yoursalt$UGhjCXAJKpWWpeN8xsF.c/" + case "pxe", "ipxe": + clusterAsset.Master = []NodeAsset{ + { + Hostname: "k8s-master01", + IP: "", }, + } + clusterAsset.Password = "$6$mX6/gt6yDD8LmSqb$rQ95JPHeWBZQ0Gyjvw5/hUbGK57TJXjeXtDauom0Tr4z88mn4qDYtH/yc8nDxE/8HOhy.Fx4WYS1vTTune1l50" + default: + return nil, errors.New("unsupported platform") + } + + clusterAsset.Runtime = "crio" + clusterAsset.Kubernetes = Kubernetes{ + KubernetesVersion: "v1.29.1", + KubernetesAPIVersion: "v1beta3", + ApiServerEndpoint: "", + ImageRegistry: "registry.k8s.io", + PauseImage: "pause:3.9", + ReleaseImageURL: "", + Token: GenerateToken(), + CertificateKey: "a301c9c55596c54c5d4c7173aa1e3b6fd304130b0c703bb23149c0c69f94b8e0", + Network: Network{ + ServiceSubnet: "10.96.0.0/16", + PodSubnet: "10.244.0.0/16", + Plugin: "", }, - Housekeeper: Housekeeper{ - OperatorImageUrl: OperatorImageUrl, - ControllerImageUrl: ControllerImageUrl, - MaxUnavailable: 2, - }, - }, nil + } + + clusterAsset.Housekeeper = Housekeeper{ + OperatorImageURL: OperatorImageURL, + ControllerImageURL: ControllerImageURL, + MaxUnavailable: 2, + } + + return clusterAsset, nil } diff --git a/pkg/configmanager/asset/hookconfig.go b/pkg/configmanager/asset/hookconfig.go new file mode 100644 index 0000000000000000000000000000000000000000..001d8e49f6f386f5f601f3464776b137f5e6f799 --- /dev/null +++ b/pkg/configmanager/asset/hookconfig.go @@ -0,0 +1,232 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package asset + +import ( + "bufio" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + OneMB = 1024 * 1024 // 1MB + MaxHookFileSize = OneMB + ShellFileType = "shell" + YAMLFileType = "yaml" + YAMLFileExt = ".yaml" + YMLFileExt = ".yml" +) + +//若传入的是一个目录,则会解析当前目录下的文件(注意:不会递归处理子目录下的文件) +//若传入的是一个文件而非目录,则会直接解析该文件并返回 +func GetCmdHooks(conf *HookConf) error { + if conf == nil { + return errors.New("received nil pointer for HookConf parameter") + } + + if conf.PreHookScript != "" { + ShellFiles, err := getDirAndShells(conf.PreHookScript) + if err != nil { + return err + } + conf.ShellFiles = ShellFiles + } + + if conf.PostHookYaml != "" { + postHookFiles, err := getDirAndYamls(conf.PostHookYaml) + if err != nil { + return err + } + conf.PostHookFiles = postHookFiles + } + + return nil +} + +func getDirAndShells(p string) ([]ShellFile, error) { + var ( + hookFiles []ShellFile + totalFileSize int64 + ) + fileInfo, err := os.Stat(p) + if err != nil { + return nil, err + } + if !fileInfo.IsDir() { + hf, err := resolveFile(p, ShellFileType) + if err != nil { + return nil, err + } + hookFiles = append(hookFiles, hf) + + if fileInfo.Size() > MaxHookFileSize { + return nil, fmt.Errorf("total size of shell script file exceeds the limit: %d bytes (max: %d bytes)", fileInfo.Size(), MaxHookFileSize) + } + return hookFiles, nil + } + + files, err := os.ReadDir(p) + if err != nil { + return nil, fmt.Errorf("failed to read directory: %s", err) + } + if len(files) == 0 { + return nil, fmt.Errorf("the file list is empty") + } + for _, file := range files { + if file.IsDir() { + continue // Skip directories + } + filePath := filepath.Join(p, file.Name()) + if err := checkHookFile(filePath, ShellFileType); err != nil { + logrus.Debugf("failed to check hook file: %v", err) + continue + } + hf, err := resolveFile(filePath, ShellFileType) + if err != nil { + logrus.Debugf("failed to resolve hook file %s: %v\n", filePath, err) + continue + } + hookFiles = append(hookFiles, hf) + + // Get file info for size + fileStat, err := os.Stat(filePath) + if err != nil { + return nil, err + } + totalFileSize += fileStat.Size() + } + if len(hookFiles) == 0 { + return nil, fmt.Errorf("no valid hook files found in folder: %s", p) + } + if totalFileSize > MaxHookFileSize { + return nil, fmt.Errorf("total size of shell script file in the directory exceeds the limit: %d bytes (max: %d bytes)", fileInfo.Size(), MaxHookFileSize) + } + + return hookFiles, nil +} + +func getDirAndYamls(path string) ([]string, error) { + file, err := os.Stat(path) + if err != nil { + return nil, err + } + + if file.IsDir() { + return resolvePostHookPath(path) + } + if err := checkHookFile(path, YAMLFileType); err != nil { + return nil, err + } + return []string{path}, nil +} + +func resolveFile(f string, fileType string) (ShellFile, error) { + var hf ShellFile + hf.Name = path.Base(f) + + if err := checkHookFile(f, fileType); err != nil { + return hf, err + } + + file, err := os.Open(f) + if err != nil { + return hf, err + } + defer file.Close() + + fileInfo, err := file.Stat() + if err != nil { + return hf, err + } + hf.Mode = int(fileInfo.Mode().Perm()) + + content, err := io.ReadAll(file) + if err != nil { + return hf, err + } + hf.Content = content + + return hf, nil +} + +func resolvePostHookPath(p string) ([]string, error) { + var files []string + + rd, err := os.ReadDir(p) + if err != nil { + return nil, fmt.Errorf("failed to read directory: %s", err) + } + + if len(rd) == 0 { + return nil, fmt.Errorf("empty directory: %s", p) + } + + for _, fi := range rd { + if err := checkHookFile(path.Join(p, fi.Name()), YAMLFileType); err == nil { + files = append(files, path.Join(p, fi.Name())) + } else { + logrus.Debugf("failed to check hook file:%v", err) + } + } + if len(files) == 0 { + return nil, fmt.Errorf("no valid hook files found in directory: %s", p) + } + return files, nil +} + +func checkHookFile(fileName string, fileType string) error { + fileInfo, err := os.Stat(fileName) + if err != nil { + return err + } + + if !fileInfo.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", fileInfo.Name()) + } + + switch fileType { + case ShellFileType: + file, err := os.Open(fileName) + if err != nil { + return err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + if scanner.Scan() { + firstLine := scanner.Text() + if !strings.HasPrefix(firstLine, "#!") { + return fmt.Errorf("non-executable file: %s", fileName) + } + } + case YAMLFileType: + ext := filepath.Ext(fileName) + if ext != YAMLFileExt && ext != YMLFileExt { + return fmt.Errorf("%s is an invalid file extension", fileName) + } + default: + logrus.Debugf("unknown file type") + } + return nil +} diff --git a/pkg/configmanager/asset/infraasset.go b/pkg/configmanager/asset/infraasset.go deleted file mode 100644 index 8661732154eb6d2b0191fe4b4e22355028a783e4..0000000000000000000000000000000000000000 --- a/pkg/configmanager/asset/infraasset.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package asset - -import ( - "errors" - "nestos-kubernetes-deployer/cmd/command/opts" - "runtime" -) - -type InfraAsset interface { -} - -func InitInfraAsset(clusterAsset *ClusterAsset, opts *opts.OptionsList) (InfraAsset, error) { - - setStringValue(&clusterAsset.Architecture, opts.Arch, runtime.GOARCH) - setStringValue(&clusterAsset.Platform, opts.Platform, "libvirt") - switch clusterAsset.Platform { - case "openstack", "Openstack", "OpenStack": - openstackAsset, ok := convertMap(clusterAsset.InfraPlatform, "openstack") - if !ok { - return nil, errors.New("failed to get openstack asset") - } - infraAsset, err := initOpenStackAssetFromMap(openstackAsset, opts) - if err != nil { - return nil, err - } - return infraAsset, nil - case "libvirt", "Libvirt": - libvirtAsset, ok := convertMap(clusterAsset.InfraPlatform, "libvirt") - if !ok { - return nil, errors.New("failed to get libvirt asset") - } - infraAsset, err := initLibvirtAssetFromMap(libvirtAsset, opts, clusterAsset.Architecture) - if err != nil { - return nil, err - } - return infraAsset, nil - default: - return nil, errors.New("unsupported platform") - } -} - -func convertMap(inputMap interface{}, platform string) (map[string]interface{}, bool) { - resultMap := make(map[string]interface{}) - - if inputMap == nil { - // If inputMap is nil, return an empty map corresponding to the platform structure. - switch platform { - case "openstack", "Openstack", "OpenStack": - return map[string]interface{}{ - "username": "", - "password": "", - "tenant_name": "", - "auth_url": "", - "region": "", - "internal_network": "", - "external_network": "", - "glance_name": "", - "availability_zone": "", - }, true - case "libvirt", "Libvirt": - return map[string]interface{}{ - "uri": "", - "osimage_path": "", - "cidr": "", - "gateway": "", - }, true - default: - return resultMap, false - } - } - - // Check if the inputMap is of type map[interface{}]interface{}. - if inputMap, ok := inputMap.(map[interface{}]interface{}); ok { - for key, value := range inputMap { - keyStr, ok := key.(string) - if !ok { - return resultMap, false - } - resultMap[keyStr] = value - } - } else { - // If not, handle other types as needed. - return resultMap, false - } - - return resultMap, true -} - -type OpenStackAsset struct { - UserName string - Password string - Tenant_Name string - Auth_URL string - Region string - Internal_Network string - External_Network string - Glance_Name string - Availability_Zone string -} - -func initOpenStackAssetFromMap(openstackMap map[string]interface{}, opts *opts.OptionsList) (InfraAsset, error) { - openstackAsset := &OpenStackAsset{} - - updateFieldFromMap("username", &openstackAsset.UserName, openstackMap) - updateFieldFromMap("password", &openstackAsset.Password, openstackMap) - updateFieldFromMap("tenant_name", &openstackAsset.Tenant_Name, openstackMap) - updateFieldFromMap("auth_url", &openstackAsset.Auth_URL, openstackMap) - updateFieldFromMap("region", &openstackAsset.Region, openstackMap) - updateFieldFromMap("internal_network", &openstackAsset.Internal_Network, openstackMap) - updateFieldFromMap("external_network", &openstackAsset.External_Network, openstackMap) - updateFieldFromMap("glance_name", &openstackAsset.Glance_Name, openstackMap) - updateFieldFromMap("availability_zone", &openstackAsset.Availability_Zone, openstackMap) - - if err := checkStringValue(&openstackAsset.UserName, opts.InfraPlatform.OpenStack.UserName, "openstack_username"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.Password, opts.InfraPlatform.OpenStack.Password, "openstack_password"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.Tenant_Name, opts.InfraPlatform.OpenStack.Tenant_Name, "openstack_tenant_name"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.Auth_URL, opts.InfraPlatform.OpenStack.Auth_URL, "openstack_auth_url"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.Region, opts.InfraPlatform.OpenStack.Region, "openstack_region"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.Internal_Network, opts.InfraPlatform.OpenStack.Internal_Network, "openstack_internal_network"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.External_Network, opts.InfraPlatform.OpenStack.External_Network, "openstack_external_network"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.Glance_Name, opts.InfraPlatform.OpenStack.Glance_Name, "openstack_glance_name"); err != nil { - return nil, err - } - if err := checkStringValue(&openstackAsset.Availability_Zone, opts.InfraPlatform.OpenStack.Availability_Zone, "openstack_availability_zone"); err != nil { - return nil, err - } - - return openstackAsset, nil -} - -type LibvirtAsset struct { - URI string - OSImage string - CIDR string - Gateway string -} - -func initLibvirtAssetFromMap(libvirtMap map[string]interface{}, opts *opts.OptionsList, arch string) (InfraAsset, error) { - libvirtAsset := &LibvirtAsset{} - - updateFieldFromMap("uri", &libvirtAsset.URI, libvirtMap) - updateFieldFromMap("osimage", &libvirtAsset.OSImage, libvirtMap) - updateFieldFromMap("cidr", &libvirtAsset.CIDR, libvirtMap) - updateFieldFromMap("gateway", &libvirtAsset.Gateway, libvirtMap) - - osImage := "https://nestos.org.cn/nestos20230928/nestos-for-container/x86_64/NestOS-For-Container-22.03-LTS-SP2.20230928.0-qemu.x86_64.qcow2" - if arch == "arm64" || arch == "aarch64" { - osImage = "https://nestos.org.cn/nestos20230928/nestos-for-container/aarch64/NestOS-For-Container-22.03-LTS-SP2.20230928.0-qemu.aarch64.qcow2" - } - - setStringValue(&libvirtAsset.URI, opts.InfraPlatform.Libvirt.URI, "qemu:///system") - setStringValue(&libvirtAsset.OSImage, opts.InfraPlatform.Libvirt.OSImage, osImage) - setStringValue(&libvirtAsset.CIDR, opts.InfraPlatform.Libvirt.CIDR, "192.168.132.0/24") - setStringValue(&libvirtAsset.Gateway, opts.InfraPlatform.Libvirt.Gateway, "192.168.132.1") - - return libvirtAsset, nil -} - -func updateFieldFromMap(fieldName string, fieldValue *string, inputMap map[string]interface{}) { - if value, ok := inputMap[fieldName]; ok { - if strValue, ok := value.(string); ok && *fieldValue == "" { - *fieldValue = strValue - } - } -} diff --git a/pkg/configmanager/asset/infraasset/infraasset.go b/pkg/configmanager/asset/infraasset/infraasset.go new file mode 100644 index 0000000000000000000000000000000000000000..363b90edbe9ccb4bf8ca520eb92978647d27b10a --- /dev/null +++ b/pkg/configmanager/asset/infraasset/infraasset.go @@ -0,0 +1,156 @@ +/* +Copyright 2023 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infraasset + +import ( + "errors" + "nestos-kubernetes-deployer/cmd/command/opts" + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "runtime" + "strings" +) + +type InfraAsset interface { + InitAsset(assetMap map[string]interface{}, opts *opts.OptionsList, args ...interface{}) (InfraAsset, error) +} + +func InitInfraAsset(clusterAsset *asset.ClusterAsset, opts *opts.OptionsList) (InfraAsset, error) { + asset.SetStringValue(&clusterAsset.Architecture, opts.Arch, runtime.GOARCH) + asset.SetStringValue(&clusterAsset.Platform, opts.Platform, "libvirt") + + switch strings.ToLower(clusterAsset.Platform) { + case "openstack": + assetMap, ok := convertMap(clusterAsset.InfraPlatform, "openstack") + if !ok { + return nil, errors.New("failed to get openstack asset") + } + + openstackAsset := &OpenStackAsset{} + infraAsset, err := openstackAsset.InitAsset(assetMap, opts) + if err != nil { + return nil, err + } + return infraAsset, nil + case "libvirt": + assetMap, ok := convertMap(clusterAsset.InfraPlatform, "libvirt") + if !ok { + return nil, errors.New("failed to get libvirt asset") + } + + libvirtAsset := &LibvirtAsset{} + infraAsset, err := libvirtAsset.InitAsset(assetMap, opts, clusterAsset.Architecture) + if err != nil { + return nil, err + } + return infraAsset, nil + case "pxe": + assetMap, ok := convertMap(clusterAsset.InfraPlatform, "pxe") + if !ok { + return nil, errors.New("failed to get pxe asset") + } + + pxeAsset := &PXEAsset{} + infraAsset, err := pxeAsset.InitAsset(assetMap, opts) + if err != nil { + return nil, err + } + return infraAsset, nil + case "ipxe": + assetMap, ok := convertMap(clusterAsset.InfraPlatform, "ipxe") + if !ok { + return nil, errors.New("failed to get ipxe asset") + } + + ipxeAsset := &IPXEAsset{} + infraAsset, err := ipxeAsset.InitAsset(assetMap, opts) + if err != nil { + return nil, err + } + return infraAsset, nil + default: + return nil, errors.New("unsupported platform") + } +} + +func convertMap(inputMap interface{}, platform string) (map[string]interface{}, bool) { + resultMap := make(map[string]interface{}) + + if inputMap == nil { + // If inputMap is nil, return an empty map corresponding to the platform structure. + switch strings.ToLower(platform) { + case "libvirt": + return map[string]interface{}{ + "uri": "", + "osImage": "", + "cidr": "", + "gateway": "", + }, true + case "openstack": + return map[string]interface{}{ + "username": "", + "password": "", + "tenantName": "", + "authURL": "", + "region": "", + "internalNetwork": "", + "externalNetwork": "", + "glanceName": "", + "availabilityZone": "", + }, true + case "pxe": + return map[string]interface{}{ + "httpServerPort": "", + "httpRootDir": "", + "tftpServerIP": "", + "tftpServerPort": "", + "tftpRootDir": "", + }, true + case "ipxe": + return map[string]interface{}{ + "ipxePort": "", + "ipxeFilePath": "", + "ipxeOSInstallTreePath": "", + }, true + default: + return resultMap, false + } + } + + // Check if the inputMap is of type map[interface{}]interface{}. + if inputMap, ok := inputMap.(map[interface{}]interface{}); ok { + for key, value := range inputMap { + keyStr, ok := key.(string) + if !ok { + return resultMap, false + } + resultMap[keyStr] = value + } + } else { + // If not, handle other types as needed. + return resultMap, false + } + + return resultMap, true +} + +func updateFieldFromMap(fieldName string, fieldValue *string, inputMap map[string]interface{}) { + if value, ok := inputMap[fieldName]; ok { + if strValue, ok := value.(string); ok && *fieldValue == "" { + *fieldValue = strValue + } + } +} diff --git a/pkg/configmanager/asset/infraasset/ipxeasset.go b/pkg/configmanager/asset/infraasset/ipxeasset.go new file mode 100644 index 0000000000000000000000000000000000000000..d053c8d9cbb6a33a9ea5a9e9aa49183a3638a70f --- /dev/null +++ b/pkg/configmanager/asset/infraasset/ipxeasset.go @@ -0,0 +1,44 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infraasset + +import ( + "nestos-kubernetes-deployer/cmd/command/opts" + "nestos-kubernetes-deployer/pkg/configmanager/asset" +) + +type IPXEAsset struct { + IP string + Port string + FilePath string `yaml:"filePath"` + OSInstallTreePath string `yaml:"osInstallTreePath"` +} + +func (ia *IPXEAsset) InitAsset(ipxeMap map[string]interface{}, opts *opts.OptionsList, args ...interface{}) (InfraAsset, error) { + updateFieldFromMap("port", &ia.Port, ipxeMap) + asset.SetStringValue(&ia.Port, opts.InfraPlatform.IPXE.Port, "9080") + + updateFieldFromMap("filePath", &ia.FilePath, ipxeMap) + if err := asset.CheckStringValue(&ia.FilePath, opts.InfraPlatform.IPXE.FilePath, "ipxe-osInstallTreePath"); err != nil { + return nil, err + } + + updateFieldFromMap("osInstallTreePath", &ia.OSInstallTreePath, ipxeMap) + asset.SetStringValue(&ia.OSInstallTreePath, opts.InfraPlatform.IPXE.OSInstallTreePath, "/var/www/html/") + + return ia, nil +} diff --git a/pkg/configmanager/asset/infraasset/libvirtasset.go b/pkg/configmanager/asset/infraasset/libvirtasset.go new file mode 100644 index 0000000000000000000000000000000000000000..7fc9c4094e85b969c35e04e8666d9a4be5f53732 --- /dev/null +++ b/pkg/configmanager/asset/infraasset/libvirtasset.go @@ -0,0 +1,47 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infraasset + +import ( + "nestos-kubernetes-deployer/cmd/command/opts" + "nestos-kubernetes-deployer/pkg/configmanager/asset" +) + +type LibvirtAsset struct { + URI string + OSPath string `yaml:"osPath"` + CIDR string + Gateway string +} + +func (la *LibvirtAsset) InitAsset(libvirtMap map[string]interface{}, opts *opts.OptionsList, args ...interface{}) (InfraAsset, error) { + updateFieldFromMap("uri", &la.URI, libvirtMap) + asset.SetStringValue(&la.URI, opts.InfraPlatform.Libvirt.URI, "qemu:///system") + + updateFieldFromMap("osPath", &la.OSPath, libvirtMap) + if err := asset.CheckStringValue(&la.OSPath, opts.InfraPlatform.Libvirt.OSPath, "libvirt-osPath"); err != nil { + return nil, err + } + + updateFieldFromMap("cidr", &la.CIDR, libvirtMap) + asset.SetStringValue(&la.CIDR, opts.InfraPlatform.Libvirt.CIDR, "192.168.132.0/24") + + updateFieldFromMap("gateway", &la.Gateway, libvirtMap) + asset.SetStringValue(&la.Gateway, opts.InfraPlatform.Libvirt.Gateway, "192.168.132.1") + + return la, nil +} diff --git a/pkg/configmanager/asset/infraasset/openstackasset.go b/pkg/configmanager/asset/infraasset/openstackasset.go new file mode 100644 index 0000000000000000000000000000000000000000..de87f8daf91d5c3eccc19717fee7eaf46382ed1a --- /dev/null +++ b/pkg/configmanager/asset/infraasset/openstackasset.go @@ -0,0 +1,73 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infraasset + +import ( + "nestos-kubernetes-deployer/cmd/command/opts" + "nestos-kubernetes-deployer/pkg/configmanager/asset" +) + +type OpenStackAsset struct { + UserName string `yaml:"username"` + Password string `yaml:"password"` + TenantName string `yaml:"tenantName"` + AuthURL string `yaml:"authURL"` + Region string `yaml:"region"` + InternalNetwork string `yaml:"internalNetwork"` + ExternalNetwork string `yaml:"externalNetwork"` + GlanceName string `yaml:"glanceName"` + AvailabilityZone string `yaml:"availabilityZone"` +} + +func (oa *OpenStackAsset) InitAsset(openstackMap map[string]interface{}, opts *opts.OptionsList, args ...interface{}) (InfraAsset, error) { + updateFieldFromMap("username", &oa.UserName, openstackMap) + asset.SetStringValue(&oa.UserName, opts.InfraPlatform.OpenStack.UserName, "admin") + + updateFieldFromMap("password", &oa.Password, openstackMap) + if err := asset.CheckStringValue(&oa.Password, opts.InfraPlatform.OpenStack.Password, "openstack-password"); err != nil { + return nil, err + } + + updateFieldFromMap("tenantName", &oa.TenantName, openstackMap) + asset.SetStringValue(&oa.TenantName, opts.InfraPlatform.OpenStack.TenantName, "admin") + + updateFieldFromMap("authURL", &oa.AuthURL, openstackMap) + asset.SetStringValue(&oa.AuthURL, opts.InfraPlatform.OpenStack.AuthURL, "http://controller:5000/v3") + + updateFieldFromMap("region", &oa.Region, openstackMap) + asset.SetStringValue(&oa.Region, opts.InfraPlatform.OpenStack.Region, "RegionOne") + + updateFieldFromMap("internalNetwork", &oa.InternalNetwork, openstackMap) + if err := asset.CheckStringValue(&oa.InternalNetwork, opts.InfraPlatform.OpenStack.InternalNetwork, "openstack-internalNetwork"); err != nil { + return nil, err + } + + updateFieldFromMap("externalNetwork", &oa.ExternalNetwork, openstackMap) + if err := asset.CheckStringValue(&oa.ExternalNetwork, opts.InfraPlatform.OpenStack.ExternalNetwork, "openstack-externalNetwork"); err != nil { + return nil, err + } + + updateFieldFromMap("glanceName", &oa.GlanceName, openstackMap) + if err := asset.CheckStringValue(&oa.GlanceName, opts.InfraPlatform.OpenStack.GlanceName, "openstack-glanceName"); err != nil { + return nil, err + } + + updateFieldFromMap("availabilityZone", &oa.AvailabilityZone, openstackMap) + asset.SetStringValue(&oa.AvailabilityZone, opts.InfraPlatform.OpenStack.AvailabilityZone, "nova") + + return oa, nil +} diff --git a/pkg/configmanager/asset/infraasset/pxeasset.go b/pkg/configmanager/asset/infraasset/pxeasset.go new file mode 100644 index 0000000000000000000000000000000000000000..ffaba7da747671c2a8e0ffc5859be76fa1741513 --- /dev/null +++ b/pkg/configmanager/asset/infraasset/pxeasset.go @@ -0,0 +1,51 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infraasset + +import ( + "nestos-kubernetes-deployer/cmd/command/opts" + "nestos-kubernetes-deployer/pkg/configmanager/asset" +) + +type PXEAsset struct { + IP string + HTTPServerPort string `yaml:"httpServerPort"` + HTTPRootDir string `yaml:"httpRootDir"` + TFTPServerPort string `yaml:"tftpServerPort"` + TFTPRootDir string `yaml:"tftpRootDir"` +} + +func (pa *PXEAsset) InitAsset(pxeMap map[string]interface{}, opts *opts.OptionsList, args ...interface{}) (InfraAsset, error) { + updateFieldFromMap("ip", &pa.IP, pxeMap) + if err := asset.CheckStringValue(&pa.IP, opts.InfraPlatform.PXE.IP, "pxe-ip"); err != nil { + return nil, err + } + + updateFieldFromMap("httpServerPort", &pa.HTTPServerPort, pxeMap) + asset.SetStringValue(&pa.HTTPServerPort, opts.InfraPlatform.PXE.HTTPServerPort, "9080") + + updateFieldFromMap("httpRootDir", &pa.HTTPRootDir, pxeMap) + asset.SetStringValue(&pa.HTTPRootDir, opts.InfraPlatform.PXE.HTTPRootDir, "/var/www/html/") + + updateFieldFromMap("tftpServerPort", &pa.TFTPServerPort, pxeMap) + asset.SetStringValue(&pa.TFTPServerPort, opts.InfraPlatform.PXE.TFTPServerPort, "69") + + updateFieldFromMap("tftpRootDir", &pa.TFTPRootDir, pxeMap) + asset.SetStringValue(&pa.TFTPRootDir, opts.InfraPlatform.PXE.TFTPRootDir, "/var/lib/tftpboot/") + + return pa, nil +} diff --git a/pkg/configmanager/asset/node_asset.go b/pkg/configmanager/asset/nodeasset.go similarity index 66% rename from pkg/configmanager/asset/node_asset.go rename to pkg/configmanager/asset/nodeasset.go index b8e856950a869b8ba2ed0d24238950aba99029e9..b8ce4868c6e19a5e3165d7218601de032aab3a8c 100644 --- a/pkg/configmanager/asset/node_asset.go +++ b/pkg/configmanager/asset/nodeasset.go @@ -19,11 +19,10 @@ package asset import "nestos-kubernetes-deployer/pkg/utils" type NodeAsset struct { - Hostname string - IP string - HardwareInfo - Ignitions `json:"ignitions"` - Certs []utils.StorageContent `json:"-" yaml:"-"` // Certificates content (not printed in JSON and YAML) + Hostname string + IP string + HardwareInfo `yaml:"hardwareInfo,omitempty"` + Certs []utils.StorageContent `json:"-" yaml:"-"` // Certificates content (not printed in JSON and YAML) } type HardwareInfo struct { @@ -31,9 +30,3 @@ type HardwareInfo struct { RAM uint Disk uint } - -type Ignitions struct { - CreateIgnContent []byte `json:"-" yaml:"-"` - CreateIgnPath string `json:"create_ign_path"` - MergeIgnPath string `json:"merge_ign_path"` -} diff --git a/pkg/configmanager/globalconfig/globalconfig.go b/pkg/configmanager/globalconfig/globalconfig.go index 45e7475f0a8997b3785b318d4da673b2657b034d..d278e86165c750d5dc72efa124b4ba561cb499b4 100644 --- a/pkg/configmanager/globalconfig/globalconfig.go +++ b/pkg/configmanager/globalconfig/globalconfig.go @@ -31,9 +31,9 @@ const GlobalConfigFile = "global_config.yaml" func InitGlobalConfig(opts *opts.OptionsList) (*GlobalConfig, error) { globalAsset := &GlobalConfig{ - Log_Level: "default log level", - ClusterConfig_Path: "", - PersistDir: opts.RootOptDir, // default persist directory + LogLevel: "default log level", + ClusterConfigPath: "", + PersistDir: opts.RootOptDir, // default persist directory BootstrapUrl: BootstrapUrl{ BootstrapIgnPort: "9080", // default port }, @@ -52,8 +52,8 @@ func InitGlobalConfig(opts *opts.OptionsList) (*GlobalConfig, error) { } } - if opts.NKD.Log_Level != "" { - globalAsset.Log_Level = opts.NKD.Log_Level + if opts.NKD.LogLevel != "" { + globalAsset.LogLevel = opts.NKD.LogLevel } if opts.NKD.BootstrapIgnHost != "" { globalAsset.BootstrapIgnHost = opts.NKD.BootstrapIgnHost @@ -63,7 +63,8 @@ func InitGlobalConfig(opts *opts.OptionsList) (*GlobalConfig, error) { globalAsset.BootstrapIgnPort = opts.NKD.BootstrapIgnPort } if !utils.IsPortOpen(globalAsset.BootstrapIgnPort) { - return nil, fmt.Errorf("The port %s is occupied.", globalAsset.BootstrapIgnPort) + errMsg := fmt.Sprintf("The port %s is occupied.", globalAsset.BootstrapIgnPort) + return nil, fmt.Errorf(errMsg) } if globalAsset.BootstrapIgnHost == "" { @@ -89,15 +90,15 @@ func InitGlobalConfig(opts *opts.OptionsList) (*GlobalConfig, error) { // ========== Structure method ========== type GlobalConfig struct { - Log_Level string - ClusterConfig_Path string - PersistDir string // default: /etc/nkd + LogLevel string `yaml:"logLevel"` + ClusterConfigPath string `yaml:"clusterConfigPath"` + PersistDir string // default: /etc/nkd BootstrapUrl } type BootstrapUrl struct { - BootstrapIgnHost string `yaml:"bootstrap_ign_host"` - BootstrapIgnPort string `yaml:"bootstrap_ign_port"` + BootstrapIgnHost string `yaml:"bootstrapIgnHost"` + BootstrapIgnPort string `yaml:"bootstrapIgnPort"` } // Delete deletes the global asset. diff --git a/pkg/configmanager/manager.go b/pkg/configmanager/manager.go index 4920120e8ca7189e51c38f7006c7f526d3aa334d..414597f6ab23bd4e2826312c5f4e629d115b907c 100644 --- a/pkg/configmanager/manager.go +++ b/pkg/configmanager/manager.go @@ -20,6 +20,7 @@ import ( "errors" "nestos-kubernetes-deployer/cmd/command/opts" "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/asset/infraasset" "nestos-kubernetes-deployer/pkg/configmanager/globalconfig" "os" "path/filepath" @@ -77,18 +78,19 @@ func Initial(opts *opts.OptionsList) error { func initializeClusterAsset(fileData *asset.ClusterAsset, opts *opts.OptionsList) error { // Init infra asset - infraAsset, err := asset.InitInfraAsset(fileData, opts) + infraAsset, err := infraasset.InitInfraAsset(fileData, opts) if err != nil { return err } // Init cluster asset - clusterAsset, err := fileData.InitClusterAsset(infraAsset, opts) + clusterAsset, err := fileData.InitClusterAsset(opts) if err != nil { return err } + clusterAsset.InfraPlatform = infraAsset - ClusterAsset[fileData.Cluster_ID] = clusterAsset + ClusterAsset[fileData.ClusterID] = clusterAsset return nil } @@ -108,6 +110,10 @@ func GetBootstrapIgnHost() string { return GlobalConfig.BootstrapIgnHost } +func GetBootstrapIgnHostPort() string { + return GetBootstrapIgnHost() + ":" + GetBootstrapIgnPort() +} + func GetClusterConfig(clusterID string) (*asset.ClusterAsset, error) { clusterConfig, ok := ClusterAsset[clusterID] if !ok { @@ -123,7 +129,7 @@ func Persist() error { // Persist cluster for _, clusterAsset := range ClusterAsset { - clusterDir := filepath.Join(persistDir, clusterAsset.Cluster_ID) + clusterDir := filepath.Join(persistDir, clusterAsset.ClusterID) if err := os.MkdirAll(clusterDir, 0644); err != nil { return err } diff --git a/pkg/configmanager/asset/node_os_asset.go b/pkg/configmanager/runtime/contained.go similarity index 59% rename from pkg/configmanager/asset/node_os_asset.go rename to pkg/configmanager/runtime/contained.go index d5a672f39c18b3109f9002ae7d8968eb747c7e5d..268a0481bc4655448e4bbf335c92731d3c466f92 100644 --- a/pkg/configmanager/asset/node_os_asset.go +++ b/pkg/configmanager/runtime/contained.go @@ -13,25 +13,20 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - -package asset +package runtime import ( - "fmt" - "strings" + "nestos-kubernetes-deployer/pkg/api" ) -var ( - mapRuntime = map[string]string{ - "isulad": "/var/run/isulad.sock", - "docker": "/var/run/dockershim.sock", - "crio": "unix:///var/run/crio/crio.sock", - } -) +type containerdRuntime struct { +} + +func (ir *containerdRuntime) GetRuntimeCriSocket() string { + return "unix:///var/run/containerd/containerd.sock" +} -func GetRuntimeCriSocket(runtime string) (string, error) { - if content, ok := mapRuntime[strings.ToLower(runtime)]; ok { - return content, nil - } - return "", fmt.Errorf("runtime %s not found", runtime) +func IsContainerd(rt api.Runtime) bool { + _, ok := rt.(*containerdRuntime) + return ok } diff --git a/pkg/configmanager/runtime/crio.go b/pkg/configmanager/runtime/crio.go new file mode 100644 index 0000000000000000000000000000000000000000..0a8c79eeb3957bbd909c29d143a6b0e3958472e9 --- /dev/null +++ b/pkg/configmanager/runtime/crio.go @@ -0,0 +1,29 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package runtime + +import "nestos-kubernetes-deployer/pkg/api" + +type crioRuntime struct{} + +func (cr *crioRuntime) GetRuntimeCriSocket() string { + return "unix:///var/run/crio/crio.sock" +} + +func IsCrio(rt api.Runtime) bool { + _, ok := rt.(*crioRuntime) + return ok +} diff --git a/pkg/configmanager/runtime/docker.go b/pkg/configmanager/runtime/docker.go new file mode 100644 index 0000000000000000000000000000000000000000..f6b26d13e07d577997aae7d0788c92348d9c650e --- /dev/null +++ b/pkg/configmanager/runtime/docker.go @@ -0,0 +1,29 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package runtime + +import "nestos-kubernetes-deployer/pkg/api" + +type dockerRuntime struct{} + +func (dr *dockerRuntime) GetRuntimeCriSocket() string { + return "/var/run/dockershim.sock" +} + +func IsDocker(rt api.Runtime) bool { + _, ok := rt.(*dockerRuntime) + return ok +} diff --git a/pkg/configmanager/runtime/isulad.go b/pkg/configmanager/runtime/isulad.go new file mode 100644 index 0000000000000000000000000000000000000000..f83a2cf77331f8e6caf03f7ab235519c9c43f893 --- /dev/null +++ b/pkg/configmanager/runtime/isulad.go @@ -0,0 +1,32 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package runtime + +import ( + "nestos-kubernetes-deployer/pkg/api" +) + +type isuladRuntime struct { +} + +func (ir *isuladRuntime) GetRuntimeCriSocket() string { + return "/var/run/isulad.sock" +} + +func IsIsulad(rt api.Runtime) bool { + _, ok := rt.(*isuladRuntime) + return ok +} diff --git a/pkg/configmanager/runtime/runtime.go b/pkg/configmanager/runtime/runtime.go new file mode 100644 index 0000000000000000000000000000000000000000..869b2fe5a011ea845d0135f65cbf53a88116a654 --- /dev/null +++ b/pkg/configmanager/runtime/runtime.go @@ -0,0 +1,48 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package runtime + +import ( + "nestos-kubernetes-deployer/pkg/api" + "nestos-kubernetes-deployer/pkg/constants" + "strings" + + "github.com/pkg/errors" +) + +var ( + mapRuntime = map[string]api.Runtime{ + constants.Isulad: &isuladRuntime{}, + constants.Docker: &dockerRuntime{}, + constants.Crio: &crioRuntime{}, + constants.Containerd: &containerdRuntime{}, + } +) + +func GetRuntime(runtime string) (api.Runtime, error) { + runtime = strings.ToLower(runtime) + if runtime == "" { + return mapRuntime[constants.Isulad], nil + } + + rt, ok := mapRuntime[runtime] + if !ok { + return nil, errors.New("unsupported runtime") + } + + return rt, nil +} diff --git a/pkg/configmanager/runtime/runtime_test.go b/pkg/configmanager/runtime/runtime_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2a9da81dd622ea4bd8e8edc3d14aaf3602dd9777 --- /dev/null +++ b/pkg/configmanager/runtime/runtime_test.go @@ -0,0 +1,47 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package runtime_test + +import ( + "nestos-kubernetes-deployer/pkg/configmanager/runtime" + "nestos-kubernetes-deployer/pkg/constants" + "testing" +) + +func TestGetRuntime(t *testing.T) { + for _, runtimeName := range []string{constants.Isulad, constants.Docker, constants.Crio} { + rt, err := runtime.GetRuntime(runtimeName) + if err != nil { + t.Errorf("Unexpected error for supported runtime %s: %v", runtimeName, err) + } + if rt == nil { + t.Errorf("Unexpected nil runtime for supported runtime %s", runtimeName) + } + } + + unknownRuntime := "unknown" + rt, err := runtime.GetRuntime(unknownRuntime) + if err == nil { + t.Errorf("Expected error for unsupported runtime %s", unknownRuntime) + } + if rt != nil { + t.Errorf("Expected nil runtime for unsupported runtime %s", unknownRuntime) + } + expectedErrorMessage := "unsupported runtime" + if err != nil && err.Error() != expectedErrorMessage { + t.Errorf("Expected error message '%s' for unsupported runtime, got '%s'", expectedErrorMessage, err.Error()) + } +} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..77894a677ad1bbb4618f625566f3d1be6f6e0ec9 --- /dev/null +++ b/pkg/constants/constants.go @@ -0,0 +1,71 @@ +/* +Copyright 2023 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package constants + +import "os" + +const ( + // 节点类型标识符 + Controlplane = "controlplane" + Master = "master" + Worker = "worker" + + // 容器运行类型标识符 + Isulad = "isulad" + Docker = "docker" + Crio = "crio" + Containerd = "containerd" + + // 操作系统引导文件存储文件夹 + BootConfigSaveDir = "bootconfig" + // 操作系统引导阶段可能启用的服务 + InitClusterService = "init-cluster.service" + JoinMasterService = "join-master.service" + JoinWorkerService = "join-worker.service" + KubeletService = "kubelet.service" + ReleaseImagePivotService = "release-image-pivot.service" + SetKernelPara = "set-kernel-para.service" + BootConfigSystemdPath = "bootconfig/systemd" + // 操作系统引导阶段可能写入的文件 + InitClusterYaml = "/etc/nkdfiles/init-config.yaml.template" + IsuladConfig = "/etc/isulad/daemon.json.template" + DockerConfig = "/etc/docker/daemon.json.template" + ReleaseImagePivotFile = "/etc/nkdfiles/node-pivot.sh.template" + SetKernelParaConf = "/etc/sysctl.d/kubernetes.conf" + KubeletServiceConf = "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf.template" + Hosts = "/etc/hosts.template" + HookFilesPath = "/etc/nkdfiles/hookfiles/" + BootConfigFilesPath = "bootconfig/files" + // 引导配置文件名称 + ControlplaneIgn = "controlplane.ign" + ControlplaneMergeIgn = "controlplane-merge.ign" + MasterIgn = "master.ign" + MasterMergeIgn = "master-merge.ign" + WorkerIgn = "worker.ign" + WorkerMergeIgn = "worker-merge.ign" + KickstartSuffix = ".cfg" + IPXECfg = "ipxe.cfg" + + CertsFiles = "certs.json" + RpmPackageList = "/packagelist/" + + SetHostname = "set-hostname.service" + + SystemdServiceMode os.FileMode = 0644 + StorageFilesMode os.FileMode = 0755 + SaveFileDirMode os.FileMode = 0750 + BootConfigFileMode os.FileMode = 0644 +) diff --git a/pkg/httpserver/httpserver.go b/pkg/httpserver/httpserver.go new file mode 100644 index 0000000000000000000000000000000000000000..fb62b5b2afd0cba1ccf6656a39c1c4b3b344483b --- /dev/null +++ b/pkg/httpserver/httpserver.go @@ -0,0 +1,196 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package httpserver + +import ( + "context" + "errors" + "fmt" + "nestos-kubernetes-deployer/pkg/constants" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/sirupsen/logrus" +) + +const TimeOut = 30 * 60 + +// HTTPService encapsulates the properties of the HTTP file service +type HTTPService struct { + Port string + DirPath string + PackageDir string + FileCache map[string][]byte + running bool + server *http.Server + mutex sync.RWMutex + HttpLastRequestTime int64 `json:"http_last_request_time"` + Ch chan struct{} +} + +func NewHTTPService(port string) *HTTPService { + return &HTTPService{ + Port: port, + FileCache: make(map[string][]byte), + HttpLastRequestTime: time.Now().Unix(), + Ch: make(chan struct{}, 1), + } +} + +// AddFileToCache add file content to the file cache +func (hs *HTTPService) AddFileToCache(fileName string, content []byte) error { + if len(content) == 0 { + return fmt.Errorf("failed to add file '%s' to cache: content is empty", fileName) + } + hs.mutex.Lock() + defer hs.mutex.Unlock() + + if !strings.HasPrefix(fileName, "/") { + fileName = "/" + fileName + } + hs.FileCache[fileName] = content + + return nil +} + +func (hs *HTTPService) Start() error { + // Check if the http server is already running + if hs.running { + return errors.New("HTTP server is already running") + } + + var dirPath string + if hs.DirPath != "" { + var err error + dirPath, err = filepath.Abs(hs.DirPath) + if err != nil { + return err + } + } + + hs.mutex.Lock() + defer hs.mutex.Unlock() + + smux := http.NewServeMux() + // 处理目录请求 + smux.HandleFunc("/dir/", func(w http.ResponseWriter, r *http.Request) { + hs.HttpLastRequestTime = time.Now().Unix() + rpath := filepath.Join(dirPath, r.URL.Path[len("/dir/"):]) + _, err := os.Stat(rpath) + if err != nil { + // 如果请求对应目录,返回目录下的文件列表 + http.FileServer(http.Dir(rpath)).ServeHTTP(w, r) + return + } + + // 如果请求是文件,返回文件内容 + http.ServeFile(w, r, rpath) + }) + + // 处理rpm软件包请求 + smux.HandleFunc(constants.RpmPackageList, func(w http.ResponseWriter, r *http.Request) { + hs.HttpLastRequestTime = time.Now().Unix() + rpath := filepath.Join(hs.PackageDir, r.URL.Path[len(constants.RpmPackageList):]) + _, err := os.Stat(rpath) + if err != nil { + http.FileServer(http.Dir(rpath)).ServeHTTP(w, r) + return + } + http.ServeFile(w, r, rpath) + }) + + // 处理文件请求 + smux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + hs.HttpLastRequestTime = time.Now().Unix() + rpath := r.URL.Path + fileContent, ok := hs.FileCache[rpath] + if !ok { + http.NotFound(w, r) + return + } + fmt.Fprintf(w, "%s", fileContent) + }) + + hs.server = &http.Server{ + Addr: ":" + hs.Port, + Handler: smux, + } + + logrus.Infof("HTTP server is listening on port %s...\n", hs.Port) + go func() { + for { + val := time.Now().Unix() - hs.HttpLastRequestTime + if val < TimeOut { + time.Sleep(30 * time.Second) + continue + } + hs.server.Close() + hs.running = false + hs.server = nil + hs.Ch <- struct{}{} + return + } + }() + + hs.running = true + + if err := hs.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logrus.Errorf("ListenAndServe(): %v", err) + hs.running = false + hs.Ch <- struct{}{} + return err + } else { + logrus.Println("http server closed") + } + + return nil +} + +func (hs *HTTPService) Stop() error { + if !hs.running { + logrus.Warn("HTTP server is not running.") + return nil + } + + if hs.server == nil { + hs.running = false + logrus.Infof("HTTP server stopped.") + return nil + } + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + if err := hs.server.Shutdown(ctx); err != nil { + logrus.Errorf("Shut down the http server: %v", err) + return err + } + hs.server = nil + return nil +} + +func StartHTTPService(httpService *HTTPService) { + go func() { + if err := httpService.Start(); err != nil { + logrus.Errorf("error starting HTTP service: %v", err) + } + }() +} diff --git a/test/httpserver_test/httpserver_test.go b/pkg/httpserver/httpserver_test.go similarity index 80% rename from test/httpserver_test/httpserver_test.go rename to pkg/httpserver/httpserver_test.go index 8c4b5d7a866d5c3f2f7d2b4af3815a96dd589d9b..c63534a8a29f8f877909b257ba172c3797021ef2 100644 --- a/test/httpserver_test/httpserver_test.go +++ b/pkg/httpserver/httpserver_test.go @@ -23,22 +23,24 @@ import ( "testing" ) -func TestHttpFileService(t *testing.T) { - // Create a new file service instance - fileService := httpserver.NewFileService("9080") +func TestHTTPService(t *testing.T) { + // Create a new http service instance + httpService := &httpserver.HTTPService{ + Port: "9080", + } // Start the file service - if err := fileService.Start(); err != nil { + if err := httpService.Start(); err != nil { t.Fatalf("Error starting file service: %v", err) } - defer fileService.Stop() + defer httpService.Stop() // Add test file to the file service testContent := []byte("Hello, world!") - fileService.AddFileToCache("test.txt", testContent) + httpService.AddFileToCache("test.txt", testContent) // Make an HTTP request to retrieve the test file content - resp, err := http.Get("http://localhost:9080/test.txt") + resp, err := http.Get("http://localhost:9080/file/test.txt") if err != nil { t.Fatalf("Error making GET request: %v", err) } diff --git a/pkg/httpserver/server.go b/pkg/httpserver/server.go deleted file mode 100644 index 094acc37e34e2ab137f589a222b9ceebb1fb27f4..0000000000000000000000000000000000000000 --- a/pkg/httpserver/server.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// service.go -package httpserver - -import ( - "errors" - "net/http" - "os" - "sync" - - "github.com/sirupsen/logrus" -) - -// HttpFileService encapsulates the properties of the HTTP file service -type HttpFileService struct { - Port string - server *http.Server - running bool - fileCache map[string][]byte - mutex sync.RWMutex -} - -// NewFileService creates a new instance of file service -func NewFileService(port string) *HttpFileService { - return &HttpFileService{ - Port: port, - running: false, - fileCache: make(map[string][]byte), - } -} - -// AddFileToCache add file content to the file cache -func (fs *HttpFileService) AddFileToCache(fileName string, content []byte) { - fs.mutex.Lock() - defer fs.mutex.Unlock() - fileName = "/" + fileName - fs.fileCache[fileName] = content -} - -// RemoveFileFromCache removes file content from the file cache -func (fs *HttpFileService) RemoveFileFromCache(fileName string) { - fs.mutex.Lock() - defer fs.mutex.Unlock() - - delete(fs.fileCache, fileName) -} - -func (fs *HttpFileService) Start() error { - // Set up HTTP route - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - err := fs.handleFileRequest(w, r) - if err != nil { - logrus.Errorf("Error handling file request: %v", err) - - var statusCode int - var errorMessage string - - if os.IsNotExist(err) { - statusCode = http.StatusNotFound - errorMessage = "File Not Found" - } else { - statusCode = http.StatusInternalServerError - errorMessage = "Internal Server Error" - } - - http.Error(w, errorMessage, statusCode) - return - } - }) - - fs.server = &http.Server{ - Addr: ":" + fs.Port, - } - - go func() { - logrus.Infof("HTTP server listening on port %s...\n", fs.Port) - fs.running = true - if fs.server != nil { - if err := fs.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - logrus.Errorf("ListenAndServe(): %v", err) - fs.running = false - return - } - } else { - logrus.Error("Server is nil. Cannot start.") - } - }() - - return nil -} - -// handleFileRequest handles file requests -func (fs *HttpFileService) handleFileRequest(w http.ResponseWriter, r *http.Request) error { - // Get the requested file path - filePath := r.URL.Path - fs.mutex.RLock() - defer fs.mutex.RUnlock() - - // Check if the file exists in the cache - fileContent, ok := fs.fileCache[filePath] - if !ok || len(fileContent) == 0 { - return os.ErrNotExist - } - - // Set the content type of the file - contentType := http.DetectContentType(fileContent) - w.Header().Set("Content-Type", contentType) - - // Write file content directly into the response - _, err := w.Write(fileContent) - if err != nil { - errMsg := "unable to write file to response: " + err.Error() - return errors.New(errMsg) - } - - return nil -} - -// Stop method stops the file service -func (fs *HttpFileService) Stop() error { - if !fs.running || fs.server == nil { - logrus.Warn("Server is not running.") - return nil - } - - fs.mutex.Lock() - defer fs.mutex.Unlock() - - logrus.Info("Stopping http server...") - if err := fs.server.Close(); err != nil { - logrus.Errorf("Error closing server: %v", err) - return errors.New("error closing server: " + err.Error()) - } - - // Clear the file cache - for fileName := range fs.fileCache { - delete(fs.fileCache, fileName) - } - - fs.running = false - return nil -} diff --git a/pkg/ignition/machine/master.go b/pkg/ignition/machine/master.go deleted file mode 100644 index d33349f462b342f6ccf7984739bffd972017d9a1..0000000000000000000000000000000000000000 --- a/pkg/ignition/machine/master.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package machine - -import ( - "nestos-kubernetes-deployer/pkg/configmanager" - "nestos-kubernetes-deployer/pkg/configmanager/asset" - "nestos-kubernetes-deployer/pkg/ignition" - "nestos-kubernetes-deployer/pkg/utils" - "os" - "path/filepath" - - igntypes "github.com/coreos/ignition/v2/config/v3_2/types" - "github.com/sirupsen/logrus" -) - -const ( - MasterIgnFilename = "master.ign" - ControlplaneIgnFilename = "controlplane.ign" - masterMergeIgnFilename = "master-merge.ign" - controlplaneMergeIgnFilename = "controlplane-merge.ign" -) - -type Master struct { - ClusterAsset *asset.ClusterAsset - Bootstrap_baseurl string -} - -func (m *Master) GenerateFiles() error { - sshkeyContent, err := os.ReadFile(m.ClusterAsset.SSHKey) - if err != nil { - logrus.Debug("Failed to read sshkey content:", err) - return err - } - - // Get template dependency configuration - masterTemplateData, err := ignition.GetTmplData(m.ClusterAsset) - if err != nil { - return err - } - ignitionDir := filepath.Join(configmanager.GetPersistDir(), m.ClusterAsset.Cluster_ID, "ignition") - - for i, master := range m.ClusterAsset.Master { - nodeType := getNodeTypeName(i) - masterTemplateData.NodeName = master.Hostname - - generateFile := ignition.Common{ - UserName: m.ClusterAsset.UserName, - SSHKey: string(sshkeyContent), - PassWord: m.ClusterAsset.Password, - NodeType: nodeType, - TmplData: masterTemplateData, - EnabledServices: ignition.EnabledServices, - Config: &igntypes.Config{}, - } - - // Generate Ignition data - if err := generateFile.Generate(); err != nil { - logrus.Errorf("failed to generate %s ignition file: %v", master.Hostname, err) - return err - } - - filename := MasterIgnFilename - mergeFilename := masterMergeIgnFilename - if i == 0 { - filename = ControlplaneIgnFilename - mergeFilename = controlplaneMergeIgnFilename - mergeCertificatesIntoConfig(generateFile.Config, master.Certs) - } - - m.ClusterAsset.Master[i].Ignitions.CreateIgnPath = filepath.Join(ignitionDir, filename) - m.ClusterAsset.Master[i].Ignitions.MergeIgnPath = filepath.Join(ignitionDir, mergeFilename) - - if err := ignition.SaveFile(generateFile.Config, ignitionDir, filename); err != nil { - return err - } - - mergerConfig := ignition.GenerateMergeIgnition(m.Bootstrap_baseurl, filename) - if err := ignition.SaveFile(mergerConfig, ignitionDir, mergeFilename); err != nil { - return err - } - - data, err := ignition.Marshal(generateFile.Config) - if err != nil { - logrus.WithError(err).Error("Failed to Marshal ignition config") - return err - } - m.ClusterAsset.Master[i].CreateIgnContent = data - } - - return nil -} - -func getNodeTypeName(index int) string { - if index == 0 { - return "controlplane" - } - return "master" -} - -// Merge certificates into ignition.Config -func mergeCertificatesIntoConfig(config *igntypes.Config, certs []utils.StorageContent) { - for _, file := range certs { - ignFile := ignition.FileWithContents(file.Path, file.Mode, file.Content) - config.Storage.Files = ignition.AppendFiles(config.Storage.Files, ignFile) - } -} diff --git a/pkg/ignition/machine/worker.go b/pkg/ignition/machine/worker.go deleted file mode 100644 index 7f08ac44f926b6405dd377c6c0f715e69dcf64e3..0000000000000000000000000000000000000000 --- a/pkg/ignition/machine/worker.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package machine - -import ( - "nestos-kubernetes-deployer/pkg/configmanager" - "nestos-kubernetes-deployer/pkg/configmanager/asset" - "nestos-kubernetes-deployer/pkg/ignition" - "os" - "path/filepath" - - igntypes "github.com/coreos/ignition/v2/config/v3_2/types" - "github.com/sirupsen/logrus" -) - -const ( - WorkerIgnFilename = "worker.ign" - workerMergeIgnFilename = "worker-merge.ign" -) - -type Worker struct { - ClusterAsset *asset.ClusterAsset - Bootstrap_baseurl string -} - -func (w *Worker) GenerateFiles() error { - sshkeyContent, err := os.ReadFile(w.ClusterAsset.SSHKey) - if err != nil { - logrus.Debug("Failed to read sshkey content:", err) - return err - } - - workerTemplateData, err := ignition.GetTmplData(w.ClusterAsset) - if err != nil { - return err - } - generateFile := ignition.Common{ - UserName: w.ClusterAsset.UserName, - SSHKey: string(sshkeyContent), - PassWord: w.ClusterAsset.Password, - NodeType: "worker", - TmplData: workerTemplateData, - EnabledServices: ignition.EnabledServices, - Config: &igntypes.Config{}, - } - - // Generate Ignition data - if err := generateFile.Generate(); err != nil { - logrus.Errorf("failed to generate %s ignition file: %v", w.ClusterAsset.Worker[0].Hostname, err) - return err - } - - ignitionDir := filepath.Join(configmanager.GetPersistDir(), w.ClusterAsset.Cluster_ID, "ignition") - - if err := ignition.SaveFile(generateFile.Config, ignitionDir, WorkerIgnFilename); err != nil { - return err - } - - mergerConfig := ignition.GenerateMergeIgnition(w.Bootstrap_baseurl, WorkerIgnFilename) - if err := ignition.SaveFile(mergerConfig, ignitionDir, workerMergeIgnFilename); err != nil { - return err - } - - data, err := ignition.Marshal(generateFile.Config) - if err != nil { - logrus.Errorf("failed to Marshal ignition config: %v", err) - return err - } - - for i, _ := range w.ClusterAsset.Worker { - w.ClusterAsset.Worker[i].Ignitions.CreateIgnPath = filepath.Join(ignitionDir, WorkerIgnFilename) - w.ClusterAsset.Worker[i].Ignitions.MergeIgnPath = filepath.Join(ignitionDir, workerMergeIgnFilename) - w.ClusterAsset.Worker[i].CreateIgnContent = data - } - - return nil -} diff --git a/pkg/ignition/node.go b/pkg/ignition/node.go deleted file mode 100644 index ff7cf38cd4a54c92b2c5330d5e9af36f5a435d1e..0000000000000000000000000000000000000000 --- a/pkg/ignition/node.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package ignition - -import ( - "os" - "path/filepath" - - "github.com/clarketm/json" - "github.com/sirupsen/logrus" - - ignutil "github.com/coreos/ignition/v2/config/util" - igntypes "github.com/coreos/ignition/v2/config/v3_2/types" - "github.com/vincent-petithory/dataurl" -) - -func Marshal(input interface{}) ([]byte, error) { - return json.Marshal(input) -} - -/* -FileWithContents creates an ignition file with the given contents. -Parameters: - - path (string): The file path. - - mode (int): The file permissions. - - contents ([]byte): The file content as a byte slice. - -Returns: - - igntypes.File: Ignition file configuration. -*/ -func FileWithContents(path string, mode int, contents []byte) igntypes.File { - return igntypes.File{ - Node: igntypes.Node{ - Path: path, - Overwrite: ignutil.BoolToPtr(true), - }, - FileEmbedded1: igntypes.FileEmbedded1{ - Mode: &mode, - Contents: igntypes.Resource{ - Source: ignutil.StrToPtr(dataurl.EncodeBytes(contents)), - }, - }, - } -} - -func AppendFiles(files []igntypes.File, file igntypes.File) []igntypes.File { - for i, f := range files { - if f.Node.Path == file.Node.Path { - files[i] = file - return files - } - } - files = append(files, file) - return files -} - -/* -Save the ignition config -Parameters: -config - the ignition config to be saved -filePath - the path to save the file -fileName - the name to save the file -*/ -func SaveFile(config *igntypes.Config, filePath string, fileName string) error { - data, err := Marshal(config) - if err != nil { - logrus.Errorf("failed to Marshal ignition config: %v", err) - return err - } - fullPath := filepath.Join(filePath, fileName) - if err := os.MkdirAll(filepath.Dir(fullPath), 0750); err != nil { - logrus.Errorf("failed to Mkdir: %v", err) - return err - } - if err := os.WriteFile(fullPath, data, 0644); err != nil { - logrus.Errorf("failed to save ignition file: %v", err) - return err - } - return nil -} diff --git a/pkg/infra/infra.go b/pkg/infra/infra.go index 81338bce9e49ecb591505144af374cc5cb3e4658..8879605885f6f2f04932e3c64f44e0b30969a407 100644 --- a/pkg/infra/infra.go +++ b/pkg/infra/infra.go @@ -1,5 +1,5 @@ /* -Copyright 2023 KylinSoft Co., Ltd. +Copyright 2024 KylinSoft Co., Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,59 +16,28 @@ limitations under the License. package infra -import ( - "nestos-kubernetes-deployer/pkg/infra/terraform" - "path/filepath" - - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -type Cluster struct { - PersistDir string - ClusterID string - Node string - Count uint +type Infrastructure interface { + Deploy() error + Extend() error + Destroy() error } -func (c *Cluster) Deploy() (err error) { - tfFileDir := filepath.Join(c.PersistDir, c.ClusterID, c.Node) - outputs, err := terraform.ExecuteApplyTerraform(tfFileDir, c.PersistDir) - if err != nil { - return errors.Wrap(err, "failed to execute terraform apply") - } - logrus.Println(string(outputs)) - - return nil +type InfraPlatform struct { + infra Infrastructure } -func (c *Cluster) Extend() (err error) { - tfFileDir := filepath.Join(c.PersistDir, c.ClusterID, c.Node) - outputs, err := terraform.ExecuteApplyTerraform(tfFileDir, c.PersistDir) - if err != nil { - return errors.Wrap(err, "failed to execute terraform apply") - } - logrus.Println(string(outputs)) - - return nil +func (p *InfraPlatform) SetInfra(infra Infrastructure) { + p.infra = infra } -func (c *Cluster) Destroy() (err error) { - // tf file directory. - tfFileDir := filepath.Join(c.PersistDir, c.ClusterID, c.Node) - err = terraform.ExecuteDestroyTerraform(tfFileDir, c.PersistDir) - if err != nil { - return errors.Wrap(err, "failed to execute terraform destroy") - } +func (p *InfraPlatform) Deploy() error { + return p.infra.Deploy() +} - return nil +func (p *InfraPlatform) Extend() error { + return p.infra.Extend() } -func InstanceCluster(persistDir string, clusterID string, nodeType string, count uint) *Cluster { - return &Cluster{ - PersistDir: persistDir, - ClusterID: clusterID, - Node: nodeType, - Count: count, - } +func (p *InfraPlatform) Destroy() error { + return p.infra.Destroy() } diff --git a/pkg/infra/libvirt.go b/pkg/infra/libvirt.go new file mode 100644 index 0000000000000000000000000000000000000000..e6122e167066109a6e30b4caf86029721ff928ca --- /dev/null +++ b/pkg/infra/libvirt.go @@ -0,0 +1,64 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infra + +import ( + "nestos-kubernetes-deployer/pkg/terraform" + "path/filepath" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type Libvirt struct { + PersistDir string + ClusterID string + Node string + Count uint +} + +func (l *Libvirt) Deploy() error { + tfFileDir := filepath.Join(l.PersistDir, l.ClusterID, l.Node) + outputs, err := terraform.ExecuteApplyTerraform(tfFileDir, l.PersistDir) + if err != nil { + return errors.Wrap(err, "failed to execute terraform apply") + } + logrus.Println(string(outputs)) + + return nil +} + +func (l *Libvirt) Extend() error { + tfFileDir := filepath.Join(l.PersistDir, l.ClusterID, l.Node) + outputs, err := terraform.ExecuteApplyTerraform(tfFileDir, l.PersistDir) + if err != nil { + return errors.Wrap(err, "failed to execute terraform apply") + } + logrus.Println(string(outputs)) + + return nil +} + +func (l *Libvirt) Destroy() error { + tfFileDir := filepath.Join(l.PersistDir, l.ClusterID, l.Node) + err := terraform.ExecuteDestroyTerraform(tfFileDir, l.PersistDir) + if err != nil { + return errors.Wrap(err, "failed to execute terraform destroy") + } + + return nil +} diff --git a/pkg/infra/openstack.go b/pkg/infra/openstack.go new file mode 100644 index 0000000000000000000000000000000000000000..f1cc56ec326c150831289962eb89c2bee45e8515 --- /dev/null +++ b/pkg/infra/openstack.go @@ -0,0 +1,64 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package infra + +import ( + "nestos-kubernetes-deployer/pkg/terraform" + "path/filepath" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type OpenStack struct { + PersistDir string + ClusterID string + Node string + Count uint +} + +func (o *OpenStack) Deploy() error { + tfFileDir := filepath.Join(o.PersistDir, o.ClusterID, o.Node) + outputs, err := terraform.ExecuteApplyTerraform(tfFileDir, o.PersistDir) + if err != nil { + return errors.Wrap(err, "failed to execute terraform apply") + } + logrus.Println(string(outputs)) + + return nil +} + +func (o *OpenStack) Extend() error { + tfFileDir := filepath.Join(o.PersistDir, o.ClusterID, o.Node) + outputs, err := terraform.ExecuteApplyTerraform(tfFileDir, o.PersistDir) + if err != nil { + return errors.Wrap(err, "failed to execute terraform apply") + } + logrus.Println(string(outputs)) + + return nil +} + +func (o *OpenStack) Destroy() error { + tfFileDir := filepath.Join(o.PersistDir, o.ClusterID, o.Node) + err := terraform.ExecuteDestroyTerraform(tfFileDir, o.PersistDir) + if err != nil { + return errors.Wrap(err, "failed to execute terraform destroy") + } + + return nil +} diff --git a/pkg/osmanager/bootconfig/cloudinit/cloudinit.go b/pkg/osmanager/bootconfig/cloudinit/cloudinit.go new file mode 100644 index 0000000000000000000000000000000000000000..0004149f4b20ba75c10d58efaab37e3b07a27caf --- /dev/null +++ b/pkg/osmanager/bootconfig/cloudinit/cloudinit.go @@ -0,0 +1,106 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudinit + +import ( + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/constants" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig" + "path/filepath" +) + +const ( + cloudinitControlplane = "controlplane.cfg" + cloudinitMaster = "master.cfg" + cloudinitWorker = "worker.cfg" +) + +type Cloudinit struct { + ClusterAsset *asset.ClusterAsset + BootstrapBaseurl string +} + +func NewCloudinit(clusterAsset *asset.ClusterAsset, bootstrapBaseurl string) *Cloudinit { + return &Cloudinit{ + ClusterAsset: clusterAsset, + BootstrapBaseurl: bootstrapBaseurl, + } +} + +var ( + enabledFiles = []string{ + constants.ReleaseImagePivotFile, + constants.SetKernelParaConf, + constants.Hosts, + } + + enabledServices = []string{ + constants.ReleaseImagePivotService, + constants.SetKernelPara, + } +) + +func (c *Cloudinit) GenerateBootConfig() error { + if err := c.generateNodeConfig(constants.Controlplane, constants.InitClusterService, constants.InitClusterYaml, cloudinitControlplane); err != nil { + return err + } + + if len(c.ClusterAsset.Master) > 1 { + if err := c.generateNodeConfig(constants.Master, constants.JoinMasterService, "", cloudinitMaster); err != nil { + return err + } + } + + if len(c.ClusterAsset.Worker) > 0 { + if err := c.generateNodeConfig(constants.Worker, constants.JoinWorkerService, "", cloudinitWorker); err != nil { + return err + } + } + + return nil +} + +func (c *Cloudinit) generateNodeConfig(nodeType, service string, yamlPath string, filename string) error { + tmpl := newTemplate(c.ClusterAsset, &CloudinitConfig{}, append(enabledServices, service), enabledFiles) + if yamlPath != "" { + tmpl.enabledFiles = append(tmpl.enabledFiles, yamlPath) + } + if err := tmpl.GenerateBootConfig(c.BootstrapBaseurl, nodeType); err != nil { + return err + } + savePath := bootconfig.GetSavePath(c.ClusterAsset.ClusterID) + if err := bootconfig.SaveYAML(tmpl.config, savePath, filename, "#cloud-config\n"); err != nil { + return err + } + + switch nodeType { + case constants.Controlplane: + c.ClusterAsset.BootConfig.Controlplane = asset.BootFile{ + Path: filepath.Join(savePath, filename), + } + case constants.Master: + c.ClusterAsset.BootConfig.Master = asset.BootFile{ + Path: filepath.Join(savePath, filename), + } + case constants.Worker: + c.ClusterAsset.BootConfig.Worker = asset.BootFile{ + Path: filepath.Join(savePath, filename), + } + } + + return nil +} diff --git a/pkg/osmanager/bootconfig/cloudinit/template.go b/pkg/osmanager/bootconfig/cloudinit/template.go new file mode 100644 index 0000000000000000000000000000000000000000..768fabcefe8d6c9ea8be73ae43792fd528a78799 --- /dev/null +++ b/pkg/osmanager/bootconfig/cloudinit/template.go @@ -0,0 +1,145 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cloudinit + +import ( + "encoding/base64" + "fmt" + "io/fs" + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/runtime" + "nestos-kubernetes-deployer/pkg/constants" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig" + "nestos-kubernetes-deployer/pkg/utils" + "os" + + "github.com/sirupsen/logrus" +) + +type template struct { + clusterAsset *asset.ClusterAsset + config *CloudinitConfig + enabledServices []string + enabledFiles []string +} + +func newTemplate(clusterAsset *asset.ClusterAsset, config *CloudinitConfig, enabledServices, enabledFiles []string) *template { + return &template{ + clusterAsset: clusterAsset, + config: config, + enabledServices: enabledServices, + enabledFiles: enabledFiles, + } +} + +func (t *template) GenerateBootConfig(url string, nodeType string) error { + var ( + bootConfigFiles []bootconfig.File + bootConfigSystemd bootconfig.Systemd + ) + sshkeyContent, err := os.ReadFile(t.clusterAsset.SSHKey) + if err != nil { + logrus.Debug("Failed to read sshkey content:", err) + return err + } + tmplData, err := bootconfig.GetTmplData(t.clusterAsset) + if err != nil { + return err + } + if nodeType == constants.Controlplane { + tmplData.IsControlPlane = true + tmplData.CertsUrl = utils.ConstructURL(url, constants.CertsFiles) + } + + //set container engine config + engine, err := runtime.GetRuntime(t.clusterAsset.Runtime) + if err != nil { + return err + } + tmplData.CriSocket = engine.GetRuntimeCriSocket() + if runtime.IsIsulad(engine) { + t.enabledFiles = append(t.enabledFiles, constants.IsuladConfig) + } else if runtime.IsDocker(engine) { + t.enabledFiles = append(t.enabledFiles, constants.DockerConfig) + } else if runtime.IsCrio(engine) { + t.enabledFiles = append(t.enabledFiles, constants.KubeletServiceConf) + } + + if err := bootconfig.AppendStorageFiles(&bootConfigFiles, "/", constants.BootConfigFilesPath, tmplData, t.enabledFiles); err != nil { + logrus.Errorf("failed to add files to an cloudinit config: %v", err) + return err + } + + if err := bootconfig.AppendSystemdUnits(&bootConfigSystemd, constants.BootConfigSystemdPath, tmplData, t.enabledServices); err != nil { + logrus.Errorf("failed to add systemd units to an cloudinit config: %v", err) + return err + } + + t.config = &CloudinitConfig{ + SSHPasswordAuth: true, + SSHAuthorizedKeys: []string{ + string(sshkeyContent), + }, + + Chpasswd: ChpasswdConfig{ + List: t.clusterAsset.UserName + ":" + t.clusterAsset.Password, + Expire: false, + }, + } + + for _, f := range bootConfigFiles { + cf := WriteFile{ + EnCoding: "b64", + Content: base64.StdEncoding.EncodeToString(f.Contents.Source), + Path: f.Path, + Permissions: f.Mode, + } + t.config.WriteFiles = append(t.config.WriteFiles, cf) + } + + if len(t.clusterAsset.HookConf.ShellFiles) > 0 { + for _, sf := range t.clusterAsset.HookConf.ShellFiles { + wf := WriteFile{ + EnCoding: "b64", + Content: base64.StdEncoding.EncodeToString(sf.Content), + Path: constants.HookFilesPath + sf.Name, + Permissions: fs.FileMode(sf.Mode), + } + t.config.WriteFiles = append(t.config.WriteFiles, wf) + } + } + + for _, u := range bootConfigSystemd.Units { + cf := WriteFile{ + Content: u.Contents, + Path: fmt.Sprintf("/etc/systemd/system/%s", u.Name), + Permissions: constants.SystemdServiceMode, + } + t.config.WriteFiles = append(t.config.WriteFiles, cf) + t.config.RunCmds = append(t.config.RunCmds, "systemctl enable "+u.Name) + t.config.RunCmds = append(t.config.RunCmds, "systemctl start "+u.Name) + } + hf := WriteFile{ + Content: bootconfig.CreateSetHostnameUnit(), + Path: fmt.Sprintf("/etc/systemd/system/%s", constants.SetHostname), + Permissions: constants.SystemdServiceMode, + } + t.config.WriteFiles = append(t.config.WriteFiles, hf) + t.config.RunCmds = append(t.config.RunCmds, "systemctl enable "+constants.SetHostname) + t.config.RunCmds = append(t.config.RunCmds, "systemctl start "+constants.SetHostname) + + return nil +} diff --git a/pkg/osmanager/bootconfig/cloudinit/types.go b/pkg/osmanager/bootconfig/cloudinit/types.go new file mode 100644 index 0000000000000000000000000000000000000000..a29fb63feddc91c937b94f49274a7b53aa5ec270 --- /dev/null +++ b/pkg/osmanager/bootconfig/cloudinit/types.go @@ -0,0 +1,38 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cloudinit + +import "os" + +type CloudinitConfig struct { + SSHPasswordAuth bool `yaml:"ssh_pwauth"` + SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys,omitempty"` + Chpasswd ChpasswdConfig `yaml:"chpasswd,omitempty"` + WriteFiles []WriteFile `yaml:"write_files,omitempty"` + RunCmds []interface{} `yaml:"runcmd,omitempty"` +} + +type ChpasswdConfig struct { + List string `yaml:"list,omitempty"` + Expire bool `yaml:"expire"` +} + +type WriteFile struct { + EnCoding string `yaml:"encoding,omitempty"` + Content string `yaml:"content,omitempty"` + Path string `yaml:"path,omitempty"` + Permissions os.FileMode `yaml:"permissions,omitempty"` +} diff --git a/pkg/osmanager/bootconfig/ignition/ignition.go b/pkg/osmanager/bootconfig/ignition/ignition.go new file mode 100644 index 0000000000000000000000000000000000000000..542d8b4c0d02e3c2001c5f2b13cb208d854eca42 --- /dev/null +++ b/pkg/osmanager/bootconfig/ignition/ignition.go @@ -0,0 +1,155 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package ignition + +import ( + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/constants" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig" + "path/filepath" + + ignutil "github.com/coreos/ignition/v2/config/util" + igntypes "github.com/coreos/ignition/v2/config/v3_2/types" + "github.com/sirupsen/logrus" + "github.com/vincent-petithory/dataurl" +) + +var ( + enabledFiles = []string{ + constants.ReleaseImagePivotFile, + constants.SetKernelParaConf, + constants.KubeletServiceConf, + constants.Hosts, + } + + enabledServices = []string{ + constants.KubeletService, + constants.ReleaseImagePivotService, + constants.SetKernelPara, + } +) + +type Ignition struct { + ClusterAsset *asset.ClusterAsset + BootstrapBaseurl string +} + +func NewIgnition(clusterAsset *asset.ClusterAsset, bootstrapBaseurl string) *Ignition { + return &Ignition{ + ClusterAsset: clusterAsset, + BootstrapBaseurl: bootstrapBaseurl, + } +} + +func (ig *Ignition) GenerateBootConfig() error { + if err := ig.generateNodeIgnition(constants.Controlplane, constants.InitClusterService, constants.InitClusterYaml, constants.ControlplaneIgn, constants.ControlplaneMergeIgn); err != nil { + return err + } + + if len(ig.ClusterAsset.Master) > 1 { + if err := ig.generateNodeIgnition(constants.Master, constants.JoinMasterService, "", constants.MasterIgn, constants.MasterMergeIgn); err != nil { + return err + } + } + + if len(ig.ClusterAsset.Worker) > 0 { + if err := ig.generateNodeIgnition(constants.Worker, constants.JoinWorkerService, "", constants.WorkerIgn, constants.WorkerMergeIgn); err != nil { + return err + } + } + + return nil +} + +func (ig *Ignition) generateNodeIgnition(nodeType, service string, yamlPath string, ignFilename string, mergeIgnFilename string) error { + tmpl := newTemplate(ig.ClusterAsset, &igntypes.Config{}, append(enabledServices, service), enabledFiles) + if yamlPath != "" { + tmpl.enabledFiles = append(tmpl.enabledFiles, yamlPath) + } + + if err := tmpl.GenerateBootConfig(); err != nil { + return err + } + + // Merge certificates directly into the configuration + if nodeType == constants.Controlplane { + for _, cert := range ig.ClusterAsset.Master[0].Certs { + ignFile := fileWithContents(cert.Path, cert.Mode, cert.Content) + tmpl.config.Storage.Files = appendFiles(tmpl.config.Storage.Files, ignFile) + } + } + + savePath := bootconfig.GetSavePath(ig.ClusterAsset.ClusterID) + if err := bootconfig.SaveJSON(tmpl.config, savePath, ignFilename); err != nil { + return err + } + mergeIgnFile := generateMergeIgnition(ig.BootstrapBaseurl, ignFilename) + if err := bootconfig.SaveJSON(mergeIgnFile, savePath, mergeIgnFilename); err != nil { + return err + } + + ignData, err := bootconfig.Marshal(tmpl.config) + if err != nil { + logrus.WithError(err).Errorf("failed to Marshal ignition config for %s node", nodeType) + return err + } + + switch nodeType { + case constants.Controlplane: + ig.ClusterAsset.BootConfig.Controlplane = asset.BootFile{ + Content: ignData, + Path: filepath.Join(savePath, mergeIgnFilename), + } + case constants.Master: + ig.ClusterAsset.BootConfig.Master = asset.BootFile{ + Content: ignData, + Path: filepath.Join(savePath, mergeIgnFilename), + } + case constants.Worker: + ig.ClusterAsset.BootConfig.Worker = asset.BootFile{ + Content: ignData, + Path: filepath.Join(savePath, mergeIgnFilename), + } + } + + return nil +} + +func fileWithContents(path string, mode int, contents []byte) igntypes.File { + return igntypes.File{ + Node: igntypes.Node{ + Path: path, + Overwrite: ignutil.BoolToPtr(true), + }, + FileEmbedded1: igntypes.FileEmbedded1{ + Mode: &mode, + Contents: igntypes.Resource{ + Source: ignutil.StrToPtr(dataurl.EncodeBytes(contents)), + }, + }, + } +} + +func appendFiles(files []igntypes.File, file igntypes.File) []igntypes.File { + for i, f := range files { + if f.Node.Path == file.Node.Path { + files[i] = file + return files + } + } + files = append(files, file) + return files +} diff --git a/pkg/ignition/merge-bootstrap.go b/pkg/osmanager/bootconfig/ignition/merge_bootstrap.go similarity index 51% rename from pkg/ignition/merge-bootstrap.go rename to pkg/osmanager/bootconfig/ignition/merge_bootstrap.go index 8ca68ea86653d227decf79bad017a3faca6e932d..60bcf0d805be671f8207d72e437f74759f5179ac 100644 --- a/pkg/ignition/merge-bootstrap.go +++ b/pkg/osmanager/bootconfig/ignition/merge_bootstrap.go @@ -16,45 +16,23 @@ limitations under the License. package ignition import ( - "fmt" - "net/url" + "nestos-kubernetes-deployer/pkg/constants" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig" + "nestos-kubernetes-deployer/pkg/utils" ignutil "github.com/coreos/ignition/v2/config/util" igntypes "github.com/coreos/ignition/v2/config/v3_2/types" ) -// set-hostname.service用于动态设置节点hostname, -// ${hostname}变量会通过基础设施模块(terraform)为变量赋值(参见https://developer.hashicorp.com/terraform/language/functions/templatefile) -// terraform模板文件修改部分:templatefile(var.instance_ign[count.index], { hostname = var.instance_hostname[count.index] }) -func createSetHostnameUnit() string { - unit := `[Unit] -Description=Set hostname -ConditionPathExists=!/var/log/set-hostname.stamp -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/usr/bin/hostnamectl set-hostname ${hostname} -ExecStart=/bin/touch /var/log/set-hostname.stamp -[Install] -WantedBy=multi-user.target` - return unit -} - -func GenerateMergeIgnition(bootstrapIgnitionHost string, role string) *igntypes.Config { - setHostnameUnit := createSetHostnameUnit() +func generateMergeIgnition(bootstrapIgnitionHost string, role string) *igntypes.Config { + setHostnameUnit := bootconfig.CreateSetHostnameUnit() ign := igntypes.Config{ Ignition: igntypes.Ignition{ Version: igntypes.MaxVersion.String(), Config: igntypes.IgnitionConfig{ Merge: []igntypes.Resource{{ - Source: ignutil.StrToPtr(func() *url.URL { - return &url.URL{ - Scheme: "http", - Host: bootstrapIgnitionHost, - Path: fmt.Sprintf("%s", role), - } - }().String()), + Source: ignutil.StrToPtr(utils.ConstructURL(bootstrapIgnitionHost, role)), }}, }, }, @@ -62,7 +40,7 @@ func GenerateMergeIgnition(bootstrapIgnitionHost string, role string) *igntypes. Units: []igntypes.Unit{ { Contents: &setHostnameUnit, - Name: "set-hostname.service", + Name: constants.SetHostname, Enabled: ignutil.BoolToPtr(true), }, }, diff --git a/pkg/osmanager/bootconfig/ignition/template.go b/pkg/osmanager/bootconfig/ignition/template.go new file mode 100644 index 0000000000000000000000000000000000000000..1f8d67f8141a41aeb95a40ad7ebb007529c50568 --- /dev/null +++ b/pkg/osmanager/bootconfig/ignition/template.go @@ -0,0 +1,158 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package ignition + +import ( + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/runtime" + "nestos-kubernetes-deployer/pkg/constants" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig" + "os" + + "github.com/sirupsen/logrus" + "github.com/vincent-petithory/dataurl" + + ignutil "github.com/coreos/ignition/v2/config/util" + igntypes "github.com/coreos/ignition/v2/config/v3_2/types" +) + +type template struct { + clusterAsset *asset.ClusterAsset + config *igntypes.Config + enabledServices []string + enabledFiles []string +} + +func newTemplate(clusterAsset *asset.ClusterAsset, config *igntypes.Config, enabledServices, enabledFiles []string) *template { + return &template{ + clusterAsset: clusterAsset, + config: config, + enabledServices: enabledServices, + enabledFiles: enabledFiles, + } +} + +func (t *template) GenerateBootConfig() error { + var ( + files []bootconfig.File + systemdConfig bootconfig.Systemd + ) + + sshkeyContent, err := os.ReadFile(t.clusterAsset.SSHKey) + if err != nil { + logrus.Debug("Failed to read sshkey content:", err) + return err + } + tmplData, err := bootconfig.GetTmplData(t.clusterAsset) + if err != nil { + return err + } + + //set container engine config + engine, err := runtime.GetRuntime(t.clusterAsset.Runtime) + if err != nil { + return err + } + tmplData.CriSocket = engine.GetRuntimeCriSocket() + if runtime.IsIsulad(engine) { + t.enabledFiles = append(t.enabledFiles, constants.IsuladConfig) + } else if runtime.IsDocker(engine) { + t.enabledFiles = append(t.enabledFiles, constants.DockerConfig) + } + + if err := bootconfig.AppendStorageFiles(&files, "/", constants.BootConfigFilesPath, tmplData, t.enabledFiles); err != nil { + logrus.Errorf("failed to add files to an Ignition config: %v", err) + return err + } + if err := bootconfig.AppendSystemdUnits(&systemdConfig, constants.BootConfigSystemdPath, tmplData, t.enabledServices); err != nil { + logrus.Errorf("failed to add systemd units to an Ignition config: %v", err) + return err + } + + t.config = &igntypes.Config{ + Ignition: igntypes.Ignition{ + Version: igntypes.MaxVersion.String(), + }, + Passwd: igntypes.Passwd{ + Users: []igntypes.PasswdUser{ + { + Name: t.clusterAsset.UserName, + SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{ + igntypes.SSHAuthorizedKey(sshkeyContent), + }, + PasswordHash: &t.clusterAsset.Password, + }, + }, + }, + Storage: igntypes.Storage{ + Links: []igntypes.Link{ + { + Node: igntypes.Node{Path: "/etc/local/time"}, + LinkEmbedded1: igntypes.LinkEmbedded1{ + Target: "/usr/share/zoneinfo/Asia/Shanghai", + }, + }, + }, + }, + } + + for _, f := range files { + str := dataurl.EncodeBytes(f.Contents.Source) + mode := int(f.Mode.Perm()) + ignFile := igntypes.File{ + Node: igntypes.Node{ + Path: f.Path, + Overwrite: f.Overwrite, + }, + FileEmbedded1: igntypes.FileEmbedded1{ + Mode: &mode, + Contents: igntypes.Resource{ + Source: &str, + }, + }, + } + t.config.Storage.Files = append(t.config.Storage.Files, ignFile) + } + + if len(t.clusterAsset.HookConf.ShellFiles) > 0 { + for _, sf := range t.clusterAsset.HookConf.ShellFiles { + ignHookFile := igntypes.File{ + Node: igntypes.Node{ + Path: constants.HookFilesPath, + Overwrite: ignutil.BoolToPtr(true), + }, + FileEmbedded1: igntypes.FileEmbedded1{ + Mode: &sf.Mode, + Contents: igntypes.Resource{ + Source: ignutil.StrToPtr(dataurl.EncodeBytes(sf.Content)), + }, + }, + } + t.config.Storage.Files = append(t.config.Storage.Files, ignHookFile) + } + } + + for _, u := range systemdConfig.Units { + unit := igntypes.Unit{ + Name: u.Name, + Contents: ignutil.StrToPtr(u.Contents), + Enabled: u.Enabled, + } + t.config.Systemd.Units = append(t.config.Systemd.Units, unit) + } + + return nil +} diff --git a/pkg/osmanager/bootconfig/kickstart/kickstart.go b/pkg/osmanager/bootconfig/kickstart/kickstart.go new file mode 100644 index 0000000000000000000000000000000000000000..2098ce742409fcb00c45648204d30127b8f93af5 --- /dev/null +++ b/pkg/osmanager/bootconfig/kickstart/kickstart.go @@ -0,0 +1,96 @@ +package kickstart + +import ( + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/constants" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig" + "os" + "path/filepath" + "strings" +) + +type Kickstart struct { + ClusterAsset *asset.ClusterAsset + BootstrapBaseurl string +} + +func NewKickstart(clusterAsset *asset.ClusterAsset, bootstrapBaseurl string) *Kickstart { + return &Kickstart{ + ClusterAsset: clusterAsset, + BootstrapBaseurl: bootstrapBaseurl, + } +} + +var ( + enabledFiles = []string{ + constants.ReleaseImagePivotFile, + constants.SetKernelParaConf, + constants.Hosts, + } + + enabledServices = []string{ + constants.ReleaseImagePivotService, + constants.SetKernelPara, + } +) + +func (c *Kickstart) GenerateBootConfig() error { + if err := c.generateNodeConfig(constants.Controlplane, constants.InitClusterService, constants.InitClusterYaml, c.ClusterAsset.Master[0].Hostname+constants.KickstartSuffix); err != nil { + return err + } + + n := len(c.ClusterAsset.Master) + if n > 1 { + for i := 1; i < n; i++ { + if err := c.generateNodeConfig(constants.Master, constants.JoinMasterService, "", c.ClusterAsset.Master[i].Hostname+constants.KickstartSuffix); err != nil { + return err + } + } + } + + if err := c.generateNodeConfig(constants.Worker, constants.JoinWorkerService, "", constants.Worker+constants.KickstartSuffix); err != nil { + return err + } + + return nil +} + +func (c *Kickstart) generateNodeConfig(nodeType, service string, yamlPath string, filename string) error { + tmpl := newTemplate(c.ClusterAsset, append(enabledServices, service), enabledFiles) + if yamlPath != "" { + tmpl.enabledFiles = append(tmpl.enabledFiles, yamlPath) + } + if err := tmpl.GenerateBootConfig(c.BootstrapBaseurl, nodeType, strings.TrimSuffix(filename, constants.KickstartSuffix)); err != nil { + return err + } + savePath := bootconfig.GetSavePath(c.ClusterAsset.ClusterID) + if err := bootconfig.SaveFile(tmpl.config, savePath, filename); err != nil { + return err + } + + ksPath := filepath.Join(savePath, filename) + ksContent, err := os.ReadFile(ksPath) + if err != nil { + return err + } + + switch nodeType { + case constants.Controlplane: + c.ClusterAsset.BootConfig.Controlplane = asset.BootFile{ + Content: ksContent, + Path: ksPath, + } + case constants.Master: + c.ClusterAsset.BootConfig.KickstartMaster = append(c.ClusterAsset.BootConfig.KickstartMaster, asset.BootFile{ + Content: ksContent, + Path: ksPath, + }) + case constants.Worker: + c.ClusterAsset.BootConfig.Worker = asset.BootFile{ + Content: ksContent, + Path: ksPath, + } + } + + return nil +} diff --git a/pkg/osmanager/bootconfig/kickstart/template.go b/pkg/osmanager/bootconfig/kickstart/template.go new file mode 100644 index 0000000000000000000000000000000000000000..9d3fc0ff40855fc17623f385ab80019e9de27671 --- /dev/null +++ b/pkg/osmanager/bootconfig/kickstart/template.go @@ -0,0 +1,128 @@ +package kickstart + +import ( + "fmt" + "nestos-kubernetes-deployer/data" + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/runtime" + "nestos-kubernetes-deployer/pkg/constants" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig" + "nestos-kubernetes-deployer/pkg/utils" + + "github.com/sirupsen/logrus" +) + +type KsTempData struct { + Hostname string + Password string + Files []File + Systemds []string + IsDocker bool + IsIsulad bool +} + +type File struct { + ChangeMod string + Content string +} + +type template struct { + clusterAsset *asset.ClusterAsset + config []byte + enabledServices []string + enabledFiles []string +} + +func newTemplate(clusterAsset *asset.ClusterAsset, enabledServices, enabledFiles []string) *template { + return &template{ + clusterAsset: clusterAsset, + enabledServices: enabledServices, + enabledFiles: enabledFiles, + } +} + +func (t *template) GenerateBootConfig(url string, nodeType string, hostname string) error { + files := []bootconfig.File{} + systemds := bootconfig.Systemd{} + ksData := KsTempData{ + Password: t.clusterAsset.Password, + Hostname: hostname, + } + + tmplData, err := bootconfig.GetTmplData(t.clusterAsset) + if err != nil { + return fmt.Errorf("failed to get template data: %v", err) + } + + if nodeType == constants.Controlplane { + tmplData.IsControlPlane = true + tmplData.CertsUrl = utils.ConstructURL(url, constants.CertsFiles) + } + + engine, err := runtime.GetRuntime(t.clusterAsset.Runtime) + if err != nil { + return fmt.Errorf("failed to get runtime: %v", err) + } + + tmplData.CriSocket = engine.GetRuntimeCriSocket() + if runtime.IsIsulad(engine) { + t.enabledFiles = append(t.enabledFiles, constants.IsuladConfig) + ksData.IsIsulad = true + } else if runtime.IsDocker(engine) { + t.enabledFiles = append(t.enabledFiles, constants.DockerConfig) + ksData.IsDocker = true + } else if runtime.IsCrio(engine) { + t.enabledFiles = append(t.enabledFiles, constants.KubeletServiceConf) + } + + if err := bootconfig.AppendStorageFiles(&files, "/", constants.BootConfigFilesPath, tmplData, t.enabledFiles); err != nil { + logrus.Errorf("failed to add files to a kickstart config: %v", err) + return err + } + + if err := bootconfig.AppendSystemdUnits(&systemds, constants.BootConfigSystemdPath, tmplData, t.enabledServices); err != nil { + logrus.Errorf("failed to add systemd units to a kickstart config: %v", err) + return err + } + + for _, f := range files { + ksData.Files = append(ksData.Files, File{ + Content: fmt.Sprintf("cat <<'EOF'> %s\n%s\nEOF", f.Path, string(f.Contents.Source)), + ChangeMod: fmt.Sprintf("chmod %o %s", f.Mode, f.Path), + }) + } + + if len(t.clusterAsset.HookConf.ShellFiles) > 0 { + for _, sf := range t.clusterAsset.HookConf.ShellFiles { + hookFilePath := constants.HookFilesPath + sf.Name + ksData.Files = append(ksData.Files, File{ + Content: fmt.Sprintf("cat <<'EOF'> %s\n%s\nEOF", hookFilePath, string(sf.Content)), + ChangeMod: fmt.Sprintf("chmod %o %s", sf.Mode, hookFilePath), + }) + } + } + + for _, u := range systemds.Units { + fp := fmt.Sprintf("/etc/systemd/system/%s", u.Name) + ksData.Files = append(ksData.Files, File{ + Content: fmt.Sprintf("cat <<'EOF'> %s\n%s\nEOF", fp, string(u.Contents)), + ChangeMod: fmt.Sprintf("chmod %o %s", constants.BootConfigFileMode, fp), + }) + ksData.Systemds = append(ksData.Systemds, fmt.Sprintf("systemctl enable %s", u.Name)) + } + ksData.Systemds = append(ksData.Systemds, fmt.Sprintf("systemctl enable %s", constants.KubeletService)) + + file, err := data.Assets.Open("kickstart/" + nodeType + "/kickstart.cfg.template") + if err != nil { + return fmt.Errorf("failed to open kickstart template file: %v", err) + } + defer file.Close() + + _, data, err := utils.GetCompleteFile("kickstart.cfg.template", file, ksData) + if err != nil { + return fmt.Errorf("failed to generate kickstart file: %v", err) + } + + t.config = data + return nil +} diff --git a/pkg/ignition/common.go b/pkg/osmanager/bootconfig/tools.go similarity index 36% rename from pkg/ignition/common.go rename to pkg/osmanager/bootconfig/tools.go index 69157460f9356f2be4ff9d527b701d3be80ebfad..839e8f8ffc7a1a13104bdf269d38204283ffc7a3 100644 --- a/pkg/ignition/common.go +++ b/pkg/osmanager/bootconfig/tools.go @@ -1,5 +1,5 @@ /* -Copyright 2023 KylinSoft Co., Ltd. +Copyright 2024 KylinSoft Co., Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,30 +13,25 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package ignition +package bootconfig import ( + "errors" "fmt" "nestos-kubernetes-deployer/data" + "nestos-kubernetes-deployer/pkg/configmanager" "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/runtime" + "nestos-kubernetes-deployer/pkg/constants" "nestos-kubernetes-deployer/pkg/utils" + "os" "path" + "path/filepath" "strings" + "github.com/clarketm/json" ignutil "github.com/coreos/ignition/v2/config/util" - igntypes "github.com/coreos/ignition/v2/config/v3_2/types" - "github.com/sirupsen/logrus" -) - -var ( - EnabledServices = []string{ - "kubelet.service", - "set-kernel-para.service", - "init-cluster.service", - "join-master.service", - "release-image-pivot.service", - "join-worker.service", - } + "gopkg.in/yaml.v2" ) type TmplData struct { @@ -55,69 +50,87 @@ type TmplData struct { CertificateKey string Hsip string //HostName + IP KubeadmApiVersion string + HookFilesPath string + CertsUrl string + IsControlPlane bool + IsDocker bool + IsIsulad bool + IsCrio bool + IsContainerd bool + IsNestOS bool + IsGeneralOS bool + PackageList []string + RpmPackageCurl string + RegistryMirrors string } -type Common struct { - UserName string - SSHKey string - PassWord string - NodeType string - TmplData interface{} - EnabledServices []string - Config *igntypes.Config -} +func GetTmplData(c *asset.ClusterAsset) (*TmplData, error) { + var ( + hsipStrings []string + rpmPackageCurl string + ) -func (c *Common) Generate() error { - c.Config = &igntypes.Config{ - Ignition: igntypes.Ignition{ - Version: igntypes.MaxVersion.String(), - }, - Passwd: igntypes.Passwd{ - Users: []igntypes.PasswdUser{ - { - Name: c.UserName, - SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{ - igntypes.SSHAuthorizedKey(c.SSHKey), - }, - PasswordHash: &c.PassWord, - }, - }, - }, - Storage: igntypes.Storage{ - Links: []igntypes.Link{ - { - Node: igntypes.Node{Path: "/etc/local/time"}, - LinkEmbedded1: igntypes.LinkEmbedded1{ - Target: "/usr/share/zoneinfo/Asia/Shanghai", - }, - }, - }, - }, + for _, master := range c.Master { + hsipStrings = append(hsipStrings, master.IP+" "+master.Hostname) } + hsip := strings.Join(hsipStrings, "\n") - nodeFilesPath := fmt.Sprintf("ignition/%s/files", c.NodeType) - if err := appendStorageFiles(c.Config, "/", nodeFilesPath, c.TmplData); err != nil { - logrus.Errorf("failed to add files to a ignition config: %v", err) - return err + engine, err := runtime.GetRuntime(c.Runtime) + if err != nil { + return nil, err } - nodeUnitPath := fmt.Sprintf("ignition/%s/systemd/", c.NodeType) - if err := appendSystemdUnits(c.Config, nodeUnitPath, c.TmplData, c.EnabledServices); err != nil { - logrus.Errorf("failed to add systemd units to a ignition config: %v", err) - return err + + if c.IsGeneralOS && len(c.Kubernetes.RpmPackagePath) > 0 { + rpmPackageCurl = utils.ConstructURL(configmanager.GetBootstrapIgnHostPort(), constants.RpmPackageList) } - return nil + deflash := strings.TrimPrefix(strings.TrimPrefix(c.Kubernetes.RegistryMirror, "http://"), "https://") + + return &TmplData{ + APIServerURL: c.Kubernetes.ApiServerEndpoint, + ImageRegistry: c.Kubernetes.ImageRegistry, + Runtime: c.Runtime, + PauseImage: c.Kubernetes.PauseImage, + KubeVersion: c.Kubernetes.KubernetesVersion, + KubeadmApiVersion: c.Kubernetes.KubernetesAPIVersion, + ServiceSubnet: c.Network.ServiceSubnet, + PodSubnet: c.Network.PodSubnet, + Token: c.Kubernetes.Token, + CaCertHash: c.Kubernetes.CaCertHash, + ReleaseImageURl: c.Kubernetes.ReleaseImageURL, + CertificateKey: c.Kubernetes.CertificateKey, + Hsip: hsip, + HookFilesPath: constants.HookFilesPath, + IsDocker: runtime.IsDocker(engine), + IsIsulad: runtime.IsIsulad(engine), + IsCrio: runtime.IsCrio(engine), + IsContainerd: runtime.IsContainerd(engine), + IsNestOS: c.IsNestOS, + IsGeneralOS: c.IsGeneralOS, + PackageList: c.PackageList, + RpmPackageCurl: rpmPackageCurl, + RegistryMirrors: deflash, + }, nil } /* -AppendStorageFiles add files to a ignition config -Parameters: - - config: the ignition config to be modified - - tmplData: struct to used to render templates +AppendStorageFiles: 向提供的切片中追加存储文件的信息。 +参数: + - config:指向 File 结构切片的指针,文件信息将被追加到其中。 + - base:要从中开始遍历目录的基本路径。 + - uri:要处理的文件或目录的 URI。 + - tmplData:用于模板渲染的数据。 + - enabledFiles:已启用处理的文件名列表。 */ -func appendStorageFiles(config *igntypes.Config, base string, uri string, tmplData interface{}) error { +func AppendStorageFiles(config *[]File, base string, uri string, tmplData interface{}, enabledFiles []string) error { + enabled := make(map[string]struct{}, len(enabledFiles)) + for _, s := range enabledFiles { + enabled[s] = struct{}{} + } + file, err := data.Assets.Open(uri) if err != nil { + fmt.Printf("err: %v\n", err) return err } defer file.Close() @@ -137,7 +150,7 @@ func appendStorageFiles(config *igntypes.Config, base string, uri string, tmplDa for _, childInfo := range children { name := childInfo.Name() - err = appendStorageFiles(config, path.Join(base, name), path.Join(uri, name), tmplData) + err = AppendStorageFiles(config, path.Join(base, name), path.Join(uri, name), tmplData, enabledFiles) if err != nil { return err } @@ -148,20 +161,23 @@ func appendStorageFiles(config *igntypes.Config, base string, uri string, tmplDa if err != nil { return err } - ignFile := FileWithContents(strings.TrimSuffix(base, ".template"), 0755, data) - config.Storage.Files = AppendFiles(config.Storage.Files, ignFile) + if _, ok := enabled[base]; ok { + ignFile := fileWithContents(strings.TrimSuffix(base, ".template"), constants.StorageFilesMode, data) + *config = appendFiles(*config, ignFile) + } return nil } /* -Add systemd units to a ignition config -Parameters: - - config: the ignition config to be modified - - uri: path under data/ignition specifying the systemd units files to be included - - tmplData: struct to used to render templates - - enabledServices: a list of systemd units to be enabled by default +AppendSystemdUnits: 向 Systemd 结构中追加信息 +参数: + - config:指向 Systemd 结构的指针,其中将添加Systemd信息。 + - uri:要打开的目录的 URI。 + - tmplData:用于模板渲染的数据。 + - enabledServices:已启用处理的服务名列表 */ -func appendSystemdUnits(config *igntypes.Config, uri string, tmplData interface{}, enabledServices []string) error { + +func AppendSystemdUnits(config *Systemd, uri string, tmplData interface{}, enabledServices []string) error { enabled := make(map[string]struct{}, len(enabledServices)) for _, s := range enabledServices { enabled[s] = struct{}{} @@ -188,45 +204,108 @@ func appendSystemdUnits(config *igntypes.Config, uri string, tmplData interface{ if err != nil { return err } - unit := igntypes.Unit{ - Name: name, - Contents: ignutil.StrToPtr(string(contents)), - } + if _, ok := enabled[name]; ok { + unit := Unit{ + Name: name, + Contents: string(contents), + } unit.Enabled = ignutil.BoolToPtr(true) + config.Units = append(config.Units, unit) } - config.Systemd.Units = append(config.Systemd.Units, unit) } return nil } -func GetTmplData(c *asset.ClusterAsset) (*TmplData, error) { - var hsip string - for i := 0; i < len(c.Master); i++ { - temp := c.Master[i].IP + " " + c.Master[i].Hostname + "\n" - hsip = hsip + temp +func GetSavePath(clusterID string) string { + return filepath.Join(configmanager.GetPersistDir(), clusterID, constants.BootConfigSaveDir) +} + +func SaveYAML(data interface{}, filePath string, fileName string, header string) error { + return saveFile(data, filePath, fileName, header, yaml.Marshal) +} + +func SaveJSON(data interface{}, filePath string, fileName string) error { + return saveFile(data, filePath, fileName, "", json.Marshal) +} + +func Marshal(input interface{}) ([]byte, error) { + return json.Marshal(input) +} + +func SaveFile(data []byte, filePath, fileName string) error { + if data == nil { + return errors.New("data is nil") } - criSocket, err := asset.GetRuntimeCriSocket(c.Runtime) + return saveDataToFile(string(data), filePath, fileName) +} + +func saveFile(data interface{}, filePath, fileName, header string, marshalFunc func(interface{}) ([]byte, error)) error { + if data == nil { + return errors.New("data is nil") + } + + dataBytes, err := marshalFunc(data) if err != nil { - logrus.Errorf("Error getting runtime %s: %v\n", c.Runtime, err) - return nil, err + return fmt.Errorf("failed to marshal data: %w", err) } + dataString := header + string(dataBytes) - return &TmplData{ - APIServerURL: c.Kubernetes.ApiServerEndpoint, - ImageRegistry: c.Kubernetes.ImageRegistry, - Runtime: c.Runtime, - CriSocket: criSocket, - PauseImage: c.Kubernetes.PauseImage, - KubeVersion: c.Kubernetes.KubernetesVersion, - KubeadmApiVersion: c.Kubernetes.KubernetesAPIVersion, - ServiceSubnet: c.Network.ServiceSubnet, - PodSubnet: c.Network.PodSubnet, - Token: c.Kubernetes.Token, - CaCertHash: c.Kubernetes.CaCertHash, - ReleaseImageURl: c.Kubernetes.ReleaseImageURL, - CertificateKey: c.Kubernetes.CertificateKey, - Hsip: hsip, - }, nil + return saveDataToFile(dataString, filePath, fileName) +} + +func saveDataToFile(data, filePath, fileName string) error { + fullPath := filepath.Join(filePath, fileName) + if err := os.MkdirAll(filepath.Dir(fullPath), os.ModePerm); err != nil { + return fmt.Errorf("failed to create directory: %w", err) + } + if err := os.WriteFile(fullPath, []byte(data), os.ModePerm); err != nil { + return fmt.Errorf("failed to save file: %w", err) + } + + return nil +} + +func fileWithContents(path string, mode os.FileMode, contents []byte) File { + return File{ + Node: Node{ + Path: path, + Overwrite: ignutil.BoolToPtr(true), + }, + FileEmbedded1: FileEmbedded1{ + Mode: mode, + Contents: Resource{ + Source: contents, + }, + }, + } +} + +func appendFiles(files []File, file File) []File { + for i, f := range files { + if f.Node.Path == file.Node.Path { + files[i] = file + return files + } + } + files = append(files, file) + return files +} + +// set-hostname.service用于动态设置节点hostname, +// ${hostname}变量会通过基础设施模块(terraform)为变量赋值(参见https://developer.hashicorp.com/terraform/language/functions/templatefile) +// terraform模板文件修改部分:templatefile(var.instance_ign[count.index], { hostname = var.instance_hostname[count.index] }) +func CreateSetHostnameUnit() string { + unit := `[Unit] +Description=Set hostname +ConditionPathExists=!/var/log/set-hostname.stamp +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/hostnamectl set-hostname ${hostname} +ExecStart=/bin/touch /var/log/set-hostname.stamp +[Install] +WantedBy=multi-user.target` + return unit } diff --git a/pkg/osmanager/bootconfig/types.go b/pkg/osmanager/bootconfig/types.go new file mode 100644 index 0000000000000000000000000000000000000000..2d9945beddc9151674c22610285d853b38099fad --- /dev/null +++ b/pkg/osmanager/bootconfig/types.go @@ -0,0 +1,44 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bootconfig + +import "os" + +type File struct { + Node + FileEmbedded1 +} +type Node struct { + Overwrite *bool `json:"overwrite,omitempty"` + Path string `json:"path"` +} +type FileEmbedded1 struct { + Contents Resource `json:"contents,omitempty"` + Mode os.FileMode `json:"mode,omitempty"` +} +type Resource struct { + Source []byte `json:"source,omitempty"` +} + +type Systemd struct { + Units []Unit `json:"units,omitempty"` +} +type Unit struct { + Contents string `json:"contents,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Name string `json:"name"` +} diff --git a/pkg/osmanager/generalos/generalos.go b/pkg/osmanager/generalos/generalos.go new file mode 100644 index 0000000000000000000000000000000000000000..d4066dcb9551e556b60ab181f3bee52cdec590f8 --- /dev/null +++ b/pkg/osmanager/generalos/generalos.go @@ -0,0 +1,93 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package generalos + +import ( + "errors" + "nestos-kubernetes-deployer/pkg/cert" + "nestos-kubernetes-deployer/pkg/configmanager" + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig/cloudinit" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig/kickstart" + "nestos-kubernetes-deployer/pkg/terraform" + "strings" + + "github.com/sirupsen/logrus" +) + +type GeneralOS struct { + conf *asset.ClusterAsset + certs *cert.CertGenerator + cloudinitFile *cloudinit.Cloudinit + kickstartFile *kickstart.Kickstart + infraMaster *terraform.Infra + infraWorker *terraform.Infra +} + +func NewGeneralOS(conf *asset.ClusterAsset) (*GeneralOS, error) { + if conf == nil { + return nil, errors.New("cluster asset config is nil") + } + if len(conf.Master) == 0 { + return nil, errors.New("master node config is empty") + } + + certGenerator := cert.NewCertGenerator(conf.ClusterID, &conf.Master[0]) + cloudinitFile := cloudinit.NewCloudinit(conf, configmanager.GetBootstrapIgnHostPort()) + kickstartFile := kickstart.NewKickstart(conf, configmanager.GetBootstrapIgnHostPort()) + return &GeneralOS{ + conf: conf, + certs: certGenerator, + cloudinitFile: cloudinitFile, + kickstartFile: kickstartFile, + infraMaster: &terraform.Infra{}, + infraWorker: &terraform.Infra{}, + }, nil +} + +func (g *GeneralOS) GenerateResourceFiles() error { + if err := g.certs.GenerateAllFiles(); err != nil { + logrus.Errorf("Error generating all certs files: %v", err) + return err + } + g.conf.CaCertHash = g.certs.CaCertHash + logrus.Infof("Certificates generated successfully") + + switch strings.ToLower(g.conf.Platform) { + case "libvirt", "openstack": + if err := g.cloudinitFile.GenerateBootConfig(); err != nil { + logrus.Errorf("failed to generate cloudinit file: %v", err) + return err + } + + if err := g.infraMaster.Generate(g.conf, "master"); err != nil { + logrus.Errorf("Failed to generate master terraform file") + return err + } + if err := g.infraWorker.Generate(g.conf, "worker"); err != nil { + logrus.Errorf("Failed to generate worker terraform file") + return err + } + case "pxe", "ipxe": + if err := g.kickstartFile.GenerateBootConfig(); err != nil { + logrus.Errorf("failed to generate kickstart file: %v", err) + return err + } + } + + return nil +} diff --git a/pkg/osmanager/nestos/nestos.go b/pkg/osmanager/nestos/nestos.go new file mode 100644 index 0000000000000000000000000000000000000000..f89dd11257572e99eb041f33b746cdd8e0bf6ccb --- /dev/null +++ b/pkg/osmanager/nestos/nestos.go @@ -0,0 +1,94 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nestos + +import ( + "errors" + "nestos-kubernetes-deployer/pkg/cert" + "nestos-kubernetes-deployer/pkg/configmanager" + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig/ignition" + "nestos-kubernetes-deployer/pkg/osmanager/bootconfig/kickstart" + "nestos-kubernetes-deployer/pkg/terraform" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" +) + +type NestOS struct { + conf *asset.ClusterAsset + certs *cert.CertGenerator + ignitionFile *ignition.Ignition + kickstartFile *kickstart.Kickstart + infraMaster *terraform.Infra + infraWorker *terraform.Infra +} + +func NewNestOS(conf *asset.ClusterAsset) (*NestOS, error) { + if conf == nil { + return nil, errors.New("cluster asset config is nil") + } + if len(conf.Master) == 0 { + return nil, errors.New("master node config is empty") + } + + certGenerator := cert.NewCertGenerator(conf.ClusterID, &conf.Master[0]) + ignitionFile := ignition.NewIgnition(conf, configmanager.GetBootstrapIgnHostPort()) + kickstartFile := kickstart.NewKickstart(conf, filepath.Join(configmanager.GetPersistDir(), conf.ClusterID)) + return &NestOS{ + conf: conf, + certs: certGenerator, + ignitionFile: ignitionFile, + kickstartFile: kickstartFile, + infraMaster: &terraform.Infra{}, + infraWorker: &terraform.Infra{}, + }, nil +} + +func (n *NestOS) GenerateResourceFiles() error { + if err := n.certs.GenerateAllFiles(); err != nil { + logrus.Errorf("Error generating all certs files: %v", err) + return err + } + n.conf.CaCertHash = n.certs.CaCertHash + logrus.Infof("Certificates generated successfully") + + switch strings.ToLower(n.conf.Platform) { + case "libvirt", "openstack": + if err := n.ignitionFile.GenerateBootConfig(); err != nil { + logrus.Errorf("failed to generate ignition file: %v", err) + return err + } + + if err := n.infraMaster.Generate(n.conf, "master"); err != nil { + logrus.Errorf("Failed to generate master terraform file") + return err + } + if err := n.infraWorker.Generate(n.conf, "worker"); err != nil { + logrus.Errorf("Failed to generate worker terraform file") + return err + } + case "pxe", "ipxe": + if err := n.kickstartFile.GenerateBootConfig(); err != nil { + logrus.Errorf("failed to generate kickstart file: %v", err) + return err + } + } + + return nil +} diff --git a/pkg/osmanager/osmanager.go b/pkg/osmanager/osmanager.go new file mode 100644 index 0000000000000000000000000000000000000000..cd5615470de285fa161ce6e159dde74284134ae5 --- /dev/null +++ b/pkg/osmanager/osmanager.go @@ -0,0 +1,63 @@ +package osmanager + +import ( + "fmt" + "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/osmanager/generalos" + "nestos-kubernetes-deployer/pkg/osmanager/nestos" + "strings" +) + +const ( + nestosType = "nestos" + generalosType = "generalos" +) + +type osmanager struct { + config *asset.ClusterAsset +} + +func NewOSManager(config *asset.ClusterAsset) *osmanager { + return &osmanager{ + config: config, + } +} + +func (o *osmanager) GenerateOSConfig() error { + if o.IsNestOS() { + osDep, err := nestos.NewNestOS(o.config) + if err != nil { + return fmt.Errorf("error creating NestOS osmanager instance: %v", err) + } + if err := osDep.GenerateResourceFiles(); err != nil { + return fmt.Errorf("error generating NestOS resource files: %v", err) + } + return nil + } else if o.IsGeneralOS() { + osDep, err := generalos.NewGeneralOS(o.config) + if err != nil { + return fmt.Errorf("error creating GeneralOS osmanager instance: %v", err) + } + if err := osDep.GenerateResourceFiles(); err != nil { + return fmt.Errorf("error generating GeneralOS resource files: %v", err) + } + return nil + } + return fmt.Errorf("unsupported OS type: %s", o.config.OSImage.Type) +} + +func (o *osmanager) IsNestOS() bool { + if strings.ToLower(o.config.OSImage.Type) == nestosType { + o.config.OSImage.IsNestOS = true + return true + } + return false +} + +func (o *osmanager) IsGeneralOS() bool { + if strings.ToLower(o.config.OSImage.Type) == generalosType { + o.config.OSImage.IsGeneralOS = true + return true + } + return false +} diff --git a/pkg/infra/terraform/deploy.go b/pkg/terraform/deploy.go similarity index 100% rename from pkg/infra/terraform/deploy.go rename to pkg/terraform/deploy.go diff --git a/pkg/infra/terraform/destroy.go b/pkg/terraform/destroy.go similarity index 100% rename from pkg/infra/terraform/destroy.go rename to pkg/terraform/destroy.go diff --git a/pkg/infra/generate.go b/pkg/terraform/generate.go similarity index 62% rename from pkg/infra/generate.go rename to pkg/terraform/generate.go index 9225cf0c70ce2cb08eef0290ad9c5ce37b3a94a5..1cf08aaa2f454462a1ce3e6c20eb921733dfd95a 100644 --- a/pkg/infra/generate.go +++ b/pkg/terraform/generate.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package infra +package terraform import ( "fmt" @@ -22,6 +22,7 @@ import ( "nestos-kubernetes-deployer/data" "nestos-kubernetes-deployer/pkg/configmanager" "nestos-kubernetes-deployer/pkg/configmanager/asset" + "nestos-kubernetes-deployer/pkg/configmanager/asset/infraasset" "os" "path/filepath" "reflect" @@ -29,104 +30,98 @@ import ( "text/template" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) -type Platform interface { - SetPlatform(asset.InfraAsset) +type Infra struct { + ClusterID string + Platform interface{} + Master Node + Worker Node + MachineType string } type OpenStack struct { - Username string - Password string - Tenant_Name string - Auth_URL string - Region string - Internal_Network string - External_Network string - Glance_Name string - Availability_Zone string -} - -func (openstack *OpenStack) SetPlatform(infraAsset asset.InfraAsset) { - if openstackAsset, ok := infraAsset.(*asset.OpenStackAsset); ok { - openstack.Username = openstackAsset.UserName - openstack.Password = openstackAsset.Password - openstack.Tenant_Name = openstackAsset.Tenant_Name - openstack.Auth_URL = openstackAsset.Auth_URL - openstack.Region = openstackAsset.Region - openstack.Internal_Network = openstackAsset.Internal_Network - openstack.External_Network = openstackAsset.External_Network - openstack.Glance_Name = openstackAsset.Glance_Name - openstack.Availability_Zone = openstackAsset.Availability_Zone - } + Username string + Password string + TenantName string + AuthURL string + Region string + InternalNetwork string + ExternalNetwork string + GlanceName string + AvailabilityZone string } type Libvirt struct { - URI string - OSImage_Path string - CIDR string - Gateway string -} - -func (libvirt *Libvirt) SetPlatform(infraAsset asset.InfraAsset) { - if libvirtAsset, ok := infraAsset.(*asset.LibvirtAsset); ok { - libvirt.URI = libvirtAsset.URI - libvirt.OSImage_Path = libvirtAsset.OSImage - libvirt.CIDR = libvirtAsset.CIDR - libvirt.Gateway = libvirtAsset.Gateway - } -} - -type Infra struct { - ClusterID string - Platform - Master Node - Worker Node - MachineType string + URI string + OSImage string + CIDR string + Gateway string } type Node struct { - Count int - CPU []string - RAM []string - Disk []string - Hostname []string - IP []string - Ign_Path []string + Count int + CPU []string + RAM []string + Disk []string + Hostname []string + IP []string + BootConfig []string } func (infra *Infra) Generate(conf *asset.ClusterAsset, node string) (err error) { - infra.ClusterID = conf.Cluster_ID + infra.ClusterID = conf.ClusterID - switch conf.Platform { - case "openstack", "Openstack", "OpenStack": - infra.Platform = &OpenStack{} - case "libvirt", "Libvirt": - infra.Platform = &Libvirt{} + switch strings.ToLower(conf.Platform) { + case "libvirt": + libvirtAsset := conf.InfraPlatform.(*infraasset.LibvirtAsset) + infra.Platform = &Libvirt{ + URI: libvirtAsset.URI, + OSImage: libvirtAsset.OSPath, + CIDR: libvirtAsset.CIDR, + Gateway: libvirtAsset.Gateway, + } + case "openstack": + openstackAsset := conf.InfraPlatform.(*infraasset.OpenStackAsset) + infra.Platform = &OpenStack{ + Username: openstackAsset.UserName, + Password: openstackAsset.Password, + TenantName: openstackAsset.TenantName, + AuthURL: openstackAsset.AuthURL, + Region: openstackAsset.Region, + InternalNetwork: openstackAsset.InternalNetwork, + ExternalNetwork: openstackAsset.ExternalNetwork, + GlanceName: openstackAsset.GlanceName, + AvailabilityZone: openstackAsset.AvailabilityZone, + } default: - return errors.New("unsupported platform") + logrus.Errorf("unsupported platform") + return err } - infra.Platform.SetPlatform(conf.InfraPlatform) - if node == "master" { var ( - master_cpu []uint - master_ram []uint - master_disk []uint - master_hostname []string - master_ip []string - master_ignPath []string + master_cpu []uint + master_ram []uint + master_disk []uint + master_hostname []string + master_ip []string + master_bootConfig []string ) infra.Master.Count = len(conf.Master) - for _, master := range conf.Master { + for i, master := range conf.Master { master_cpu = append(master_cpu, master.CPU) master_ram = append(master_ram, master.RAM) master_disk = append(master_disk, master.Disk) master_hostname = append(master_hostname, master.Hostname) master_ip = append(master_ip, master.IP) - master_ignPath = append(master_ignPath, master.Ignitions.MergeIgnPath) + if i == 0 { + master_bootConfig = append(master_bootConfig, conf.BootConfig.Controlplane.Path) + continue + } + master_bootConfig = append(master_bootConfig, conf.BootConfig.Master.Path) } infra.Master.CPU, err = convertSliceToStrings(master_cpu) if err != nil { @@ -148,18 +143,18 @@ func (infra *Infra) Generate(conf *asset.ClusterAsset, node string) (err error) if err != nil { return err } - infra.Master.Ign_Path, err = convertSliceToStrings(master_ignPath) + infra.Master.BootConfig, err = convertSliceToStrings(master_bootConfig) if err != nil { return err } } else if node == "worker" { var ( - worker_cpu []uint - worker_ram []uint - worker_disk []uint - worker_hostname []string - worker_ip []string - worker_ignPath []string + worker_cpu []uint + worker_ram []uint + worker_disk []uint + worker_hostname []string + worker_ip []string + worker_bootConfig []string ) infra.Worker.Count = len(conf.Worker) @@ -172,7 +167,7 @@ func (infra *Infra) Generate(conf *asset.ClusterAsset, node string) (err error) } worker_ip = append(worker_ip, worker.IP) worker_hostname = append(worker_hostname, worker.Hostname) - worker_ignPath = append(worker_ignPath, worker.Ignitions.MergeIgnPath) + worker_bootConfig = append(worker_bootConfig, conf.BootConfig.Worker.Path) } infra.Worker.CPU, err = convertSliceToStrings(worker_cpu) if err != nil { @@ -194,7 +189,7 @@ func (infra *Infra) Generate(conf *asset.ClusterAsset, node string) (err error) if err != nil { return err } - infra.Worker.Ign_Path, err = convertSliceToStrings(worker_ignPath) + infra.Worker.BootConfig, err = convertSliceToStrings(worker_bootConfig) if err != nil { return err } @@ -206,22 +201,23 @@ func (infra *Infra) Generate(conf *asset.ClusterAsset, node string) (err error) case "arm64", "aarch64": infra.MachineType = "virt" default: - return errors.New("unsupported architecture") + logrus.Errorf("unsupported architecture") + return err } persistDir := configmanager.GetPersistDir() - if err := os.MkdirAll(filepath.Join(persistDir, conf.Cluster_ID, node), 0644); err != nil { + if err := os.MkdirAll(filepath.Join(persistDir, conf.ClusterID, node), 0644); err != nil { return err } - outputFile, err := os.Create(filepath.Join(persistDir, conf.Cluster_ID, node, fmt.Sprintf("%s.tf", node))) + outputFile, err := os.Create(filepath.Join(persistDir, conf.ClusterID, node, fmt.Sprintf("%s.tf", node))) if err != nil { return errors.Wrap(err, "failed to create terraform config file") } defer outputFile.Close() // Read template. - tfFilePath := filepath.Join("terraform", conf.Platform, fmt.Sprintf("%s.tf.template", node)) + tfFilePath := filepath.Join("terraform", strings.ToLower(conf.OSImage.Type), conf.Platform, fmt.Sprintf("%s.tf.template", node)) tfFile, err := data.Assets.Open(tfFilePath) if err != nil { return err diff --git a/pkg/infra/terraform/logger.go b/pkg/terraform/logger.go similarity index 100% rename from pkg/infra/terraform/logger.go rename to pkg/terraform/logger.go diff --git a/pkg/infra/terraform/state.go b/pkg/terraform/state.go similarity index 100% rename from pkg/infra/terraform/state.go rename to pkg/terraform/state.go diff --git a/pkg/infra/terraform/terraform.go b/pkg/terraform/terraform.go similarity index 88% rename from pkg/infra/terraform/terraform.go rename to pkg/terraform/terraform.go index 9de724bb4858da48b1aefe75a3b80a8c4fb3d885..396086dba68efc144a19618ab7eec2b1c9f5c830 100644 --- a/pkg/infra/terraform/terraform.go +++ b/pkg/terraform/terraform.go @@ -19,6 +19,7 @@ package terraform import ( "context" "fmt" + "nestos-kubernetes-deployer/pkg/bufferedprinter" "os" "path/filepath" @@ -51,8 +52,20 @@ func newTFExec(tfFileDir string) (*tfexec.Terraform, error) { } } - tf.SetStdout(os.Stdout) - tf.SetStderr(os.Stderr) + bpDebug := bufferedprinter.New(func(args ...interface{}) { + lp := bufferedprinter.TrimLastNewline(args...) + logrus.Debug(lp...) + }) + defer bpDebug.Close() + + bpError := bufferedprinter.New(func(args ...interface{}) { + lp := bufferedprinter.TrimLastNewline(args...) + logrus.Error(lp...) + }) + defer bpError.Close() + + tf.SetStdout(bpDebug) + tf.SetStderr(bpError) tf.SetLogger(newPrintfer()) diff --git a/pkg/tftpserver/tftpserver.go b/pkg/tftpserver/tftpserver.go new file mode 100644 index 0000000000000000000000000000000000000000..15c9bde088c2c02e419b650804d7979fa50a90ce --- /dev/null +++ b/pkg/tftpserver/tftpserver.go @@ -0,0 +1,104 @@ +/* +Copyright 2024 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tftpserver + +import ( + "io" + "os" + "path/filepath" + + "github.com/pin/tftp" + "github.com/sirupsen/logrus" +) + +type TFTPService struct { + IP string + Port string + RootDir string + server *tftp.Server +} + +func NewTFTPService(ip string, port string, rootDir string) *TFTPService { + return &TFTPService{ + IP: ip, + Port: port, + RootDir: rootDir, + } +} + +func (t *TFTPService) Start() error { + tftpHandler := TFTPHandler{ + RootDir: t.RootDir, + } + t.server = tftp.NewServer(tftpHandler.ReadHandler, tftpHandler.WriteHandler) + tftpServerAddr := t.IP + ":" + t.Port + logrus.Printf("TFTP server is listening on %s\n", tftpServerAddr) + err := t.server.ListenAndServe(tftpServerAddr) + if err != nil { + logrus.Println(err) + return err + } + + return nil +} + +type TFTPHandler struct { + RootDir string +} + +// ReadHandler handles TFTP read requests +func (h *TFTPHandler) ReadHandler(filename string, rf io.ReaderFrom) error { + filePath := filepath.Join(h.RootDir, filename) + file, err := os.Open(filePath) + if err != nil { + return err + } + defer file.Close() + + _, err = rf.ReadFrom(file) + if err != nil { + return err + } + + return nil +} + +// WriteHandler handles TFTP write requests +func (h *TFTPHandler) WriteHandler(filename string, wt io.WriterTo) error { + filePath := filepath.Join(h.RootDir, filename) + file, err := os.Create(filePath) + if err != nil { + return err + } + defer file.Close() + + _, err = wt.WriteTo(file) + if err != nil { + return err + } + + return nil +} + +func (t *TFTPService) Stop() error { + if t.server != nil { + t.server.Shutdown() + } + t.server = nil + + return nil +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 2cf807e655204a13b80c12bd00fea6d9e2bba9e6..495c65c730f7eb89f44e7519c38ef88fc901ea01 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -19,6 +19,7 @@ package utils import ( "fmt" "net" + "net/url" "os/user" "path/filepath" "strings" @@ -90,3 +91,12 @@ func IsPortOpen(port string) bool { defer listener.Close() return true } + +func ConstructURL(bootstrapIgnitionHost string, role string) string { + u := url.URL{ + Scheme: "http", + Host: bootstrapIgnitionHost, + Path: role, + } + return u.String() +} diff --git a/vendor/github.com/natefinch/lumberjack/.gitignore b/vendor/github.com/natefinch/lumberjack/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..836562412fe8a44fa99a515eeff68d2bc1a86daa --- /dev/null +++ b/vendor/github.com/natefinch/lumberjack/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/natefinch/lumberjack/.travis.yml b/vendor/github.com/natefinch/lumberjack/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..65dcbc56dc89106e255355cadc9bf8d36ea97746 --- /dev/null +++ b/vendor/github.com/natefinch/lumberjack/.travis.yml @@ -0,0 +1,6 @@ +language: go + +go: + - 1.8 + - 1.7 + - 1.6 \ No newline at end of file diff --git a/vendor/github.com/natefinch/lumberjack/LICENSE b/vendor/github.com/natefinch/lumberjack/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c3d4cc307d291c4b1f50c18f30b0e4af73e31ddc --- /dev/null +++ b/vendor/github.com/natefinch/lumberjack/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Nate Finch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/natefinch/lumberjack/README.md b/vendor/github.com/natefinch/lumberjack/README.md new file mode 100644 index 0000000000000000000000000000000000000000..060eae52a20e294ea60697b7c3adebb2ef3b505f --- /dev/null +++ b/vendor/github.com/natefinch/lumberjack/README.md @@ -0,0 +1,179 @@ +# lumberjack [![GoDoc](https://godoc.org/gopkg.in/natefinch/lumberjack.v2?status.png)](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [![Build Status](https://travis-ci.org/natefinch/lumberjack.svg?branch=v2.0)](https://travis-ci.org/natefinch/lumberjack) [![Build status](https://ci.appveyor.com/api/projects/status/00gchpxtg4gkrt5d)](https://ci.appveyor.com/project/natefinch/lumberjack) [![Coverage Status](https://coveralls.io/repos/natefinch/lumberjack/badge.svg?branch=v2.0)](https://coveralls.io/r/natefinch/lumberjack?branch=v2.0) + +### Lumberjack is a Go package for writing logs to rolling files. + +Package lumberjack provides a rolling logger. + +Note that this is v2.0 of lumberjack, and should be imported using gopkg.in +thusly: + + import "gopkg.in/natefinch/lumberjack.v2" + +The package name remains simply lumberjack, and the code resides at +https://github.com/natefinch/lumberjack under the v2.0 branch. + +Lumberjack is intended to be one part of a logging infrastructure. +It is not an all-in-one solution, but instead is a pluggable +component at the bottom of the logging stack that simply controls the files +to which logs are written. + +Lumberjack plays well with any logging package that can write to an +io.Writer, including the standard library's log package. + +Lumberjack assumes that only one process is writing to the output files. +Using the same lumberjack configuration from multiple processes on the same +machine will result in improper behavior. + + +**Example** + +To use lumberjack with the standard library's log package, just pass it into the SetOutput function when your application starts. + +Code: + +```go +log.SetOutput(&lumberjack.Logger{ + Filename: "/var/log/myapp/foo.log", + MaxSize: 500, // megabytes + MaxBackups: 3, + MaxAge: 28, //days + Compress: true, // disabled by default +}) +``` + + + +## type Logger +``` go +type Logger struct { + // Filename is the file to write logs to. Backup log files will be retained + // in the same directory. It uses -lumberjack.log in + // os.TempDir() if empty. + Filename string `json:"filename" yaml:"filename"` + + // MaxSize is the maximum size in megabytes of the log file before it gets + // rotated. It defaults to 100 megabytes. + MaxSize int `json:"maxsize" yaml:"maxsize"` + + // MaxAge is the maximum number of days to retain old log files based on the + // timestamp encoded in their filename. Note that a day is defined as 24 + // hours and may not exactly correspond to calendar days due to daylight + // savings, leap seconds, etc. The default is not to remove old log files + // based on age. + MaxAge int `json:"maxage" yaml:"maxage"` + + // MaxBackups is the maximum number of old log files to retain. The default + // is to retain all old log files (though MaxAge may still cause them to get + // deleted.) + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + + // LocalTime determines if the time used for formatting the timestamps in + // backup files is the computer's local time. The default is to use UTC + // time. + LocalTime bool `json:"localtime" yaml:"localtime"` + + // Compress determines if the rotated log files should be compressed + // using gzip. The default is not to perform compression. + Compress bool `json:"compress" yaml:"compress"` + // contains filtered or unexported fields +} +``` +Logger is an io.WriteCloser that writes to the specified filename. + +Logger opens or creates the logfile on first Write. If the file exists and +is less than MaxSize megabytes, lumberjack will open and append to that file. +If the file exists and its size is >= MaxSize megabytes, the file is renamed +by putting the current time in a timestamp in the name immediately before the +file's extension (or the end of the filename if there's no extension). A new +log file is then created using original filename. + +Whenever a write would cause the current log file exceed MaxSize megabytes, +the current file is closed, renamed, and a new log file created with the +original name. Thus, the filename you give Logger is always the "current" log +file. + +Backups use the log file name given to Logger, in the form `name-timestamp.ext` +where name is the filename without the extension, timestamp is the time at which +the log was rotated formatted with the time.Time format of +`2006-01-02T15-04-05.000` and the extension is the original extension. For +example, if your Logger.Filename is `/var/log/foo/server.log`, a backup created +at 6:30pm on Nov 11 2016 would use the filename +`/var/log/foo/server-2016-11-04T18-30-00.000.log` + +### Cleaning Up Old Log Files +Whenever a new logfile gets created, old log files may be deleted. The most +recent files according to the encoded timestamp will be retained, up to a +number equal to MaxBackups (or all of them if MaxBackups is 0). Any files +with an encoded timestamp older than MaxAge days are deleted, regardless of +MaxBackups. Note that the time encoded in the timestamp is the rotation +time, which may differ from the last time that file was written to. + +If MaxBackups and MaxAge are both 0, no old log files will be deleted. + + + + + + + + + + + +### func (\*Logger) Close +``` go +func (l *Logger) Close() error +``` +Close implements io.Closer, and closes the current logfile. + + + +### func (\*Logger) Rotate +``` go +func (l *Logger) Rotate() error +``` +Rotate causes Logger to close the existing log file and immediately create a +new one. This is a helper function for applications that want to initiate +rotations outside of the normal rotation rules, such as in response to +SIGHUP. After rotating, this initiates a cleanup of old log files according +to the normal rules. + +**Example** + +Example of how to rotate in response to SIGHUP. + +Code: + +```go +l := &lumberjack.Logger{} +log.SetOutput(l) +c := make(chan os.Signal, 1) +signal.Notify(c, syscall.SIGHUP) + +go func() { + for { + <-c + l.Rotate() + } +}() +``` + +### func (\*Logger) Write +``` go +func (l *Logger) Write(p []byte) (n int, err error) +``` +Write implements io.Writer. If a write would cause the log file to be larger +than MaxSize, the file is closed, renamed to include a timestamp of the +current time, and a new log file is created using the original log file name. +If the length of the write is greater than MaxSize, an error is returned. + + + + + + + + + +- - - +Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) diff --git a/vendor/github.com/natefinch/lumberjack/chown.go b/vendor/github.com/natefinch/lumberjack/chown.go new file mode 100644 index 0000000000000000000000000000000000000000..11d0669723285b1c708027106be4910ace0d41ce --- /dev/null +++ b/vendor/github.com/natefinch/lumberjack/chown.go @@ -0,0 +1,11 @@ +// +build !linux + +package lumberjack + +import ( + "os" +) + +func chown(_ string, _ os.FileInfo) error { + return nil +} diff --git a/vendor/github.com/natefinch/lumberjack/chown_linux.go b/vendor/github.com/natefinch/lumberjack/chown_linux.go new file mode 100644 index 0000000000000000000000000000000000000000..2758ec9cedd6e94a4e5b1a735e614efcd7a9e6ca --- /dev/null +++ b/vendor/github.com/natefinch/lumberjack/chown_linux.go @@ -0,0 +1,19 @@ +package lumberjack + +import ( + "os" + "syscall" +) + +// os_Chown is a var so we can mock it out during tests. +var os_Chown = os.Chown + +func chown(name string, info os.FileInfo) error { + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode()) + if err != nil { + return err + } + f.Close() + stat := info.Sys().(*syscall.Stat_t) + return os_Chown(name, int(stat.Uid), int(stat.Gid)) +} diff --git a/vendor/github.com/natefinch/lumberjack/lumberjack.go b/vendor/github.com/natefinch/lumberjack/lumberjack.go new file mode 100644 index 0000000000000000000000000000000000000000..46d97c553115506d8972f3c3c54216d346e9c61d --- /dev/null +++ b/vendor/github.com/natefinch/lumberjack/lumberjack.go @@ -0,0 +1,541 @@ +// Package lumberjack provides a rolling logger. +// +// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in +// thusly: +// +// import "gopkg.in/natefinch/lumberjack.v2" +// +// The package name remains simply lumberjack, and the code resides at +// https://github.com/natefinch/lumberjack under the v2.0 branch. +// +// Lumberjack is intended to be one part of a logging infrastructure. +// It is not an all-in-one solution, but instead is a pluggable +// component at the bottom of the logging stack that simply controls the files +// to which logs are written. +// +// Lumberjack plays well with any logging package that can write to an +// io.Writer, including the standard library's log package. +// +// Lumberjack assumes that only one process is writing to the output files. +// Using the same lumberjack configuration from multiple processes on the same +// machine will result in improper behavior. +package lumberjack + +import ( + "compress/gzip" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "time" +) + +const ( + backupTimeFormat = "2006-01-02T15-04-05.000" + compressSuffix = ".gz" + defaultMaxSize = 100 +) + +// ensure we always implement io.WriteCloser +var _ io.WriteCloser = (*Logger)(nil) + +// Logger is an io.WriteCloser that writes to the specified filename. +// +// Logger opens or creates the logfile on first Write. If the file exists and +// is less than MaxSize megabytes, lumberjack will open and append to that file. +// If the file exists and its size is >= MaxSize megabytes, the file is renamed +// by putting the current time in a timestamp in the name immediately before the +// file's extension (or the end of the filename if there's no extension). A new +// log file is then created using original filename. +// +// Whenever a write would cause the current log file exceed MaxSize megabytes, +// the current file is closed, renamed, and a new log file created with the +// original name. Thus, the filename you give Logger is always the "current" log +// file. +// +// Backups use the log file name given to Logger, in the form +// `name-timestamp.ext` where name is the filename without the extension, +// timestamp is the time at which the log was rotated formatted with the +// time.Time format of `2006-01-02T15-04-05.000` and the extension is the +// original extension. For example, if your Logger.Filename is +// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would +// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log` +// +// Cleaning Up Old Log Files +// +// Whenever a new logfile gets created, old log files may be deleted. The most +// recent files according to the encoded timestamp will be retained, up to a +// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files +// with an encoded timestamp older than MaxAge days are deleted, regardless of +// MaxBackups. Note that the time encoded in the timestamp is the rotation +// time, which may differ from the last time that file was written to. +// +// If MaxBackups and MaxAge are both 0, no old log files will be deleted. +type Logger struct { + // Filename is the file to write logs to. Backup log files will be retained + // in the same directory. It uses -lumberjack.log in + // os.TempDir() if empty. + Filename string `json:"filename" yaml:"filename"` + + // MaxSize is the maximum size in megabytes of the log file before it gets + // rotated. It defaults to 100 megabytes. + MaxSize int `json:"maxsize" yaml:"maxsize"` + + // MaxAge is the maximum number of days to retain old log files based on the + // timestamp encoded in their filename. Note that a day is defined as 24 + // hours and may not exactly correspond to calendar days due to daylight + // savings, leap seconds, etc. The default is not to remove old log files + // based on age. + MaxAge int `json:"maxage" yaml:"maxage"` + + // MaxBackups is the maximum number of old log files to retain. The default + // is to retain all old log files (though MaxAge may still cause them to get + // deleted.) + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + + // LocalTime determines if the time used for formatting the timestamps in + // backup files is the computer's local time. The default is to use UTC + // time. + LocalTime bool `json:"localtime" yaml:"localtime"` + + // Compress determines if the rotated log files should be compressed + // using gzip. The default is not to perform compression. + Compress bool `json:"compress" yaml:"compress"` + + size int64 + file *os.File + mu sync.Mutex + + millCh chan bool + startMill sync.Once +} + +var ( + // currentTime exists so it can be mocked out by tests. + currentTime = time.Now + + // os_Stat exists so it can be mocked out by tests. + os_Stat = os.Stat + + // megabyte is the conversion factor between MaxSize and bytes. It is a + // variable so tests can mock it out and not need to write megabytes of data + // to disk. + megabyte = 1024 * 1024 +) + +// Write implements io.Writer. If a write would cause the log file to be larger +// than MaxSize, the file is closed, renamed to include a timestamp of the +// current time, and a new log file is created using the original log file name. +// If the length of the write is greater than MaxSize, an error is returned. +func (l *Logger) Write(p []byte) (n int, err error) { + l.mu.Lock() + defer l.mu.Unlock() + + writeLen := int64(len(p)) + if writeLen > l.max() { + return 0, fmt.Errorf( + "write length %d exceeds maximum file size %d", writeLen, l.max(), + ) + } + + if l.file == nil { + if err = l.openExistingOrNew(len(p)); err != nil { + return 0, err + } + } + + if l.size+writeLen > l.max() { + if err := l.rotate(); err != nil { + return 0, err + } + } + + n, err = l.file.Write(p) + l.size += int64(n) + + return n, err +} + +// Close implements io.Closer, and closes the current logfile. +func (l *Logger) Close() error { + l.mu.Lock() + defer l.mu.Unlock() + return l.close() +} + +// close closes the file if it is open. +func (l *Logger) close() error { + if l.file == nil { + return nil + } + err := l.file.Close() + l.file = nil + return err +} + +// Rotate causes Logger to close the existing log file and immediately create a +// new one. This is a helper function for applications that want to initiate +// rotations outside of the normal rotation rules, such as in response to +// SIGHUP. After rotating, this initiates compression and removal of old log +// files according to the configuration. +func (l *Logger) Rotate() error { + l.mu.Lock() + defer l.mu.Unlock() + return l.rotate() +} + +// rotate closes the current file, moves it aside with a timestamp in the name, +// (if it exists), opens a new file with the original filename, and then runs +// post-rotation processing and removal. +func (l *Logger) rotate() error { + if err := l.close(); err != nil { + return err + } + if err := l.openNew(); err != nil { + return err + } + l.mill() + return nil +} + +// openNew opens a new log file for writing, moving any old log file out of the +// way. This methods assumes the file has already been closed. +func (l *Logger) openNew() error { + err := os.MkdirAll(l.dir(), 0744) + if err != nil { + return fmt.Errorf("can't make directories for new logfile: %s", err) + } + + name := l.filename() + mode := os.FileMode(0644) + info, err := os_Stat(name) + if err == nil { + // Copy the mode off the old logfile. + mode = info.Mode() + // move the existing file + newname := backupName(name, l.LocalTime) + if err := os.Rename(name, newname); err != nil { + return fmt.Errorf("can't rename log file: %s", err) + } + + // this is a no-op anywhere but linux + if err := chown(name, info); err != nil { + return err + } + } + + // we use truncate here because this should only get called when we've moved + // the file ourselves. if someone else creates the file in the meantime, + // just wipe out the contents. + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return fmt.Errorf("can't open new logfile: %s", err) + } + l.file = f + l.size = 0 + return nil +} + +// backupName creates a new filename from the given name, inserting a timestamp +// between the filename and the extension, using the local time if requested +// (otherwise UTC). +func backupName(name string, local bool) string { + dir := filepath.Dir(name) + filename := filepath.Base(name) + ext := filepath.Ext(filename) + prefix := filename[:len(filename)-len(ext)] + t := currentTime() + if !local { + t = t.UTC() + } + + timestamp := t.Format(backupTimeFormat) + return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext)) +} + +// openExistingOrNew opens the logfile if it exists and if the current write +// would not put it over MaxSize. If there is no such file or the write would +// put it over the MaxSize, a new file is created. +func (l *Logger) openExistingOrNew(writeLen int) error { + l.mill() + + filename := l.filename() + info, err := os_Stat(filename) + if os.IsNotExist(err) { + return l.openNew() + } + if err != nil { + return fmt.Errorf("error getting log file info: %s", err) + } + + if info.Size()+int64(writeLen) >= l.max() { + return l.rotate() + } + + file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + // if we fail to open the old log file for some reason, just ignore + // it and open a new log file. + return l.openNew() + } + l.file = file + l.size = info.Size() + return nil +} + +// genFilename generates the name of the logfile from the current time. +func (l *Logger) filename() string { + if l.Filename != "" { + return l.Filename + } + name := filepath.Base(os.Args[0]) + "-lumberjack.log" + return filepath.Join(os.TempDir(), name) +} + +// millRunOnce performs compression and removal of stale log files. +// Log files are compressed if enabled via configuration and old log +// files are removed, keeping at most l.MaxBackups files, as long as +// none of them are older than MaxAge. +func (l *Logger) millRunOnce() error { + if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress { + return nil + } + + files, err := l.oldLogFiles() + if err != nil { + return err + } + + var compress, remove []logInfo + + if l.MaxBackups > 0 && l.MaxBackups < len(files) { + preserved := make(map[string]bool) + var remaining []logInfo + for _, f := range files { + // Only count the uncompressed log file or the + // compressed log file, not both. + fn := f.Name() + if strings.HasSuffix(fn, compressSuffix) { + fn = fn[:len(fn)-len(compressSuffix)] + } + preserved[fn] = true + + if len(preserved) > l.MaxBackups { + remove = append(remove, f) + } else { + remaining = append(remaining, f) + } + } + files = remaining + } + if l.MaxAge > 0 { + diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge)) + cutoff := currentTime().Add(-1 * diff) + + var remaining []logInfo + for _, f := range files { + if f.timestamp.Before(cutoff) { + remove = append(remove, f) + } else { + remaining = append(remaining, f) + } + } + files = remaining + } + + if l.Compress { + for _, f := range files { + if !strings.HasSuffix(f.Name(), compressSuffix) { + compress = append(compress, f) + } + } + } + + for _, f := range remove { + errRemove := os.Remove(filepath.Join(l.dir(), f.Name())) + if err == nil && errRemove != nil { + err = errRemove + } + } + for _, f := range compress { + fn := filepath.Join(l.dir(), f.Name()) + errCompress := compressLogFile(fn, fn+compressSuffix) + if err == nil && errCompress != nil { + err = errCompress + } + } + + return err +} + +// millRun runs in a goroutine to manage post-rotation compression and removal +// of old log files. +func (l *Logger) millRun() { + for _ = range l.millCh { + // what am I going to do, log this? + _ = l.millRunOnce() + } +} + +// mill performs post-rotation compression and removal of stale log files, +// starting the mill goroutine if necessary. +func (l *Logger) mill() { + l.startMill.Do(func() { + l.millCh = make(chan bool, 1) + go l.millRun() + }) + select { + case l.millCh <- true: + default: + } +} + +// oldLogFiles returns the list of backup log files stored in the same +// directory as the current log file, sorted by ModTime +func (l *Logger) oldLogFiles() ([]logInfo, error) { + files, err := ioutil.ReadDir(l.dir()) + if err != nil { + return nil, fmt.Errorf("can't read log file directory: %s", err) + } + logFiles := []logInfo{} + + prefix, ext := l.prefixAndExt() + + for _, f := range files { + if f.IsDir() { + continue + } + if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil { + logFiles = append(logFiles, logInfo{t, f}) + continue + } + if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil { + logFiles = append(logFiles, logInfo{t, f}) + continue + } + // error parsing means that the suffix at the end was not generated + // by lumberjack, and therefore it's not a backup file. + } + + sort.Sort(byFormatTime(logFiles)) + + return logFiles, nil +} + +// timeFromName extracts the formatted time from the filename by stripping off +// the filename's prefix and extension. This prevents someone's filename from +// confusing time.parse. +func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) { + if !strings.HasPrefix(filename, prefix) { + return time.Time{}, errors.New("mismatched prefix") + } + if !strings.HasSuffix(filename, ext) { + return time.Time{}, errors.New("mismatched extension") + } + ts := filename[len(prefix) : len(filename)-len(ext)] + return time.Parse(backupTimeFormat, ts) +} + +// max returns the maximum size in bytes of log files before rolling. +func (l *Logger) max() int64 { + if l.MaxSize == 0 { + return int64(defaultMaxSize * megabyte) + } + return int64(l.MaxSize) * int64(megabyte) +} + +// dir returns the directory for the current filename. +func (l *Logger) dir() string { + return filepath.Dir(l.filename()) +} + +// prefixAndExt returns the filename part and extension part from the Logger's +// filename. +func (l *Logger) prefixAndExt() (prefix, ext string) { + filename := filepath.Base(l.filename()) + ext = filepath.Ext(filename) + prefix = filename[:len(filename)-len(ext)] + "-" + return prefix, ext +} + +// compressLogFile compresses the given log file, removing the +// uncompressed log file if successful. +func compressLogFile(src, dst string) (err error) { + f, err := os.Open(src) + if err != nil { + return fmt.Errorf("failed to open log file: %v", err) + } + defer f.Close() + + fi, err := os_Stat(src) + if err != nil { + return fmt.Errorf("failed to stat log file: %v", err) + } + + if err := chown(dst, fi); err != nil { + return fmt.Errorf("failed to chown compressed log file: %v", err) + } + + // If this file already exists, we presume it was created by + // a previous attempt to compress the log file. + gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) + if err != nil { + return fmt.Errorf("failed to open compressed log file: %v", err) + } + defer gzf.Close() + + gz := gzip.NewWriter(gzf) + + defer func() { + if err != nil { + os.Remove(dst) + err = fmt.Errorf("failed to compress log file: %v", err) + } + }() + + if _, err := io.Copy(gz, f); err != nil { + return err + } + if err := gz.Close(); err != nil { + return err + } + if err := gzf.Close(); err != nil { + return err + } + + if err := f.Close(); err != nil { + return err + } + if err := os.Remove(src); err != nil { + return err + } + + return nil +} + +// logInfo is a convenience struct to return the filename and its embedded +// timestamp. +type logInfo struct { + timestamp time.Time + os.FileInfo +} + +// byFormatTime sorts by newest time formatted in the name. +type byFormatTime []logInfo + +func (b byFormatTime) Less(i, j int) bool { + return b[i].timestamp.After(b[j].timestamp) +} + +func (b byFormatTime) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +func (b byFormatTime) Len() int { + return len(b) +} diff --git a/vendor/github.com/pin/tftp/.gitignore b/vendor/github.com/pin/tftp/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..daf913b1b347aae6de6f48d599bc89ef8c8693d6 --- /dev/null +++ b/vendor/github.com/pin/tftp/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/pin/tftp/.travis.yml b/vendor/github.com/pin/tftp/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..edbec9a6a22b16ac9b5ae4dfb09c36d726ee7764 --- /dev/null +++ b/vendor/github.com/pin/tftp/.travis.yml @@ -0,0 +1,8 @@ +language: go + +os: + - linux + - osx + +before_install: + - ulimit -n 4096 diff --git a/vendor/github.com/pin/tftp/CONTRIBUTORS b/vendor/github.com/pin/tftp/CONTRIBUTORS new file mode 100644 index 0000000000000000000000000000000000000000..c8c331dee6f90ec242eb49ea43f533568f9faed5 --- /dev/null +++ b/vendor/github.com/pin/tftp/CONTRIBUTORS @@ -0,0 +1,4 @@ +Dmitri Popov +Mojo Talantikite +Giovanni Bajo +Andrew Danforth diff --git a/vendor/github.com/pin/tftp/LICENSE b/vendor/github.com/pin/tftp/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..dada3d0955898c72084325d4c1832e55251eea15 --- /dev/null +++ b/vendor/github.com/pin/tftp/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) +Copyright (c) 2016 Dmitri Popov + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/pin/tftp/README.md b/vendor/github.com/pin/tftp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e360cc88213197df3eb21fa6ba9aa63b97c2510b --- /dev/null +++ b/vendor/github.com/pin/tftp/README.md @@ -0,0 +1,171 @@ +TFTP server and client library for Golang +========================================= + +[![GoDoc](https://godoc.org/github.com/pin/tftp?status.svg)](https://godoc.org/github.com/pin/tftp) +[![Build Status](https://travis-ci.org/pin/tftp.svg?branch=master)](https://travis-ci.org/pin/tftp) + +Implements: + * [RFC 1350](https://tools.ietf.org/html/rfc1350) - The TFTP Protocol (Revision 2) + * [RFC 2347](https://tools.ietf.org/html/rfc2347) - TFTP Option Extension + * [RFC 2348](https://tools.ietf.org/html/rfc2348) - TFTP Blocksize Option + +Partially implements (tsize server side only): + * [RFC 2349](https://tools.ietf.org/html/rfc2349) - TFTP Timeout Interval and Transfer Size Options + +Set of features is sufficient for PXE boot support. + +``` go +import "github.com/pin/tftp" +``` + +The package is cohesive to Golang `io`. Particularly it implements +`io.ReaderFrom` and `io.WriterTo` interfaces. That allows efficient data +transmission without unnecessary memory copying and allocations. + + +TFTP Server +----------- + +```go + +// readHandler is called when client starts file download from server +func readHandler(filename string, rf io.ReaderFrom) error { + file, err := os.Open(filename) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return err + } + n, err := rf.ReadFrom(file) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return err + } + fmt.Printf("%d bytes sent\n", n) + return nil +} + +// writeHandler is called when client starts file upload to server +func writeHandler(filename string, wt io.WriterTo) error { + file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return err + } + n, err := wt.WriteTo(file) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return err + } + fmt.Printf("%d bytes received\n", n) + return nil +} + +func main() { + // use nil in place of handler to disable read or write operations + s := tftp.NewServer(readHandler, writeHandler) + s.SetTimeout(5 * time.Second) // optional + err := s.ListenAndServe(":69") // blocks until s.Shutdown() is called + if err != nil { + fmt.Fprintf(os.Stdout, "server: %v\n", err) + os.Exit(1) + } +} +``` + +TFTP Client +----------- +Upload file to server: + +```go +c, err := tftp.NewClient("172.16.4.21:69") +file, err := os.Open(path) +c.SetTimeout(5 * time.Second) // optional +rf, err := c.Send("foobar.txt", "octet") +n, err := rf.ReadFrom(file) +fmt.Printf("%d bytes sent\n", n) +``` + +Download file from server: + +```go +c, err := tftp.NewClient("172.16.4.21:69") +wt, err := c.Receive("foobar.txt", "octet") +file, err := os.Create(path) +// Optionally obtain transfer size before actual data. +if n, ok := wt.(IncomingTransfer).Size(); ok { + fmt.Printf("Transfer size: %d\n", n) +} +n, err := wt.WriteTo(file) +fmt.Printf("%d bytes received\n", n) +``` + +Note: please handle errors better :) + +TSize option +------------ + +PXE boot ROM often expects tsize option support from a server: client +(e.g. computer that boots over the network) wants to know size of a +download before the actual data comes. Server has to obtain stream +size and send it to a client. + +Often it will happen automatically because TFTP library tries to check +if `io.Reader` provided to `ReadFrom` method also satisfies +`io.Seeker` interface (`os.File` for instance) and uses `Seek` to +determine file size. + +In case `io.Reader` you provide to `ReadFrom` in read handler does not +satisfy `io.Seeker` interface or you do not want TFTP library to call +`Seek` on your reader but still want to respond with tsize option +during outgoing request you can use an `OutgoingTransfer` interface: + +```go + +func readHandler(filename string, rf io.ReaderFrom) error { + ... + // Set transfer size before calling ReadFrom. + rf.(tftp.OutgoingTransfer).SetSize(myFileSize) + ... + // ReadFrom ... + +``` + +Similarly, it is possible to obtain size of a file that is about to be +received using `IncomingTransfer` interface (see `Size` method). + +Remote Address +-------------- + +The `OutgoingTransfer` and `IncomingTransfer` interfaces also provide the +`RemoteAddr` method which returns the peer IP address and port as a +`net.UDPAddr`. This can be used for detailed logging in a server handler. + +```go + +func readHandler(filename string, rf io.ReaderFrom) error { + ... + raddr := rf.(tftp.OutgoingTransfer).RemoteAddr() + log.Println("RRQ from", raddr.String()) + ... + // ReadFrom ... +``` + +Backoff +------- + +The default backoff before retransmitting an unacknowledged packet is a +random duration between 0 and 1 second. This behavior can be overridden +in clients and servers by providing a custom backoff calculation function. + +```go + s := tftp.NewServer(readHandler, writeHandler) + s.SetBackoff(func (attempts int) time.Duration { + return time.Duration(attempts) * time.Second + }) +``` + +or, for no backoff + +```go + s.SetBackoff(func (int) time.Duration { return 0 }) +``` diff --git a/vendor/github.com/pin/tftp/backoff.go b/vendor/github.com/pin/tftp/backoff.go new file mode 100644 index 0000000000000000000000000000000000000000..5c5326c2eecc09d80ddc8543ae371726c0a32160 --- /dev/null +++ b/vendor/github.com/pin/tftp/backoff.go @@ -0,0 +1,35 @@ +package tftp + +import ( + "math/rand" + "time" +) + +const ( + defaultTimeout = 5 * time.Second + defaultRetries = 5 +) + +type backoffFunc func(int) time.Duration + +type backoff struct { + attempt int + handler backoffFunc +} + +func (b *backoff) reset() { + b.attempt = 0 +} + +func (b *backoff) count() int { + return b.attempt +} + +func (b *backoff) backoff() { + if b.handler == nil { + time.Sleep(time.Duration(rand.Int63n(int64(time.Second)))) + } else { + time.Sleep(b.handler(b.attempt)) + } + b.attempt++ +} diff --git a/vendor/github.com/pin/tftp/client.go b/vendor/github.com/pin/tftp/client.go new file mode 100644 index 0000000000000000000000000000000000000000..9f1802de42d08f220584af25abe5e3df2cd365a8 --- /dev/null +++ b/vendor/github.com/pin/tftp/client.go @@ -0,0 +1,125 @@ +package tftp + +import ( + "fmt" + "io" + "net" + "strconv" + "time" +) + +// NewClient creates TFTP client for server on address provided. +func NewClient(addr string) (*Client, error) { + a, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, fmt.Errorf("resolving address %s: %v", addr, err) + } + return &Client{ + addr: a, + timeout: defaultTimeout, + retries: defaultRetries, + }, nil +} + +// SetTimeout sets maximum time client waits for single network round-trip to succeed. +// Default is 5 seconds. +func (c *Client) SetTimeout(t time.Duration) { + if t <= 0 { + c.timeout = defaultTimeout + } + c.timeout = t +} + +// SetRetries sets maximum number of attempts client made to transmit a packet. +// Default is 5 attempts. +func (c *Client) SetRetries(count int) { + if count < 1 { + c.retries = defaultRetries + } + c.retries = count +} + +// SetBackoff sets a user provided function that is called to provide a +// backoff duration prior to retransmitting an unacknowledged packet. +func (c *Client) SetBackoff(h backoffFunc) { + c.backoff = h +} + +type Client struct { + addr *net.UDPAddr + timeout time.Duration + retries int + backoff backoffFunc + blksize int + tsize bool +} + +// Send starts outgoing file transmission. It returns io.ReaderFrom or error. +func (c Client) Send(filename string, mode string) (io.ReaderFrom, error) { + conn, err := net.ListenUDP("udp", &net.UDPAddr{}) + if err != nil { + return nil, err + } + s := &sender{ + send: make([]byte, datagramLength), + receive: make([]byte, datagramLength), + conn: conn, + retry: &backoff{handler: c.backoff}, + timeout: c.timeout, + retries: c.retries, + addr: c.addr, + mode: mode, + } + if c.blksize != 0 { + s.opts = make(options) + s.opts["blksize"] = strconv.Itoa(c.blksize) + } + n := packRQ(s.send, opWRQ, filename, mode, s.opts) + addr, err := s.sendWithRetry(n) + if err != nil { + return nil, err + } + s.addr = addr + s.opts = nil + return s, nil +} + +// Receive starts incoming file transmission. It returns io.WriterTo or error. +func (c Client) Receive(filename string, mode string) (io.WriterTo, error) { + conn, err := net.ListenUDP("udp", &net.UDPAddr{}) + if err != nil { + return nil, err + } + if c.timeout == 0 { + c.timeout = defaultTimeout + } + r := &receiver{ + send: make([]byte, datagramLength), + receive: make([]byte, datagramLength), + conn: conn, + retry: &backoff{handler: c.backoff}, + timeout: c.timeout, + retries: c.retries, + addr: c.addr, + autoTerm: true, + block: 1, + mode: mode, + } + if c.blksize != 0 || c.tsize { + r.opts = make(options) + } + if c.blksize != 0 { + r.opts["blksize"] = strconv.Itoa(c.blksize) + } + if c.tsize { + r.opts["tsize"] = "0" + } + n := packRQ(r.send, opRRQ, filename, mode, r.opts) + l, addr, err := r.receiveWithRetry(n) + if err != nil { + return nil, err + } + r.l = l + r.addr = addr + return r, nil +} diff --git a/vendor/github.com/pin/tftp/netascii/netascii.go b/vendor/github.com/pin/tftp/netascii/netascii.go new file mode 100644 index 0000000000000000000000000000000000000000..92cee03bbce84133c3ad9576cdc8086f59c48814 --- /dev/null +++ b/vendor/github.com/pin/tftp/netascii/netascii.go @@ -0,0 +1,108 @@ +package netascii + +// TODO: make it work not only on linux + +import "io" + +const ( + CR = '\x0d' + LF = '\x0a' + NUL = '\x00' +) + +func ToReader(r io.Reader) io.Reader { + return &toReader{ + r: r, + buf: make([]byte, 256), + } +} + +type toReader struct { + r io.Reader + buf []byte + n int + i int + err error + lf bool + nul bool +} + +func (r *toReader) Read(p []byte) (int, error) { + var n int + for n < len(p) { + if r.lf { + p[n] = LF + n++ + r.lf = false + continue + } + if r.nul { + p[n] = NUL + n++ + r.nul = false + continue + } + if r.i < r.n { + if r.buf[r.i] == LF { + p[n] = CR + r.lf = true + } else if r.buf[r.i] == CR { + p[n] = CR + r.nul = true + + } else { + p[n] = r.buf[r.i] + } + r.i++ + n++ + continue + } + if r.err == nil { + r.n, r.err = r.r.Read(r.buf) + r.i = 0 + } else { + return n, r.err + } + } + return n, r.err +} + +type fromWriter struct { + w io.Writer + buf []byte + i int + cr bool +} + +func FromWriter(w io.Writer) io.Writer { + return &fromWriter{ + w: w, + buf: make([]byte, 256), + } +} + +func (w *fromWriter) Write(p []byte) (n int, err error) { + for n < len(p) { + if w.cr { + if p[n] == LF { + w.buf[w.i] = LF + } + if p[n] == NUL { + w.buf[w.i] = CR + } + w.cr = false + w.i++ + } else if p[n] == CR { + w.cr = true + } else { + w.buf[w.i] = p[n] + w.i++ + } + n++ + if w.i == len(w.buf) || n == len(p) { + _, err = w.w.Write(w.buf[:w.i]) + w.i = 0 + } + } + return n, err +} diff --git a/vendor/github.com/pin/tftp/packet.go b/vendor/github.com/pin/tftp/packet.go new file mode 100644 index 0000000000000000000000000000000000000000..1ac77428fd0784b884f3860d0f22fe4626c70c18 --- /dev/null +++ b/vendor/github.com/pin/tftp/packet.go @@ -0,0 +1,190 @@ +package tftp + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +const ( + opRRQ = uint16(1) // Read request (RRQ) + opWRQ = uint16(2) // Write request (WRQ) + opDATA = uint16(3) // Data + opACK = uint16(4) // Acknowledgement + opERROR = uint16(5) // Error + opOACK = uint16(6) // Options Acknowledgment +) + +const ( + blockLength = 512 + datagramLength = 516 +) + +type options map[string]string + +// RRQ/WRQ packet +// +// 2 bytes string 1 byte string 1 byte +// -------------------------------------------------- +// | Opcode | Filename | 0 | Mode | 0 | +// -------------------------------------------------- +type pRRQ []byte +type pWRQ []byte + +// packRQ returns length of the packet in b +func packRQ(p []byte, op uint16, filename, mode string, opts options) int { + binary.BigEndian.PutUint16(p, op) + n := 2 + n += copy(p[2:len(p)-10], filename) + p[n] = 0 + n++ + n += copy(p[n:], mode) + p[n] = 0 + n++ + for name, value := range opts { + n += copy(p[n:], name) + p[n] = 0 + n++ + n += copy(p[n:], value) + p[n] = 0 + n++ + } + return n +} + +func unpackRQ(p []byte) (filename, mode string, opts options, err error) { + bs := bytes.Split(p[2:], []byte{0}) + if len(bs) < 2 { + return "", "", nil, fmt.Errorf("missing filename or mode") + } + filename = string(bs[0]) + mode = string(bs[1]) + if len(bs) < 4 { + return filename, mode, nil, nil + } + opts = make(options) + for i := 2; i+1 < len(bs); i += 2 { + opts[string(bs[i])] = string(bs[i+1]) + } + return filename, mode, opts, nil +} + +// OACK packet +// +// +----------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ +// | Opcode | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | +// +----------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ +type pOACK []byte + +func packOACK(p []byte, opts options) int { + binary.BigEndian.PutUint16(p, opOACK) + n := 2 + for name, value := range opts { + n += copy(p[n:], name) + p[n] = 0 + n++ + n += copy(p[n:], value) + p[n] = 0 + n++ + } + return n +} + +func unpackOACK(p []byte) (opts options, err error) { + bs := bytes.Split(p[2:], []byte{0}) + opts = make(options) + for i := 0; i+1 < len(bs); i += 2 { + opts[string(bs[i])] = string(bs[i+1]) + } + return opts, nil +} + +// ERROR packet +// +// 2 bytes 2 bytes string 1 byte +// ------------------------------------------ +// | Opcode | ErrorCode | ErrMsg | 0 | +// ------------------------------------------ +type pERROR []byte + +func packERROR(p []byte, code uint16, message string) int { + binary.BigEndian.PutUint16(p, opERROR) + binary.BigEndian.PutUint16(p[2:], code) + n := copy(p[4:len(p)-2], message) + p[4+n] = 0 + return n + 5 +} + +func (p pERROR) code() uint16 { + return binary.BigEndian.Uint16(p[2:]) +} + +func (p pERROR) message() string { + return string(p[4:]) +} + +// DATA packet +// +// 2 bytes 2 bytes n bytes +// ---------------------------------- +// | Opcode | Block # | Data | +// ---------------------------------- +type pDATA []byte + +func (p pDATA) block() uint16 { + return binary.BigEndian.Uint16(p[2:]) +} + +// ACK packet +// +// 2 bytes 2 bytes +// ----------------------- +// | Opcode | Block # | +// ----------------------- +type pACK []byte + +func (p pACK) block() uint16 { + return binary.BigEndian.Uint16(p[2:]) +} + +func parsePacket(p []byte) (interface{}, error) { + l := len(p) + if l < 2 { + return nil, fmt.Errorf("short packet") + } + opcode := binary.BigEndian.Uint16(p) + switch opcode { + case opRRQ: + if l < 4 { + return nil, fmt.Errorf("short RRQ packet: %d", l) + } + return pRRQ(p), nil + case opWRQ: + if l < 4 { + return nil, fmt.Errorf("short WRQ packet: %d", l) + } + return pWRQ(p), nil + case opDATA: + if l < 4 { + return nil, fmt.Errorf("short DATA packet: %d", l) + } + return pDATA(p), nil + case opACK: + if l < 4 { + return nil, fmt.Errorf("short ACK packet: %d", l) + } + return pACK(p), nil + case opERROR: + if l < 5 { + return nil, fmt.Errorf("short ERROR packet: %d", l) + } + return pERROR(p), nil + case opOACK: + if l < 6 { + return nil, fmt.Errorf("short OACK packet: %d", l) + } + return pOACK(p), nil + default: + return nil, fmt.Errorf("unknown opcode: %d", opcode) + } +} diff --git a/vendor/github.com/pin/tftp/receiver.go b/vendor/github.com/pin/tftp/receiver.go new file mode 100644 index 0000000000000000000000000000000000000000..6e0153d2641f219216446dcb3f4fc85af65c8144 --- /dev/null +++ b/vendor/github.com/pin/tftp/receiver.go @@ -0,0 +1,234 @@ +package tftp + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "strconv" + "time" + + "github.com/pin/tftp/netascii" +) + +// IncomingTransfer provides methods that expose information associated with +// an incoming transfer. +type IncomingTransfer interface { + // Size returns the size of an incoming file if the request included the + // tsize option (see RFC2349). To differentiate a zero-sized file transfer + // from a request without tsize use the second boolean "ok" return value. + Size() (n int64, ok bool) + + // RemoteAddr returns the remote peer's IP address and port. + RemoteAddr() net.UDPAddr +} + +func (r *receiver) RemoteAddr() net.UDPAddr { return *r.addr } + +func (r *receiver) Size() (n int64, ok bool) { + if r.opts != nil { + if s, ok := r.opts["tsize"]; ok { + n, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return 0, false + } + return n, true + } + } + return 0, false +} + +type receiver struct { + send []byte + receive []byte + addr *net.UDPAddr + tid int + conn *net.UDPConn + block uint16 + retry *backoff + timeout time.Duration + retries int + l int + autoTerm bool + dally bool + mode string + opts options +} + +func (r *receiver) WriteTo(w io.Writer) (n int64, err error) { + if r.mode == "netascii" { + w = netascii.FromWriter(w) + } + if r.opts != nil { + err := r.sendOptions() + if err != nil { + r.abort(err) + return 0, err + } + } + binary.BigEndian.PutUint16(r.send[0:2], opACK) + for { + if r.l > 0 { + l, err := w.Write(r.receive[4:r.l]) + n += int64(l) + if err != nil { + r.abort(err) + return n, err + } + if r.l < len(r.receive) { + if r.autoTerm { + r.terminate() + r.conn.Close() + } + return n, nil + } + } + binary.BigEndian.PutUint16(r.send[2:4], r.block) + r.block++ // send ACK for current block and expect next one + ll, _, err := r.receiveWithRetry(4) + if err != nil { + r.abort(err) + return n, err + } + r.l = ll + } +} + +func (r *receiver) sendOptions() error { + for name, value := range r.opts { + if name == "blksize" { + err := r.setBlockSize(value) + if err != nil { + delete(r.opts, name) + continue + } + } else { + delete(r.opts, name) + } + } + if len(r.opts) > 0 { + m := packOACK(r.send, r.opts) + r.block = 1 // expect data block number 1 + ll, _, err := r.receiveWithRetry(m) + if err != nil { + r.abort(err) + return err + } + r.l = ll + } + return nil +} + +func (r *receiver) setBlockSize(blksize string) error { + n, err := strconv.Atoi(blksize) + if err != nil { + return err + } + if n < 512 { + return fmt.Errorf("blkzise too small: %d", n) + } + if n > 65464 { + return fmt.Errorf("blksize too large: %d", n) + } + r.receive = make([]byte, n+4) + return nil +} + +func (r *receiver) receiveWithRetry(l int) (int, *net.UDPAddr, error) { + r.retry.reset() + for { + n, addr, err := r.receiveDatagram(l) + if _, ok := err.(net.Error); ok && r.retry.count() < r.retries { + r.retry.backoff() + continue + } + return n, addr, err + } +} + +func (r *receiver) receiveDatagram(l int) (int, *net.UDPAddr, error) { + err := r.conn.SetReadDeadline(time.Now().Add(r.timeout)) + if err != nil { + return 0, nil, err + } + _, err = r.conn.WriteToUDP(r.send[:l], r.addr) + if err != nil { + return 0, nil, err + } + for { + c, addr, err := r.conn.ReadFromUDP(r.receive) + if err != nil { + return 0, nil, err + } + if !addr.IP.Equal(r.addr.IP) || (r.tid != 0 && addr.Port != r.tid) { + continue + } + p, err := parsePacket(r.receive[:c]) + if err != nil { + return 0, addr, err + } + r.tid = addr.Port + switch p := p.(type) { + case pDATA: + if p.block() == r.block { + return c, addr, nil + } + case pOACK: + opts, err := unpackOACK(p) + if r.block != 1 { + continue + } + if err != nil { + r.abort(err) + return 0, addr, err + } + for name, value := range opts { + if name == "blksize" { + err := r.setBlockSize(value) + if err != nil { + continue + } + } + } + r.block = 0 // ACK with block number 0 + r.opts = opts + return 0, addr, nil + case pERROR: + return 0, addr, fmt.Errorf("code: %d, message: %s", + p.code(), p.message()) + } + } +} + +func (r *receiver) terminate() error { + binary.BigEndian.PutUint16(r.send[2:4], r.block) + if r.dally { + for i := 0; i < 3; i++ { + _, _, err := r.receiveDatagram(4) + if err != nil { + return nil + } + } + return fmt.Errorf("dallying termination failed") + } else { + _, err := r.conn.WriteToUDP(r.send[:4], r.addr) + if err != nil { + return err + } + } + return nil +} + +func (r *receiver) abort(err error) error { + if r.conn == nil { + return nil + } + n := packERROR(r.send, 1, err.Error()) + _, err = r.conn.WriteToUDP(r.send[:n], r.addr) + if err != nil { + return err + } + r.conn.Close() + r.conn = nil + return nil +} diff --git a/vendor/github.com/pin/tftp/sender.go b/vendor/github.com/pin/tftp/sender.go new file mode 100644 index 0000000000000000000000000000000000000000..d018c4f0758229d415760734de3b1f65c8ae67e3 --- /dev/null +++ b/vendor/github.com/pin/tftp/sender.go @@ -0,0 +1,243 @@ +package tftp + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "strconv" + "time" + + "github.com/pin/tftp/netascii" +) + +// OutgoingTransfer provides methods to set the outgoing transfer size and +// retrieve the remote address of the peer. +type OutgoingTransfer interface { + // SetSize is used to set the outgoing transfer size (tsize option: RFC2349) + // manually in a server write transfer handler. + // + // It is not necessary in most cases; when the io.Reader provided to + // ReadFrom also satisfies io.Seeker (e.g. os.File) the transfer size will + // be determined automatically. Seek will not be attempted when the + // transfer size option is set with SetSize. + // + // The value provided will be used only if SetSize is called before ReadFrom + // and only on in a server read handler. + SetSize(n int64) + + // RemoteAddr returns the remote peer's IP address and port. + RemoteAddr() net.UDPAddr +} + +type sender struct { + conn *net.UDPConn + addr *net.UDPAddr + tid int + send []byte + receive []byte + retry *backoff + timeout time.Duration + retries int + block uint16 + mode string + opts options +} + +func (s *sender) RemoteAddr() net.UDPAddr { return *s.addr } + +func (s *sender) SetSize(n int64) { + if s.opts != nil { + if _, ok := s.opts["tsize"]; ok { + s.opts["tsize"] = strconv.FormatInt(n, 10) + } + } +} + +func (s *sender) ReadFrom(r io.Reader) (n int64, err error) { + if s.mode == "netascii" { + r = netascii.ToReader(r) + } + if s.opts != nil { + // check that tsize is set + if ts, ok := s.opts["tsize"]; ok { + // check that tsize is not set with SetSize already + i, err := strconv.ParseInt(ts, 10, 64) + if err == nil && i == 0 { + if rs, ok := r.(io.Seeker); ok { + pos, err := rs.Seek(0, 1) + if err != nil { + return 0, err + } + size, err := rs.Seek(0, 2) + if err != nil { + return 0, err + } + s.opts["tsize"] = strconv.FormatInt(size, 10) + _, err = rs.Seek(pos, 0) + if err != nil { + return 0, err + } + } + } + } + err = s.sendOptions() + if err != nil { + s.abort(err) + return 0, err + } + } + s.block = 1 // start data transmission with block 1 + binary.BigEndian.PutUint16(s.send[0:2], opDATA) + for { + l, err := io.ReadFull(r, s.send[4:]) + n += int64(l) + if err != nil && err != io.ErrUnexpectedEOF { + if err == io.EOF { + binary.BigEndian.PutUint16(s.send[2:4], s.block) + _, err = s.sendWithRetry(4) + if err != nil { + s.abort(err) + return n, err + } + s.conn.Close() + return n, nil + } + s.abort(err) + return n, err + } + binary.BigEndian.PutUint16(s.send[2:4], s.block) + _, err = s.sendWithRetry(4 + l) + if err != nil { + s.abort(err) + return n, err + } + if l < len(s.send)-4 { + s.conn.Close() + return n, nil + } + s.block++ + } +} + +func (s *sender) sendOptions() error { + for name, value := range s.opts { + if name == "blksize" { + err := s.setBlockSize(value) + if err != nil { + delete(s.opts, name) + continue + } + } else if name == "tsize" { + if value != "0" { + s.opts["tsize"] = value + } else { + delete(s.opts, name) + continue + } + } else { + delete(s.opts, name) + } + } + if len(s.opts) > 0 { + m := packOACK(s.send, s.opts) + _, err := s.sendWithRetry(m) + if err != nil { + return err + } + } + return nil +} + +func (s *sender) setBlockSize(blksize string) error { + n, err := strconv.Atoi(blksize) + if err != nil { + return err + } + if n < 512 { + return fmt.Errorf("blkzise too small: %d", n) + } + if n > 65464 { + return fmt.Errorf("blksize too large: %d", n) + } + s.send = make([]byte, n+4) + return nil +} + +func (s *sender) sendWithRetry(l int) (*net.UDPAddr, error) { + s.retry.reset() + for { + addr, err := s.sendDatagram(l) + if _, ok := err.(net.Error); ok && s.retry.count() < s.retries { + s.retry.backoff() + continue + } + return addr, err + } +} + +func (s *sender) sendDatagram(l int) (*net.UDPAddr, error) { + err := s.conn.SetReadDeadline(time.Now().Add(s.timeout)) + if err != nil { + return nil, err + } + _, err = s.conn.WriteToUDP(s.send[:l], s.addr) + if err != nil { + return nil, err + } + for { + n, addr, err := s.conn.ReadFromUDP(s.receive) + if err != nil { + return nil, err + } + if !addr.IP.Equal(s.addr.IP) || (s.tid != 0 && addr.Port != s.tid) { + continue + } + p, err := parsePacket(s.receive[:n]) + if err != nil { + continue + } + s.tid = addr.Port + switch p := p.(type) { + case pACK: + if p.block() == s.block { + return addr, nil + } + case pOACK: + opts, err := unpackOACK(p) + if s.block != 0 { + continue + } + if err != nil { + s.abort(err) + return addr, err + } + for name, value := range opts { + if name == "blksize" { + err := s.setBlockSize(value) + if err != nil { + continue + } + } + } + return addr, nil + case pERROR: + return nil, fmt.Errorf("sending block %d: code=%d, error: %s", + s.block, p.code(), p.message()) + } + } +} + +func (s *sender) abort(err error) error { + if s.conn == nil { + return nil + } + n := packERROR(s.send, 1, err.Error()) + _, err = s.conn.WriteToUDP(s.send[:n], s.addr) + if err != nil { + return err + } + s.conn.Close() + s.conn = nil + return nil +} diff --git a/vendor/github.com/pin/tftp/server.go b/vendor/github.com/pin/tftp/server.go new file mode 100644 index 0000000000000000000000000000000000000000..755119bf37bd815bda8ff781c4d86a724bd80cad --- /dev/null +++ b/vendor/github.com/pin/tftp/server.go @@ -0,0 +1,199 @@ +package tftp + +import ( + "fmt" + "io" + "net" + "sync" + "time" +) + +// NewServer creates TFTP server. It requires two functions to handle +// read and write requests. +// In case nil is provided for read or write handler the respective +// operation is disabled. +func NewServer(readHandler func(filename string, rf io.ReaderFrom) error, + writeHandler func(filename string, wt io.WriterTo) error) *Server { + return &Server{ + readHandler: readHandler, + writeHandler: writeHandler, + timeout: defaultTimeout, + retries: defaultRetries, + } +} + +type Server struct { + readHandler func(filename string, rf io.ReaderFrom) error + writeHandler func(filename string, wt io.WriterTo) error + backoff backoffFunc + conn *net.UDPConn + quit chan chan struct{} + wg sync.WaitGroup + timeout time.Duration + retries int +} + +// SetTimeout sets maximum time server waits for single network +// round-trip to succeed. +// Default is 5 seconds. +func (s *Server) SetTimeout(t time.Duration) { + if t <= 0 { + s.timeout = defaultTimeout + } else { + s.timeout = t + } +} + +// SetRetries sets maximum number of attempts server made to transmit a +// packet. +// Default is 5 attempts. +func (s *Server) SetRetries(count int) { + if count < 1 { + s.retries = defaultRetries + } else { + s.retries = count + } +} + +// SetBackoff sets a user provided function that is called to provide a +// backoff duration prior to retransmitting an unacknowledged packet. +func (s *Server) SetBackoff(h backoffFunc) { + s.backoff = h +} + +// ListenAndServe binds to address provided and start the server. +// ListenAndServe returns when Shutdown is called. +func (s *Server) ListenAndServe(addr string) error { + a, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return err + } + conn, err := net.ListenUDP("udp", a) + if err != nil { + return err + } + s.Serve(conn) + return nil +} + +// Serve starts server provided already opened UDP connecton. It is +// useful for the case when you want to run server in separate goroutine +// but still want to be able to handle any errors opening connection. +// Serve returns when Shutdown is called or connection is closed. +func (s *Server) Serve(conn *net.UDPConn) { + s.conn = conn + s.quit = make(chan chan struct{}) + for { + select { + case q := <-s.quit: + q <- struct{}{} + return + default: + err := s.processRequest(s.conn) + if err != nil { + // TODO: add logging handler + } + } + } +} + +// Shutdown make server stop listening for new requests, allows +// server to finish outstanding transfers and stops server. +func (s *Server) Shutdown() { + s.conn.Close() + q := make(chan struct{}) + s.quit <- q + <-q + s.wg.Wait() +} + +func (s *Server) processRequest(conn *net.UDPConn) error { + var buffer []byte + buffer = make([]byte, datagramLength) + n, remoteAddr, err := conn.ReadFromUDP(buffer) + if err != nil { + return fmt.Errorf("reading UDP: %v", err) + } + p, err := parsePacket(buffer[:n]) + if err != nil { + return err + } + switch p := p.(type) { + case pWRQ: + filename, mode, opts, err := unpackRQ(p) + if err != nil { + return fmt.Errorf("unpack WRQ: %v", err) + } + //fmt.Printf("got WRQ (filename=%s, mode=%s, opts=%v)\n", filename, mode, opts) + conn, err := net.ListenUDP("udp", &net.UDPAddr{}) + if err != nil { + return err + } + if err != nil { + return fmt.Errorf("open transmission: %v", err) + } + wt := &receiver{ + send: make([]byte, datagramLength), + receive: make([]byte, datagramLength), + conn: conn, + retry: &backoff{handler: s.backoff}, + timeout: s.timeout, + retries: s.retries, + addr: remoteAddr, + mode: mode, + opts: opts, + } + s.wg.Add(1) + go func() { + if s.writeHandler != nil { + err := s.writeHandler(filename, wt) + if err != nil { + wt.abort(err) + } else { + wt.terminate() + wt.conn.Close() + } + } else { + wt.abort(fmt.Errorf("server does not support write requests")) + } + s.wg.Done() + }() + case pRRQ: + filename, mode, opts, err := unpackRQ(p) + if err != nil { + return fmt.Errorf("unpack RRQ: %v", err) + } + //fmt.Printf("got RRQ (filename=%s, mode=%s, opts=%v)\n", filename, mode, opts) + conn, err := net.ListenUDP("udp", &net.UDPAddr{}) + if err != nil { + return err + } + rf := &sender{ + send: make([]byte, datagramLength), + receive: make([]byte, datagramLength), + tid: remoteAddr.Port, + conn: conn, + retry: &backoff{handler: s.backoff}, + timeout: s.timeout, + retries: s.retries, + addr: remoteAddr, + mode: mode, + opts: opts, + } + s.wg.Add(1) + go func() { + if s.readHandler != nil { + err := s.readHandler(filename, rf) + if err != nil { + rf.abort(err) + } + } else { + rf.abort(fmt.Errorf("server does not support read requests")) + } + s.wg.Done() + }() + default: + return fmt.Errorf("unexpected %T", p) + } + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 533811c5c4fcc1b6184cd2533d530c5464e4d6aa..c224d174412373943d431ffaad05fcf62be018ba 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -101,6 +101,13 @@ github.com/modern-go/reflect2 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit github.com/munnerz/goautoneg +# github.com/natefinch/lumberjack v2.0.0+incompatible +## explicit +github.com/natefinch/lumberjack +# github.com/pin/tftp v2.1.0+incompatible +## explicit +github.com/pin/tftp +github.com/pin/tftp/netascii # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors @@ -201,6 +208,8 @@ google.golang.org/protobuf/types/known/timestamppb # gopkg.in/inf.v0 v0.9.1 ## explicit gopkg.in/inf.v0 +# gopkg.in/natefinch/lumberjack.v2 v2.2.1 +## explicit; go 1.13 # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2