mirror of
https://github.com/alibaba/higress.git
synced 2026-04-21 20:17:29 +08:00
Support nacos discovery (#108)
This commit is contained in:
163
pkg/kube/client.go
Normal file
163
pkg/kube/client.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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 kube
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
istiokube "istio.io/istio/pkg/kube"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
clienttesting "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
higressclient "github.com/alibaba/higress/client/pkg/clientset/versioned"
|
||||
higressfake "github.com/alibaba/higress/client/pkg/clientset/versioned/fake"
|
||||
higressinformer "github.com/alibaba/higress/client/pkg/informers/externalversions"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
istiokube.Client
|
||||
|
||||
// Higress returns the Higress kube client.
|
||||
Higress() higressclient.Interface
|
||||
|
||||
// HigressInformer returns an informer for the higress client
|
||||
HigressInformer() higressinformer.SharedInformerFactory
|
||||
}
|
||||
|
||||
type client struct {
|
||||
istiokube.Client
|
||||
|
||||
higress higressclient.Interface
|
||||
higressInformer higressinformer.SharedInformerFactory
|
||||
|
||||
// If enable, will wait for cache syncs with extremely short delay. This should be used only for tests
|
||||
fastSync bool
|
||||
informerWatchesPending *atomic.Int32
|
||||
}
|
||||
|
||||
const resyncInterval = 0
|
||||
|
||||
func NewFakeClient(objects ...runtime.Object) Client {
|
||||
c := &client{
|
||||
Client: istiokube.NewFakeClient(objects...),
|
||||
}
|
||||
c.higress = higressfake.NewSimpleClientset()
|
||||
c.higressInformer = higressinformer.NewSharedInformerFactoryWithOptions(c.higress, resyncInterval)
|
||||
|
||||
// https://github.com/kubernetes/kubernetes/issues/95372
|
||||
// There is a race condition in the client fakes, where events that happen between the List and Watch
|
||||
// of an informer are dropped. To avoid this, we explicitly manage the list and watch, ensuring all lists
|
||||
// have an associated watch before continuing.
|
||||
// This would likely break any direct calls to List(), but for now our tests don't do that anyways. If we need
|
||||
// to in the future we will need to identify the Lists that have a corresponding Watch, possibly by looking
|
||||
// at created Informers
|
||||
// an atomic.Int is used instead of sync.WaitGroup because wg.Add and wg.Wait cannot be called concurrently
|
||||
listReactor := func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
c.informerWatchesPending.Inc()
|
||||
return false, nil, nil
|
||||
}
|
||||
watchReactor := func(tracker clienttesting.ObjectTracker) func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
|
||||
return func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
|
||||
gvr := action.GetResource()
|
||||
ns := action.GetNamespace()
|
||||
watch, err := tracker.Watch(gvr, ns)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
c.informerWatchesPending.Dec()
|
||||
return true, watch, nil
|
||||
}
|
||||
}
|
||||
fc := c.higress.(*higressfake.Clientset)
|
||||
fc.PrependReactor("list", "&", listReactor)
|
||||
fc.PrependWatchReactor("*", watchReactor(fc.Tracker()))
|
||||
c.fastSync = true
|
||||
return c
|
||||
}
|
||||
|
||||
func NewClient(clientConfig clientcmd.ClientConfig) (Client, error) {
|
||||
var c client
|
||||
istioClient, err := istiokube.NewClient(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Client = istioClient
|
||||
|
||||
c.higress, err = higressclient.NewForConfig(istioClient.RESTConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.higressInformer = higressinformer.NewSharedInformerFactory(c.higress, resyncInterval)
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c *client) Higress() higressclient.Interface {
|
||||
return c.higress
|
||||
}
|
||||
|
||||
func (c *client) HigressInformer() higressinformer.SharedInformerFactory {
|
||||
return c.higressInformer
|
||||
}
|
||||
|
||||
func (c *client) RunAndWait(stop <-chan struct{}) {
|
||||
c.Client.RunAndWait(stop)
|
||||
c.higressInformer.Start(stop)
|
||||
if c.fastSync {
|
||||
fastWaitForCacheSync(stop, c.higressInformer)
|
||||
_ = wait.PollImmediate(time.Microsecond*100, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
select {
|
||||
case <-stop:
|
||||
return false, fmt.Errorf("channel closed")
|
||||
default:
|
||||
}
|
||||
if c.informerWatchesPending.Load() == 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
} else {
|
||||
c.higressInformer.WaitForCacheSync(stop)
|
||||
}
|
||||
}
|
||||
|
||||
type reflectInformerSync interface {
|
||||
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
|
||||
}
|
||||
|
||||
// Wait for cache sync immediately, rather than with 100ms delay which slows tests
|
||||
// See https://github.com/kubernetes/kubernetes/issues/95262#issuecomment-703141573
|
||||
func fastWaitForCacheSync(stop <-chan struct{}, informerFactory reflectInformerSync) {
|
||||
returnImmediately := make(chan struct{})
|
||||
close(returnImmediately)
|
||||
_ = wait.PollImmediate(time.Microsecond*100, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
select {
|
||||
case <-stop:
|
||||
return false, fmt.Errorf("channel closed")
|
||||
default:
|
||||
}
|
||||
for _, synced := range informerFactory.WaitForCacheSync(returnImmediately) {
|
||||
if !synced {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user