Use the kube-system UID to identify if the member cluster already exists

This commit is contained in:
Xinzhao Xu
2022-01-27 11:27:41 +08:00
parent 794129d056
commit d3ed81059f
4 changed files with 55 additions and 93 deletions

View File

@@ -576,7 +576,6 @@ func (c *clusterController) syncCluster(key string) error {
klog.Errorf("Failed to get kubernetes version, %#v", err)
return err
}
cluster.Status.KubernetesVersion = version.GitVersion
nodes, err := clusterDt.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
@@ -584,7 +583,6 @@ func (c *clusterController) syncCluster(key string) error {
klog.Errorf("Failed to get cluster nodes, %#v", err)
return err
}
cluster.Status.NodeCount = len(nodes.Items)
configz, err := c.tryToFetchKubeSphereComponents(clusterDt.config.Host, clusterDt.transport)
@@ -599,6 +597,13 @@ func (c *clusterController) syncCluster(key string) error {
cluster.Status.KubeSphereVersion = v
}
// Use kube-system namespace UID as cluster ID
kubeSystem, err := clusterDt.client.CoreV1().Namespaces().Get(context.TODO(), metav1.NamespaceSystem, metav1.GetOptions{})
if err != nil {
return err
}
cluster.Status.UID = kubeSystem.UID
// label cluster host cluster if configz["multicluster"]==true
if mc, ok := configz[configzMultiCluster]; ok && mc && c.checkIfClusterIsHostCluster(nodes) {
if cluster.Labels == nil {

View File

@@ -301,13 +301,13 @@ func (h *handler) updateKubeConfig(request *restful.Request, response *restful.R
return
}
_, err = validateKubeSphereAPIServer(cluster.Spec.Connection.KubeSphereAPIEndpoint, cluster.Spec.Connection.KubeConfig)
_, err = validateKubeSphereAPIServer(config)
if err != nil {
api.HandleBadRequest(response, request, fmt.Errorf("unable validate kubesphere endpoint, %v", err))
return
}
err = h.validateMemberClusterConfiguration(cluster.Spec.Connection.KubeConfig)
err = h.validateMemberClusterConfiguration(clientSet)
if err != nil {
api.HandleBadRequest(response, request, fmt.Errorf("failed to validate member cluster configuration, err: %v", err))
}
@@ -340,20 +340,29 @@ func (h *handler) validateCluster(request *restful.Request, response *restful.Re
return
}
err = h.validateKubeConfig(cluster.Spec.Connection.KubeConfig)
config, err := k8sutil.LoadKubeConfigFromBytes(cluster.Spec.Connection.KubeConfig)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
config.Timeout = defaultTimeout
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
_, err = validateKubeSphereAPIServer(cluster.Spec.Connection.KubeSphereAPIEndpoint, cluster.Spec.Connection.KubeConfig)
if err != nil {
if err = h.validateKubeConfig(cluster.Name, clientSet); err != nil {
api.HandleBadRequest(response, request, err)
return
}
if _, err = validateKubeSphereAPIServer(config); err != nil {
api.HandleBadRequest(response, request, fmt.Errorf("unable validate kubesphere endpoint, %v", err))
return
}
err = h.validateMemberClusterConfiguration(cluster.Spec.Connection.KubeConfig)
if err != nil {
if err = h.validateMemberClusterConfiguration(clientSet); err != nil {
api.HandleBadRequest(response, request, fmt.Errorf("failed to validate member cluster configuration, err: %v", err))
}
@@ -361,8 +370,8 @@ func (h *handler) validateCluster(request *restful.Request, response *restful.Re
}
// validateKubeConfig takes base64 encoded kubeconfig and check its validity
func (h *handler) validateKubeConfig(kubeconfig []byte) error {
config, err := k8sutil.LoadKubeConfigFromBytes(kubeconfig)
func (h *handler) validateKubeConfig(clusterName string, clientSet kubernetes.Interface) error {
kubeSystem, err := clientSet.CoreV1().Namespaces().Get(context.TODO(), metav1.NamespaceSystem, metav1.GetOptions{})
if err != nil {
return err
}
@@ -372,60 +381,30 @@ func (h *handler) validateKubeConfig(kubeconfig []byte) error {
return err
}
// clusters with the exactly same KubernetesAPIEndpoint considered to be one
// clusters with the exactly same kube-system namespace UID considered to be one
// MUST not import the same cluster twice
for _, cluster := range clusters {
if len(cluster.Spec.Connection.KubernetesAPIEndpoint) != 0 && cluster.Spec.Connection.KubernetesAPIEndpoint == config.Host {
return fmt.Errorf("existing cluster %s with the exacty same server address, MUST not import the same cluster twice", cluster.Name)
for _, existedCluster := range clusters {
if existedCluster.Status.UID == kubeSystem.UID {
return fmt.Errorf("cluster %s already exists (%s), MUST not import the same cluster twice", clusterName, existedCluster.Name)
}
}
config.Timeout = defaultTimeout
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
return err
}
_, err = clientSet.Discovery().ServerVersion()
return err
}
// validateKubeSphereAPIServer uses version api to check the accessibility
// If kubesphere apiserver endpoint is not provided, use kube-apiserver proxy instead
func validateKubeSphereAPIServer(ksEndpoint string, kubeconfig []byte) (*version.Info, error) {
if len(ksEndpoint) == 0 && len(kubeconfig) == 0 {
return nil, fmt.Errorf("neither kubesphere api endpoint nor kubeconfig was provided")
func validateKubeSphereAPIServer(config *rest.Config) (*version.Info, error) {
transport, err := rest.TransportFor(config)
if err != nil {
return nil, err
}
client := http.Client{
Timeout: defaultTimeout,
Timeout: defaultTimeout,
Transport: transport,
}
path := fmt.Sprintf("%s/kapis/version", ksEndpoint)
if len(ksEndpoint) != 0 {
_, err := url.Parse(ksEndpoint)
if err != nil {
return nil, err
}
} else {
config, err := k8sutil.LoadKubeConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}
transport, err := rest.TransportFor(config)
if err != nil {
return nil, err
}
client.Transport = transport
path = fmt.Sprintf("%s/api/v1/namespaces/%s/services/:%s:/proxy/kapis/version", config.Host, KubesphereNamespace, KubeSphereApiServer)
}
response, err := client.Get(path)
response, err := client.Get(fmt.Sprintf("%s/api/v1/namespaces/%s/services/:%s:/proxy/kapis/version", config.Host, KubesphereNamespace, KubeSphereApiServer))
if err != nil {
return nil, err
}
@@ -450,13 +429,13 @@ func validateKubeSphereAPIServer(ksEndpoint string, kubeconfig []byte) (*version
// validateMemberClusterConfiguration compares host and member cluster jwt, if they are not same, it changes member
// cluster jwt to host's, then restart member cluster ks-apiserver.
func (h *handler) validateMemberClusterConfiguration(memberKubeconfig []byte) error {
func (h *handler) validateMemberClusterConfiguration(clientSet kubernetes.Interface) error {
hConfig, err := h.getHostClusterConfig()
if err != nil {
return err
}
mConfig, err := h.getMemberClusterConfig(memberKubeconfig)
mConfig, err := h.getMemberClusterConfig(clientSet)
if err != nil {
return err
}
@@ -469,19 +448,7 @@ func (h *handler) validateMemberClusterConfiguration(memberKubeconfig []byte) er
}
// getMemberClusterConfig returns KubeSphere running config by the given member cluster kubeconfig
func (h *handler) getMemberClusterConfig(kubeconfig []byte) (*config.Config, error) {
config, err := k8sutil.LoadKubeConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}
config.Timeout = defaultTimeout
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
func (h *handler) getMemberClusterConfig(clientSet kubernetes.Interface) (*config.Config, error) {
memberCm, err := clientSet.CoreV1().ConfigMaps(KubesphereNamespace).Get(context.Background(), KubeSphereConfigName, metav1.GetOptions{})
if err != nil {
return nil, err

View File

@@ -21,8 +21,6 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
@@ -32,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/client-go/kubernetes"
k8s "k8s.io/client-go/kubernetes"
k8sfake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/clientcmd"
@@ -42,7 +41,6 @@ import (
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/version"
)
const (
@@ -372,32 +370,15 @@ func TestValidateKubeConfig(t *testing.T) {
_ = env.Stop()
}()
err = h.validateKubeConfig([]byte(base64EncodedKubeConfig))
if err != nil {
t.Fatal(err)
}
}
var ver = version.Get()
func endpoint(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(ver)
}
func TestValidateKubeSphereEndpoint(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(endpoint))
defer svr.Close()
got, err := validateKubeSphereAPIServer(svr.URL, nil)
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(&ver, got); len(diff) != 0 {
t.Errorf("%T +got, -expected %v", ver, diff)
err = h.validateKubeConfig("test", clientSet)
if err != nil {
t.Fatal(err)
}
}
func TestValidateMemberClusterConfiguration(t *testing.T) {
@@ -448,15 +429,20 @@ func TestValidateMemberClusterConfiguration(t *testing.T) {
_ = env.Stop()
}()
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
t.Fatal(err)
}
addMemberClusterResource(hostCm, t)
err = h.validateMemberClusterConfiguration([]byte(base64EncodedKubeConfig))
err = h.validateMemberClusterConfiguration(clientSet)
if err != nil {
t.Fatal(err)
}
addMemberClusterResource(memberCm, t)
err = h.validateMemberClusterConfiguration([]byte(base64EncodedKubeConfig))
err = h.validateMemberClusterConfiguration(clientSet)
if err == nil {
t.Fatal()
}

View File

@@ -19,6 +19,7 @@ package v1alpha1
import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
@@ -168,6 +169,9 @@ type ClusterStatus struct {
// every amount of time, like 5 minutes.
// +optional
Configz map[string]bool `json:"configz,omitempty"`
// UID is the kube-system namespace UID of the cluster, which represents the unique ID of the cluster.
UID types.UID `json:"uid,omitempty"`
}
// +genclient