Skip to content

Commit

Permalink
chore: switchover before updating or deleting pods (#8731)
Browse files Browse the repository at this point in the history
  • Loading branch information
leon-inf authored Jan 8, 2025
1 parent 87efae1 commit fd21c31
Show file tree
Hide file tree
Showing 47 changed files with 1,900 additions and 2,098 deletions.
6 changes: 2 additions & 4 deletions apis/apps/v1/deprecated.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
viper "github.com/apecloud/kubeblocks/pkg/viperx"
)
Expand Down Expand Up @@ -217,7 +216,6 @@ func (t *InstanceTemplate) GetReplicas() int32 {
return defaultInstanceTemplateReplicas
}

// GetOrdinals TODO(free6om): Remove after resolving the circular dependencies between apps and workloads.
func (t *InstanceTemplate) GetOrdinals() workloads.Ordinals {
return workloads.Ordinals{}
func (t *InstanceTemplate) GetOrdinals() Ordinals {
return t.Ordinals
}
22 changes: 22 additions & 0 deletions apis/apps/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,15 @@ type InstanceTemplate struct {
// +optional
Replicas *int32 `json:"replicas,omitempty"`

// Specifies the desired Ordinals of this InstanceTemplate.
// The Ordinals used to specify the ordinal of the instance (pod) names to be generated under this InstanceTemplate.
//
// For example, if Ordinals is {ranges: [{start: 0, end: 1}], discrete: [7]},
// then the instance names generated under this InstanceTemplate would be
// $(cluster.name)-$(component.name)-$(template.name)-0、$(cluster.name)-$(component.name)-$(template.name)-1 and
// $(cluster.name)-$(component.name)-$(template.name)-7
Ordinals Ordinals `json:"ordinals,omitempty"`

// Specifies a map of key-value pairs to be merged into the Pod's existing annotations.
// Existing keys will have their values overwritten, while new keys will be added to the annotations.
//
Expand Down Expand Up @@ -661,3 +670,16 @@ type InstanceTemplate struct {
// +optional
VolumeClaimTemplates []ClusterComponentVolumeClaimTemplate `json:"volumeClaimTemplates,omitempty"`
}

// Range represents a range with a start and an end value.
// It is used to define a continuous segment.
type Range struct {
Start int32 `json:"start"`
End int32 `json:"end"`
}

// Ordinals represents a combination of continuous segments and individual values.
type Ordinals struct {
Ranges []Range `json:"ranges,omitempty"`
Discrete []int32 `json:"discrete,omitempty"`
}
41 changes: 41 additions & 0 deletions apis/apps/v1/zz_generated.deepcopy.go

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

226 changes: 71 additions & 155 deletions apis/workloads/v1/instanceset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
)

// +genclient
Expand Down Expand Up @@ -83,7 +85,7 @@ type InstanceSetSpec struct {
// For example, if Ordinals is {ranges: [{start: 0, end: 1}], discrete: [7]},
// then the instance names generated under the default template would be
// $(cluster.name)-$(component.name)-0、$(cluster.name)-$(component.name)-1 and $(cluster.name)-$(component.name)-7
DefaultTemplateOrdinals Ordinals `json:"defaultTemplateOrdinals,omitempty"`
DefaultTemplateOrdinals kbappsv1.Ordinals `json:"defaultTemplateOrdinals,omitempty"`

// Defines the minimum number of seconds a newly created pod should be ready
// without any of its container crashing to be considered available.
Expand Down Expand Up @@ -200,6 +202,11 @@ type InstanceSetSpec struct {
// +optional
MembershipReconfiguration *MembershipReconfiguration `json:"membershipReconfiguration,omitempty"`

// Provides variables which are used to call Actions.
//
// +optional
TemplateVars map[string]string `json:"templateVars,omitempty"`

// Members(Pods) update strategy.
//
// - serial: update Members one by one that guarantee minimum component unavailable time.
Expand Down Expand Up @@ -306,150 +313,10 @@ type InstanceSetStatus struct {
TemplatesStatus []InstanceTemplateStatus `json:"templatesStatus,omitempty"`
}

// Range represents a range with a start and an end value.
// It is used to define a continuous segment.
type Range struct {
Start int32 `json:"start"`
End int32 `json:"end"`
}

// Ordinals represents a combination of continuous segments and individual values.
type Ordinals struct {
Ranges []Range `json:"ranges,omitempty"`
Discrete []int32 `json:"discrete,omitempty"`
}

// InstanceTemplate allows customization of individual replica configurations within a Component,
// without altering the base component template defined in ClusterComponentSpec.
// It enables the application of distinct settings to specific instances (replicas),
// providing flexibility while maintaining a common configuration baseline.
type InstanceTemplate struct {
// Name specifies the unique name of the instance Pod created using this InstanceTemplate.
// This name is constructed by concatenating the component's name, the template's name, and the instance's ordinal
// using the pattern: $(cluster.name)-$(component.name)-$(template.name)-$(ordinal). Ordinals start from 0.
// The specified name overrides any default naming conventions or patterns.
//
// +kubebuilder:validation:MaxLength=54
// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
// +kubebuilder:validation:Required
Name string `json:"name"`

// Specifies the number of instances (Pods) to create from this InstanceTemplate.
// This field allows setting how many replicated instances of the component,
// with the specific overrides in the InstanceTemplate, are created.
// The default value is 1. A value of 0 disables instance creation.
//
// +kubebuilder:default=1
// +kubebuilder:validation:Minimum=0
// +optional
Replicas *int32 `json:"replicas,omitempty"`

// Specifies the desired Ordinals of this InstanceTemplate.
// The Ordinals used to specify the ordinal of the instance (pod) names to be generated under this InstanceTemplate.
//
// For example, if Ordinals is {ranges: [{start: 0, end: 1}], discrete: [7]},
// then the instance names generated under this InstanceTemplate would be
// $(cluster.name)-$(component.name)-$(template.name)-0、$(cluster.name)-$(component.name)-$(template.name)-1 and
// $(cluster.name)-$(component.name)-$(template.name)-7
Ordinals Ordinals `json:"ordinals,omitempty"`

// Specifies a map of key-value pairs to be merged into the Pod's existing annotations.
// Existing keys will have their values overwritten, while new keys will be added to the annotations.
//
// +optional
Annotations map[string]string `json:"annotations,omitempty"`

// Specifies a map of key-value pairs that will be merged into the Pod's existing labels.
// Values for existing keys will be overwritten, and new keys will be added.
//
// +optional
Labels map[string]string `json:"labels,omitempty"`

// Specifies an override for the first container's image in the pod.
//
// +optional
Image *string `json:"image,omitempty"`

// Specifies the scheduling policy for the Component.
//
// +optional
SchedulingPolicy *SchedulingPolicy `json:"schedulingPolicy,omitempty"`

// Specifies an override for the resource requirements of the first container in the Pod.
// This field allows for customizing resource allocation (CPU, memory, etc.) for the container.
//
// +optional
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`

// Defines Env to override.
// Add new or override existing envs.
// +optional
Env []corev1.EnvVar `json:"env,omitempty"`

// Defines Volumes to override.
// Add new or override existing volumes.
// +optional
Volumes []corev1.Volume `json:"volumes,omitempty"`

// Defines VolumeMounts to override.
// Add new or override existing volume mounts of the first container in the pod.
// +optional
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`

// Defines VolumeClaimTemplates to override.
// Add new or override existing volume claim templates.
// +optional
VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"`
}

// SchedulingPolicy the scheduling policy.
// Deprecated: Unify with apps/v1alpha1.SchedulingPolicy
type SchedulingPolicy struct {
// If specified, the Pod will be dispatched by specified scheduler.
// If not specified, the Pod will be dispatched by default scheduler.
//
// +optional
SchedulerName string `json:"schedulerName,omitempty"`

// NodeSelector is a selector which must be true for the Pod to fit on a node.
// Selector which must match a node's labels for the Pod to be scheduled on that node.
// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
//
// +optional
// +mapType=atomic
NodeSelector map[string]string `json:"nodeSelector,omitempty"`

// NodeName is a request to schedule this Pod onto a specific node. If it is non-empty,
// the scheduler simply schedules this Pod onto that node, assuming that it fits resource
// requirements.
//
// +optional
NodeName string `json:"nodeName,omitempty"`

// Specifies a group of affinity scheduling rules of the Cluster, including NodeAffinity, PodAffinity, and PodAntiAffinity.
//
// +optional
Affinity *corev1.Affinity `json:"affinity,omitempty"`

// Allows Pods to be scheduled onto nodes with matching taints.
// Each toleration in the array allows the Pod to tolerate node taints based on
// specified `key`, `value`, `effect`, and `operator`.
//
// - The `key`, `value`, and `effect` identify the taint that the toleration matches.
// - The `operator` determines how the toleration matches the taint.
//
// Pods with matching tolerations are allowed to be scheduled on tainted nodes, typically reserved for specific purposes.
//
// +optional
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`

// TopologySpreadConstraints describes how a group of Pods ought to spread across topology
// domains. Scheduler will schedule Pods in a way which abides by the constraints.
// All topologySpreadConstraints are ANDed.
//
// +optional
TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"`
}
// InstanceTemplate allows customization of individual replica configurations in a Component.
//
// +kubebuilder:object:generate=false
type InstanceTemplate = kbappsv1.InstanceTemplate

type PodUpdatePolicyType string

Expand Down Expand Up @@ -564,6 +431,11 @@ type MembershipReconfiguration struct {
//
// +optional
PromoteAction *Action `json:"promoteAction,omitempty"`

// Defines the procedure for a controlled transition of a role to a new replica.
//
// +optional
Switchover *kbappsv1.Action `json:"switchover,omitempty"`
}

// MemberUpdateStrategy defines Cluster Component update strategy.
Expand Down Expand Up @@ -687,19 +559,63 @@ const (
ReasonInstanceUpdateRestricted = "InstanceUpdateRestricted"
)

const defaultInstanceTemplateReplicas = 1
// IsInstancesReady gives Instance level 'ready' state when all instances are available
func (r *InstanceSet) IsInstancesReady() bool {
if r == nil {
return false
}
// check whether the cluster has been initialized
if r.Status.ReadyInitReplicas != r.Status.InitReplicas {
return false
}
// check whether latest spec has been sent to the underlying workload
if r.Status.ObservedGeneration != r.Generation {
return false
}
// check whether the underlying workload is ready
if r.Spec.Replicas == nil {
return false
}
replicas := *r.Spec.Replicas
if r.Status.Replicas != replicas ||
r.Status.ReadyReplicas != replicas ||
r.Status.UpdatedReplicas != replicas {
return false
}
// check availableReplicas only if minReadySeconds is set
if r.Spec.MinReadySeconds > 0 && r.Status.AvailableReplicas != replicas {
return false
}

func (t *InstanceTemplate) GetName() string {
return t.Name
return true
}

func (t *InstanceTemplate) GetReplicas() int32 {
if t.Replicas != nil {
return *t.Replicas
// IsInstanceSetReady gives InstanceSet level 'ready' state:
// 1. all instances are available
// 2. and all members have role set (if they are role-ful)
func (r *InstanceSet) IsInstanceSetReady() bool {
instancesReady := r.IsInstancesReady()
if !instancesReady {
return false
}
return defaultInstanceTemplateReplicas
}

func (t *InstanceTemplate) GetOrdinals() Ordinals {
return t.Ordinals
// check whether role probe has done
if len(r.Spec.Roles) == 0 {
return true
}
membersStatus := r.Status.MembersStatus
if len(membersStatus) != int(*r.Spec.Replicas) {
return false
}
if r.Status.ReadyWithoutPrimary {
return true
}
hasLeader := false
for _, status := range membersStatus {
if status.ReplicaRole != nil && status.ReplicaRole.IsLeader {
hasLeader = true
break
}
}
return hasLeader
}
Loading

0 comments on commit fd21c31

Please sign in to comment.