Skip to content

Commit 5079ca7

Browse files
Warashiffjlabo
andauthored
Implement the sync state calculation for the kubernetes livestate plugin (#5676)
* Implement the sync state calculation for the kubernetes livestate plugin. Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Add TODO for implementing drift detection in the Kubernetes livestate plugin Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Fix the reason text of the sync state Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Fix lint errors Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Add parallel tests for k8s livestate plugin Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Add tests for the sync state calculation Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Rename the test cases to clear the spec Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> * Update pkg/app/pipedv1/plugin/kubernetes/livestate/plugin_test.go Co-authored-by: Yoshiki Fujikane <[email protected]> Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> --------- Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]> Co-authored-by: Yoshiki Fujikane <[email protected]>
1 parent 8e71ecd commit 5079ca7

File tree

2 files changed

+632
-1
lines changed

2 files changed

+632
-1
lines changed

pkg/app/pipedv1/plugin/kubernetes/livestate/plugin.go

+94-1
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ import (
1818
"cmp"
1919
"context"
2020
"fmt"
21+
"strings"
2122

2223
"go.uber.org/zap"
2324

2425
kubeconfig "github.com/pipe-cd/pipecd/pkg/app/pipedv1/plugin/kubernetes/config"
2526
"github.com/pipe-cd/pipecd/pkg/app/pipedv1/plugin/kubernetes/provider"
2627
"github.com/pipe-cd/pipecd/pkg/app/pipedv1/plugin/kubernetes/toolregistry"
2728
config "github.com/pipe-cd/pipecd/pkg/configv1"
29+
"github.com/pipe-cd/pipecd/pkg/plugin/diff"
2830
"github.com/pipe-cd/pipecd/pkg/plugin/sdk"
2931
)
3032

@@ -75,15 +77,78 @@ func (p Plugin) GetLivestate(ctx context.Context, _ sdk.ConfigNone, deployTarget
7577
resourceStates = append(resourceStates, m.ToResourceState(deployTarget.Name))
7678
}
7779

80+
manifests, err := p.loadManifests(ctx, input, cfg.Spec, provider.NewLoader(toolRegistry))
81+
if err != nil {
82+
input.Logger.Error("Failed to load manifests", zap.Error(err))
83+
return nil, err
84+
}
85+
86+
liveManifests := make([]provider.Manifest, 0, len(namespacedLiveResources)+len(clusterScopedLiveResources))
87+
liveManifests = append(liveManifests, namespacedLiveResources...)
88+
liveManifests = append(liveManifests, clusterScopedLiveResources...)
89+
90+
// Calculate SyncState by comparing live manifests with desired manifests
91+
// TODO: Implement drift detection ignore configs
92+
diffResult, err := provider.DiffList(liveManifests, manifests, input.Logger,
93+
diff.WithEquateEmpty(),
94+
diff.WithIgnoreAddingMapKeys(),
95+
diff.WithCompareNumberAndNumericString(),
96+
)
97+
if err != nil {
98+
input.Logger.Error("Failed to calculate diff", zap.Error(err))
99+
return nil, err
100+
}
101+
102+
syncState := calculateSyncState(diffResult, input.Request.DeploymentSource.CommitHash)
103+
78104
return &sdk.GetLivestateResponse{
79105
LiveState: sdk.ApplicationLiveState{
80106
Resources: resourceStates,
81107
HealthStatus: sdk.ApplicationHealthStateUnknown, // TODO: Implement health status calculation
82108
},
83-
SyncState: sdk.ApplicationSyncState{}, // TODO: Implement sync state calculation
109+
SyncState: syncState,
84110
}, nil
85111
}
86112

113+
func calculateSyncState(diffResult *provider.DiffListResult, commit string) sdk.ApplicationSyncState {
114+
if diffResult.NoChanges() {
115+
return sdk.ApplicationSyncState{
116+
Status: sdk.ApplicationSyncStateSynced,
117+
ShortReason: "",
118+
Reason: "",
119+
}
120+
}
121+
122+
total := len(diffResult.Adds) + len(diffResult.Deletes) + len(diffResult.Changes)
123+
shortReason := fmt.Sprintf("There are %d manifests not synced (%d adds, %d deletes, %d changes)",
124+
total,
125+
len(diffResult.Adds),
126+
len(diffResult.Deletes),
127+
len(diffResult.Changes),
128+
)
129+
130+
if len(commit) > 7 {
131+
commit = commit[:7]
132+
}
133+
134+
var b strings.Builder
135+
b.WriteString(fmt.Sprintf("Diff between the defined state in Git at commit %s and actual state in cluster:\n\n", commit))
136+
b.WriteString("--- Actual (LiveState)\n+++ Expected (Git)\n\n")
137+
138+
details := diffResult.Render(provider.DiffRenderOptions{
139+
MaskSecret: true,
140+
MaskConfigMap: true,
141+
MaxChangedManifests: 3,
142+
})
143+
b.WriteString(details)
144+
145+
return sdk.ApplicationSyncState{
146+
Status: sdk.ApplicationSyncStateOutOfSync,
147+
ShortReason: shortReason,
148+
Reason: b.String(),
149+
}
150+
}
151+
87152
// Name implements sdk.LivestatePlugin.
88153
func (p Plugin) Name() string {
89154
return "kubernetes" // TODO: make this constant to share with deployment plugin
@@ -93,3 +158,31 @@ func (p Plugin) Name() string {
93158
func (p Plugin) Version() string {
94159
return "0.0.1" // TODO: make this constant to share with deployment plugin
95160
}
161+
162+
type loader interface {
163+
// LoadManifests renders and loads all manifests for application.
164+
LoadManifests(ctx context.Context, input provider.LoaderInput) ([]provider.Manifest, error)
165+
}
166+
167+
// TODO: share this implementation with the deployment plugin
168+
func (p Plugin) loadManifests(ctx context.Context, input *sdk.GetLivestateInput, spec *kubeconfig.KubernetesApplicationSpec, loader loader) ([]provider.Manifest, error) {
169+
manifests, err := loader.LoadManifests(ctx, provider.LoaderInput{
170+
PipedID: input.Request.PipedID,
171+
AppID: input.Request.ApplicationID,
172+
CommitHash: input.Request.DeploymentSource.CommitHash,
173+
AppName: input.Request.ApplicationName,
174+
AppDir: input.Request.DeploymentSource.ApplicationDirectory,
175+
ConfigFilename: input.Request.DeploymentSource.ApplicationConfigFilename,
176+
Manifests: spec.Input.Manifests,
177+
Namespace: spec.Input.Namespace,
178+
TemplatingMethod: provider.TemplatingMethodNone, // TODO: Implement detection of templating method or add it to the config spec.
179+
180+
// TODO: Define other fields for LoaderInput
181+
})
182+
183+
if err != nil {
184+
return nil, err
185+
}
186+
187+
return manifests, nil
188+
}

0 commit comments

Comments
 (0)