Add update cluster kubeconfig API
This commit is contained in:
5
pkg/api/cluster/v1alpha1/types.go
Normal file
5
pkg/api/cluster/v1alpha1/types.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
type UpdateClusterRequest struct {
|
||||||
|
KubeConfig []byte `json:"kubeconfig"`
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
rt "runtime"
|
rt "runtime"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"kubesphere.io/kubesphere/pkg/utils/iputil"
|
"kubesphere.io/kubesphere/pkg/utils/iputil"
|
||||||
@@ -34,8 +35,6 @@ import (
|
|||||||
|
|
||||||
openpitrixv2alpha1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v2alpha1"
|
openpitrixv2alpha1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v2alpha1"
|
||||||
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/emicklei/go-restful"
|
"github.com/emicklei/go-restful"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
|
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
@@ -228,6 +227,7 @@ func (s *APIServer) installKubeSphereAPIs() {
|
|||||||
s.KubernetesClient.KubeSphere(), s.EventsClient, s.LoggingClient, s.AuditingClient, amOperator, rbacAuthorizer, s.MonitoringClient, s.RuntimeCache, s.Config.MeteringOptions))
|
s.KubernetesClient.KubeSphere(), s.EventsClient, s.LoggingClient, s.AuditingClient, amOperator, rbacAuthorizer, s.MonitoringClient, s.RuntimeCache, s.Config.MeteringOptions))
|
||||||
urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), rbacAuthorizer, s.KubernetesClient.Config()))
|
urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), rbacAuthorizer, s.KubernetesClient.Config()))
|
||||||
urlruntime.Must(clusterkapisv1alpha1.AddToContainer(s.container,
|
urlruntime.Must(clusterkapisv1alpha1.AddToContainer(s.container,
|
||||||
|
s.KubernetesClient.KubeSphere(),
|
||||||
s.InformerFactory.KubernetesSharedInformerFactory(),
|
s.InformerFactory.KubernetesSharedInformerFactory(),
|
||||||
s.InformerFactory.KubeSphereSharedInformerFactory(),
|
s.InformerFactory.KubeSphereSharedInformerFactory(),
|
||||||
s.Config.MultiClusterOptions.ProxyPublishService,
|
s.Config.MultiClusterOptions.ProxyPublishService,
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ import (
|
|||||||
"kubesphere.io/api/cluster/v1alpha1"
|
"kubesphere.io/api/cluster/v1alpha1"
|
||||||
|
|
||||||
"kubesphere.io/kubesphere/pkg/api"
|
"kubesphere.io/kubesphere/pkg/api"
|
||||||
|
clusterv1alpha1 "kubesphere.io/kubesphere/pkg/api/cluster/v1alpha1"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/config"
|
"kubesphere.io/kubesphere/pkg/apiserver/config"
|
||||||
|
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||||
clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
|
clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
|
||||||
"kubesphere.io/kubesphere/pkg/version"
|
"kubesphere.io/kubesphere/pkg/version"
|
||||||
@@ -63,6 +65,7 @@ const (
|
|||||||
var errClusterConnectionIsNotProxy = fmt.Errorf("cluster is not using proxy connection")
|
var errClusterConnectionIsNotProxy = fmt.Errorf("cluster is not using proxy connection")
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
|
ksclient kubesphere.Interface
|
||||||
serviceLister v1.ServiceLister
|
serviceLister v1.ServiceLister
|
||||||
clusterLister clusterlister.ClusterLister
|
clusterLister clusterlister.ClusterLister
|
||||||
configMapLister v1.ConfigMapLister
|
configMapLister v1.ConfigMapLister
|
||||||
@@ -73,13 +76,14 @@ type handler struct {
|
|||||||
yamlPrinter *printers.YAMLPrinter
|
yamlPrinter *printers.YAMLPrinter
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHandler(k8sInformers k8sinformers.SharedInformerFactory, ksInformers externalversions.SharedInformerFactory, proxyService, proxyAddress, agentImage string) *handler {
|
func newHandler(ksclient kubesphere.Interface, k8sInformers k8sinformers.SharedInformerFactory, ksInformers externalversions.SharedInformerFactory, proxyService, proxyAddress, agentImage string) *handler {
|
||||||
|
|
||||||
if len(agentImage) == 0 {
|
if len(agentImage) == 0 {
|
||||||
agentImage = defaultAgentImage
|
agentImage = defaultAgentImage
|
||||||
}
|
}
|
||||||
|
|
||||||
return &handler{
|
return &handler{
|
||||||
|
ksclient: ksclient,
|
||||||
serviceLister: k8sInformers.Core().V1().Services().Lister(),
|
serviceLister: k8sInformers.Core().V1().Services().Lister(),
|
||||||
clusterLister: ksInformers.Cluster().V1alpha1().Clusters().Lister(),
|
clusterLister: ksInformers.Cluster().V1alpha1().Clusters().Lister(),
|
||||||
configMapLister: k8sInformers.Core().V1().ConfigMaps().Lister(),
|
configMapLister: k8sInformers.Core().V1().ConfigMaps().Lister(),
|
||||||
@@ -133,7 +137,6 @@ func (h *handler) generateAgentDeployment(request *restful.Request, response *re
|
|||||||
response.Write(buf.Bytes())
|
response.Write(buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
func (h *handler) populateProxyAddress() error {
|
func (h *handler) populateProxyAddress() error {
|
||||||
if len(h.proxyService) == 0 {
|
if len(h.proxyService) == 0 {
|
||||||
return fmt.Errorf("neither proxy address nor proxy service provided")
|
return fmt.Errorf("neither proxy address nor proxy service provided")
|
||||||
@@ -251,6 +254,73 @@ func (h *handler) generateDefaultDeployment(cluster *v1alpha1.Cluster, w io.Writ
|
|||||||
return h.yamlPrinter.PrintObj(&agent, w)
|
return h.yamlPrinter.PrintObj(&agent, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateKubeConfig updates the kubeconfig of the specific cluster, this API is used to update expired kubeconfig.
|
||||||
|
func (h *handler) updateKubeConfig(request *restful.Request, response *restful.Response) {
|
||||||
|
var req clusterv1alpha1.UpdateClusterRequest
|
||||||
|
if err := request.ReadEntity(&req); err != nil {
|
||||||
|
api.HandleBadRequest(response, request, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterName := request.PathParameter("cluster")
|
||||||
|
obj, err := h.clusterLister.Get(clusterName)
|
||||||
|
if err != nil {
|
||||||
|
api.HandleBadRequest(response, request, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cluster := obj.DeepCopy()
|
||||||
|
if _, ok := cluster.Labels[v1alpha1.HostCluster]; ok {
|
||||||
|
api.HandleBadRequest(response, request, fmt.Errorf("update kubeconfig of the host cluster is not allowed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// For member clusters that use proxy mode, we don't need to update the kubeconfig,
|
||||||
|
// if the certs expired, just restart the tower component in the host cluster, it will renew the cert.
|
||||||
|
if cluster.Spec.Connection.Type == v1alpha1.ConnectionTypeProxy {
|
||||||
|
api.HandleBadRequest(response, request, fmt.Errorf(
|
||||||
|
"update kubeconfig of member clusters which using proxy mode is not allowed, their certs are managed and will be renewed by tower",
|
||||||
|
))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.KubeConfig) == 0 {
|
||||||
|
api.HandleBadRequest(response, request, fmt.Errorf("cluster kubeconfig MUST NOT be empty"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config, err := loadKubeConfigFromBytes(req.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
|
||||||
|
}
|
||||||
|
if _, err = clientSet.Discovery().ServerVersion(); err != nil {
|
||||||
|
api.HandleBadRequest(response, request, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = validateKubeSphereAPIServer(cluster.Spec.Connection.KubeSphereAPIEndpoint, cluster.Spec.Connection.KubeConfig)
|
||||||
|
if 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 {
|
||||||
|
api.HandleBadRequest(response, request, fmt.Errorf("failed to validate member cluster configuration, err: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.Spec.Connection.KubeConfig = req.KubeConfig
|
||||||
|
if _, err = h.ksclient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{}); err != nil {
|
||||||
|
api.HandleBadRequest(response, request, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
@@ -252,7 +252,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(),
|
h := newHandler(ksclient, informersFactory.KubernetesSharedInformerFactory(),
|
||||||
informersFactory.KubeSphereSharedInformerFactory(),
|
informersFactory.KubeSphereSharedInformerFactory(),
|
||||||
proxyService,
|
proxyService,
|
||||||
"",
|
"",
|
||||||
@@ -333,7 +333,7 @@ func TestValidateKubeConfig(t *testing.T) {
|
|||||||
informersFactory.KubernetesSharedInformerFactory().Core().V1().Services().Informer().GetIndexer().Add(service)
|
informersFactory.KubernetesSharedInformerFactory().Core().V1().Services().Informer().GetIndexer().Add(service)
|
||||||
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Informer().GetIndexer().Add(cluster)
|
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Informer().GetIndexer().Add(cluster)
|
||||||
|
|
||||||
h := newHandler(informersFactory.KubernetesSharedInformerFactory(),
|
h := newHandler(ksclient, informersFactory.KubernetesSharedInformerFactory(),
|
||||||
informersFactory.KubeSphereSharedInformerFactory(),
|
informersFactory.KubeSphereSharedInformerFactory(),
|
||||||
proxyService,
|
proxyService,
|
||||||
"",
|
"",
|
||||||
@@ -409,7 +409,7 @@ func TestValidateMemberClusterConfiguration(t *testing.T) {
|
|||||||
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Informer().GetIndexer().Add(cluster)
|
informersFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Informer().GetIndexer().Add(cluster)
|
||||||
informersFactory.KubernetesSharedInformerFactory().Core().V1().ConfigMaps().Informer().GetIndexer().Add(hostCm)
|
informersFactory.KubernetesSharedInformerFactory().Core().V1().ConfigMaps().Informer().GetIndexer().Add(hostCm)
|
||||||
|
|
||||||
h := newHandler(informersFactory.KubernetesSharedInformerFactory(),
|
h := newHandler(ksclient, informersFactory.KubernetesSharedInformerFactory(),
|
||||||
informersFactory.KubeSphereSharedInformerFactory(),
|
informersFactory.KubeSphereSharedInformerFactory(),
|
||||||
proxyService,
|
proxyService,
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
|
|
||||||
"kubesphere.io/kubesphere/pkg/api"
|
"kubesphere.io/kubesphere/pkg/api"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||||
|
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||||
"kubesphere.io/kubesphere/pkg/constants"
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
)
|
)
|
||||||
@@ -37,6 +38,7 @@ const (
|
|||||||
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
||||||
|
|
||||||
func AddToContainer(container *restful.Container,
|
func AddToContainer(container *restful.Container,
|
||||||
|
ksclient kubesphere.Interface,
|
||||||
k8sInformers k8sinformers.SharedInformerFactory,
|
k8sInformers k8sinformers.SharedInformerFactory,
|
||||||
ksInformers externalversions.SharedInformerFactory,
|
ksInformers externalversions.SharedInformerFactory,
|
||||||
proxyService string,
|
proxyService string,
|
||||||
@@ -44,7 +46,7 @@ func AddToContainer(container *restful.Container,
|
|||||||
agentImage string) error {
|
agentImage string) error {
|
||||||
|
|
||||||
webservice := runtime.NewWebService(GroupVersion)
|
webservice := runtime.NewWebService(GroupVersion)
|
||||||
h := newHandler(k8sInformers, ksInformers, proxyService, proxyAddress, agentImage)
|
h := newHandler(ksclient, k8sInformers, ksInformers, 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").
|
||||||
@@ -61,6 +63,13 @@ func AddToContainer(container *restful.Container,
|
|||||||
Returns(http.StatusOK, api.StatusOK, nil).
|
Returns(http.StatusOK, api.StatusOK, nil).
|
||||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.MultiClusterTag}))
|
Metadata(restfulspec.KeyOpenAPITags, []string{constants.MultiClusterTag}))
|
||||||
|
|
||||||
|
webservice.Route(webservice.PUT("/clusters/{cluster}/kubeconfig").
|
||||||
|
Doc("Update cluster kubeconfig.").
|
||||||
|
Param(webservice.PathParameter("cluster", "Name of the cluster.").Required(true)).
|
||||||
|
To(h.updateKubeConfig).
|
||||||
|
Returns(http.StatusOK, api.StatusOK, nil).
|
||||||
|
Metadata(restfulspec.KeyOpenAPITags, []string{constants.MultiClusterTag}))
|
||||||
|
|
||||||
container.Add(webservice)
|
container.Add(webservice)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ func generateSwaggerJson() []byte {
|
|||||||
informerFactory := informers.NewNullInformerFactory()
|
informerFactory := informers.NewNullInformerFactory()
|
||||||
|
|
||||||
urlruntime.Must(oauth.AddToContainer(container, nil, nil, nil, nil, nil, nil))
|
urlruntime.Must(oauth.AddToContainer(container, nil, nil, nil, nil, nil, nil))
|
||||||
urlruntime.Must(clusterkapisv1alpha1.AddToContainer(container, informerFactory.KubernetesSharedInformerFactory(),
|
urlruntime.Must(clusterkapisv1alpha1.AddToContainer(container, clientsets.KubeSphere(), informerFactory.KubernetesSharedInformerFactory(),
|
||||||
informerFactory.KubeSphereSharedInformerFactory(), "", "", ""))
|
informerFactory.KubeSphereSharedInformerFactory(), "", "", ""))
|
||||||
urlruntime.Must(devopsv1alpha2.AddToContainer(container, ""))
|
urlruntime.Must(devopsv1alpha2.AddToContainer(container, ""))
|
||||||
urlruntime.Must(devopsv1alpha3.AddToContainer(container, ""))
|
urlruntime.Must(devopsv1alpha3.AddToContainer(container, ""))
|
||||||
|
|||||||
Reference in New Issue
Block a user