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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions apis/v1alpha1/membercluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,21 @@ type MemberClusterStatus struct {

// Capacity represents the total resources of all the nodes within the member cluster.

// +required
Capacity v1.ResourceList `json:"capacity"`
// +optional
Capacity v1.ResourceList `json:"capacity,omitempty"`

// Allocatable represents the total resources of all the nodes within the member cluster that are available for scheduling.

// +required
Allocatable v1.ResourceList `json:"allocatable"`
// +optional
Allocatable v1.ResourceList `json:"allocatable,omitempty"`
}

const (
// ConditionTypeMemberClusterReadyToJoin is used to track the readiness of the hub cluster
// controller to accept the new member cluster.
// its conditionStatus can only be "True" == ReadyToJoin
ConditionTypeMemberClusterReadyToJoin string = "ReadyToJoin"

// ConditionTypeMemberClusterJoin is used to track the join state of the memberCluster.
// its conditionStatus can be "True" == Joined, "Unknown" == Joining/Leaving, "False" == Left
ConditionTypeMemberClusterJoin string = "Joined"
Expand Down
33 changes: 20 additions & 13 deletions config/crd/bases/fleet.azure.com_clusterresourceplacements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ metadata:
spec:
group: fleet.azure.com
names:
categories:
- fleet-workload
kind: ClusterResourcePlacement
listKind: ClusterResourcePlacementList
plural: clusterresourceplacements
Expand Down Expand Up @@ -48,26 +50,27 @@ spec:
policy:
description: Policy represents the placement policy to select clusters
to place all the selected resources. Default is place to the entire
fleet if this field is omitted
fleet if this field is omitted.
properties:
Affinity:
description: Affinity represents the selected resources' scheduling
constraints If not set, the entire fleet can be scheduling candidate.
constraints. If not set, the entire fleet can be scheduling
candidate.
properties:
clusterAffinity:
description: ClusterAffinity describes cluster affinity scheduling
rules for the resources.
properties:
clusterSelectorTerms:
description: ClusterSelectorTerms is a list of cluster
selector terms. The terms are ORed. kubebuilder:validation:MaxItems=10
selector terms. The terms are `ORed`. kubebuilder:validation:MaxItems=10
items:
description: ClusterSelectorTerm represents the requirements
to selected clusters
to selected clusters.
properties:
labelSelector:
description: LabelSelector is a list of cluster
selector requirements by cluster's labels. kubebuilder:validation:MaxItems=10
requirements by cluster's labels. kubebuilder:validation:MaxItems=10
properties:
matchExpressions:
description: matchExpressions is a list of label
Expand Down Expand Up @@ -132,11 +135,12 @@ spec:
type: object
resourceSelectors:
description: ResourceSelectors is used to select cluster scoped resources.
kubebuilder:validation:MaxItems=100
The selectors are `ORed`. kubebuilder:validation:MaxItems=100
items:
description: 'ClusterResourceSelector is used to specify cluster
scoped resources to be selected. Note: When the resource is of
type `namespace`, ALL the resources in it is selected'
scoped resources to be selected. Note: When the cluster resource
is of type `namespace`, ALL the resources in this namespace are
selected. All the fields present in this structure are `ANDed`.'
properties:
group:
description: Group is the group name of the target resource.
Expand Down Expand Up @@ -201,7 +205,9 @@ spec:
description: Version is the version of the target resource.
type: string
required:
- group
- kind
- version
type: object
type: array
required:
Expand Down Expand Up @@ -289,11 +295,12 @@ spec:
resources status. kubebuilder:validation:MaxItems=1000
items:
description: FailedResourcePlacement shows the failure details of
a failed resource placement
a failed resource placement.
properties:
clusterName:
description: ClusterName is the name of the cluster that
type: integer
description: ClusterName is the name of the cluster that this
resource is placed on.
type: string
condition:
description: Condition contains the failed condition status
for this failed to place resource.
Expand Down Expand Up @@ -366,7 +373,7 @@ spec:
type: string
namespace:
description: Namespace is the namespace of the resource, the
resource is cluster scoped if the value is empty
resource is cluster scoped if the value is empty.
type: string
version:
description: Version is the version of the selected resource.
Expand Down Expand Up @@ -395,7 +402,7 @@ spec:
type: string
namespace:
description: Namespace is the namespace of the resource, the
resource is cluster scoped if the value is empty
resource is cluster scoped if the value is empty.
type: string
version:
description: Version is the version of the selected resource.
Expand Down
2 changes: 0 additions & 2 deletions config/crd/bases/fleet.azure.com_memberclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,6 @@ spec:
type: object
type: array
required:
- allocatable
- capacity
- conditions
type: object
required:
Expand Down
49 changes: 34 additions & 15 deletions pkg/controllers/membercluster/membercluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@ import (
)

const (
eventReasonNamespaceCreated = "NamespaceCreated"
eventReasonNamespaceDeleted = "NamespaceDeleted"
eventReasonRoleCreated = "RoleCreated"
eventReasonRoleUpdated = "RoleUpdated"
eventReasonRoleBindingCreated = "RoleBindingCreated"
eventReasonRoleBindingUpdated = "RoleBindingUpdated"
eventReasonIMCCreated = "InternalMemberClusterCreated"
eventReasonIMCSpecUpdated = "InternalMemberClusterSpecUpdated"
reasonMemberClusterJoined = "MemberClusterJoined"
reasonMemberClusterLeft = "MemberClusterLeft"
eventReasonNamespaceCreated = "NamespaceCreated"
eventReasonNamespaceDeleted = "NamespaceDeleted"
eventReasonRoleCreated = "RoleCreated"
eventReasonRoleUpdated = "RoleUpdated"
eventReasonRoleBindingCreated = "RoleBindingCreated"
eventReasonRoleBindingUpdated = "RoleBindingUpdated"
eventReasonIMCCreated = "InternalMemberClusterCreated"
eventReasonIMCSpecUpdated = "InternalMemberClusterSpecUpdated"
reasonMemberClusterReadyToJoin = "MemberClusterReadyToJoin"
reasonMemberClusterJoined = "MemberClusterJoined"
reasonMemberClusterLeft = "MemberClusterLeft"
)

// Reconciler reconciles a MemberCluster object
Expand Down Expand Up @@ -102,16 +103,17 @@ func (r *Reconciler) join(ctx context.Context, mc *fleetv1alpha1.MemberCluster)
return ctrl.Result{}, err
}

markMemberClusterReadyToJoin(r.recorder, mc)
joinedCond := imc.GetCondition(fleetv1alpha1.ConditionTypeInternalMemberClusterJoin)
if joinedCond != nil && joinedCond.Status == metav1.ConditionTrue {
r.copyMemberClusterStatusFromInternalMC(mc, imc)
if err := r.updateMemberClusterStatus(ctx, mc); err != nil {
klog.ErrorS(err, "cannot update member cluster status as Joined",
"internalMemberCluster", imc.Name)
return ctrl.Result{}, err
}
}

if err := r.updateMemberClusterStatus(ctx, mc); err != nil {
klog.ErrorS(err, "cannot update the member cluster status",
"internalMemberCluster", klog.KObj(imc))
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}

Expand Down Expand Up @@ -380,6 +382,23 @@ func (r *Reconciler) deleteNamespace(ctx context.Context, mc *fleetv1alpha1.Memb
return nil
}

// markMemberClusterReadyToJoin is used to the update the status of the member cluster to ready to join condition.
func markMemberClusterReadyToJoin(recorder record.EventRecorder, mc apis.ConditionedObj) {
readyToJoinCond := mc.GetCondition(fleetv1alpha1.ConditionTypeMemberClusterReadyToJoin)
if readyToJoinCond != nil {
return
}
klog.V(2).InfoS("mark the member Cluster as ready to", "memberService", mc.GetName())
recorder.Event(mc, corev1.EventTypeNormal, reasonMemberClusterReadyToJoin, "member cluster is ready to join")
readyToJoinCondition := &metav1.Condition{
Type: fleetv1alpha1.ConditionTypeMemberClusterReadyToJoin,
Status: metav1.ConditionTrue,
Reason: reasonMemberClusterReadyToJoin,
ObservedGeneration: mc.GetGeneration(),
}
mc.SetConditions(*readyToJoinCondition)
}

// markMemberClusterJoined is used to the update the status of the member cluster to have the joined condition.
func markMemberClusterJoined(recorder record.EventRecorder, mc apis.ConditionedObj) {
klog.V(2).InfoS("mark the member Cluster as Joined", "memberService", mc.GetName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,18 @@ var _ = Describe("Test MemberCluster Controller", func() {
var mc fleetv1alpha1.MemberCluster
Expect(k8sClient.Get(ctx, memberClusterNamespacedName, &mc)).Should(Succeed())

readyToJoinCondition := mc.GetCondition(fleetv1alpha1.ConditionTypeMemberClusterReadyToJoin)
Expect(readyToJoinCondition).NotTo(BeNil())
Expect(readyToJoinCondition.Status).To(Equal(metav1.ConditionTrue))
Expect(readyToJoinCondition.Reason).To(Equal(reasonMemberClusterReadyToJoin))

joinCondition := mc.GetCondition(fleetv1alpha1.ConditionTypeMemberClusterJoin)
Expect(joinCondition).NotTo(BeNil())
Expect(joinCondition.Status).To(Equal(metav1.ConditionTrue))
Expect(joinCondition.Reason).To(Equal(reasonMemberClusterJoined))

heartBeatCondition := mc.GetCondition(fleetv1alpha1.ConditionTypeInternalMemberClusterHeartbeat)
Expect(heartBeatCondition).NotTo(BeNil())
Expect(heartBeatCondition.Status).To(Equal(metav1.ConditionTrue))
Expect(heartBeatCondition.Reason).To(Equal("InternalMemberClusterHeartbeatReceived"))
})
Expand Down