From c9c856dfdaf4de373977fe7a927a1c24c1878224 Mon Sep 17 00:00:00 2001 From: KubeSphere CI Bot <47586280+ks-ci-bot@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:22:58 +0800 Subject: [PATCH] Fix conflict and cherry-pick (cherry-pick from #0e8c6d5) (#6413) Fix conflict and cherry-pick (cherry-pick from #0e8c6d5) Co-authored-by: inksnw --- .../application/apprelease_controller.go | 2 +- pkg/controller/application/cluster.go | 31 ----- .../application/delete_predicate.go | 26 ++++ .../application/helm_repo_controller.go | 127 ++++++++++++++++-- pkg/simple/client/application/helper.go | 1 - 5 files changed, 143 insertions(+), 44 deletions(-) delete mode 100644 pkg/controller/application/cluster.go create mode 100644 pkg/controller/application/delete_predicate.go diff --git a/pkg/controller/application/apprelease_controller.go b/pkg/controller/application/apprelease_controller.go index 6d01bf0f2..18b2ce407 100644 --- a/pkg/controller/application/apprelease_controller.go +++ b/pkg/controller/application/apprelease_controller.go @@ -94,7 +94,7 @@ func (r *AppReleaseReconciler) SetupWithManager(mgr *controller.Manager) error { Watches( &clusterv1alpha1.Cluster{}, handler.EnqueueRequestsFromMapFunc(r.mapper), - builder.WithPredicates(ClusterDeletePredicate{}), + builder.WithPredicates(DeletePredicate{}), ). WithEventFilter(IgnoreAnnotationChangePredicate{AnnotationKey: appv2.TimeoutRecheck}). For(&appv2.ApplicationRelease{}).Complete(r) diff --git a/pkg/controller/application/cluster.go b/pkg/controller/application/cluster.go deleted file mode 100644 index 5813b4170..000000000 --- a/pkg/controller/application/cluster.go +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Please refer to the LICENSE file in the root directory of the project. - * https://github.com/kubesphere/kubesphere/blob/master/LICENSE - */ - -package application - -import ( - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" -) - -type ClusterDeletePredicate struct { - predicate.Funcs -} - -func (ClusterDeletePredicate) Update(e event.UpdateEvent) bool { - return false -} - -func (ClusterDeletePredicate) Create(_ event.CreateEvent) bool { - return false -} - -func (ClusterDeletePredicate) Delete(_ event.DeleteEvent) bool { - return true -} - -func (ClusterDeletePredicate) Generic(_ event.GenericEvent) bool { - return false -} diff --git a/pkg/controller/application/delete_predicate.go b/pkg/controller/application/delete_predicate.go new file mode 100644 index 000000000..e3e3a562e --- /dev/null +++ b/pkg/controller/application/delete_predicate.go @@ -0,0 +1,26 @@ +package application + +import ( + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +type DeletePredicate struct { + predicate.Funcs +} + +func (DeletePredicate) Update(e event.UpdateEvent) bool { + return false +} + +func (DeletePredicate) Create(_ event.CreateEvent) bool { + return false +} + +func (DeletePredicate) Delete(_ event.DeleteEvent) bool { + return true +} + +func (DeletePredicate) Generic(_ event.GenericEvent) bool { + return false +} diff --git a/pkg/controller/application/helm_repo_controller.go b/pkg/controller/application/helm_repo_controller.go index 993a45131..0970bd9a5 100644 --- a/pkg/controller/application/helm_repo_controller.go +++ b/pkg/controller/application/helm_repo_controller.go @@ -1,7 +1,18 @@ /* - * Please refer to the LICENSE file in the root directory of the project. - * https://github.com/kubesphere/kubesphere/blob/master/LICENSE - */ +Copyright 2023 The KubeSphere 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 @@ -13,6 +24,13 @@ import ( "strings" "time" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "kubesphere.io/api/constants" + tenantv1beta1 "kubesphere.io/api/tenant/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/handler" + "kubesphere.io/utils/s3" clusterv1alpha1 "kubesphere.io/api/cluster/v1alpha1" @@ -53,6 +71,22 @@ func (r *RepoReconciler) Enabled(clusterRole string) bool { return strings.EqualFold(clusterRole, string(clusterv1alpha1.ClusterRoleHost)) } +func (r *RepoReconciler) mapper(ctx context.Context, o client.Object) (requests []reconcile.Request) { + workspace := o.(*tenantv1beta1.WorkspaceTemplate) + + klog.Infof("workspace %s has been deleted", workspace.Name) + repoList := &appv2.RepoList{} + opts := &client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspace.Name})} + if err := r.List(ctx, repoList, opts); err != nil { + klog.Errorf("failed to list repo: %v", err) + return requests + } + for _, repo := range repoList.Items { + requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{Name: repo.Name}}) + } + return requests +} + func (r *RepoReconciler) SetupWithManager(mgr *kscontroller.Manager) (err error) { r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(helmRepoController) @@ -64,6 +98,11 @@ func (r *RepoReconciler) SetupWithManager(mgr *kscontroller.Manager) (err error) } return ctrl.NewControllerManagedBy(mgr). + Watches( + &tenantv1beta1.WorkspaceTemplate{}, + handler.EnqueueRequestsFromMapFunc(r.mapper), + builder.WithPredicates(DeletePredicate{}), + ). For(&appv2.Repo{}). Complete(r) } @@ -133,8 +172,23 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques klog.Errorf("get helm repo failed, error: %s", err) return reconcile.Result{}, client.IgnoreNotFound(err) } - requeueAfter := time.Duration(helmRepo.Spec.SyncPeriod) * time.Second + workspaceTemplate := &tenantv1beta1.WorkspaceTemplate{} + workspaceName := helmRepo.Labels[constants.WorkspaceLabelKey] + if workspaceName != "" { + err := r.Get(ctx, types.NamespacedName{Name: workspaceName}, workspaceTemplate) + if apierrors.IsNotFound(err) || (err == nil && !workspaceTemplate.DeletionTimestamp.IsZero()) { + klog.Infof("workspace not found or deleting %s %s", workspaceName, err) + err = r.Delete(ctx, helmRepo) + if err != nil { + klog.Errorf("delete helm repo failed, error: %s", err) + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + } + + requeueAfter := time.Duration(helmRepo.Spec.SyncPeriod) * time.Second noSync, err := r.noNeedSync(ctx, helmRepo) if err != nil { return reconcile.Result{}, err @@ -156,6 +210,33 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques klog.Errorf("load index failed, repo: %s, url: %s, err: %s", helmRepo.GetName(), helmRepo.Spec.Url, err) return reconcile.Result{}, err } + + appList := &appv2.ApplicationList{} + opts := client.ListOptions{ + LabelSelector: labels.SelectorFromSet(labels.Set{appv2.RepoIDLabelKey: helmRepo.Name}), + } + err = r.Client.List(ctx, appList, &opts) + if err != nil { + klog.Errorf("list appversion failed, error: %s", err) + return reconcile.Result{}, err + } + indexMap := make(map[string]struct{}) + for appName := range index.Entries { + shortName := application.GenerateShortNameMD5Hash(appName) + key := fmt.Sprintf("%s-%s", helmRepo.Name, shortName) + indexMap[key] = struct{}{} + } + for _, i := range appList.Items { + if _, exists := indexMap[i.Name]; !exists { + klog.Infof("app %s has been removed from the repo", i.Name) + err = r.Client.Delete(ctx, &i) + if err != nil { + klog.Errorf("delete app %s failed, error: %s", i.Name, err) + return reconcile.Result{}, err + } + } + } + for appName, versions := range index.Entries { if len(versions) == 0 { klog.Infof("no version found for %s", appName) @@ -163,20 +244,18 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques } versions = filterVersions(versions) - if len(versions) > appv2.MaxNumOfVersions { - versions = versions[:appv2.MaxNumOfVersions] - } vRequests, err := repoParseRequest(r.Client, versions, helmRepo, appName) if err != nil { klog.Errorf("parse request failed, error: %s", err) return reconcile.Result{}, err } - klog.Infof("found %d/%d versions for %s need to upgrade", len(vRequests), len(versions), appName) if len(vRequests) == 0 { continue } + klog.Infof("found %d/%d versions for %s need to upgrade", len(vRequests), len(versions), appName) + own := metav1.OwnerReference{ APIVersion: appv2.SchemeGroupVersion.String(), Kind: "Repo", @@ -204,8 +283,12 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques func repoParseRequest(cli client.Client, versions helmrepo.ChartVersions, helmRepo *appv2.Repo, appName string) (result []application.AppRequest, err error) { appVersionList := &appv2.ApplicationVersionList{} + appID := fmt.Sprintf("%s-%s", helmRepo.Name, application.GenerateShortNameMD5Hash(appName)) opts := client.ListOptions{ - LabelSelector: labels.SelectorFromSet(labels.Set{appv2.RepoIDLabelKey: helmRepo.Name}), + LabelSelector: labels.SelectorFromSet(labels.Set{ + appv2.RepoIDLabelKey: helmRepo.Name, + appv2.AppIDLabelKey: appID, + }), } err = cli.List(context.Background(), appVersionList, &opts) if err != nil { @@ -214,21 +297,43 @@ func repoParseRequest(cli client.Client, versions helmrepo.ChartVersions, helmRe } appVersionDigestMap := make(map[string]string) + versionMap := make(map[string]struct{}) + for _, ver := range versions { + v := application.FormatVersion(ver.Version) + shortName := application.GenerateShortNameMD5Hash(ver.Name) + key := fmt.Sprintf("%s-%s-%s", helmRepo.Name, shortName, v) + versionMap[key] = struct{}{} + } + for _, i := range appVersionList.Items { LegalVersion := application.FormatVersion(i.Spec.VersionName) key := fmt.Sprintf("%s-%s", i.GetLabels()[appv2.AppIDLabelKey], LegalVersion) - appVersionDigestMap[key] = i.Spec.Digest + _, exists := versionMap[key] + if !exists { + klog.Infof("delete appversion %s", i.GetName()) + err = cli.Delete(context.Background(), &i) + if err != nil { + klog.Errorf("delete appversion failed, error: %s", err) + return nil, err + } + } else { + appVersionDigestMap[key] = i.Spec.Digest + } } + for _, ver := range versions { + legalVersion := application.FormatVersion(ver.Version) shortName := application.GenerateShortNameMD5Hash(ver.Name) key := fmt.Sprintf("%s-%s-%s", helmRepo.Name, shortName, legalVersion) dig := appVersionDigestMap[key] if dig == ver.Digest { continue - } else { + } + if dig != "" { klog.Infof("digest not match, key: %s, digest: %s, ver.Digest: %s", key, dig, ver.Digest) } + vRequest := application.AppRequest{ RepoName: helmRepo.Name, VersionName: ver.Version, diff --git a/pkg/simple/client/application/helper.go b/pkg/simple/client/application/helper.go index dcc0d9df1..8392bbdbf 100644 --- a/pkg/simple/client/application/helper.go +++ b/pkg/simple/client/application/helper.go @@ -576,7 +576,6 @@ func FormatVersion(input string) string { } hash := sha1.Sum([]byte(input)) formattedVersion := hex.EncodeToString(hash[:])[:12] - klog.Warningf("Version: %s does not meet the Kubernetes naming standard, replacing with SHA-1 hash: %s", input, formattedVersion) return formattedVersion }