Skip to content

Commit 5de1bda

Browse files
authored
Merge pull request #17 from gomicro/grpc_health
Grpc health
2 parents 64704e8 + d8dc116 commit 5de1bda

File tree

618 files changed

+135193
-8344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

618 files changed

+135193
-8344
lines changed

cmd/root.go

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,42 @@
11
package cmd
22

33
import (
4+
"context"
45
"crypto/tls"
56
"errors"
7+
"fmt"
68
"net/http"
79
"os"
810

911
"github.com/certifi/gocertifi"
1012
"github.com/spf13/cobra"
13+
"google.golang.org/grpc"
14+
health_pb "google.golang.org/grpc/health/grpc_health_v1"
1115

12-
"github.com/gomicro/probe/fmt"
16+
ffmt "github.com/gomicro/probe/fmt"
1317
)
1418

1519
var (
1620
verbose bool
1721
skipVerify bool
22+
grpcFlag bool
23+
24+
// ErrGrpcBadStatus is the error returned when a non serving status is returend by a grpc service
25+
ErrGrpcBadStatus = errors.New("grpc status: not ok")
26+
// ErrGrpcConnFailure is the error returned when the grpc dial fails to connect to the specified host
27+
ErrGrpcConnFailure = errors.New("grpc conn: failure")
28+
// ErrHTTPBadStatus is the error returned when a non ok status is returned by a http service
29+
ErrHTTPBadStatus = errors.New("http status: not ok")
30+
// ErrHTTPGet is the errro returned when an http client encounters an error while performing a GET
31+
ErrHTTPGet = errors.New("http client: get")
1832
)
1933

2034
func init() {
2135
cobra.OnInitialize(initEnvs)
2236

2337
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show more verbose output")
2438
rootCmd.Flags().BoolVarP(&skipVerify, "insecure", "k", false, "permit operations for servers otherwise considered insecure")
39+
rootCmd.Flags().BoolVarP(&grpcFlag, "grpc", "g", false, "use http2 transport for grpc health check")
2540
}
2641

2742
func initEnvs() {
@@ -50,6 +65,26 @@ func Execute() {
5065
}
5166

5267
func probe(cmd *cobra.Command, args []string) {
68+
var err error
69+
70+
if grpcFlag {
71+
err = probeGrpc(args[0])
72+
} else {
73+
err = probeHttp(args[0])
74+
}
75+
76+
if err != nil {
77+
if errors.Is(err, ErrHTTPGet) || errors.Is(err, ErrGrpcConnFailure) {
78+
ffmt.Printf("%v", err.Error())
79+
os.Exit(2)
80+
}
81+
82+
ffmt.Verbosef("%v", err.Error())
83+
os.Exit(1)
84+
}
85+
}
86+
87+
func probeHttp(host string) error {
5388
pool, err := gocertifi.CACerts()
5489
if err != nil {
5590
fmt.Printf("Error: failed to create cert pool: %v\n", err.Error())
@@ -69,14 +104,40 @@ func probe(cmd *cobra.Command, args []string) {
69104

70105
client := &http.Client{Transport: transport}
71106

72-
resp, err := client.Get(args[0])
107+
resp, err := client.Get(host)
73108
if err != nil {
74-
fmt.Verbosef("error: http get: %v", err.Error())
75-
os.Exit(1)
109+
return fmt.Errorf("%w: %v", ErrHTTPGet, err.Error())
76110
}
77111

78112
if resp.StatusCode != http.StatusOK {
79-
fmt.Verbosef("error: status %v", resp.StatusCode)
80-
os.Exit(1)
113+
return fmt.Errorf("%w: status %v", ErrHTTPBadStatus, resp.StatusCode)
114+
}
115+
116+
return nil
117+
}
118+
119+
func probeGrpc(host string) error {
120+
ctx := context.Background()
121+
122+
opts := []grpc.DialOption{
123+
grpc.WithBlock(),
124+
grpc.WithInsecure(),
125+
}
126+
127+
conn, err := grpc.DialContext(ctx, host, opts...)
128+
if err != nil {
129+
return fmt.Errorf("%w: %v", ErrGrpcConnFailure, err.Error())
81130
}
131+
defer conn.Close()
132+
133+
resp, err := health_pb.NewHealthClient(conn).Check(ctx, &health_pb.HealthCheckRequest{})
134+
if err != nil {
135+
return ErrGrpcBadStatus
136+
}
137+
138+
if resp.GetStatus() != health_pb.HealthCheckResponse_SERVING {
139+
return fmt.Errorf("%w: status %v", err, resp.GetStatus())
140+
}
141+
142+
return nil
82143
}

cmd/root_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package cmd
22

33
import (
4+
"errors"
45
"fmt"
6+
"net/http"
57
"testing"
68

79
"github.com/franela/goblin"
@@ -14,15 +16,37 @@ func TestProbe(t *testing.T) {
1416
RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) })
1517

1618
g.Describe("Probe", func() {
19+
var server *bogus.Bogus
20+
var host string
21+
22+
g.BeforeEach(func() {
23+
server = bogus.New()
24+
h, p := server.HostPort()
25+
host = fmt.Sprintf("http://%v:%v/v1/status", h, p)
26+
})
27+
1728
g.It("should probe an endpoint", func() {
18-
server := bogus.New()
1929
server.AddPath("/v1/status").
2030
SetMethods("GET").
2131
SetPayload([]byte("ok"))
22-
h, p := server.HostPort()
2332

24-
args := []string{fmt.Sprintf("http://%v:%v/v1/status", h, p)}
25-
probe(nil, args)
33+
err := probeHttp(host)
34+
Expect(err).NotTo(HaveOccurred())
35+
36+
hr := server.HitRecords()
37+
Expect(len(hr)).To(Equal(1))
38+
Expect(hr[0].Verb).To(Equal("GET"))
39+
})
40+
41+
g.It("should error when a bad status is returned", func() {
42+
server.AddPath("/v1/status").
43+
SetMethods("GET").
44+
SetStatus(http.StatusNotFound).
45+
SetPayload([]byte("bad"))
46+
47+
err := probeHttp(host)
48+
Expect(err).To(HaveOccurred())
49+
Expect(errors.Is(err, ErrHTTPBadStatus)).To(BeTrue())
2650

2751
hr := server.HitRecords()
2852
Expect(len(hr)).To(Equal(1))

go.mod

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ require (
1717
github.com/spf13/jwalterweatherman v1.1.0 // indirect
1818
github.com/spf13/pflag v1.0.5 // indirect
1919
github.com/spf13/viper v1.6.3
20-
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
21-
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa // indirect
22-
golang.org/x/text v0.3.2 // indirect
23-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
20+
golang.org/x/net v0.0.0-20210324205630-d1beb07c2056 // indirect
21+
google.golang.org/grpc v1.36.1
2422
gopkg.in/ini.v1 v1.55.0 // indirect
2523
)

0 commit comments

Comments
 (0)