@@ -1,3 +1,2 @@
|
||||
tmp/
|
||||
.git
|
||||
.github
|
||||
|
||||
@@ -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
|
||||
|
||||
1
go.mod
1
go.mod
@@ -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
|
||||
|
||||
35
pkg/kapis/version/register.go
Normal file
35
pkg/kapis/version/register.go
Normal file
@@ -0,0 +1,35 @@
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user