Simplify SelfCheck API (#5665)

Fixes #5575

Now that only viz makes use of the `SelfCheck` api, merged the `healthcheck.proto` into `viz.proto`.

Also removed the "checkRPC" functionality that was used for handling multiple API responses and was only used by `SelfCheck`, because the extra complexity was not granted. Revert to use the plain vanilla "check" by just concatenating error responses.

## Success Output

```bash
$ bin/linkerd viz check
...
linkerd-viz
-----------
...
√ viz extension self-check
```

## Failure Examples

Failure when viz fails to connect to the k8s api:
```bash
$ bin/linkerd viz check
...
linkerd-viz
-----------
...
× viz extension self-check
    Error calling the Kubernetes API: someerror
    see https://linkerd.io/checks/#l5d-api-control-api for hints

Status check results are ×
```

Failure when viz fails to connect to Prometheus:
```bash
$ bin/linkerd viz check
...
linkerd-viz
-----------
...
× viz extension self-check
    Error calling Prometheus from the control plane: someerror
    see https://linkerd.io/checks/#l5d-api-control-api for hints

Status check results are ×
```

Failure when viz fails to connect to both the k8s api and Prometheus:
```bash
$ bin/linkerd viz check
...
linkerd-viz
-----------
...
× viz extension self-check
    Error calling the Kubernetes API: someerror
    Error calling Prometheus from the control plane: someerror
    see https://linkerd.io/checks/#l5d-api-control-api for hints

Status check results are ×
```
This commit is contained in:
Alejandro Pedraza 2021-02-05 10:13:45 -05:00 committed by GitHub
parent 704ed00a49
commit a04b30d2ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1408 additions and 1607 deletions

View File

@ -7,16 +7,14 @@ bindir=$( cd "${0%/*}" && pwd )
go install -mod=readonly github.com/golang/protobuf/protoc-gen-go
rm -rf controller/gen/common controller/gen/public controller/gen/config viz/metrics-api/gen
mkdir -p controller/gen/common/healthcheck controller/gen/common/net controller/gen/public viz/metrics-api/gen/viz
mkdir -p controller/gen/common/net controller/gen/public viz/metrics-api/gen/viz
"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/common/net.proto
"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/public.proto
"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/config/config.proto
"$bindir"/protoc -I proto --go_out=plugins=grpc,paths=source_relative:controller/gen proto/common/healthcheck.proto
"$bindir"/protoc -I proto -I viz/metrics-api/proto --go_out=plugins=grpc,paths=source_relative:viz/metrics-api/gen viz/metrics-api/proto/viz.proto
mv controller/gen/common/net.pb.go controller/gen/common/net/
mv controller/gen/public.pb.go controller/gen/public/
mv controller/gen/common/healthcheck.pb.go controller/gen/common/healthcheck/
mv viz/metrics-api/gen/viz.pb.go viz/metrics-api/gen/viz/viz.pb.go

View File

@ -1,362 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.24.0
// protoc v3.6.0
// source: common/healthcheck.proto
package healthcheck
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type CheckStatus int32
const (
CheckStatus_OK CheckStatus = 0
CheckStatus_FAIL CheckStatus = 1
CheckStatus_ERROR CheckStatus = 2
)
// Enum value maps for CheckStatus.
var (
CheckStatus_name = map[int32]string{
0: "OK",
1: "FAIL",
2: "ERROR",
}
CheckStatus_value = map[string]int32{
"OK": 0,
"FAIL": 1,
"ERROR": 2,
}
)
func (x CheckStatus) Enum() *CheckStatus {
p := new(CheckStatus)
*p = x
return p
}
func (x CheckStatus) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (CheckStatus) Descriptor() protoreflect.EnumDescriptor {
return file_common_healthcheck_proto_enumTypes[0].Descriptor()
}
func (CheckStatus) Type() protoreflect.EnumType {
return &file_common_healthcheck_proto_enumTypes[0]
}
func (x CheckStatus) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use CheckStatus.Descriptor instead.
func (CheckStatus) EnumDescriptor() ([]byte, []int) {
return file_common_healthcheck_proto_rawDescGZIP(), []int{0}
}
type CheckResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
SubsystemName string `protobuf:"bytes,1,opt,name=SubsystemName,proto3" json:"SubsystemName,omitempty"`
CheckDescription string `protobuf:"bytes,2,opt,name=CheckDescription,proto3" json:"CheckDescription,omitempty"`
Status CheckStatus `protobuf:"varint,3,opt,name=Status,proto3,enum=linkerd2.common.healthcheck.CheckStatus" json:"Status,omitempty"`
FriendlyMessageToUser string `protobuf:"bytes,4,opt,name=FriendlyMessageToUser,proto3" json:"FriendlyMessageToUser,omitempty"`
}
func (x *CheckResult) Reset() {
*x = CheckResult{}
if protoimpl.UnsafeEnabled {
mi := &file_common_healthcheck_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CheckResult) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CheckResult) ProtoMessage() {}
func (x *CheckResult) ProtoReflect() protoreflect.Message {
mi := &file_common_healthcheck_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CheckResult.ProtoReflect.Descriptor instead.
func (*CheckResult) Descriptor() ([]byte, []int) {
return file_common_healthcheck_proto_rawDescGZIP(), []int{0}
}
func (x *CheckResult) GetSubsystemName() string {
if x != nil {
return x.SubsystemName
}
return ""
}
func (x *CheckResult) GetCheckDescription() string {
if x != nil {
return x.CheckDescription
}
return ""
}
func (x *CheckResult) GetStatus() CheckStatus {
if x != nil {
return x.Status
}
return CheckStatus_OK
}
func (x *CheckResult) GetFriendlyMessageToUser() string {
if x != nil {
return x.FriendlyMessageToUser
}
return ""
}
type SelfCheckRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *SelfCheckRequest) Reset() {
*x = SelfCheckRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_common_healthcheck_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SelfCheckRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SelfCheckRequest) ProtoMessage() {}
func (x *SelfCheckRequest) ProtoReflect() protoreflect.Message {
mi := &file_common_healthcheck_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SelfCheckRequest.ProtoReflect.Descriptor instead.
func (*SelfCheckRequest) Descriptor() ([]byte, []int) {
return file_common_healthcheck_proto_rawDescGZIP(), []int{1}
}
type SelfCheckResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Results []*CheckResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"`
}
func (x *SelfCheckResponse) Reset() {
*x = SelfCheckResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_common_healthcheck_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SelfCheckResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SelfCheckResponse) ProtoMessage() {}
func (x *SelfCheckResponse) ProtoReflect() protoreflect.Message {
mi := &file_common_healthcheck_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SelfCheckResponse.ProtoReflect.Descriptor instead.
func (*SelfCheckResponse) Descriptor() ([]byte, []int) {
return file_common_healthcheck_proto_rawDescGZIP(), []int{2}
}
func (x *SelfCheckResponse) GetResults() []*CheckResult {
if x != nil {
return x.Results
}
return nil
}
var File_common_healthcheck_proto protoreflect.FileDescriptor
var file_common_healthcheck_proto_rawDesc = []byte{
0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63,
0x68, 0x65, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x6c, 0x69, 0x6e, 0x6b,
0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65, 0x61, 0x6c,
0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xd7, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63,
0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x73, 0x79,
0x73, 0x74, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a,
0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x06, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x6c, 0x69, 0x6e, 0x6b,
0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65, 0x61, 0x6c,
0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x46,
0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x6f,
0x55, 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x46, 0x72, 0x69, 0x65,
0x6e, 0x64, 0x6c, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x55, 0x73, 0x65,
0x72, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x11, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65,
0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65,
0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6c, 0x69,
0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x68, 0x65,
0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2a, 0x2a,
0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a,
0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12,
0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64,
0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f,
0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_common_healthcheck_proto_rawDescOnce sync.Once
file_common_healthcheck_proto_rawDescData = file_common_healthcheck_proto_rawDesc
)
func file_common_healthcheck_proto_rawDescGZIP() []byte {
file_common_healthcheck_proto_rawDescOnce.Do(func() {
file_common_healthcheck_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_healthcheck_proto_rawDescData)
})
return file_common_healthcheck_proto_rawDescData
}
var file_common_healthcheck_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_common_healthcheck_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_common_healthcheck_proto_goTypes = []interface{}{
(CheckStatus)(0), // 0: linkerd2.common.healthcheck.CheckStatus
(*CheckResult)(nil), // 1: linkerd2.common.healthcheck.CheckResult
(*SelfCheckRequest)(nil), // 2: linkerd2.common.healthcheck.SelfCheckRequest
(*SelfCheckResponse)(nil), // 3: linkerd2.common.healthcheck.SelfCheckResponse
}
var file_common_healthcheck_proto_depIdxs = []int32{
0, // 0: linkerd2.common.healthcheck.CheckResult.Status:type_name -> linkerd2.common.healthcheck.CheckStatus
1, // 1: linkerd2.common.healthcheck.SelfCheckResponse.results:type_name -> linkerd2.common.healthcheck.CheckResult
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_common_healthcheck_proto_init() }
func file_common_healthcheck_proto_init() {
if File_common_healthcheck_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_common_healthcheck_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CheckResult); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_common_healthcheck_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SelfCheckRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_common_healthcheck_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SelfCheckResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_common_healthcheck_proto_rawDesc,
NumEnums: 1,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_common_healthcheck_proto_goTypes,
DependencyIndexes: file_common_healthcheck_proto_depIdxs,
EnumInfos: file_common_healthcheck_proto_enumTypes,
MessageInfos: file_common_healthcheck_proto_msgTypes,
}.Build()
File_common_healthcheck_proto = out.File
file_common_healthcheck_proto_rawDesc = nil
file_common_healthcheck_proto_goTypes = nil
file_common_healthcheck_proto_depIdxs = nil
}

View File

@ -12,7 +12,6 @@ import (
"time"
"github.com/linkerd/linkerd2/controller/api/public"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
configPb "github.com/linkerd/linkerd2/controller/gen/config"
l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
"github.com/linkerd/linkerd2/pkg/config"
@ -304,11 +303,6 @@ type Checker struct {
// check is the function that's called to execute the check; if the function
// returns an error, the check fails
check func(context.Context) error
// checkRPC is an alternative to check that can be used to perform a remote
// check using the SelfCheck gRPC endpoint; check status is based on the value
// of the gRPC response
checkRPC func(context.Context) (*healthcheckPb.SelfCheckResponse, error)
}
// NewChecker returns a new instance of checker type
@ -355,12 +349,6 @@ func (c *Checker) WithCheck(check func(context.Context) error) *Checker {
return c
}
// WithCheckRPC returns a checker with the provided checkRPC func
func (c *Checker) WithCheckRPC(checkRPC func(context.Context) (*healthcheckPb.SelfCheckResponse, error)) *Checker {
c.checkRPC = checkRPC
return c
}
// CheckResult encapsulates a check's identifying information and output
// Note there exists an analogous user-facing type, `cmd.check`, for output via
// `linkerd check -o json`.
@ -1477,17 +1465,6 @@ func (hc *HealthChecker) RunChecks(observer CheckObserver) bool {
}
}
}
if checker.checkRPC != nil {
if !hc.runCheckRPC(c.ID, &checker, observer) {
if !checker.warning {
success = false
}
if checker.fatal {
return success
}
}
}
}
}
}
@ -1539,77 +1516,6 @@ func (hc *HealthChecker) runCheck(categoryID CategoryID, c *Checker, observer Ch
}
}
// runCheckRPC calls `c` which itself should make a gRPC call returning `*healthcheckPb.SelfCheckResponse`
// (which can contain multiple responses) or error.
// If that call returns an error, we send it to `observer` and return false.
// Otherwise, we send to `observer` a success message with `c.description` and then proceed to check the
// multiple responses contained in the response.
// We keep on retrying the same call until all the responses have an OK status
// (or until timeout/deadline is reached), sending a message to `observer` for each response,
// while making sure no duplicate messages are sent.
func (hc *HealthChecker) runCheckRPC(categoryID CategoryID, c *Checker, observer CheckObserver) bool {
observedResults := []CheckResult{}
for {
ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
defer cancel()
checkRsp, err := c.checkRPC(ctx)
if se, ok := err.(*SkipError); ok {
log.Debugf("Skipping check: %s. Reason: %s", c.description, se.Reason)
return true
}
checkResult := &CheckResult{
Category: categoryID,
Description: c.description,
HintAnchor: c.hintAnchor,
Warning: c.warning,
}
if vs, ok := err.(*VerboseSuccess); ok {
checkResult.Description = fmt.Sprintf("%s\n%s", checkResult.Description, vs.Message)
} else if err != nil {
// errors at the gRPC-call level are not retried
// but we do retry below if the response Status is not OK
checkResult.Err = &CategoryError{categoryID, err}
observer(checkResult)
return false
}
// General description, only shown once.
// The following calls to `observer()` track specific result entries.
if !checkResult.alreadyObserved(observedResults) {
observer(checkResult)
observedResults = append(observedResults, *checkResult)
}
for _, check := range checkRsp.Results {
checkResult.Err = nil
checkResult.Description = fmt.Sprintf("[%s] %s", check.SubsystemName, check.CheckDescription)
if check.Status != healthcheckPb.CheckStatus_OK {
checkResult.Err = &CategoryError{categoryID, fmt.Errorf(check.FriendlyMessageToUser)}
checkResult.Retry = time.Now().Before(c.retryDeadline)
// only show the waiting message during retries,
// and send the underlying error on the last try
if !c.surfaceErrorOnRetry && checkResult.Retry {
checkResult.Err = errors.New("waiting for check to complete")
}
observer(checkResult)
} else if !checkResult.alreadyObserved(observedResults) {
observer(checkResult)
}
observedResults = append(observedResults, *checkResult)
}
if checkResult.Retry {
log.Debug("Retrying on error")
time.Sleep(retryWindow)
continue
}
return checkResult.Err == nil
}
}
func (hc *HealthChecker) controlPlaneComponentsSelector() string {
return fmt.Sprintf("%s,!%s", k8s.ControllerNSLabel, LinkerdCNIResourceLabel)
}
@ -2323,15 +2229,6 @@ func (hc *HealthChecker) checkClockSkew(ctx context.Context) error {
return nil
}
func (cr *CheckResult) alreadyObserved(previousResults []CheckResult) bool {
for _, result := range previousResults {
if result.Description == cr.Description && result.Err == cr.Err {
return true
}
}
return false
}
// CheckRoles checks that the expected roles exist.
func CheckRoles(ctx context.Context, kubeAPI *k8s.KubernetesAPI, shouldExist bool, namespace string, expectedNames []string, labelSelector string) error {
options := metav1.ListOptions{

View File

@ -12,7 +12,6 @@ import (
"time"
"github.com/golang/protobuf/ptypes/duration"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
configPb "github.com/linkerd/linkerd2/controller/gen/config"
"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
"github.com/linkerd/linkerd2/pkg/identity"
@ -148,8 +147,8 @@ func TestHealthChecker(t *testing.T) {
checkers: []Checker{
{
description: "skipRpc",
checkRPC: func(context.Context) (*healthcheckPb.SelfCheckResponse, error) {
return nil, &SkipError{Reason: "needs skipping"}
check: func(context.Context) error {
return &SkipError{Reason: "needs skipping"}
},
retryDeadline: time.Time{},
},

View File

@ -1,24 +0,0 @@
syntax = "proto3";
package linkerd2.common.healthcheck;
option go_package = "github.com/linkerd/linkerd2/controller/gen/common/healthcheck";
enum CheckStatus {
OK = 0;
FAIL = 1;
ERROR = 2;
}
message CheckResult {
string SubsystemName = 1;
string CheckDescription = 2;
CheckStatus Status = 3;
string FriendlyMessageToUser = 4;
}
message SelfCheckRequest {}
message SelfCheckResponse {
repeated CheckResult results = 1;
}

View File

@ -27,7 +27,5 @@ linkerd-viz
√ grafana is installed and configured correctly
√ can initialize the client
√ viz extension self-check
√ [kubernetes] linkerd viz can talk to Kubernetes
√ [prometheus] linkerd viz can talk to Prometheus
Status check results are √

View File

@ -9,7 +9,6 @@ import (
"net/url"
"github.com/golang/protobuf/proto"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/protohttp"
pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"
@ -62,8 +61,8 @@ func (c *grpcOverHTTPClient) Gateways(ctx context.Context, req *pb.GatewaysReque
return &msg, err
}
func (c *grpcOverHTTPClient) SelfCheck(ctx context.Context, req *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) {
var msg healthcheckPb.SelfCheckResponse
func (c *grpcOverHTTPClient) SelfCheck(ctx context.Context, req *pb.SelfCheckRequest, _ ...grpc.CallOption) (*pb.SelfCheckResponse, error) {
var msg pb.SelfCheckResponse
err := c.apiRequest(ctx, "SelfCheck", req, &msg)
return &msg, err
}

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,6 @@ import (
"time"
"github.com/golang/protobuf/ptypes/duration"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
"github.com/linkerd/linkerd2/controller/k8s"
pkgK8s "github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/prometheus"
@ -175,33 +174,33 @@ func (s *grpcServer) ListPods(ctx context.Context, req *pb.ListPodsRequest) (*pb
return &rsp, nil
}
func (s *grpcServer) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) {
k8sClientCheck := &healthcheckPb.CheckResult{
func (s *grpcServer) SelfCheck(ctx context.Context, in *pb.SelfCheckRequest) (*pb.SelfCheckResponse, error) {
k8sClientCheck := &pb.CheckResult{
SubsystemName: k8sClientSubsystemName,
CheckDescription: k8sClientCheckDescription,
Status: healthcheckPb.CheckStatus_OK,
Status: pb.CheckStatus_OK,
}
_, err := s.k8sAPI.Pod().Lister().List(labels.Everything())
if err != nil {
k8sClientCheck.Status = healthcheckPb.CheckStatus_ERROR
k8sClientCheck.Status = pb.CheckStatus_ERROR
k8sClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling the Kubernetes API: %s", err)
}
response := &healthcheckPb.SelfCheckResponse{
Results: []*healthcheckPb.CheckResult{
response := &pb.SelfCheckResponse{
Results: []*pb.CheckResult{
k8sClientCheck,
},
}
if s.prometheusAPI != nil {
promClientCheck := &healthcheckPb.CheckResult{
promClientCheck := &pb.CheckResult{
SubsystemName: promClientSubsystemName,
CheckDescription: promClientCheckDescription,
Status: healthcheckPb.CheckStatus_OK,
Status: pb.CheckStatus_OK,
}
_, err = s.queryProm(ctx, fmt.Sprintf(podQuery, ""))
if err != nil {
promClientCheck.Status = healthcheckPb.CheckStatus_ERROR
promClientCheck.Status = pb.CheckStatus_ERROR
promClientCheck.FriendlyMessageToUser = fmt.Sprintf("Error calling Prometheus from the control plane: %s", err)
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"net/http"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
"github.com/linkerd/linkerd2/controller/k8s"
"github.com/linkerd/linkerd2/pkg/prometheus"
"github.com/linkerd/linkerd2/pkg/protohttp"
@ -146,7 +145,7 @@ func (h *handler) handleTopRoutes(w http.ResponseWriter, req *http.Request) {
}
func (h *handler) handleSelfCheck(w http.ResponseWriter, req *http.Request) {
var protoRequest healthcheckPb.SelfCheckRequest
var protoRequest pb.SelfCheckRequest
err := protohttp.HTTPRequestToProto(req, &protoRequest)
if err != nil {
protohttp.WriteErrorToHTTPResponse(w, err)

View File

@ -8,7 +8,6 @@ import (
"testing"
"github.com/golang/protobuf/proto"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
vizClient "github.com/linkerd/linkerd2/viz/metrics-api/client"
pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"
)
@ -53,9 +52,9 @@ func (m *mockGrpcServer) ListServices(ctx context.Context, req *pb.ListServicesR
return m.ResponseToReturn.(*pb.ListServicesResponse), m.ErrorToReturn
}
func (m *mockGrpcServer) SelfCheck(ctx context.Context, req *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) {
func (m *mockGrpcServer) SelfCheck(ctx context.Context, req *pb.SelfCheckRequest) (*pb.SelfCheckResponse, error) {
m.LastRequestReceived = req
return m.ResponseToReturn.(*healthcheckPb.SelfCheckResponse), m.ErrorToReturn
return m.ResponseToReturn.(*pb.SelfCheckResponse), m.ErrorToReturn
}
type grpcCallTestCase struct {

View File

@ -3,13 +3,31 @@ syntax = "proto3";
package linkerd2.viz;
import "google/protobuf/duration.proto";
import "common/healthcheck.proto";
import "common/net.proto";
option go_package = "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz";
message Empty {}
enum CheckStatus {
OK = 0;
FAIL = 1;
ERROR = 2;
}
message CheckResult {
string SubsystemName = 1;
string CheckDescription = 2;
CheckStatus Status = 3;
string FriendlyMessageToUser = 4;
}
message SelfCheckRequest {}
message SelfCheckResponse {
repeated CheckResult results = 1;
}
message ListServicesRequest {
string namespace = 1;
}
@ -504,7 +522,7 @@ service Api {
rpc ListServices(ListServicesRequest) returns (ListServicesResponse) {}
rpc SelfCheck(linkerd2.common.healthcheck.SelfCheckRequest) returns (linkerd2.common.healthcheck.SelfCheckResponse) {}
rpc SelfCheck(SelfCheckRequest) returns (SelfCheckResponse) {}
}

View File

@ -8,7 +8,6 @@ import (
"sync"
"time"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
"github.com/linkerd/linkerd2/controller/k8s"
pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"
promv1 "github.com/prometheus/client_golang/api/prometheus/v1"
@ -25,7 +24,7 @@ type MockAPIClient struct {
GatewaysResponseToReturn *pb.GatewaysResponse
TopRoutesResponseToReturn *pb.TopRoutesResponse
EdgesResponseToReturn *pb.EdgesResponse
SelfCheckResponseToReturn *healthcheckPb.SelfCheckResponse
SelfCheckResponseToReturn *pb.SelfCheckResponse
}
// StatSummary provides a mock of a Public API method.
@ -59,7 +58,7 @@ func (c *MockAPIClient) ListServices(ctx context.Context, in *pb.ListServicesReq
}
// SelfCheck provides a mock of a Public API method.
func (c *MockAPIClient) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) {
func (c *MockAPIClient) SelfCheck(ctx context.Context, in *pb.SelfCheckRequest, _ ...grpc.CallOption) (*pb.SelfCheckResponse, error) {
return c.SelfCheckResponseToReturn, c.ErrorToReturn
}

View File

@ -3,10 +3,10 @@ package healthcheck
import (
"context"
"crypto/x509"
"errors"
"fmt"
"strings"
healthcheckPb "github.com/linkerd/linkerd2/controller/gen/common/healthcheck"
"github.com/linkerd/linkerd2/pkg/healthcheck"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/tls"
@ -233,8 +233,28 @@ func (hc *HealthChecker) VizCategory() healthcheck.Category {
// "waiting for check to complete" while things converge. If after the timeout
// it still hasn't converged, we show the real error (a 503 usually).
WithRetryDeadline(hc.RetryDeadline).
WithCheckRPC(func(ctx context.Context) (*healthcheckPb.SelfCheckResponse, error) {
return hc.vizAPIClient.SelfCheck(ctx, &healthcheckPb.SelfCheckRequest{})
WithCheck(func(ctx context.Context) error {
results, err := hc.vizAPIClient.SelfCheck(ctx, &pb.SelfCheckRequest{})
if err != nil {
return err
}
if len(results.GetResults()) == 0 {
return errors.New("No results returned")
}
errs := []string{}
for _, res := range results.GetResults() {
if res.GetStatus() != pb.CheckStatus_OK {
errs = append(errs, res.GetFriendlyMessageToUser())
}
}
if len(errs) == 0 {
return nil
}
errsStr := strings.Join(errs, "\n ")
return errors.New(errsStr)
}),
}, true)
}