diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 3c72773b9..c05c41c9a 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -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) { diff --git a/pkg/kapis/gateway/v1alpha1/handler.go b/pkg/kapis/gateway/v1alpha1/handler.go index a31351c12..6e2c0974c 100644 --- a/pkg/kapis/gateway/v1alpha1/handler.go +++ b/pkg/kapis/gateway/v1alpha1/handler.go @@ -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 + } +} diff --git a/pkg/kapis/gateway/v1alpha1/register.go b/pkg/kapis/gateway/v1alpha1/register.go index c3bc84687..bbcf715af 100644 --- a/pkg/kapis/gateway/v1alpha1/register.go +++ b/pkg/kapis/gateway/v1alpha1/register.go @@ -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 } diff --git a/pkg/models/gateway/gateway.go b/pkg/models/gateway/gateway.go index 24d64804b..ec4d4d506 100644 --- a/pkg/models/gateway/gateway.go +++ b/pkg/models/gateway/gateway.go @@ -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 +} diff --git a/pkg/models/gateway/gateway_test.go b/pkg/models/gateway/gateway_test.go index 5e2e170fb..1b8efeb08 100644 --- a/pkg/models/gateway/gateway_test.go +++ b/pkg/models/gateway/gateway_test.go @@ -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)) + } + }) + } +} diff --git a/pkg/utils/conversions/core/v1/conversion.go b/pkg/utils/conversions/core/v1/conversion.go new file mode 100644 index 000000000..3424a7125 --- /dev/null +++ b/pkg/utils/conversions/core/v1/conversion.go @@ -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) +}