🐛 fix: Check Applied condition before evaluating rollout status - #1243
Conversation
|
/hold |
WalkthroughAdds helpers to centralize status gating and changes rollout status evaluation to require a present WorkApplied condition (observed for the current generation and Status=True) before evaluating WorkProgressing/WorkDegraded; tests and integration suites were updated to insert WorkApplied where appropriate. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested labels
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🧰 Additional context used🧠 Learnings (3)📚 Learning: 2025-07-23T10:10:42.066ZApplied to files:
📚 Learning: 2025-09-03T08:43:34.751ZApplied to files:
📚 Learning: 2025-07-01T02:27:10.927ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
🔇 Additional comments (12)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
af4dc06 to
31d8a33
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (86)
go.sumis excluded by!**/*.sumvendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/LICENSEis excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/message.gois excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/option.gois excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/protocol.gois excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/write_producer_message.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/LICENSEis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/.gitignoreis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/00version.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/README.mdis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/adminapi.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/adminoptions.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/api.htmlis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_darwin_amd64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_darwin_arm64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_dynamic.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_glibc_linux_amd64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_glibc_linux_arm64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_musl_linux_amd64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_musl_linux_arm64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_windows.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/config.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/consumer.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/context.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/error.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/error_gen.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/event.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/generated_errors.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/glue_rdkafka.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/handle.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/header.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/kafka.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/.gitignoreis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/LICENSES.txtis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/README.mdis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/bundle-import.shis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/import.shis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_darwin_amd64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_darwin_arm64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_glibc_linux_amd64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_glibc_linux_arm64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_musl_linux_amd64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_musl_linux_arm64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_windows.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/rdkafka.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/rdkafka_mock.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/log.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/message.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/metadata.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/misc.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/mockcluster.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/offset.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/producer.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/select_rdkafka.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/testconf-example.jsonis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/time.gois excluded by!vendor/**vendor/modules.txtis excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1/rollout.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/addon/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/cluster/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/csr/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/event/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/lease/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/options/generic.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/agent/client/manifestwork.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/source/client/manifestwork.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/constants/constants.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/clients/agentclient.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/clients/baseclient.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/clients/sourceclient.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/metrics/metrics_collector.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/builder/optionsbuilder.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/agentoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/sourceoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/agentoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/options_noop.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/sourceoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/mqtt/agentoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/mqtt/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/mqtt/sourceoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/ratelimiter.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/utils/ratelimiter.gois excluded by!vendor/**
📒 Files selected for processing (2)
go.mod(1 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T10:10:42.066Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1086
File: test/integration/util/grpc.go:146-146
Timestamp: 2025-07-23T10:10:42.066Z
Learning: In OCM codebase, there are two different GRPCServerOptions types: the local one in pkg/server/grpc/options.go (which only has GRPCServerConfig field) and the SDK one from open-cluster-management.io/sdk-go/pkg/cloudevents/server/grpc/options (which has ServerBindPort and other fields with default values). Test code uses the SDK version via grpcoptions import alias.
Applied to files:
go.mod
🧬 Code graph analysis (1)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go (1)
vendor/open-cluster-management.io/api/work/v1/types.go (1)
WorkApplied(500-500)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: integration
- GitHub Check: verify
- GitHub Check: e2e-hosted
- GitHub Check: unit
- GitHub Check: e2e-singleton
- GitHub Check: build
- GitHub Check: e2e
🔇 Additional comments (2)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go (2)
221-221: Good addition of Applied condition check.Retrieving the Applied condition aligns with the PR objective to check hub-side application status before evaluating agent-side conditions.
224-240: No backward compatibility issue—code correctly handles ManifestWorks without Applied condition.The Applied condition is set by the spoke-side controller (pkg/work/spoke/controllers/manifestcontroller/manifestwork_reconciler.go:108) during normal manifest reconciliation. ManifestWorks created without the Applied condition is expected behavior, explicitly documented in appliedmanifestwork_reconciler.go:42–47 with graceful handling for missing conditions. The lines 224–240 correctly gate rollout evaluation until the condition exists and observes the current generation—this is proper design, not a bug. Old ManifestWorks will eventually have the condition set as the spoke applies them.
Likely an incorrect or invalid review comment.
f0f9234 to
82c57fa
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1243 +/- ##
==========================================
+ Coverage 62.19% 62.22% +0.03%
==========================================
Files 210 210
Lines 17084 17099 +15
==========================================
+ Hits 10625 10640 +15
Misses 5342 5342
Partials 1117 1117
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go (1)
902-1135: Comprehensive test coverage, but consider adding one more scenario.The test cases thoroughly cover various combinations of Progressing and Degraded conditions, and correctly inject the Applied condition in all scenarios that should progress past ToApply status.
However, consider adding a test case for:
- Applied condition exists but
Applied.ObservedGeneration != ManifestWork.Generation(should return ToApply)This scenario is explicitly checked in the reconcile logic (lines 234-235 of manifestworkreplicaset_deploy_reconcile.go) but not directly tested here.
Add this test case to verify the Applied ObservedGeneration gating:
{ name: "applied condition with unobserved generation - should return ToApply", manifestWork: &workapiv1.ManifestWork{ ObjectMeta: metav1.ObjectMeta{ Name: "test-mw", Namespace: "cls1", Generation: 2, CreationTimestamp: creationTime, }, Status: workapiv1.ManifestWorkStatus{ Conditions: []metav1.Condition{ { Type: workapiv1.WorkApplied, Status: metav1.ConditionTrue, ObservedGeneration: 1, // Stale generation LastTransitionTime: now, Reason: "Applied", }, { Type: workapiv1.WorkProgressing, Status: metav1.ConditionFalse, ObservedGeneration: 2, LastTransitionTime: now, Reason: "Completed", }, }, }, }, expectedStatus: clustersdkv1alpha1.ToApply, expectedLastTransition: nil, },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (86)
go.sumis excluded by!**/*.sumvendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/LICENSEis excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/message.gois excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/option.gois excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/protocol.gois excluded by!vendor/**vendor/github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2/write_producer_message.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/LICENSEis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/.gitignoreis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/00version.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/README.mdis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/adminapi.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/adminoptions.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/api.htmlis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_darwin_amd64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_darwin_arm64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_dynamic.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_glibc_linux_amd64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_glibc_linux_arm64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_musl_linux_amd64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_musl_linux_arm64.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/build_windows.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/config.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/consumer.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/context.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/error.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/error_gen.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/event.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/generated_errors.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/glue_rdkafka.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/handle.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/header.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/kafka.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/.gitignoreis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/LICENSES.txtis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/README.mdis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/bundle-import.shis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/import.shis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_darwin_amd64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_darwin_arm64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_glibc_linux_amd64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_glibc_linux_arm64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_musl_linux_amd64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_musl_linux_arm64.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/librdkafka_windows.ais excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/rdkafka.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/librdkafka_vendor/rdkafka_mock.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/log.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/message.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/metadata.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/misc.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/mockcluster.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/offset.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/producer.gois excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/select_rdkafka.his excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/testconf-example.jsonis excluded by!vendor/**vendor/github.com/confluentinc/confluent-kafka-go/v2/kafka/time.gois excluded by!vendor/**vendor/modules.txtis excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/apis/cluster/v1alpha1/rollout.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/addon/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/cluster/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/csr/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/event/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/lease/client.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/options/generic.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/agent/client/manifestwork.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/source/client/manifestwork.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/constants/constants.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/clients/agentclient.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/clients/baseclient.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/clients/sourceclient.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/metrics/metrics_collector.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/builder/optionsbuilder.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/agentoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/sourceoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/agentoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/options_noop.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/kafka/sourceoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/mqtt/agentoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/mqtt/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/mqtt/sourceoptions.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/options.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/ratelimiter.gois excluded by!vendor/**vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/utils/ratelimiter.gois excluded by!vendor/**
📒 Files selected for processing (6)
go.mod(1 hunks)pkg/registration/register/grpc/spoke_driver.go(3 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go(1 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go(8 hunks)pkg/work/hub/manager.go(2 hunks)pkg/work/spoke/spokeagent.go(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- go.mod
- pkg/registration/register/grpc/spoke_driver.go
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-07-23T10:10:42.066Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1086
File: test/integration/util/grpc.go:146-146
Timestamp: 2025-07-23T10:10:42.066Z
Learning: In OCM codebase, there are two different GRPCServerOptions types: the local one in pkg/server/grpc/options.go (which only has GRPCServerConfig field) and the SDK one from open-cluster-management.io/sdk-go/pkg/cloudevents/server/grpc/options (which has ServerBindPort and other fields with default values). Test code uses the SDK version via grpcoptions import alias.
Applied to files:
pkg/work/hub/manager.gopkg/work/spoke/spokeagent.go
📚 Learning: 2025-10-28T02:55:13.893Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1224
File: pkg/registration/register/grpc/spoke_driver.go:89-98
Timestamp: 2025-10-28T02:55:13.893Z
Learning: In pkg/registration/register/grpc/spoke_driver.go (Go), when calling cloudeventscsr.NewAgentClientHolder with GenericClientOptions, the watcher store does not need to be explicitly provided via WithClientWatcherStore. The GenericClientOptions.AgentClient() method automatically creates a default AgentInformerWatcherStore if none is provided, which satisfies the NewAgentClientHolder requirements.
Applied to files:
pkg/work/hub/manager.gopkg/work/spoke/spokeagent.go
📚 Learning: 2025-09-03T08:43:34.751Z
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1158
File: test/integration/work/completedmanifestwork_test.go:216-256
Timestamp: 2025-09-03T08:43:34.751Z
Learning: In TTL=0 ManifestWork deletion tests, avoid waiting for WorkComplete condition because the controller deletes the resource immediately upon completion, creating a race condition where the test tries to check completion status on an already-deleted resource.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go
📚 Learning: 2025-07-01T02:27:10.927Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1053
File: vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/agent/client/manifestwork.go:237-237
Timestamp: 2025-07-01T02:27:10.927Z
Learning: In OCM ManifestWork agent client, when a work is being deleted (DeletionTimestamp set and finalizers removed), the agent publishes a status update event (types.UpdateRequestAction) with ResourceDeleted condition set to True to inform the hub that deletion is complete, rather than publishing a delete request event.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go
📚 Learning: 2025-09-08T06:10:32.388Z
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1164
File: test/integration/work/deleteoption_test.go:42-42
Timestamp: 2025-09-08T06:10:32.388Z
Learning: The startWorkAgent function in the OCM test suite uses variadic parameters for decorators (...agentOptionsDecorator), allowing it to be called with just ctx and clusterName without requiring an explicit nil decorator parameter.
Applied to files:
pkg/work/spoke/spokeagent.go
🧬 Code graph analysis (4)
pkg/work/hub/manager.go (1)
vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/builder/optionsbuilder.go (1)
NewConfigLoader(24-29)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go (1)
vendor/open-cluster-management.io/api/work/v1/types.go (1)
WorkApplied(500-500)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go (1)
vendor/open-cluster-management.io/api/work/v1/types.go (1)
WorkApplied(500-500)
pkg/work/spoke/spokeagent.go (1)
vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/builder/optionsbuilder.go (1)
NewConfigLoader(24-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: integration
- GitHub Check: unit
- GitHub Check: verify
- GitHub Check: e2e-singleton
- GitHub Check: cloudevents-integration
- GitHub Check: e2e-hosted
- GitHub Check: e2e
🔇 Additional comments (5)
pkg/work/hub/manager.go (1)
21-21: LGTM! Clean refactoring to builder-based config loading.The migration from
generic.NewConfigLoadertobuilder.NewConfigLoaderis straightforward and maintains the same API surface. Error handling and control flow remain unchanged.Also applies to: 80-80
pkg/work/spoke/spokeagent.go (1)
25-25: LGTM! Consistent builder-based config loading.The change aligns with the builder pattern migration seen in
pkg/work/hub/manager.go. The multi-line formatting of the config loader call improves readability.Also applies to: 229-230
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go (2)
231-233: Excellent documentation of the ordering requirement.The comment clearly explains why Applied must be checked before Progressing/Degraded to prevent using stale timestamps from previous generations. This is critical for correct rollout timeout calculations.
221-240: Fix misleading comment about which controller sets WorkApplied condition.Line 224 comment incorrectly states Applied condition is set by "hub controller" when it's actually set by the spoke/agent controller during manifest reconciliation. Update the comment to accurately reflect that Applied represents the spoke controller's view of work application state. The logic itself is correct—returning
ToApplyfor works without an Applied condition is the intended safe behavior, not a breaking change. Existing ManifestWorks will naturally receive the Applied condition as the spoke controller reconciles them.Likely an incorrect or invalid review comment.
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go (1)
594-601: Good test setup aligning with the new Applied condition requirement.The test correctly sets the Applied condition with matching ObservedGeneration before simulating the Progressing/Degraded states. This ensures the gating logic allows evaluation of rollout status.
82c57fa to
65130d6
Compare
|
/assign @qiujian16 @youngbupark |
|
@haoqing0110: GitHub didn't allow me to assign the following users: youngbupark. Note that only open-cluster-management-io members with read permissions, repo collaborators and people who have commented on this issue/PR can be assigned. Additionally, issues/PRs can only have 10 assignees at the same time. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
| // IMPORTANT: Check Applied condition FIRST to ensure the work has been properly applied | ||
| // before checking agent-side conditions. This prevents using stale timestamps from | ||
| // previous generations when conditions update their ObservedGeneration without changing Status. | ||
| if appliedCond == nil || |
There was a problem hiding this comment.
Is this the only change made to fix this issue?
There was a problem hiding this comment.
another fix is open-cluster-management-io/sdk-go#163
65130d6 to
1df92e6
Compare
1df92e6 to
e6ab9c9
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
test/integration/work/manifestworkreplicaset_test.go (1)
389-394: LGTM! Consistent test updates align with controller requirements.All test blocks correctly add the
WorkAppliedcondition withStatus=Trueand matchingObservedGenerationbefore settingWorkProgressingandWorkDegradedconditions. This properly tests the new gating logic in the controller.Consider adding test coverage for
Applied.Status=Falsescenario:To ensure robust behavior, add a test case that verifies the controller returns
ToApplystatus when the Applied condition exists but hasStatus=False. Example test scenario:// Test that work stays in ToApply when Applied condition is False workCopy := work.DeepCopy() meta.SetStatusCondition(&workCopy.Status.Conditions, metav1.Condition{ Type: workapiv1.WorkApplied, Status: metav1.ConditionFalse, // Application failed Reason: "ApplyFailed", ObservedGeneration: workCopy.Generation, }) // Verify rollout doesn't proceedAlso applies to: 413-418, 456-461, 518-525, 593-600
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (98)
go.sumis excluded by!**/*.sumvendor/cloud.google.com/go/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/auth/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/auth/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/auth/README.mdis excluded by!vendor/**vendor/cloud.google.com/go/auth/auth.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/compute.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/detect.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/doc.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/filetypes.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/aws_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/executable_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/externalaccount.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/file_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/info.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/programmatic_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/url_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/x509_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccountuser/externalaccountuser.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/gdch/gdch.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/impersonate/idtoken.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/impersonate/impersonate.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/stsexchange/sts_exchange.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/selfsignedjwt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/dial_socketopt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/directpath.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/grpctransport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/pool.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/httptransport/httptransport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/httptransport/transport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/compute.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/manufacturer.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/manufacturer_linux.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/manufacturer_windows.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/credsfile/credsfile.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/credsfile/filetype.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/credsfile/parse.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/internal.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/jwt/jwt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/retry/retry.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cba.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/default_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/enterprise_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/secureconnect_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/workload_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/headers/headers.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/s2a.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/transport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/trustboundary/external_accounts_config_providers.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/trustboundary/trust_boundary.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/version.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/oauth2adapt/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/auth/oauth2adapt/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/auth/oauth2adapt/oauth2adapt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/threelegged.gois excluded by!vendor/**vendor/cloud.google.com/go/compute/metadata/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/compute/metadata/metadata.gois excluded by!vendor/**vendor/cloud.google.com/go/compute/metadata/retry.gois excluded by!vendor/**vendor/cloud.google.com/go/iam/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/iam_policy.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/options.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/policy.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/resource_policy_member.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/internal/detect/detect.gois excluded by!vendor/**vendor/cloud.google.com/go/internal/pubsub/message.gois excluded by!vendor/**vendor/cloud.google.com/go/internal/pubsub/publish.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/auxiliary.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/auxiliary_go123.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/doc.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/helpers.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/info.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/pubsub.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/pubsub_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/schema.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/schema_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/schema_client.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/subscription_admin_client.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/topic_admin_client.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/version.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/debug.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/doc.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/flow_controller.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/distribution/distribution.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/scheduler/publish_scheduler.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/scheduler/receive_scheduler.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/version.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/iterator.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/message.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/nodebug.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/publisher.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/pubsub.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/pullstream.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/service.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/shutdown.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/subscriber.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/trace.gois excluded by!vendor/**
📒 Files selected for processing (4)
go.mod(7 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go(1 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go(8 hunks)test/integration/work/manifestworkreplicaset_test.go(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go
- go.mod
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1158
File: test/integration/work/completedmanifestwork_test.go:216-256
Timestamp: 2025-09-03T08:43:34.751Z
Learning: In TTL=0 ManifestWork deletion tests, avoid waiting for WorkComplete condition because the controller deletes the resource immediately upon completion, creating a race condition where the test tries to check completion status on an already-deleted resource.
📚 Learning: 2025-11-06T08:55:13.306Z
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1242
File: pkg/work/spoke/controllers/manifestcontroller/manifestwork_reconciler.go:88-88
Timestamp: 2025-11-06T08:55:13.306Z
Learning: In pkg/work/spoke/controllers/manifestcontroller/manifestwork_controller.go, the sync method initializes a logger with manifestWorkName and attaches it to the context before calling reconcile methods. Therefore, reconcile methods (like manifestworkReconciler.reconcile) that use klog.FromContext(ctx) automatically inherit the manifestWorkName context and do not need to add it again.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go
📚 Learning: 2025-09-03T08:43:34.751Z
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1158
File: test/integration/work/completedmanifestwork_test.go:216-256
Timestamp: 2025-09-03T08:43:34.751Z
Learning: In TTL=0 ManifestWork deletion tests, avoid waiting for WorkComplete condition because the controller deletes the resource immediately upon completion, creating a race condition where the test tries to check completion status on an already-deleted resource.
Applied to files:
test/integration/work/manifestworkreplicaset_test.go
📚 Learning: 2025-07-01T02:27:10.927Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1053
File: vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/agent/client/manifestwork.go:237-237
Timestamp: 2025-07-01T02:27:10.927Z
Learning: In OCM ManifestWork agent client, when a work is being deleted (DeletionTimestamp set and finalizers removed), the agent publishes a status update event (types.UpdateRequestAction) with ResourceDeleted condition set to True to inform the hub that deletion is complete, rather than publishing a delete request event.
Applied to files:
test/integration/work/manifestworkreplicaset_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: verify
- GitHub Check: unit
- GitHub Check: grpc-work-integration
- GitHub Check: integration
- GitHub Check: mqtt-work-integration
- GitHub Check: e2e
- GitHub Check: e2e-singleton
- GitHub Check: e2e-hosted
🔇 Additional comments (2)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go (2)
221-221: LGTM!Correctly retrieves the WorkApplied condition, consistent with how Progressing and Degraded conditions are retrieved.
224-233: Excellent documentation.The comments clearly explain the gating logic and the rationale for checking the Applied condition first to prevent using stale timestamps. This will help future maintainers understand the ordering requirements.
e6ab9c9 to
6fe8455
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
go.mod (1)
5-5: Replace directive points to personal fork (unresolved from previous review).The replace directive still uses a personal fork (
github.com/haoqing0110/sdk-go) with a pseudo-version, which was flagged in a previous review. This introduces supply chain risk and makes builds non-reproducible.Action items:
- Verify when the upstream PR to
open-cluster-management.io/sdk-gowill be released, and plan to remove this replace directive immediately after- If the change is urgent and cannot wait, add a comment in the repository (e.g., in a code or documentation file) documenting why the fork is needed, linking to the upstream PR, and creating a tracked issue to remove the replace directive once released
Once the official release is available, update line 5 to remove the replace directive and ensure the
requirestatement on line 46 uses the official semantic version.
🧹 Nitpick comments (1)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go (1)
221-241: Applied-gated rollout status check looks correct; consider syncing comments with the new Status check.The new
appliedCondretrieval and gating condition correctly ensure we only honor Progressing/Degraded when:
WorkAppliedexists,WorkApplied.ObservedGenerationmatchesmanifestWork.Generation,WorkApplied.StatusisConditionTrue,- and Progressing/Degraded (if present) are for the same generation.
This matches the intended “Applied-first” behavior and closes the stale-condition window addressed in the linked issue.
One small doc nit: the comment block above still only mentions “no Applied” and “Applied hasn’t observed the latest spec”, but the code also returns
ToApplywhenApplied.Status != True(apply failed). You may want to add a bullet like “Applied condition is False (apply failed)” so the comment fully mirrors the behavior.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (296)
go.sumis excluded by!**/*.sumvendor/cloud.google.com/go/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/auth/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/auth/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/auth/README.mdis excluded by!vendor/**vendor/cloud.google.com/go/auth/auth.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/compute.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/detect.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/doc.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/filetypes.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/aws_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/executable_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/externalaccount.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/file_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/info.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/programmatic_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/url_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/x509_provider.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/externalaccountuser/externalaccountuser.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/gdch/gdch.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/impersonate/idtoken.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/impersonate/impersonate.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/internal/stsexchange/sts_exchange.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/credentials/selfsignedjwt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/dial_socketopt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/directpath.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/grpctransport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/grpctransport/pool.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/httptransport/httptransport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/httptransport/transport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/compute.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/manufacturer.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/manufacturer_linux.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/compute/manufacturer_windows.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/credsfile/credsfile.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/credsfile/filetype.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/credsfile/parse.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/internal.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/jwt/jwt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/retry/retry.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cba.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/default_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/enterprise_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/secureconnect_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/cert/workload_cert.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/headers/headers.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/s2a.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/transport/transport.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/trustboundary/external_accounts_config_providers.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/trustboundary/trust_boundary.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/internal/version.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/oauth2adapt/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/auth/oauth2adapt/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/auth/oauth2adapt/oauth2adapt.gois excluded by!vendor/**vendor/cloud.google.com/go/auth/threelegged.gois excluded by!vendor/**vendor/cloud.google.com/go/compute/metadata/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/compute/metadata/metadata.gois excluded by!vendor/**vendor/cloud.google.com/go/compute/metadata/retry.gois excluded by!vendor/**vendor/cloud.google.com/go/iam/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/iam_policy.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/options.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/policy.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/iam/apiv1/iampb/resource_policy_member.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/internal/detect/detect.gois excluded by!vendor/**vendor/cloud.google.com/go/internal/pubsub/message.gois excluded by!vendor/**vendor/cloud.google.com/go/internal/pubsub/publish.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/CHANGES.mdis excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/LICENSEis excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/auxiliary.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/auxiliary_go123.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/doc.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/helpers.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/info.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/pubsub.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/pubsub_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/schema.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/pubsubpb/schema_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/schema_client.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/subscription_admin_client.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/topic_admin_client.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/apiv1/version.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/debug.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/doc.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/flow_controller.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/distribution/distribution.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/scheduler/publish_scheduler.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/scheduler/receive_scheduler.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/internal/version.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/iterator.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/message.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/nodebug.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/publisher.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/pubsub.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/pullstream.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/service.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/shutdown.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/subscriber.gois excluded by!vendor/**vendor/cloud.google.com/go/pubsub/v2/trace.gois excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/CHANGES.mdis excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/README.mdis excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/compress.gois excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/curly.gois excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/entity_accessors.gois excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/json.gois excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/jsoniter.gois excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/jsr311.gois excluded by!vendor/**vendor/github.com/emicklei/go-restful/v3/route.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/.cirrus.ymlis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/.editorconfigis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/.gitattributesis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/.gitignoreis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/CHANGELOG.mdis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.mdis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/README.mdis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/backend_fen.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/backend_inotify.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/backend_kqueue.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/backend_other.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/backend_windows.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/fsnotify.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/darwin.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_darwin.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_dragonfly.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_freebsd.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_kqueue.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_linux.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_netbsd.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_openbsd.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_solaris.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/debug_windows.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/freebsd.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/internal.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/unix.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/unix2.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/internal/windows.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/mkdoc.zshis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/shared.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/staticcheck.confis excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/system_bsd.gois excluded by!vendor/**vendor/github.com/fsnotify/fsnotify/system_darwin.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/README.mdis excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/bytestring.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/cache.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/common.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/decode.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/doc.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/encode.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/encode_map.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/encode_map_go117.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/omitzero_go124.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/omitzero_pre_go124.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/simplevalue.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/stream.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/structfields.gois excluded by!vendor/**vendor/github.com/fxamacker/cbor/v2/tag.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/compiler/context.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/compiler/extensions.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/compiler/helpers.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/compiler/reader.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/jsonschema/models.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/jsonschema/reader.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/jsonschema/writer.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/openapiv2/document.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.gois excluded by!vendor/**vendor/github.com/google/gnostic-models/openapiv3/document.gois excluded by!vendor/**vendor/github.com/google/s2a-go/.gitignoreis excluded by!vendor/**vendor/github.com/google/s2a-go/CODE_OF_CONDUCT.mdis excluded by!vendor/**vendor/github.com/google/s2a-go/CONTRIBUTING.mdis excluded by!vendor/**vendor/github.com/google/s2a-go/LICENSE.mdis excluded by!vendor/**vendor/github.com/google/s2a-go/README.mdis excluded by!vendor/**vendor/github.com/google/s2a-go/fallback/s2a_fallback.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/authinfo/authinfo.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/handshaker/handshaker.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/handshaker/service/service.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/proto/common_go_proto/common.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/proto/s2a_context_go_proto/s2a_context.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/proto/s2a_go_proto/s2a.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/proto/s2a_go_proto/s2a_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/proto/v2/common_go_proto/common.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/proto/v2/s2a_context_go_proto/s2a_context.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/proto/v2/s2a_go_proto/s2a.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/proto/v2/s2a_go_proto/s2a_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/aeadcrypter/aeadcrypter.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/aeadcrypter/aesgcm.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/aeadcrypter/chachapoly.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/aeadcrypter/common.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/halfconn/ciphersuite.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/halfconn/counter.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/halfconn/expander.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/internal/halfconn/halfconn.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/record.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/record/ticketsender.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/tokenmanager/tokenmanager.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/v2/README.mdis excluded by!vendor/**vendor/github.com/google/s2a-go/internal/v2/certverifier/certverifier.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/v2/remotesigner/remotesigner.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/v2/s2av2.gois excluded by!vendor/**vendor/github.com/google/s2a-go/internal/v2/tlsconfigstore/tlsconfigstore.gois excluded by!vendor/**vendor/github.com/google/s2a-go/retry/retry.gois excluded by!vendor/**vendor/github.com/google/s2a-go/s2a.gois excluded by!vendor/**vendor/github.com/google/s2a-go/s2a_options.gois excluded by!vendor/**vendor/github.com/google/s2a-go/s2a_utils.gois excluded by!vendor/**vendor/github.com/google/s2a-go/stream/s2a_stream.gois excluded by!vendor/**vendor/github.com/googleapis/enterprise-certificate-proxy/LICENSEis excluded by!vendor/**vendor/github.com/googleapis/enterprise-certificate-proxy/client/client.gois excluded by!vendor/**vendor/github.com/googleapis/enterprise-certificate-proxy/client/util/util.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/.release-please-manifest.jsonis excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/CHANGES.mdis excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/LICENSEis excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/apierror/apierror.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/apierror/internal/proto/README.mdis excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/apierror/internal/proto/custom_error.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/googleapis/gax-go/v2/apierror/internal/proto/custom_error.protois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/apierror/internal/proto/error.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/googleapis/gax-go/v2/apierror/internal/proto/error.protois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/call_option.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/callctx/callctx.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/content_type.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/gax.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/header.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/internal/version.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/internallog/grpclog/grpclog.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/internallog/internal/internal.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/internallog/internallog.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/invoke.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/iterator/iterator.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/proto_json_stream.gois excluded by!vendor/**vendor/github.com/googleapis/gax-go/v2/release-please-config.jsonis excluded by!vendor/**vendor/github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/client.gois excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/BUILD.bazelis excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/annotations.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/annotations.protois excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/annotations_protoopaque.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/buf.gen.yamlis excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2.protois excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/errors.gois excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/handler.gois excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.gois excluded by!vendor/**vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/query.gois excluded by!vendor/**vendor/github.com/modern-go/reflect2/safe_type.gois excluded by!vendor/**vendor/github.com/stoewer/go-strcase/.golangci.ymlis excluded by!vendor/**vendor/github.com/stoewer/go-strcase/camel.gois excluded by!vendor/**vendor/github.com/stoewer/go-strcase/helper.gois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/authpb/auth.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/go.etcd.io/etcd/api/v3/authpb/auth.protois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/etcdserver.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/etcdserver.protois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal.protois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal_stringer.gois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.protois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/membershippb/membership.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/go.etcd.io/etcd/api/v3/membershippb/membership.protois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/mvccpb/kv.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/go.etcd.io/etcd/api/v3/mvccpb/kv.protois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/v3rpc/rpctypes/error.gois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/v3rpc/rpctypes/metadatafields.gois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/version/version.gois excluded by!vendor/**vendor/go.etcd.io/etcd/api/v3/versionpb/version.pb.gois excluded by!**/*.pb.go,!vendor/**vendor/go.etcd.io/etcd/api/v3/versionpb/version.protois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/dir_unix.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/dir_windows.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/fileutil.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_flock.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_linux.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_solaris.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_unix.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_windows.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_darwin.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_unix.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_unsupported.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/purge.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync_darwin.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync_linux.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/logutil/log_format.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap_journal.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/tlsutil.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/keepalive_listener.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/limit_listen.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_opts.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_tls.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_solaris.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_unix.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_wasm.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_windows.gois excluded by!vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/tls.gois excluded by!vendor/**
📒 Files selected for processing (4)
go.mod(7 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go(1 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go(8 hunks)test/integration/work/manifestworkreplicaset_test.go(5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-06T08:55:13.306Z
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1242
File: pkg/work/spoke/controllers/manifestcontroller/manifestwork_reconciler.go:88-88
Timestamp: 2025-11-06T08:55:13.306Z
Learning: In pkg/work/spoke/controllers/manifestcontroller/manifestwork_controller.go, the sync method initializes a logger with manifestWorkName and attaches it to the context before calling reconcile methods. Therefore, reconcile methods (like manifestworkReconciler.reconcile) that use klog.FromContext(ctx) automatically inherit the manifestWorkName context and do not need to add it again.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go
📚 Learning: 2025-07-23T10:10:42.066Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1086
File: test/integration/util/grpc.go:146-146
Timestamp: 2025-07-23T10:10:42.066Z
Learning: In OCM codebase, there are two different GRPCServerOptions types: the local one in pkg/server/grpc/options.go (which only has GRPCServerConfig field) and the SDK one from open-cluster-management.io/sdk-go/pkg/cloudevents/server/grpc/options (which has ServerBindPort and other fields with default values). Test code uses the SDK version via grpcoptions import alias.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.gogo.mod
📚 Learning: 2025-09-03T08:43:34.751Z
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1158
File: test/integration/work/completedmanifestwork_test.go:216-256
Timestamp: 2025-09-03T08:43:34.751Z
Learning: In TTL=0 ManifestWork deletion tests, avoid waiting for WorkComplete condition because the controller deletes the resource immediately upon completion, creating a race condition where the test tries to check completion status on an already-deleted resource.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.gotest/integration/work/manifestworkreplicaset_test.go
📚 Learning: 2025-07-01T02:27:10.927Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1053
File: vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/agent/client/manifestwork.go:237-237
Timestamp: 2025-07-01T02:27:10.927Z
Learning: In OCM ManifestWork agent client, when a work is being deleted (DeletionTimestamp set and finalizers removed), the agent publishes a status update event (types.UpdateRequestAction) with ResourceDeleted condition set to True to inform the hub that deletion is complete, rather than publishing a delete request event.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.gotest/integration/work/manifestworkreplicaset_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: mqtt-work-integration
- GitHub Check: grpc-work-integration
- GitHub Check: e2e
- GitHub Check: e2e-hosted
- GitHub Check: e2e-singleton
- GitHub Check: integration
- GitHub Check: unit
- GitHub Check: build
- GitHub Check: verify
🔇 Additional comments (4)
go.mod (1)
34-39: I need to verify the version changes in go.mod and research compatibility issues.Now I need to check the go.mod file to confirm the exact version changes being made:
Based on my verification research, I can now provide the rewritten review comment:
No blocking compatibility issues found; however, verify test execution with these versions.
Kubernetes v1.34 will not include any removal or deprecation, focusing on enhancements. controller-runtime v0.22 is designed for k8s.io/* v0.34, and controller-runtime v0.22.3 includes a bump to k8s.io/* v0.34.1—the version pairing in this PR aligns with upstream expectations.
The codebase's extensive use of Kubernetes client libraries (k8s.io/api, k8s.io/apimachinery, k8s.io/client-go, k8s.io/apiserver, k8s.io/component-base) is standard and no version-specific TODOs or deprecation warnings were found in the code.
Recommended next step: Confirm the integration test suite passes with these updated versions to validate the reconciliation logic for
ManifestWorkReplicaSetfunctions correctly.pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go (2)
594-601: Good: ProgressDeadline test now models Applied-first gating.Setting
WorkAppliedtoTruewithObservedGeneration == mw.Generationbefore Progressing/Degraded inTestRequeueWithProgressDeadlinealigns the fixture with the new controller gating and ensures the rollout timeout is exercised only after successful apply.
902-908: ClusterRolloutStatusFunc tests correctly incorporate WorkApplied precondition.The added
WorkApplied=Trueconditions (with matchingObservedGeneration) in the non‑ToApply scenarios make the table-driven tests accurately reflect the new rollout-status contract:
- Gating on Applied before interpreting Progressing/Degraded.
- Ensuring status/LastTransitionTime expectations are validated only when the work is known-applied for the current generation.
This is a solid update that protects these tests from future regressions in the gating logic.
Also applies to: 933-939, 971-977, 1009-1015, 1047-1053, 1079-1084, 1117-1122
test/integration/work/manifestworkreplicaset_test.go (1)
389-394: Integration rollout tests now correctly require Applied=True before agent conditions.Across the progressive, max‑failure, min‑success‑time, and deadline‑tolerating tests, the new
WorkAppliedconditions (Status=True,ObservedGeneration == workCopy.Generation) are set before Progressing/Degraded. This matches the controller’s Applied-first gating and ensures these scenarios are exercising the intended rollout behavior, not relying on pre‑Applied agent conditions.Also applies to: 413-418, 456-461, 518-525, 593-600
6fe8455 to
c146fb8
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go (1)
902-908: Consistent additions, but consider edge case coverage.The WorkApplied conditions are consistently added to all test cases where Progressing has matching ObservedGeneration. This correctly validates the new gating logic.
However, based on issue #1237's objectives about preventing premature rollout advancement, consider adding test cases for these edge scenarios:
- WorkApplied with Status=False
- WorkApplied with ObservedGeneration < current Generation (stale Applied condition)
- Progressing=False with old ObservedGeneration but newer WorkApplied.ObservedGeneration
These would verify the production code properly handles stale or inconsistent Applied conditions to prevent the rollout gaps mentioned in the issue.
Also applies to: 933-939, 971-977, 1009-1015, 1047-1053, 1078-1084, 1116-1122
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go(1 hunks)pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go(8 hunks)test/integration/work/manifestworkreplicaset_test.go(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_reconcile.go
- test/integration/work/manifestworkreplicaset_test.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1158
File: test/integration/work/completedmanifestwork_test.go:216-256
Timestamp: 2025-09-03T08:43:34.751Z
Learning: In TTL=0 ManifestWork deletion tests, avoid waiting for WorkComplete condition because the controller deletes the resource immediately upon completion, creating a race condition where the test tries to check completion status on an already-deleted resource.
📚 Learning: 2025-09-03T08:43:34.751Z
Learnt from: qiujian16
Repo: open-cluster-management-io/ocm PR: 1158
File: test/integration/work/completedmanifestwork_test.go:216-256
Timestamp: 2025-09-03T08:43:34.751Z
Learning: In TTL=0 ManifestWork deletion tests, avoid waiting for WorkComplete condition because the controller deletes the resource immediately upon completion, creating a race condition where the test tries to check completion status on an already-deleted resource.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go
📚 Learning: 2025-07-01T02:27:10.927Z
Learnt from: skeeey
Repo: open-cluster-management-io/ocm PR: 1053
File: vendor/open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/agent/client/manifestwork.go:237-237
Timestamp: 2025-07-01T02:27:10.927Z
Learning: In OCM ManifestWork agent client, when a work is being deleted (DeletionTimestamp set and finalizers removed), the agent publishes a status update event (types.UpdateRequestAction) with ResourceDeleted condition set to True to inform the hub that deletion is complete, rather than publishing a delete request event.
Applied to files:
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: build
- GitHub Check: integration
- GitHub Check: unit
- GitHub Check: verify
- GitHub Check: e2e-singleton
- GitHub Check: e2e
- GitHub Check: e2e-hosted
- GitHub Check: grpc-work-integration
- GitHub Check: mqtt-work-integration
🔇 Additional comments (1)
pkg/work/hub/controllers/manifestworkreplicasetcontroller/manifestworkreplicaset_deploy_test.go (1)
594-601: LGTM! WorkApplied condition correctly added.The addition of the WorkApplied condition with matching ObservedGeneration correctly simulates a work that has been applied before transitioning to Progressing/Degraded states. This aligns with the PR objective to gate rollout status evaluation on the Applied condition.
c146fb8 to
4cb6735
Compare
|
@haoqing0110 do we have a clearer doc on rollingout and how conditions determine each step? Is this still consistent with what you wrote in enhancement proposal? |
|
/unhold |
|
@qiujian16 yes, need to update the manfiestwork rollout condition requirements part. @youngbupark this PR may have a little effect on https://github.com/open-cluster-management-io/enhancements/pull/160/files , need to add a column for Applied, but it does not affect the overall design. |
| // - Degraded condition exists but hasn't observed the latest spec | ||
| // (Degraded is optional, but if it exists, we wait for it to catch up) | ||
| if progressingCond == nil || | ||
| // |
There was a problem hiding this comment.
move this to a separated func and build ut around it.
This if branch is too complicated, try to simplify it to make easier to read in the separated func.
There was a problem hiding this comment.
Fixed. Add a new shouldReturnToApply func.
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Qing Hao <qhao@redhat.com>
4cb6735 to
2176f60
Compare
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: haoqing0110, qiujian16 The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
26edb94
into
open-cluster-management-io:main
🤖 Generated with Claude Code
Summary
Related issue(s)
Fixes #1237
Summary by CodeRabbit
Bug Fixes
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.