diff --git a/pkg/app/pipedv1/controller/planner_test.go b/pkg/app/pipedv1/controller/planner_test.go index e22d1bb3a6..b31a898e4b 100644 --- a/pkg/app/pipedv1/controller/planner_test.go +++ b/pkg/app/pipedv1/controller/planner_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" "google.golang.org/grpc" + reflectionpb "google.golang.org/grpc/reflection/grpc_reflection_v1" "github.com/pipe-cd/pipecd/pkg/app/pipedv1/plugin" config "github.com/pipe-cd/pipecd/pkg/configv1" @@ -116,6 +117,31 @@ func pointerBool(b bool) *bool { return &b } +func (p *fakePlugin) ServerReflectionInfo(ctx context.Context, opts ...grpc.CallOption) (reflectionpb.ServerReflection_ServerReflectionInfoClient, error) { + return &fakeServerReflectionInfoClient{}, nil +} + +type fakeServerReflectionInfoClient struct { + reflectionpb.ServerReflection_ServerReflectionInfoClient +} + +func (c *fakeServerReflectionInfoClient) Send(req *reflectionpb.ServerReflectionRequest) error { + return nil +} +func (c *fakeServerReflectionInfoClient) Recv() (*reflectionpb.ServerReflectionResponse, error) { + return &reflectionpb.ServerReflectionResponse{ + MessageResponse: &reflectionpb.ServerReflectionResponse_ListServicesResponse{ + ListServicesResponse: &reflectionpb.ListServiceResponse{ + Service: []*reflectionpb.ServiceResponse{ + { + Name: "pipecd.plugin.api.v1alpha1.livestate.LivestateService", + }, + }, + }, + }, + }, nil +} + func TestBuildQuickSyncStages(t *testing.T) { t.Parallel() diff --git a/pkg/app/pipedv1/livestatereporter/livestatereporter.go b/pkg/app/pipedv1/livestatereporter/livestatereporter.go index 56f63596d2..acff48e34a 100644 --- a/pkg/app/pipedv1/livestatereporter/livestatereporter.go +++ b/pkg/app/pipedv1/livestatereporter/livestatereporter.go @@ -212,9 +212,9 @@ func (r *reporter) flush(ctx context.Context, app *model.Application, repo git.R return err } - pluginClis, err := r.pluginRegistry.GetPluginClientsByAppConfig(cfg.Spec) + pluginClis, err := r.pluginRegistry.GetLivestateSupportedClientsByAppConfig(cfg.Spec) if err != nil { - r.logger.Error("failed to get plugin clients", zap.Error(err)) + r.logger.Error("unable to determine plugin", zap.Error(err)) return err } diff --git a/pkg/app/pipedv1/livestatereporter/livestatereporter_test.go b/pkg/app/pipedv1/livestatereporter/livestatereporter_test.go index 3d12a95858..67dea7a4f9 100644 --- a/pkg/app/pipedv1/livestatereporter/livestatereporter_test.go +++ b/pkg/app/pipedv1/livestatereporter/livestatereporter_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "google.golang.org/grpc" + reflectionpb "google.golang.org/grpc/reflection/grpc_reflection_v1" "github.com/pipe-cd/pipecd/pkg/app/pipedv1/plugin" "github.com/pipe-cd/pipecd/pkg/app/server/service/pipedservice" @@ -123,6 +124,31 @@ func (p *fakePlugin) GetLivestate(ctx context.Context, in *livestate.GetLivestat }, nil } +type fakeServerReflectionInfoClient struct { + reflectionpb.ServerReflection_ServerReflectionInfoClient +} + +func (p *fakePlugin) ServerReflectionInfo(ctx context.Context, opts ...grpc.CallOption) (reflectionpb.ServerReflection_ServerReflectionInfoClient, error) { + return &fakeServerReflectionInfoClient{}, nil +} + +func (c *fakeServerReflectionInfoClient) Send(req *reflectionpb.ServerReflectionRequest) error { + return nil +} +func (c *fakeServerReflectionInfoClient) Recv() (*reflectionpb.ServerReflectionResponse, error) { + return &reflectionpb.ServerReflectionResponse{ + MessageResponse: &reflectionpb.ServerReflectionResponse_ListServicesResponse{ + ListServicesResponse: &reflectionpb.ListServiceResponse{ + Service: []*reflectionpb.ServiceResponse{ + { + Name: "pipecd.plugin.api.v1alpha1.livestate.LivestateService", + }, + }, + }, + }, + }, nil +} + type fakeAPILister struct { applicationLister apps []*model.Application diff --git a/pkg/app/pipedv1/plugin/registry.go b/pkg/app/pipedv1/plugin/registry.go index fbeab9803f..9f8ba0660a 100644 --- a/pkg/app/pipedv1/plugin/registry.go +++ b/pkg/app/pipedv1/plugin/registry.go @@ -18,9 +18,13 @@ import ( "context" "fmt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + config "github.com/pipe-cd/pipecd/pkg/configv1" pluginapi "github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1" "github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1/deployment" + livestate "github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1/livestate" ) // Plugin represents a plugin with its name and client. @@ -33,23 +37,25 @@ type Plugin struct { type PluginRegistry interface { GetPluginClientByStageName(name string) (pluginapi.PluginClient, error) GetPluginClientsByAppConfig(cfg *config.GenericApplicationSpec) ([]pluginapi.PluginClient, error) + GetLivestateSupportedClientsByAppConfig(cfg *config.GenericApplicationSpec) ([]pluginapi.PluginClient, error) } type pluginRegistry struct { - nameBasedPlugins map[string]pluginapi.PluginClient // key: plugin name - stageBasedPlugins map[string]pluginapi.PluginClient // key: stage name + nameBasedPlugins map[string]Plugin // key: plugin name + stageBasedPlugins map[string]Plugin // key: stage name - // TODO: add more fields if needed (e.g. deploymentBasedPlugins, livestateBasedPlugins) + livestateSupportedPlugins map[string]Plugin // key: plugin name } // NewPluginRegistry creates a new PluginRegistry based on the given plugins. func NewPluginRegistry(ctx context.Context, plugins []Plugin) (PluginRegistry, error) { - nameBasedPlugins := make(map[string]pluginapi.PluginClient) - stageBasedPlugins := make(map[string]pluginapi.PluginClient) + nameBasedPlugins := make(map[string]Plugin) + stageBasedPlugins := make(map[string]Plugin) + livestateSupportedPlugins := make(map[string]Plugin) for _, plg := range plugins { // add the plugin to the name-based plugins - nameBasedPlugins[plg.Name] = plg.Cli + nameBasedPlugins[plg.Name] = plg // add the plugin to the stage-based plugins res, err := plg.Cli.FetchDefinedStages(ctx, &deployment.FetchDefinedStagesRequest{}) @@ -58,13 +64,24 @@ func NewPluginRegistry(ctx context.Context, plugins []Plugin) (PluginRegistry, e } for _, stage := range res.Stages { - stageBasedPlugins[stage] = plg.Cli + stageBasedPlugins[stage] = plg + } + + _, err = plg.Cli.GetLivestate(ctx, &livestate.GetLivestateRequest{}) + st, ok := status.FromError(err) + if !ok { + return nil, err + } + + if st.Code() != codes.Unimplemented { + livestateSupportedPlugins[plg.Name] = plg } } return &pluginRegistry{ - nameBasedPlugins: nameBasedPlugins, - stageBasedPlugins: stageBasedPlugins, + nameBasedPlugins: nameBasedPlugins, + stageBasedPlugins: stageBasedPlugins, + livestateSupportedPlugins: livestateSupportedPlugins, }, nil } @@ -75,7 +92,7 @@ func (pr *pluginRegistry) GetPluginClientByStageName(name string) (pluginapi.Plu return nil, fmt.Errorf("no plugin found for the specified stage") } - return plugin, nil + return plugin.Cli, nil } // GetPluginClientsByAppConfig returns the plugin clients based on the given configuration. @@ -84,6 +101,20 @@ func (pr *pluginRegistry) GetPluginClientByStageName(name string) (pluginapi.Plu // 2. If the plugins are specified, it will determine the plugins based on the plugin names. // 3. If neither the pipeline nor the plugins are specified, it will return an error. func (pr *pluginRegistry) GetPluginClientsByAppConfig(cfg *config.GenericApplicationSpec) ([]pluginapi.PluginClient, error) { + plugins, err := pr.getPluginClientsByAppConfig(cfg) + if err != nil { + return nil, err + } + + clis := make([]pluginapi.PluginClient, 0, len(plugins)) + for _, p := range plugins { + clis = append(clis, p.Cli) + } + + return clis, nil +} + +func (pr *pluginRegistry) getPluginClientsByAppConfig(cfg *config.GenericApplicationSpec) ([]Plugin, error) { if cfg.Pipeline != nil && len(cfg.Pipeline.Stages) > 0 { return pr.getPluginClientsByPipeline(cfg.Pipeline) } @@ -95,12 +126,12 @@ func (pr *pluginRegistry) GetPluginClientsByAppConfig(cfg *config.GenericApplica return nil, fmt.Errorf("no plugin specified") } -func (pr *pluginRegistry) getPluginClientsByPipeline(pipeline *config.DeploymentPipeline) ([]pluginapi.PluginClient, error) { +func (pr *pluginRegistry) getPluginClientsByPipeline(pipeline *config.DeploymentPipeline) ([]Plugin, error) { if len(pipeline.Stages) == 0 { return nil, fmt.Errorf("no stages are set in the pipeline") } - plugins := make([]pluginapi.PluginClient, 0, len(pipeline.Stages)) + plugins := make([]Plugin, 0, len(pipeline.Stages)) for _, stage := range pipeline.Stages { plugin, ok := pr.stageBasedPlugins[stage.Name.String()] if !ok { @@ -112,12 +143,12 @@ func (pr *pluginRegistry) getPluginClientsByPipeline(pipeline *config.Deployment return plugins, nil } -func (pr *pluginRegistry) getPluginClientsByNames(names map[string]struct{}) ([]pluginapi.PluginClient, error) { +func (pr *pluginRegistry) getPluginClientsByNames(names map[string]struct{}) ([]Plugin, error) { if len(names) == 0 { return nil, fmt.Errorf("no plugin names are set") } - plugins := make([]pluginapi.PluginClient, 0, len(names)) + plugins := make([]Plugin, 0, len(names)) for name := range names { plugin, ok := pr.nameBasedPlugins[name] if !ok { @@ -128,3 +159,20 @@ func (pr *pluginRegistry) getPluginClientsByNames(names map[string]struct{}) ([] return plugins, nil } + +// GetLivestateSupportedClientsByAppConfig returns the livestate supported plugin clients +func (pr *pluginRegistry) GetLivestateSupportedClientsByAppConfig(cfg *config.GenericApplicationSpec) ([]pluginapi.PluginClient, error) { + plugins, err := pr.getPluginClientsByAppConfig(cfg) + if err != nil { + return nil, err + } + + livestateSupported := make([]pluginapi.PluginClient, 0, len(plugins)) + for _, p := range plugins { + if _, ok := pr.livestateSupportedPlugins[p.Name]; ok { + livestateSupported = append(livestateSupported, p.Cli) + } + } + + return livestateSupported, nil +} diff --git a/pkg/app/pipedv1/plugin/registry_test.go b/pkg/app/pipedv1/plugin/registry_test.go index 95b9074868..f39b80829e 100644 --- a/pkg/app/pipedv1/plugin/registry_test.go +++ b/pkg/app/pipedv1/plugin/registry_test.go @@ -51,9 +51,9 @@ func TestPluginRegistry_GetPluginClientsByAppConfig(t *testing.T) { }, setup: func() *pluginRegistry { return &pluginRegistry{ - stageBasedPlugins: map[string]pluginapi.PluginClient{ - "stage1": fakePluginClient{name: "stage1"}, - "stage2": fakePluginClient{name: "stage2"}, + stageBasedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1", Cli: fakePluginClient{name: "stage1"}}, + "stage2": {Name: "stage2", Cli: fakePluginClient{name: "stage2"}}, }, } }, @@ -71,9 +71,9 @@ func TestPluginRegistry_GetPluginClientsByAppConfig(t *testing.T) { }, setup: func() *pluginRegistry { return &pluginRegistry{ - nameBasedPlugins: map[string]pluginapi.PluginClient{ - "plugin1": fakePluginClient{name: "plugin1"}, - "plugin2": fakePluginClient{name: "plugin2"}, + nameBasedPlugins: map[string]Plugin{ + "plugin1": {Name: "plugin1", Cli: fakePluginClient{name: "plugin1"}}, + "plugin2": {Name: "plugin2", Cli: fakePluginClient{name: "plugin2"}}, }, } }, @@ -96,13 +96,13 @@ func TestPluginRegistry_GetPluginClientsByAppConfig(t *testing.T) { }, setup: func() *pluginRegistry { return &pluginRegistry{ - stageBasedPlugins: map[string]pluginapi.PluginClient{ - "stage1": fakePluginClient{name: "stage1"}, - "stage2": fakePluginClient{name: "stage2"}, + stageBasedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1", Cli: fakePluginClient{name: "stage1"}}, + "stage2": {Name: "stage2", Cli: fakePluginClient{name: "stage2"}}, }, - nameBasedPlugins: map[string]pluginapi.PluginClient{ - "plugin1": fakePluginClient{name: "plugin1"}, - "plugin2": fakePluginClient{name: "plugin2"}, + nameBasedPlugins: map[string]Plugin{ + "plugin1": {Name: "plugin1", Cli: fakePluginClient{name: "plugin1"}}, + "plugin2": {Name: "plugin2", Cli: fakePluginClient{name: "plugin2"}}, }, } }, @@ -117,7 +117,7 @@ func TestPluginRegistry_GetPluginClientsByAppConfig(t *testing.T) { cfg: &config.GenericApplicationSpec{}, setup: func() *pluginRegistry { return &pluginRegistry{ - nameBasedPlugins: map[string]pluginapi.PluginClient{}, + nameBasedPlugins: map[string]Plugin{}, } }, wantErr: true, @@ -135,6 +135,7 @@ func TestPluginRegistry_GetPluginClientsByAppConfig(t *testing.T) { }) } } + func TestPluginRegistry_getPluginClientsByPipeline(t *testing.T) { t.Parallel() @@ -142,7 +143,7 @@ func TestPluginRegistry_getPluginClientsByPipeline(t *testing.T) { name string pipeline *config.DeploymentPipeline setup func() *pluginRegistry - expected []pluginapi.PluginClient + expected []Plugin wantErr bool }{ { @@ -155,15 +156,15 @@ func TestPluginRegistry_getPluginClientsByPipeline(t *testing.T) { }, setup: func() *pluginRegistry { return &pluginRegistry{ - stageBasedPlugins: map[string]pluginapi.PluginClient{ - "stage1": fakePluginClient{name: "stage1"}, - "stage2": fakePluginClient{name: "stage2"}, + stageBasedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1"}, + "stage2": {Name: "stage2"}, }, } }, - expected: []pluginapi.PluginClient{ - fakePluginClient{name: "stage1"}, - fakePluginClient{name: "stage2"}, + expected: []Plugin{ + {Name: "stage1"}, + {Name: "stage2"}, }, wantErr: false, }, @@ -174,7 +175,7 @@ func TestPluginRegistry_getPluginClientsByPipeline(t *testing.T) { }, setup: func() *pluginRegistry { return &pluginRegistry{ - stageBasedPlugins: map[string]pluginapi.PluginClient{}, + stageBasedPlugins: map[string]Plugin{}, } }, expected: nil, @@ -190,7 +191,7 @@ func TestPluginRegistry_getPluginClientsByPipeline(t *testing.T) { }, setup: func() *pluginRegistry { return &pluginRegistry{ - stageBasedPlugins: map[string]pluginapi.PluginClient{}, + stageBasedPlugins: map[string]Plugin{}, } }, expected: nil, @@ -209,6 +210,7 @@ func TestPluginRegistry_getPluginClientsByPipeline(t *testing.T) { }) } } + func TestPluginRegistry_getPluginClientsByNames(t *testing.T) { t.Parallel() @@ -216,7 +218,7 @@ func TestPluginRegistry_getPluginClientsByNames(t *testing.T) { name string pluginNames map[string]struct{} setup func() *pluginRegistry - expected []pluginapi.PluginClient + expected []Plugin wantErr bool }{ { @@ -224,15 +226,15 @@ func TestPluginRegistry_getPluginClientsByNames(t *testing.T) { pluginNames: map[string]struct{}{"plugin1": {}, "plugin2": {}}, setup: func() *pluginRegistry { return &pluginRegistry{ - nameBasedPlugins: map[string]pluginapi.PluginClient{ - "plugin1": fakePluginClient{name: "plugin1"}, - "plugin2": fakePluginClient{name: "plugin2"}, + nameBasedPlugins: map[string]Plugin{ + "plugin1": {Name: "plugin1"}, + "plugin2": {Name: "plugin2"}, }, } }, - expected: []pluginapi.PluginClient{ - fakePluginClient{name: "plugin1"}, - fakePluginClient{name: "plugin2"}, + expected: []Plugin{ + {Name: "plugin1"}, + {Name: "plugin2"}, }, wantErr: false, }, @@ -241,9 +243,9 @@ func TestPluginRegistry_getPluginClientsByNames(t *testing.T) { pluginNames: map[string]struct{}{}, setup: func() *pluginRegistry { return &pluginRegistry{ - nameBasedPlugins: map[string]pluginapi.PluginClient{ - "plugin1": fakePluginClient{name: "plugin1"}, - "plugin2": fakePluginClient{name: "plugin2"}, + nameBasedPlugins: map[string]Plugin{ + "plugin1": {Name: "plugin1"}, + "plugin2": {Name: "plugin2"}, }, } }, @@ -254,9 +256,9 @@ func TestPluginRegistry_getPluginClientsByNames(t *testing.T) { pluginNames: map[string]struct{}{"plugin1": {}, "plugin2": {}}, setup: func() *pluginRegistry { return &pluginRegistry{ - nameBasedPlugins: map[string]pluginapi.PluginClient{ - "plugin3": fakePluginClient{name: "plugin3"}, - "plugin4": fakePluginClient{name: "plugin4"}, + nameBasedPlugins: map[string]Plugin{ + "plugin3": {Name: "plugin3"}, + "plugin4": {Name: "plugin4"}, }, } }, @@ -275,6 +277,7 @@ func TestPluginRegistry_getPluginClientsByNames(t *testing.T) { }) } } + func TestPluginRegistry_GetPluginClientByStageName(t *testing.T) { t.Parallel() @@ -290,8 +293,8 @@ func TestPluginRegistry_GetPluginClientByStageName(t *testing.T) { stage: "stage1", setup: func() *pluginRegistry { return &pluginRegistry{ - stageBasedPlugins: map[string]pluginapi.PluginClient{ - "stage1": fakePluginClient{name: "stage1"}, + stageBasedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1", Cli: fakePluginClient{name: "stage1"}}, }, } }, @@ -303,8 +306,8 @@ func TestPluginRegistry_GetPluginClientByStageName(t *testing.T) { stage: "stage2", setup: func() *pluginRegistry { return &pluginRegistry{ - stageBasedPlugins: map[string]pluginapi.PluginClient{ - "stage1": fakePluginClient{name: "stage1"}, + stageBasedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1", Cli: fakePluginClient{name: "stage1"}}, }, } }, @@ -324,3 +327,137 @@ func TestPluginRegistry_GetPluginClientByStageName(t *testing.T) { }) } } + +func TestPluginRegistry_GetLivestateSupportedClientsByAppConfig(t *testing.T) { + t.Parallel() + + type args struct { + cfg *config.GenericApplicationSpec + } + tests := []struct { + name string + cfg *config.GenericApplicationSpec + setup func() *pluginRegistry + expected []pluginapi.PluginClient + wantErr bool + }{ + { + name: "returns only livestate supported plugin clients by pipeline", + cfg: &config.GenericApplicationSpec{ + Pipeline: &config.DeploymentPipeline{ + Stages: []config.PipelineStage{ + {Name: "stage1"}, + {Name: "stage2"}, + }, + }, + }, + setup: func() *pluginRegistry { + return &pluginRegistry{ + stageBasedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1", Cli: fakePluginClient{name: "stage1"}}, + "stage2": {Name: "stage2", Cli: fakePluginClient{name: "stage2"}}, + }, + livestateSupportedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1", Cli: fakePluginClient{name: "stage1"}}, + }, + } + }, + expected: []pluginapi.PluginClient{ + fakePluginClient{name: "stage1"}, + }, + wantErr: false, + }, + { + name: "returns only livestate supported plugin clients by plugin names", + cfg: &config.GenericApplicationSpec{ + Plugins: map[string]struct{}{"plugin1": {}, "plugin2": {}}, + }, + setup: func() *pluginRegistry { + return &pluginRegistry{ + nameBasedPlugins: map[string]Plugin{ + "plugin1": {Name: "plugin1", Cli: fakePluginClient{name: "plugin1"}}, + "plugin2": {Name: "plugin2", Cli: fakePluginClient{name: "plugin2"}}, + }, + livestateSupportedPlugins: map[string]Plugin{ + "plugin2": {Name: "plugin2", Cli: fakePluginClient{name: "plugin2"}}, + }, + } + }, + expected: []pluginapi.PluginClient{ + fakePluginClient{name: "plugin2"}, + }, + wantErr: false, + }, + { + name: "returns empty slice if no plugin is livestate supported", + cfg: &config.GenericApplicationSpec{ + Plugins: map[string]struct{}{"plugin1": {}, "plugin2": {}}, + }, + setup: func() *pluginRegistry { + return &pluginRegistry{ + nameBasedPlugins: map[string]Plugin{ + "plugin1": {Name: "plugin1", Cli: fakePluginClient{name: "plugin1"}}, + "plugin2": {Name: "plugin2", Cli: fakePluginClient{name: "plugin2"}}, + }, + livestateSupportedPlugins: map[string]Plugin{}, + } + }, + expected: []pluginapi.PluginClient{}, + wantErr: false, + }, + { + name: "returns error if no plugin specified", + cfg: &config.GenericApplicationSpec{}, + setup: func() *pluginRegistry { + return &pluginRegistry{ + nameBasedPlugins: map[string]Plugin{}, + stageBasedPlugins: map[string]Plugin{}, + livestateSupportedPlugins: map[string]Plugin{}, + } + }, + expected: nil, + wantErr: true, + }, + { + name: "returns only livestate supported plugin clients when both pipeline and plugins are specified (pipeline takes precedence)", + cfg: &config.GenericApplicationSpec{ + Pipeline: &config.DeploymentPipeline{ + Stages: []config.PipelineStage{ + {Name: "stage1"}, + {Name: "stage2"}, + }, + }, + Plugins: map[string]struct{}{"plugin1": {}, "plugin2": {}}, + }, + setup: func() *pluginRegistry { + return &pluginRegistry{ + stageBasedPlugins: map[string]Plugin{ + "stage1": {Name: "stage1", Cli: fakePluginClient{name: "stage1"}}, + "stage2": {Name: "stage2", Cli: fakePluginClient{name: "stage2"}}, + }, + nameBasedPlugins: map[string]Plugin{ + "plugin1": {Name: "plugin1", Cli: fakePluginClient{name: "plugin1"}}, + "plugin2": {Name: "plugin2", Cli: fakePluginClient{name: "plugin2"}}, + }, + livestateSupportedPlugins: map[string]Plugin{ + "stage2": {Name: "stage2", Cli: fakePluginClient{name: "stage2"}}, + }, + } + }, + expected: []pluginapi.PluginClient{ + fakePluginClient{name: "stage2"}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + pr := tt.setup() + got, err := pr.GetLivestateSupportedClientsByAppConfig(tt.cfg) + assert.ElementsMatch(t, tt.expected, got) + assert.Equal(t, tt.wantErr, err != nil) + }) + } +} diff --git a/pkg/plugin/api/v1alpha1/client.go b/pkg/plugin/api/v1alpha1/client.go index 57b75fa2a3..63c6cba564 100644 --- a/pkg/plugin/api/v1alpha1/client.go +++ b/pkg/plugin/api/v1alpha1/client.go @@ -18,6 +18,7 @@ import ( "context" "google.golang.org/grpc" + reflectionpb "google.golang.org/grpc/reflection/grpc_reflection_v1" "github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1/deployment" "github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1/livestate" @@ -27,12 +28,14 @@ import ( type PluginClient interface { deployment.DeploymentServiceClient livestate.LivestateServiceClient + reflectionpb.ServerReflectionClient Close() error } type client struct { deployment.DeploymentServiceClient livestate.LivestateServiceClient + reflectionpb.ServerReflectionClient conn *grpc.ClientConn } @@ -45,6 +48,7 @@ func NewClient(ctx context.Context, address string, opts ...rpcclient.DialOption return &client{ DeploymentServiceClient: deployment.NewDeploymentServiceClient(conn), LivestateServiceClient: livestate.NewLivestateServiceClient(conn), + ServerReflectionClient: reflectionpb.NewServerReflectionClient(conn), conn: conn, }, nil } diff --git a/pkg/plugin/sdk/plugin.go b/pkg/plugin/sdk/plugin.go index e1a05255aa..f6f3738778 100644 --- a/pkg/plugin/sdk/plugin.go +++ b/pkg/plugin/sdk/plugin.go @@ -21,12 +21,13 @@ import ( "net/http/pprof" "time" - "github.com/pipe-cd/piped-plugin-sdk-go/logpersister" - "github.com/pipe-cd/piped-plugin-sdk-go/toolregistry" "github.com/spf13/cobra" "go.uber.org/zap" "golang.org/x/sync/errgroup" + "github.com/pipe-cd/piped-plugin-sdk-go/logpersister" + "github.com/pipe-cd/piped-plugin-sdk-go/toolregistry" + "github.com/pipe-cd/pipecd/pkg/admin" "github.com/pipe-cd/pipecd/pkg/cli" config "github.com/pipe-cd/pipecd/pkg/configv1" @@ -320,6 +321,7 @@ func (p *Plugin[Config, DeployTargetConfig, ApplicationConfigSpec]) run(ctx cont rpc.WithLogUnaryInterceptor(input.Logger), rpc.WithRequestValidationUnaryInterceptor(), rpc.WithSignalHandlingUnaryInterceptor(), + rpc.WithGRPCReflection(), } ) if p.tls {