Skip to content

Commit

Permalink
Create and List Connection Diagnostics (gravitational#14781)
Browse files Browse the repository at this point in the history
In Teleport Discover, we want to improve the UX of adding a resource.
We'll suggest users to test accessing a resource, and give them immediate feedback using the UI.

Note: we'll add this feature to Server Access, later one we'll add other resources.

After adding a server resource, the user will be presented with a command to test the connection:
`tsh ssh --test-id=123 user@host`

Running that command will provide the user feedback right in the UI of whether or not it worked.
If not, a short message will be displayed describing why it didn't work and how the user can solve the problem.

---

To achieve this we'll need a couple of changes:
1. new resource that stores the result of trying to connect to a given resource, so that the UI can ask for it
2. change `tsh ssh` to receive the `--test-id` flag and write the result to a record of this new resource

This PR addresses 1 - new resource.

A new resource is created:
```
Connection Diagnostics
- success (bool)
- message (string)
```

Two new rpc methods:
- create connection diagnostics
- read connection diagnostics

One new HTTP Endpoint:
- read connection diagnostics

Related to
gravitational#13957
  • Loading branch information
marcoandredinis authored Aug 1, 2022
1 parent 75f4a87 commit 89f4d54
Show file tree
Hide file tree
Showing 19 changed files with 2,469 additions and 1,259 deletions.
19 changes: 19 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2779,3 +2779,22 @@ func (c *Client) GetClusterCACert(ctx context.Context) (*proto.GetClusterCACertR
}
return resp, nil
}

// GetConnectionDiagnostic reads a connection diagnostic
func (c *Client) GetConnectionDiagnostic(ctx context.Context, name string) (types.ConnectionDiagnostic, error) {
req := &proto.GetConnectionDiagnosticRequest{
Name: name,
}
res, err := c.grpc.GetConnectionDiagnostic(ctx, req, c.callOpts...)
return res, trail.FromGRPC(err)
}

// CreateConnectionDiagnostic creates a new connection diagnostic.
func (c *Client) CreateConnectionDiagnostic(ctx context.Context, connectionDiagnostic types.ConnectionDiagnostic) error {
connectionDiagnosticV1, ok := connectionDiagnostic.(*types.ConnectionDiagnosticV1)
if !ok {
return trace.BadParameter("invalid type %T", connectionDiagnostic)
}
_, err := c.grpc.CreateConnectionDiagnostic(ctx, connectionDiagnosticV1, c.callOpts...)
return trail.FromGRPC(err)
}
1,016 changes: 639 additions & 377 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions api/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1920,6 +1920,12 @@ message InventoryPingRequest { string ServerID = 1; }
// inventory ping request.
message InventoryPingResponse { int64 Duration = 1 [ (gogoproto.casttype) = "time.Duration" ]; }

// GetConnectionDiagnosticRequest is a request to return a connection diagnostic.
message GetConnectionDiagnosticRequest {
// Name is the name of the connection diagnostic.
string Name = 1 [ (gogoproto.jsontag) = "name" ];
}

// AuthService is authentication/authorization service implementation
service AuthService {
// InventoryControlStream is the per-instance stream used to advertise teleport instance
Expand Down Expand Up @@ -2398,6 +2404,12 @@ service AuthService {
// GenerateCertAuthorityCRL creates an empty CRL for the specified CA.
rpc GenerateCertAuthorityCRL(CertAuthorityRequest) returns (CRL);

// CreateConnectionDiagnostic creates a new connection diagnostic.
rpc CreateConnectionDiagnostic(types.ConnectionDiagnosticV1) returns (google.protobuf.Empty);
// GetConnectionDiagnostic reads a connection diagnostic.
rpc GetConnectionDiagnostic(GetConnectionDiagnosticRequest)
returns (types.ConnectionDiagnosticV1);

// ChangeUserAuthentication allows a user to change their password and if enabled,
// also adds a new MFA device. After successful invocation, a new web session is created as well
// as a new set of recovery codes (if user meets the requirements to receive them), invalidating
Expand Down
110 changes: 110 additions & 0 deletions api/types/connection_diagnostic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
Copyright 2022 Gravitational, Inc.
Licensed 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 types

import (
"github.com/gravitational/teleport/api/utils"
"github.com/gravitational/trace"
)

// ConnectionDiagnostic represents a Connection Diagnostic.
type ConnectionDiagnostic interface {
// ResourceWithLabels provides common resource methods.
ResourceWithLabels

// Whether the connection was successful
IsSuccess() bool

// The underlying message
GetMessage() string
}

type ConnectionsDiagnostic []ConnectionDiagnostic

var _ ConnectionDiagnostic = &ConnectionDiagnosticV1{}

// NewConnectionDiagnosticV1 creates a new ConnectionDiagnosticV1 resource.
func NewConnectionDiagnosticV1(name string, labels map[string]string, spec ConnectionDiagnosticSpecV1) (*ConnectionDiagnosticV1, error) {
c := &ConnectionDiagnosticV1{
ResourceHeader: ResourceHeader{
Version: V1,
Kind: KindConnectionDiagnostic,
Metadata: Metadata{
Name: name,
Labels: labels,
},
},
Spec: spec,
}

if err := c.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}

return c, nil
}

// CheckAndSetDefaults checks and sets default values for any missing fields.
func (c *ConnectionDiagnosticV1) CheckAndSetDefaults() error {
if c.Spec.Message == "" {
return trace.BadParameter("ConnectionDiagnosticV1.Spec missing Message field")
}

return nil
}

// GetAllLabels returns combined static and dynamic labels.
func (c *ConnectionDiagnosticV1) GetAllLabels() map[string]string {
return CombineLabels(c.Metadata.Labels, nil)
}

// GetStaticLabels returns the connection diagnostic static labels.
func (c *ConnectionDiagnosticV1) GetStaticLabels() map[string]string {
return c.Metadata.Labels
}

// IsSuccess returns whether the connection was successful
func (c *ConnectionDiagnosticV1) IsSuccess() bool {
return c.Spec.Success
}

// GetMessage returns the connection diagnostic message.
func (c *ConnectionDiagnosticV1) GetMessage() string {
return c.Spec.Message
}

// MatchSearch goes through select field values and tries to
// match against the list of search values.
func (c *ConnectionDiagnosticV1) MatchSearch(values []string) bool {
fieldVals := append(utils.MapToStrings(c.GetAllLabels()), c.GetName())
return MatchSearch(fieldVals, values, nil)
}

// Origin returns the origin value of the resource.
func (c *ConnectionDiagnosticV1) Origin() string {
return c.Metadata.Labels[OriginLabel]
}

// SetOrigin sets the origin value of the resource.
func (c *ConnectionDiagnosticV1) SetOrigin(o string) {
c.Metadata.Labels[OriginLabel] = o
}

// SetStaticLabels sets the connection diagnostic static labels.
func (c *ConnectionDiagnosticV1) SetStaticLabels(sl map[string]string) {
c.Metadata.Labels = sl
}
3 changes: 3 additions & 0 deletions api/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ const (
// KindSessionTracker is a resource that tracks a live session.
KindSessionTracker = "session_tracker"

// KindConnectionDiagnostic is a resource that tracks the result of testing a connection
KindConnectionDiagnostic = "connection_diagnostic"

// V5 is the fifth version of resources.
V5 = "v5"

Expand Down
Loading

0 comments on commit 89f4d54

Please sign in to comment.