Change Kubernetes client to official go-client in the events binding. (#436)
* Change Kubernetes client to official go-client in the events binding. * Change adding flags to init * go mod tidy * Proper call for handler * Remove commented out code * Refactor indentation
This commit is contained in:
parent
467375c254
commit
c6623da87e
|
|
@ -10,22 +10,27 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
// GetKubeClient returns a kubernetes client
|
||||
func GetKubeClient() (*kubernetes.Clientset, error) {
|
||||
var kubeconfig *string
|
||||
// nolint:gochecknoglobals
|
||||
var kubeconfig *string
|
||||
|
||||
// nolint:gochecknoinits
|
||||
func init() {
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
|
||||
} else {
|
||||
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
|
||||
}
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// GetKubeClient returns a kubernetes client
|
||||
func GetKubeClient() (*kubernetes.Clientset, error) {
|
||||
flag.Parse()
|
||||
conf, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
conf, err = clientcmd.BuildConfigFromFlags("", *kubeconfig)
|
||||
|
|
@ -6,20 +6,35 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
kubeclient "github.com/dapr/components-contrib/authentication/kubernetes"
|
||||
|
||||
"github.com/dapr/components-contrib/bindings"
|
||||
"github.com/dapr/dapr/pkg/logger"
|
||||
"github.com/kubernetes-client/go/kubernetes/client"
|
||||
"github.com/kubernetes-client/go/kubernetes/config"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
type kubernetesInput struct {
|
||||
config *client.Configuration
|
||||
client *client.APIClient
|
||||
namespace string
|
||||
logger logger.Logger
|
||||
kubeClient kubernetes.Interface
|
||||
namespace string
|
||||
resyncPeriodInSec time.Duration // nolint:stylecheck
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type EventResponse struct {
|
||||
Event string `json:"event"`
|
||||
OldVal v1.Event `json:"oldVal"`
|
||||
NewVal v1.Event `json:"newVal"`
|
||||
}
|
||||
|
||||
var _ = bindings.InputBinding(&kubernetesInput{})
|
||||
|
|
@ -30,55 +45,98 @@ func NewKubernetes(logger logger.Logger) bindings.InputBinding {
|
|||
}
|
||||
|
||||
func (k *kubernetesInput) Init(metadata bindings.Metadata) error {
|
||||
c, err := config.LoadKubeConfig()
|
||||
client, err := kubeclient.GetKubeClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.config = c
|
||||
k.client = client.NewAPIClient(c)
|
||||
k.kubeClient = client
|
||||
return k.parseMetadata(metadata)
|
||||
}
|
||||
|
||||
func (k *kubernetesInput) parseMetadata(metadata bindings.Metadata) error {
|
||||
ns, found := metadata.Properties["namespace"]
|
||||
if found {
|
||||
k.namespace = ns
|
||||
if val, ok := metadata.Properties["namespace"]; ok && val != "" {
|
||||
k.namespace = val
|
||||
} else {
|
||||
k.namespace = "default"
|
||||
return errors.New("namespace is missing in metadata")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *kubernetesInput) Read(handler func(*bindings.ReadResponse) error) error {
|
||||
watch := client.WatchClient{
|
||||
Cfg: k.config,
|
||||
Client: k.client,
|
||||
Path: "/api/v1/namespaces/" + k.namespace + "/events",
|
||||
MakerFn: func() interface{} { return &client.V1Event{} },
|
||||
}
|
||||
|
||||
resultChan, errChan, err := watch.Connect(context.Background(), "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
done := false
|
||||
for !done {
|
||||
select {
|
||||
case obj, ok := <-resultChan:
|
||||
if !ok {
|
||||
done = true
|
||||
break
|
||||
}
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handler(&bindings.ReadResponse{
|
||||
Data: data,
|
||||
})
|
||||
case err := <-errChan:
|
||||
return err
|
||||
if val, ok := metadata.Properties["resyncPeriodInSec"]; ok && val != "" {
|
||||
intval, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
k.logger.Warnf("invalid resyncPeriodInSec %s; %v; defaulting to 10s", val, err)
|
||||
k.resyncPeriodInSec = time.Second * 10
|
||||
} else {
|
||||
k.resyncPeriodInSec = time.Second * time.Duration(intval)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *kubernetesInput) Read(handler func(*bindings.ReadResponse) error) error {
|
||||
watchlist := cache.NewListWatchFromClient(
|
||||
k.kubeClient.CoreV1().RESTClient(),
|
||||
"events",
|
||||
k.namespace,
|
||||
fields.Everything())
|
||||
var resultChan chan EventResponse = make(chan EventResponse)
|
||||
_, controller := cache.NewInformer(
|
||||
watchlist,
|
||||
&v1.Event{},
|
||||
k.resyncPeriodInSec,
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
if obj != nil {
|
||||
resultChan <- EventResponse{
|
||||
Event: "add",
|
||||
NewVal: *(obj.(*v1.Event)),
|
||||
OldVal: v1.Event{},
|
||||
}
|
||||
} else {
|
||||
k.logger.Warnf("Nil Object in Add handle %v", obj)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
if obj != nil {
|
||||
resultChan <- EventResponse{
|
||||
Event: "delete",
|
||||
OldVal: *(obj.(*v1.Event)),
|
||||
NewVal: v1.Event{},
|
||||
}
|
||||
} else {
|
||||
k.logger.Warnf("Nil Object in Delete handle %v", obj)
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
if oldObj != nil && newObj != nil {
|
||||
resultChan <- EventResponse{
|
||||
Event: "update",
|
||||
OldVal: *(oldObj.(*v1.Event)),
|
||||
NewVal: *(newObj.(*v1.Event)),
|
||||
}
|
||||
} else {
|
||||
k.logger.Warnf("Nil Objects in Update handle %v %v", oldObj, newObj)
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
stopCh := make(chan struct{})
|
||||
sigterm := make(chan os.Signal, 1)
|
||||
signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
|
||||
go controller.Run(stopCh)
|
||||
done := false
|
||||
for !done {
|
||||
select {
|
||||
case obj := <-resultChan:
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
k.logger.Errorf("Error marshalling event %w", err)
|
||||
} else {
|
||||
handler(&bindings.ReadResponse{
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
case <-sigterm:
|
||||
done = true
|
||||
close(stopCh)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -6,72 +6,46 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dapr/components-contrib/bindings"
|
||||
"github.com/dapr/dapr/pkg/logger"
|
||||
"github.com/kubernetes-client/go/kubernetes/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type staticHandler struct {
|
||||
Code int
|
||||
Body string
|
||||
QueryParams url.Values
|
||||
}
|
||||
|
||||
func (s *staticHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||
res.WriteHeader(s.Code)
|
||||
res.Write([]byte(s.Body))
|
||||
s.QueryParams = req.URL.Query()
|
||||
}
|
||||
|
||||
func TestParseMetadata(t *testing.T) {
|
||||
nsName := "fooNamespace"
|
||||
m := bindings.Metadata{}
|
||||
m.Properties = map[string]string{"namespace": nsName}
|
||||
t.Run("parse metadata", func(t *testing.T) {
|
||||
resyncPeriod := time.Second * 15
|
||||
m := bindings.Metadata{}
|
||||
m.Properties = map[string]string{"namespace": nsName, "resyncPeriodInSec": "15"}
|
||||
|
||||
i := kubernetesInput{logger: logger.NewLogger("test")}
|
||||
i.parseMetadata(m)
|
||||
i := kubernetesInput{logger: logger.NewLogger("test")}
|
||||
i.parseMetadata(m)
|
||||
|
||||
assert.Equal(t, nsName, i.namespace, "The namespaces should be the same.")
|
||||
}
|
||||
|
||||
func TestReadItem(t *testing.T) {
|
||||
server := httptest.NewServer(&staticHandler{
|
||||
Code: 200,
|
||||
Body: `{"type":"ADDED","object":{"kind":"Event","apiVersion":"v1","metadata":{"name":"kube-system","selfLink":"/api/v1/namespaces/default/test","uid":"164931a7-3d75-11e9-a0a0-2683b9459238","resourceVersion":"227","creationTimestamp":"2019-03-03T05:27:50Z","annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"name\":\"kube-system\",\"namespace\":\"\"}}\n"}},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}}}\n`,
|
||||
assert.Equal(t, nsName, i.namespace, "The namespaces should be the same.")
|
||||
assert.Equal(t, resyncPeriod, i.resyncPeriodInSec, "The resyncPeriod should be the same.")
|
||||
})
|
||||
defer server.Close()
|
||||
t.Run("parse metadata no namespace", func(t *testing.T) {
|
||||
m := bindings.Metadata{}
|
||||
m.Properties = map[string]string{"resyncPeriodInSec": "15"}
|
||||
|
||||
u, err := url.Parse(server.URL)
|
||||
if !assert.NoError(t, err, "URL Parsing failed!") {
|
||||
t.FailNow()
|
||||
}
|
||||
i := kubernetesInput{logger: logger.NewLogger("test")}
|
||||
err := i.parseMetadata(m)
|
||||
|
||||
cfg := &client.Configuration{}
|
||||
cfg.Host = u.Host
|
||||
cfg.Scheme = u.Scheme
|
||||
|
||||
i := &kubernetesInput{
|
||||
config: cfg,
|
||||
client: client.NewAPIClient(cfg),
|
||||
logger: logger.NewLogger("test"),
|
||||
}
|
||||
count := 0
|
||||
i.Read(func(res *bindings.ReadResponse) error {
|
||||
count++
|
||||
|
||||
result := client.Result{}
|
||||
json.Unmarshal(res.Data, &result)
|
||||
|
||||
assert.Equal(t, "ADDED", result.Type, "Unexpected watch event type: %v", result.Type)
|
||||
return nil
|
||||
assert.NotNil(t, err, "Expected err to be returned.")
|
||||
assert.Equal(t, "namespace is missing in metadata", err.Error(), "Error message not same.")
|
||||
})
|
||||
t.Run("parse metadata invalid resync period", func(t *testing.T) {
|
||||
m := bindings.Metadata{}
|
||||
m.Properties = map[string]string{"namespace": nsName, "resyncPeriodInSec": "invalid"}
|
||||
|
||||
assert.Equal(t, 1, count, "Expected 1 item, saw %v\n", count)
|
||||
i := kubernetesInput{logger: logger.NewLogger("test")}
|
||||
err := i.parseMetadata(m)
|
||||
|
||||
assert.Nil(t, err, "Expected err to be nil.")
|
||||
assert.Equal(t, nsName, i.namespace, "The namespaces should be the same.")
|
||||
assert.Equal(t, time.Second*10, i.resyncPeriodInSec, "The resyncPeriod should be the same.")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -47,7 +47,6 @@ require (
|
|||
github.com/influxdata/influxdb-client-go v1.4.0
|
||||
github.com/jackc/pgx/v4 v4.6.0
|
||||
github.com/json-iterator/go v1.1.8
|
||||
github.com/kubernetes-client/go v0.0.0-20190625181339-cd8e39e789c7
|
||||
github.com/mitchellh/mapstructure v1.3.2 // indirect
|
||||
github.com/nats-io/gnatsd v1.4.1
|
||||
github.com/nats-io/go-nats v1.7.2
|
||||
|
|
@ -81,6 +80,7 @@ require (
|
|||
gopkg.in/couchbase/gocb.v1 v1.6.4
|
||||
gopkg.in/couchbase/gocbcore.v7 v7.1.16 // indirect
|
||||
gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4 // indirect
|
||||
k8s.io/api v0.17.0
|
||||
k8s.io/apimachinery v0.17.0
|
||||
k8s.io/client-go v0.17.0
|
||||
)
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -247,6 +247,7 @@ github.com/emicklei/go-restful v2.10.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQm
|
|||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fasthttp-contrib/sessions v0.0.0-20160905201309-74f6ac73d5d5 h1:M4CVMQ5ueVmGZAtkW2bsO+ftesCYpfxl27JTqtzKBzE=
|
||||
github.com/fasthttp-contrib/sessions v0.0.0-20160905201309-74f6ac73d5d5/go.mod h1:MQXNGeXkpojWTxbN7vXoE3f7EmlA11MlJbsrJpVBINA=
|
||||
|
|
@ -1210,6 +1211,7 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
|||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package kubernetes
|
|||
import (
|
||||
"errors"
|
||||
|
||||
kubeclient "github.com/dapr/components-contrib/authentication/kubernetes"
|
||||
"github.com/dapr/components-contrib/secretstores"
|
||||
"github.com/dapr/dapr/pkg/logger"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -26,7 +27,7 @@ func NewKubernetesSecretStore(logger logger.Logger) secretstores.SecretStore {
|
|||
|
||||
// Init creates a Kubernetes client
|
||||
func (k *kubernetesSecretStore) Init(metadata secretstores.Metadata) error {
|
||||
client, err := GetKubeClient()
|
||||
client, err := kubeclient.GetKubeClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue