Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bin/
bin/
34 changes: 34 additions & 0 deletions simulator/resourceapplier/resourceapplier.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package resourceapplier

import (
"context"
"encoding/json"

"golang.org/x/xerrors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
)

Expand Down Expand Up @@ -124,6 +126,9 @@ func (s *Service) Create(ctx context.Context, resource *unstructured.Unstructure
return xerrors.Errorf("failed to create resource: %w", err)
}

if gvk.Kind == "Pod" {
return s.PatchPodStatus(ctx, resource)
}
return nil
}

Expand Down Expand Up @@ -315,3 +320,32 @@ func (s *Service) addMutateBeforeUpdating(gvr schema.GroupVersionResource, fn []

s.mutateBeforeUpdating[gvr] = append(s.mutateBeforeUpdating[gvr], fn...)
}

func (s *Service) PatchPodStatus(ctx context.Context, resource *unstructured.Unstructured) error {
gvk := resource.GroupVersionKind()
gvr, err := s.findGVRForGVK(gvk)
if err != nil {
return err
}

namespace := resource.GetNamespace()
newStatus := resource.Object["status"]
patchData := map[string]interface{}{
"status": newStatus,
}
patchBytes, err := json.Marshal(patchData)
if err != nil {
return err
}
_, err = s.clients.DynamicClient.Resource(gvr).Namespace(namespace).Patch(
ctx,
resource.GetName(),
types.MergePatchType,
patchBytes, metav1.PatchOptions{},
"status",
)
if err != nil {
return xerrors.Errorf("failed to patch status: %w, gvr: %v, name: %v", err, gvr, resource.GetName())
}
return nil
}
96 changes: 96 additions & 0 deletions simulator/resourceapplier/resourceapplier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,54 @@ func TestResourceApplier_createPods(t *testing.T) {
filtered: false,
wantErr: false,
},
{
name: "create a Pod have succeeded status",
podToCreate: &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "succeeded-pod",
Namespace: "default",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-1",
Image: "image-1",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodSucceeded,
},
},
podAfterCreate: &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "succeeded-pod",
Namespace: "default",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-1",
Image: "image-1",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodSucceeded,
},
},
filter: nil,
filtered: false,
wantErr: false,
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -337,6 +385,54 @@ func TestResourceApplier_updatePods(t *testing.T) {
},
wantErr: false,
},
{
name: "update an Pod have succeeded status",
originalPod: &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "pod-1",
Namespace: "default",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-1",
Image: "image-1",
},
},
},
},
updatePod: func(pod *corev1.Pod) {
pod.Status = corev1.PodStatus{
Phase: corev1.PodSucceeded,
}
},
podAfterUpdate: &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "pod-1",
Namespace: "default",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container-1",
Image: "image-1",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodSucceeded,
},
},
wantErr: false,
},
}

for _, tt := range tests {
Expand Down
24 changes: 21 additions & 3 deletions simulator/syncer/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,27 @@ func (s *Service) addFunc(obj interface{}) {
}
}

func (s *Service) updateFunc(_, newObj interface{}) {
func (s *Service) updateFunc(oldObj, newObj interface{}) {
ctx := context.Background()
unstructObj, ok := newObj.(*unstructured.Unstructured)
newUnstructuredObj, ok := newObj.(*unstructured.Unstructured)
if !ok {
klog.Error("Failed to convert runtime.Object to *unstructured.Unstructured")
return
}

err := s.resourceApplierService.Update(ctx, unstructObj)
oldUnstructuredObj, ok := oldObj.(*unstructured.Unstructured)
if !ok {
klog.Error("Failed to convert runtime.Object to *unstructured.Unstructured")
return
}

if newUnstructuredObj.GetKind() == "Pod" && !podStatusEqual(oldUnstructuredObj, newUnstructuredObj) {
if patchErr := s.resourceApplierService.PatchPodStatus(ctx, newUnstructuredObj); patchErr != nil {
klog.Errorf("Failed to patch pod status: %v", patchErr)
}
}

err := s.resourceApplierService.Update(ctx, newUnstructuredObj)
Comment on lines +90 to +110
Copy link
Contributor

Choose a reason for hiding this comment

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

The logic of applying resource of real clusters is encapsulated in resourceapplier.
And this fix should also be applied to oneshotimporter and recorder.
So we should confine this fix to resourceapplier.
We could use defer to bypass the filters in updateFunc.

Copy link
Contributor Author

@Park-Jiyeonn Park-Jiyeonn Jun 23, 2025

Choose a reason for hiding this comment

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

Thank you, it's good suggestion. Let me make some modifications.

if err != nil {
if errors.IsNotFound(err) {
// We just ignore the not found error because the scheduler may preempt the Pods, or users may remove the resources for debugging.
Expand Down Expand Up @@ -124,3 +136,9 @@ func (s *Service) deleteFunc(obj interface{}) {
}
}
}

func podStatusEqual(oldPod, newPod *unstructured.Unstructured) bool {
oldPhase, _, _ := unstructured.NestedString(oldPod.Object, "status", "phase")
newPhase, _, _ := unstructured.NestedString(newPod.Object, "status", "phase")
return oldPhase == newPhase
}