Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5873bb3
feat: [FEP-0001] support annotation-based placement (#829)
ytimocin Sep 22, 2026
000611f
interface: [FEP-0001] add APIs for stage update run APIs for placemen…
michaelawyu Sep 23, 2026
51a058f
feat: support vendor-neutral hub kubeconfig authentication for member…
ssijbabu Sep 24, 2026
a0a38bd
test: add scheduled-to-bound coverage for TestPickBindingsToRoll (#847)
akshita317 Sep 24, 2026
6baf58f
fix: report negative label indexes without a nil wrapped error (#939)
akshita317 Sep 28, 2026
6221437
chore: bump the all-others group across 1 directory with 4 updates (#…
dependabot[bot] Oct 1, 2026
54c554f
chore: bump codecov/codecov-action from 7.1.0 to 7.1.1 (#934)
dependabot[bot] Oct 1, 2026
2e6ad57
feat: [FEP-0001] use field indexes in the new work generator (#942)
michaelawyu Oct 5, 2026
a025570
feat: [FEP-0001] add work applier (1/) (#911)
michaelawyu Oct 6, 2026
d5b45d4
docs: update security vulnerability instructions (#952)
sjwaight Oct 7, 2026
ea05fcb
fix: surface unknown eviction conditions in draincluster (#767)
akshita317 Oct 7, 2026
29f4f73
fix: preserve update run transition timestamp (#935)
britaniar Oct 8, 2026
8a562ff
fix: preserve placement status metric timestamps (#936)
britaniar Oct 8, 2026
546bfef
feat: support before-stage tasks on the staged update delete stage (#…
Copilot Oct 8, 2026
0d770fe
fix: update distroless base for OpenSSL CVEs (#963)
Copilot Oct 8, 2026
58812a2
chore: bump step-security/harden-runner from 2.21.1 to 2.22.0 (#958)
dependabot[bot] Oct 9, 2026
d059a7c
chore: bump thehanimo/pr-title-checker from 1.4.3 to 2.0.0 (#956)
dependabot[bot] Oct 12, 2026
74427b8
test: [FEP-0001] integration tests for the new work generator (with m…
michaelawyu Oct 12, 2026
272e1c1
fix: address a faulty method call in the code base caused by earlier …
michaelawyu Oct 12, 2026
b319982
fix: remediate Go CVEs from 2026-W42 scan (#969)
Copilot Oct 12, 2026
75b1411
Merge remote-tracking branch 'cncf/main' into britaniar-sync-upstream…
britaniar Oct 12, 2026
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
42 changes: 42 additions & 0 deletions .github/.copilot/breadcrumbs/2026-08-12-2353-delete-stage-tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Support Before-Stage Tasks on the Delete Stage

## Overview

Allow staged update strategies to gate the delete stage with an Approval task, the same before-stage task the
update stages support, so that removing the resources from the clusters that left a placement can be held until
a sign-off is given.

## Plan

1. Add an optional `deleteStage` to the shared update strategy API, with `beforeStageTasks`.
2. Snapshot the tasks and initialize their statuses as the `beforeStageTaskStatus` of the delete stage.
3. Evaluate the tasks with the same code that evaluates the before-stage tasks of the update stages.
4. Add API validation, unit, integration and E2E coverage for both cluster-scoped and namespaced runs.
5. Regenerate API code and CRDs, then run the tests and the repository quality checks.

## Decisions

- The delete stage is configured as a stage (`deleteStage.beforeStageTasks`) rather than with a delete stage
specific task list, so that the API, the status and the approval requests use the same vocabulary as the
update stages, and so that the delete stage can take more configurations later.
- The before-stage tasks of the delete stage follow the update stages: at most one task, and it must be an
Approval. A soak time before the removal is already possible with a TimedWait in the afterStageTasks of the
last update stage, as the delete stage only starts once the last update stage, including its after-stage tasks,
completes.
- The approval request is named `<updateRun>-before-delete-stage` with the generic before-stage name format.
`delete-stage` also is the stage label value of the approval request, as the name of the delete stage,
`kubernetes-fleet.io/deleteStage`, is not a valid label value. An update stage cannot have this name.
- The tasks only gate the start of the deletion and they are skipped if there is no cluster to delete, the same
as an update stage with no clusters.
- An update run that is stopped while it waits for the tasks stops without deleting any binding.

## Success Criteria

- [x] No delete stage configuration preserves immediate deletion.
- [x] The approval must be given before any binding is deleted.
- [x] Cluster-scoped and namespaced runs keep the bindings until the tasks pass.
- [x] Generated API and CRD artifacts are current.

## Follow-ups

- Document the delete stage configuration in the staged update docs (website repository).
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Cleanup Leftover Works

## Goal

Implement `cleanupLeftOverWorks` so manifests previously applied or prepared by leftover Work objects are removed unless still referenced by the current linked Work set.

## Plan

- [x] Collect manifest identifiers whose Applied condition is true or has the preparing-to-process reason.
- [x] Exclude identifiers present in `seenManifIDs`.
- [x] Remove the collected manifests in parallel through `removeOneLeftOverManifest`.
- [x] Aggregate per-manifest failures and run focused validation.

## Success Criteria

- Current manifests are preserved.
- Only previously applied or pre-processed leftover manifests are selected.
- Selected manifests are removed through the existing ownership-aware helper in one parallel batch.

## Validation

- `gofmt` completed successfully.
- Editor diagnostics report no errors in `cleanup.go`.
- `go test ./pkg/v1/controllers/workapplier` is currently blocked by pre-existing unused `leftOverWorks` and `seenManifestIDs` variables in `controller.go`, where the surrounding cleanup call site is still unfinished.

## Placement Deletion Follow-Up

- [ ] Preserve explicit sync strategy values while applying defaults.
- [ ] Delete linked AppliedWork objects with foreground or orphan propagation according to `WhenPlacementDeleted`.
- [ ] After `foregroundDeletionWaitTime`, explicitly clean tracked manifests blocking foreground deletion.
- [ ] Remove Work cleanup finalizers only after the corresponding AppliedWork is gone.
- [ ] Wire placement and leftover cleanup into reconciliation and validate the package.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Fix: Preserve UpdateRun Condition Transition Timestamp

## Overview

Prevent hub-agent reconciliation and restarts from restamping the UpdateRun status metric when the selected Kubernetes condition has not transitioned.

## Plan

1. Trace UpdateRun condition reconciliation and metric emission.
2. Emit the selected condition's persisted `LastTransitionTime` instead of reconciliation time.
3. Add focused unit tests for unchanged failed conditions and actual status transitions.
4. Run the smallest relevant UpdateRun unit test suite.

## Success Criteria

- [x] Re-emitting an unchanged failed condition preserves the metric timestamp.
- [x] A condition status transition emits the new transition timestamp.
- [x] Focused UpdateRun tests pass.

## Implementation Notes

- Kubernetes `meta.SetStatusCondition` already preserves `LastTransitionTime` when `Status` is unchanged and advances it when `Status` changes.
- The restamping occurred in metric emission through `SetToCurrentTime()`, independently of the persisted condition.
- `emitUpdateRunStatusMetric` now sets the gauge from the selected condition's `LastTransitionTime`.
- Added table-driven unit coverage for failed, initialized, executing, and succeeded runs. Each case emits twice to prove reconciliation does not restamp the selected condition's timestamp.
- Validation: `go test ./pkg/controllers/updaterun -run 'TestEmitUpdateRunStatusMetricUsesConditionTransitionTime|TestDetermineFailureType|TestIsFailureReason' -count=1`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Add Work Owner Binding Field Index

**Date**: October 2, 2026 00:01 AEST
**Task**: Add a composite field index for Work objects keyed by owner binding namespace and name.

## Requirements

1. Add the `workOwnedByBinding` custom field.
2. Format indexed values as `%s/%s`.
3. Extract the owner namespace from `WorkOwnerNamespaceLabelKey`.
4. Extract the owner binding name from `WorkOwnedByPlacementBindingLabelKey`.
5. Register the Work field extractor in `SetupWithHubControllerManager`.
6. Use the custom field index when listing Work objects by owner binding.
7. Use the existing owner-and-index custom field when listing secondary placement resource snapshots.

## Plan

1. Add exported field-name and value-format constants to `pkg/v1/utils/fieldindexers/hub.go`.
2. Add a Work owner-binding field extractor using the owner namespace and binding labels, while distinguishing a missing owner-namespace label from a valid empty cluster-scoped namespace.
3. Register the extractor for `placementv1alpha1.Work` in `SetupWithHubControllerManager`.
4. Update `listWorksByOwnerBinding` to query the composite custom field while retaining the Work namespace filter.
5. Update secondary placement resource snapshot retrieval to query the existing composite owner-and-index field.
6. Run the targeted field-indexer and workgenerator package tests and formatting.

## Decisions

1. Use the owner-binding label as the indexed binding-name source.
2. Permit an empty owner namespace only when the owner-namespace label is present, preserving support for cluster-scoped bindings.
3. Unit tests are omitted for now at the user's request.

## Status

Implementation complete.

## Validation

- `gofmt -w pkg/v1/utils/fieldindexers/hub.go pkg/v1/controllers/workgenerator/retrieval.go`
- `go test ./pkg/v1/utils/fieldindexers ./pkg/v1/controllers/workgenerator` (passed; packages currently have no test files)
- `git diff --check` (passed)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Implementation: October 2026 KubeFleet Main Sync

## Overview

Pull the latest changes from `upstream` (`Azure/fleet`) and merge the latest `cncf` (`kubefleet-dev/kubefleet`) main branch into `Azure/fleet` while preserving Fleet-specific module paths, build settings, Azure Linux distroless bases, and linting standards.

## Key Changes & Resolutions

1. **Upstream fast-forward**: Fast-forwarded branch to `upstream/main` (`23641d78 fix: protect reconcile labels on managed jobs and deployments (#1362)`).
2. **CNCF merge**: Merged `cncf/main` (commit `b3199824`).
3. **Dockerfiles**: Preserved Azure Linux 3.0 distroless base (`mcr.microsoft.com/azurelinux/distroless/base:3.0`) for `docker/hub-agent.Dockerfile`, `docker/member-agent.Dockerfile`, and `docker/refresh-token.Dockerfile` for FIPS compatibility.
4. **Module paths**: Rewrote all incoming `github.com/kubefleet-dev/kubefleet` module references in 31 Go files to `go.goms.io/fleet`.
5. **Conflict resolutions**:
- `apis/placement/v1alpha1/zz_generated.deepcopy.go`: Kept existing `go.goms.io/fleet/apis/placement/v1beta1` import.
- `go.mod` / `go.sum`: Preserved Azure Fleet's `github.com/grpc-ecosystem/grpc-gateway/v2` dependency, accepted updated `ginkgo`/`gomega` and dependency bumps, ran `go mod tidy`.
- `pkg/controllers/placement/controller_integration_test.go`: Adapted to CNCF's label-based sorting and removed `metricsutils`, referencing `go.goms.io/fleet`.
- `pkg/controllers/placement/controller_test.go`: Added `hubmetrics` with `go.goms.io/fleet` module prefix.
- `pkg/controllers/updaterun/controller_integration_test.go`: Adapted to condition transition timestamp assertions with `go.goms.io/fleet` imports.
- `pkg/controllers/updaterun/execution_test.go`: Added controller utils import with `go.goms.io/fleet` prefix.
- `pkg/utils/apiresources.go`: Disabled `kfplacementv1alpha1.GroupVersion.Group` placement with `go.goms.io/fleet` prefix.
- `pkg/utils/labels/labels_test.go`: Retained both `TestExtractLabelFromMemberCluster` and CNCF's new error message and policy index label tests.
- `pkg/v1/controllers/workgenerator/retrieval.go`: Added `fieldindexers` import with `go.goms.io/fleet` prefix.
- `tools/fleet/cmd/draincluster/draincluster_test.go`: Added `condition` import with `go.goms.io/fleet` prefix.
- `SECURITY.md`: Trimmed trailing whitespace on reporting section.

## Verification

- `go build ./...` passed.
- `go vet ./...` passed.
- Unit tests in `pkg/utils/labels`, `tools/fleet/cmd/draincluster`, `pkg/controllers/placement`, and `pkg/controllers/updaterun` passed.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
KUBEFLEET_CI_TEST_RUNNER_NAME: 'ginkgo'

- name: Upload Codecov report
uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
## Repository upload token - get it from codecov.io. Required only for private repositories
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-title-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: thehanimo/pr-title-checker@7fbfe05602bdd86f926d3fb3bccb6f3aed43bc70 # v1.4.3
- uses: thehanimo/pr-title-checker@b69bc3a841cf4e3cfef2d822625d8c90a588e9da # v2.0.0
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
pass_on_octokit_error: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ jobs:
# follow-up issue). Auditing egress at least records what it reaches out
# to until that action is replaced.
- name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/workflow-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@351661ca32ac09a36dc5ee2d536e3128f2a3c8ed # v2.22.0
with:
egress-policy: audit

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ crd-package: ## Package the raw CRDs into a release tarball with a SHA-256 check
@echo "Packaged CRDs into $(CRD_PACKAGE_DIR)/$(CRD_PACKAGE_NAME).tgz"

.PHONY: crd-verify
crd-verify: ## Verify the chart CRD directories cover every CRD in config/crd/bases; note (chenyu1): kubefleet.dev CRDs are ignored for now until the implementation is completed.
crd-verify: ## Verify the chart CRD directories cover every CRD in config/crd/bases; note (chenyu1): placement.kubefleet.dev and rollout.kubefleet.dev CRDs are ignored for now until the implementation is completed.
@bases="$$(mktemp)"; charts="$$(mktemp)"; \
ls config/crd/bases/ | grep -v '^placement\.kubefleet\.dev' | sort > "$$bases"; \
ls config/crd/bases/ | grep -Ev '^(placement|rollout)\.kubefleet\.dev' | sort > "$$bases"; \
{ ls charts/hub-agent/templates/crds/; ls charts/member-agent/templates/crds/; } | sort > "$$charts"; \
missing="$$(comm -3 "$$bases" "$$charts")"; \
rm -f "$$bases" "$$charts"; \
Expand Down
12 changes: 7 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,18 @@ being finalized:
(see the [CNCF TAG-Security `SECURITY.md` template](https://github.com/cncf/tag-security)
for the conventional `cncf-<project>-distributors-announce@lists.cncf.io` form). Whether
KubeFleet stands one up depends on demonstrated downstream demand.
- **GitHub private vulnerability reporting:** to be enabled on this repository as the
preferred reporting channel; the maintainer mailing list (see below) remains the fallback
until it is.
- **GitHub private vulnerability reporting:** report them via the KubeFleet Security Advisories [Report a vulnerability](https://github.com/kubefleet-dev/kubefleet/security/advisories/new) feature.

This section will be updated as each item is decided.

## Reporting Security Issues

**Please do not report security vulnerabilities through public GitHub issues.** Instead,
report them to the [KubeFleet maintainers](mailto:kubefleet@microsoft.com).
**Please do not report security vulnerabilities through public GitHub issues.**

Instead, report them via the KubeFleet Security Advisories [Report a vulnerability](https://github.com/kubefleet-dev/kubefleet/security/advisories/new) feature.

You can also follow up with the [KubeFleet maintainers](mailto:kubefleet@microsoft.com).

We prefer all communications to be in English.

You should receive a response as soon as possible. If for some reason you do not, please
Expand Down
2 changes: 1 addition & 1 deletion apis/cluster/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions apis/kubefleet.dev/placement/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,61 @@ const (
WorkKind = "Work"
)

// The annotation and label keys that KubeFleet reserves for the placement APIs.
//
// Note that these keys carry the KubeFleet domain itself rather than the API group; they are
// set on arbitrary Kubernetes objects, not only on the objects of this group.
const (
// KubeFleetPrefix is the domain that prefixes every reserved annotation and label key below,
// per the Kubernetes convention that reserves unprefixed keys for end users.
KubeFleetPrefix = "kubefleet.dev/"

// ClusterSelectorsAnnotation is the annotation that requests annotation-based placement for the
// resource it is set on. Its value is a semicolon-separated list of cluster selectors, each of
// which is a comma-separated list of LABEL_KEY=LABEL_VALUE label matchers with an optional
// count=N|All directive, e.g.:
//
// kubefleet.dev/cluster-selectors: "env=staging,count=All;env=canary,region=eastus,count=1"
//
// KubeFleet keeps a PlacementPolicy (or ClusterPlacementPolicy) object in sync with the
// annotation for as long as it is present.
//
// The key names what the value holds rather than what KubeFleet does with it, matching the
// clusterSelectors field of the generated policy: the annotation is one way to write that field,
// and the two are read together often enough that they should not have to be translated.
//
// Within the annotation, `region` may be used in place of the well-known
// topology.kubernetes.io/region label key, and `alias` in place of the cluster alias label
// (placement/v1beta1's ClusterAliasLabel).
ClusterSelectorsAnnotation = KubeFleetPrefix + "cluster-selectors"
)

// The labels that record, on a placement policy KubeFleet generated from an annotation, the
// resource whose annotation caused it to exist.
//
// These keys carry the API group rather than the bare KubeFleet domain, since unlike the keys
// above they are set on objects of this group.
//
// The labels exist so that a policy can be found with a List when its generated name is not known;
// the owner reference on the policy remains the authoritative record of where it came from. Note
// that ParentNameLabel is lossy: the name of a resource can run to 253 bytes while a label value
// stops at 63, so for a longer name the label holds a prefix and a hash instead, and selecting on
// it with the resource's own name matches nothing.
const (
// ParentAPIGroupLabel holds the API group of the resource a policy was generated from. It is
// the empty string for resources in the core API group, and is always present, so that
// core-group resources can be selected as readily as any other.
ParentAPIGroupLabel = "placement.kubefleet.dev/parent-api-group"

// ParentKindLabel holds the kind of the resource a policy was generated from, spelled as the
// kind itself is (Deployment, not deployment).
ParentKindLabel = "placement.kubefleet.dev/parent-kind"

// ParentNameLabel holds the name of the resource a policy was generated from, shortened if it
// does not fit in a label value.
ParentNameLabel = "placement.kubefleet.dev/parent-name"
)

type ObjectReference struct {
// The namespace of the referenced object.
//
Expand Down
5 changes: 5 additions & 0 deletions apis/kubefleet.dev/placement/v1alpha1/gvk_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@ var (
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)

const (
// The kind names for all the custom resources in this GV.
AppliedWorkKind = "AppliedWork"
)
Loading
Loading