Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement resource pruning in k8s plugin #5455

Merged
merged 27 commits into from
Jan 14, 2025

Conversation

Warashi
Copy link
Contributor

@Warashi Warashi commented Dec 26, 2024

What this PR does:

as title

Why we need it:

to prune the resources which removed in the git repository

Which issue(s) this PR fixes:

Part of #4980

Does this PR introduce a user-facing change?: No

  • How are users affected by this change:
  • Is this breaking change:
  • How to migrate (if breaking change):

Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Copy link

codecov bot commented Dec 26, 2024

Codecov Report

Attention: Patch coverage is 41.07143% with 132 lines in your changes missing coverage. Please review.

Project coverage is 26.19%. Comparing base (792015b) to head (40af37c).
Report is 17 commits behind head on master.

Files with missing lines Patch % Lines
.../app/pipedv1/plugin/kubernetes/provider/kubectl.go 0.00% 77 Missing ⚠️
...app/pipedv1/plugin/kubernetes/deployment/server.go 65.51% 16 Missing and 4 partials ⚠️
...app/pipedv1/plugin/kubernetes/provider/manifest.go 19.04% 17 Missing ⚠️
...g/app/pipedv1/plugin/kubernetes/provider/loader.go 0.00% 13 Missing ⚠️
...app/pipedv1/plugin/kubernetes/provider/resource.go 89.36% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5455      +/-   ##
==========================================
+ Coverage   26.15%   26.19%   +0.04%     
==========================================
  Files         456      457       +1     
  Lines       48992    49303     +311     
==========================================
+ Hits        12814    12916     +102     
- Misses      35156    35362     +206     
- Partials     1022     1025       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 89 to 90
// Set the namespace for all manifests.
manifests[i].body.SetNamespace(input.Namespace)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this to ensure the loaded manifests' namespaces are matched with applied resource.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, we have to check the input.Namespace is empty or not.

Comment on lines +92 to +98
// Add builtin labels and annotations for tracking application live state.
manifests[i].AddLabels(map[string]string{
LabelManagedBy: ManagedByPiped,
LabelPiped: input.PipedID,
LabelApplication: input.AppID,
LabelCommitHash: input.CommitHash,
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want these labels to retrieve live resources when pruning the resources.

Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
@Warashi Warashi marked this pull request as ready for review December 26, 2024 07:47
Copy link
Member

@ffjlabo ffjlabo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen everything yet, but I left some comments 🙏

Comment on lines 236 to 240
// getAPIResources retrieves the list of available API resources from the Kubernetes cluster.
// It runs the `kubectl api-resources` command with the specified kubeconfig and returns the
// names of the resources that support the "list", "get", and "delete" verbs.
func (c *Kubectl) getAPIResources(ctx context.Context, kubeconfig string) ([]string, error) {
args := []string{"api-resources", "--namespaced=false", "--verbs=list,get,delete", "--output=name"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] How about fixing the name as getClusterScopedAPIResources?
It seems that the method calls kubectl api-resources --namespaced=false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed this on this commit.
c2a747f

Comment on lines 34 to 36
groupKind schema.GroupKind
namespace string
name string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to comment on why piped identifies these three values. 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added comments on this commit.
6fb0aab

Comment on lines +307 to +308
if namespace == "" {
args = append(args, "--all-namespaces")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ask] In this case, does the result include the cluster scoped resources?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result does not include the cluster scoped resources.
I checked with the kubectl v1.32.0.

$ kubectl version
Client Version: v1.32.0
Kustomize Version: v5.5.0
Server Version: v1.31.0

$ kubectl get all --all-namespaces
NAMESPACE            NAME                                             READY   STATUS    RESTARTS   AGE
kube-system          pod/coredns-6f6b679f8f-czgq7                     1/1     Running   0          2d5h
kube-system          pod/coredns-6f6b679f8f-j7vcr                     1/1     Running   0          2d5h
kube-system          pod/etcd-kind-control-plane                      1/1     Running   0          2d5h
kube-system          pod/kindnet-rxcpn                                1/1     Running   0          2d5h
kube-system          pod/kube-apiserver-kind-control-plane            1/1     Running   0          2d5h
kube-system          pod/kube-controller-manager-kind-control-plane   1/1     Running   0          2d5h
kube-system          pod/kube-proxy-58jbg                             1/1     Running   0          2d5h
kube-system          pod/kube-scheduler-kind-control-plane            1/1     Running   0          2d5h
local-path-storage   pod/local-path-provisioner-57c5987fd4-52wtm      1/1     Running   0          2d5h

NAMESPACE     NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP                  2d5h
kube-system   service/kube-dns     ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   2d5h

NAMESPACE     NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-system   daemonset.apps/kindnet      1         1         1       1            1           kubernetes.io/os=linux   2d5h
kube-system   daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   2d5h

NAMESPACE            NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
kube-system          deployment.apps/coredns                  2/2     2            2           2d5h
local-path-storage   deployment.apps/local-path-provisioner   1/1     1            1           2d5h

NAMESPACE            NAME                                                DESIRED   CURRENT   READY   AGE
kube-system          replicaset.apps/coredns-6f6b679f8f                  2         2         2       2d5h
local-path-storage   replicaset.apps/local-path-provisioner-57c5987fd4   1         1         1       2d5h

@@ -171,8 +200,7 @@ func FindSameManifests(olds, news []Manifest) []WorkloadPair {
pairs := make([]WorkloadPair, 0)
oldMap := make(map[ResourceKey]Manifest, len(olds))
nomalizeKey := func(k ResourceKey) ResourceKey {
// Ignoring APIVersion because user can upgrade to the new APIVersion for the same workload.
k.apiVersion = ""
// Normalize the key by removing the default namespace.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use k.normalize() or k.normalizeNamespace() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed to use k.normalize() on this commit.
48c0fb3

Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
@Warashi Warashi requested a review from ffjlabo December 27, 2024 06:28
Copy link
Member

@ffjlabo ffjlabo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the more comments 🙏

return model.StageStatus_STAGE_FAILURE
}

clusterLiveResources, err := kubectl.GetAllClusterScoped(ctx, deployTargetConfig.KubeConfigPath,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] clusterLiveResources -> clusterScopedLiveResources

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applied on this commit
6392bca

pkg/app/pipedv1/plugin/kubernetes/provider/resource.go Outdated Show resolved Hide resolved
)

{
keys := make(map[ResourceKey]struct{}, len(manifests))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMO] How about changing keys -> normalizedKeys to distinguish the original key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applied on this commit
e1dec97

}

{
keys := make(map[ResourceKey]struct{}, len(manifests))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMO] How about changing keys -> normalizedKeys to distinguish the original key?

Same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applied on this commit
e1dec97

{
keys := make(map[ResourceKey]struct{}, len(manifests))
for _, m := range manifests {
keys[m.Key().normalize().withoutNamespace()] = struct{}{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] It would be nice to add comment to show why using withoutNamespace()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added comments on this commit.
e1dec97

Comment on lines 88 to 92
func normalizeGroupKind(gk schema.GroupKind) schema.GroupKind {
gk.Group = strings.ToLower(gk.Group)
gk.Kind = strings.ToLower(gk.Kind)
return gk
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[imo] How about unifying to normalize to avoid using only this method in other code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applied on this commit.
bcfd121

Warashi and others added 5 commits December 27, 2024 15:45
Co-authored-by: Yoshiki Fujikane <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
Signed-off-by: Shinnosuke Sawada-Dazai <[email protected]>
@Warashi Warashi requested a review from ffjlabo December 27, 2024 06:59

running := filepath.Join("./", "testdata", "prune", "running")

// read the running application config from the example file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// read the running application config from the example file
// read the running application config from the testdata file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. applied on this commit.
f47da4b


target := filepath.Join("./", "testdata", "prune", "target")

// read the running application config from the example file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// read the running application config from the example file
// read the running application config from the testdata file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. applied on this commit.
f47da4b

@@ -136,6 +136,8 @@ func setupTestPluginConfigAndDynamicClient(t *testing.T) (*config.PipedPlugin, d
}

func TestDeploymentService_executeK8sSyncStage(t *testing.T) {
t.Parallel()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[imo] It might not be set to avoid operation collision for each tests. WDYT>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the operation collision does not occur because the envtest's dummy cluster is launched in each test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. It is in setupTestPluginConfigAndDynamicClient. Thanks, I got your point.


// The service should be created in the new namespace
service, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}).Namespace("test-2").Get(context.Background(), "simple", metav1.GetOptions{})
require.NoError(t, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[imo] If possible, it would be nice to check the error is not found. (I'm not sure about the spec for the dynamic client 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I added the assertion on this commit.
b12e317

Comment on lines 489 to 492
require.Equal(t, "piped", service.GetLabels()["pipecd.dev/managed-by"])
require.Equal(t, "piped-id", service.GetLabels()["pipecd.dev/piped"])
require.Equal(t, "app-id", service.GetLabels()["pipecd.dev/application"])
require.Equal(t, "0012345678", service.GetLabels()["pipecd.dev/commit-hash"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[imo] In addition to them, we need to check the resource key to check whether the namespace has changed correcty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I added resource key assertion on this commit.
d75fd94

@ffjlabo
Copy link
Member

ffjlabo commented Dec 27, 2024

Also imo about the test 🙏

  • How about using t.Run for each preparating or checking.
  • It would be nice to add the testcase for the custom resouce's ones
    • When the Custom Resource's preferred version is v1 and the actual resource's version is v1beta1, Is it possible to remove the resource?

That's all for now 🙏

@Warashi
Copy link
Contributor Author

Warashi commented Jan 6, 2025

@ffjlabo Thanks.

  • How about using t.Run for each preparating or checking.

I fixed the tests on this commit.
34c7807

  • It would be nice to add the testcase for the custom resouce's ones
    • When the Custom Resource's preferred version is v1 and the actual resource's version is v1beta1, Is it possible to remove the resource?

I added v1beta1 pruning assertions in the cluster-scoped test because it already uses the custom resources.
40af37c

@Warashi Warashi requested a review from ffjlabo January 6, 2025 00:57
@ffjlabo
Copy link
Member

ffjlabo commented Jan 7, 2025

@Warashi Thank you for the great contribution! Those fixes look good!

BTW, I noticed that it might also be nice to add a test case when updating the resource's API version and deleting it (e.g., HPA autoscaling/v1 -> autoscaling/v2, and later remove the resource).
But it might take some more time to do. Is it worth doing now? WDYT?

@Warashi
Copy link
Contributor Author

Warashi commented Jan 7, 2025

@ffjlabo Thank you!

BTW, I noticed that it might also be nice to add a test case when updating the resource's API version and deleting it (e.g., HPA autoscaling/v1 -> autoscaling/v2, and later remove the resource). But it might take some more time to do. Is it worth doing now? WDYT?

This PR is already large, so I want to do it on another PR.

@ffjlabo
Copy link
Member

ffjlabo commented Jan 7, 2025

@Warashi OK! Thanks!

Copy link
Member

@ffjlabo ffjlabo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGreatTM 👍

@t-kikuc t-kikuc merged commit 58fda21 into master Jan 14, 2025
18 checks passed
@t-kikuc t-kikuc deleted the k8s-plugin-retrieve-live-state-from-cluster branch January 14, 2025 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants