From ae8b3dcb12bbd1049c0365c7eafc78f3432389d4 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 3 Feb 2022 13:20:02 +0100 Subject: [PATCH 1/7] Validate TypeInstance input during TypeInstance creation --- cmd/argo-actions/main.go | 23 ++++--- cmd/cli/cmd/typeinstance/create.go | 25 +++++-- internal/cli/client/hub.go | 4 ++ internal/cli/validate/validate.go | 4 ++ pkg/argo-actions/upload_type_instances.go | 34 +++++++--- pkg/hub/client/client.go | 1 + pkg/hub/client/public/client.go | 68 +++++++++++++++++++ pkg/sdk/validation/manifest/typeinstance.go | 62 +++++++++++++++++ .../validation/manifest/typeinstance_test.go | 47 +++++++++++++ 9 files changed, 243 insertions(+), 25 deletions(-) create mode 100644 pkg/sdk/validation/manifest/typeinstance.go create mode 100644 pkg/sdk/validation/manifest/typeinstance_test.go diff --git a/cmd/argo-actions/main.go b/cmd/argo-actions/main.go index dc576aa37..d68a80bab 100644 --- a/cmd/argo-actions/main.go +++ b/cmd/argo-actions/main.go @@ -9,6 +9,7 @@ import ( "capact.io/capact/internal/logger" argoactions "capact.io/capact/pkg/argo-actions" "capact.io/capact/pkg/hub/client/local" + "capact.io/capact/pkg/hub/client/public" "github.com/vrischmann/envconfig" "go.uber.org/zap" @@ -16,12 +17,13 @@ import ( // Config for the argo-actions command. type Config struct { - Action string - DownloadConfig []argoactions.DownloadConfig `envconfig:"optional"` - UploadConfig argoactions.UploadConfig `envconfig:"optional"` - UpdateConfig argoactions.UpdateConfig `envconfig:"optional"` - LocalHubEndpoint string `envconfig:"default=http://capact-hub-local.capact-system/graphql"` - Logger logger.Config + Action string + DownloadConfig []argoactions.DownloadConfig `envconfig:"optional"` + UploadConfig argoactions.UploadConfig `envconfig:"optional"` + UpdateConfig argoactions.UpdateConfig `envconfig:"optional"` + LocalHubEndpoint string `envconfig:"default=http://capact-hub-local.capact-system/graphql"` + PublicHubEndpoint string `envconfig:"default=http://capact-hub-public.capact-system/graphql"` + Logger logger.Config } func main() { @@ -37,20 +39,21 @@ func main() { logger, err := logger.New(cfg.Logger) exitOnError(err, "while creating zap logger") - client := local.NewDefaultClient(cfg.LocalHubEndpoint) + localClient := local.NewDefaultClient(cfg.LocalHubEndpoint) + publicClient := public.NewDefaultClient(cfg.PublicHubEndpoint) switch cfg.Action { case argoactions.DownloadAction: log := logger.With(zap.String("Action", argoactions.DownloadAction)) - action = argoactions.NewDownloadAction(log, client, cfg.DownloadConfig) + action = argoactions.NewDownloadAction(log, localClient, cfg.DownloadConfig) case argoactions.UploadAction: log := logger.With(zap.String("Action", argoactions.UploadAction)) - action = argoactions.NewUploadAction(log, client, cfg.UploadConfig) + action = argoactions.NewUploadAction(log, localClient, publicClient, cfg.UploadConfig) case argoactions.UpdateAction: log := logger.With(zap.String("Action", argoactions.UpdateAction)) - action = argoactions.NewUpdateAction(log, client, cfg.UpdateConfig) + action = argoactions.NewUpdateAction(log, localClient, cfg.UpdateConfig) default: err := fmt.Errorf("Invalid action: %s", cfg.Action) diff --git a/cmd/cli/cmd/typeinstance/create.go b/cmd/cli/cmd/typeinstance/create.go index cfc69e1c0..1d96f5f95 100644 --- a/cmd/cli/cmd/typeinstance/create.go +++ b/cmd/cli/cmd/typeinstance/create.go @@ -14,6 +14,8 @@ import ( "capact.io/capact/internal/cli/printer" gqllocalapi "capact.io/capact/pkg/hub/api/graphql/local" + "capact.io/capact/pkg/sdk/validation/manifest" + "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/util/yaml" @@ -85,12 +87,28 @@ func NewCreate() *cobra.Command { func createTI(ctx context.Context, opts createOptions, resourcePrinter *printer.ResourcePrinter) error { typeInstanceToCreate := &gqllocalapi.CreateTypeInstancesInput{} + server := config.GetDefaultContext() + hubCli, err := client.NewHub(server) + if err != nil { + return err + } + for _, path := range opts.TypeInstancesFiles { out, err := loadCreateTypeInstanceFromFile(path) if err != nil { return err } + for _, ti := range out.TypeInstances { + validationResult, err := manifest.ValidateTI(ctx, ti, hubCli) + if err != nil { + return err + } + if len(validationResult.Errors) > 0 { + return fmt.Errorf("%s", validationResult.Errors) + } + } + typeInstanceToCreate = mergeCreateTypeInstances(typeInstanceToCreate, out) } @@ -99,13 +117,6 @@ func createTI(ctx context.Context, opts createOptions, resourcePrinter *printer. typeInstanceToCreate.UsesRelations = []*gqllocalapi.TypeInstanceUsesRelationInput{} } - server := config.GetDefaultContext() - - hubCli, err := client.NewHub(server) - if err != nil { - return err - } - createdTI, err := hubCli.CreateTypeInstances(ctx, typeInstanceToCreate) if err != nil { return err diff --git a/internal/cli/client/hub.go b/internal/cli/client/hub.go index 769c54f34..7a3ee8222 100644 --- a/internal/cli/client/hub.go +++ b/internal/cli/client/hub.go @@ -25,6 +25,7 @@ type Hub interface { ListInterfaces(ctx context.Context, opts ...public.InterfaceOption) ([]*gqlpublicapi.Interface, error) ListTypeInstances(ctx context.Context, filter *gqllocalapi.TypeInstanceFilter, opts ...local.TypeInstancesOption) ([]gqllocalapi.TypeInstance, error) ListImplementationRevisions(ctx context.Context, opts ...public.ListImplementationRevisionsOption) ([]*gqlpublicapi.ImplementationRevision, error) + FindTypeRevision(ctx context.Context, ref gqlpublicapi.TypeReference, opts ...public.TypeRevisionOption) (*gqlpublicapi.TypeRevision, error) FindTypeInstance(ctx context.Context, id string, opts ...local.TypeInstancesOption) (*gqllocalapi.TypeInstance, error) CreateTypeInstance(ctx context.Context, in *gqllocalapi.CreateTypeInstanceInput, opts ...local.TypeInstancesOption) (*gqllocalapi.TypeInstance, error) CreateTypeInstances(ctx context.Context, in *gqllocalapi.CreateTypeInstancesInput) ([]gqllocalapi.CreateTypeInstanceOutput, error) @@ -33,6 +34,9 @@ type Hub interface { FindInterfaceRevision(ctx context.Context, ref gqlpublicapi.InterfaceReference, opts ...public.InterfaceRevisionOption) (*gqlpublicapi.InterfaceRevision, error) FindTypeInstancesTypeRef(ctx context.Context, ids []string) (map[string]gqllocalapi.TypeInstanceTypeReference, error) CheckManifestRevisionsExist(ctx context.Context, manifestRefs []gqlpublicapi.ManifestReference) (map[gqlpublicapi.ManifestReference]bool, error) + // not implemented + GetInterfaceLatestRevisionString(ctx context.Context, ref gqlpublicapi.InterfaceReference) (string, error) + ListImplementationRevisionsForInterface(ctx context.Context, ref gqlpublicapi.InterfaceReference, opts ...public.ListImplementationRevisionsForInterfaceOption) ([]gqlpublicapi.ImplementationRevision, error) } // NewHub returns client for Capact Hub configured with saved credentials for a given server URL. diff --git a/internal/cli/validate/validate.go b/internal/cli/validate/validate.go index b49f3a849..148c243be 100644 --- a/internal/cli/validate/validate.go +++ b/internal/cli/validate/validate.go @@ -59,6 +59,10 @@ func (r *ValidationResult) Error() string { errMsgs = append(errMsgs, err.Error()) } + if r.Path == "" { + return fmt.Sprintf("\n * %s\n", strings.Join(errMsgs, "\n * ")) + } + return fmt.Sprintf("%q:\n * %s\n", r.Path, strings.Join(errMsgs, "\n * ")) } diff --git a/pkg/argo-actions/upload_type_instances.go b/pkg/argo-actions/upload_type_instances.go index 0812bf79c..1289a6e47 100644 --- a/pkg/argo-actions/upload_type_instances.go +++ b/pkg/argo-actions/upload_type_instances.go @@ -7,7 +7,10 @@ import ( "path/filepath" graphqllocal "capact.io/capact/pkg/hub/api/graphql/local" + "capact.io/capact/pkg/sdk/validation/manifest" + "capact.io/capact/pkg/hub/client/local" + "capact.io/capact/pkg/hub/client/public" "github.com/pkg/errors" "go.uber.org/zap" "sigs.k8s.io/yaml" @@ -25,17 +28,19 @@ type UploadConfig struct { // Upload implements the Action interface. // It is used to upload TypeInstances to the Local Hub. type Upload struct { - log *zap.Logger - client *local.Client - cfg UploadConfig + log *zap.Logger + localClient *local.Client + publicClient *public.Client + cfg UploadConfig } // NewUploadAction returns a new Upload instance. -func NewUploadAction(log *zap.Logger, client *local.Client, cfg UploadConfig) Action { +func NewUploadAction(log *zap.Logger, localClient *local.Client, publicClient *public.Client, cfg UploadConfig) Action { return &Upload{ - log: log, - client: client, - cfg: cfg, + log: log, + localClient: localClient, + publicClient: publicClient, + cfg: cfg, } } @@ -83,6 +88,19 @@ func (u *Upload) Do(ctx context.Context) error { return errors.Wrap(err, "while rendering CreateTypeInstancesInput") } + for _, ti := range payload.TypeInstances { + u.log.Info(fmt.Sprintf("Validating TypeInstance... %s", *ti.Alias)) + u.log.Info(fmt.Sprintf("ti %+v", *ti)) + u.log.Info(fmt.Sprintf("TypeRef %+v", *ti.TypeRef)) + validationResult, err := manifest.ValidateTI(ctx, ti, u.publicClient) + if err != nil { + return err + } + if len(validationResult.Errors) > 0 { + return fmt.Errorf("%s", validationResult.Errors) + } + } + u.log.Info("Uploading TypeInstances to Hub...", zap.Int("TypeInstance count", len(payload.TypeInstances))) uploadOutput, err := u.uploadTypeInstances(ctx, payload) @@ -112,5 +130,5 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m } func (u *Upload) uploadTypeInstances(ctx context.Context, in *graphqllocal.CreateTypeInstancesInput) ([]graphqllocal.CreateTypeInstanceOutput, error) { - return u.client.CreateTypeInstances(ctx, in) + return u.localClient.CreateTypeInstances(ctx, in) } diff --git a/pkg/hub/client/client.go b/pkg/hub/client/client.go index e2813f42d..b8138ba64 100644 --- a/pkg/hub/client/client.go +++ b/pkg/hub/client/client.go @@ -35,6 +35,7 @@ type Local interface { // Public interface aggregates methods to interact with Capact Public Hub. type Public interface { ListTypes(ctx context.Context, opts ...public.TypeOption) ([]*hubpublicgraphql.Type, error) + FindTypeRevision(ctx context.Context, ref hubpublicgraphql.TypeReference, opts ...public.TypeRevisionOption) (*hubpublicgraphql.TypeRevision, error) GetInterfaceLatestRevisionString(ctx context.Context, ref hubpublicgraphql.InterfaceReference) (string, error) FindInterfaceRevision(ctx context.Context, ref hubpublicgraphql.InterfaceReference, opts ...public.InterfaceRevisionOption) (*hubpublicgraphql.InterfaceRevision, error) ListImplementationRevisionsForInterface(ctx context.Context, ref hubpublicgraphql.InterfaceReference, opts ...public.ListImplementationRevisionsForInterfaceOption) ([]hubpublicgraphql.ImplementationRevision, error) diff --git a/pkg/hub/client/public/client.go b/pkg/hub/client/public/client.go index 8ea67ef55..b597a1b8d 100644 --- a/pkg/hub/client/public/client.go +++ b/pkg/hub/client/public/client.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + "capact.io/capact/pkg/httputil" gqlpublicapi "capact.io/capact/pkg/hub/api/graphql/public" "github.com/avast/retry-go" @@ -25,6 +26,15 @@ func NewClient(cli *graphql.Client) *Client { return &Client{client: cli} } +// NewDefaultClient creates ready to use client with default values. +func NewDefaultClient(endpoint string, opts ...httputil.ClientOption) *Client { + httpClient := httputil.NewClient(opts...) + clientOpt := graphql.WithHTTPClient(httpClient) + client := graphql.NewClient(endpoint, clientOpt) + + return NewClient(client) +} + // FindInterfaceRevision returns the InterfaceRevision for the given InterfaceReference. // It will return nil, if the InterfaceRevision is not found. func (c *Client) FindInterfaceRevision(ctx context.Context, ref gqlpublicapi.InterfaceReference, opts ...InterfaceRevisionOption) (*gqlpublicapi.InterfaceRevision, error) { @@ -91,6 +101,36 @@ func (c *Client) ListTypes(ctx context.Context, opts ...TypeOption) ([]*gqlpubli return resp.Types, nil } +func (c *Client) FindTypeRevision(ctx context.Context, ref gqlpublicapi.TypeReference, opts ...TypeRevisionOption) (*gqlpublicapi.TypeRevision, error) { + findOpts := &TypeRevisionOptions{} + findOpts.Apply(opts...) + + query, params := c.typeQueryForRef(findOpts.fields, ref) + req := graphql.NewRequest(fmt.Sprintf(`query FindTypeRevision($typePath: NodePath!, %s) { + type(path: $typePath) { + %s + } + }`, params.Query(), query)) + + req.Var("typePath", ref.Path) + params.PopulateVars(req) + + var resp struct { + Type struct { + Revision *gqlpublicapi.TypeRevision `json:"rev"` + } `json:"type"` + } + err := retry.Do(func() error { + return c.client.Run(ctx, req, &resp) + }, retry.Attempts(retryAttempts)) + + if err != nil { + return nil, errors.Wrap(err, "while executing query to fetch Hub Type Revision") + } + + return resp.Type.Revision, nil +} + // ListInterfaces returns all Interfaces. By default, only root fields are populated. Use options to add // latestRevision fields or apply additional filtering. func (c *Client) ListInterfaces(ctx context.Context, opts ...InterfaceOption) ([]*gqlpublicapi.Interface, error) { @@ -356,3 +396,31 @@ func (c *Client) specificInterfaceRevision(fields string, rev string) (string, A "$interfaceRev: Version!": rev, } } + +func (c *Client) typeQueryForRef(fields string, ref gqlpublicapi.TypeReference) (string, Args) { + if ref.Revision == "" { + return c.latestTypeRevision(fields) + } + + return c.specificTypeRevision(fields, ref.Revision) +} + +func (c *Client) latestTypeRevision(fields string) (string, Args) { + latestRevision := fmt.Sprintf(` + rev: latestRevision { + %s + }`, fields) + + return latestRevision, Args{} +} + +func (c *Client) specificTypeRevision(fields string, rev string) (string, Args) { + specificRevision := fmt.Sprintf(` + rev: revision(revision: $typeRev) { + %s + }`, fields) + + return specificRevision, Args{ + "$typeRev: Version!": rev, + } +} diff --git a/pkg/sdk/validation/manifest/typeinstance.go b/pkg/sdk/validation/manifest/typeinstance.go new file mode 100644 index 000000000..3ba499266 --- /dev/null +++ b/pkg/sdk/validation/manifest/typeinstance.go @@ -0,0 +1,62 @@ +package manifest + +import ( + "context" + "encoding/json" + "fmt" + + graphql "capact.io/capact/pkg/hub/api/graphql/public" + "k8s.io/utils/strings/slices" + + "capact.io/capact/pkg/hub/client" + + gqllocalapi "capact.io/capact/pkg/hub/api/graphql/local" +) + +type TypeJSONSchema struct { + Properties map[string]struct { + Id string `json:"$id"` + Type string `json:"type"` + Title string `json:"title"` + } `json:"properties"` +} + +// ValidateTI is responsible for validating the TypeInstance. +func ValidateTI(ctx context.Context, ti *gqllocalapi.CreateTypeInstanceInput, cl client.Public) (ValidationResult, error) { + if ti == nil { + return ValidationResult{}, nil + } + var errors []error + + typeRevision, err := cl.FindTypeRevision(ctx, graphql.TypeReference{ + Path: ti.TypeRef.Path, + Revision: ti.TypeRef.Revision, + }) + if err != nil { + return ValidationResult{}, err + } + + var typeJSONSchema TypeJSONSchema + if err := json.Unmarshal([]byte(fmt.Sprintf("%v", typeRevision.Spec.JSONSchema)), &typeJSONSchema); err != nil { + return ValidationResult{}, err + } + + var validKeys []string + for key := range typeJSONSchema.Properties { + validKeys = append(validKeys, key) + } + + // validate the keys + mapValues := ti.Value.(map[string]interface{}) + if len(mapValues) > 0 { + for key := range mapValues { + if !slices.Contains(validKeys, key) { + errors = append(errors, fmt.Errorf("key value: %s no defined by the Type", key)) + } + } + } + + return ValidationResult{ + Errors: errors, + }, nil +} diff --git a/pkg/sdk/validation/manifest/typeinstance_test.go b/pkg/sdk/validation/manifest/typeinstance_test.go new file mode 100644 index 000000000..ade8f43e6 --- /dev/null +++ b/pkg/sdk/validation/manifest/typeinstance_test.go @@ -0,0 +1,47 @@ +package manifest_test + +import ( + "testing" +) + +func TestValidateTI(t *testing.T) { + //tests := map[string]struct { + // typeInstance gqllocalapi.CreateTypeInstanceInput + // typeRevision hubpublicgraphql.TypeRevision + // expErrors []error + //}{ + // "": { + // typeInstance: gqllocalapi.CreateTypeInstanceInput{ + // TypeRef: &gqllocalapi.TypeInstanceTypeReferenceInput{ + // Path: "test", + // Revision: "0.1.0", + // }, + // Value: manifest.TypeJSONSchema{ + // Properties: map[string]struct { + // Id string "json:\"$id\"" + // Type string "json:\"type\"" + // Title string "json:\"title\"" + // }{ + // "test": {}, + // "test2": {}, + // "test3": {}, + // }, + // }, + // }, + // }, + //} + // + //for tn, tc := range tests { + // t.Run(tn, func(t *testing.T) { + // //given + // hubCli := fakeHub{} + // + // // when + // validationResults, err := manifest.ValidateTI(context.Background(),&tc.typeInstance,hubCli) + // + // // then + // require.NoError(t, err) + // assert.Equal(t, tc.expErrors, validationResults.Errors) + // }) + //} +} From 52c623f346fe830fb71dae0642b0156c6af3a66e Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 10 Feb 2022 10:38:04 +0100 Subject: [PATCH 2/7] Fixes after review --- cmd/argo-actions/main.go | 11 +- cmd/cli/cmd/typeinstance/create.go | 26 ++-- cmd/cli/cmd/typeinstance/edit.go | 29 +--- pkg/argo-actions/update_type_instances.go | 53 +++----- pkg/argo-actions/upload_type_instances.go | 42 +++--- pkg/sdk/validation/typeinstance.go | 155 +++++++++++++++------- pkg/sdk/validation/typeinstance_test.go | 87 ++++++------ test/e2e/action_test.go | 1 + 8 files changed, 209 insertions(+), 195 deletions(-) diff --git a/cmd/argo-actions/main.go b/cmd/argo-actions/main.go index 097a6f34c..c441e44a8 100644 --- a/cmd/argo-actions/main.go +++ b/cmd/argo-actions/main.go @@ -8,6 +8,7 @@ import ( "capact.io/capact/internal/logger" argoactions "capact.io/capact/pkg/argo-actions" + hubclient "capact.io/capact/pkg/hub/client" "capact.io/capact/pkg/hub/client/local" "capact.io/capact/pkg/hub/client/public" @@ -42,6 +43,12 @@ func main() { localClient := local.NewDefaultClient(cfg.LocalHubEndpoint) publicClient := public.NewDefaultClient(cfg.PublicHubEndpoint) + // TODO: Consider using connection `hubclient.New` and route requests through Gateway + client := hubclient.Client{ + Local: localClient, + Public: publicClient, + } + switch cfg.Action { case argoactions.DownloadAction: log := logger.With(zap.String("Action", argoactions.DownloadAction)) @@ -49,11 +56,11 @@ func main() { case argoactions.UploadAction: log := logger.With(zap.String("Action", argoactions.UploadAction)) - action = argoactions.NewUploadAction(log, localClient, publicClient, cfg.UploadConfig) + action = argoactions.NewUploadAction(log, &client, cfg.UploadConfig) case argoactions.UpdateAction: log := logger.With(zap.String("Action", argoactions.UpdateAction)) - action = argoactions.NewUpdateAction(log, localClient, publicClient, cfg.UpdateConfig) + action = argoactions.NewUpdateAction(log, &client, cfg.UpdateConfig) default: err := fmt.Errorf("Invalid action: %s", cfg.Action) diff --git a/cmd/cli/cmd/typeinstance/create.go b/cmd/cli/cmd/typeinstance/create.go index 40ce9219a..35ab9a40d 100644 --- a/cmd/cli/cmd/typeinstance/create.go +++ b/cmd/cli/cmd/typeinstance/create.go @@ -13,7 +13,6 @@ import ( "capact.io/capact/internal/cli/heredoc" "capact.io/capact/internal/cli/printer" gqllocalapi "capact.io/capact/pkg/hub/api/graphql/local" - "capact.io/capact/pkg/sdk/apis/0.0.1/types" "capact.io/capact/pkg/sdk/validation" "github.com/pkg/errors" @@ -99,26 +98,17 @@ func createTI(ctx context.Context, opts createOptions, resourcePrinter *printer. return err } - for _, ti := range out.TypeInstances { - validationResult, err := validation.ValidateTI(ctx, &validation.TypeInstanceValidation{ - Alias: ti.Alias, - Value: ti.Value, - TypeRef: types.TypeRef{ - Path: ti.TypeRef.Path, - Revision: ti.TypeRef.Revision, - }, - }, hubCli) - if err != nil { - return errors.Wrap(err, "while validating TypeInstance") - } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() - } - } - typeInstanceToCreate = mergeCreateTypeInstances(typeInstanceToCreate, out) } + validationResult, err := validation.ValidateTypeInstancesToCreate(ctx, hubCli, typeInstanceToCreate) + if err != nil { + return errors.Wrap(err, "while validating TypeInstances") + } + if validationResult.Len() > 0 { + return validationResult.ErrorOrNil() + } + // HACK: UsesRelations are required on GQL side so at least empty array needs to be send if typeInstanceToCreate.UsesRelations == nil { typeInstanceToCreate.UsesRelations = []*gqllocalapi.TypeInstanceUsesRelationInput{} diff --git a/cmd/cli/cmd/typeinstance/edit.go b/cmd/cli/cmd/typeinstance/edit.go index 60b3a0489..f54794b9b 100644 --- a/cmd/cli/cmd/typeinstance/edit.go +++ b/cmd/cli/cmd/typeinstance/edit.go @@ -9,7 +9,6 @@ import ( "capact.io/capact/internal/cli/client" "capact.io/capact/internal/cli/config" gqllocalapi "capact.io/capact/pkg/hub/api/graphql/local" - "capact.io/capact/pkg/sdk/apis/0.0.1/types" "capact.io/capact/pkg/sdk/validation" "github.com/AlecAivazis/survey/v2" @@ -61,28 +60,12 @@ func editTI(ctx context.Context, opts editOptions, w io.Writer) error { return err } - for _, ti := range typeInstanceToUpdate { - if ti.TypeInstance == nil { - continue - } - currentTI, err := hubCli.FindTypeInstance(ctx, ti.ID) - if err != nil { - return errors.Wrapf(err, "while finding TypeInstance %s", ti.ID) - } - - validationResult, err := validation.ValidateTI(ctx, &validation.TypeInstanceValidation{ - Value: ti.TypeInstance.Value, - TypeRef: types.TypeRef{ - Path: currentTI.TypeRef.Path, - Revision: currentTI.TypeRef.Revision, - }, - }, hubCli) - if err != nil { - return errors.Wrap(err, "while validating TypeInstance") - } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() - } + validationResult, err := validation.ValidateTypeInstanceToUpdate(ctx, hubCli, typeInstanceToUpdate) + if err != nil { + return errors.Wrap(err, "while validating TypeInstance") + } + if validationResult.Len() > 0 { + return validationResult.ErrorOrNil() } _, err = hubCli.UpdateTypeInstances(ctx, typeInstanceToUpdate) diff --git a/pkg/argo-actions/update_type_instances.go b/pkg/argo-actions/update_type_instances.go index 50d686206..ee053d36f 100644 --- a/pkg/argo-actions/update_type_instances.go +++ b/pkg/argo-actions/update_type_instances.go @@ -6,12 +6,9 @@ import ( "path" "path/filepath" - "capact.io/capact/pkg/sdk/apis/0.0.1/types" - "capact.io/capact/pkg/sdk/validation" - graphqllocal "capact.io/capact/pkg/hub/api/graphql/local" - "capact.io/capact/pkg/hub/client/local" - "capact.io/capact/pkg/hub/client/public" + hubclient "capact.io/capact/pkg/hub/client" + "capact.io/capact/pkg/sdk/validation" "github.com/pkg/errors" "go.uber.org/zap" "sigs.k8s.io/yaml" @@ -29,19 +26,17 @@ type UpdateConfig struct { // Update implements the Action interface. // It is used to update existing TypeInstances in the Local Hub. type Update struct { - log *zap.Logger - localClient *local.Client - publicClient *public.Client - cfg UpdateConfig + log *zap.Logger + client *hubclient.Client + cfg UpdateConfig } // NewUpdateAction returns a new Update instance. -func NewUpdateAction(log *zap.Logger, localClient *local.Client, publicClient *public.Client, cfg UpdateConfig) Action { +func NewUpdateAction(log *zap.Logger, client *hubclient.Client, cfg UpdateConfig) Action { return &Update{ - log: log, - localClient: localClient, - publicClient: publicClient, - cfg: cfg, + log: log, + client: client, + cfg: cfg, } } @@ -91,28 +86,12 @@ func (u *Update) Do(ctx context.Context) error { u.log.Info("Validating TypeInstances") - for _, ti := range payload { - if ti.TypeInstance == nil { - continue - } - currentTI, err := u.localClient.FindTypeInstance(ctx, ti.ID) - if err != nil { - return errors.Wrapf(err, "while finding TypeInstance %s", ti.ID) - } - - validationResult, err := validation.ValidateTI(ctx, &validation.TypeInstanceValidation{ - Value: ti.TypeInstance.Value, - TypeRef: types.TypeRef{ - Path: currentTI.TypeRef.Path, - Revision: currentTI.TypeRef.Revision, - }, - }, u.publicClient) - if err != nil { - return errors.Wrap(err, "while validating TypeInstance") - } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() - } + validationResult, err := validation.ValidateTypeInstanceToUpdate(ctx, u.client, payload) + if err != nil { + return errors.Wrap(err, "while validating TypeInstance") + } + if validationResult.Len() > 0 { + return validationResult.ErrorOrNil() } u.log.Info("Updating TypeInstances in Hub...", zap.Int("TypeInstance count", len(payload))) @@ -142,5 +121,5 @@ func (u *Update) render(payload []graphqllocal.UpdateTypeInstancesInput, values } func (u *Update) updateTypeInstances(ctx context.Context, in []graphqllocal.UpdateTypeInstancesInput) ([]graphqllocal.TypeInstance, error) { - return u.localClient.UpdateTypeInstances(ctx, in) + return u.client.Local.UpdateTypeInstances(ctx, in) } diff --git a/pkg/argo-actions/upload_type_instances.go b/pkg/argo-actions/upload_type_instances.go index 917fc1788..cefa9ad87 100644 --- a/pkg/argo-actions/upload_type_instances.go +++ b/pkg/argo-actions/upload_type_instances.go @@ -7,11 +7,9 @@ import ( "path/filepath" graphqllocal "capact.io/capact/pkg/hub/api/graphql/local" - "capact.io/capact/pkg/sdk/apis/0.0.1/types" + hubclient "capact.io/capact/pkg/hub/client" "capact.io/capact/pkg/sdk/validation" - "capact.io/capact/pkg/hub/client/local" - "capact.io/capact/pkg/hub/client/public" "github.com/pkg/errors" "go.uber.org/zap" "sigs.k8s.io/yaml" @@ -29,19 +27,17 @@ type UploadConfig struct { // Upload implements the Action interface. // It is used to upload TypeInstances to the Local Hub. type Upload struct { - log *zap.Logger - localClient *local.Client - publicClient *public.Client - cfg UploadConfig + log *zap.Logger + client *hubclient.Client + cfg UploadConfig } // NewUploadAction returns a new Upload instance. -func NewUploadAction(log *zap.Logger, localClient *local.Client, publicClient *public.Client, cfg UploadConfig) Action { +func NewUploadAction(log *zap.Logger, client *hubclient.Client, cfg UploadConfig) Action { return &Upload{ - log: log, - localClient: localClient, - publicClient: publicClient, - cfg: cfg, + log: log, + client: client, + cfg: cfg, } } @@ -91,20 +87,12 @@ func (u *Upload) Do(ctx context.Context) error { u.log.Info("Validating TypeInstances") - for _, ti := range payload.TypeInstances { - validationResult, err := validation.ValidateTI(ctx, &validation.TypeInstanceValidation{ - Value: ti.Value, - TypeRef: types.TypeRef{ - Path: ti.TypeRef.Path, - Revision: ti.TypeRef.Revision, - }, - }, u.publicClient) - if err != nil { - return errors.Wrap(err, "while validating TypeInstance") - } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() - } + validationResult, err := validation.ValidateTypeInstancesToCreate(ctx, u.client, payload) + if err != nil { + return errors.Wrap(err, "while validating TypeInstances") + } + if validationResult.Len() > 0 { + return validationResult.ErrorOrNil() } u.log.Info("Uploading TypeInstances to Hub...", zap.Int("TypeInstance count", len(payload.TypeInstances))) @@ -136,5 +124,5 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m } func (u *Upload) uploadTypeInstances(ctx context.Context, in *graphqllocal.CreateTypeInstancesInput) ([]graphqllocal.CreateTypeInstanceOutput, error) { - return u.localClient.CreateTypeInstances(ctx, in) + return u.client.Local.CreateTypeInstances(ctx, in) } diff --git a/pkg/sdk/validation/typeinstance.go b/pkg/sdk/validation/typeinstance.go index 4f0dce51a..7eec6ff5a 100644 --- a/pkg/sdk/validation/typeinstance.go +++ b/pkg/sdk/validation/typeinstance.go @@ -3,84 +3,141 @@ package validation import ( "context" "encoding/json" - "strings" + "fmt" + graphqllocal "capact.io/capact/pkg/hub/api/graphql/local" "capact.io/capact/pkg/sdk/apis/0.0.1/types" "github.com/pkg/errors" "github.com/xeipuuv/gojsonschema" ) -// TypeInstanceValidation gather the necessary data to validate TypeInstance. -type TypeInstanceValidation struct { - Alias *string - TypeRef types.TypeRef - Value interface{} +type typeInstanceData struct { + alias *string + id string + value interface{} + typeRefWithRevision string } -// ValidateTI is responsible for validating the TypeInstance. -func ValidateTI(ctx context.Context, ti *TypeInstanceValidation, hub HubClient) (Result, error) { - if ti == nil { - return Result{}, nil +// TypeInstanceValidationHubClient defines Hub methods needed for validation of TypeInstances. +type TypeInstanceValidationHubClient interface { + HubClient + FindTypeInstancesTypeRef(ctx context.Context, ids []string) (map[string]graphqllocal.TypeInstanceTypeReference, error) +} + +// ValidateTypeInstancesToCreate is responsible for validating TypeInstance which do not exist and will be created. +func ValidateTypeInstancesToCreate(ctx context.Context, client TypeInstanceValidationHubClient, typeInstance *graphqllocal.CreateTypeInstancesInput) (Result, error) { + var typeInstanceCollection []typeInstanceData + typeRefCollection := TypeRefCollection{} + + for _, ti := range typeInstance.TypeInstances { + if ti == nil { + continue + } + typeRef := types.TypeRef{ + Path: ti.TypeRef.Path, + Revision: ti.TypeRef.Revision, + } + name := getManifestPathWithRevision(ti.TypeRef.Path, ti.TypeRef.Revision) + typeRefCollection[name] = TypeRef{ + TypeRef: typeRef, + } + typeInstanceCollection = append(typeInstanceCollection, typeInstanceData{ + alias: ti.Alias, + value: ti.Value, + typeRefWithRevision: name, + }) } - if _, ok := ti.Value.(map[string]interface{}); !ok { - return Result{}, nil + schemasCollection, err := ResolveTypeRefsToJSONSchemas(ctx, client, typeRefCollection) + if err != nil { + return nil, errors.Wrapf(err, "while resolving TypeRefs to JSON Schemas") } + return validateTypeInstances(schemasCollection, typeInstanceCollection) +} - resultBldr := NewResultBuilder("TypeInstance value") +// ValidateTypeInstanceToUpdate is responsible for validating TypeInstance which exists and will be updated. +func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValidationHubClient, typeInstanceToUpdate []graphqllocal.UpdateTypeInstancesInput) (Result, error) { + var typeInstanceIds []string + idToTypeNameMap := map[string]string{} + for _, ti := range typeInstanceToUpdate { + typeInstanceIds = append(typeInstanceIds, ti.ID) + } - typeName := getTypeNameFromPath(ti.TypeRef.Path) - typeRevision, err := ResolveTypeRefsToJSONSchemas(ctx, hub, TypeRefCollection{ - typeName: TypeRef{ - TypeRef: ti.TypeRef, - }, - }) + typeInstancesTypeRef, err := client.FindTypeInstancesTypeRef(ctx, typeInstanceIds) if err != nil { - return Result{}, errors.Wrap(err, "while resolving TypeRefs to JSON Schemas") + return nil, errors.Wrapf(err, "while finding TypeInstance Type reference") } - valuesJSON, err := convertTypeInstanceValueToJSONBytes(ti.Value) - if err != nil { - return Result{}, errors.Wrap(err, "while converting TypeInstance value to JSON bytes") + typeRefCollection := TypeRefCollection{} + for id, typeReference := range typeInstancesTypeRef { + name := getManifestPathWithRevision(typeReference.Path, typeReference.Revision) + typeRefCollection[name] = TypeRef{ + TypeRef: types.TypeRef{ + Path: typeReference.Path, + Revision: typeReference.Revision, + }, + } + idToTypeNameMap[id] = name } - schemaLoader := gojsonschema.NewStringLoader(typeRevision[typeName].Value) - dataLoader := gojsonschema.NewBytesLoader(valuesJSON) + var typeInstanceCollection []typeInstanceData + for _, ti := range typeInstanceToUpdate { + if ti.TypeInstance == nil { + continue + } + typeInstanceCollection = append(typeInstanceCollection, typeInstanceData{ + id: ti.ID, + value: ti.TypeInstance.Value, + typeRefWithRevision: idToTypeNameMap[ti.ID], + }) + } - result, err := gojsonschema.Validate(schemaLoader, dataLoader) + schemasCollection, err := ResolveTypeRefsToJSONSchemas(ctx, client, typeRefCollection) if err != nil { - return nil, err - } - if !result.Valid() { - for _, err := range result.Errors() { - name := "" - if ti.Alias != nil { - name = *ti.Alias - } - resultBldr.ReportIssue(name, err.String()) - } + return nil, errors.Wrapf(err, "while resolving TypeRefs to JSON Schemas") } - return resultBldr.Result(), nil + return validateTypeInstances(schemasCollection, typeInstanceCollection) } -func convertTypeInstanceValueToJSONBytes(values interface{}) ([]byte, error) { - parameters := make(map[string]json.RawMessage) - valueMap := values.(map[string]interface{}) +func validateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollection []typeInstanceData) (Result, error) { + resultBldr := NewResultBuilder("Validation TypeInstances") - for name := range valueMap { - value := valueMap[name] - valueData, err := json.Marshal(&value) + for _, ti := range typeInstanceCollection { + if _, ok := ti.value.(map[string]interface{}); !ok { + return Result{}, errors.New("could not create map from TypeInstance Value") + } + valuesJSON, err := json.Marshal(ti.value) if err != nil { - return nil, errors.Wrapf(err, "while marshaling %s parameter to JSON", name) + return Result{}, errors.Wrap(err, "while converting TypeInstance value to JSON bytes") + } + if _, ok := schemaCollection[ti.typeRefWithRevision]; !ok { + return Result{}, fmt.Errorf("could not find Schema for type %s", ti.typeRefWithRevision) } - parameters[name] = valueData + schemaLoader := gojsonschema.NewStringLoader(schemaCollection[ti.typeRefWithRevision].Value) + dataLoader := gojsonschema.NewBytesLoader(valuesJSON) + + result, err := gojsonschema.Validate(schemaLoader, dataLoader) + if err != nil { + return nil, errors.Wrap(err, "while validating JSON schema for TypeInstance") + } + if !result.Valid() { + for _, err := range result.Errors() { + msg := "" + if ti.alias != nil { + msg = fmt.Sprintf("TypeInstance with alias %s", *ti.alias) + } else if ti.id != "" { + msg = fmt.Sprintf("TypeInstance with id %s", ti.id) + } + resultBldr.ReportIssue(msg, err.String()) + } + } } - return json.Marshal(parameters) + + return resultBldr.Result(), nil } -func getTypeNameFromPath(path string) string { - parts := strings.Split(path, ".") - return parts[len(parts)-1] +func getManifestPathWithRevision(path string, revision string) string { + return path + ":" + revision } diff --git a/pkg/sdk/validation/typeinstance_test.go b/pkg/sdk/validation/typeinstance_test.go index 93af6e2f2..4a852b13a 100644 --- a/pkg/sdk/validation/typeinstance_test.go +++ b/pkg/sdk/validation/typeinstance_test.go @@ -1,59 +1,69 @@ -package validation_test +package validation import ( - "context" "fmt" "testing" - gqlpublicapi "capact.io/capact/pkg/hub/api/graphql/public" - "capact.io/capact/pkg/sdk/apis/0.0.1/types" - "capact.io/capact/pkg/sdk/validation" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestValidateTI(t *testing.T) { +func TestValidateTypeInstances(t *testing.T) { tests := map[string]struct { - types []*gqlpublicapi.Type - typeInstance validation.TypeInstanceValidation - expError error + schemaCollection SchemaCollection + typeInstanceCollection []typeInstanceData + expError error }{ "When TypeInstance values do not contain the required property": { - types: []*gqlpublicapi.Type{validation.AWSCredsTypeRevFixture()}, - typeInstance: validation.TypeInstanceValidation{ - TypeRef: types.TypeRef{ - Path: "cap.type.aws.auth.creds", - Revision: "0.1.0", + schemaCollection: SchemaCollection{ + "cap.type.aws.auth.creds:0.1.0": { + Value: fmt.Sprintf("%v", AWSCredsTypeRevFixture().Revisions[0].Spec.JSONSchema), + Required: false, }, - Value: map[string]interface{}{ - "test1": "test", - "test2": "test", + }, + typeInstanceCollection: []typeInstanceData{ + { + typeRefWithRevision: "cap.type.aws.auth.creds:0.1.0", + value: map[string]interface{}{ + "test1": "test", + "test2": "test", + }, + alias: pointerToAlias("aws-creds"), }, }, - expError: fmt.Errorf("%s", "- TypeInstance value \"\":\n * (root): key is required"), + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance with alias aws-creds\":\n * (root): key is required"), }, "When TypeInstance value does not meet Type property constraints": { - types: []*gqlpublicapi.Type{validation.AWSElasticsearchTypeRevFixture()}, - typeInstance: validation.TypeInstanceValidation{ - TypeRef: types.TypeRef{ - Path: "cap.type.aws.elasticsearch.install-input", - Revision: "0.1.0", + schemaCollection: SchemaCollection{ + "cap.type.aws.elasticsearch.install-input:0.1.0": { + Value: fmt.Sprintf("%v", AWSElasticsearchTypeRevFixture().Revisions[0].Spec.JSONSchema), + Required: false, }, - Value: map[string]interface{}{ - "replicas": 5, + }, + typeInstanceCollection: []typeInstanceData{ + { + typeRefWithRevision: "cap.type.aws.elasticsearch.install-input:0.1.0", + value: map[string]interface{}{ + "replicas": 5, + }, + id: "5605af48-c34f-4bdc-b2d8-53c679bdfa5a", }, }, - expError: fmt.Errorf("%s", "- TypeInstance value \"\":\n * replicas: Invalid type. Expected: string, given: integer"), + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance with id 5605af48-c34f-4bdc-b2d8-53c679bdfa5a\":\n * replicas: Invalid type. Expected: string, given: integer"), }, "When TypeInstance contain the required property": { - types: []*gqlpublicapi.Type{validation.AWSCredsTypeRevFixture()}, - typeInstance: validation.TypeInstanceValidation{ - TypeRef: types.TypeRef{ - Path: "cap.type.aws.auth.creds", - Revision: "0.1.0", + schemaCollection: SchemaCollection{ + "cap.type.aws.auth.creds:0.1.0": { + Value: fmt.Sprintf("%v", AWSCredsTypeRevFixture().Revisions[0].Spec.JSONSchema), + Required: false, }, - Value: map[string]interface{}{ - "key": "aaa", + }, + typeInstanceCollection: []typeInstanceData{ + { + typeRefWithRevision: "cap.type.aws.auth.creds:0.1.0", + value: map[string]interface{}{ + "key": "aaa", + }, }, }, expError: nil, @@ -62,13 +72,8 @@ func TestValidateTI(t *testing.T) { for tn, tc := range tests { t.Run(tn, func(t *testing.T) { - // given - hubCli := validation.FakeHubCli{ - Types: tc.types, - } - // when - validationResults, err := validation.ValidateTI(context.Background(), &tc.typeInstance, &hubCli) + validationResults, err := validateTypeInstances(tc.schemaCollection, tc.typeInstanceCollection) // then require.NoError(t, err) @@ -76,3 +81,7 @@ func TestValidateTI(t *testing.T) { }) } } + +func pointerToAlias(alias string) *string { + return &alias +} diff --git a/test/e2e/action_test.go b/test/e2e/action_test.go index 0ad88d8d9..52b76f184 100644 --- a/test/e2e/action_test.go +++ b/test/e2e/action_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package e2e From f7d2711ac20a8ba38d667a28d063b41ad817fb4f Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 10 Feb 2022 14:29:45 +0100 Subject: [PATCH 3/7] Fix e2e-tests --- pkg/sdk/validation/typeinstance.go | 10 +++++----- pkg/sdk/validation/typeinstance_test.go | 8 ++++---- test/e2e/hub_test.go | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pkg/sdk/validation/typeinstance.go b/pkg/sdk/validation/typeinstance.go index 7eec6ff5a..ffadafa65 100644 --- a/pkg/sdk/validation/typeinstance.go +++ b/pkg/sdk/validation/typeinstance.go @@ -26,7 +26,7 @@ type TypeInstanceValidationHubClient interface { // ValidateTypeInstancesToCreate is responsible for validating TypeInstance which do not exist and will be created. func ValidateTypeInstancesToCreate(ctx context.Context, client TypeInstanceValidationHubClient, typeInstance *graphqllocal.CreateTypeInstancesInput) (Result, error) { - var typeInstanceCollection []typeInstanceData + var typeInstanceCollection []*typeInstanceData typeRefCollection := TypeRefCollection{} for _, ti := range typeInstance.TypeInstances { @@ -41,7 +41,7 @@ func ValidateTypeInstancesToCreate(ctx context.Context, client TypeInstanceValid typeRefCollection[name] = TypeRef{ TypeRef: typeRef, } - typeInstanceCollection = append(typeInstanceCollection, typeInstanceData{ + typeInstanceCollection = append(typeInstanceCollection, &typeInstanceData{ alias: ti.Alias, value: ti.Value, typeRefWithRevision: name, @@ -80,12 +80,12 @@ func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValida idToTypeNameMap[id] = name } - var typeInstanceCollection []typeInstanceData + var typeInstanceCollection []*typeInstanceData for _, ti := range typeInstanceToUpdate { if ti.TypeInstance == nil { continue } - typeInstanceCollection = append(typeInstanceCollection, typeInstanceData{ + typeInstanceCollection = append(typeInstanceCollection, &typeInstanceData{ id: ti.ID, value: ti.TypeInstance.Value, typeRefWithRevision: idToTypeNameMap[ti.ID], @@ -100,7 +100,7 @@ func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValida return validateTypeInstances(schemasCollection, typeInstanceCollection) } -func validateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollection []typeInstanceData) (Result, error) { +func validateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollection []*typeInstanceData) (Result, error) { resultBldr := NewResultBuilder("Validation TypeInstances") for _, ti := range typeInstanceCollection { diff --git a/pkg/sdk/validation/typeinstance_test.go b/pkg/sdk/validation/typeinstance_test.go index 4a852b13a..084e3ff61 100644 --- a/pkg/sdk/validation/typeinstance_test.go +++ b/pkg/sdk/validation/typeinstance_test.go @@ -11,7 +11,7 @@ import ( func TestValidateTypeInstances(t *testing.T) { tests := map[string]struct { schemaCollection SchemaCollection - typeInstanceCollection []typeInstanceData + typeInstanceCollection []*typeInstanceData expError error }{ "When TypeInstance values do not contain the required property": { @@ -21,7 +21,7 @@ func TestValidateTypeInstances(t *testing.T) { Required: false, }, }, - typeInstanceCollection: []typeInstanceData{ + typeInstanceCollection: []*typeInstanceData{ { typeRefWithRevision: "cap.type.aws.auth.creds:0.1.0", value: map[string]interface{}{ @@ -40,7 +40,7 @@ func TestValidateTypeInstances(t *testing.T) { Required: false, }, }, - typeInstanceCollection: []typeInstanceData{ + typeInstanceCollection: []*typeInstanceData{ { typeRefWithRevision: "cap.type.aws.elasticsearch.install-input:0.1.0", value: map[string]interface{}{ @@ -58,7 +58,7 @@ func TestValidateTypeInstances(t *testing.T) { Required: false, }, }, - typeInstanceCollection: []typeInstanceData{ + typeInstanceCollection: []*typeInstanceData{ { typeRefWithRevision: "cap.type.aws.auth.creds:0.1.0", value: map[string]interface{}{ diff --git a/test/e2e/hub_test.go b/test/e2e/hub_test.go index e2061ca6e..f75ddca5d 100644 --- a/test/e2e/hub_test.go +++ b/test/e2e/hub_test.go @@ -250,7 +250,7 @@ var _ = Describe("GraphQL API", func() { // create TypeInstance createdTypeInstance, err := cli.CreateTypeInstance(ctx, &gqllocalapi.CreateTypeInstanceInput{ TypeRef: &gqllocalapi.TypeInstanceTypeReferenceInput{ - Path: "cap.type.capactio.capact.ti", + Path: "cap.type.capactio.capact.validation.single-key", Revision: "0.1.0", }, Attributes: []*gqllocalapi.AttributeReferenceInput{ @@ -260,7 +260,7 @@ var _ = Describe("GraphQL API", func() { }, }, Value: map[string]interface{}{ - "foo": "bar", + "key": "bar", }, }) Expect(err).ToNot(HaveOccurred()) @@ -281,14 +281,14 @@ var _ = Describe("GraphQL API", func() { }, Spec: &gqllocalapi.TypeInstanceResourceVersionSpec{ Value: map[string]interface{}{ - "foo": "bar", + "key": "bar", }, }, } Expect(typeInstance).To(Equal(&gqllocalapi.TypeInstance{ ID: createdTypeInstance.ID, TypeRef: &gqllocalapi.TypeInstanceTypeReference{ - Path: "cap.type.capactio.capact.ti", + Path: "cap.type.capactio.capact.validation.single-key", Revision: "0.1.0", }, Uses: []*gqllocalapi.TypeInstance{}, @@ -639,7 +639,7 @@ func includes(ids []string, expID string) bool { func typeInstance(ver string) *gqllocalapi.CreateTypeInstanceInput { return &gqllocalapi.CreateTypeInstanceInput{ TypeRef: &gqllocalapi.TypeInstanceTypeReferenceInput{ - Path: "cap.type.sample-v" + ver, + Path: "cap.type.capactio.capact.validation.single-key", Revision: "0.1.0", }, Attributes: []*gqllocalapi.AttributeReferenceInput{ @@ -649,7 +649,7 @@ func typeInstance(ver string) *gqllocalapi.CreateTypeInstanceInput { }, }, Value: map[string]interface{}{ - "sample-v" + ver: true, + "key": "sample-v" + ver, }, } } From 354571f7f15ac857070945b7d03823a34d8311a3 Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Tue, 15 Feb 2022 15:59:52 +0100 Subject: [PATCH 4/7] Correction after review --- cmd/argo-actions/README.md | 1 + cmd/argo-actions/main.go | 9 +- cmd/k8s-engine/main.go | 5 +- .../charts/engine/templates/deployment.yaml | 4 + pkg/argo-actions/download_type_instances.go | 6 +- pkg/sdk/apis/0.0.1/types/types.extend.go | 8 ++ .../renderer/argo/dedicated_renderer_test.go | 2 +- pkg/sdk/renderer/argo/renderer_test.go | 8 +- ...rmost_workflow_with_user_input.golden.yaml | 4 + ...ted_PostgreSQL_change_password.golden.yaml | 8 ++ .../PostgreSQL_change_password.golden.yaml | 8 ++ ...nput_and_without_TypeInstances.golden.yaml | 4 + ...k_installation_with_user_input.golden.yaml | 4 + ...tallation_with_GCP_SA_injected.golden.yaml | 8 ++ ...attermost_with_AWS_RDS_install.golden.yaml | 8 ++ ...tallation_with_GCP_SA_injected.golden.yaml | 8 ++ ..._with_CloudSQL_using_Terraform.golden.yaml | 8 ++ ..._with_existing_DB_installation.golden.yaml | 12 +++ ...additional_parameters_injected.golden.yaml | 8 ++ ...back_to_Bitnami_Implementation.golden.yaml | 4 + ...put_-_reference_by_ManifestRef.golden.yaml | 8 ++ ...nal_input_-_reference_by_alias.golden.yaml | 8 ++ pkg/sdk/renderer/argo/typeinstance_handler.go | 36 ++++++- pkg/sdk/validation/typeinstance.go | 102 +++++++++--------- pkg/sdk/validation/typeinstance_test.go | 76 +++++++++---- 25 files changed, 271 insertions(+), 86 deletions(-) diff --git a/cmd/argo-actions/README.md b/cmd/argo-actions/README.md index 9efc6f218..2a64bf578 100644 --- a/cmd/argo-actions/README.md +++ b/cmd/argo-actions/README.md @@ -35,6 +35,7 @@ The following environment variables can be set: |--------------------------|----------|-------------------------------------------------|--------------------------------------------------------| | APP_ACTION | yes | | Defines action to perform | | APP_LOCAL_HUB_ENDPOINT | no | http://capact-hub-local.capact-system/graphql | Defines local Hub Endpoint | +| APP_PUBLIC_HUB_ENDPOINT | no | http://capact-hub-public.capact-system/graphql | Defines public Hub Endpoint | | APP_DOWNLOAD_CONFIG | no | | For download action defines Type Instances to download | | APP_LOGGER_DEV_MODE | no | `false` | Enable additional log messages | diff --git a/cmd/argo-actions/main.go b/cmd/argo-actions/main.go index c441e44a8..bf0eab4bf 100644 --- a/cmd/argo-actions/main.go +++ b/cmd/argo-actions/main.go @@ -40,19 +40,16 @@ func main() { logger, err := logger.New(cfg.Logger) exitOnError(err, "while creating zap logger") - localClient := local.NewDefaultClient(cfg.LocalHubEndpoint) - publicClient := public.NewDefaultClient(cfg.PublicHubEndpoint) - // TODO: Consider using connection `hubclient.New` and route requests through Gateway client := hubclient.Client{ - Local: localClient, - Public: publicClient, + Local: local.NewDefaultClient(cfg.LocalHubEndpoint), + Public: public.NewDefaultClient(cfg.PublicHubEndpoint), } switch cfg.Action { case argoactions.DownloadAction: log := logger.With(zap.String("Action", argoactions.DownloadAction)) - action = argoactions.NewDownloadAction(log, localClient, cfg.DownloadConfig) + action = argoactions.NewDownloadAction(log, &client, cfg.DownloadConfig) case argoactions.UploadAction: log := logger.With(zap.String("Action", argoactions.UploadAction)) diff --git a/cmd/k8s-engine/main.go b/cmd/k8s-engine/main.go index a4fc02591..2c0deedb2 100644 --- a/cmd/k8s-engine/main.go +++ b/cmd/k8s-engine/main.go @@ -63,6 +63,9 @@ type Config struct { Logger logger.Config + LocalHubEndpoint string `envconfig:"default=http://capact-hub-local.capact-system/graphql"` + PublicHubEndpoint string `envconfig:"default=http://capact-hub-public.capact-system/graphql"` + GraphQLGateway struct { Endpoint string `envconfig:"default=http://capact-gateway/graphql"` Username string @@ -107,7 +110,7 @@ func main() { exitOnError(err, "while creating manager") hubClient := getHubClient(&cfg) - typeInstanceHandler := argo.NewTypeInstanceHandler(cfg.HubActionsImage) + typeInstanceHandler := argo.NewTypeInstanceHandler(cfg.HubActionsImage, cfg.LocalHubEndpoint, cfg.PublicHubEndpoint) interfaceIOValidator := actionvalidation.NewValidator(hubClient) policyIOValidator := policyvalidation.NewValidator(hubClient) wfValidator := renderer.NewWorkflowInputValidator(interfaceIOValidator, policyIOValidator) diff --git a/deploy/kubernetes/charts/capact/charts/engine/templates/deployment.yaml b/deploy/kubernetes/charts/capact/charts/engine/templates/deployment.yaml index bf642f528..a2080c910 100644 --- a/deploy/kubernetes/charts/capact/charts/engine/templates/deployment.yaml +++ b/deploy/kubernetes/charts/capact/charts/engine/templates/deployment.yaml @@ -48,6 +48,10 @@ spec: value: "{{ .Values.global.gateway.auth.username }}" - name: APP_GRAPHQLGATEWAY_PASSWORD value: "{{ .Values.global.gateway.auth.password }}" + - name: APP_LOCAL_HUB_ENDPOINT + value: "http://capact-hub-local.{{.Release.Namespace}}.svc.cluster.local/graphql" + - name: APP_PUBLIC_HUB_ENDPOINT + value: "http://capact-hub-public.{{.Release.Namespace}}.svc.cluster.local/graphql" - name: APP_BUILTIN_RUNNER_IMAGE value: "{{ .Values.global.containerRegistry.path }}/{{ .Values.builtInRunner.image.name }}:{{ .Values.global.containerRegistry.overrideTag | default .Chart.AppVersion }}" - name: APP_BUILTIN_RUNNER_TIMEOUT diff --git a/pkg/argo-actions/download_type_instances.go b/pkg/argo-actions/download_type_instances.go index 947e97844..7ef5c0c7b 100644 --- a/pkg/argo-actions/download_type_instances.go +++ b/pkg/argo-actions/download_type_instances.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "capact.io/capact/pkg/hub/client/local" + hubclient "capact.io/capact/pkg/hub/client" "capact.io/capact/pkg/runner" "github.com/pkg/errors" "go.uber.org/zap" @@ -25,11 +25,11 @@ type DownloadConfig struct { type Download struct { log *zap.Logger cfg []DownloadConfig - client *local.Client + client *hubclient.Client } // NewDownloadAction returns a new Download instance. -func NewDownloadAction(log *zap.Logger, client *local.Client, cfg []DownloadConfig) Action { +func NewDownloadAction(log *zap.Logger, client *hubclient.Client, cfg []DownloadConfig) Action { return &Download{ log: log, cfg: cfg, diff --git a/pkg/sdk/apis/0.0.1/types/types.extend.go b/pkg/sdk/apis/0.0.1/types/types.extend.go index cabcabc5d..06c1cf1d1 100644 --- a/pkg/sdk/apis/0.0.1/types/types.extend.go +++ b/pkg/sdk/apis/0.0.1/types/types.extend.go @@ -1,6 +1,10 @@ // Package types holds manually added types. package types +import ( + "fmt" +) + // OCFPathPrefix defines path prefix that all OCF manifest must have. const OCFPathPrefix = "cap." @@ -20,6 +24,10 @@ type ManifestRef struct { Revision string `json:"revision"` // Version of the manifest content in the SemVer format. } +func (in *ManifestRef) String() string { + return fmt.Sprintf("%s:%s", in.Path, in.Revision) +} + // ManifestRefWithOptRevision specifies type by path and optional revision. // +kubebuilder:object:generate=true type ManifestRefWithOptRevision struct { diff --git a/pkg/sdk/renderer/argo/dedicated_renderer_test.go b/pkg/sdk/renderer/argo/dedicated_renderer_test.go index cc0278687..3f3f3298d 100644 --- a/pkg/sdk/renderer/argo/dedicated_renderer_test.go +++ b/pkg/sdk/renderer/argo/dedicated_renderer_test.go @@ -14,7 +14,7 @@ func createFakeDedicatedRendererObject(t *testing.T) *dedicatedRenderer { require.NoError(t, err) genUUID := func() string { return "uuid" } - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7") + typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "", "") typeInstanceHandler.SetGenUUID(genUUID) policyIOValidator := policyvalidation.NewValidator(fakeCli) diff --git a/pkg/sdk/renderer/argo/renderer_test.go b/pkg/sdk/renderer/argo/renderer_test.go index 7ec1a71f7..22fa68501 100644 --- a/pkg/sdk/renderer/argo/renderer_test.go +++ b/pkg/sdk/renderer/argo/renderer_test.go @@ -36,7 +36,7 @@ func TestRenderHappyPath(t *testing.T) { policy := policy.NewAllowAll() genUUID := func() string { return "uuid" } // it has to be static because of parallel testing - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7") + typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") typeInstanceHandler.SetGenUUID(genUUID) ownerID := "default/action" @@ -294,7 +294,7 @@ func TestRenderHappyPathWithCustomPolicies(t *testing.T) { tt := test t.Run(tt.name, func(t *testing.T) { genUUID := genUUIDFn(strconv.Itoa(tc)) - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7") + typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") typeInstanceHandler.SetGenUUID(genUUID) interfaceIOValidator := actionvalidation.NewValidator(fakeCli) @@ -341,7 +341,7 @@ func TestRendererMaxDepth(t *testing.T) { require.NoError(t, err) policy := policy.NewAllowAll() - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7") + typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") typeInstanceHandler.SetGenUUID(genUUIDFn("")) interfaceIOValidator := actionvalidation.NewValidator(fakeCli) @@ -380,7 +380,7 @@ func TestRendererDenyAllPolicy(t *testing.T) { require.NoError(t, err) policy := policy.NewDenyAll() - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7") + typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") typeInstanceHandler.SetGenUUID(genUUIDFn("")) interfaceIOValidator := actionvalidation.NewValidator(fakeCli) diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml index b02d2fa35..723c7a6f9 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml @@ -1238,6 +1238,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml index 1690087bc..bf7498229 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml @@ -290,6 +290,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{6fc7dd6b-d150-4af3-a1aa-a868962b7d68,/firstRole.yaml},{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -312,6 +316,10 @@ args: value: /update/payload - name: APP_UPDATE_CONFIG_TYPE_INSTANCES_DIR value: /update/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml index 6f458378f..19e712db2 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml @@ -271,6 +271,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{6fc7dd6b-d150-4af3-a1aa-a868962b7d68,/role.yaml},{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -293,6 +297,10 @@ args: value: /update/payload - name: APP_UPDATE_CONFIG_TYPE_INSTANCES_DIR value: /update/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml index 23cf8e558..6ef517f70 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml @@ -264,6 +264,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml index c0614e990..33fafc945 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml @@ -1216,6 +1216,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml index 813c232af..c0949af32 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml @@ -247,6 +247,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{c268d3f5-8834-434b-bea2-b677793611c5,/gcp-sa.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -295,6 +299,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml index 1472aff02..cfd5a6b18 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml @@ -860,6 +860,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -1568,6 +1572,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml index 5c81fd2df..6c378b8a5 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml @@ -583,6 +583,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{c268d3f5-8834-434b-bea2-b677793611c5,/gcp-sa.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -1269,6 +1273,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml index bdb2d3e97..88d3a7c1e 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml @@ -644,6 +644,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{c268d3f5-8834-434b-bea2-b677793611c5,/gcp-sa.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -1341,6 +1345,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml index 7b4dba4b7..5fadd77bc 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml @@ -1059,6 +1059,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -1105,6 +1109,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -1124,6 +1132,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml index 2519164eb..7b523a209 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml @@ -550,6 +550,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -598,6 +602,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml index 737351536..2f57142b4 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml @@ -264,6 +264,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml index e272701e6..24b5e9ab2 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml @@ -551,6 +551,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -995,6 +999,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml index 974d81526..d091110ce 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml @@ -551,6 +551,10 @@ args: value: DownloadAction - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 name: "" resources: {} @@ -995,6 +999,10 @@ args: value: /upload/payload - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances + - name: APP_LOCAL_HUB_ENDPOINT + value: 'http://capact-hub-local.capact-system/graphql ' + - name: APP_PUBLIC_HUB_ENDPOINT + value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 imagePullPolicy: IfNotPresent name: "" diff --git a/pkg/sdk/renderer/argo/typeinstance_handler.go b/pkg/sdk/renderer/argo/typeinstance_handler.go index 43644d8ad..1bda46ed1 100644 --- a/pkg/sdk/renderer/argo/typeinstance_handler.go +++ b/pkg/sdk/renderer/argo/typeinstance_handler.go @@ -17,14 +17,18 @@ import ( // TypeInstanceHandler provides functionality to handle TypeInstance operations such as // injecting download step and upload step. type TypeInstanceHandler struct { - hubActionsImage string - genUUID func() string + hubActionsImage string + localHubEndpoint string + publicHubEndpoint string + genUUID func() string } // NewTypeInstanceHandler returns a new TypeInstanceHandler instance. -func NewTypeInstanceHandler(hubActionsImage string) *TypeInstanceHandler { +func NewTypeInstanceHandler(hubActionsImage string, localHubEndpoint string, publicHubEndpoint string) *TypeInstanceHandler { return &TypeInstanceHandler{ - hubActionsImage: hubActionsImage, + hubActionsImage: hubActionsImage, + localHubEndpoint: localHubEndpoint, + publicHubEndpoint: publicHubEndpoint, genUUID: func() string { return uuid.New().String() }, @@ -70,6 +74,14 @@ func (r *TypeInstanceHandler) AddInputTypeInstances(rootWorkflow *Workflow, inst Name: "APP_DOWNLOAD_CONFIG", Value: strings.Join(typeInstanceToDownload, ","), }, + { + Name: "APP_LOCAL_HUB_ENDPOINT", + Value: r.localHubEndpoint, + }, + { + Name: "APP_PUBLIC_HUB_ENDPOINT", + Value: r.publicHubEndpoint, + }, }, }, Outputs: wfv1.Outputs{ @@ -197,6 +209,14 @@ func (r *TypeInstanceHandler) AddUploadTypeInstancesStep(rootWorkflow *Workflow, Name: "APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR", Value: "/upload/typeInstances", }, + { + Name: "APP_LOCAL_HUB_ENDPOINT", + Value: r.localHubEndpoint, + }, + { + Name: "APP_PUBLIC_HUB_ENDPOINT", + Value: r.publicHubEndpoint, + }, }, }, Inputs: wfv1.Inputs{ @@ -288,6 +308,14 @@ func (r *TypeInstanceHandler) AddUpdateTypeInstancesStep(rootWorkflow *Workflow, Name: "APP_UPDATE_CONFIG_TYPE_INSTANCES_DIR", Value: "/update/typeInstances", }, + { + Name: "APP_LOCAL_HUB_ENDPOINT", + Value: r.localHubEndpoint, + }, + { + Name: "APP_PUBLIC_HUB_ENDPOINT", + Value: r.publicHubEndpoint, + }, }, }, Inputs: wfv1.Inputs{ diff --git a/pkg/sdk/validation/typeinstance.go b/pkg/sdk/validation/typeinstance.go index ffadafa65..ef3599e6d 100644 --- a/pkg/sdk/validation/typeinstance.go +++ b/pkg/sdk/validation/typeinstance.go @@ -5,17 +5,30 @@ import ( "encoding/json" "fmt" + "capact.io/capact/internal/ptr" graphqllocal "capact.io/capact/pkg/hub/api/graphql/local" "capact.io/capact/pkg/sdk/apis/0.0.1/types" "github.com/pkg/errors" "github.com/xeipuuv/gojsonschema" ) -type typeInstanceData struct { - alias *string - id string - value interface{} - typeRefWithRevision string +// TypeInstanceEssentialData contains essential TypeInstance Data for validation purpose. +type TypeInstanceEssentialData struct { + Value interface{} + TypeRef types.ManifestRef + Alias *string + ID *string +} + +func (ti *TypeInstanceEssentialData) String() string { + return fmt.Sprintf("ID: %s,Alias: %s", ti.stringPtrToString(ti.ID), ti.stringPtrToString(ti.Alias)) +} + +func (ti *TypeInstanceEssentialData) stringPtrToString(p *string) string { + if p != nil { + return *p + } + return "" } // TypeInstanceValidationHubClient defines Hub methods needed for validation of TypeInstances. @@ -26,25 +39,24 @@ type TypeInstanceValidationHubClient interface { // ValidateTypeInstancesToCreate is responsible for validating TypeInstance which do not exist and will be created. func ValidateTypeInstancesToCreate(ctx context.Context, client TypeInstanceValidationHubClient, typeInstance *graphqllocal.CreateTypeInstancesInput) (Result, error) { - var typeInstanceCollection []*typeInstanceData + var typeInstanceCollection []*TypeInstanceEssentialData typeRefCollection := TypeRefCollection{} for _, ti := range typeInstance.TypeInstances { - if ti == nil { + if ti == nil || ti.TypeRef == nil { continue } - typeRef := types.TypeRef{ + manifestRef := types.ManifestRef{ Path: ti.TypeRef.Path, Revision: ti.TypeRef.Revision, } - name := getManifestPathWithRevision(ti.TypeRef.Path, ti.TypeRef.Revision) - typeRefCollection[name] = TypeRef{ - TypeRef: typeRef, + typeRefCollection[manifestRef.String()] = TypeRef{ + TypeRef: types.TypeRef(manifestRef), } - typeInstanceCollection = append(typeInstanceCollection, &typeInstanceData{ - alias: ti.Alias, - value: ti.Value, - typeRefWithRevision: name, + typeInstanceCollection = append(typeInstanceCollection, &TypeInstanceEssentialData{ + Alias: ti.Alias, + Value: ti.Value, + TypeRef: manifestRef, }) } @@ -52,13 +64,12 @@ func ValidateTypeInstancesToCreate(ctx context.Context, client TypeInstanceValid if err != nil { return nil, errors.Wrapf(err, "while resolving TypeRefs to JSON Schemas") } - return validateTypeInstances(schemasCollection, typeInstanceCollection) + return ValidateTypeInstances(schemasCollection, typeInstanceCollection) } -// ValidateTypeInstanceToUpdate is responsible for validating TypeInstance which exists and will be updated. +// ValidateTypeInstanceToUpdate is responsible for validating TypeInstance which exists and will be updated. func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValidationHubClient, typeInstanceToUpdate []graphqllocal.UpdateTypeInstancesInput) (Result, error) { var typeInstanceIds []string - idToTypeNameMap := map[string]string{} for _, ti := range typeInstanceToUpdate { typeInstanceIds = append(typeInstanceIds, ti.ID) } @@ -69,26 +80,29 @@ func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValida } typeRefCollection := TypeRefCollection{} - for id, typeReference := range typeInstancesTypeRef { - name := getManifestPathWithRevision(typeReference.Path, typeReference.Revision) - typeRefCollection[name] = TypeRef{ - TypeRef: types.TypeRef{ - Path: typeReference.Path, - Revision: typeReference.Revision, - }, + for _, typeReference := range typeInstancesTypeRef { + manifestRef := types.ManifestRef{ + Path: typeReference.Path, + Revision: typeReference.Revision, + } + typeRefCollection[manifestRef.String()] = TypeRef{ + TypeRef: types.TypeRef(manifestRef), } - idToTypeNameMap[id] = name } - var typeInstanceCollection []*typeInstanceData + var typeInstanceCollection []*TypeInstanceEssentialData for _, ti := range typeInstanceToUpdate { if ti.TypeInstance == nil { continue } - typeInstanceCollection = append(typeInstanceCollection, &typeInstanceData{ - id: ti.ID, - value: ti.TypeInstance.Value, - typeRefWithRevision: idToTypeNameMap[ti.ID], + typeRef, ok := typeInstancesTypeRef[ti.ID] + if !ok { + return nil, errors.Wrapf(err, "while finding TypeInstance Type reference for id %s", ti.ID) + } + typeInstanceCollection = append(typeInstanceCollection, &TypeInstanceEssentialData{ + ID: ptr.String(ti.ID), + Value: ti.TypeInstance.Value, + TypeRef: types.ManifestRef(typeRef), }) } @@ -97,25 +111,26 @@ func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValida return nil, errors.Wrapf(err, "while resolving TypeRefs to JSON Schemas") } - return validateTypeInstances(schemasCollection, typeInstanceCollection) + return ValidateTypeInstances(schemasCollection, typeInstanceCollection) } -func validateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollection []*typeInstanceData) (Result, error) { +//ValidateTypeInstances is responsible for validating TypeInstance. +func ValidateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollection []*TypeInstanceEssentialData) (Result, error) { resultBldr := NewResultBuilder("Validation TypeInstances") for _, ti := range typeInstanceCollection { - if _, ok := ti.value.(map[string]interface{}); !ok { + if _, ok := ti.Value.(map[string]interface{}); !ok { return Result{}, errors.New("could not create map from TypeInstance Value") } - valuesJSON, err := json.Marshal(ti.value) + valuesJSON, err := json.Marshal(ti.Value) if err != nil { return Result{}, errors.Wrap(err, "while converting TypeInstance value to JSON bytes") } - if _, ok := schemaCollection[ti.typeRefWithRevision]; !ok { - return Result{}, fmt.Errorf("could not find Schema for type %s", ti.typeRefWithRevision) + if _, ok := schemaCollection[ti.TypeRef.String()]; !ok { + return Result{}, fmt.Errorf("could not find Schema for type %s", ti.TypeRef.String()) } - schemaLoader := gojsonschema.NewStringLoader(schemaCollection[ti.typeRefWithRevision].Value) + schemaLoader := gojsonschema.NewStringLoader(schemaCollection[ti.TypeRef.String()].Value) dataLoader := gojsonschema.NewBytesLoader(valuesJSON) result, err := gojsonschema.Validate(schemaLoader, dataLoader) @@ -124,12 +139,7 @@ func validateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollec } if !result.Valid() { for _, err := range result.Errors() { - msg := "" - if ti.alias != nil { - msg = fmt.Sprintf("TypeInstance with alias %s", *ti.alias) - } else if ti.id != "" { - msg = fmt.Sprintf("TypeInstance with id %s", ti.id) - } + msg := fmt.Sprintf("TypeInstance(%s)", ti.String()) resultBldr.ReportIssue(msg, err.String()) } } @@ -137,7 +147,3 @@ func validateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollec return resultBldr.Result(), nil } - -func getManifestPathWithRevision(path string, revision string) string { - return path + ":" + revision -} diff --git a/pkg/sdk/validation/typeinstance_test.go b/pkg/sdk/validation/typeinstance_test.go index 084e3ff61..7e7511de1 100644 --- a/pkg/sdk/validation/typeinstance_test.go +++ b/pkg/sdk/validation/typeinstance_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + "capact.io/capact/internal/ptr" + "capact.io/capact/pkg/sdk/apis/0.0.1/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -11,7 +13,7 @@ import ( func TestValidateTypeInstances(t *testing.T) { tests := map[string]struct { schemaCollection SchemaCollection - typeInstanceCollection []*typeInstanceData + typeInstanceCollection []*TypeInstanceEssentialData expError error }{ "When TypeInstance values do not contain the required property": { @@ -21,17 +23,20 @@ func TestValidateTypeInstances(t *testing.T) { Required: false, }, }, - typeInstanceCollection: []*typeInstanceData{ + typeInstanceCollection: []*TypeInstanceEssentialData{ { - typeRefWithRevision: "cap.type.aws.auth.creds:0.1.0", - value: map[string]interface{}{ + TypeRef: types.ManifestRef{ + Path: "cap.type.aws.auth.creds", + Revision: "0.1.0", + }, + Value: map[string]interface{}{ "test1": "test", "test2": "test", }, - alias: pointerToAlias("aws-creds"), + Alias: ptr.String("aws-creds"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance with alias aws-creds\":\n * (root): key is required"), + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: ,Alias: aws-creds)\":\n * (root): key is required"), }, "When TypeInstance value does not meet Type property constraints": { schemaCollection: SchemaCollection{ @@ -40,16 +45,19 @@ func TestValidateTypeInstances(t *testing.T) { Required: false, }, }, - typeInstanceCollection: []*typeInstanceData{ + typeInstanceCollection: []*TypeInstanceEssentialData{ { - typeRefWithRevision: "cap.type.aws.elasticsearch.install-input:0.1.0", - value: map[string]interface{}{ + TypeRef: types.ManifestRef{ + Path: "cap.type.aws.elasticsearch.install-input", + Revision: "0.1.0", + }, + Value: map[string]interface{}{ "replicas": 5, }, - id: "5605af48-c34f-4bdc-b2d8-53c679bdfa5a", + ID: ptr.String("5605af48-c34f-4bdc-b2d8-53c679bdfa5a"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance with id 5605af48-c34f-4bdc-b2d8-53c679bdfa5a\":\n * replicas: Invalid type. Expected: string, given: integer"), + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: 5605af48-c34f-4bdc-b2d8-53c679bdfa5a,Alias: )\":\n * replicas: Invalid type. Expected: string, given: integer"), }, "When TypeInstance contain the required property": { schemaCollection: SchemaCollection{ @@ -58,22 +66,56 @@ func TestValidateTypeInstances(t *testing.T) { Required: false, }, }, - typeInstanceCollection: []*typeInstanceData{ + typeInstanceCollection: []*TypeInstanceEssentialData{ { - typeRefWithRevision: "cap.type.aws.auth.creds:0.1.0", - value: map[string]interface{}{ + TypeRef: types.ManifestRef{ + Path: "cap.type.aws.auth.creds", + Revision: "0.1.0", + }, + Value: map[string]interface{}{ "key": "aaa", }, }, }, expError: nil, }, + "When there is a collection of TypeInstance with an incorrect value": { + schemaCollection: SchemaCollection{ + "cap.type.aws.auth.creds:0.1.0": { + Value: fmt.Sprintf("%v", AWSCredsTypeRevFixture().Revisions[0].Spec.JSONSchema), + Required: false, + }, + }, + typeInstanceCollection: []*TypeInstanceEssentialData{ + { + TypeRef: types.ManifestRef{ + Path: "cap.type.aws.auth.creds", + Revision: "0.1.0", + }, + Value: map[string]interface{}{ + "test1": "test", + }, + Alias: ptr.String("aws-creds"), + }, + { + TypeRef: types.ManifestRef{ + Path: "cap.type.aws.auth.creds", + Revision: "0.1.0", + }, + Value: map[string]interface{}{ + "test2": "test", + }, + Alias: ptr.String("aws-creds-2"), + }, + }, + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: ,Alias: aws-creds)\":\n * (root): key is required\n- Validation TypeInstances \"TypeInstance(ID: ,Alias: aws-creds-2)\":\n * (root): key is required"), + }, } for tn, tc := range tests { t.Run(tn, func(t *testing.T) { // when - validationResults, err := validateTypeInstances(tc.schemaCollection, tc.typeInstanceCollection) + validationResults, err := ValidateTypeInstances(tc.schemaCollection, tc.typeInstanceCollection) // then require.NoError(t, err) @@ -81,7 +123,3 @@ func TestValidateTypeInstances(t *testing.T) { }) } } - -func pointerToAlias(alias string) *string { - return &alias -} From b0e310bf0ef3f74fcc31043b434fe0bae3245e9a Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 17 Feb 2022 12:01:02 +0100 Subject: [PATCH 5/7] Small refactoring --- pkg/sdk/renderer/argo/renderer_test.go | 14 ++++++++---- ...rmost_workflow_with_user_input.golden.yaml | 2 +- ...ted_PostgreSQL_change_password.golden.yaml | 4 ++-- .../PostgreSQL_change_password.golden.yaml | 4 ++-- ...nput_and_without_TypeInstances.golden.yaml | 2 +- ...k_installation_with_user_input.golden.yaml | 2 +- ...tallation_with_GCP_SA_injected.golden.yaml | 4 ++-- ...attermost_with_AWS_RDS_install.golden.yaml | 4 ++-- ...tallation_with_GCP_SA_injected.golden.yaml | 4 ++-- ..._with_CloudSQL_using_Terraform.golden.yaml | 4 ++-- ..._with_existing_DB_installation.golden.yaml | 6 ++--- ...additional_parameters_injected.golden.yaml | 4 ++-- ...back_to_Bitnami_Implementation.golden.yaml | 2 +- ...put_-_reference_by_ManifestRef.golden.yaml | 4 ++-- ...nal_input_-_reference_by_alias.golden.yaml | 4 ++-- pkg/sdk/validation/typeinstance.go | 22 +++++++++++-------- pkg/sdk/validation/typeinstance_test.go | 9 ++++---- 17 files changed, 52 insertions(+), 43 deletions(-) diff --git a/pkg/sdk/renderer/argo/renderer_test.go b/pkg/sdk/renderer/argo/renderer_test.go index 22fa68501..c0de63296 100644 --- a/pkg/sdk/renderer/argo/renderer_test.go +++ b/pkg/sdk/renderer/argo/renderer_test.go @@ -21,6 +21,12 @@ import ( "sigs.k8s.io/yaml" ) +const ( + hubActionsImage = "alpine:3.7" + localHubEndpoint = "http://capact-hub-local.capact-system/graphql" + publicHubEndpoint = "http://capact-hub-public.capact-system/graphql" +) + // TestRenderHappyPath tests that renderer generates valid Argo Workflows. // // This test is based on golden file. @@ -36,7 +42,7 @@ func TestRenderHappyPath(t *testing.T) { policy := policy.NewAllowAll() genUUID := func() string { return "uuid" } // it has to be static because of parallel testing - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") + typeInstanceHandler := NewTypeInstanceHandler(hubActionsImage, localHubEndpoint, publicHubEndpoint) typeInstanceHandler.SetGenUUID(genUUID) ownerID := "default/action" @@ -294,7 +300,7 @@ func TestRenderHappyPathWithCustomPolicies(t *testing.T) { tt := test t.Run(tt.name, func(t *testing.T) { genUUID := genUUIDFn(strconv.Itoa(tc)) - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") + typeInstanceHandler := NewTypeInstanceHandler(hubActionsImage, localHubEndpoint, publicHubEndpoint) typeInstanceHandler.SetGenUUID(genUUID) interfaceIOValidator := actionvalidation.NewValidator(fakeCli) @@ -341,7 +347,7 @@ func TestRendererMaxDepth(t *testing.T) { require.NoError(t, err) policy := policy.NewAllowAll() - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") + typeInstanceHandler := NewTypeInstanceHandler(hubActionsImage, localHubEndpoint, publicHubEndpoint) typeInstanceHandler.SetGenUUID(genUUIDFn("")) interfaceIOValidator := actionvalidation.NewValidator(fakeCli) @@ -380,7 +386,7 @@ func TestRendererDenyAllPolicy(t *testing.T) { require.NoError(t, err) policy := policy.NewDenyAll() - typeInstanceHandler := NewTypeInstanceHandler("alpine:3.7", "http://capact-hub-local.capact-system/graphql ", "http://capact-hub-public.capact-system/graphql") + typeInstanceHandler := NewTypeInstanceHandler(hubActionsImage, localHubEndpoint, publicHubEndpoint) typeInstanceHandler.SetGenUUID(genUUIDFn("")) interfaceIOValidator := actionvalidation.NewValidator(fakeCli) diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml index 723c7a6f9..8ebd82a82 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Mattermost_workflow_with_user_input.golden.yaml @@ -1239,7 +1239,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml index bf7498229..8ab9025c7 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Nested_PostgreSQL_change_password.golden.yaml @@ -291,7 +291,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{6fc7dd6b-d150-4af3-a1aa-a868962b7d68,/firstRole.yaml},{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -317,7 +317,7 @@ args: - name: APP_UPDATE_CONFIG_TYPE_INSTANCES_DIR value: /update/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml index 19e712db2..672eaf07b 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_change_password.golden.yaml @@ -272,7 +272,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{6fc7dd6b-d150-4af3-a1aa-a868962b7d68,/role.yaml},{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -298,7 +298,7 @@ args: - name: APP_UPDATE_CONFIG_TYPE_INSTANCES_DIR value: /update/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml index 6ef517f70..55b8723d5 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/PostgreSQL_workflow_with_user_input_and_without_TypeInstances.golden.yaml @@ -265,7 +265,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml index 33fafc945..19c53f739 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPath/Workflow_with_apps_stack_installation_with_user_input.golden.yaml @@ -1217,7 +1217,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml index c0949af32..de83d11da 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml @@ -248,7 +248,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{c268d3f5-8834-434b-bea2-b677793611c5,/gcp-sa.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -300,7 +300,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml index cfd5a6b18..4ad4d43da 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_AWS_RDS_install.golden.yaml @@ -861,7 +861,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -1573,7 +1573,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml index 6c378b8a5..b088182b6 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_PostgreSQL_installation_with_GCP_SA_injected.golden.yaml @@ -584,7 +584,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{c268d3f5-8834-434b-bea2-b677793611c5,/gcp-sa.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -1274,7 +1274,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml index 88d3a7c1e..b3ca22138 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_CloudSQL_using_Terraform.golden.yaml @@ -645,7 +645,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{c268d3f5-8834-434b-bea2-b677793611c5,/gcp-sa.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -1346,7 +1346,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml index 5fadd77bc..44097bd97 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Mattermost_with_existing_DB_installation.golden.yaml @@ -1060,7 +1060,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -1110,7 +1110,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{f2421415-b8a4-464b-be12-b617794411c5,/postgresql.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -1133,7 +1133,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml index 7b523a209..3592e4dff 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/RDS_installation_with_AWS_SA_and_additional_parameters_injected.golden.yaml @@ -551,7 +551,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -603,7 +603,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml index 2f57142b4..2c872c54c 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Unmet_policy_constraints_-_fallback_to_Bitnami_Implementation.golden.yaml @@ -265,7 +265,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml index 24b5e9ab2..bcf2dd83a 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_ManifestRef.golden.yaml @@ -552,7 +552,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -1000,7 +1000,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml index d091110ce..0727e379e 100644 --- a/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml +++ b/pkg/sdk/renderer/argo/testdata/TestRenderHappyPathWithCustomPolicies/Workflow_policy_injects_additional_input_-_reference_by_alias.golden.yaml @@ -552,7 +552,7 @@ args: - name: APP_DOWNLOAD_CONFIG value: '{517cf827-233c-4bf1-8fc9-48534424dd58,/aws-credentials.yaml}' - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 @@ -1000,7 +1000,7 @@ args: - name: APP_UPLOAD_CONFIG_TYPE_INSTANCES_DIR value: /upload/typeInstances - name: APP_LOCAL_HUB_ENDPOINT - value: 'http://capact-hub-local.capact-system/graphql ' + value: http://capact-hub-local.capact-system/graphql - name: APP_PUBLIC_HUB_ENDPOINT value: http://capact-hub-public.capact-system/graphql image: alpine:3.7 diff --git a/pkg/sdk/validation/typeinstance.go b/pkg/sdk/validation/typeinstance.go index ef3599e6d..a3d655683 100644 --- a/pkg/sdk/validation/typeinstance.go +++ b/pkg/sdk/validation/typeinstance.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strings" "capact.io/capact/internal/ptr" graphqllocal "capact.io/capact/pkg/hub/api/graphql/local" @@ -21,14 +22,17 @@ type TypeInstanceEssentialData struct { } func (ti *TypeInstanceEssentialData) String() string { - return fmt.Sprintf("ID: %s,Alias: %s", ti.stringPtrToString(ti.ID), ti.stringPtrToString(ti.Alias)) -} - -func (ti *TypeInstanceEssentialData) stringPtrToString(p *string) string { - if p != nil { - return *p + if ti == nil { + return "" + } + var tiMetadata []string + if ti.ID != nil { + tiMetadata = append(tiMetadata, fmt.Sprintf("ID: %s", *ti.ID)) + } + if ti.Alias != nil { + tiMetadata = append(tiMetadata, fmt.Sprintf("Alias: %s", *ti.Alias)) } - return "" + return strings.Join(tiMetadata, ", ") } // TypeInstanceValidationHubClient defines Hub methods needed for validation of TypeInstances. @@ -97,7 +101,7 @@ func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValida } typeRef, ok := typeInstancesTypeRef[ti.ID] if !ok { - return nil, errors.Wrapf(err, "while finding TypeInstance Type reference for id %s", ti.ID) + return nil, errors.Wrapf(err, "while finding TypeInstance Type reference for id %q", ti.ID) } typeInstanceCollection = append(typeInstanceCollection, &TypeInstanceEssentialData{ ID: ptr.String(ti.ID), @@ -127,7 +131,7 @@ func ValidateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollec return Result{}, errors.Wrap(err, "while converting TypeInstance value to JSON bytes") } if _, ok := schemaCollection[ti.TypeRef.String()]; !ok { - return Result{}, fmt.Errorf("could not find Schema for type %s", ti.TypeRef.String()) + return Result{}, fmt.Errorf("could not find Schema for type %q", ti.TypeRef.String()) } schemaLoader := gojsonschema.NewStringLoader(schemaCollection[ti.TypeRef.String()].Value) diff --git a/pkg/sdk/validation/typeinstance_test.go b/pkg/sdk/validation/typeinstance_test.go index 7e7511de1..25404948f 100644 --- a/pkg/sdk/validation/typeinstance_test.go +++ b/pkg/sdk/validation/typeinstance_test.go @@ -31,12 +31,11 @@ func TestValidateTypeInstances(t *testing.T) { }, Value: map[string]interface{}{ "test1": "test", - "test2": "test", }, Alias: ptr.String("aws-creds"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: ,Alias: aws-creds)\":\n * (root): key is required"), + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(Alias: aws-creds)\":\n * (root): key is required"), }, "When TypeInstance value does not meet Type property constraints": { schemaCollection: SchemaCollection{ @@ -57,9 +56,9 @@ func TestValidateTypeInstances(t *testing.T) { ID: ptr.String("5605af48-c34f-4bdc-b2d8-53c679bdfa5a"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: 5605af48-c34f-4bdc-b2d8-53c679bdfa5a,Alias: )\":\n * replicas: Invalid type. Expected: string, given: integer"), + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: 5605af48-c34f-4bdc-b2d8-53c679bdfa5a)\":\n * replicas: Invalid type. Expected: string, given: integer"), }, - "When TypeInstance contain the required property": { + "When TypeInstance contains the required property": { schemaCollection: SchemaCollection{ "cap.type.aws.auth.creds:0.1.0": { Value: fmt.Sprintf("%v", AWSCredsTypeRevFixture().Revisions[0].Spec.JSONSchema), @@ -108,7 +107,7 @@ func TestValidateTypeInstances(t *testing.T) { Alias: ptr.String("aws-creds-2"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: ,Alias: aws-creds)\":\n * (root): key is required\n- Validation TypeInstances \"TypeInstance(ID: ,Alias: aws-creds-2)\":\n * (root): key is required"), + expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(Alias: aws-creds)\":\n * (root): key is required\n- Validation TypeInstances \"TypeInstance(Alias: aws-creds-2)\":\n * (root): key is required"), }, } From 1602c3e9889df6e8041d9bb1d5e499966c36972d Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Thu, 17 Feb 2022 20:16:30 +0100 Subject: [PATCH 6/7] Fix documentation error --- .mlc.config.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mlc.config.json b/.mlc.config.json index b0a5641df..50448da0d 100644 --- a/.mlc.config.json +++ b/.mlc.config.json @@ -14,6 +14,9 @@ }, { "pattern": "^http://capact-hub-local.capact-system/graphql$" + }, + { + "pattern": "^http://capact-hub-public.capact-system/graphql$" } ], "timeout": "20s", From 652e1c0e3eaf4c72367cedad4d2a527f2bf34e0c Mon Sep 17 00:00:00 2001 From: Mateusz Kuziemko Date: Fri, 18 Feb 2022 10:35:14 +0100 Subject: [PATCH 7/7] Improvements for IssueBuilder --- cmd/cli/cmd/typeinstance/create.go | 7 +++--- cmd/cli/cmd/typeinstance/edit.go | 7 +++--- pkg/argo-actions/update_type_instances.go | 7 +++--- pkg/argo-actions/upload_type_instances.go | 7 +++--- pkg/sdk/validation/issue_builder.go | 17 ++++++++++----- pkg/sdk/validation/typeinstance.go | 26 ++++++++++++----------- pkg/sdk/validation/typeinstance_test.go | 7 +++--- 7 files changed, 46 insertions(+), 32 deletions(-) diff --git a/cmd/cli/cmd/typeinstance/create.go b/cmd/cli/cmd/typeinstance/create.go index 35ab9a40d..f97eef567 100644 --- a/cmd/cli/cmd/typeinstance/create.go +++ b/cmd/cli/cmd/typeinstance/create.go @@ -101,12 +101,13 @@ func createTI(ctx context.Context, opts createOptions, resourcePrinter *printer. typeInstanceToCreate = mergeCreateTypeInstances(typeInstanceToCreate, out) } - validationResult, err := validation.ValidateTypeInstancesToCreate(ctx, hubCli, typeInstanceToCreate) + r := validation.ResultAggregator{} + err = r.Report(validation.ValidateTypeInstancesToCreate(ctx, hubCli, typeInstanceToCreate)) if err != nil { return errors.Wrap(err, "while validating TypeInstances") } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() + if r.ErrorOrNil() != nil { + return r.ErrorOrNil() } // HACK: UsesRelations are required on GQL side so at least empty array needs to be send diff --git a/cmd/cli/cmd/typeinstance/edit.go b/cmd/cli/cmd/typeinstance/edit.go index f54794b9b..e2d22aacf 100644 --- a/cmd/cli/cmd/typeinstance/edit.go +++ b/cmd/cli/cmd/typeinstance/edit.go @@ -60,12 +60,13 @@ func editTI(ctx context.Context, opts editOptions, w io.Writer) error { return err } - validationResult, err := validation.ValidateTypeInstanceToUpdate(ctx, hubCli, typeInstanceToUpdate) + r := validation.ResultAggregator{} + err = r.Report(validation.ValidateTypeInstanceToUpdate(ctx, hubCli, typeInstanceToUpdate)) if err != nil { return errors.Wrap(err, "while validating TypeInstance") } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() + if r.ErrorOrNil() != nil { + return r.ErrorOrNil() } _, err = hubCli.UpdateTypeInstances(ctx, typeInstanceToUpdate) diff --git a/pkg/argo-actions/update_type_instances.go b/pkg/argo-actions/update_type_instances.go index ee053d36f..522796593 100644 --- a/pkg/argo-actions/update_type_instances.go +++ b/pkg/argo-actions/update_type_instances.go @@ -86,12 +86,13 @@ func (u *Update) Do(ctx context.Context) error { u.log.Info("Validating TypeInstances") - validationResult, err := validation.ValidateTypeInstanceToUpdate(ctx, u.client, payload) + r := validation.ResultAggregator{} + err = r.Report(validation.ValidateTypeInstanceToUpdate(ctx, u.client, payload)) if err != nil { return errors.Wrap(err, "while validating TypeInstance") } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() + if r.ErrorOrNil() != nil { + return r.ErrorOrNil() } u.log.Info("Updating TypeInstances in Hub...", zap.Int("TypeInstance count", len(payload))) diff --git a/pkg/argo-actions/upload_type_instances.go b/pkg/argo-actions/upload_type_instances.go index cefa9ad87..a65c31fda 100644 --- a/pkg/argo-actions/upload_type_instances.go +++ b/pkg/argo-actions/upload_type_instances.go @@ -87,12 +87,13 @@ func (u *Upload) Do(ctx context.Context) error { u.log.Info("Validating TypeInstances") - validationResult, err := validation.ValidateTypeInstancesToCreate(ctx, u.client, payload) + r := validation.ResultAggregator{} + err = r.Report(validation.ValidateTypeInstancesToCreate(ctx, u.client, payload)) if err != nil { return errors.Wrap(err, "while validating TypeInstances") } - if validationResult.Len() > 0 { - return validationResult.ErrorOrNil() + if r.ErrorOrNil() != nil { + return r.ErrorOrNil() } u.log.Info("Uploading TypeInstances to Hub...", zap.Int("TypeInstance count", len(payload.TypeInstances))) diff --git a/pkg/sdk/validation/issue_builder.go b/pkg/sdk/validation/issue_builder.go index 85ff7ef1a..f42fc19b6 100644 --- a/pkg/sdk/validation/issue_builder.go +++ b/pkg/sdk/validation/issue_builder.go @@ -39,8 +39,8 @@ func (bldr *IssueBuilder) ReportIssue(field, format string, args ...interface{}) return bldr } -// Result returns validation result index by field name. -func (bldr *IssueBuilder) Result() Result { +// ResultWithCustomErrorFormat returns validation result index by field name with custom error format. +func (bldr *IssueBuilder) ResultWithCustomErrorFormat(errorFormatFn func(bldr *IssueBuilder, issueField string) multierr.ErrorFormatFunc) Result { if bldr == nil { return nil } @@ -49,15 +49,22 @@ func (bldr *IssueBuilder) Result() Result { if issues == nil { continue } - issues.ErrorFormat = headeredErrListFormatFunc(fmt.Sprintf("- %s %q", bldr.header, field)) + issues.ErrorFormat = errorFormatFn(bldr, field) } return bldr.issues } -// headeredErrListFormatFunc is a basic formatter that outputs the errors as +// Result returns validation result index by field name. +func (bldr *IssueBuilder) Result() Result { + return bldr.ResultWithCustomErrorFormat(func(bldr *IssueBuilder, issueField string) multierr.ErrorFormatFunc { + return HeaderedErrListFormatFunc(fmt.Sprintf("- %s %q", bldr.header, issueField)) + }) +} + +// HeaderedErrListFormatFunc is a basic formatter that outputs the errors as // a bullet point list with a given header. -func headeredErrListFormatFunc(fieldName string) multierr.ErrorFormatFunc { +func HeaderedErrListFormatFunc(fieldName string) multierr.ErrorFormatFunc { return func(es []error) string { points := make([]string, len(es)) for i, err := range es { diff --git a/pkg/sdk/validation/typeinstance.go b/pkg/sdk/validation/typeinstance.go index a3d655683..064563b03 100644 --- a/pkg/sdk/validation/typeinstance.go +++ b/pkg/sdk/validation/typeinstance.go @@ -9,6 +9,7 @@ import ( "capact.io/capact/internal/ptr" graphqllocal "capact.io/capact/pkg/hub/api/graphql/local" "capact.io/capact/pkg/sdk/apis/0.0.1/types" + "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/xeipuuv/gojsonschema" ) @@ -22,15 +23,16 @@ type TypeInstanceEssentialData struct { } func (ti *TypeInstanceEssentialData) String() string { - if ti == nil { - return "" + emptyMetadataMsg := "empty metadata" + if ti == nil || (ti.ID == nil && ti.Alias == nil) { + return emptyMetadataMsg } var tiMetadata []string if ti.ID != nil { - tiMetadata = append(tiMetadata, fmt.Sprintf("ID: %s", *ti.ID)) + tiMetadata = append(tiMetadata, fmt.Sprintf("ID: %q", *ti.ID)) } if ti.Alias != nil { - tiMetadata = append(tiMetadata, fmt.Sprintf("Alias: %s", *ti.Alias)) + tiMetadata = append(tiMetadata, fmt.Sprintf("Alias: %q", *ti.Alias)) } return strings.Join(tiMetadata, ", ") } @@ -101,7 +103,7 @@ func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValida } typeRef, ok := typeInstancesTypeRef[ti.ID] if !ok { - return nil, errors.Wrapf(err, "while finding TypeInstance Type reference for id %q", ti.ID) + return nil, errors.Wrapf(err, "while finding TypeInstance Type reference for ID %q", ti.ID) } typeInstanceCollection = append(typeInstanceCollection, &TypeInstanceEssentialData{ ID: ptr.String(ti.ID), @@ -120,7 +122,7 @@ func ValidateTypeInstanceToUpdate(ctx context.Context, client TypeInstanceValida //ValidateTypeInstances is responsible for validating TypeInstance. func ValidateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollection []*TypeInstanceEssentialData) (Result, error) { - resultBldr := NewResultBuilder("Validation TypeInstances") + resultBldr := NewResultBuilder("TypeInstance with") for _, ti := range typeInstanceCollection { if _, ok := ti.Value.(map[string]interface{}); !ok { @@ -141,13 +143,13 @@ func ValidateTypeInstances(schemaCollection SchemaCollection, typeInstanceCollec if err != nil { return nil, errors.Wrap(err, "while validating JSON schema for TypeInstance") } - if !result.Valid() { - for _, err := range result.Errors() { - msg := fmt.Sprintf("TypeInstance(%s)", ti.String()) - resultBldr.ReportIssue(msg, err.String()) - } + + for _, err := range result.Errors() { + resultBldr.ReportIssue(ti.String(), err.String()) } } - return resultBldr.Result(), nil + return resultBldr.ResultWithCustomErrorFormat(func(bldr *IssueBuilder, issueField string) multierror.ErrorFormatFunc { + return HeaderedErrListFormatFunc(fmt.Sprintf("- %s %s", bldr.header, issueField)) + }), nil } diff --git a/pkg/sdk/validation/typeinstance_test.go b/pkg/sdk/validation/typeinstance_test.go index 25404948f..408460a18 100644 --- a/pkg/sdk/validation/typeinstance_test.go +++ b/pkg/sdk/validation/typeinstance_test.go @@ -1,6 +1,7 @@ package validation import ( + "errors" "fmt" "testing" @@ -35,7 +36,7 @@ func TestValidateTypeInstances(t *testing.T) { Alias: ptr.String("aws-creds"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(Alias: aws-creds)\":\n * (root): key is required"), + expError: errors.New("- TypeInstance with Alias: \"aws-creds\":\n * (root): key is required"), }, "When TypeInstance value does not meet Type property constraints": { schemaCollection: SchemaCollection{ @@ -56,7 +57,7 @@ func TestValidateTypeInstances(t *testing.T) { ID: ptr.String("5605af48-c34f-4bdc-b2d8-53c679bdfa5a"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(ID: 5605af48-c34f-4bdc-b2d8-53c679bdfa5a)\":\n * replicas: Invalid type. Expected: string, given: integer"), + expError: errors.New("- TypeInstance with ID: \"5605af48-c34f-4bdc-b2d8-53c679bdfa5a\":\n * replicas: Invalid type. Expected: string, given: integer"), }, "When TypeInstance contains the required property": { schemaCollection: SchemaCollection{ @@ -107,7 +108,7 @@ func TestValidateTypeInstances(t *testing.T) { Alias: ptr.String("aws-creds-2"), }, }, - expError: fmt.Errorf("%s", "- Validation TypeInstances \"TypeInstance(Alias: aws-creds)\":\n * (root): key is required\n- Validation TypeInstances \"TypeInstance(Alias: aws-creds-2)\":\n * (root): key is required"), + expError: errors.New("- TypeInstance with Alias: \"aws-creds\":\n * (root): key is required\n- TypeInstance with Alias: \"aws-creds-2\":\n * (root): key is required"), }, }