Skip to content
This repository has been archived by the owner on Aug 12, 2022. It is now read-only.

Commit

Permalink
feat: ConfigureProvider to return diagnostics, add diag.USER and Unsq…
Browse files Browse the repository at this point in the history
…uashDiag (#226)
  • Loading branch information
disq authored Apr 26, 2022
1 parent 9228d73 commit 2fb881c
Show file tree
Hide file tree
Showing 12 changed files with 421 additions and 344 deletions.
13 changes: 9 additions & 4 deletions cqproto/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ func (g GRPCClient) ConfigureProvider(ctx context.Context, request *ConfigurePro
if err != nil {
return nil, err
}
return &ConfigureProviderResponse{res.GetError()}, nil
return &ConfigureProviderResponse{
Diagnostics: diagnosticsFromProto("", res.Diagnostics),
}, nil
}

func (g GRPCClient) FetchResources(ctx context.Context, request *FetchResourcesRequest) (FetchResourcesStream, error) {
Expand Down Expand Up @@ -170,7 +172,10 @@ func (g *GRPCServer) ConfigureProvider(ctx context.Context, request *internal.Co
if err != nil {
return nil, err
}
return &internal.ConfigureProvider_Response{Error: resp.Error}, nil
return &internal.ConfigureProvider_Response{
Error: resp.Diagnostics.Error(), // For backwards compatibility
Diagnostics: diagnosticsToProto(resp.Diagnostics),
}, nil
}

func (g *GRPCServer) FetchResources(request *internal.FetchResources_Request, server internal.Provider_FetchResourcesServer) error {
Expand Down Expand Up @@ -410,7 +415,7 @@ func diagnosticsFromProto(resourceName string, in []*internal.Diagnostic) diag.D
pdiag := &ProviderDiagnostic{
ResourceName: resourceName,
ResourceId: p.GetResourceId(),
DiagnosticType: diag.DiagnosticType(p.GetType()),
DiagnosticType: diag.Type(p.GetType()),
DiagnosticSeverity: diag.Severity(p.GetSeverity()),
Summary: p.GetSummary(),
Details: p.GetDetail(),
Expand All @@ -419,7 +424,7 @@ func diagnosticsFromProto(resourceName string, in []*internal.Diagnostic) diag.D
diagnostics[i] = diag.NewRedactedDiagnostic(pdiag, &ProviderDiagnostic{
ResourceName: resourceName,
ResourceId: r.GetResourceId(),
DiagnosticType: diag.DiagnosticType(r.GetType()),
DiagnosticType: diag.Type(r.GetType()),
DiagnosticSeverity: diag.Severity(r.GetSeverity()),
Summary: r.GetSummary(),
Details: r.GetDetail(),
Expand Down
614 changes: 313 additions & 301 deletions cqproto/internal/plugin.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cqproto/internal/plugin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ message ConfigureProvider {
}
message Response {
string error = 1;
repeated Diagnostic diagnostics = 2;
}
}

Expand Down
8 changes: 4 additions & 4 deletions cqproto/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ type ConfigureProviderRequest struct {
}

type ConfigureProviderResponse struct {
// Error should be set to a string describing the error.
// Diagnostics about the configure action. If includes ERROR severity, operation is aborted.
// The error can be either from malformed configuration or failure to setup
Error string
Diagnostics diag.Diagnostics
}

// FetchResourcesRequest represents a CloudQuery RPC request of one or more resources
Expand Down Expand Up @@ -208,7 +208,7 @@ type ConnectionDetails struct {
type ProviderDiagnostic struct {
ResourceName string
ResourceId []string
DiagnosticType diag.DiagnosticType
DiagnosticType diag.Type
DiagnosticSeverity diag.Severity
Summary string
Details string
Expand All @@ -218,7 +218,7 @@ func (p ProviderDiagnostic) Severity() diag.Severity {
return p.DiagnosticSeverity
}

func (p ProviderDiagnostic) Type() diag.DiagnosticType {
func (p ProviderDiagnostic) Type() diag.Type {
return p.DiagnosticType
}

Expand Down
28 changes: 24 additions & 4 deletions provider/diag/diagnostic.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,24 @@ const (
PANIC
)

type DiagnosticType int
func (s Severity) String() string {
switch s {
case IGNORE:
return "Ignore"
case WARNING:
return "Warning"
case ERROR:
return "Error"
case PANIC:
return "Panic"
default:
return "Unknown"
}
}

type Type int

func (d DiagnosticType) String() string {
func (d Type) String() string {
switch d {
case RESOLVING:
return "Resolving"
Expand All @@ -25,6 +40,10 @@ func (d DiagnosticType) String() string {
return "Throttle"
case DATABASE:
return "Database"
case USER:
return "User"
case INTERNAL:
return "Internal"
case UNKNOWN:
fallthrough
default:
Expand All @@ -33,19 +52,20 @@ func (d DiagnosticType) String() string {
}

const (
UNKNOWN DiagnosticType = iota
UNKNOWN Type = iota
RESOLVING
ACCESS
THROTTLE
DATABASE
SCHEMA
INTERNAL
USER
)

type Diagnostic interface {
error
Severity() Severity
Type() DiagnosticType
Type() Type
Description() Description
}

Expand Down
4 changes: 3 additions & 1 deletion provider/diag/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package diag
import (
"bytes"
"fmt"
"reflect"
"strings"

"github.com/hashicorp/errwrap"
Expand Down Expand Up @@ -110,7 +111,7 @@ func (diags Diagnostics) Squash() Diagnostics {
}
}

key := fmt.Sprintf("%s_%s_%d_%d", keygen.Error(), keygen.Description().Resource, keygen.Severity(), keygen.Type())
key := fmt.Sprintf("%s_%s_%s_%d_%d", reflect.ValueOf(keygen).Type().String(), keygen.Error(), keygen.Description().Resource, keygen.Severity(), keygen.Type())
if sd, ok := dd[key]; ok {
sd.count += CountDiag(d)
continue
Expand All @@ -122,6 +123,7 @@ func (diags Diagnostics) Squash() Diagnostics {
dd[key] = nsd
sdd = append(sdd, nsd)
}

return sdd
}

Expand Down
14 changes: 7 additions & 7 deletions provider/diag/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ type BaseError struct {
// Detail is an optional second message, typically used to communicate a potential fix to the user.
detail string

// DiagnosticType indicates the classification family of this diagnostic
diagnosticType DiagnosticType
// Type indicates the classification family of this diagnostic
diagnosticType Type

// if noOverwrite is true, further Options won't overwrite previously set values. Valid for the duration of one "invocation"
noOverwrite bool
}

// NewBaseError creates a BaseError from given error, except the given error is a BaseError itself
func NewBaseError(err error, dt DiagnosticType, opts ...BaseErrorOption) *BaseError {
func NewBaseError(err error, dt Type, opts ...BaseErrorOption) *BaseError {
be, ok := err.(*BaseError)
if !ok {
be = &BaseError{
Expand Down Expand Up @@ -90,7 +90,7 @@ func (e BaseError) Description() Description {
}
}

func (e BaseError) Type() DiagnosticType {
func (e BaseError) Type() Type {
return e.diagnosticType
}

Expand Down Expand Up @@ -138,7 +138,7 @@ func WithOptionalSeverity(s Severity) BaseErrorOption {
}
}

func WithType(dt DiagnosticType) BaseErrorOption {
func WithType(dt Type) BaseErrorOption {
return func(e *BaseError) {
if !e.noOverwrite || dt > e.diagnosticType {
e.diagnosticType = dt
Expand Down Expand Up @@ -186,8 +186,8 @@ func WithError(err error) BaseErrorOption {
}
}

// Convert an error to Diagnostics, or return if it's already of type diagnostic(s). nil error returns nil value.
func FromError(err error, dt DiagnosticType, opts ...BaseErrorOption) Diagnostics {
// FromError converts an error to Diagnostics, or return if it's already of type diagnostic(s). nil error returns nil value.
func FromError(err error, dt Type, opts ...BaseErrorOption) Diagnostics {
if err == nil {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion provider/diag/flat.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type FlatDiag struct {
Err string
Resource string
ResourceID []string
Type DiagnosticType
Type Type
Severity Severity
Summary string
Description Description
Expand Down
2 changes: 1 addition & 1 deletion provider/diag/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func (n nativeError) Severity() Severity {
return ERROR
}

func (n nativeError) Type() DiagnosticType {
func (n nativeError) Type() Type {
return INTERNAL
}

Expand Down
24 changes: 23 additions & 1 deletion provider/diag/squash.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ func (s SquashedDiag) Description() Description {
return description
}

// Count returns the number of diagnostics inside the squashed diagnostic
func (s SquashedDiag) Count() uint64 {
return s.count
}

// Redacted returns the redacted version of the first diagnostic, if there is any
func (s SquashedDiag) Redacted() Diagnostic {
rd, ok := s.Diagnostic.(Redactable)
if !ok {
Expand All @@ -52,6 +54,11 @@ func (s SquashedDiag) Redacted() Diagnostic {
}
}

// Unsquash returns the first diagnostic of the squashed set
func (s SquashedDiag) Unsquash() Diagnostic {
return s.Diagnostic
}

type Countable interface {
Count() uint64
}
Expand All @@ -64,4 +71,19 @@ func CountDiag(d Diagnostic) uint64 {
return 1
}

var _ Countable = (*SquashedDiag)(nil)
type Unsquashable interface {
Unsquash() Diagnostic
}

func UnsquashDiag(d Diagnostic) Diagnostic {
if c, ok := d.(Unsquashable); ok {
return c.Unsquash()
}

return d
}

var (
_ Countable = (*SquashedDiag)(nil)
_ Unsquashable = (*SquashedDiag)(nil)
)
34 changes: 24 additions & 10 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Provider struct {
// Version of the provider
Version string
// Configure the provider and return context
Configure func(hclog.Logger, interface{}) (schema.ClientMeta, error)
Configure func(hclog.Logger, interface{}) (schema.ClientMeta, diag.Diagnostics)
// ResourceMap is all resources supported by this plugin
ResourceMap map[string]*schema.Table
// Configuration decoded from configure request
Expand Down Expand Up @@ -96,12 +96,16 @@ func (p *Provider) GetProviderConfig(_ context.Context, _ *cqproto.GetProviderCo

func (p *Provider) ConfigureProvider(_ context.Context, request *cqproto.ConfigureProviderRequest) (*cqproto.ConfigureProviderResponse, error) {
if p.Logger == nil {
return &cqproto.ConfigureProviderResponse{Error: fmt.Sprintf("provider %s logger not defined, make sure to run it with serve", p.Name)}, fmt.Errorf("provider %s logger not defined, make sure to run it with serve", p.Name)
return &cqproto.ConfigureProviderResponse{
Diagnostics: diag.FromError(fmt.Errorf("provider %s logger not defined, make sure to run it with serve", p.Name), diag.INTERNAL),
}, nil
}

if p.meta != nil {
if !IsDebug() {
return &cqproto.ConfigureProviderResponse{Error: fmt.Sprintf("provider %s was already configured", p.Name)}, fmt.Errorf("provider %s was already configured", p.Name)
return &cqproto.ConfigureProviderResponse{
Diagnostics: diag.FromError(fmt.Errorf("provider %s was already configured", p.Name), diag.INTERNAL),
}, nil
}

p.Logger.Info("Reconfiguring provider: Previous configuration has been reset.")
Expand All @@ -119,7 +123,9 @@ func (p *Provider) ConfigureProvider(_ context.Context, request *cqproto.Configu
p.dbURL = request.Connection.DSN
providerConfig := p.Config()
if err := defaults.Set(providerConfig); err != nil {
return &cqproto.ConfigureProviderResponse{}, err
return &cqproto.ConfigureProviderResponse{
Diagnostics: diag.FromError(err, diag.INTERNAL),
}, nil
}
// if we received an empty config we notify in log and only use defaults.
if len(request.Config) == 0 {
Expand All @@ -129,24 +135,32 @@ func (p *Provider) ConfigureProvider(_ context.Context, request *cqproto.Configu
// this part will be deprecated.
if err := hclsimple.Decode("config.json", request.Config, nil, providerConfig); err != nil {
p.Logger.Error("Failed to load configuration.", "error", err)
return &cqproto.ConfigureProviderResponse{}, err
return &cqproto.ConfigureProviderResponse{
Diagnostics: diag.FromError(err, diag.USER),
}, nil
}
}

client, err := p.Configure(p.Logger, providerConfig)
if err != nil {
return &cqproto.ConfigureProviderResponse{}, err
client, diags := p.Configure(p.Logger, providerConfig)
if diags.HasErrors() {
return &cqproto.ConfigureProviderResponse{
Diagnostics: diags,
}, nil
}

tables := make(map[string]string)
for r, t := range p.ResourceMap {
if err := getTableDuplicates(r, t, tables); err != nil {
return &cqproto.ConfigureProviderResponse{}, err
return &cqproto.ConfigureProviderResponse{
Diagnostics: diags.Add(diag.FromError(err, diag.INTERNAL)),
}, nil
}
}

p.meta = client
return &cqproto.ConfigureProviderResponse{}, nil
return &cqproto.ConfigureProviderResponse{
Diagnostics: diags,
}, nil
}

func (p *Provider) FetchResources(ctx context.Context, request *cqproto.FetchResourcesRequest, sender cqproto.FetchResourcesSender) error {
Expand Down
Loading

0 comments on commit 2fb881c

Please sign in to comment.