Skip to content

Commit 04394f5

Browse files
authored
Add LivestatePlugin to implement Livestate/DriftDetection with SDK (#5603)
* Implement LivestatePlugin interface and gRPC server registration for live state retrieval Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Fix import order in livestate.go for consistency Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Clarify GetLivestate method documentation to specify application context Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> --------- Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
1 parent b3f1ccc commit 04394f5

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

pkg/plugin/sdk/livestate.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2025 The PipeCD Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package sdk
16+
17+
import (
18+
"context"
19+
"encoding/json"
20+
"fmt"
21+
22+
"google.golang.org/grpc"
23+
"google.golang.org/grpc/codes"
24+
"google.golang.org/grpc/status"
25+
26+
"github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1/livestate"
27+
)
28+
29+
var (
30+
livestateServiceServer interface {
31+
Plugin
32+
33+
Register(server *grpc.Server)
34+
setCommonFields(commonFields)
35+
setConfig([]byte) error
36+
livestate.LivestateServiceServer
37+
}
38+
)
39+
40+
// LivestatePlugin is the interface that must be implemented by a Livestate plugin.
41+
// In addition to the Plugin interface, it provides a method to get the live state of the resources.
42+
// The Config and DeployTargetConfig are the plugin's config defined in piped's config.
43+
type LivestatePlugin[Config, DeployTargetConfig any] interface {
44+
Plugin
45+
46+
// GetLivestate returns the live state of the resources in the given application.
47+
// It returns the resources' live state and the difference between the desired state and the live state.
48+
// It's allowed to return only the resources' live state if the difference is not available, or only the difference if the live state is not available.
49+
GetLivestate(context.Context, *Config, []*DeployTarget[DeployTargetConfig], TODO) (TODO, error)
50+
}
51+
52+
// LivestatePluginServer is a wrapper for LivestatePlugin to satisfy the LivestateServiceServer interface.
53+
// It is used to register the plugin to the gRPC server.
54+
type LivestatePluginServer[Config, DeployTargetConfig any] struct {
55+
livestate.UnimplementedLivestateServiceServer
56+
commonFields
57+
58+
base LivestatePlugin[Config, DeployTargetConfig]
59+
config Config
60+
}
61+
62+
// RegisterLivestatePlugin registers the given LivestatePlugin to the sdk.
63+
func RegisterLivestatePlugin[Config, DeployTargetConfig any](plugin LivestatePlugin[Config, DeployTargetConfig]) {
64+
livestateServiceServer = &LivestatePluginServer[Config, DeployTargetConfig]{base: plugin}
65+
}
66+
67+
// Name returns the name of the plugin.
68+
func (s *LivestatePluginServer[Config, DeployTargetConfig]) Name() string {
69+
return s.base.Name()
70+
}
71+
72+
// Version returns the version of the plugin.
73+
func (s *LivestatePluginServer[Config, DeployTargetConfig]) Version() string {
74+
return s.base.Version()
75+
}
76+
77+
// Register registers the plugin to the gRPC server.
78+
func (s *LivestatePluginServer[Config, DeployTargetConfig]) Register(server *grpc.Server) {
79+
livestate.RegisterLivestateServiceServer(server, s)
80+
}
81+
82+
// setCommonFields sets the common fields to the plugin server.
83+
func (s *LivestatePluginServer[Config, DeployTargetConfig]) setCommonFields(c commonFields) {
84+
s.commonFields = c
85+
}
86+
87+
// setConfig sets the configuration to the plugin server.
88+
func (s *LivestatePluginServer[Config, DeployTargetConfig]) setConfig(bytes []byte) error {
89+
if bytes == nil {
90+
return nil
91+
}
92+
if err := json.Unmarshal(bytes, &s.config); err != nil {
93+
return fmt.Errorf("failed to unmarshal config: %w", err)
94+
}
95+
return nil
96+
}
97+
98+
// GetLivestate returns the live state of the resources in the given application.
99+
func (s *LivestatePluginServer[Config, DeployTargetConfig]) GetLivestate(context.Context, *livestate.GetLivestateRequest) (*livestate.GetLivestateResponse, error) {
100+
return nil, status.Errorf(codes.Unimplemented, "method GetLivestate not implemented")
101+
}

pkg/plugin/sdk/sdk.go

+14
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,20 @@ func (s *plugin) run(ctx context.Context, input cli.Input) (runErr error) {
186186
opts = append(opts, rpc.WithPrometheusUnaryInterceptor())
187187
}
188188

189+
if livestateServiceServer != nil {
190+
livestateServiceServer.setCommonFields(commonFields{
191+
config: cfg,
192+
logger: input.Logger.Named("livestate-service"),
193+
logPersister: persister,
194+
client: pipedapiClient,
195+
})
196+
if err := livestateServiceServer.setConfig(cfg.Config); err != nil {
197+
input.Logger.Error("failed to set configuration", zap.Error(err))
198+
return err
199+
}
200+
opts = append(opts, rpc.WithService(livestateServiceServer))
201+
}
202+
189203
server := rpc.NewServer(deploymentServiceServer, opts...)
190204
group.Go(func() error {
191205
return server.Run(ctx)

0 commit comments

Comments
 (0)