use more friendly error messages (#2364)
This commit is contained in:
@@ -118,13 +118,13 @@ func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request, han
|
||||
}
|
||||
|
||||
if !isClusterReady(cluster) {
|
||||
http.Error(w, fmt.Sprintf("cluster is not ready"), http.StatusInternalServerError)
|
||||
http.Error(w, fmt.Sprintf("cluster %s is not ready", cluster.Name), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
innCluster := c.getInnerCluster(cluster.Name)
|
||||
if innCluster == nil {
|
||||
http.Error(w, fmt.Sprintf("cluster not ready"), http.StatusInternalServerError)
|
||||
http.Error(w, fmt.Sprintf("cluster %s is not ready", cluster.Name), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,13 @@ import (
|
||||
"fmt"
|
||||
"github.com/emicklei/go-restful"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
v1 "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -29,12 +31,10 @@ import (
|
||||
|
||||
const (
|
||||
defaultAgentImage = "kubesphere/tower:v1.0"
|
||||
defaultTimeout = 5 * time.Second
|
||||
defaultTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
var errClusterConnectionIsNotProxy = fmt.Errorf("cluster is not using proxy connection")
|
||||
var errNon200Response = fmt.Errorf("non-200 response returned from endpoint")
|
||||
var errInvalidResponse = fmt.Errorf("invalid response from kubesphere apiserver")
|
||||
|
||||
type handler struct {
|
||||
serviceLister v1.ServiceLister
|
||||
@@ -45,7 +45,7 @@ type handler struct {
|
||||
yamlPrinter *printers.YAMLPrinter
|
||||
}
|
||||
|
||||
func NewHandler(serviceLister v1.ServiceLister, clusterLister clusterlister.ClusterLister, proxyService, proxyAddress, agentImage string) *handler {
|
||||
func newHandler(serviceLister v1.ServiceLister, clusterLister clusterlister.ClusterLister, proxyService, proxyAddress, agentImage string) *handler {
|
||||
|
||||
if len(agentImage) == 0 {
|
||||
agentImage = defaultAgentImage
|
||||
@@ -222,7 +222,7 @@ func (h *handler) generateDefaultDeployment(cluster *v1alpha1.Cluster, w io.Writ
|
||||
}
|
||||
|
||||
// ValidateCluster validate cluster kubeconfig and kubesphere apiserver address, check their accessibility
|
||||
func (h *handler) ValidateCluster(request *restful.Request, response *restful.Response) {
|
||||
func (h *handler) validateCluster(request *restful.Request, response *restful.Response) {
|
||||
var cluster v1alpha1.Cluster
|
||||
|
||||
err := request.ReadEntity(&cluster)
|
||||
@@ -241,7 +241,7 @@ func (h *handler) ValidateCluster(request *restful.Request, response *restful.Re
|
||||
return
|
||||
}
|
||||
|
||||
err = validateKubeConfig(cluster.Spec.Connection.KubeConfig)
|
||||
err = h.validateKubeConfig(cluster.Spec.Connection.KubeConfig)
|
||||
if err != nil {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
@@ -257,12 +257,25 @@ func (h *handler) ValidateCluster(request *restful.Request, response *restful.Re
|
||||
}
|
||||
|
||||
// validateKubeConfig takes base64 encoded kubeconfig and check its validity
|
||||
func validateKubeConfig(kubeconfig []byte) error {
|
||||
func (h *handler) validateKubeConfig(kubeconfig []byte) error {
|
||||
config, err := loadKubeConfigFromBytes(kubeconfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clusters, err := h.clusterLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// clusters with the exactly same KubernetesAPIEndpoint 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)
|
||||
}
|
||||
}
|
||||
|
||||
config.Timeout = defaultTimeout
|
||||
|
||||
clientSet, err := kubernetes.NewForConfig(config)
|
||||
@@ -327,14 +340,19 @@ func validateKubeSphereAPIServer(ksEndpoint string, kubeconfig []byte) (*version
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responseBytes, _ := ioutil.ReadAll(response.Body)
|
||||
responseBody := string(responseBytes)
|
||||
|
||||
response.Body = ioutil.NopCloser(bytes.NewBuffer(responseBytes))
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, errNon200Response
|
||||
return nil, fmt.Errorf("invalid response: %s , please make sure ks-apiserver.kubesphere-system.svc of member cluster is up and running", responseBody)
|
||||
}
|
||||
|
||||
ver := version.Info{}
|
||||
err = json.NewDecoder(response.Body).Decode(&ver)
|
||||
if err != nil {
|
||||
return nil, errInvalidResponse
|
||||
return nil, fmt.Errorf("invalid response: %s , please make sure ks-apiserver.kubesphere-system.svc of member cluster is up and running", responseBody)
|
||||
}
|
||||
|
||||
return &ver, nil
|
||||
|
||||
@@ -142,7 +142,7 @@ func TestGeranteAgentDeployment(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.description, func(t *testing.T) {
|
||||
h := NewHandler(informersFactory.KubernetesSharedInformerFactory().Core().V1().Services().Lister(),
|
||||
h := newHandler(informersFactory.KubernetesSharedInformerFactory().Core().V1().Services().Lister(),
|
||||
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister(),
|
||||
proxyService,
|
||||
"",
|
||||
@@ -215,6 +215,20 @@ users:
|
||||
`
|
||||
|
||||
func TestValidateKubeConfig(t *testing.T) {
|
||||
k8sclient := k8sfake.NewSimpleClientset(service)
|
||||
ksclient := fake.NewSimpleClientset(cluster)
|
||||
|
||||
informersFactory := informers.NewInformerFactories(k8sclient, ksclient, nil, nil, nil, nil)
|
||||
|
||||
informersFactory.KubernetesSharedInformerFactory().Core().V1().Services().Informer().GetIndexer().Add(service)
|
||||
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Informer().GetIndexer().Add(cluster)
|
||||
|
||||
h := newHandler(informersFactory.KubernetesSharedInformerFactory().Core().V1().Services().Lister(),
|
||||
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister(),
|
||||
proxyService,
|
||||
"",
|
||||
agentImage)
|
||||
|
||||
config, err := loadKubeConfigFromBytes([]byte(base64EncodedKubeConfig))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -247,7 +261,7 @@ func TestValidateKubeConfig(t *testing.T) {
|
||||
_ = env.Stop()
|
||||
}()
|
||||
|
||||
err = validateKubeConfig([]byte(base64EncodedKubeConfig))
|
||||
err = h.validateKubeConfig([]byte(base64EncodedKubeConfig))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func AddToContainer(container *restful.Container,
|
||||
agentImage string) error {
|
||||
|
||||
webservice := runtime.NewWebService(GroupVersion)
|
||||
h := NewHandler(k8sInformers.Core().V1().Services().Lister(), ksInformers.Cluster().V1alpha1().Clusters().Lister(), proxyService, proxyAddress, agentImage)
|
||||
h := newHandler(k8sInformers.Core().V1().Services().Lister(), ksInformers.Cluster().V1alpha1().Clusters().Lister(), proxyService, proxyAddress, agentImage)
|
||||
|
||||
// returns deployment yaml for cluster agent
|
||||
webservice.Route(webservice.GET("/clusters/{cluster}/agent/deployment").
|
||||
@@ -36,7 +36,7 @@ func AddToContainer(container *restful.Container,
|
||||
webservice.Route(webservice.POST("/clusters/validation").
|
||||
Doc("").
|
||||
Param(webservice.BodyParameter("cluster", "cluster specification")).
|
||||
To(h.ValidateCluster).
|
||||
To(h.validateCluster).
|
||||
Returns(http.StatusOK, api.StatusOK, nil))
|
||||
|
||||
container.Add(webservice)
|
||||
|
||||
Reference in New Issue
Block a user