未验证 提交 27ca024b 编写于 作者: Z zryfish 提交者: GitHub

add version api (#2127)

add cluster validation api
上级 e119fd8a
......@@ -7,7 +7,15 @@ FROM golang:1.12 as ks-apiserver-builder
COPY / /go/src/kubesphere.io/kubesphere
WORKDIR /go/src/kubesphere.io/kubesphere
RUN CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor go build -i -ldflags '-w -s' -o ks-apiserver cmd/ks-apiserver/apiserver.go
RUN GIT_VERSION=$(git describe --always --dirty) && \
GIT_HASH=$(git rev-parse HEAD) && \
BUILDDATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') && \
CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor \
go build -i -ldflags \
'-w -s -X kubesphere.io/kubesphere/pkg/version.version=$(GIT_VERSION) \
-X kubesphere.io/kubesphere/pkg/version.gitCommit=$(GIT_HASH) \
-X kubesphere.io/kubesphere/pkg/version.buildDate=$(BUILDDATE)' \
-o ks-apiserver cmd/ks-apiserver/apiserver.go
FROM alpine:3.9
RUN apk add --update ca-certificates && update-ca-certificates
......
......@@ -107,6 +107,7 @@ require (
sigs.k8s.io/controller-runtime v0.5.0
sigs.k8s.io/controller-tools v0.2.4
sigs.k8s.io/kubefed v0.2.0-alpha.1
sigs.k8s.io/testing_frameworks v0.1.2
)
replace (
......
......@@ -60,6 +60,7 @@ import (
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2"
terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2"
"kubesphere.io/kubesphere/pkg/kapis/version"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
......@@ -202,6 +203,7 @@ func (s *APIServer) installKubeSphereAPIs() {
s.InformerFactory.KubernetesSharedInformerFactory()))
urlruntime.Must(notificationv1.AddToContainer(s.container, s.Config.NotificationOptions.Endpoint))
urlruntime.Must(alertingv1.AddToContainer(s.container, s.Config.AlertingOptions.Endpoint))
urlruntime.Must(version.AddToContainer(s.container, s.KubernetesClient.Discovery()))
}
func (s *APIServer) Run(stopCh <-chan struct{}) (err error) {
......@@ -253,7 +255,7 @@ func (s *APIServer) buildHandlerChain() {
default:
fallthrough
case authorizationoptions.RBAC:
excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"}
excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*", "/kapis/version"}
pathAuthorizer, _ := path.NewAuthorizer(excludedPaths)
amOperator := am.NewReadOnlyOperator(s.InformerFactory)
authorizers = unionauthorizer.New(pathAuthorizer, authorizerfactory.NewRBACAuthorizer(amOperator))
......
......@@ -352,7 +352,7 @@ func (c *ClusterController) syncCluster(key string) error {
clientSet, err = kubernetes.NewForConfig(clusterConfig)
if err != nil {
klog.Errorf("Failed to create ClientSet from config, %#v", err)
return nil
return err
}
if !cluster.Spec.JoinFederation { // trying to unJoin federation
......
......@@ -2,6 +2,7 @@ package v1alpha1
import (
"bytes"
"encoding/json"
"fmt"
"github.com/emicklei/go-restful"
"io"
......@@ -10,20 +11,30 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1"
clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
"kubesphere.io/kubesphere/pkg/version"
"net/http"
"net/url"
"strings"
"time"
"k8s.io/cli-runtime/pkg/printers"
)
const (
defaultAgentImage = "kubesphere/tower:v1.0"
defaultTimeout = 5 * 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 {
serviceLister v1.ServiceLister
......@@ -87,7 +98,6 @@ func (h *handler) GenerateAgentDeployment(request *restful.Request, response *re
}
response.Write(buf.Bytes())
}
//
......@@ -138,7 +148,7 @@ func (h *handler) populateProxyAddress() error {
func (h *handler) generateDefaultDeployment(cluster *v1alpha1.Cluster, w io.Writer) error {
if cluster.Spec.Connection.Type == v1alpha1.ConnectionTypeDirect {
return ErrClusterConnectionIsNotProxy
return errClusterConnectionIsNotProxy
}
agent := appsv1.Deployment{
......@@ -174,6 +184,7 @@ func (h *handler) generateDefaultDeployment(cluster *v1alpha1.Cluster, w io.Writ
fmt.Sprintf("--name=%s", cluster.Name),
fmt.Sprintf("--token=%s", cluster.Spec.Connection.Token),
fmt.Sprintf("--proxy-server=%s", h.proxyAddress),
fmt.Sprintf("--keepalive=30s"),
fmt.Sprintf("--kubesphere-service=ks-apiserver.kubesphere-system.svc:80"),
fmt.Sprintf("--kubernetes-service=kubernetes.default.svc:443"),
},
......@@ -198,3 +209,102 @@ func (h *handler) generateDefaultDeployment(cluster *v1alpha1.Cluster, w io.Writ
return h.yamlPrinter.PrintObj(&agent, w)
}
// ValidateCluster validate cluster kubeconfig and kubesphere apiserver address, check their accessibility
func (h *handler) ValidateCluster(request *restful.Request, response *restful.Response) {
var cluster v1alpha1.Cluster
err := request.ReadEntity(&cluster)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
if cluster.Spec.Connection.Type != v1alpha1.ConnectionTypeDirect {
api.HandleBadRequest(response, request, fmt.Errorf("cluster connection type is not direct"))
return
}
if len(cluster.Spec.Connection.KubeConfig) == 0 || len(cluster.Spec.Connection.KubeSphereAPIEndpoint) == 0 {
api.HandleBadRequest(response, request, fmt.Errorf("cluster kubeconfig and kubesphere endpoint should not be empty"))
return
}
err = validateKubeConfig(cluster.Spec.Connection.KubeConfig)
if err != nil {
api.HandleBadRequest(response, request, err)
return
}
_, err = validateKubeSphereAPIServer(cluster.Spec.Connection.KubeSphereAPIEndpoint)
if err != nil {
api.HandleBadRequest(response, request, fmt.Errorf("unable validate kubesphere endpoint, %v", err))
return
}
response.WriteHeader(http.StatusOK)
}
// validateKubeConfig takes base64 encoded kubeconfig and check its validity
func validateKubeConfig(kubeconfig []byte) error {
config, err := loadKubeConfigFromBytes(kubeconfig)
if err != nil {
return err
}
config.Timeout = defaultTimeout
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
return err
}
_, err = clientSet.Discovery().ServerVersion()
return err
}
func loadKubeConfigFromBytes(kubeconfig []byte) (*rest.Config, error) {
clientConfig, err := clientcmd.NewClientConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}
config, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
return config, nil
}
// validateKubeSphereAPIServer uses version api to check the accessibility
func validateKubeSphereAPIServer(ksEndpoint string) (*version.Info, error) {
_, err := url.Parse(ksEndpoint)
if err != nil {
return nil, err
}
path := fmt.Sprintf("%s/kapis/version", ksEndpoint)
client := http.Client{
Timeout: defaultTimeout,
}
response, err := client.Get(path)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errNon200Response
}
ver := version.Info{}
err = json.NewDecoder(response.Body).Decode(&ver)
if err != nil {
return nil, errInvalidResponse
}
return &ver, nil
}
......@@ -2,14 +2,22 @@ package v1alpha1
import (
"bytes"
"encoding/json"
"fmt"
"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/printers"
fake2 "k8s.io/client-go/kubernetes/fake"
k8sfake "k8s.io/client-go/kubernetes/fake"
"kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/version"
"net/http"
"net/http/httptest"
"net/url"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/testing_frameworks/integration"
"testing"
)
......@@ -81,6 +89,7 @@ spec:
- --name=gondor
- --token=randomtoken
- --proxy-server=http://139.198.121.121:8080
- --keepalive=30s
- --kubesphere-service=ks-apiserver.kubesphere-system.svc:80
- --kubernetes-service=kubernetes.default.svc:443
image: kubesphere/tower:v1.0
......@@ -97,7 +106,7 @@ status: {}
`
func TestGeranteAgentDeployment(t *testing.T) {
k8sclient := fake2.NewSimpleClientset(service)
k8sclient := k8sfake.NewSimpleClientset(service)
ksclient := fake.NewSimpleClientset(cluster)
informersFactory := informers.NewInformerFactories(k8sclient, ksclient, nil, nil, nil, nil)
......@@ -124,7 +133,7 @@ func TestGeranteAgentDeployment(t *testing.T) {
{
description: "test direct connection cluster",
expectingError: true,
expectedError: ErrClusterConnectionIsNotProxy,
expectedError: errClusterConnectionIsNotProxy,
cluster: directConnectionCluster,
},
}
......@@ -159,7 +168,6 @@ func TestGeranteAgentDeployment(t *testing.T) {
}
})
}
}
func TestInnerGenerateAgentDeployment(t *testing.T) {
......@@ -177,10 +185,91 @@ func TestInnerGenerateAgentDeployment(t *testing.T) {
t.Error(err)
}
t.Log(buf.String())
if diff := cmp.Diff(buf.String(), expected); len(diff) != 0 {
t.Error(diff)
}
}
var base64EncodedKubeConfig = `
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKYzNCb1pYSmxNQjRYRFRJd01EVXlOekEzTWpFME1Gb1hEVE13TURVeU5UQTNNakUwTUZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYTndhR1Z5WlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTWVxCjRwNldVQVJmeUtRcEpZM1ZGcEVhT3Y5REZBQkZhQXBpbUswQTBBL05KV25yaGl0MjhnL3ZXMno5bmxkeHQwZzgKc1J0ZEp4TUxmOHF5WkEramZudU5jUDBLUTJ5a3VxZE41c29MdE1TUmt1K1dHZFNkWlJvTVpEakdKbHRSUEdVRQpnOHd6OE9zdWRMcmZ5Zlcxdy8vUFRPWnRLNmsrNUhQZGtuWU5KcU9UZGJDTEVma2RZbFB1ZXNZTTFKamRacXlNClJQRDM1RXpUSEowR05jYlBzbUlyZ05WZGR4Nmh5RmIxTFZ0QXRqa0tWY2lNR1k1UlFTQWlQdVVGaGQreUcrOHUKUWlqQlgvV09ESlFOelJCQWFPdWRWdURqSkhIZ0lBV3FzcC9qSllWQkdRYnNad3djTTRUSEJPb2k3N1krYVR3SAphcHRaMitVMzgwbFY4d0tJR3NFQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFDVjY0VVpCZkcxc1d4ampJWTZ5Vm0rdzhxTFgKS0ZYanR3VElGb1Y3WENOT2RDbEQ0a2NsN1pyTnl5UEtUaTFOV3BDbUVzSXZPVjRSRXV6a2ZWZ2Rzc0tHL2dYVwpGNGV2dStZTFh1UEk1RHJFejNGaW5OMGxVcFNZMVg3b1Y5N2JyU1lmdE53aWJQbUVFTEVHbWJvMnNHS3VoL21BCjRJZ0tmZklqWVdSYjE3TlZLb2s0am1WTnhkMmNCL09GeFlsY2xndlc2THpxc1BDdnpWbDdDRWErRElyNHZLamgKRlhvOERXejMzclUySCs0RjNMOThzWGE1OWNITWZPb1kzZ3pzUE5LYnQwbWsxeTNOUmZocTZYTTJXemkrUFZkcAppbUVqUlY4UUl5c3Zhc25sTjFIY1FtcWtMaFdLYlRoOEc5MGhiTzlwdTFRNE0wMmZ3STg0ZVlSUWZNUT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: 127.0.0.1:6578
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJZkcyYVlKYnRYT2N3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWE53YUdWeVpUQWVGdzB5TURBMU1qY3dOekl4TkRCYUZ3MHpNREExTWpVd056SXhOREJhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXRiR3JpeFBxOWF6QU1wNGsKM0VsaElmanhOZXhkY2VORGJENkFMMXVGSWJUMk1uVnRXeFFXOU5ueVFwVU9NMzN4clNJMkJlVW1uU0U5RlR6cAo1bTBmaGVBOGhtRHRSZWVOckNxVnBJQy9kNUV2eDJ3NTJ2NXVCYUNXd09ZUGpQMi9uL0Y3THQwMmN3TkFXWE5pCmFRTi9uRGl6TjJrRXFoSmZiL0tyNGx3eEEzTDExVXJhMDNTRUp0U3FXSzBKQ1pnL0lzUnRFNFFqZXp1WWhiVWkKcWU0TmdqZjN1ZFBMMXQyeVpCK3hSTE1sNTFqenhYaXQ0U2pHSFJ2UEt1VHlkc1AxdEtINXdYdnhqaEZTZjN3UQpMSHFQd3hQWXVKSG5MWlhPaElnTnEzNnk4Rlp0WkdQRUhDVDFlUEh5cWhQaEFZZGlBRlRXT3pRN2FlS1puZWJzClpSblJMUUlEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFCZGNMN2I4akVuYjRSSndqZ3ZrVUxSQXJLVGk1WFhRSzllUwpsMlNkNEF3Ris5QWg3WElUazJFQ3J0WVR2WEM0K2trS1BxK2FuYjlOOFptenA0R0ovd3N5VEN2dHo5eGlQMTd3ClFCUXREdFA3eS9venlLc24rUzYvSDg2Y0JqdGZGT0dMYm5CekRBY3J0S3YyeUxxY3pyNlYzSDBnZDI3MVdlSkQKcU81U3czSEZoTHhERDRXSVVSQnFLOTJPVUhnSzVQOWRHaWdkK2MvQ0xWMFdJS1kzN3JGR2MrU3VUa2JOQXNmaQpmVmhBYXRsYlpQdE1QekJoV1hkM0JWcTMxTmtBM1F4aWUzdWc4Tm9OZ0czUHFPanJkZHA2aEs0Sk5wMGtkSGFvClZHRXMxcUdzOGJjekxNTTVzeUdkSndNbXVNUEdnaGxheUZrZlJ2RmpZWDlwbllJUStpTT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdGJHcml4UHE5YXpBTXA0azNFbGhJZmp4TmV4ZGNlTkRiRDZBTDF1RkliVDJNblZ0Cld4UVc5Tm55UXBVT00zM3hyU0kyQmVVbW5TRTlGVHpwNW0wZmhlQThobUR0UmVlTnJDcVZwSUMvZDVFdngydzUKMnY1dUJhQ1d3T1lQalAyL24vRjdMdDAyY3dOQVdYTmlhUU4vbkRpek4ya0VxaEpmYi9LcjRsd3hBM0wxMVVyYQowM1NFSnRTcVdLMEpDWmcvSXNSdEU0UWplenVZaGJVaXFlNE5namYzdWRQTDF0MnlaQit4UkxNbDUxanp4WGl0CjRTakdIUnZQS3VUeWRzUDF0S0g1d1h2eGpoRlNmM3dRTEhxUHd4UFl1SkhuTFpYT2hJZ05xMzZ5OEZadFpHUEUKSENUMWVQSHlxaFBoQVlkaUFGVFdPelE3YWVLWm5lYnNaUm5STFFJREFRQUJBb0lCQVFDSDFnQ054YUpQY1l0dgpURlA2Yk5HMWVFdTlLS3pqekNoSDhLSWN4YXRPZTkvajhXNkVQUXk4bVlSSXl1OEhDQTE2aHEwazB5Qi9NSzVlCkJtQkg2U1U4RFZ5eWloeFp1cmRzRTVvMGxoeU80M2g0K3l4MTBPbW9RMXJ4ZEE0RU5tRGd6c1J0VU95NEo2SWcKUGVkQTQyQ3dCcVBWdFNuTGpGalZkUE9VRTZDQkZsa01nTVR2R1I5SE9EaVovdmhqRXhFQWg0UFFmMkl3ZXZhVwpxTXFsOVJNVFprVEUyVUJEbHZMWVRNcG5pdGlSSWdLdEdmSk1WTW81WWR4SWpOMXRnUHcwZmN5ZXkrM2JhdDZQCkVUSjk5K21KNDJMZGYzaTlNNkNvTDVaa1BjcEE0ckVSR1RKamtaT3FESzhPMVZXSDNtQ21WYTFyVGN0bmpJbVMKaGRGWElJWUJBb0dCQU84R25uekhzSVFXaExBSkw5U3VWWjhBODl5WHZuWHBFYUUxaHBRdEViRFlEaTBPcGg1UAp0cVVISlZ0T0ViaTYvai9tY1pwTnNNVzR2QitiV3hkbDA4U0g1Qnp2eW9qOEkvbUlVNDFCQkYya3hxMk8yNlJRCjRkdFQrN3NWUUFCT1ArUE0vcWtMOWVKdFI3blYwamRGd3BvcmtzcFo5cE9BZHNnUGVnUzlxUHFOQW9HQkFNS1kKeU5OQWdYRjhBdEV3ZEk5OVB2ZTVoYzN6QjZUclVOMStYNVNUZWNYWnFVSUNwMlJGTFNtL3RiVkRuay9VaXBXeQpYSjgzT084VGFIUjIrYk5OZU5NK2REK0diakVWdDNVMk1XSzN2azFjcy9oWjZrK0F2aE9iSXBrLzR2VEVRUW1FCjh4bkl5bkpPNWJleFQybkEyUWVYblVPbjZzdWtxVnk3YlBLSnNOa2hBb0dBV1YzNUxhQWZvQk1uUXdYOFN5RnYKUThiQVptNlp1RTRPMkY1QjFlN1AyWFcrUHh4bUFaaytLWTkxYVNEVVFXUXdvVVdRbmVlRU96aXBwWXVaVURNegpMUnk5cmcvOWdwLzY5MVlBSHlUNjgrUWlvRXQwVllna0diUFp2NFhmYXYzV3AxNUNySU9iU0RBaGpCcWt3U09rCjhhMXU4WmNYT09qa0FFTEJGVHF3RGhVQ2dZQVVqQjFvY1A4NkJHWW53SDRPU0tORmRRbHozWjJKQkcvZGMyS1UKUlo0dURmV1pTcjV5RC92YzFLbFRJbmlzNVR4YzRpQjFqMWNycDFqNE16ZmFmdXVySW9VVDBCWUNpTkIrUitLZgpFZGUrUTNPZFhhRW9FK2YrR2Z0bFF5R3J4cTAzWEJwdk5veHAxWHJjRXBUWURjemN5RjJLcjBoVGlHZDVxekN0Cnkyd3BBUUtCZ1FDWTFKdlp0YkFlZll2RXQ4c3JETVZsb1FOWVVKTURxWnBLenpqR0w5S3FqdlFwbjJOL2EwNmsKUzNsMndWWExXNy8xb0RMV2gxZGZLbUJlUlJhZUxJMXIvS3FyeTRjUStiVEIzTzBwS2R3WjFFYXNQajBDTnlaUAp0YnFkOEFWa09pRkFNYlRWaVZuR3RzSnJ3c1V2N1NSTUU4ckloMVJva0ZtRGRpN0J1UVl5emc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
`
func TestValidateKubeConfig(t *testing.T) {
config, err := loadKubeConfigFromBytes([]byte(base64EncodedKubeConfig))
if err != nil {
t.Fatal(err)
}
// config.Host is schemaless, we need to add schema manually
u, err := url.Parse(fmt.Sprintf("http://%s", config.Host))
if err != nil {
t.Fatal(err)
}
// we need to specify apiserver port to match above kubeconfig
env := &envtest.Environment{
Config: config,
ControlPlane: integration.ControlPlane{
APIServer: &integration.APIServer{
Args: envtest.DefaultKubeAPIServerFlags,
URL: u,
},
},
}
cfg, err := env.Start()
if err != nil {
t.Log(cfg)
t.Fatal(err)
}
defer func() {
_ = env.Stop()
}()
err = 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)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(&ver, got); len(diff) != 0 {
t.Errorf("%T +got, -expected %v", ver, diff)
}
}
......@@ -33,6 +33,12 @@ func AddToContainer(container *restful.Container,
To(h.GenerateAgentDeployment).
Returns(http.StatusOK, api.StatusOK, nil))
webservice.Route(webservice.POST("/clusters/validation").
Doc("").
Param(webservice.BodyParameter("cluster", "cluster specification")).
To(h.ValidateCluster).
Returns(http.StatusOK, api.StatusOK, nil))
container.Add(webservice)
return nil
......
package version
import (
"github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/version"
)
func AddToContainer(container *restful.Container, k8sDiscovery discovery.DiscoveryInterface) error {
webservice := runtime.NewWebService(schema.GroupVersion{})
webservice.Route(webservice.GET("/version").
To(func(request *restful.Request, response *restful.Response) {
ksVersion := version.Get()
if k8sDiscovery != nil {
k8sVersion, err := k8sDiscovery.ServerVersion()
if err == nil {
ksVersion.Kubernetes = k8sVersion
} else {
klog.Errorf("Failed to get kubernetes version, error %v", err)
}
}
response.WriteAsJson(ksVersion)
})).
Doc("KubeSphere version")
container.Add(webservice)
return nil
}
......@@ -98,6 +98,11 @@ func NewKubernetesClient(options *KubernetesOptions) (Client, error) {
return nil, err
}
k.discoveryClient, err = discovery.NewDiscoveryClientForConfig(config)
if err != nil {
return nil, err
}
k.ks, err = kubesphere.NewForConfig(config)
if err != nil {
return nil, err
......
......@@ -16,4 +16,43 @@ limitations under the License.
package version
var Version = "v0.0.0"
import (
"fmt"
"runtime"
apimachineryversion "k8s.io/apimachinery/pkg/version"
)
var (
version = "v0.0.0"
gitCommit = "unknown"
gitTreeState = "unknown"
buildDate = "unknown"
)
type Info struct {
Version string `json:"gitVersion"`
GitCommit string `json:"gitCommit"`
GitTreeState string `json:"gitTreeState"`
BuildDate string `json:"buildDate"`
GoVersion string `json:"goVersion"`
Compiler string `json:"compiler"`
Platform string `json:"platform"`
Kubernetes *apimachineryversion.Info `json:"kubernetes,omitempty"`
}
// Get returns the overall codebase version. It's for
// detecting what code a binary was built from.
func Get() Info {
// These variables typically come from -ldflags settings and
// in their absence fallback to the default settings
return Info{
Version: version,
GitCommit: gitCommit,
GitTreeState: gitTreeState,
BuildDate: buildDate,
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册