From d8fec51424906e16f551a78454624263e0a537f4 Mon Sep 17 00:00:00 2001 From: lauk001 Date: Thu, 18 Jan 2024 09:56:34 +0800 Subject: [PATCH] Optimize the deployment mode of network plugins --- cmd/command/opts/opts.go | 1 + cmd/command/setup_opts.go | 2 + cmd/deploy.go | 79 +++++++ cmd/upgrade.go | 2 +- data/assets_vfsdata.go | 10 +- .../install-cni-plugin.service.template | 18 -- docs/config_file_desc.md | 3 +- pkg/configmanager/asset/clusterasset.go | 5 +- pkg/ignition/common.go | 8 - pkg/kubeclient/ctl.go | 198 ++++++++---------- 10 files changed, 175 insertions(+), 151 deletions(-) delete mode 100644 data/data/ignition/controlplane/systemd/install-cni-plugin.service.template diff --git a/cmd/command/opts/opts.go b/cmd/command/opts/opts.go index d1c326c..e9d80a7 100644 --- a/cmd/command/opts/opts.go +++ b/cmd/command/opts/opts.go @@ -97,6 +97,7 @@ type WorkerConfig struct { type NetworkConfig struct { ServiceSubnet string PodSubnet string + Plugin string DNS DnsConfig } diff --git a/cmd/command/setup_opts.go b/cmd/command/setup_opts.go index e99fe1c..b498c12 100644 --- a/cmd/command/setup_opts.go +++ b/cmd/command/setup_opts.go @@ -51,6 +51,7 @@ func SetupDeployCmdOpts(deployCmd *cobra.Command) { flags.StringVarP(&opts.Opts.CertificateKey, "CertificateKey", "", "", "Specifies the certificate key to be added to the master node") flags.StringVarP(&opts.Opts.NetWork.ServiceSubnet, "service-subnet", "", "", "Specify the subnet for Kubernetes services") flags.StringVarP(&opts.Opts.NetWork.PodSubnet, "pod-subnet", "", "", "Specify the subnet for Kubernetes Pods.") + flags.StringVarP(&opts.Opts.NetWork.Plugin, "network-plugin-url", "", "", "URL for the network plugin") flags.StringVarP(&opts.Opts.NetWork.DNS.ImageVersion, "image-version", "", "", "Specify the version of the CoreDNS container image") flags.StringVarP(&opts.Opts.Housekeeper.ControllerImageUrl, "controller-image-url", "", "", "Specify the URL of the container image for the housekeeper controller component") flags.StringVarP(&opts.Opts.Housekeeper.OperatorImageUrl, "operator-image-url", "", "", "Specify the URL of the container image for the housekeeper operator component") @@ -107,6 +108,7 @@ func SetupTemplateCmdOpts(templateCmd *cobra.Command) { flags.StringVarP(&opts.Opts.CertificateKey, "CertificateKey", "", "", "Specifies the certificate key to be added to the master node") flags.StringVarP(&opts.Opts.NetWork.ServiceSubnet, "service-subnet", "", "", "Specify the subnet for Kubernetes services") flags.StringVarP(&opts.Opts.NetWork.PodSubnet, "pod-subnet", "", "", "Specify the subnet for Kubernetes Pods.") + flags.StringVarP(&opts.Opts.NetWork.Plugin, "network-plugin-url", "", "", "URL for the network plugin") flags.StringVarP(&opts.Opts.NetWork.DNS.ImageVersion, "image-version", "", "", "Specify the version of the CoreDNS container image") flags.StringVarP(&opts.Opts.Housekeeper.ControllerImageUrl, "controller-image-url", "", "", "Specify the URL of the container image for the housekeeper controller component") flags.StringVarP(&opts.Opts.Housekeeper.OperatorImageUrl, "operator-image-url", "", "", "Specify the URL of the container image for the housekeeper operator component") diff --git a/cmd/deploy.go b/cmd/deploy.go index 0d84f3a..a71367c 100755 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -17,6 +17,8 @@ package cmd import ( "context" + "fmt" + "io/ioutil" "nestos-kubernetes-deployer/cmd/command" "nestos-kubernetes-deployer/cmd/command/opts" "nestos-kubernetes-deployer/data" @@ -27,7 +29,10 @@ import ( "nestos-kubernetes-deployer/pkg/infra" "nestos-kubernetes-deployer/pkg/kubeclient" "nestos-kubernetes-deployer/pkg/utils" + "net/http" + "os" "path/filepath" + "strings" "time" "github.com/sirupsen/logrus" @@ -61,6 +66,9 @@ func runDeployCmd(cmd *cobra.Command, args []string) error { logrus.Errorf("Failed to get cluster config using the cluster id: %v", err) return err } + if !kubeclient.IsKubectlInstalled() { + return fmt.Errorf("kubectl is not installed") + } if err := deployCluster(config); err != nil { logrus.Errorf("Failed to deploy %s cluster: %v", clusterID, err) @@ -80,6 +88,7 @@ func deployCluster(conf *asset.ClusterAsset) error { logrus.Errorf("Failed to get cluster deploy config: %v", err) return err } + if err := createCluster(conf); err != nil { logrus.Errorf("Failed to create cluster: %v", err) return err @@ -97,6 +106,14 @@ func deployCluster(conf *asset.ClusterAsset) error { return err } + os.Setenv("KUBECONFIG", configPath) // set kubeconfig environment variable + // apply network plugin + if err := applyNetworkPlugin(conf.Network.Plugin); err != nil { + logrus.Errorf("Failed to apply network plugin: %v", err) + return err + } + logrus.Info("Network plugin deployment completed successfully.") + if conf.Housekeeper.DeployHousekeeper { logrus.Info("Starting deployment of Housekeeper...") if err := deployHousekeeper(conf.Housekeeper, configPath); err != nil { @@ -310,3 +327,65 @@ func deployHousekeeper(tmplData interface{}, kubeconfig string) error { } return nil } + +func applyNetworkPlugin(pluginConfigPath string) error { + var content []byte + var err error + + // Check if the pluginConfigPath is an HTTP(S) link or a local file path + if strings.HasPrefix(pluginConfigPath, "http://") || strings.HasPrefix(pluginConfigPath, "https://") { + response, err := http.Get(pluginConfigPath) + if err != nil { + logrus.Errorf("Failed to fetch network plugin configuration from URL: %v", err) + return err + } + defer response.Body.Close() + + content, err = ioutil.ReadAll(response.Body) + if err != nil { + logrus.Errorf("Failed to read content from HTTP response: %v", err) + return err + } + } else { + // Read the content from the local file + content, err = ioutil.ReadFile(pluginConfigPath) + if err != nil { + logrus.Errorf("Failed to read network plugin configuration file: %v", err) + return err + } + } + + // 在类似NestOS 或者 Fedora CoreOS 这类不可变基础设施中,目录/usr为只读目录。在支持FlexVolume时,默认路径为 + // "/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") { + content = []byte(strings.ReplaceAll(string(content), + "/usr/libexec/kubernetes/kubelet-plugins", + "/opt/libexec/kubernetes/kubelet-plugins")) + } + + // Save the modified content to a file in the "/tmp" directory with a fixed name + tmpFilePath := "/tmp/modified-plugin-config.yaml" + + err = ioutil.WriteFile(tmpFilePath, content, 0644) + if err != nil { + logrus.Errorf("Failed to write content to file: %v", err) + return err + } + + // Apply the modified configuration using kubeclient + if err := kubeclient.RunKubectlApplyWithYaml(tmpFilePath); err != nil { + logrus.Errorf("Failed to apply network plugin configuration: %v", err) + return err + } + + // removal of the temporary file + defer func() { + if err := os.Remove(tmpFilePath); err != nil { + logrus.Errorf("Failed to remove temporary file: %v", err) + } + }() + + return nil +} diff --git a/cmd/upgrade.go b/cmd/upgrade.go index f1d3fb6..2075df2 100755 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -99,7 +99,7 @@ spec: `, clusterConfig.Housekeeper.OSImageURL, clusterConfig.Housekeeper.KubeVersion, clusterConfig.Housekeeper.EvictPodForce, clusterConfig.Housekeeper.MaxUnavailable) adminconfig := filepath.Join(configmanager.GetPersistDir(), clusterConfig.Cluster_ID, "admin.config") - if err := kubeclient.DeployCR(yamlData, adminconfig); err != nil { + if err := kubeclient.ApplyHousekeeperCR(yamlData, adminconfig); err != nil { logrus.Errorf("Failed to deploy Custom Resource: %v", err) return err } diff --git a/data/assets_vfsdata.go b/data/assets_vfsdata.go index 6cb20d1..993921c 100644 --- a/data/assets_vfsdata.go +++ b/data/assets_vfsdata.go @@ -155,7 +155,7 @@ var Assets = func() http.FileSystem { }, "/ignition/controlplane/systemd": &vfsgen۰DirInfo{ name: "systemd", - modTime: time.Date(2024, 1, 16, 1, 0, 41, 883548620, time.UTC), + modTime: time.Date(2024, 1, 16, 6, 16, 45, 185790140, time.UTC), }, "/ignition/controlplane/systemd/disable-selinux.service": &vfsgen۰CompressedFileInfo{ name: "disable-selinux.service", @@ -171,13 +171,6 @@ var Assets = func() http.FileSystem { compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xac\x51\x3b\x6e\xc3\x30\x0c\xdd\x75\x0a\xf7\x00\x8a\x4e\xa0\xa1\x9f\x0c\xdd\x8a\xa4\x45\x87\x20\x03\x23\x31\x0e\x11\x99\x72\x45\x2a\xb0\x6f\x5f\x38\x4d\x8d\x02\x41\xd3\xa5\x2b\xc9\xc7\xf7\xdb\xbc\x31\xe9\xd6\x3c\xa1\x84\x42\xbd\x52\x66\x4f\x4c\xda\x1c\xeb\x0e\x0b\xa3\xa2\x34\x21\x55\x51\x2c\x66\x85\x1f\x95\x0a\x8a\x17\x54\x7b\x9c\xb6\xc9\xf6\x50\x60\x21\x58\x4e\x14\xb0\x89\x24\xb0\x4b\x68\x05\x13\x71\x1d\xe6\x79\xc1\x84\x20\x68\xa9\x83\x16\x6d\x4f\xa7\xac\xdf\x3b\x73\xbf\x57\x2c\xff\xfa\xf1\x31\x73\xa4\xc9\xc8\x0b\xe8\x61\x39\x90\xa8\x78\x77\x82\xe2\x52\x6e\xdd\x15\x91\x42\xd7\xdf\x86\x5c\x69\xf8\x1b\xc2\x39\xce\xaa\x7e\xbd\xbe\x9b\xcf\xa7\xc0\xed\x25\xe5\x0b\xc0\x6c\xd6\x5f\x76\xb6\xe6\x75\xec\xd1\x67\x46\x39\x64\x35\x2b\xec\x80\xf8\x1c\xda\x72\x20\xf5\x23\x8a\x59\x0e\x18\xd6\x0a\x45\xfd\x54\x1a\xc4\xae\x39\x37\x68\x6d\xc8\xbc\xa7\xd6\x3b\xd4\xe0\xf8\x18\x2f\x3c\xe7\xe1\x62\x84\x2e\x35\xd6\xd6\x3e\x65\x88\x36\x60\xd1\x9f\x8f\xdc\x8e\xd8\x69\xae\xe1\xd0\xdc\x56\xf9\xcc\xa2\x90\xd2\xd6\xbc\x03\x2b\xc6\x87\xd1\x77\x35\x29\xd9\x2a\x58\x16\x0a\xa5\x45\xfd\x0c\x00\x00\xff\xff\x89\x0d\xc2\xdf\x62\x02\x00\x00"), }, - "/ignition/controlplane/systemd/install-cni-plugin.service.template": &vfsgen۰CompressedFileInfo{ - name: "install-cni-plugin.service.template", - modTime: time.Date(2024, 1, 16, 1, 0, 41, 883548620, time.UTC), - uncompressedSize: 756, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x9c\x92\x4f\x6b\x22\x41\x10\xc5\xef\xf3\x29\x6a\xf5\x90\x53\x4f\xc7\x65\x09\xbb\x81\x39\x64\x8d\x87\x81\x80\xa2\xfb\xe7\x10\x25\xb4\x3d\xe5\x58\xd8\x53\x3d\xdb\x5d\xed\x3a\x84\x7c\xf7\x45\x0d\xc4\x05\x09\x92\x63\xf3\x7b\xaf\xde\x83\x7e\x8f\x3f\x99\x64\x91\xdd\x63\xb4\x81\x5a\x21\xcf\x05\x71\x14\xe3\x1c\x58\x26\x60\x94\xbf\x3e\x6c\xa0\x75\xa9\x26\x86\x95\x0f\xb0\x49\x4b\x0c\x8c\x82\x31\x9b\xe2\x9f\x44\x01\x63\x41\x4c\xa2\xac\x4b\x51\x30\xe4\x11\xc3\x96\x2c\x66\x77\x2b\xc1\x70\x1e\x0d\x3d\x57\xb4\x0f\x9b\x18\x59\x8f\x76\x14\x25\x16\x7a\x6b\x82\x76\xbe\xd6\xff\x3b\xc4\x34\xed\x59\xfd\xa7\x13\xc3\xa1\xb0\xb2\x4c\xea\x58\xf4\xd5\x96\x3d\xce\x8e\x81\x8b\xec\x47\xd7\x62\xe1\x19\xe3\xda\x4b\x36\xc5\xc6\x10\x1f\xfa\x8d\x76\x24\x45\x87\x31\x1b\xed\xd0\xce\xc4\x04\x29\x96\x26\xae\x41\x59\xe8\x45\xac\x40\x11\x5c\xc5\x7e\x8a\x41\x3b\x5a\xe2\x0e\xad\xee\xfb\x56\xde\x1e\xf5\x15\x68\x14\xab\x79\x53\x69\x6b\x1c\x59\x9f\x77\xa6\x71\xbd\x77\xef\xe9\x3e\x28\x60\xd3\xe0\x2d\x0c\xef\x1e\xca\xe1\xf8\xa9\x9c\xfc\xfa\x32\x19\x8f\x1f\x9e\x86\xe5\xfd\x54\xbf\x0b\x3f\x9a\x08\xb0\x35\x2e\xe1\x2d\xcc\x7b\x83\x6f\x9f\xf3\xc1\xcd\xd7\xfc\x3a\xbf\x9e\xeb\xc1\xcd\xbc\xa7\x4f\xe0\xf3\x73\x5e\xb6\x33\xac\x1b\x64\x79\x79\x79\xe5\x17\x84\xee\x77\x61\xc5\x81\x69\x5b\xd7\x81\x5a\x9d\x35\x80\x52\x07\x9d\xe7\x15\xd5\xc5\x41\xf1\xb6\x27\x6d\xaa\x86\x38\xdf\xc3\x93\xbb\x7a\x49\xac\xc5\x27\xbb\x86\x4b\x7e\xbc\x3c\xa2\x45\xf6\xdb\xb0\x60\xf5\xbd\x2b\x9a\xe4\x84\x54\x8a\x18\x72\x31\xa1\x46\xf9\x17\x00\x00\xff\xff\xb3\x23\x14\x67\xf4\x02\x00\x00"), - }, "/ignition/controlplane/systemd/kubelet.service": &vfsgen۰CompressedFileInfo{ name: "kubelet.service", modTime: time.Date(2024, 1, 16, 1, 0, 41, 883548620, time.UTC), @@ -527,7 +520,6 @@ var Assets = func() http.FileSystem { fs["/ignition/controlplane/systemd"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ fs["/ignition/controlplane/systemd/disable-selinux.service"].(os.FileInfo), fs["/ignition/controlplane/systemd/init-cluster.service"].(os.FileInfo), - fs["/ignition/controlplane/systemd/install-cni-plugin.service.template"].(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), diff --git a/data/data/ignition/controlplane/systemd/install-cni-plugin.service.template b/data/data/ignition/controlplane/systemd/install-cni-plugin.service.template deleted file mode 100644 index 4126148..0000000 --- a/data/data/ignition/controlplane/systemd/install-cni-plugin.service.template +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=install cni network plugin for kubernetes -Requires=init-cluster.service -After=init-cluster.service -ConditionPathExists=/var/log/init-cluster.stamp -ConditionPathExists=!/var/log/install-cni-plugin.stamp - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=bash -c "sed -i 's#usr/libexec/#opt/libexec/#g' /etc/nkd/calico.yaml" -ExecStart=bash -c "sed -i 's/# - name: CALICO_IPV4POOL_CIDR/- name: CALICO_IPV4POOL_CIDR/g' /etc/nkd/calico.yaml" -ExecStart=bash -c "sed -i 's/# value: \"192.168.0.0\/16\"/ value: \"{{.IpSegment}}\/16\"/g' /etc/nkd/calico.yaml" -ExecStart=kubectl apply -f /etc/nkd/calico.yaml --kubeconfig=/etc/kubernetes/admin.conf -ExecStart=/bin/touch /var/log/install-cni-plugin.stamp - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/docs/config_file_desc.md b/docs/config_file_desc.md index babee9e..0dc33cd 100644 --- a/docs/config_file_desc.md +++ b/docs/config_file_desc.md @@ -40,7 +40,8 @@ kubernetes: # 集群相关配置列表 certificatekey: "" # 添加新的控制面节点时用来解密所下载的Secret中的证书的秘钥 network: # k8s集群网络配置 service_subnet: "10.96.0.0/16" # k8s创建的service的IP地址网段 - pod_subnet: "10.100.0.0/16" # k8s集群网络的IP地址网段 + pod_subnet: "10.244.0.0/16" # k8s集群网络的IP地址网段 + plugin: https://projectcalico.docs.tigera.io/archive/v3.22/manifests/calico.yaml # 网络插件 coredns_image_version: "v1.8.6" # coredns镜像版本 housekeeper: # housekeeper相关配置列表 deployhousekeeper: false # 是否部署housekeeper diff --git a/pkg/configmanager/asset/clusterasset.go b/pkg/configmanager/asset/clusterasset.go index 856223d..e7ed5a9 100644 --- a/pkg/configmanager/asset/clusterasset.go +++ b/pkg/configmanager/asset/clusterasset.go @@ -199,6 +199,7 @@ type Kubernetes struct { type Network struct { Service_Subnet string Pod_Subnet string + Plugin string CoreDNS_Image_Version string } @@ -296,6 +297,7 @@ func (clusterAsset *ClusterAsset) InitClusterAsset(infraAsset InfraAsset, opts * setStringValue(&clusterAsset.Kubernetes.Token, opts.Token, cf.Token) setStringValue(&clusterAsset.Kubernetes.Network.Service_Subnet, opts.NetWork.ServiceSubnet, cf.Service_Subnet) setStringValue(&clusterAsset.Kubernetes.Network.Pod_Subnet, opts.NetWork.PodSubnet, cf.Network.Pod_Subnet) + setStringValue(&clusterAsset.Kubernetes.Network.Plugin, opts.NetWork.Plugin, cf.Network.Plugin) setStringValue(&clusterAsset.Kubernetes.Network.CoreDNS_Image_Version, opts.NetWork.DNS.ImageVersion, cf.Network.CoreDNS_Image_Version) if clusterAsset.Housekeeper.DeployHousekeeper || opts.Housekeeper.DeployHousekeeper { @@ -388,7 +390,8 @@ func GetDefaultClusterConfig(arch string) (*ClusterAsset, error) { CertificateKey: "a301c9c55596c54c5d4c7173aa1e3b6fd304130b0c703bb23149c0c69f94b8e0", Network: Network{ Service_Subnet: "10.96.0.0/16", - Pod_Subnet: "10.100.0.0/16", + Pod_Subnet: "10.244.0.0/16", + Plugin: "https://projectcalico.docs.tigera.io/archive/v3.22/manifests/calico.yaml", CoreDNS_Image_Version: "v1.8.6", }, }, diff --git a/pkg/ignition/common.go b/pkg/ignition/common.go index a366fa3..82005b6 100644 --- a/pkg/ignition/common.go +++ b/pkg/ignition/common.go @@ -20,7 +20,6 @@ import ( "nestos-kubernetes-deployer/data" "nestos-kubernetes-deployer/pkg/configmanager/asset" "nestos-kubernetes-deployer/pkg/utils" - "net" "path" "strings" @@ -35,7 +34,6 @@ var ( "set-kernel-para.service", "disable-selinux.service", "init-cluster.service", - "install-cni-plugin.service", "join-master.service", "release-image-pivot.service", "join-worker.service", @@ -52,7 +50,6 @@ type TmplData struct { PodSubnet string Token string CorednsImageTag string - IpSegment string ReleaseImageURl string CertificateKey string Hsip string //HostName + IP @@ -203,10 +200,6 @@ func appendSystemdUnits(config *igntypes.Config, uri string, tmplData interface{ func GetTmplData(c *asset.ClusterAsset) *TmplData { var hsip string - ip := net.ParseIP(c.Master[0].IP) - ipSegment := ip.To4() - ipSegment[2] = 0 - ipSegment[3] = 0 for i := 0; i < len(c.Master); i++ { temp := c.Master[i].IP + " " + c.Master[i].Hostname + "\n" hsip = hsip + temp @@ -221,7 +214,6 @@ func GetTmplData(c *asset.ClusterAsset) *TmplData { PodSubnet: c.Network.Pod_Subnet, Token: c.Kubernetes.Token, CorednsImageTag: c.Network.CoreDNS_Image_Version, - IpSegment: ipSegment.String(), ReleaseImageURl: c.Kubernetes.Release_Image_URL, CertificateKey: c.Kubernetes.CertificateKey, Hsip: hsip, diff --git a/pkg/kubeclient/ctl.go b/pkg/kubeclient/ctl.go index 2c27dfe..10eebaf 100644 --- a/pkg/kubeclient/ctl.go +++ b/pkg/kubeclient/ctl.go @@ -17,6 +17,7 @@ package kubeclient import ( "context" + "os/exec" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" @@ -32,6 +33,28 @@ import ( "sigs.k8s.io/yaml" ) +const ( + // Constants for CRD API groups, versions, and resources + CRDAPIGroup = "apiextensions.k8s.io" + CRDAPIVersion = "v1" + CRDResource = "customresourcedefinitions" + + // custom resource + HousekeeperAPIGroup = "housekeeper.io" + HousekeeperAPIVersion = "v1alpha1" + HousekeeperResource = "updates" + + // NAMESPACE + NSResource = "namespaces" + NSAPIVersion = "v1" + + // RBAC + RBACAPIGroup = "rbac.authorization.k8s.io" + RBACAPIVersion = "v1" + ClusterRolesResource = "clusterroles" + ClusterRoleBindingsResource = "clusterrolebindings" +) + // CreateClient creates a Kubernetes clientset. // Parameters: // - kubeconfig: Path to the kubeconfig file. @@ -78,136 +101,70 @@ func CreateDynamicClient(kubeconfig string) (dynamic.Interface, error) { return dynamicClient, nil } -func DeployCRD(yamlContent string, kubeconfig string) error { - client, err := CreateDynamicClient(kubeconfig) - if err != nil { - return err - } - - // Parse YAML into CustomResourceDefinition +// parseYAMLToUnstructured parses YAML into Unstructured object +func parseYAMLToUnstructured(yamlContent string) (*unstructured.Unstructured, error) { unstructuredObj := &unstructured.Unstructured{} if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { logrus.Errorf("Error parsing YAML as Unstructured: %v", err) - return err - } - - // Specify the API group, version, and resource for CustomResourceDefinitions - apiGroup := "apiextensions.k8s.io" - apiVersion := "v1" - resource := "customresourcedefinitions" - - // Create the CRD using the dynamic client - _, err = client.Resource(schema.GroupVersionResource{ - Group: apiGroup, - Version: apiVersion, - Resource: resource, - }).Create(context.TODO(), unstructuredObj, metav1.CreateOptions{}) - if err != nil { - logrus.Errorf("error creating CRD: %v", err) - return err + return nil, err } - - return nil + return unstructuredObj, nil } -func DeployNamespace(yamlContent string, kubeconfig string) error { +// deployResource deploys a resource using dynamic client +func deployResource(yamlContent, kubeconfig string, apiGroup, apiVersion, resource string) error { client, err := CreateDynamicClient(kubeconfig) if err != nil { - logrus.Errorf("error creating dynamic client: %v", err) return err } - // Parse YAML content into Unstructured object - unstructuredObj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { - logrus.Errorf("error converting YAML to Unstructured: %v", err) + unstructuredObj, err := parseYAMLToUnstructured(yamlContent) + if err != nil { return err } - // Specify the API group, version, and resource for Namespaces - apiGroup, apiVersion, resource := "", "v1", "namespaces" - - // Create the Namespace using the dynamic client _, err = client.Resource(schema.GroupVersionResource{ Group: apiGroup, Version: apiVersion, Resource: resource, }).Create(context.TODO(), unstructuredObj, metav1.CreateOptions{}) if err != nil { - logrus.Errorf("error creating Namespace: %v", err) + logrus.Errorf("Error creating resource %s: %v", resource, err) return err } return nil } -func DeployClusterRole(yamlContent string, kubeconfig string) error { - client, err := CreateDynamicClient(kubeconfig) - if err != nil { - return err - } - - unstructuredObj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { - logrus.Errorf("Error parsing YAML as Unstructured: %v", err) - return err - } +// DeployCRD deploys a CustomResourceDefinition. +func DeployCRD(yamlContent string, kubeconfig string) error { + return deployResource(yamlContent, kubeconfig, CRDAPIGroup, CRDAPIVersion, CRDResource) +} - apiGroup := "rbac.authorization.k8s.io" - apiVersion := "v1" - resource := "clusterroles" +// DeployNamespace deploys a Namespace. +func DeployNamespace(yamlContent string, kubeconfig string) error { + return deployResource(yamlContent, kubeconfig, "", NSAPIVersion, NSResource) +} - _, err = client.Resource(schema.GroupVersionResource{ - Group: apiGroup, - Version: apiVersion, - Resource: resource, - }).Create(context.TODO(), unstructuredObj, metav1.CreateOptions{}) - if err != nil { - logrus.Errorf("error creating CRD: %v", err) - return err - } - return nil +// DeployClusterRole deploys a ClusterRole. +func DeployClusterRole(yamlContent string, kubeconfig string) error { + return deployResource(yamlContent, kubeconfig, RBACAPIGroup, RBACAPIVersion, ClusterRolesResource) } +// DeployClusterRoleBinding deploys a ClusterRoleBinding. func DeployClusterRoleBinding(yamlContent string, kubeconfig string) error { - client, err := CreateDynamicClient(kubeconfig) - if err != nil { - return err - } - - unstructuredObj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { - logrus.Errorf("Error parsing YAML as Unstructured: %v", err) - return err - } - - apiGroup := "rbac.authorization.k8s.io" - apiVersion := "v1" - resource := "clusterrolebindings" - - _, err = client.Resource(schema.GroupVersionResource{ - Group: apiGroup, - Version: apiVersion, - Resource: resource, - }).Create(context.TODO(), unstructuredObj, metav1.CreateOptions{}) - if err != nil { - logrus.Errorf("error creating CRD: %v", err) - return err - } - - return nil + return deployResource(yamlContent, kubeconfig, RBACAPIGroup, RBACAPIVersion, ClusterRoleBindingsResource) } +// DeployDeployment deploys a Deployment. func DeployDeployment(yamlContent string, kubeconfig string, namespace string) error { clientset, err := CreateClient(kubeconfig) if err != nil { return err } - // Parse YAML content into Unstructured object - unstructuredObj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { - logrus.Errorf("error converting YAML to Unstructured: %v", err) + unstructuredObj, err := parseYAMLToUnstructured(yamlContent) + if err != nil { return err } @@ -228,16 +185,15 @@ func DeployDeployment(yamlContent string, kubeconfig string, namespace string) e return nil } +// DeployDaemonSet deploys a DaemonSet. func DeployDaemonSet(yamlContent string, kubeconfig string, namespace string) error { clientset, err := CreateClient(kubeconfig) if err != nil { return err } - // Parse YAML content into Unstructured object - unstructuredObj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { - logrus.Errorf("error converting YAML to Unstructured: %v", err) + unstructuredObj, err := parseYAMLToUnstructured(yamlContent) + if err != nil { return err } @@ -258,7 +214,7 @@ func DeployDaemonSet(yamlContent string, kubeconfig string, namespace string) er return nil } -func DeployCR(yamlContent string, kubeconfig string) error { +func ApplyHousekeeperCR(yamlContent string, kubeconfig string) error { // Create a dynamic client for interacting with the Kubernetes API server client, err := CreateDynamicClient(kubeconfig) if err != nil { @@ -266,23 +222,17 @@ func DeployCR(yamlContent string, kubeconfig string) error { } // Parse the YAML content into an Unstructured object - unstructuredObj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(yamlContent), unstructuredObj); err != nil { - logrus.Errorf("Error parsing YAML as Unstructured: %v", err) + unstructuredObj, err := parseYAMLToUnstructured(yamlContent) + if err != nil { return err } - // Specify the API group, version, and resource for the custom resource - apiGroup := "housekeeper.io" - apiVersion := "v1alpha1" - resource := "updates" - // Try to get the existing custom resource existingObj, err := client. Resource(schema.GroupVersionResource{ - Group: apiGroup, - Version: apiVersion, - Resource: resource, + Group: HousekeeperAPIGroup, + Version: HousekeeperAPIVersion, + Resource: HousekeeperResource, }). Namespace(unstructuredObj.GetNamespace()). Get(context.TODO(), unstructuredObj.GetName(), metav1.GetOptions{}) @@ -292,9 +242,9 @@ func DeployCR(yamlContent string, kubeconfig string) error { // Custom resource doesn't exist, create it _, err = client. Resource(schema.GroupVersionResource{ - Group: apiGroup, - Version: apiVersion, - Resource: resource, + Group: HousekeeperAPIGroup, + Version: HousekeeperAPIVersion, + Resource: HousekeeperResource, }). Namespace(unstructuredObj.GetNamespace()). Create(context.TODO(), unstructuredObj, metav1.CreateOptions{}) @@ -314,9 +264,9 @@ func DeployCR(yamlContent string, kubeconfig string) error { unstructuredObj.SetResourceVersion(existingObj.GetResourceVersion()) _, err = client. Resource(schema.GroupVersionResource{ - Group: apiGroup, - Version: apiVersion, - Resource: resource, + Group: HousekeeperAPIGroup, + Version: HousekeeperAPIVersion, + Resource: HousekeeperResource, }). Namespace(unstructuredObj.GetNamespace()). Update(context.TODO(), unstructuredObj, metav1.UpdateOptions{}) @@ -327,3 +277,25 @@ func DeployCR(yamlContent string, kubeconfig string) error { return nil } + +func RunKubectlApplyWithYaml(yamlFilePath string) error { + kubectlArgs := []string{"apply", "-f", yamlFilePath} + cmd := exec.Command("kubectl", kubectlArgs...) + // cmd.Stdout = os.Stdout + // cmd.Stderr = os.Stderr + + // run kubectl apply + err := cmd.Run() + if err != nil { + logrus.Errorf("Error executing kubectl apply: %v", err) + return err + } + + return nil +} + +// isKubectlInstalled checks if kubectl is installed on the system. +func IsKubectlInstalled() bool { + _, err := exec.LookPath("kubectl") + return err == nil +} -- Gitee