You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/10/26 08:13:29 UTC

[incubator-servicecomb-service-center] branch master updated: SCB-984 Add Health Check command (#466)

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

littlecui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new 2794106  SCB-984 Add Health Check command (#466)
2794106 is described below

commit 27941064687719530cfafeaf311d1704c8a9922e
Author: little-cui <su...@qq.com>
AuthorDate: Fri Oct 26 16:13:25 2018 +0800

    SCB-984 Add Health Check command (#466)
---
 pkg/client/sc/apis.go          | 22 ++++++++++++++++
 pkg/client/sc/client.go        | 18 +------------
 pkg/client/sc/config.go        | 23 ++++++++++++++--
 pkg/task/service_test.go       |  2 ++
 scctl/bootstrap/bootstrap.go   |  1 +
 scctl/pkg/cmd/cmd.go           | 10 ++++++-
 scctl/pkg/plugin/README.md     | 35 +++++++++++++++++++-----
 scctl/pkg/plugin/health/cmd.go | 60 ++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 145 insertions(+), 26 deletions(-)

diff --git a/pkg/client/sc/apis.go b/pkg/client/sc/apis.go
index 9fff250..63e96e7 100644
--- a/pkg/client/sc/apis.go
+++ b/pkg/client/sc/apis.go
@@ -33,6 +33,7 @@ const (
 	apiVersionURL  = "/version"
 	apiDumpURL     = "/v4/default/admin/dump"
 	apiClustersURL = "/v4/default/admin/clusters"
+	apiHealthURL   = "/v4/default/registry/health"
 	apiSchemasURL  = "/v4/%s/registry/microservices/%s/schemas"
 	apiSchemaURL   = "/v4/%s/registry/microservices/%s/schemas/%s"
 )
@@ -203,3 +204,24 @@ func (c *SCClient) GetClusters() (registry.Clusters, *scerr.Error) {
 
 	return clusters.Clusters, nil
 }
+
+func (c *SCClient) HealthCheck() *scerr.Error {
+	headers := c.commonHeaders()
+	// only default domain has admin permission
+	headers.Set("X-Domain-Name", "default")
+	resp, err := c.URLClient.HttpDo(http.MethodGet, c.Config.Addr+apiHealthURL, headers, nil)
+	if err != nil {
+		return scerr.NewError(scerr.ErrUnavailableBackend, err.Error())
+	}
+	defer resp.Body.Close()
+
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return scerr.NewError(scerr.ErrInternal, err.Error())
+	}
+
+	if resp.StatusCode != http.StatusOK {
+		return c.toError(body)
+	}
+	return nil
+}
diff --git a/pkg/client/sc/client.go b/pkg/client/sc/client.go
index 62b9c1e..0d95dcd 100644
--- a/pkg/client/sc/client.go
+++ b/pkg/client/sc/client.go
@@ -17,26 +17,10 @@ package sc
 
 import (
 	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
-	"io/ioutil"
-	"strings"
-	"time"
 )
 
 func NewSCClient(cfg Config) (*SCClient, error) {
-	ssl := strings.Index(cfg.Addr, "https://") >= 0
-	if ssl && len(cfg.CertKeyPWD) == 0 && len(cfg.CertKeyPWDPath) > 0 {
-		content, _ := ioutil.ReadFile(cfg.CertKeyPWDPath)
-		cfg.CertKeyPWD = string(content)
-	}
-	client, err := rest.GetURLClient(rest.URLClientOption{
-		SSLEnabled:     ssl,
-		VerifyPeer:     cfg.VerifyPeer,
-		CAFile:         cfg.CAFile,
-		CertFile:       cfg.CertFile,
-		CertKeyFile:    cfg.CertKeyFile,
-		CertKeyPWD:     cfg.CertKeyPWD,
-		RequestTimeout: 10 * time.Second,
-	})
+	client, err := rest.GetURLClient(cfg.Merge())
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/client/sc/config.go b/pkg/client/sc/config.go
index 2a707a7..0f8d64f 100644
--- a/pkg/client/sc/config.go
+++ b/pkg/client/sc/config.go
@@ -15,11 +15,30 @@
 
 package sc
 
-import "github.com/apache/incubator-servicecomb-service-center/pkg/rest"
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
+	"io/ioutil"
+	"strings"
+	"time"
+)
 
 type Config struct {
 	rest.URLClientOption
-	Addr           string
+	Addr string
+	// TODO Expandable header not only token header
 	Token          string
 	CertKeyPWDPath string
 }
+
+func (cfg *Config) Merge() rest.URLClientOption {
+	ssl := strings.Index(cfg.Addr, "https://") >= 0
+	if ssl && len(cfg.CertKeyPWD) == 0 && len(cfg.CertKeyPWDPath) > 0 {
+		content, _ := ioutil.ReadFile(cfg.CertKeyPWDPath)
+		cfg.CertKeyPWD = string(content)
+	}
+	cfg.SSLEnabled = ssl
+	if cfg.RequestTimeout == 0 {
+		cfg.RequestTimeout = 10 * time.Second
+	}
+	return cfg.URLClientOption
+}
diff --git a/pkg/task/service_test.go b/pkg/task/service_test.go
index 6b4bfe6..ee4076e 100644
--- a/pkg/task/service_test.go
+++ b/pkg/task/service_test.go
@@ -90,6 +90,8 @@ func TestBaseAsyncTasker_AddTask(t *testing.T) {
 		t.Fatalf("second time add task should return prev result")
 	}
 	<-testCtx2.Done()
+	// pkg/task/executor.go:53
+	<-time.After(time.Millisecond)
 	lt, _ = at.LatestHandled("test")
 	if lt.Err().Error() != "test2" {
 		t.Fatalf("should get second handled task 'test2'")
diff --git a/scctl/bootstrap/bootstrap.go b/scctl/bootstrap/bootstrap.go
index cb0c88b..5a153b3 100644
--- a/scctl/bootstrap/bootstrap.go
+++ b/scctl/bootstrap/bootstrap.go
@@ -21,3 +21,4 @@ import _ "github.com/apache/incubator-servicecomb-service-center/scctl/pkg/plugi
 import _ "github.com/apache/incubator-servicecomb-service-center/scctl/pkg/plugin/get/instance"
 import _ "github.com/apache/incubator-servicecomb-service-center/scctl/pkg/plugin/get/schema"
 import _ "github.com/apache/incubator-servicecomb-service-center/scctl/pkg/plugin/get/cluster"
+import _ "github.com/apache/incubator-servicecomb-service-center/scctl/pkg/plugin/health"
diff --git a/scctl/pkg/cmd/cmd.go b/scctl/pkg/cmd/cmd.go
index 567cf53..f84dd23 100644
--- a/scctl/pkg/cmd/cmd.go
+++ b/scctl/pkg/cmd/cmd.go
@@ -22,6 +22,7 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/scctl/pkg/version"
 	"github.com/spf13/cobra"
 	"os"
+	"time"
 )
 
 const (
@@ -36,18 +37,22 @@ var rootCmd = &cobra.Command{
 var ScClientConfig sc.Config
 
 func init() {
+	var timeout string
 	rootCmd.PersistentFlags().BoolP("verbose", "v", false, "make the operation more talkative")
 	rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
 		if v, _ := cmd.Flags().GetBool("verbose"); v {
 			os.Setenv("DEBUG_MODE", "1")
 		}
+		if d, err := time.ParseDuration(timeout); err == nil && d > 0 {
+			ScClientConfig.RequestTimeout = d
+		}
 	}
 
 	rootCmd.PersistentFlags().StringVar(&ScClientConfig.Addr, "addr",
 		"http://"+util.GetEnvString("HOSTING_SERVER_IP", "127.0.0.1")+":30100",
 		"the http host and port of service center, can be overrode by env HOSTING_SERVER_IP.")
 
-	rootCmd.PersistentFlags().StringVarP(&ScClientConfig.Token, "token", "t", "",
+	rootCmd.PersistentFlags().StringVar(&ScClientConfig.Token, "token", "",
 		"the auth token string to access service center.")
 
 	rootCmd.PersistentFlags().BoolVarP(&ScClientConfig.VerifyPeer, "peer", "p", false,
@@ -62,6 +67,9 @@ func init() {
 		"the passphase file path to decrypt key file.")
 	rootCmd.PersistentFlags().StringVar(&ScClientConfig.CertKeyPWD, "pass", "",
 		"the passphase string to decrypt key file.")
+
+	rootCmd.PersistentFlags().StringVarP(&timeout, "timeout", "t", "10s",
+		"the maximum time allowed for the request.")
 }
 
 func RootCmd() *cobra.Command {
diff --git a/scctl/pkg/plugin/README.md b/scctl/pkg/plugin/README.md
index 39096e8..e244676 100644
--- a/scctl/pkg/plugin/README.md
+++ b/scctl/pkg/plugin/README.md
@@ -1,7 +1,7 @@
 scctl
 ========
 
-`scctl` is a command line client for ServiceCenter.
+`scctl` is a command line client for service center.
 
 ## Global options
 
@@ -11,12 +11,15 @@ scctl
 - `key` the key file path to access service center, can be overrode by `$SSL_ROOT`/server_key.pem.
 - `pass` the passphase string to decrypt key file.
 - `pass-file` the passphase file path to decrypt key file, can be overrode by `$SSL_ROOT`/cert_pwd.
+- `timeout` the maximum time allowed for the request.
 
 ## Get commands
 
+The `get` command is the root command for getting any type resources from service center. 
+
 ### service [options]
 
-Get the microservices list from ServiceCenter. `service` command can be instead of `svc`.
+Get the microservices list from service center. `service` command can be instead of `svc`.
 
 #### Options
 
@@ -51,7 +54,7 @@ Get the microservices list from ServiceCenter. `service` command can be instead
 
 ### instance [options]
 
-Get the instances list from ServiceCenter. `instance` command can be instead of `inst`.
+Get the instances list from service center. `instance` command can be instead of `inst`.
 
 #### Options
 
@@ -80,7 +83,7 @@ Get the instances list from ServiceCenter. `instance` command can be instead of
 
 ### schema [options]
 
-Get the schemas content from ServiceCenter.
+Get the schemas content from service center.
 
 #### Options
 
@@ -136,7 +139,7 @@ ls -l schemas/springmvc/provider.v0.0.1
 
 ### cluster [options]
 
-Get the registry clusters managed by ServiceCenter.
+Get the registry clusters managed by service center.
 
 #### Examples
 ```bash
@@ -149,7 +152,7 @@ Get the registry clusters managed by ServiceCenter.
 
 ## Diagnose commands
 
-The diagnostic command can output the ServiceCenter health report. 
+The `diagnose` command can output the service center health report. 
 If the service center is isolated from etcd, the diagnosis will print wrong information.
 
 #### Options
@@ -174,4 +177,24 @@ echo exit $?
 #   instance: [[rest://127.0.0.1:30100/]]
 # error: 1. found in etcd but not in cache
 # exit 1
+```
+
+## Health Check commands
+
+The `health` command can check the service center health. 
+
+#### Exit codes
+
+- `0` the service center is healthy.
+- `1` an internal error occurred in scctl.
+- `2` the service center is unavailable.
+- `3` the service center is abnormal.
+
+#### Examples
+```bash
+./scctl health
+# Registry service is unavailable(invoke request failed: Get http://127.0.0.1:30100/v4/default/registry/health: dial tcp 127.0.0.1:30100: getsockopt: connection refused)
+
+echo exit $?
+# exit 2
 ```
\ No newline at end of file
diff --git a/scctl/pkg/plugin/health/cmd.go b/scctl/pkg/plugin/health/cmd.go
new file mode 100644
index 0000000..1dd6be8
--- /dev/null
+++ b/scctl/pkg/plugin/health/cmd.go
@@ -0,0 +1,60 @@
+// Licensed to the 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.
+// The 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 health
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/client/sc"
+	"github.com/apache/incubator-servicecomb-service-center/scctl/pkg/cmd"
+	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
+	"github.com/spf13/cobra"
+)
+
+const (
+	ExistInternal    = iota + cmd.ExitError
+	ExistUnavailable // connection timeout or refuse
+	ExistAbnormal    // abnormal
+)
+
+func init() {
+	NewHealthCommand(cmd.RootCmd())
+}
+
+func NewHealthCommand(parent *cobra.Command) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "health [options]",
+		Short: "Output the health check result of service center",
+		Run:   HealthCommandFunc,
+	}
+
+	parent.AddCommand(cmd)
+	return cmd
+}
+
+func HealthCommandFunc(_ *cobra.Command, args []string) {
+	scClient, err := sc.NewSCClient(cmd.ScClientConfig)
+	if err != nil {
+		cmd.StopAndExit(ExistInternal, err)
+	}
+	scErr := scClient.HealthCheck()
+	if scErr != nil {
+		switch scErr.Code {
+		case scerr.ErrUnavailableBackend:
+			cmd.StopAndExit(ExistUnavailable, scErr)
+		default:
+			cmd.StopAndExit(ExistAbnormal, scErr)
+		}
+	}
+}