This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-rover.git


The following commit(s) were added to refs/heads/main by this push:
     new 72b7b74  Add grpc client (#4)
72b7b74 is described below

commit 72b7b743daeb732bcee883aa59a31c607401db63
Author: mrproliu <[email protected]>
AuthorDate: Fri Feb 25 00:03:46 2022 +0800

    Add grpc client (#4)
---
 configs/rover_configs.yaml             |  19 +++-
 go.mod                                 |   7 ++
 go.sum                                 |  11 +++
 pkg/core/{config.go => api.go}         |  13 +--
 pkg/core/{config.go => backend/api.go} |  24 +++--
 pkg/core/backend/client.go             | 166 +++++++++++++++++++++++++++++++++
 pkg/core/backend/config.go             |  30 ++++++
 pkg/core/backend/status.go             |  80 ++++++++++++++++
 pkg/core/config.go                     |   8 +-
 pkg/core/module.go                     |  25 ++++-
 10 files changed, 364 insertions(+), 19 deletions(-)

diff --git a/configs/rover_configs.yaml b/configs/rover_configs.yaml
index 70df2dc..a1d5591 100644
--- a/configs/rover_configs.yaml
+++ b/configs/rover_configs.yaml
@@ -15,4 +15,21 @@
 # limitations under the License.
 #
 
-core:
\ No newline at end of file
+core:
+  backend:
+    # The backend server address
+    addr: ${ROVER_BACKEND_ADDR:localhost:11800}
+    # The TLS switch
+    enable_TLS: ${ROVER_BACKEND_ENABLE_TLS:false}
+    # The file path of client.pem. The config only works when opening the TLS 
switch.
+    client_pem_path: ${ROVER_BACKEND_PEM_PATH:"client.pem"}
+    # The file path of client.key. The config only works when opening the TLS 
switch.
+    client_key_path: ${ROVER_BACKEND_KEY_PATH:"client.key"}
+    # InsecureSkipVerify controls whether a client verifies the server's 
certificate chain and host name.
+    insecure_skip_verify: ${ROVER_BACKEND_INSECURE_SKIP_VERIFY:false}
+    # The file path oca.pem. The config only works when opening the TLS switch.
+    ca_pem_path: ${ROVER_BACKEND_CA_PEM_PATH:"ca.pem"}
+    # How frequently to check the connection(second)
+    check_period: ${ROVER_BACKEND_CHECK_PERIOD:5}
+    # The auth value when send request
+    authentication: ${ROVER_BACKEND_AUTHENTICATION:""}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 1381a55..90a2a35 100644
--- a/go.mod
+++ b/go.mod
@@ -3,13 +3,17 @@ module github.com/apache/skywalking-rover
 go 1.17
 
 require (
+       github.com/hashicorp/go-multierror v1.1.1
        github.com/sirupsen/logrus v1.8.1
        github.com/spf13/cobra v1.3.0
        github.com/spf13/viper v1.10.1
+       google.golang.org/grpc v1.44.0
 )
 
 require (
        github.com/fsnotify/fsnotify v1.5.1 // indirect
+       github.com/golang/protobuf v1.5.2 // indirect
+       github.com/hashicorp/errwrap v1.0.0 // indirect
        github.com/hashicorp/hcl v1.0.0 // indirect
        github.com/inconshreveable/mousetrap v1.0.0 // indirect
        github.com/magiconair/properties v1.8.5 // indirect
@@ -20,8 +24,11 @@ require (
        github.com/spf13/jwalterweatherman v1.1.0 // indirect
        github.com/spf13/pflag v1.0.5 // indirect
        github.com/subosito/gotenv v1.2.0 // indirect
+       golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
        golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
        golang.org/x/text v0.3.7 // indirect
+       google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // 
indirect
+       google.golang.org/protobuf v1.27.1 // indirect
        gopkg.in/ini.v1 v1.66.2 // indirect
        gopkg.in/yaml.v2 v2.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index ad42fdc..4748765 100644
--- a/go.sum
+++ b/go.sum
@@ -149,6 +149,7 @@ github.com/golang/protobuf v1.4.2/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
 github.com/golang/protobuf v1.4.3/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod 
h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.1/go.mod 
h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
+github.com/golang/protobuf v1.5.2 
h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod 
h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.3/go.mod 
h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod 
h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -164,6 +165,7 @@ github.com/google/go-cmp v0.5.2/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.3/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/google/go-cmp v0.5.6/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.0.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod 
h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -194,6 +196,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod 
h1:BDjrQk3hbvj6Nolgz8mAMFb
 github.com/hashicorp/consul/api v1.11.0/go.mod 
h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
 github.com/hashicorp/consul/api v1.12.0/go.mod 
h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
 github.com/hashicorp/consul/sdk v0.8.0/go.mod 
h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
+github.com/hashicorp/errwrap v1.0.0 
h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 github.com/hashicorp/errwrap v1.0.0/go.mod 
h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.0/go.mod 
h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod 
h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
@@ -205,6 +208,8 @@ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod 
h1:0y9vanUI8NX6FsYoO3zeMjh
 github.com/hashicorp/go-msgpack v0.5.3/go.mod 
h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 github.com/hashicorp/go-multierror v1.0.0/go.mod 
h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 github.com/hashicorp/go-multierror v1.1.0/go.mod 
h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
+github.com/hashicorp/go-multierror v1.1.1 
h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod 
h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod 
h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
 github.com/hashicorp/go-rootcerts v1.0.2/go.mod 
h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod 
h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
@@ -443,6 +448,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod 
h1:RBQZq4jEuRlivfhVLd
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod 
h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod 
h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod 
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d 
h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod 
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod 
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -610,6 +616,7 @@ golang.org/x/tools v0.1.5/go.mod 
h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 
h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/api v0.4.0/go.mod 
h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod 
h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -713,6 +720,7 @@ google.golang.org/genproto 
v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ6
 google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod 
h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod 
h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod 
h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa 
h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod 
h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod 
h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -742,6 +750,8 @@ google.golang.org/grpc v1.40.0/go.mod 
h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
 google.golang.org/grpc v1.40.1/go.mod 
h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.42.0/go.mod 
h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.43.0/go.mod 
h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
+google.golang.org/grpc v1.44.0/go.mod 
h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod 
h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod 
h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod 
h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -755,6 +765,7 @@ google.golang.org/protobuf v1.24.0/go.mod 
h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 google.golang.org/protobuf v1.25.0/go.mod 
h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod 
h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod 
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 
h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
 google.golang.org/protobuf v1.27.1/go.mod 
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod 
h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/pkg/core/config.go b/pkg/core/api.go
similarity index 78%
copy from pkg/core/config.go
copy to pkg/core/api.go
index 096a90f..7559ccc 100644
--- a/pkg/core/config.go
+++ b/pkg/core/api.go
@@ -17,13 +17,10 @@
 
 package core
 
-import "github.com/apache/skywalking-rover/pkg/module"
+import "github.com/apache/skywalking-rover/pkg/core/backend"
 
-type Config struct {
-       // module common config
-       module.Config `mapstructure:",squash"`
-}
-
-func (c *Config) IsActive() bool {
-       return true
+// Operator when the other module operate with core module
+type Operator interface {
+       // BackendOperator for operate with backend client
+       BackendOperator() backend.Operator
 }
diff --git a/pkg/core/config.go b/pkg/core/backend/api.go
similarity index 66%
copy from pkg/core/config.go
copy to pkg/core/backend/api.go
index 096a90f..6730fc8 100644
--- a/pkg/core/config.go
+++ b/pkg/core/backend/api.go
@@ -15,15 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package core
+package backend
 
-import "github.com/apache/skywalking-rover/pkg/module"
+import "google.golang.org/grpc"
 
-type Config struct {
-       // module common config
-       module.Config `mapstructure:",squash"`
-}
+type ConnectionStatus int8
+
+const (
+       _ ConnectionStatus = iota
+       Connected
+       Disconnect
+)
 
-func (c *Config) IsActive() bool {
-       return true
+type Operator interface {
+       // GetConnection of rover to backend server
+       GetConnection() grpc.ClientConnInterface
+       // GetConnectionStatus of connection
+       GetConnectionStatus() ConnectionStatus
+       // RegisterListener of connection status change
+       RegisterListener() chan<- ConnectionStatus
 }
diff --git a/pkg/core/backend/client.go b/pkg/core/backend/client.go
new file mode 100644
index 0000000..2890413
--- /dev/null
+++ b/pkg/core/backend/client.go
@@ -0,0 +1,166 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 backend
+
+import (
+       "context"
+       "crypto/tls"
+       "crypto/x509"
+       "fmt"
+       "io/ioutil"
+       "os"
+
+       "google.golang.org/grpc"
+       "google.golang.org/grpc/credentials"
+       "google.golang.org/grpc/credentials/insecure"
+       "google.golang.org/grpc/metadata"
+)
+
+type Client struct {
+       config *Config
+
+       conn      *grpc.ClientConn
+       status    ConnectionStatus
+       listeners []chan<- ConnectionStatus
+       ctx       context.Context
+       cancel    context.CancelFunc
+}
+
+func NewClient(config *Config) *Client {
+       return &Client{config: config}
+}
+
+// Start the backend client and connect to server
+func (c *Client) Start(parent context.Context) error {
+       c.ctx, c.cancel = context.WithCancel(parent)
+       // build config
+       options, err := c.buildConfig(c.config)
+       if err != nil {
+               return err
+       }
+
+       // build connection
+       addr := c.config.Addr
+       conn, err := grpc.Dial(addr, options...)
+       if err != nil {
+               return err
+       }
+       c.conn = conn
+
+       // register status change
+       go c.registerCheckStatus(c.ctx)
+       return nil
+}
+
+func (c *Client) GetConnection() grpc.ClientConnInterface {
+       return c.conn
+}
+
+func (c *Client) Stop() error {
+       c.cancel()
+       return c.conn.Close()
+}
+
+func (c *Client) buildConfig(conf *Config) ([]grpc.DialOption, error) {
+       options := make([]grpc.DialOption, 0)
+
+       if conf.EnableTLS {
+               t, err := configTLS(conf)
+               if err != nil {
+                       return nil, err
+               }
+               options = append(options, 
grpc.WithTransportCredentials(credentials.NewTLS(t)))
+       } else {
+               options = append(options, 
grpc.WithTransportCredentials(insecure.NewCredentials()))
+       }
+
+       if conf.Authentication != "" {
+               authHeader := metadata.New(map[string]string{"Authentication": 
conf.Authentication})
+               options = append(options,
+                       grpc.WithStreamInterceptor(func(ctx context.Context, 
desc *grpc.StreamDesc, cc *grpc.ClientConn,
+                               method string, streamer grpc.Streamer, opts 
...grpc.CallOption) (grpc.ClientStream, error) {
+                               ctx = metadata.NewOutgoingContext(ctx, 
authHeader)
+                               stream, err := streamer(ctx, desc, cc, method, 
opts...)
+                               if err != nil {
+                                       c.reportError(err)
+                               }
+                               return stream, err
+                       }),
+                       grpc.WithUnaryInterceptor(func(ctx context.Context, 
method string, req, reply interface{},
+                               cc *grpc.ClientConn, invoker grpc.UnaryInvoker, 
opts ...grpc.CallOption) error {
+                               ctx = metadata.NewOutgoingContext(ctx, 
authHeader)
+                               err := invoker(ctx, method, req, reply, cc, 
opts...)
+                               if err != nil {
+                                       c.reportError(err)
+                               }
+                               return err
+                       }))
+       }
+
+       return options, nil
+}
+
+// configTLS loads and parse the TLS configs.
+func configTLS(conf *Config) (tc *tls.Config, tlsErr error) {
+       if err := checkTLSFile(conf.CaPemPath); err != nil {
+               return nil, err
+       }
+       tlsConfig := new(tls.Config)
+       tlsConfig.Renegotiation = tls.RenegotiateNever
+       tlsConfig.InsecureSkipVerify = conf.InsecureSkipVerify
+       caPem, err := ioutil.ReadFile(conf.CaPemPath)
+       if err != nil {
+               return nil, err
+       }
+       certPool := x509.NewCertPool()
+       if !certPool.AppendCertsFromPEM(caPem) {
+               return nil, fmt.Errorf("failed to append certificates")
+       }
+       tlsConfig.RootCAs = certPool
+
+       if conf.ClientKeyPath != "" && conf.ClientPemPath != "" {
+               if err := checkTLSFile(conf.ClientKeyPath); err != nil {
+                       return nil, err
+               }
+               if err := checkTLSFile(conf.ClientPemPath); err != nil {
+                       return nil, err
+               }
+               clientPem, err := tls.LoadX509KeyPair(conf.ClientPemPath, 
conf.ClientKeyPath)
+               if err != nil {
+                       return nil, err
+               }
+               tlsConfig.Certificates = []tls.Certificate{clientPem}
+       }
+       return tlsConfig, nil
+}
+
+// checkTLSFile checks the TLS files.
+func checkTLSFile(path string) error {
+       file, err := os.Open(path)
+       if err != nil {
+               return err
+       }
+       stat, err := file.Stat()
+       if err != nil {
+               return err
+       }
+       if stat.Size() == 0 {
+               return fmt.Errorf("the TLS file is illegal: %s", path)
+       }
+       return nil
+}
diff --git a/pkg/core/backend/config.go b/pkg/core/backend/config.go
new file mode 100644
index 0000000..1729aa9
--- /dev/null
+++ b/pkg/core/backend/config.go
@@ -0,0 +1,30 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 backend
+
+type Config struct {
+       Addr string `mapstructure:"addr"` // Server address
+       // TLS settings
+       EnableTLS          bool   `mapstructure:"enable_tls"`           // 
Enable TLS connect to server
+       ClientPemPath      string `mapstructure:"client_pem_path"`      // The 
file path of client.pem. The config only works when opening the TLS switch.
+       ClientKeyPath      string `mapstructure:"client_key_path"`      // The 
file path of client.key. The config only works when opening the TLS switch.
+       CaPemPath          string `mapstructure:"ca_pem_path"`          // The 
file path oca.pem. The config only works when opening the TLS switch.
+       InsecureSkipVerify bool   `mapstructure:"insecure_skip_verify"` // 
Controls whether a client verifies the server's certificate chain and host name.
+       Authentication     string `mapstructure:"authentication"`       // The 
auth value when send request
+       CheckPeriod        int    `mapstructure:"check_period"`         // How 
frequently to check the connection(second)
+}
diff --git a/pkg/core/backend/status.go b/pkg/core/backend/status.go
new file mode 100644
index 0000000..c72d975
--- /dev/null
+++ b/pkg/core/backend/status.go
@@ -0,0 +1,80 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 backend
+
+import (
+       "context"
+       "time"
+
+       "google.golang.org/grpc/codes"
+       "google.golang.org/grpc/connectivity"
+       "google.golang.org/grpc/status"
+)
+
+func (c *Client) registerCheckStatus(ctx context.Context) {
+       ctx, cancel := context.WithCancel(ctx)
+       defer cancel()
+       timeTicker := time.NewTicker(time.Duration(c.config.CheckPeriod) * 
time.Second)
+       for {
+               select {
+               case <-timeTicker.C:
+                       state := c.conn.GetState()
+                       if state == connectivity.Shutdown || state == 
connectivity.TransientFailure {
+                               c.updateStatus(Disconnect)
+                       } else if state == connectivity.Ready || state == 
connectivity.Idle {
+                               c.updateStatus(Connected)
+                       }
+               case <-ctx.Done():
+                       timeTicker.Stop()
+                       return
+               }
+       }
+}
+
+func (c *Client) GetConnectionStatus() ConnectionStatus {
+       return c.status
+}
+
+func (c *Client) RegisterListener() chan<- ConnectionStatus {
+       statuses := make(chan ConnectionStatus, 1)
+       c.listeners = append(c.listeners, statuses)
+       return statuses
+}
+
+func (c *Client) reportError(err error) {
+       if err == nil {
+               return
+       }
+       fromError, ok := status.FromError(err)
+       if ok {
+               errCode := fromError.Code()
+               if errCode == codes.Unavailable || errCode == 
codes.PermissionDenied ||
+                       errCode == codes.Unauthenticated || errCode == 
codes.ResourceExhausted || errCode == codes.Unknown {
+                       c.updateStatus(Disconnect)
+               }
+       }
+}
+
+func (c *Client) updateStatus(s ConnectionStatus) {
+       if c.status != s {
+               c.status = s
+               for _, lis := range c.listeners {
+                       lis <- s
+               }
+       }
+}
diff --git a/pkg/core/config.go b/pkg/core/config.go
index 096a90f..5d7a4f8 100644
--- a/pkg/core/config.go
+++ b/pkg/core/config.go
@@ -17,11 +17,17 @@
 
 package core
 
-import "github.com/apache/skywalking-rover/pkg/module"
+import (
+       "github.com/apache/skywalking-rover/pkg/core/backend"
+       "github.com/apache/skywalking-rover/pkg/module"
+)
 
 type Config struct {
        // module common config
        module.Config `mapstructure:",squash"`
+
+       // backend connection
+       BackendConfig *backend.Config `mapstructure:"backend"`
 }
 
 func (c *Config) IsActive() bool {
diff --git a/pkg/core/module.go b/pkg/core/module.go
index 3511b77..d044bfa 100644
--- a/pkg/core/module.go
+++ b/pkg/core/module.go
@@ -20,13 +20,18 @@ package core
 import (
        "context"
 
+       "github.com/apache/skywalking-rover/pkg/core/backend"
        "github.com/apache/skywalking-rover/pkg/module"
+
+       "github.com/hashicorp/go-multierror"
 )
 
 const ModuleName = "core"
 
 type Module struct {
        config *Config
+
+       backendClient *backend.Client
 }
 
 func NewModule() *Module {
@@ -46,9 +51,27 @@ func (m *Module) Config() module.ConfigInterface {
 }
 
 func (m *Module) Start(ctx context.Context, mgr *module.Manager) error {
+       // backend client
+       if m.config.BackendConfig != nil {
+               m.backendClient = backend.NewClient(m.config.BackendConfig)
+               if err := m.backendClient.Start(ctx); err != nil {
+                       return err
+               }
+       }
        return nil
 }
 
 func (m *Module) Shutdown(ctx context.Context, mgr *module.Manager) error {
-       return nil
+       var result *multierror.Error
+       if m.backendClient != nil {
+               result = multierror.Append(result, m.backendClient.Stop())
+       }
+       return result.ErrorOrNil()
+}
+
+func (m *Module) ClientGrpcOperator() backend.Operator {
+       if m.backendClient == nil {
+               return nil
+       }
+       return m.backendClient
 }

Reply via email to