add kiali client with authentication supports
Signed-off-by: Roland.Ma <rolandma@yunify.com>
This commit is contained in:
@@ -29,8 +29,6 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/utils/signals"
|
||||
"kubesphere.io/kubesphere/pkg/utils/term"
|
||||
"kubesphere.io/kubesphere/pkg/version"
|
||||
|
||||
tracing "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2"
|
||||
)
|
||||
|
||||
func NewAPIServerCommand() *cobra.Command {
|
||||
@@ -90,8 +88,6 @@ cluster's shared state through which all other components interact.`,
|
||||
|
||||
func Run(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
|
||||
|
||||
initializeServicemeshConfig(s)
|
||||
|
||||
apiserver, err := s.NewAPIServer(stopCh)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -104,15 +100,3 @@ func Run(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
|
||||
|
||||
return apiserver.Run(stopCh)
|
||||
}
|
||||
|
||||
func initializeServicemeshConfig(s *options.ServerRunOptions) {
|
||||
// Config jaeger query endpoint address
|
||||
if s.ServiceMeshOptions != nil && len(s.ServiceMeshOptions.JaegerQueryHost) != 0 {
|
||||
tracing.JaegerQueryUrl = s.ServiceMeshOptions.JaegerQueryHost
|
||||
}
|
||||
|
||||
// Set the kiali query endpoint address
|
||||
if s.ServiceMeshOptions != nil && len(s.ServiceMeshOptions.KialiQueryHost) != 0 {
|
||||
tracing.KialiQueryUrl = s.ServiceMeshOptions.KialiQueryHost
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ func (s *APIServer) installKubeSphereAPIs() {
|
||||
auth.NewLoginRecorder(s.KubernetesClient.KubeSphere(),
|
||||
s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister()),
|
||||
s.Config.AuthenticationOptions))
|
||||
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container))
|
||||
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.Config.ServiceMeshOptions, s.container, s.KubernetesClient.Kubernetes(), s.CacheClient))
|
||||
urlruntime.Must(networkv1alpha2.AddToContainer(s.container, s.Config.NetworkOptions.WeaveScopeHost))
|
||||
urlruntime.Must(devopsv1alpha2.AddToContainer(s.container,
|
||||
s.InformerFactory.KubeSphereSharedInformerFactory(),
|
||||
|
||||
@@ -29,5 +29,5 @@ func init() {
|
||||
}
|
||||
|
||||
func Install(c *restful.Container) {
|
||||
urlruntime.Must(v1alpha2.AddToContainer(c))
|
||||
urlruntime.Must(v1alpha2.AddToContainer(nil, c, nil, nil))
|
||||
}
|
||||
|
||||
@@ -17,44 +17,67 @@ limitations under the License.
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/kiali"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/servicemesh"
|
||||
)
|
||||
|
||||
// default jaeger query api endpoint address
|
||||
var JaegerQueryUrl = "http://jaeger-query.istio-system.svc:16686"
|
||||
const (
|
||||
KubesphereNamespace = "kubesphere-system"
|
||||
KubeSphereServiceAccount = "kubesphere"
|
||||
)
|
||||
|
||||
/*
|
||||
Use Kiali API directly if config existed in configmap.
|
||||
Such as:
|
||||
kubectl -n kubesphere-system get cm kubesphere-config -oyaml
|
||||
...
|
||||
kialiQueryHost: http://kiali.istio-system:20001
|
||||
...
|
||||
type Handler struct {
|
||||
opt *servicemesh.Options
|
||||
client *kiali.Client
|
||||
}
|
||||
|
||||
Otherwise, use the API provided by kiali code.
|
||||
|
||||
Announce: The API provided by kiali code will deprecated in the future.
|
||||
*/
|
||||
|
||||
var KialiQueryUrl string
|
||||
func NewHandler(o *servicemesh.Options, client kubernetes.Interface, cache cache.Interface) *Handler {
|
||||
if o != nil && o.KialiQueryHost != "" {
|
||||
sa, err := client.CoreV1().ServiceAccounts(KubesphereNamespace).Get(context.TODO(), KubeSphereServiceAccount, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
secret, err := client.CoreV1().Secrets(KubesphereNamespace).Get(context.TODO(), sa.Secrets[0].Name, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
return &Handler{
|
||||
opt: o,
|
||||
client: kiali.NewDefaultClient(
|
||||
cache,
|
||||
string(secret.Data["token"]),
|
||||
o.KialiQueryHost,
|
||||
),
|
||||
}
|
||||
}
|
||||
klog.Warningf("get ServiceAccount's Secret failed %v", err)
|
||||
}
|
||||
klog.Warningf("get ServiceAccount failed %v", err)
|
||||
}
|
||||
// Handler should return Status code 400, instead of crash ks-apiserver
|
||||
// when no client is defined.
|
||||
return &Handler{opt: o, client: nil}
|
||||
}
|
||||
|
||||
// Get app metrics
|
||||
func getAppMetrics(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetAppMetrics(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
app := request.PathParameter("app")
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/apps/%s/metrics?%s", KialiQueryUrl, namespace, app, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/apps/%s/metrics?%s", namespace, app, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get workload metrics
|
||||
func getWorkloadMetrics(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetWorkloadMetrics(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
workload := request.PathParameter("workload")
|
||||
|
||||
@@ -62,77 +85,114 @@ func getWorkloadMetrics(request *restful.Request, response *restful.Response) {
|
||||
request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s&workload=%s", request.Request.URL.RawQuery, namespace, workload)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/workloads/%s/metrics?%s", KialiQueryUrl, namespace, workload, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/workloads/%s/metrics?%s", namespace, workload, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get service metrics
|
||||
func getServiceMetrics(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetServiceMetrics(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
service := request.PathParameter("service")
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/services/%s/metrics?%s", KialiQueryUrl, namespace, service, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/services/%s/metrics?%s", namespace, service, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get namespace metrics
|
||||
func getNamespaceMetrics(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetNamespaceMetrics(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/metrics?%s", KialiQueryUrl, namespace, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/metrics?%s", namespace, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get service graph for namespace
|
||||
func getNamespaceGraph(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetNamespaceGraph(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
|
||||
if len(namespace) > 0 {
|
||||
request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s", request.Request.URL.RawQuery, namespace)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/graph?%s", KialiQueryUrl, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/graph?%s", request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get namespace health
|
||||
func getNamespaceHealth(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetNamespaceHealth(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/health?%s", KialiQueryUrl, namespace, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/health?%s", namespace, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get workload health
|
||||
func getWorkloadHealth(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetWorkloadHealth(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
workload := request.PathParameter("workload")
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/workloads/%s/health?%s", KialiQueryUrl, namespace, workload, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/workloads/%s/health?%s", namespace, workload, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get app health
|
||||
func getAppHealth(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetAppHealth(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
app := request.PathParameter("app")
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/apps/%s/health?%s", KialiQueryUrl, namespace, app, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/apps/%s/health?%s", namespace, app, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
// Get service health
|
||||
func getServiceHealth(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetServiceHealth(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
service := request.PathParameter("service")
|
||||
url := fmt.Sprintf("%s/kiali/api/namespaces/%s/services/%s/health?%s", KialiQueryUrl, namespace, service, request.Request.URL.RawQuery)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("/kiali/api/namespaces/%s/services/%s/health?%s", namespace, service, request.Request.URL.RawQuery)
|
||||
h.getData(response, url)
|
||||
}
|
||||
|
||||
func getServiceTracing(request *restful.Request, response *restful.Response) {
|
||||
func (h *Handler) GetServiceTracing(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
service := request.PathParameter("service")
|
||||
serviceName := fmt.Sprintf("%s.%s", service, namespace)
|
||||
url := fmt.Sprintf("%s/api/traces?%s&service=%s", JaegerQueryUrl, request.Request.URL.RawQuery, serviceName)
|
||||
getData(response, url)
|
||||
url := fmt.Sprintf("%s/api/traces?%s&service=%s", h.opt.JaegerQueryHost, request.Request.URL.RawQuery, serviceName)
|
||||
h.getJaegerData(response, url)
|
||||
}
|
||||
|
||||
func getData(response *restful.Response, url string) {
|
||||
func (h *Handler) getData(response *restful.Response, url string) {
|
||||
|
||||
if h.client == nil {
|
||||
err := errors.New("kiali url is not defined")
|
||||
api.HandleInternalError(response, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.client.Get(url)
|
||||
klog.V(4).Infof("Proxy request to %s", url)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorf("query url %s failed with err %v", url, err)
|
||||
api.HandleInternalError(response, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
klog.Errorf("read response error : %v", err)
|
||||
api.HandleInternalError(response, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
// need to set header for proper response
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
_, err = response.Write(body)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorf("write response failed %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: to be removed with a security Jaeger client
|
||||
func (h *Handler) getJaegerData(response *restful.Response, url string) {
|
||||
|
||||
resp, err := http.Get(url)
|
||||
klog.V(4).Infof("Proxy request to %s", url)
|
||||
|
||||
|
||||
@@ -22,24 +22,29 @@ import (
|
||||
"github.com/emicklei/go-restful"
|
||||
restfulspec "github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/servicemesh"
|
||||
)
|
||||
|
||||
const groupName = "servicemesh.kubesphere.io"
|
||||
|
||||
var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha2"}
|
||||
|
||||
func AddToContainer(c *restful.Container) error {
|
||||
func AddToContainer(o *servicemesh.Options, c *restful.Container, client kubernetes.Interface, cache cache.Interface) error {
|
||||
|
||||
tags := []string{"ServiceMesh"}
|
||||
|
||||
webservice := runtime.NewWebService(GroupVersion)
|
||||
|
||||
h := NewHandler(o, client, cache)
|
||||
|
||||
// Get service metrics
|
||||
// GET /namespaces/{namespace}/services/{service}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/metrics").
|
||||
To(getServiceMetrics).
|
||||
To(h.GetServiceMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get service metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace")).
|
||||
@@ -59,7 +64,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
// Get app metrics
|
||||
// Get /namespaces/{namespace}/apps/{app}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/apps/{app}/metrics").
|
||||
To(getAppMetrics).
|
||||
To(h.GetAppMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get app metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace")).
|
||||
@@ -79,7 +84,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
// Get workload metrics
|
||||
// Get /namespaces/{namespace}/workloads/{workload}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/workloads/{workload}/metrics").
|
||||
To(getWorkloadMetrics).
|
||||
To(h.GetWorkloadMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get workload metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)).
|
||||
@@ -99,7 +104,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
// Get namespace metrics
|
||||
// Get /namespaces/{namespace}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/metrics").
|
||||
To(getNamespaceMetrics).
|
||||
To(h.GetNamespaceMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)).
|
||||
@@ -118,7 +123,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
// Get namespace graph
|
||||
// Get /namespaces/{namespace}/graph
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/graph").
|
||||
To(getNamespaceGraph).
|
||||
To(h.GetNamespaceGraph).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get service graph for a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
|
||||
@@ -133,7 +138,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
|
||||
// Get namespace health
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/health").
|
||||
To(getNamespaceHealth).
|
||||
To(h.GetNamespaceHealth).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get app/service/workload health of a namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
|
||||
@@ -145,7 +150,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
|
||||
// Get workloads health
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/workloads/{workload}/health").
|
||||
To(getWorkloadHealth).
|
||||
To(h.GetWorkloadHealth).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get workload health").
|
||||
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
|
||||
@@ -156,7 +161,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
|
||||
// Get app health
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/apps/{app}/health").
|
||||
To(getAppHealth).
|
||||
To(h.GetAppHealth).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get app health").
|
||||
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
|
||||
@@ -167,7 +172,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
|
||||
// Get service health
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/health").
|
||||
To(getServiceHealth).
|
||||
To(h.GetServiceHealth).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get service health").
|
||||
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
|
||||
@@ -178,7 +183,7 @@ func AddToContainer(c *restful.Container) error {
|
||||
|
||||
// Get service tracing
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/traces").
|
||||
To(getServiceTracing).
|
||||
To(h.GetServiceTracing).
|
||||
Doc("Get tracing of a service, should have servicemesh enabled first").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(webservice.PathParameter("namespace", "namespace of service").Required(true)).
|
||||
|
||||
174
pkg/simple/client/kiali/client.go
Normal file
174
pkg/simple/client/kiali/client.go
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright 2021 KubeSphere Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kiali
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
)
|
||||
|
||||
// Kiali token Response
|
||||
type TokenResponse struct {
|
||||
// The username for the token
|
||||
Username string `json:"username"`
|
||||
// The authentication token
|
||||
Token string `json:"token"`
|
||||
// The expired time for the token
|
||||
ExpiresOn string `json:"expiresOn"`
|
||||
}
|
||||
|
||||
// Kiali Authentication Strategy
|
||||
type Strategy string
|
||||
|
||||
const (
|
||||
AuthStrategyToken Strategy = "token"
|
||||
AuthStrategyAnonymous Strategy = "anonymous"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthURL = "%s/kiali/api/authenticate"
|
||||
KialiTokenCacheKey = "kubesphere:kubesphere:kiali"
|
||||
)
|
||||
|
||||
type HttpClient interface {
|
||||
// Do is an interface of http client Do method,
|
||||
// that sends an HTTP request and returns an HTTP response.
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
|
||||
// PostForm is an interface of http client PostForm method,
|
||||
// that issues a POST to the specified URL.
|
||||
PostForm(url string, data url.Values) (resp *http.Response, err error)
|
||||
}
|
||||
|
||||
// Kiali Client
|
||||
type Client struct {
|
||||
Strategy Strategy
|
||||
cache cache.Interface
|
||||
client HttpClient
|
||||
ServiceToken string
|
||||
Host string
|
||||
}
|
||||
|
||||
// NewClient creates an instance of Kiali Client.
|
||||
func NewClient(strategy Strategy,
|
||||
cache cache.Interface,
|
||||
client HttpClient,
|
||||
serviceToken string,
|
||||
host string) *Client {
|
||||
|
||||
return &Client{
|
||||
Strategy: strategy,
|
||||
cache: cache,
|
||||
client: client,
|
||||
ServiceToken: serviceToken,
|
||||
Host: host,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NewDefaultClient creates an instance of Kiali Client with default http settings.
|
||||
func NewDefaultClient(
|
||||
cache cache.Interface,
|
||||
serviceToken string,
|
||||
host string) *Client {
|
||||
return &Client{
|
||||
Strategy: AuthStrategyToken,
|
||||
cache: cache,
|
||||
client: &http.Client{},
|
||||
ServiceToken: serviceToken,
|
||||
Host: host,
|
||||
}
|
||||
}
|
||||
|
||||
// authenticate sends auth request with Kubernetes token and
|
||||
// get Kiali token from the response.
|
||||
func (c *Client) authenticate() (*TokenResponse, error) {
|
||||
resp, err := c.client.PostForm(fmt.Sprintf(AuthURL, c.Host), url.Values{
|
||||
"token": {c.ServiceToken},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
token := TokenResponse{}
|
||||
err = json.Unmarshal(body, &token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// Get issues a GET to the Kiali server with the url.
|
||||
func (c *Client) Get(url string) (resp *http.Response, err error) {
|
||||
|
||||
if req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s%s", c.Host, url), nil); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if c.Strategy == AuthStrategyToken {
|
||||
err := c.SetToken(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
c.clearTokenCache(err)
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) clearTokenCache(err error) {
|
||||
if c.cache != nil && err != nil {
|
||||
c.cache.Del(KialiTokenCacheKey)
|
||||
}
|
||||
}
|
||||
|
||||
// SetToken gets token from the Kiali server/cache and sets Bearer token to the request header.
|
||||
func (c *Client) SetToken(req *http.Request) error {
|
||||
if c.cache != nil {
|
||||
token, err := c.cache.Get(KialiTokenCacheKey)
|
||||
if err == nil {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
token, err := c.authenticate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.Token))
|
||||
|
||||
if c.cache != nil {
|
||||
c.cache.Set(KialiTokenCacheKey, token.Token, time.Hour)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
150
pkg/simple/client/kiali/client_test.go
Normal file
150
pkg/simple/client/kiali/client_test.go
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2021 KubeSphere Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kiali
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
)
|
||||
|
||||
func TestClient_Get(t *testing.T) {
|
||||
type fields struct {
|
||||
Strategy Strategy
|
||||
cache cache.Interface
|
||||
client HttpClient
|
||||
ServiceToken string
|
||||
Host string
|
||||
}
|
||||
type args struct {
|
||||
url string
|
||||
}
|
||||
token, _ := json.Marshal(
|
||||
&TokenResponse{
|
||||
Username: "test",
|
||||
Token: "test",
|
||||
},
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantResp *http.Response
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Anonymous",
|
||||
fields: fields{
|
||||
Strategy: AuthStrategyAnonymous,
|
||||
cache: nil,
|
||||
client: &MockClient{
|
||||
requestResult: "fake",
|
||||
},
|
||||
ServiceToken: "token",
|
||||
Host: "http://kiali.istio-system.svc",
|
||||
},
|
||||
args: args{url: "http://kiali.istio-system.svc"},
|
||||
wantResp: &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("fake"))),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Token",
|
||||
fields: fields{
|
||||
Strategy: AuthStrategyToken,
|
||||
cache: nil,
|
||||
client: &MockClient{
|
||||
tokenResult: token,
|
||||
requestResult: "fake",
|
||||
},
|
||||
ServiceToken: "token",
|
||||
Host: "http://kiali.istio-system.svc",
|
||||
},
|
||||
args: args{url: "http://kiali.istio-system.svc"},
|
||||
wantResp: &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("fake"))),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Token",
|
||||
fields: fields{
|
||||
Strategy: AuthStrategyToken,
|
||||
cache: cache.NewSimpleCache(),
|
||||
client: &MockClient{
|
||||
tokenResult: token,
|
||||
requestResult: "fake",
|
||||
},
|
||||
ServiceToken: "token",
|
||||
Host: "http://kiali.istio-system.svc",
|
||||
},
|
||||
args: args{url: "http://kiali.istio-system.svc"},
|
||||
wantResp: &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("fake"))),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := NewClient(
|
||||
tt.fields.Strategy,
|
||||
tt.fields.cache,
|
||||
tt.fields.client,
|
||||
tt.fields.ServiceToken,
|
||||
tt.fields.Host,
|
||||
)
|
||||
gotResp, err := c.Get(tt.args.url)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Client.Get() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotResp, tt.wantResp) {
|
||||
t.Errorf("Client.Get() = %v, want %v", gotResp, tt.wantResp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type MockClient struct {
|
||||
tokenResult []byte
|
||||
requestResult string
|
||||
}
|
||||
|
||||
func (c *MockClient) Do(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(c.requestResult))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *MockClient) PostForm(url string, data url.Values) (resp *http.Response, err error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(c.tokenResult)),
|
||||
}, nil
|
||||
}
|
||||
@@ -133,7 +133,7 @@ func generateSwaggerJson() []byte {
|
||||
urlruntime.Must(resourcesv1alpha3.AddToContainer(container, informerFactory, nil))
|
||||
urlruntime.Must(tenantv1alpha2.AddToContainer(container, informerFactory, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil))
|
||||
urlruntime.Must(terminalv1alpha2.AddToContainer(container, clientsets.Kubernetes(), nil, nil))
|
||||
urlruntime.Must(metricsv1alpha2.AddToContainer(container))
|
||||
urlruntime.Must(metricsv1alpha2.AddToContainer(nil, container, clientsets.Kubernetes(), nil))
|
||||
urlruntime.Must(networkv1alpha2.AddToContainer(container, ""))
|
||||
alertingOptions := &alerting.Options{}
|
||||
alertingClient, _ := alerting.NewRuleClient(alertingOptions)
|
||||
|
||||
Reference in New Issue
Block a user