diff --git a/charts/openyurt/templates/yurthub-staticpod.yaml b/charts/openyurt/templates/yurthub-staticpod.yaml index a9ebd604db4..64596bf6e79 100644 --- a/charts/openyurt/templates/yurthub-staticpod.yaml +++ b/charts/openyurt/templates/yurthub-staticpod.yaml @@ -48,6 +48,9 @@ spec: {{`{{if .organizations }}`}} - --hub-cert-organizations={{`{{.organizations}}`}} {{`{{end}}`}} + {{`{{if .nodePoolName }}`}} + - --nodepool-name={{`{{.nodePoolName}}`}} + {{`{{end}}`}} livenessProbe: httpGet: host: {{ .Values.yurtHub.yurthubServerAddr | quote }} diff --git a/pkg/util/kubeconfig/kubeconfig.go b/pkg/util/kubeconfig/kubeconfig.go index 9625d2d3351..ca713622390 100644 --- a/pkg/util/kubeconfig/kubeconfig.go +++ b/pkg/util/kubeconfig/kubeconfig.go @@ -24,6 +24,8 @@ import ( clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + + yurtclientset "github.com/openyurtio/yurt-app-manager-api/pkg/yurtappmanager/client/clientset/versioned" ) // CreateBasic creates a basic, general KubeConfig object that then can be extended @@ -125,3 +127,18 @@ func GetAuthInfoFromKubeConfig(config *clientcmdapi.Config) *clientcmdapi.AuthIn } return nil } + +// ToYurtClientSet converts a KubeConfig object to a yurtClient +func ToYurtClientSet(config *clientcmdapi.Config) (yurtclientset.Interface, error) { + overrides := clientcmd.ConfigOverrides{Timeout: "10s"} + clientConfig, err := clientcmd.NewDefaultClientConfig(*config, &overrides).ClientConfig() + if err != nil { + return nil, errors.Wrap(err, "failed to create yurt client configuration from kubeconfig") + } + + client, err := yurtclientset.NewForConfig(clientConfig) + if err != nil { + return nil, errors.Wrap(err, "failed to create yurt client") + } + return client, nil +} diff --git a/pkg/util/kubernetes/kubeadm/app/util/apiclient/idempotency.go b/pkg/util/kubernetes/kubeadm/app/util/apiclient/idempotency.go index cf576c708b0..3603d510d94 100644 --- a/pkg/util/kubernetes/kubeadm/app/util/apiclient/idempotency.go +++ b/pkg/util/kubernetes/kubeadm/app/util/apiclient/idempotency.go @@ -29,6 +29,8 @@ import ( clientsetretry "k8s.io/client-go/util/retry" "github.com/openyurtio/openyurt/pkg/util/kubernetes/kubeadm/app/constants" + nodepoolv1alpha1 "github.com/openyurtio/yurt-app-manager-api/pkg/yurtappmanager/apis/apps/v1alpha1" + yurtclientset "github.com/openyurtio/yurt-app-manager-api/pkg/yurtappmanager/client/clientset/versioned" ) // ConfigMapMutator is a function that mutates the given ConfigMap and optionally returns an error @@ -131,3 +133,49 @@ func GetConfigMapWithRetry(client clientset.Interface, namespace, name string) ( } return nil, lastError } + +func GetNodePoolInfoWithRetry(client yurtclientset.Interface, name string) (*nodepoolv1alpha1.NodePool, error) { + var np *nodepoolv1alpha1.NodePool + var lastError error + err := wait.ExponentialBackoff(clientsetretry.DefaultBackoff, func() (bool, error) { + var err error + np, err = client.AppsV1alpha1().NodePools().Get(context.TODO(), name, metav1.GetOptions{}) + if err == nil { + return true, nil + } + if apierrors.IsNotFound(err) { + return true, nil + } + lastError = err + return false, nil + }) + if err == nil { + return np, nil + } + return nil, lastError +} + +func JoinNodeInSpecifiedNodePool(client clientset.Interface, nodeName, nodePoolName string) error { + var node *v1.Node + var lastError error + err := wait.ExponentialBackoff(clientsetretry.DefaultBackoff, func() (bool, error) { + var err error + node, err = client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}) + if err != nil { + lastError = err + return false, nil + } + + node.Labels[nodepoolv1alpha1.LabelDesiredNodePool] = nodePoolName + _, err = client.CoreV1().Nodes().Update(context.TODO(), node, metav1.UpdateOptions{}) + if err != nil { + lastError = err + return false, nil + } + return true, nil + }) + if err == nil { + return nil + } + return lastError +} diff --git a/pkg/yurtadm/cmd/join/join.go b/pkg/yurtadm/cmd/join/join.go index a37e613176a..47985c8fa51 100644 --- a/pkg/yurtadm/cmd/join/join.go +++ b/pkg/yurtadm/cmd/join/join.go @@ -31,6 +31,7 @@ import ( "k8s.io/klog/v2" kubeconfigutil "github.com/openyurtio/openyurt/pkg/util/kubeconfig" + "github.com/openyurtio/openyurt/pkg/util/kubernetes/kubeadm/app/util/apiclient" "github.com/openyurtio/openyurt/pkg/yurtadm/cmd/join/joindata" yurtphases "github.com/openyurtio/openyurt/pkg/yurtadm/cmd/join/phases" yurtconstants "github.com/openyurtio/openyurt/pkg/yurtadm/constants" @@ -42,6 +43,7 @@ type joinOptions struct { token string nodeType string nodeName string + nodePoolName string criSocket string organizations string pauseImage string @@ -124,6 +126,10 @@ func addJoinConfigFlags(flagSet *flag.FlagSet, joinOptions *joinOptions) { &joinOptions.namespace, yurtconstants.Namespace, joinOptions.namespace, `Specify the namespace of the yurthub staticpod configmap, if not specified, the namespace will be default.`, ) + flagSet.StringVar( + &joinOptions.nodePoolName, yurtconstants.NodePoolName, joinOptions.nodePoolName, + `Specify the nodePool name. if specified, that will add node into specified nodePool.`, + ) flagSet.StringVar( &joinOptions.criSocket, yurtconstants.NodeCRISocket, joinOptions.criSocket, "Path to the CRI socket to connect", @@ -273,6 +279,7 @@ func newJoinData(args []string, opt *joinOptions) (*joinData, error) { nodeLabels: make(map[string]string), joinNodeData: &joindata.NodeRegistration{ Name: name, + NodePoolName: opt.nodePoolName, WorkingMode: opt.nodeType, CRISocket: opt.criSocket, Organizations: opt.organizations, @@ -316,6 +323,21 @@ func newJoinData(args []string, opt *joinOptions) (*joinData, error) { return nil, err } data.kubernetesVersion = k8sVersion + + // check whether specified nodePool exists + if len(opt.nodePoolName) != 0 { + yurtClient, err := kubeconfigutil.ToYurtClientSet(cfg) + if err != nil { + klog.Errorf("failed to create yurt client, %v", err) + return nil, err + } + + np, err := apiclient.GetNodePoolInfoWithRetry(yurtClient, opt.nodePoolName) + if err != nil || np == nil { + // the specified nodePool not exist, return + return nil, errors.Errorf("when --nodepool-name is specified, the specified nodePool should be exist.") + } + } klog.Infof("node join data info: %#+v", *data) // get the yurthub template from the staticpod cr diff --git a/pkg/yurtadm/cmd/join/joindata/data.go b/pkg/yurtadm/cmd/join/joindata/data.go index 94490f061a9..7c967d3cf1a 100644 --- a/pkg/yurtadm/cmd/join/joindata/data.go +++ b/pkg/yurtadm/cmd/join/joindata/data.go @@ -24,6 +24,7 @@ import ( type NodeRegistration struct { Name string + NodePoolName string CRISocket string WorkingMode string Organizations string diff --git a/pkg/yurtadm/cmd/join/phases/postcheck.go b/pkg/yurtadm/cmd/join/phases/postcheck.go index a70cae1cb52..304f02766e5 100644 --- a/pkg/yurtadm/cmd/join/phases/postcheck.go +++ b/pkg/yurtadm/cmd/join/phases/postcheck.go @@ -19,12 +19,14 @@ package phases import ( "k8s.io/klog/v2" + "github.com/openyurtio/openyurt/pkg/util/kubernetes/kubeadm/app/util/apiclient" "github.com/openyurtio/openyurt/pkg/yurtadm/cmd/join/joindata" "github.com/openyurtio/openyurt/pkg/yurtadm/util/kubernetes" "github.com/openyurtio/openyurt/pkg/yurtadm/util/yurthub" ) -// RunPostCheck executes the node health check and clean process. +// RunPostCheck executes the node health check and clean process, +// if specified nodePool, it will join node in specified nodePool. func RunPostCheck(data joindata.YurtJoinData) error { klog.V(1).Infof("check kubelet status.") if err := kubernetes.CheckKubeletStatus(); err != nil { @@ -38,6 +40,15 @@ func RunPostCheck(data joindata.YurtJoinData) error { } klog.V(1).Infof("hub agent is ready") + if len(data.NodeRegistration().NodePoolName) != 0 { + klog.V(1).Infof("starting join node in specified nodePool.") + if err := apiclient.JoinNodeInSpecifiedNodePool(data.BootstrapClient(), + data.NodeRegistration().Name, data.NodeRegistration().NodePoolName); err != nil { + return err + } + klog.V(1).Infof("join node in specified nodePool successful.") + } + if err := yurthub.CleanHubBootstrapConfig(); err != nil { return err } diff --git a/pkg/yurtadm/constants/constants.go b/pkg/yurtadm/constants/constants.go index 9863f4ab80b..2ee40451583 100644 --- a/pkg/yurtadm/constants/constants.go +++ b/pkg/yurtadm/constants/constants.go @@ -83,6 +83,8 @@ const ( NodeLabels = "node-labels" // NodeName flag sets the node name. NodeName = "node-name" + // NodePoolName flag sets the nodePool name. + NodePoolName = "nodepool-name" // NodeType flag sets the type of worker node to edge or cloud. NodeType = "node-type" // Organizations flag sets the extra organizations of hub agent client certificate. @@ -237,6 +239,9 @@ spec: {{if .organizations }} - --hub-cert-organizations={{.organizations}} {{end}} + {{if .nodePoolName }} + - --nodepool-name={{.nodePoolName}} + {{end}} livenessProbe: httpGet: host: {{.yurthubServerAddr}} diff --git a/pkg/yurtadm/util/yurthub/yurthub.go b/pkg/yurtadm/util/yurthub/yurthub.go index d5558068f50..1db536eb5be 100644 --- a/pkg/yurtadm/util/yurthub/yurthub.go +++ b/pkg/yurtadm/util/yurthub/yurthub.go @@ -64,6 +64,9 @@ func AddYurthubStaticYaml(data joindata.YurtJoinData, podManifestPath string) er "workingMode": data.NodeRegistration().WorkingMode, "organizations": data.NodeRegistration().Organizations, } + if len(data.NodeRegistration().NodePoolName) != 0 { + ctx["nodePoolName"] = data.NodeRegistration().NodePoolName + } yurthubTemplate, err := templates.SubsituteTemplate(data.YurtHubTemplate(), ctx) if err != nil {