Merge pull request #4243 from RolandMa1986/feat-gateway-pods
feat: get gateway's workload and status
This commit is contained in:
@@ -273,7 +273,7 @@ func (s *APIServer) installKubeSphereAPIs() {
|
||||
urlruntime.Must(notificationkapisv2beta1.AddToContainer(s.container, s.InformerFactory, s.KubernetesClient.Kubernetes(),
|
||||
s.KubernetesClient.KubeSphere()))
|
||||
urlruntime.Must(notificationkapisv2beta2.AddToContainer(s.container, s.Config.NotificationOptions))
|
||||
urlruntime.Must(gatewayv1alpha1.AddToContainer(s.container, s.Config.GatewayOptions, s.RuntimeCache, s.RuntimeClient))
|
||||
urlruntime.Must(gatewayv1alpha1.AddToContainer(s.container, s.Config.GatewayOptions, s.RuntimeCache, s.RuntimeClient, s.InformerFactory, s.KubernetesClient.Kubernetes()))
|
||||
}
|
||||
|
||||
func (s *APIServer) Run(ctx context.Context) (err error) {
|
||||
|
||||
@@ -17,30 +17,44 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"kubesphere.io/api/gateway/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apiserver/pkg/util/flushwriter"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"kubesphere.io/api/gateway/v1alpha1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
operator "kubesphere.io/kubesphere/pkg/models/gateway"
|
||||
|
||||
servererr "kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/gateway"
|
||||
conversionsv1 "kubesphere.io/kubesphere/pkg/utils/conversions/core/v1"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
options *gateway.Options
|
||||
gw operator.GatewayOperator
|
||||
factory informers.InformerFactory
|
||||
}
|
||||
|
||||
//newHandler create an instance of the handler
|
||||
func newHandler(options *gateway.Options, cache cache.Cache, client client.Client) *handler {
|
||||
func newHandler(options *gateway.Options, cache cache.Cache, client client.Client, factory informers.InformerFactory, k8sClient kubernetes.Interface) *handler {
|
||||
conversionsv1.RegisterConversions(scheme.Scheme)
|
||||
// Do not register Gateway scheme globally. Which will cause conflict in ks-controller-manager.
|
||||
v1alpha1.AddToScheme(client.Scheme())
|
||||
return &handler{
|
||||
options: options,
|
||||
gw: operator.NewGatewayOperator(client, cache, options),
|
||||
factory: factory,
|
||||
gw: operator.NewGatewayOperator(client, cache, options, factory, k8sClient),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,3 +140,36 @@ func (h *handler) List(request *restful.Request, response *restful.Response) {
|
||||
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) ListPods(request *restful.Request, response *restful.Response) {
|
||||
queryParam := query.ParseQueryParameter(request)
|
||||
ns := request.PathParameter("namespace")
|
||||
|
||||
result, err := h.gw.GetPods(ns, queryParam)
|
||||
if err != nil {
|
||||
api.HandleError(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) PodLog(request *restful.Request, response *restful.Response) {
|
||||
|
||||
podNamespace := request.PathParameter("namespace")
|
||||
podID := request.PathParameter("pod")
|
||||
|
||||
query := request.Request.URL.Query()
|
||||
logOptions := &corev1.PodLogOptions{}
|
||||
if err := scheme.ParameterCodec.DecodeParameters(query, corev1.SchemeGroupVersion, logOptions); err != nil {
|
||||
api.HandleError(response, request, fmt.Errorf("unable to decode query"))
|
||||
return
|
||||
}
|
||||
|
||||
fw := flushwriter.Wrap(response.ResponseWriter)
|
||||
err := h.gw.GetPodLogs(context.TODO(), podNamespace, podID, logOptions, fw)
|
||||
if err != nil {
|
||||
api.HandleError(response, request, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ 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/api/gateway/v1alpha1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -29,16 +30,17 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/gateway"
|
||||
)
|
||||
|
||||
var GroupVersion = schema.GroupVersion{Group: "gateway.kubesphere.io", Version: "v1alpha1"}
|
||||
|
||||
func AddToContainer(container *restful.Container, options *gateway.Options, cache cache.Cache, client client.Client) error {
|
||||
func AddToContainer(container *restful.Container, options *gateway.Options, cache cache.Cache, client client.Client, factory informers.InformerFactory, k8sClient kubernetes.Interface) error {
|
||||
ws := runtime.NewWebService(GroupVersion)
|
||||
|
||||
handler := newHandler(options, cache, client)
|
||||
handler := newHandler(options, cache, client, factory, k8sClient)
|
||||
|
||||
// register gateway apis
|
||||
ws.Route(ws.POST("/namespaces/{namespace}/gateways").
|
||||
@@ -85,6 +87,21 @@ func AddToContainer(container *restful.Container, options *gateway.Options, cach
|
||||
Returns(http.StatusOK, api.StatusOK, v1alpha1.Gateway{}).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GatewayTag}))
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/gateways/{gateway}/pods").
|
||||
To(handler.ListPods).
|
||||
Doc("Retrieve gateways workload pods.").
|
||||
Param(ws.PathParameter("namespace", "the watching namespace of the gateway")).
|
||||
Returns(http.StatusOK, api.StatusOK, v1alpha1.Gateway{}).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GatewayTag}))
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/gateways/{gateway}/pods/{pod}/log").
|
||||
To(handler.PodLog).
|
||||
Doc("Retrieve log of the gateway's pod").
|
||||
Param(ws.PathParameter("namespace", "the watching namespace of the gateway")).
|
||||
Param(ws.PathParameter("pod", "the pod name of the gateway")).
|
||||
Returns(http.StatusOK, api.StatusOK, v1alpha1.Gateway{}).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.GatewayTag}))
|
||||
|
||||
container.Add(ws)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,14 +19,19 @@ package gateway
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"kubesphere.io/api/gateway/v1alpha1"
|
||||
@@ -35,11 +40,14 @@ import (
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/pod"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/gateway"
|
||||
)
|
||||
|
||||
const (
|
||||
MasterLabel = "node-role.kubernetes.io/master"
|
||||
SidecarInject = "sidecar.istio.io/inject"
|
||||
gatewayPrefix = "kubesphere-router-"
|
||||
workingNamespace = "kubesphere-controls-system"
|
||||
@@ -54,19 +62,25 @@ type GatewayOperator interface {
|
||||
UpdateGateway(namespace string, obj *v1alpha1.Gateway) (*v1alpha1.Gateway, error)
|
||||
UpgradeGateway(namespace string) (*v1alpha1.Gateway, error)
|
||||
ListGateways(query *query.Query) (*api.ListResult, error)
|
||||
GetPods(namesapce string, query *query.Query) (*api.ListResult, error)
|
||||
GetPodLogs(ctx context.Context, namespace string, podName string, logOptions *corev1.PodLogOptions, responseWriter io.Writer) error
|
||||
}
|
||||
|
||||
type gatewayOperator struct {
|
||||
client client.Client
|
||||
cache cache.Cache
|
||||
options *gateway.Options
|
||||
k8sclient kubernetes.Interface
|
||||
factory informers.InformerFactory
|
||||
client client.Client
|
||||
cache cache.Cache
|
||||
options *gateway.Options
|
||||
}
|
||||
|
||||
func NewGatewayOperator(client client.Client, cache cache.Cache, options *gateway.Options) GatewayOperator {
|
||||
func NewGatewayOperator(client client.Client, cache cache.Cache, options *gateway.Options, factory informers.InformerFactory, k8sclient kubernetes.Interface) GatewayOperator {
|
||||
return &gatewayOperator{
|
||||
client: client,
|
||||
cache: cache,
|
||||
options: options,
|
||||
client: client,
|
||||
cache: cache,
|
||||
options: options,
|
||||
k8sclient: k8sclient,
|
||||
factory: factory,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +177,69 @@ func (c *gatewayOperator) convert(namespace string, svc *corev1.Service, deploy
|
||||
return &legacy
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) getMasterNodeIp() []string {
|
||||
internalIps := []string{}
|
||||
masters := &corev1.NodeList{}
|
||||
err := c.cache.List(context.TODO(), masters, &client.ListOptions{LabelSelector: labels.SelectorFromSet(
|
||||
labels.Set{
|
||||
MasterLabel: "",
|
||||
})})
|
||||
|
||||
if err != nil {
|
||||
klog.Info(err)
|
||||
return internalIps
|
||||
}
|
||||
|
||||
for _, node := range masters.Items {
|
||||
for _, address := range node.Status.Addresses {
|
||||
if address.Type == corev1.NodeInternalIP {
|
||||
internalIps = append(internalIps, address.Address)
|
||||
}
|
||||
}
|
||||
}
|
||||
return internalIps
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) updateStatus(gateway *v1alpha1.Gateway, svc *corev1.Service) (*v1alpha1.Gateway, error) {
|
||||
// append selected node ip as loadbalancer ingress ip
|
||||
if svc.Spec.Type != corev1.ServiceTypeLoadBalancer && len(svc.Status.LoadBalancer.Ingress) == 0 {
|
||||
rips := c.getMasterNodeIp()
|
||||
for _, rip := range rips {
|
||||
gIngress := corev1.LoadBalancerIngress{
|
||||
IP: rip,
|
||||
}
|
||||
svc.Status.LoadBalancer.Ingress = append(svc.Status.LoadBalancer.Ingress, gIngress)
|
||||
}
|
||||
}
|
||||
|
||||
status := unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"loadBalancer": svc.Status.LoadBalancer,
|
||||
"service": svc.Spec.Ports,
|
||||
},
|
||||
}
|
||||
|
||||
target, err := status.MarshalJSON()
|
||||
if err != nil {
|
||||
return gateway, err
|
||||
}
|
||||
if gateway.Status.Raw != nil {
|
||||
//merge with origin status
|
||||
patch, err := jsonpatch.CreateMergePatch([]byte(`{}`), target)
|
||||
if err != nil {
|
||||
return gateway, err
|
||||
}
|
||||
modified, err := jsonpatch.MergePatch(gateway.Status.Raw, patch)
|
||||
if err != nil {
|
||||
return gateway, err
|
||||
}
|
||||
gateway.Status.Raw = modified
|
||||
return gateway, err
|
||||
}
|
||||
gateway.Status.Raw = target
|
||||
return gateway, nil
|
||||
}
|
||||
|
||||
// GetGateways returns all Gateways from the project. There are at most 2 gatways exists in a project,
|
||||
// a Glabal Gateway and a Project Gateway or a Legacy Project Gateway.
|
||||
func (c *gatewayOperator) GetGateways(namespace string) ([]*v1alpha1.Gateway, error) {
|
||||
@@ -188,6 +265,22 @@ func (c *gatewayOperator) GetGateways(namespace string) ([]*v1alpha1.Gateway, er
|
||||
return nil, err
|
||||
}
|
||||
gateways = append(gateways, obj)
|
||||
|
||||
for _, g := range gateways {
|
||||
s := &corev1.Service{}
|
||||
// We supports the Service name always as same as gateway name.
|
||||
// TODO: We need a mapping relation between the service and the gateway. Label Selector should be a good option.
|
||||
err := c.client.Get(context.TODO(), client.ObjectKeyFromObject(g), s)
|
||||
if err != nil {
|
||||
klog.Info(err)
|
||||
continue
|
||||
}
|
||||
_, err = c.updateStatus(g, s)
|
||||
if err != nil {
|
||||
klog.Info(err)
|
||||
}
|
||||
}
|
||||
|
||||
return gateways, err
|
||||
}
|
||||
|
||||
@@ -312,12 +405,28 @@ func (c *gatewayOperator) ListGateways(query *query.Query) (*api.ListResult, err
|
||||
|
||||
func (c *gatewayOperator) transform(obj runtime.Object) runtime.Object {
|
||||
if g, ok := obj.(*v1alpha1.Gateway); ok {
|
||||
svc := &corev1.Service{}
|
||||
// We supports the Service name always same as gateway name.
|
||||
err := c.client.Get(context.TODO(), client.ObjectKeyFromObject(g), svc)
|
||||
if err != nil {
|
||||
klog.Info(err)
|
||||
return g
|
||||
}
|
||||
g, err := c.updateStatus(g, svc)
|
||||
if err != nil {
|
||||
klog.Info(err)
|
||||
}
|
||||
return g
|
||||
|
||||
}
|
||||
if s, ok := obj.(*corev1.Service); ok {
|
||||
if svc, ok := obj.(*corev1.Service); ok {
|
||||
d := &appsv1.Deployment{}
|
||||
c.client.Get(context.TODO(), client.ObjectKeyFromObject(s), d)
|
||||
return c.convert(s.Labels["project"], s, d)
|
||||
c.client.Get(context.TODO(), client.ObjectKeyFromObject(svc), d)
|
||||
g, err := c.updateStatus(c.convert(svc.Labels["project"], svc, d), svc)
|
||||
if err != nil {
|
||||
klog.Info(err)
|
||||
}
|
||||
return g
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -350,3 +459,50 @@ func (c *gatewayOperator) filter(object runtime.Object, filter query.Filter) boo
|
||||
return v1alpha3.DefaultObjectMetaFilter(gateway.ObjectMeta, filter)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) GetPods(namesapce string, query *query.Query) (*api.ListResult, error) {
|
||||
podGetter := pod.New(c.factory.KubernetesSharedInformerFactory())
|
||||
|
||||
//TODO: move the selector string to options
|
||||
selector, err := labels.Parse(fmt.Sprintf("app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/instance=kubesphere-router-%s-ingress", namesapce))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invild selector config")
|
||||
}
|
||||
query.LabelSelector = selector.String()
|
||||
return podGetter.List(c.getWorkingNamespace(namesapce), query)
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) GetPodLogs(ctx context.Context, namespace string, podName string, logOptions *corev1.PodLogOptions, responseWriter io.Writer) error {
|
||||
workingNamespace := c.getWorkingNamespace(namespace)
|
||||
|
||||
pods, err := c.GetPods(namespace, query.New())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !c.hasPod(pods.Items, types.NamespacedName{Namespace: workingNamespace, Name: podName}) {
|
||||
return fmt.Errorf("pod does not exist")
|
||||
}
|
||||
|
||||
podLogRequest := c.k8sclient.CoreV1().
|
||||
Pods(workingNamespace).
|
||||
GetLogs(podName, logOptions)
|
||||
reader, err := podLogRequest.Stream(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(responseWriter, reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) hasPod(slice []interface{}, key types.NamespacedName) bool {
|
||||
for _, s := range slice {
|
||||
pod, ok := s.(*corev1.Pod)
|
||||
if ok && client.ObjectKeyFromObject(pod) == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
|
||||
type fields struct {
|
||||
client client.Client
|
||||
cache cache.Cache
|
||||
options *gateway.Options
|
||||
}
|
||||
type args struct {
|
||||
@@ -50,6 +51,8 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
|
||||
var Scheme = runtime.NewScheme()
|
||||
v1alpha1.AddToScheme(Scheme)
|
||||
corev1.AddToScheme(Scheme)
|
||||
|
||||
client := fake.NewFakeClientWithScheme(Scheme)
|
||||
|
||||
client.Create(context.TODO(), &v1alpha1.Gateway{
|
||||
@@ -69,7 +72,6 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
client2 := fake.NewFakeClientWithScheme(Scheme)
|
||||
create_GlobalGateway(client2)
|
||||
|
||||
corev1.AddToScheme(Scheme)
|
||||
client3 := fake.NewFakeClientWithScheme(Scheme)
|
||||
create_LegacyGateway(client3, "project6")
|
||||
|
||||
@@ -84,6 +86,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
name: "return empty gateway list from watching namespace",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "",
|
||||
},
|
||||
@@ -96,6 +99,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
name: "return empty gateway list from working namespace",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
@@ -108,6 +112,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
name: "get gateway from watching namespace",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "",
|
||||
},
|
||||
@@ -121,6 +126,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
name: "get gateway from working namespace",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
@@ -134,6 +140,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
name: "get global gateway",
|
||||
fields: fields{
|
||||
client: client2,
|
||||
cache: &fakeClient{Client: client2},
|
||||
options: &gateway.Options{
|
||||
Namespace: "",
|
||||
},
|
||||
@@ -147,6 +154,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
name: "get Legacy gateway",
|
||||
fields: fields{
|
||||
client: client3,
|
||||
cache: &fakeClient{Client: client3},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
@@ -182,6 +190,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &gatewayOperator{
|
||||
client: tt.fields.client,
|
||||
cache: tt.fields.cache,
|
||||
options: tt.fields.options,
|
||||
}
|
||||
got, err := c.GetGateways(tt.args.namespace)
|
||||
@@ -239,6 +248,11 @@ func create_LegacyGateway(c client.Client, namespace string) {
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http", Protocol: corev1.ProtocolTCP, Port: 80,
|
||||
},
|
||||
},
|
||||
Type: corev1.ServiceTypeNodePort,
|
||||
},
|
||||
}
|
||||
@@ -288,6 +302,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
type fields struct {
|
||||
client client.Client
|
||||
options *gateway.Options
|
||||
cache cache.Cache
|
||||
}
|
||||
type args struct {
|
||||
namespace string
|
||||
@@ -296,6 +311,9 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
|
||||
var Scheme = runtime.NewScheme()
|
||||
v1alpha1.AddToScheme(Scheme)
|
||||
corev1.AddToScheme(Scheme)
|
||||
appsv1.AddToScheme(Scheme)
|
||||
|
||||
client := fake.NewFakeClientWithScheme(Scheme)
|
||||
|
||||
tests := []struct {
|
||||
@@ -309,6 +327,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
name: "creates gateway in watching namespace",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "",
|
||||
},
|
||||
@@ -339,6 +358,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
name: "creates gateway in working namespace",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
@@ -370,6 +390,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &gatewayOperator{
|
||||
client: tt.fields.client,
|
||||
cache: tt.fields.cache,
|
||||
options: tt.fields.options,
|
||||
}
|
||||
got, err := c.CreateGateway(tt.args.namespace, tt.args.obj)
|
||||
@@ -686,6 +707,9 @@ func Test_gatewayOperator_ListGateways(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: runtime.RawExtension{
|
||||
Raw: []byte("{\"loadBalancer\":{},\"service\":[{\"name\":\"http\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":0}]}\n"),
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -790,3 +814,153 @@ func (f *fakeClient) WaitForCacheSync(ctx context.Context) bool {
|
||||
func (f *fakeClient) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_gatewayOperator_status(t *testing.T) {
|
||||
type fields struct {
|
||||
client client.Client
|
||||
cache cache.Cache
|
||||
options *gateway.Options
|
||||
}
|
||||
|
||||
var Scheme = runtime.NewScheme()
|
||||
v1alpha1.AddToScheme(Scheme)
|
||||
corev1.AddToScheme(Scheme)
|
||||
appsv1.AddToScheme(Scheme)
|
||||
|
||||
client := fake.NewFakeClientWithScheme(Scheme)
|
||||
client2 := fake.NewFakeClientWithScheme(Scheme)
|
||||
|
||||
fake := &corev1.Node{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "fake-node",
|
||||
Labels: map[string]string{
|
||||
MasterLabel: "",
|
||||
},
|
||||
},
|
||||
Status: corev1.NodeStatus{
|
||||
Addresses: []corev1.NodeAddress{
|
||||
{
|
||||
Type: corev1.NodeInternalIP,
|
||||
Address: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
client2.Create(context.TODO(), fake)
|
||||
|
||||
type args struct {
|
||||
gateway *v1alpha1.Gateway
|
||||
svc *corev1.Service
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *v1alpha1.Gateway
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
gateway: &v1alpha1.Gateway{},
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http", Protocol: corev1.ProtocolTCP, Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &v1alpha1.Gateway{
|
||||
Status: runtime.RawExtension{
|
||||
Raw: []byte("{\"loadBalancer\":{},\"service\":[{\"name\":\"http\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":0}]}\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
gateway: &v1alpha1.Gateway{
|
||||
Status: runtime.RawExtension{
|
||||
Raw: []byte("{\"fake\":{}}"),
|
||||
},
|
||||
},
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http", Protocol: corev1.ProtocolTCP, Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &v1alpha1.Gateway{
|
||||
Status: runtime.RawExtension{
|
||||
Raw: []byte("{\"fake\":{},\"loadBalancer\":{},\"service\":[{\"name\":\"http\",\"port\":80,\"protocol\":\"TCP\",\"targetPort\":0}]}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Master Node IP",
|
||||
fields: fields{
|
||||
client: client2,
|
||||
cache: &fakeClient{Client: client2},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
gateway: &v1alpha1.Gateway{},
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http", Protocol: corev1.ProtocolTCP, Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &v1alpha1.Gateway{
|
||||
Status: runtime.RawExtension{
|
||||
Raw: []byte("{\"loadBalancer\":{\"ingress\":[{\"ip\":\"192.168.1.1\"}]},\"service\":[{\"name\":\"http\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":0}]}\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &gatewayOperator{
|
||||
client: tt.fields.client,
|
||||
cache: tt.fields.cache,
|
||||
options: tt.fields.options,
|
||||
}
|
||||
got, err := c.updateStatus(tt.args.gateway, tt.args.svc)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("gatewayOperator.status() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("gatewayOperator.status() has wrong object\nDiff:\n %s", diff.ObjectGoPrintSideBySide(tt.want, got))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
113
pkg/utils/conversions/core/v1/conversion.go
Normal file
113
pkg/utils/conversions/core/v1/conversion.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright The Kubernetes 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.
|
||||
*/
|
||||
|
||||
// Code generated by conversion-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
url "net/url"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// refer to https://github.com/kubernetes/kubernetes/issues/94688
|
||||
// conversions must be registered.
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*v1.PodLogOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_url_Values_To_v1_PodLogOptions(a.(*url.Values), b.(*v1.PodLogOptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_url_Values_To_v1_PodLogOptions(in *url.Values, out *v1.PodLogOptions, s conversion.Scope) error {
|
||||
// WARNING: Field TypeMeta does not have json tag, skipping.
|
||||
|
||||
if values, ok := map[string][]string(*in)["container"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_string(&values, &out.Container, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Container = ""
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["follow"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_bool(&values, &out.Follow, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Follow = false
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["previous"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_bool(&values, &out.Previous, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Previous = false
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["sinceSeconds"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_Pointer_int64(&values, &out.SinceSeconds, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.SinceSeconds = nil
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["sinceTime"]; ok && len(values) > 0 {
|
||||
if err := metav1.Convert_Slice_string_To_Pointer_v1_Time(&values, &out.SinceTime, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.SinceTime = nil
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["timestamps"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_bool(&values, &out.Timestamps, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Timestamps = false
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["tailLines"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_Pointer_int64(&values, &out.TailLines, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.TailLines = nil
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["limitBytes"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_Pointer_int64(&values, &out.LimitBytes, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.LimitBytes = nil
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["insecureSkipTLSVerifyBackend"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_bool(&values, &out.InsecureSkipTLSVerifyBackend, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.InsecureSkipTLSVerifyBackend = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_url_Values_To_v1_PodLogOptions is an autogenerated conversion function.
|
||||
func Convert_url_Values_To_v1_PodLogOptions(in *url.Values, out *v1.PodLogOptions, s conversion.Scope) error {
|
||||
return autoConvert_url_Values_To_v1_PodLogOptions(in, out, s)
|
||||
}
|
||||
Reference in New Issue
Block a user