diff --git a/pkg/controller/batchrelease/workloads/statefulset_like_controller.go b/pkg/controller/batchrelease/workloads/statefulset_like_controller.go index e6fb279..7af5067 100644 --- a/pkg/controller/batchrelease/workloads/statefulset_like_controller.go +++ b/pkg/controller/batchrelease/workloads/statefulset_like_controller.go @@ -215,7 +215,6 @@ func (c *StatefulSetLikeController) countUpdatedReadyPods(updateRevision string) updatedReadyReplicas++ } } - klog.V(3).Infof("BatchRelease(%v) observed %d updatedReadyReplicas") return updatedReadyReplicas, nil } diff --git a/pkg/util/controller_finder.go b/pkg/util/controller_finder.go index a7d11f9..ebb6b37 100644 --- a/pkg/util/controller_finder.go +++ b/pkg/util/controller_finder.go @@ -278,7 +278,7 @@ func (r *ControllerFinder) getStatefulSetLikeWorkload(namespace string, ref *rol func (r *ControllerFinder) getLatestCanaryDeployment(stable *apps.Deployment) (*apps.Deployment, error) { canaryList := &apps.DeploymentList{} selector, _ := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: map[string]string{CanaryDeploymentLabel: stable.Name}}) - err := r.List(context.TODO(), canaryList, &client.ListOptions{LabelSelector: selector}, utilclient.DisableDeepCopy) + err := r.List(context.TODO(), canaryList, &client.ListOptions{LabelSelector: selector, Namespace: stable.Namespace}, utilclient.DisableDeepCopy) if err != nil { return nil, err } else if len(canaryList.Items) == 0 { diff --git a/pkg/util/pod_utils.go b/pkg/util/pod_utils.go index b435dcd..0d86608 100644 --- a/pkg/util/pod_utils.go +++ b/pkg/util/pod_utils.go @@ -8,7 +8,6 @@ import ( utilclient "github.com/openkruise/rollouts/pkg/util/client" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" @@ -86,7 +85,7 @@ func ListOwnedPods(c client.Client, object client.Object) ([]*v1.Pod, error) { } podLister := &v1.PodList{} - err = c.List(context.TODO(), podLister, &client.ListOptions{LabelSelector: selector}, utilclient.DisableDeepCopy) + err = c.List(context.TODO(), podLister, &client.ListOptions{LabelSelector: selector, Namespace: object.GetNamespace()}, utilclient.DisableDeepCopy) if err != nil { return nil, err } @@ -96,8 +95,12 @@ func ListOwnedPods(c client.Client, object client.Object) ([]*v1.Pod, error) { if IsCompletedPod(pod) { continue } - owner := metav1.GetControllerOf(pod) - if owner == nil || owner.UID != object.GetUID() { + // we should find their indirect owner-relationship, + // such as pod -> replicaset -> deployment + owned, err := IsOwnedBy(c, pod, object) + if err != nil { + return nil, err + } else if !owned { continue } pods = append(pods, pod) diff --git a/pkg/util/workloads_utils.go b/pkg/util/workloads_utils.go index b2de708..2438869 100644 --- a/pkg/util/workloads_utils.go +++ b/pkg/util/workloads_utils.go @@ -72,6 +72,7 @@ const ( var ( knownWorkloadGVKs = []*schema.GroupVersionKind{ + &ControllerKindRS, &ControllerKindDep, &ControllerKindSts, &ControllerKruiseKindCS, @@ -231,10 +232,12 @@ func GetEmptyWorkloadObject(gvk schema.GroupVersionKind) client.Object { return nil } - switch gvk.Kind { - case ControllerKindDep.Kind: + switch gvk { + case ControllerKindRS: + return &apps.ReplicaSet{} + case ControllerKindDep: return &apps.Deployment{} - case ControllerKruiseKindCS.Kind: + case ControllerKruiseKindCS: return &appsv1alpha1.CloneSet{} default: unstructuredObject := &unstructured.Unstructured{} @@ -507,32 +510,53 @@ func IsSupportedWorkload(gvk schema.GroupVersionKind) bool { return false } +// GetOwnerWorkload return the top-level workload that is controlled by rollout, +// if the object has no owner, just return nil func GetOwnerWorkload(r client.Reader, object client.Object) (client.Object, error) { + if object == nil { + return nil, nil + } owner := metav1.GetControllerOf(object) - if owner == nil { + // We just care about the top-level workload that is referred by rollout + if owner == nil || len(object.GetAnnotations()[InRolloutProgressingAnnotation]) > 0 { return nil, nil } ownerGvk := schema.FromAPIVersionAndKind(owner.APIVersion, owner.Kind) ownerKey := types.NamespacedName{Namespace: object.GetNamespace(), Name: owner.Name} - if ownerGvk.Group == ControllerKindRS.Group && ownerGvk.Kind == ControllerKindRS.Kind { - replicaset := &apps.ReplicaSet{} - err := r.Get(context.TODO(), ownerKey, replicaset) - if err != nil { - return nil, err - } - return GetOwnerWorkload(r, replicaset) - } - ownerObj := GetEmptyWorkloadObject(ownerGvk) if ownerObj == nil { return nil, nil } err := r.Get(context.TODO(), ownerKey, ownerObj) - if err != nil { + if client.IgnoreNotFound(err) != nil { return nil, err } - return ownerObj, nil + return GetOwnerWorkload(r, ownerObj) +} + +// IsOwnedBy will return true if the child is owned by parent directly or indirectly. +func IsOwnedBy(r client.Reader, child, parent client.Object) (bool, error) { + if child == nil { + return false, nil + } + owner := metav1.GetControllerOf(child) + if owner == nil { + return false, nil + } else if owner.UID == parent.GetUID() { + return true, nil + } + ownerGvk := schema.FromAPIVersionAndKind(owner.APIVersion, owner.Kind) + ownerKey := types.NamespacedName{Namespace: child.GetNamespace(), Name: owner.Name} + ownerObj := GetEmptyWorkloadObject(ownerGvk) + if ownerObj == nil { + return false, nil + } + err := r.Get(context.TODO(), ownerKey, ownerObj) + if client.IgnoreNotFound(err) != nil { + return false, err + } + return IsOwnedBy(r, ownerObj, parent) } func IsWorkloadType(object client.Object, t WorkloadType) bool { diff --git a/test/e2e/rollout_test.go b/test/e2e/rollout_test.go index b96da8a..6a1b35c 100644 --- a/test/e2e/rollout_test.go +++ b/test/e2e/rollout_test.go @@ -65,6 +65,12 @@ var _ = SIGDescribe("Rollout", func() { cloneSet := &appsv1alpha1.CloneSetList{} k8sClient.List(context.TODO(), cloneSet, client.InNamespace(namespace)) fmt.Println(util.DumpJSON(cloneSet)) + sts := &apps.StatefulSetList{} + k8sClient.List(context.TODO(), sts, client.InNamespace(namespace)) + fmt.Println(util.DumpJSON(sts)) + asts := &appsv1beta1.StatefulSetList{} + k8sClient.List(context.TODO(), asts, client.InNamespace(namespace)) + fmt.Println(util.DumpJSON(asts)) } CreateObject := func(object client.Object, options ...client.CreateOption) {