Skip to content

Commit cf4894d

Browse files
committed
Merge remote-tracking branch 'cncf/main' into backportMarch
2 parents 7c3af36 + 43438fa commit cf4894d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+4308
-668
lines changed

.github/workflows/code-lint.yml

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ on:
1414

1515
env:
1616
# Common versions
17-
GO_VERSION: '1.24.13'
17+
GO_VERSION: "1.24.13"
1818

1919
jobs:
20-
2120
detect-noop:
2221
runs-on: ubuntu-latest
2322
outputs:
@@ -58,13 +57,33 @@ jobs:
5857
contents: read
5958

6059
steps:
61-
- name: Set up Go ${{ env.GO_VERSION }}
62-
uses: actions/setup-go@v6
63-
with:
64-
go-version: ${{ env.GO_VERSION }}
60+
- name: Set up Go ${{ env.GO_VERSION }}
61+
uses: actions/setup-go@v6
62+
with:
63+
go-version: ${{ env.GO_VERSION }}
6564

66-
- name: Check out code into the Go module directory
67-
uses: actions/checkout@v6.0.2
65+
- name: Check out code into the Go module directory
66+
uses: actions/checkout@v6.0.2
67+
68+
- name: golangci-lint
69+
run: make lint
70+
71+
helm-lint:
72+
name: "Helm Lint"
73+
runs-on: ubuntu-latest
74+
needs: detect-noop
75+
if: needs.detect-noop.outputs.noop != 'true'
76+
77+
steps:
78+
- name: Check out code
79+
uses: actions/checkout@v6.0.2
80+
81+
- name: Set up Helm
82+
uses: azure/setup-helm@v4
83+
with:
84+
version: v3.17.0
6885

69-
- name: golangci-lint
70-
run: make lint
86+
- name: Lint Helm charts
87+
run: |
88+
helm lint charts/hub-agent
89+
helm lint charts/member-agent

apis/cluster/v1beta1/membercluster_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ type MemberClusterStatus struct {
141141
// +optional
142142
ResourceUsage ResourceUsage `json:"resourceUsage,omitempty"`
143143

144+
// Namespaces is a map of namespace names to their associated work names for namespaces
145+
// that are managed by Fleet (i.e., have AppliedWork owner references when created).
146+
// The key is the namespace name and the value is the work name from the AppliedWork owner reference.
147+
// If the namespace does not have an AppliedWork owner reference, the value will be an empty string.
148+
// This field is copied from the corresponding InternalMemberCluster object.
149+
// +optional
150+
Namespaces map[string]string `json:"namespaces,omitempty"`
151+
144152
// AgentStatus is an array of current observed status, each corresponding to one member agent running in the member cluster.
145153
// +optional
146154
AgentStatus []AgentStatus `json:"agentStatus,omitempty"`

apis/cluster/v1beta1/zz_generated.deepcopy.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/hubagent/main.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,29 +103,31 @@ func main() {
103103
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
104104

105105
config := ctrl.GetConfigOrDie()
106-
config.QPS, config.Burst = float32(opts.HubQPS), opts.HubBurst
106+
config.QPS, config.Burst = float32(opts.CtrlMgrOpts.HubQPS), opts.CtrlMgrOpts.HubBurst
107107

108108
mgrOpts := ctrl.Options{
109109
Scheme: scheme,
110110
Cache: cache.Options{
111-
SyncPeriod: &opts.ResyncPeriod.Duration,
111+
SyncPeriod: &opts.CtrlMgrOpts.ResyncPeriod.Duration,
112112
DefaultTransform: cache.TransformStripManagedFields(),
113113
},
114-
LeaderElection: opts.LeaderElection.LeaderElect,
115-
LeaderElectionID: opts.LeaderElection.ResourceName,
116-
LeaderElectionNamespace: opts.LeaderElection.ResourceNamespace,
117-
LeaderElectionResourceLock: opts.LeaderElection.ResourceLock,
118-
HealthProbeBindAddress: opts.HealthProbeAddress,
114+
LeaderElection: opts.LeaderElectionOpts.LeaderElect,
115+
LeaderElectionID: "136224848560.hub.fleet.azure.com",
116+
LeaderElectionNamespace: opts.LeaderElectionOpts.ResourceNamespace,
117+
LeaseDuration: &opts.LeaderElectionOpts.LeaseDuration.Duration,
118+
RenewDeadline: &opts.LeaderElectionOpts.RenewDeadline.Duration,
119+
RetryPeriod: &opts.LeaderElectionOpts.RetryPeriod.Duration,
120+
HealthProbeBindAddress: opts.CtrlMgrOpts.HealthProbeBindAddress,
119121
Metrics: metricsserver.Options{
120-
BindAddress: opts.MetricsBindAddress,
122+
BindAddress: opts.CtrlMgrOpts.MetricsBindAddress,
121123
},
122124
WebhookServer: ctrlwebhook.NewServer(ctrlwebhook.Options{
123125
Port: FleetWebhookPort,
124126
CertDir: webhook.FleetWebhookCertDir,
125127
}),
126128
}
127-
if opts.EnablePprof {
128-
mgrOpts.PprofBindAddress = fmt.Sprintf(":%d", opts.PprofPort)
129+
if opts.CtrlMgrOpts.EnablePprof {
130+
mgrOpts.PprofBindAddress = fmt.Sprintf(":%d", opts.CtrlMgrOpts.PprofPort)
129131
}
130132
mgr, err := ctrl.NewManager(config, mgrOpts)
131133
if err != nil {
@@ -134,13 +136,13 @@ func main() {
134136
}
135137

136138
klog.V(2).InfoS("starting hubagent")
137-
if opts.EnableV1Beta1APIs {
139+
if opts.FeatureFlags.EnableV1Beta1APIs {
138140
klog.Info("Setting up memberCluster v1beta1 controller")
139141
if err = (&mcv1beta1.Reconciler{
140142
Client: mgr.GetClient(),
141-
NetworkingAgentsEnabled: opts.NetworkingAgentsEnabled,
142-
MaxConcurrentReconciles: int(math.Ceil(float64(opts.MaxFleetSizeSupported) / 100)), //one member cluster reconciler routine per 100 member clusters
143-
ForceDeleteWaitTime: opts.ForceDeleteWaitTime.Duration,
143+
NetworkingAgentsEnabled: opts.ClusterMgmtOpts.NetworkingAgentsEnabled,
144+
MaxConcurrentReconciles: int(math.Ceil(float64(opts.PlacementMgmtOpts.MaxFleetSize) / 100)), //one member cluster reconciler routine per 100 member clusters
145+
ForceDeleteWaitTime: opts.ClusterMgmtOpts.ForceDeleteWaitTime.Duration,
144146
}).SetupWithManager(mgr, "membercluster-controller"); err != nil {
145147
klog.ErrorS(err, "unable to create v1beta1 controller", "controller", "MemberCluster")
146148
exitWithErrorFunc()
@@ -156,7 +158,7 @@ func main() {
156158
exitWithErrorFunc()
157159
}
158160

159-
if opts.EnableWebhook {
161+
if opts.WebhookOpts.EnableWebhooks {
160162
// Generate webhook configuration with certificates
161163
webhookConfig, err := webhook.NewWebhookConfigFromOptions(mgr, opts, FleetWebhookPort)
162164
if err != nil {
@@ -173,7 +175,7 @@ func main() {
173175
// When using cert-manager, add a readiness check to ensure CA bundles are injected before marking ready.
174176
// This prevents the pod from accepting traffic before cert-manager has populated the webhook CA bundles,
175177
// which would cause webhook calls to fail.
176-
if opts.UseCertManager {
178+
if opts.WebhookOpts.UseCertManager {
177179
if err := mgr.AddReadyzCheck("cert-manager-ca-injection", func(req *http.Request) error {
178180
return webhookConfig.CheckCAInjection(req.Context())
179181
}); err != nil {
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
Copyright 2025 The KubeFleet Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package options
18+
19+
import (
20+
"flag"
21+
"fmt"
22+
"time"
23+
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
)
26+
27+
// ClusterManagementOptions is a set of options the KubeFleet hub agent exposes for
28+
// managing member clusters.
29+
type ClusterManagementOptions struct {
30+
// Expect that Fleet networking agents have been installed in the fleet or not. If set to true,
31+
// the hub agent will start to expect heartbeats from the networking agents on the member cluster related
32+
// resources.
33+
NetworkingAgentsEnabled bool
34+
35+
// The duration the KubeFleet hub agent will wait for new heartbeats before marking a member cluster as unhealthy.
36+
UnhealthyThreshold metav1.Duration
37+
38+
// The duration the KubeFleet hub agent will wait before force-deleting a member cluster resource after it has been
39+
// marked for deletion.
40+
ForceDeleteWaitTime metav1.Duration
41+
}
42+
43+
// AddFlags adds flags for ClusterManagementOptions to the specified FlagSet.
44+
func (o *ClusterManagementOptions) AddFlags(flags *flag.FlagSet) {
45+
flags.BoolVar(
46+
&o.NetworkingAgentsEnabled,
47+
"networking-agents-enabled",
48+
false,
49+
"Expect that Fleet networking agents have been installed in the fleet or not. If set to true, the hub agent will start to expect heartbeats from the networking agents on the member cluster related resources.",
50+
)
51+
52+
flags.Var(
53+
newClusterUnhealthyThresholdValueWithValidation(60*time.Second, &o.UnhealthyThreshold),
54+
"cluster-unhealthy-threshold",
55+
"The duration the KubeFleet hub agent will wait for new heartbeats before marking a member cluster as unhealthy. Defaults to 60 seconds. Must be a duration in the range [30s, 1h].",
56+
)
57+
58+
flags.Var(
59+
newForceDeleteWaitTimeValueWithValidation(15*time.Minute, &o.ForceDeleteWaitTime),
60+
"force-delete-wait-time",
61+
"The duration the KubeFleet hub agent will wait before force-deleting a member cluster resource after it has been marked for deletion. Defaults to 15 minutes. Must be a duration in the range [30s, 1h].",
62+
)
63+
}
64+
65+
// A list of flag variables that allow pluggable validation logic when parsing the input args.
66+
67+
type ClusterUnhealthyThresholdValueWithValidation metav1.Duration
68+
69+
func (v *ClusterUnhealthyThresholdValueWithValidation) String() string {
70+
return v.Duration.String()
71+
}
72+
73+
func (v *ClusterUnhealthyThresholdValueWithValidation) Set(s string) error {
74+
duration, err := time.ParseDuration(s)
75+
if err != nil {
76+
return fmt.Errorf("failed to parse duration: %w", err)
77+
}
78+
if duration < 30*time.Second || duration > time.Hour {
79+
return fmt.Errorf("duration must be in the range [30s, 1h]")
80+
}
81+
v.Duration = duration
82+
return nil
83+
}
84+
85+
func newClusterUnhealthyThresholdValueWithValidation(defaultVal time.Duration, p *metav1.Duration) *ClusterUnhealthyThresholdValueWithValidation {
86+
p.Duration = defaultVal
87+
return (*ClusterUnhealthyThresholdValueWithValidation)(p)
88+
}
89+
90+
type ForceDeleteWaitTimeValueWithValidation metav1.Duration
91+
92+
func (v *ForceDeleteWaitTimeValueWithValidation) String() string {
93+
return v.Duration.String()
94+
}
95+
96+
func (v *ForceDeleteWaitTimeValueWithValidation) Set(s string) error {
97+
duration, err := time.ParseDuration(s)
98+
if err != nil {
99+
return fmt.Errorf("failed to parse duration: %w", err)
100+
}
101+
if duration < 30*time.Second || duration > time.Hour {
102+
return fmt.Errorf("duration must be in the range [30s, 1h]")
103+
}
104+
v.Duration = duration
105+
return nil
106+
}
107+
108+
func newForceDeleteWaitTimeValueWithValidation(defaultVal time.Duration, p *metav1.Duration) *ForceDeleteWaitTimeValueWithValidation {
109+
p.Duration = defaultVal
110+
return (*ForceDeleteWaitTimeValueWithValidation)(p)
111+
}

0 commit comments

Comments
 (0)