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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
innCluster := c.getInnerCluster(cluster.Name)
|
innCluster := c.getInnerCluster(cluster.Name)
|
||||||
if innCluster == nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/emicklei/go-restful"
|
"github.com/emicklei/go-restful"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
v1 "k8s.io/client-go/listers/core/v1"
|
v1 "k8s.io/client-go/listers/core/v1"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
@@ -29,12 +31,10 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
defaultAgentImage = "kubesphere/tower:v1.0"
|
defaultAgentImage = "kubesphere/tower:v1.0"
|
||||||
defaultTimeout = 5 * time.Second
|
defaultTimeout = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var errClusterConnectionIsNotProxy = fmt.Errorf("cluster is not using proxy connection")
|
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 {
|
type handler struct {
|
||||||
serviceLister v1.ServiceLister
|
serviceLister v1.ServiceLister
|
||||||
@@ -45,7 +45,7 @@ type handler struct {
|
|||||||
yamlPrinter *printers.YAMLPrinter
|
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 {
|
if len(agentImage) == 0 {
|
||||||
agentImage = defaultAgentImage
|
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
|
// 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
|
var cluster v1alpha1.Cluster
|
||||||
|
|
||||||
err := request.ReadEntity(&cluster)
|
err := request.ReadEntity(&cluster)
|
||||||
@@ -241,7 +241,7 @@ func (h *handler) ValidateCluster(request *restful.Request, response *restful.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateKubeConfig(cluster.Spec.Connection.KubeConfig)
|
err = h.validateKubeConfig(cluster.Spec.Connection.KubeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.HandleBadRequest(response, request, err)
|
api.HandleBadRequest(response, request, err)
|
||||||
return
|
return
|
||||||
@@ -257,12 +257,25 @@ func (h *handler) ValidateCluster(request *restful.Request, response *restful.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validateKubeConfig takes base64 encoded kubeconfig and check its validity
|
// 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)
|
config, err := loadKubeConfigFromBytes(kubeconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
config.Timeout = defaultTimeout
|
||||||
|
|
||||||
clientSet, err := kubernetes.NewForConfig(config)
|
clientSet, err := kubernetes.NewForConfig(config)
|
||||||
@@ -327,14 +340,19 @@ func validateKubeSphereAPIServer(ksEndpoint string, kubeconfig []byte) (*version
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
responseBytes, _ := ioutil.ReadAll(response.Body)
|
||||||
|
responseBody := string(responseBytes)
|
||||||
|
|
||||||
|
response.Body = ioutil.NopCloser(bytes.NewBuffer(responseBytes))
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
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{}
|
ver := version.Info{}
|
||||||
err = json.NewDecoder(response.Body).Decode(&ver)
|
err = json.NewDecoder(response.Body).Decode(&ver)
|
||||||
if err != nil {
|
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
|
return &ver, nil
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ func TestGeranteAgentDeployment(t *testing.T) {
|
|||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
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(),
|
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister(),
|
||||||
proxyService,
|
proxyService,
|
||||||
"",
|
"",
|
||||||
@@ -215,6 +215,20 @@ users:
|
|||||||
`
|
`
|
||||||
|
|
||||||
func TestValidateKubeConfig(t *testing.T) {
|
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))
|
config, err := loadKubeConfigFromBytes([]byte(base64EncodedKubeConfig))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -247,7 +261,7 @@ func TestValidateKubeConfig(t *testing.T) {
|
|||||||
_ = env.Stop()
|
_ = env.Stop()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = validateKubeConfig([]byte(base64EncodedKubeConfig))
|
err = h.validateKubeConfig([]byte(base64EncodedKubeConfig))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func AddToContainer(container *restful.Container,
|
|||||||
agentImage string) error {
|
agentImage string) error {
|
||||||
|
|
||||||
webservice := runtime.NewWebService(GroupVersion)
|
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
|
// returns deployment yaml for cluster agent
|
||||||
webservice.Route(webservice.GET("/clusters/{cluster}/agent/deployment").
|
webservice.Route(webservice.GET("/clusters/{cluster}/agent/deployment").
|
||||||
@@ -36,7 +36,7 @@ func AddToContainer(container *restful.Container,
|
|||||||
webservice.Route(webservice.POST("/clusters/validation").
|
webservice.Route(webservice.POST("/clusters/validation").
|
||||||
Doc("").
|
Doc("").
|
||||||
Param(webservice.BodyParameter("cluster", "cluster specification")).
|
Param(webservice.BodyParameter("cluster", "cluster specification")).
|
||||||
To(h.ValidateCluster).
|
To(h.validateCluster).
|
||||||
Returns(http.StatusOK, api.StatusOK, nil))
|
Returns(http.StatusOK, api.StatusOK, nil))
|
||||||
|
|
||||||
container.Add(webservice)
|
container.Add(webservice)
|
||||||
|
|||||||
Reference in New Issue
Block a user