From ced2336f1d37e9d45157700f0028ac0284ad1725 Mon Sep 17 00:00:00 2001 From: nghialv Date: Tue, 22 Dec 2020 12:43:35 +0900 Subject: [PATCH 1/2] Add rpc api and pipectl command to list applications --- pkg/app/api/grpcapi/BUILD.bazel | 2 +- pkg/app/api/grpcapi/api.go | 56 ++++++++++++ pkg/app/api/grpcapi/utils.go | 10 +++ pkg/app/api/service/apiservice/service.proto | 11 +++ pkg/app/pipectl/cmd/application/BUILD.bazel | 1 + .../pipectl/cmd/application/application.go | 9 +- pkg/app/pipectl/cmd/application/list.go | 90 +++++++++++++++++++ pkg/model/BUILD.bazel | 1 + pkg/model/common.go | 24 +++++ 9 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 pkg/app/pipectl/cmd/application/list.go create mode 100644 pkg/model/common.go diff --git a/pkg/app/api/grpcapi/BUILD.bazel b/pkg/app/api/grpcapi/BUILD.bazel index 41cbe9d821..8f5f5a7cda 100644 --- a/pkg/app/api/grpcapi/BUILD.bazel +++ b/pkg/app/api/grpcapi/BUILD.bazel @@ -24,8 +24,8 @@ go_library( "//pkg/config:go_default_library", "//pkg/crypto:go_default_library", "//pkg/datastore:go_default_library", - "//pkg/insightstore:go_default_library", "//pkg/git:go_default_library", + "//pkg/insightstore:go_default_library", "//pkg/model:go_default_library", "//pkg/rpc/rpcauth:go_default_library", "@com_github_google_uuid//:go_default_library", diff --git a/pkg/app/api/grpcapi/api.go b/pkg/app/api/grpcapi/api.go index e160094e1f..ae45184e67 100644 --- a/pkg/app/api/grpcapi/api.go +++ b/pkg/app/api/grpcapi/api.go @@ -167,6 +167,62 @@ func (a *API) GetApplication(ctx context.Context, req *apiservice.GetApplication }, nil } +// ListApplications returns the application list of the project where the caller belongs to. +// Currently, the maximum number of returned applications is 10. +func (a *API) ListApplications(ctx context.Context, req *apiservice.ListApplicationsRequest) (*apiservice.ListApplicationsResponse, error) { + key, err := requireAPIKey(ctx, model.APIKey_READ_ONLY, a.logger) + if err != nil { + return nil, err + } + + const limit = 10 + filters := []datastore.ListFilter{ + { + Field: "ProjectId", + Operator: "==", + Value: key.ProjectId, + }, + } + if req.Name != "" { + filters = append(filters, datastore.ListFilter{ + Field: "Name", + Operator: "==", + Value: req.Name, + }) + } + if req.Kind != "" { + kind, ok := model.ApplicationKind_value[req.Kind] + if !ok { + return nil, status.Error(codes.InvalidArgument, "Invalid application kind") + } + filters = append(filters, datastore.ListFilter{ + Field: "Kind", + Operator: "==", + Value: model.ApplicationKind(kind), + }) + } + if req.EnvId != "" { + filters = append(filters, datastore.ListFilter{ + Field: "EnvId", + Operator: "==", + Value: req.EnvId, + }) + } + opts := datastore.ListOptions{ + Filters: filters, + PageSize: limit, + } + + apps, err := listApplications(ctx, a.applicationStore, opts, a.logger) + if err != nil { + return nil, err + } + + return &apiservice.ListApplicationsResponse{ + Applications: apps, + }, nil +} + func (a *API) GetDeployment(ctx context.Context, req *apiservice.GetDeploymentRequest) (*apiservice.GetDeploymentResponse, error) { key, err := requireAPIKey(ctx, model.APIKey_READ_ONLY, a.logger) if err != nil { diff --git a/pkg/app/api/grpcapi/utils.go b/pkg/app/api/grpcapi/utils.go index fee1cbac4a..762e9c59e6 100644 --- a/pkg/app/api/grpcapi/utils.go +++ b/pkg/app/api/grpcapi/utils.go @@ -54,6 +54,16 @@ func getApplication(ctx context.Context, store datastore.ApplicationStore, id st return app, nil } +func listApplications(ctx context.Context, store datastore.ApplicationStore, opts datastore.ListOptions, logger *zap.Logger) ([]*model.Application, error) { + apps, err := store.ListApplications(ctx, opts) + if err != nil { + logger.Error("failed to list applications", zap.Error(err)) + return nil, status.Error(codes.Internal, "Failed to list applications") + } + + return apps, nil +} + func getDeployment(ctx context.Context, store datastore.DeploymentStore, id string, logger *zap.Logger) (*model.Deployment, error) { deployment, err := store.GetDeployment(ctx, id) if errors.Is(err, datastore.ErrNotFound) { diff --git a/pkg/app/api/service/apiservice/service.proto b/pkg/app/api/service/apiservice/service.proto index 800c09fd2e..0312a7d364 100644 --- a/pkg/app/api/service/apiservice/service.proto +++ b/pkg/app/api/service/apiservice/service.proto @@ -29,6 +29,7 @@ service APIService { rpc AddApplication(AddApplicationRequest) returns (AddApplicationResponse) {} rpc SyncApplication(SyncApplicationRequest) returns (SyncApplicationResponse) {} rpc GetApplication(GetApplicationRequest) returns (GetApplicationResponse) {} + rpc ListApplications(ListApplicationsRequest) returns (ListApplicationsResponse) {} rpc GetDeployment(GetDeploymentRequest) returns (GetDeploymentResponse) {} @@ -64,6 +65,16 @@ message GetApplicationResponse { pipe.model.Application application = 1; } +message ListApplicationsRequest { + string name = 1; + string kind = 2; + string env_id = 3; +} + +message ListApplicationsResponse { + repeated pipe.model.Application applications = 1; +} + message GetDeploymentRequest { string deployment_id = 1; } diff --git a/pkg/app/pipectl/cmd/application/BUILD.bazel b/pkg/app/pipectl/cmd/application/BUILD.bazel index dc1f3815cd..b84b94641a 100644 --- a/pkg/app/pipectl/cmd/application/BUILD.bazel +++ b/pkg/app/pipectl/cmd/application/BUILD.bazel @@ -6,6 +6,7 @@ go_library( "add.go", "application.go", "get.go", + "list.go", "sync.go", ], importpath = "github.com/pipe-cd/pipe/pkg/app/pipectl/cmd/application", diff --git a/pkg/app/pipectl/cmd/application/application.go b/pkg/app/pipectl/cmd/application/application.go index 8661b1500d..16283cbec4 100644 --- a/pkg/app/pipectl/cmd/application/application.go +++ b/pkg/app/pipectl/cmd/application/application.go @@ -33,9 +33,12 @@ func NewCommand() *cobra.Command { Short: "Manage application resources.", } - cmd.AddCommand(newAddCommand(c)) - cmd.AddCommand(newSyncCommand(c)) - cmd.AddCommand(newGetCommand(c)) + cmd.AddCommand( + newAddCommand(c), + newSyncCommand(c), + newGetCommand(c), + newListCommand(c), + ) c.clientOptions.RegisterPersistentFlags(cmd) diff --git a/pkg/app/pipectl/cmd/application/list.go b/pkg/app/pipectl/cmd/application/list.go new file mode 100644 index 0000000000..6393180f6a --- /dev/null +++ b/pkg/app/pipectl/cmd/application/list.go @@ -0,0 +1,90 @@ +// Copyright 2020 The PipeCD Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package application + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "strings" + + "github.com/spf13/cobra" + + "github.com/pipe-cd/pipe/pkg/app/api/service/apiservice" + "github.com/pipe-cd/pipe/pkg/cli" + "github.com/pipe-cd/pipe/pkg/model" +) + +type list struct { + root *command + + appName string + envId string + appKind string + stdout io.Writer +} + +func newListCommand(root *command) *cobra.Command { + c := &list{ + root: root, + stdout: os.Stdout, + } + cmd := &cobra.Command{ + Use: "list", + Short: "Show the list of applications. Currently, the maximum number of returned applications is 10.", + RunE: cli.WithContext(c.run), + } + + cmd.Flags().StringVar(&c.appName, "app-name", c.appName, "The application name.") + cmd.Flags().StringVar(&c.envId, "env-id", c.envId, "The environment ID.") + cmd.Flags().StringVar(&c.appKind, "app-kind", c.appKind, fmt.Sprintf("The kind of application. (%s)", strings.Join(model.ApplicationKindStrings(), "|"))) + + return cmd +} + +func (c *list) run(ctx context.Context, _ cli.Telemetry) error { + if c.appKind != "" { + if _, ok := model.ApplicationKind_value[c.appKind]; !ok { + return fmt.Errorf("invalid applicaiton kind") + } + } + + cli, err := c.root.clientOptions.NewClient(ctx) + if err != nil { + return fmt.Errorf("failed to initialize client: %w", err) + } + defer cli.Close() + + req := &apiservice.ListApplicationsRequest{ + Name: c.appName, + EnvId: c.envId, + Kind: c.appKind, + } + + resp, err := cli.ListApplications(ctx, req) + if err != nil { + return fmt.Errorf("failed to list application: %w", err) + } + + bytes, err := json.Marshal(resp.Applications) + if err != nil { + return fmt.Errorf("failed to marshal applications: %w", err) + } + + fmt.Fprintln(c.stdout, string(bytes)) + return nil +} diff --git a/pkg/model/BUILD.bazel b/pkg/model/BUILD.bazel index daa2d1c43c..ec6fbbd47e 100644 --- a/pkg/model/BUILD.bazel +++ b/pkg/model/BUILD.bazel @@ -47,6 +47,7 @@ go_library( "application_live_state.go", "cloudprovider.go", "command.go", + "common.go", "datastore.go", "deployment.go", "docs.go", diff --git a/pkg/model/common.go b/pkg/model/common.go new file mode 100644 index 0000000000..25c2c997f2 --- /dev/null +++ b/pkg/model/common.go @@ -0,0 +1,24 @@ +// Copyright 2020 The PipeCD Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +// ApplicationKindStrings returns a list of available deployment kinds in string. +func ApplicationKindStrings() []string { + out := make([]string, 0, len(ApplicationKind_value)) + for k := range ApplicationKind_value { + out = append(out, k) + } + return out +} From 883c7b979286badb1af1bc52f452b6202925d107 Mon Sep 17 00:00:00 2001 From: nghialv Date: Tue, 22 Dec 2020 12:49:21 +0900 Subject: [PATCH 2/2] Fix lints --- pkg/app/pipectl/cmd/application/list.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/app/pipectl/cmd/application/list.go b/pkg/app/pipectl/cmd/application/list.go index 6393180f6a..ad0598dc0e 100644 --- a/pkg/app/pipectl/cmd/application/list.go +++ b/pkg/app/pipectl/cmd/application/list.go @@ -33,7 +33,7 @@ type list struct { root *command appName string - envId string + envID string appKind string stdout io.Writer } @@ -50,7 +50,7 @@ func newListCommand(root *command) *cobra.Command { } cmd.Flags().StringVar(&c.appName, "app-name", c.appName, "The application name.") - cmd.Flags().StringVar(&c.envId, "env-id", c.envId, "The environment ID.") + cmd.Flags().StringVar(&c.envID, "env-id", c.envID, "The environment ID.") cmd.Flags().StringVar(&c.appKind, "app-kind", c.appKind, fmt.Sprintf("The kind of application. (%s)", strings.Join(model.ApplicationKindStrings(), "|"))) return cmd @@ -71,7 +71,7 @@ func (c *list) run(ctx context.Context, _ cli.Telemetry) error { req := &apiservice.ListApplicationsRequest{ Name: c.appName, - EnvId: c.envId, + EnvId: c.envID, Kind: c.appKind, }