/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package statefulset
import (
"context"
"fmt"
"strings"
apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
errorutils "k8s.io/apimachinery/pkg/util/errors"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes"
corelisters "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/retry"
"k8s.io/kubernetes/pkg/features"
)
// StatefulPodControlObjectManager abstracts the manipulation of Pods and PVCs. The real controller implements this
// with a clientset for writes and listers for reads; for tests we provide stubs.
type StatefulPodControlObjectManager interface {
CreatePod(ctx context.Context, pod *v1.Pod) error
GetPod(namespace, podName string) (*v1.Pod, error)
UpdatePod(pod *v1.Pod) error
DeletePod(pod *v1.Pod) error
CreateClaim(claim *v1.PersistentVolumeClaim) error
GetClaim(namespace, claimName string) (*v1.PersistentVolumeClaim, error)
UpdateClaim(claim *v1.PersistentVolumeClaim) error
}
// 这是一个StatefulPodControlObjectManager接口,定义了对有状态Pod和持久卷声明的操作方法,包括创建、获取、更新和删除Pod以及创建、获取、更新持久卷声明。
// StatefulPodControl defines the interface that StatefulSetController uses to create, update, and delete Pods,
// and to update the Status of a StatefulSet. It follows the design paradigms used for PodControl, but its
// implementation provides for PVC creation, ordered Pod creation, ordered Pod termination, and Pod identity enforcement.
// Manipulation of objects is provided through objectMgr, which allows the k8s API to be mocked out for testing.
type StatefulPodControl struct {
objectMgr StatefulPodControlObjectManager
recorder record.EventRecorder
}
//该代码定义了一个名为StatefulPodControl的结构体,它是一个接口,用于创建、更新和删除Pods,以及更新StatefulSet的状态。
//它采用了与PodControl相同的设计模式,但其实现提供了PVC创建、有序Pod创建、有序Pod终止和Pod身份强制执行等功能。
//通过objectMgr对象提供对对象的操作,允许在测试中模拟k8s API。
// NewStatefulPodControl constructs a StatefulPodControl using a realStatefulPodControlObjectManager with the given
// clientset, listers and EventRecorder.
func NewStatefulPodControl(
client clientset.Interface,
podLister corelisters.PodLister,
claimLister corelisters.PersistentVolumeClaimLister,
recorder record.EventRecorder,
) *StatefulPodControl {
return &StatefulPodControl{&realStatefulPodControlObjectManager{client, podLister, claimLister}, recorder}
}
// 该函数用于构造一个StatefulPodControl对象,使用给定的clientset、listers和EventRecorder。
// 函数通过传入的参数创建一个realStatefulPodControlObjectManager对象,并将其与传入的EventRecorder对象一起封装到StatefulPodControl对象中,最后返回该对象。
// NewStatefulPodControlFromManager creates a StatefulPodControl using the given StatefulPodControlObjectManager and recorder.
func NewStatefulPodControlFromManager(om StatefulPodControlObjectManager, recorder record.EventRecorder) *StatefulPodControl {
return &StatefulPodControl{om, recorder}
}
// 该函数使用给定的StatefulPodControlObjectManager和record.EventRecorder创建一个StatefulPodControl,并返回其指针。
// realStatefulPodControlObjectManager uses a clientset.Interface and listers.
type realStatefulPodControlObjectManager struct {
client clientset.Interface
podLister corelisters.PodLister
claimLister corelisters.PersistentVolumeClaimLister
}
// 该代码定义了一个名为realStatefulPodControlObjectManager的结构体,它使用了clientset.Interface和listers。
// 结构体中有三个字段:client、podLister和claimLister,分别用于存储客户端接口、Pod列表和持久卷申领列表。
func (om *realStatefulPodControlObjectManager) CreatePod(ctx context.Context, pod *v1.Pod) error {
_, err := om.client.CoreV1().Pods(pod.Namespace).Create(ctx, pod, metav1.CreateOptions{})
return err
}
// 该函数是一个Go语言函数,它使用了上下文(Context)来控制请求的超时和取消。
// 该函数的功能是在指定的命名空间中创建一个Pod,并返回创建操作的结果(成功或错误)。
// 具体来说,该函数的实现步骤如下:
// 1. 使用om.client获取CoreV1接口,该接口提供了对Kubernetes集群中Pod资源的操作方法。
// 2. 调用Create方法,在指定的命名空间中创建Pod,并传入上下文(Context)和Pod对象。
// 3. 返回创建操作的结果,即错误信息(Error)。
// 需要注意的是,该函数中使用了context.Context参数,它允许调用者控制请求的超时和取消。在实际应用中,这非常有用,比如在处理大量请求或需要及时响应的场景中。
func (om *realStatefulPodControlObjectManager) GetPod(namespace, podName string) (*v1.Pod, error) {
return om.podLister.Pods(namespace).Get(podName)
}
// 该函数是一个Go语言的方法,定义在一个名为realStatefulPodControlObjectManager的结构体类型上。该方法的功能是在指定的命名空间
// (namespace)中获取指定名称(podName)的Pod对象,并返回该Pod对象及其可能发生的错误。 具体实现上,
// 该方法通过调用om.podLister.Pods(namespace)来获取指定命名空间下的所有Pods的列表器,
// 然后进一步调用Get(podName)方法来获取指定名称的Pod对象。如果获取成功,则将该Pod对象返回;
// 如果获取失败,则将发生的错误返回。
func (om *realStatefulPodControlObjectManager) UpdatePod(pod *v1.Pod) error {
_, err := om.client.CoreV1().Pods(pod.Namespace).Update(context.TODO(), pod, metav1.UpdateOptions{})
return err
}
// 该函数用于更新指定命名空间下的Pod。函数接收一个指向v1.Pod类型的指针作为参数,通过调用om.client.CoreV1().Pods
// (pod.Namespace).Update()方法,将该Pod对象更新至Kubernetes集群中。若更新成功,则返回nil;若更新失败,
// 则返回相应的错误信息。
func (om *realStatefulPodControlObjectManager) DeletePod(pod *v1.Pod) error {
return om.client.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})
}
func (om *realStatefulPodControlObjectManager) CreateClaim(claim *v1.PersistentVolumeClaim) error {
_, err := om.client.CoreV1().PersistentVolumeClaims(claim.Namespace).Create(context.TODO(), claim, metav1.CreateOptions{})
return err
}
// 该函数用于删除指定命名空间下的Pod。函数接收一个指向v1.Pod类型的指针作为参数,通过调用om.client.CoreV1().Pods
// (pod.Namespace).Delete()方法,根据传入的Pod的Namespace和Name属性,在Kubernetes集群中删除该Pod实例。
// 若删除成功,则返回nil;否则,返回相应的错误信息。
func (om *realStatefulPodControlObjectManager) GetClaim(namespace, claimName string) (*v1.PersistentVolumeClaim, error) {
return om.claimLister.PersistentVolumeClaims(namespace).Get(claimName)
}
//该函数用于获取指定命名空间下名为claimName的PersistentVolumeClaim资源。函数接收两个字符串参数:namespace(命名空间)和claimName
//(PersistentVolumeClaim名称)。它通过调用om.claimLister.PersistentVolumeClaims(namespace).Get(claimName)从缓存列表中获取指定的
//PersistentVolumeClaim对象,并将其以*v1.PersistentVolumeClaim类型返回。如果获取成功,则返回PersistentVolumeClaim实例及其nil错误;
//如果未能找到对应资源,则返回nil及可能的错误信息。
func (om *realStatefulPodControlObjectManager) UpdateClaim(claim *v1.PersistentVolumeClaim) error {
_, err := om.client.CoreV1().PersistentVolumeClaims(claim.Namespace).Update(context.TODO(), claim, metav1.UpdateOptions{})
return err
}
//该函数用于更新指定命名空间下的PersistentVolumeClaim资源。函数接收一个指向v1.PersistentVolumeClaim类型的指针作为参数。
//通过调用om.client.CoreV1().PersistentVolumeClaims(claim.Namespace).Update()方法,根据传入的PersistentVolumeClaim对象的Namespace属性,
//在Kubernetes集群中更新该PersistentVolumeClaim实例。若更新操作成功,则返回nil;若出现错误,则返回相应的错误信息。
func (spc *StatefulPodControl) CreateStatefulPod(ctx context.Context, set *apps.StatefulSet, pod *v1.Pod) error {
// Create the Pod's PVCs prior to creating the Pod
if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
spc.recordPodEvent("create", set, pod, err)
return err
}
// If we created the PVCs attempt to create the Pod
err := spc.objectMgr.CreatePod(ctx, pod)
// sink already exists errors
if apierrors.IsAlreadyExists(err) {
return err
}
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) {
// Set PVC policy as much as is possible at this point.
if err := spc.UpdatePodClaimForRetentionPolicy(ctx, set, pod); err != nil {
spc.recordPodEvent("update", set, pod, err)
return err
}
}
spc.recordPodEvent("create", set, pod, err)
return err
}
// 该函数用于创建有状态的Pod。在创建Pod之前,它会先创建Pod所需的PVC(持久卷声明)。如果创建PVC时出现错误,则会记录Pod事件并返回错误。
// 接下来,它会尝试创建Pod,如果Pod已存在,则直接返回错误。如果启用了StatefulSetAutoDeletePVC功能,则会更新Pod的PVC保留策略。
// 最后,它会记录Pod的创建事件并返回可能的错误
func (spc *StatefulPodControl) UpdateStatefulPod(ctx context.Context, set *apps.StatefulSet, pod *v1.Pod) error {
attemptedUpdate := false
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
// assume the Pod is consistent
consistent := true
// if the Pod does not conform to its identity, update the identity and dirty the Pod
if !identityMatches(set, pod) {
updateIdentity(set, pod)
consistent = false
}
//该函数是一个Go语言函数,它用于更新一个有状态的Pod(StatefulPod)的状态。函数的参数包括一个上下文对象ctx、一个StatefulSet对象set和一个Pod对象pod。
//函数通过使用retry.RetryOnConflict方法,在遇到冲突时进行重试,以更新Pod的状态。在重试的过程中,
//函数会检查Pod是否符合其身份要求和StatefulSet的存储要求。如果不符合,函数会相应地更新Pod的身份和PVCs,并标记Pod为“dirty”。
//如果在更新过程中出现错误,则会返回错误信息。
// if the Pod does not conform to the StatefulSet's storage requirements, update the Pod's PVC's,
// dirty the Pod, and create any missing PVCs
if !storageMatches(set, pod) {
updateStorage(set, pod)
consistent = false
if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
spc.recordPodEvent("update", set, pod, err)
return err
}
}
//该函数主要检查Pod是否符合StatefulSet的存储要求,如果不符合,则更新Pod的PVCs,标记Pod为dirty,并创建任何缺失的PVCs。
//具体流程如下: 1. 检查Pod是否符合StatefulSet的存储要求,调用storageMatches(set, pod)函数。
//2. 如果Pod不符合存储要求,则执行以下操作: - 调用updateStorage(set, pod)函数更新Pod的PVCs。 - 将consistent标记为false。 -
//调用spc.createPersistentVolumeClaims(set, pod)函数创建任何缺失的PVCs,如果创建失败,则记录Pod事件并返回错误。
//3. 如果Pod符合存储要求,则继续后续流程。
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) {
// if the Pod's PVCs are not consistent with the StatefulSet's PVC deletion policy, update the PVC
// and dirty the pod.
if match, err := spc.ClaimsMatchRetentionPolicy(ctx, set, pod); err != nil {
spc.recordPodEvent("update", set, pod, err)
return err
} else if !match {
if err := spc.UpdatePodClaimForRetentionPolicy(ctx, set, pod); err != nil {
spc.recordPodEvent("update", set, pod, err)
return err
}
consistent = false
}
}
//这段Go代码是关于StatefulSet自动删除PVC的逻辑。 首先,检查是否启用了StatefulSetAutoDeletePVC功能门。
//如果启用了,则进一步判断Pod的PVC是否与StatefulSet的PVC删除策略一致。如果不一致,则更新PVC并标记Pod为dirty。
//如果匹配失败,则调用UpdatePodClaimForRetentionPolicy函数来更新Pod的PVC,并将consistent标记为false。
//在更新过程中,如果出现错误,则记录Pod事件并返回错误。
// if the Pod is not dirty, do nothing
if consistent {
return nil
}
attemptedUpdate = true
// commit the update, retrying on conflicts
updateErr := spc.objectMgr.UpdatePod(pod)
if updateErr == nil {
return nil
}
if updated, err := spc.objectMgr.GetPod(set.Namespace, pod.Name); err == nil {
// make a copy so we don't mutate the shared cache
pod = updated.DeepCopy()
} else {
utilruntime.HandleError(fmt.Errorf("error getting updated Pod %s/%s: %w", set.Namespace, pod.Name, err))
}
//这段Go代码主要做的是更新Pod的操作。具体流程如下:
//1. 首先判断consistent是否为true,如果是,则直接返回nil。
//2. 设置attemptedUpdate为true,表示已经尝试更新Pod。
//3. 调用spc.objectMgr.UpdatePod(pod)方法更新Pod,如果更新成功,则直接返回nil。
//4. 如果更新失败,则尝试获取最新的Pod信息。
//5. 如果获取成功,则将获取到的Pod深拷贝一份,防止对共享缓存的污染。
//6. 如果获取失败,则打印错误信息。 整体来说,这段代码的逻辑比较简单,主要是通过调用UpdatePod方法更新Pod,如果更新失败则尝试重新获取Pod信息。
return updateErr
})
if attemptedUpdate {
spc.recordPodEvent("update", set, pod, err)
}
return err
}
// 这段Go代码是一个函数片段,它在一个匿名函数中执行了某种更新操作,并通过参数set和pod记录了事件。如果更新操作尝试过,
// 它会通过spc.recordPodEvent方法记录一个名为"update"的事件。最后,该函数返回一个错误值err。
func (spc *StatefulPodControl) DeleteStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
err := spc.objectMgr.DeletePod(pod)
spc.recordPodEvent("delete", set, pod, err)
return err
}
// 此函数用于删除一个有状态的Pod。它通过调用spc.objectMgr.DeletePod(pod)来删除指定的Pod,并记录事件。最后返回删除操作的错误(如果有)。
// ClaimsMatchRetentionPolicy returns false if the PVCs for pod are not consistent with set's PVC deletion policy.
// An error is returned if something is not consistent. This is expected if the pod is being otherwise updated,
// but a problem otherwise (see usage of this method in UpdateStatefulPod).
func (spc *StatefulPodControl) ClaimsMatchRetentionPolicy(ctx context.Context, set *apps.StatefulSet, pod *v1.Pod) (bool, error) {
logger := klog.FromContext(ctx)
ordinal := getOrdinal(pod)
templates := set.Spec.VolumeClaimTemplates
for i := range templates {
claimName := getPersistentVolumeClaimName(set, &templates[i], ordinal)
claim, err := spc.objectMgr.GetClaim(set.Namespace, claimName)
switch {
case apierrors.IsNotFound(err):
klog.FromContext(ctx).V(4).Info("Expected claim missing, continuing to pick up in next iteration", "PVC", klog.KObj(claim))
case err != nil:
return false, fmt.Errorf("Could not retrieve claim %s for %s when checking PVC deletion policy", claimName, pod.Name)
default:
if !claimOwnerMatchesSetAndPod(logger, claim, set, pod) {
return false, nil
}
}
}
return true, nil
}
//该函数用于检查Pod的PVC是否符合StatefulSet的PVC删除策略。
//它遍历StatefulSet的VolumeClaimTemplates,并根据模板生成PVC名称。
//然后它尝试获取该PVC,根据获取结果进行判断:
//- 如果PVC不存在,则记录日志并继续下一次迭代。
//- 如果获取PVC出现错误,则返回错误。
//- 如果PVC存在但其owner不是StatefulSet和Pod,则返回false。
//如果所有PVC都符合要求,则返回true。
// UpdatePodClaimForRetentionPolicy updates the PVCs used by pod to match the PVC deletion policy of set.
func (spc *StatefulPodControl) UpdatePodClaimForRetentionPolicy(ctx context.Context, set *apps.StatefulSet, pod *v1.Pod) error {
logger := klog.FromContext(ctx)
ordinal := getOrdinal(pod)
templates := set.Spec.VolumeClaimTemplates
for i := range templates {
claimName := getPersistentVolumeClaimName(set, &templates[i], ordinal)
claim, err := spc.objectMgr.GetClaim(set.Namespace, claimName)
switch {
case apierrors.IsNotFound(err):
logger.V(4).Info("Expected claim missing, continuing to pick up in next iteration", "PVC", klog.KObj(claim))
case err != nil:
return fmt.Errorf("Could not retrieve claim %s not found for %s when checking PVC deletion policy: %w", claimName, pod.Name, err)
default:
if !claimOwnerMatchesSetAndPod(logger, claim, set, pod) {
claim = claim.DeepCopy() // Make a copy so we don't mutate the shared cache.
needsUpdate := updateClaimOwnerRefForSetAndPod(logger, claim, set, pod)
if needsUpdate {
err := spc.objectMgr.UpdateClaim(claim)
if err != nil {
return fmt.Errorf("Could not update claim %s for delete policy ownerRefs: %w", claimName, err)
}
}
}
}
}
return nil
}
//该函数用于更新StatefulSet中的Pod的PersistentVolumeClaim(PVC)的所有者引用,以确保它们与Pod和StatefulSet正确关联。
//它遍历StatefulSet的VolumeClaimTemplates,并根据Pod的序号生成相应的PVC名称。然后,它尝试获取该PVC,根据获取结果进行不同的处理:
//- 如果PVC不存在,则记录一条日志,并继续处理下一个PVC。
//- 如果获取PVC时出现错误,则返回错误。
//- 如果PVC存在但其所有者引用与Pod和StatefulSet不匹配,则创建PVC的深拷贝,并更新其所有者引用。
//如果更新后的PVC与原始PVC不同,则将其更新到Kubernetes集群中。 最终,如果所有PVC都已正确处理,则函数返回nil。
// PodClaimIsStale returns true for a stale PVC that should block pod creation. If the scaling
// policy is deletion, and a PVC has an ownerRef that does not match the pod, the PVC is stale. This
// includes pods whose UID has not been created.
func (spc *StatefulPodControl) PodClaimIsStale(set *apps.StatefulSet, pod *v1.Pod) (bool, error) {
policy := getPersistentVolumeClaimRetentionPolicy(set)
if policy.WhenScaled == apps.RetainPersistentVolumeClaimRetentionPolicyType {
// PVCs are meant to be reused and so can't be stale.
return false, nil
}
for _, claim := range getPersistentVolumeClaims(set, pod) {
pvc, err := spc.objectMgr.GetClaim(claim.Namespace, claim.Name)
switch {
case apierrors.IsNotFound(err):
// If the claim doesn't exist yet, it can't be stale.
continue
case err != nil:
return false, err
case err == nil:
// A claim is stale if it doesn't match the pod's UID, including if the pod has no UID.
if hasStaleOwnerRef(pvc, pod) {
return true, nil
}
}
}
return false, nil
}
// SstatefulSet和Pod对应的PersistentVolumeClaim该函数用于判断Pod所使用的PVC是否为旧的(stale),如果为旧的则会阻止Pod的创建。
// 在StatefulSet缩容策略为删除时,如果PVC的ownerRef与Pod不匹配,则认为PVC是旧的。
// 函数首先根据StatefulSet的设置获取PVC的保留策略,如果策略为保留(Retain),则PVC会被重用,不会被认为是旧的。
// 然后遍历获取与Pod相关的PVC,通过查询PVC的详细信息判断PVC是否与Pod的UID匹配,如果不匹配则认为PVC是旧的。
// 如果查询PVC时发生错误,则返回错误信息。最后,如果没有发现旧的PVC,则返回false。
// recordPodEvent records an event for verb applied to a Pod in a StatefulSet. If err is nil the generated event will
// have a reason of v1.EventTypeNormal. If err is not nil the generated event will have a reason of v1.EventTypeWarning.
func (spc *StatefulPodControl) recordPodEvent(verb string, set *apps.StatefulSet, pod *v1.Pod, err error) {
if err == nil {
reason := fmt.Sprintf("Successful%s", strings.Title(verb))
message := fmt.Sprintf("%s Pod %s in StatefulSet %s successful",
strings.ToLower(verb), pod.Name, set.Name)
spc.recorder.Event(set, v1.EventTypeNormal, reason, message)
} else {
reason := fmt.Sprintf("Failed%s", strings.Title(verb))
message := fmt.Sprintf("%s Pod %s in StatefulSet %s failed error: %s",
strings.ToLower(verb), pod.Name, set.Name, err)
spc.recorder.Event(set, v1.EventTypeWarning, reason, message)
}
}
// 该函数是一个名为recordPodEvent的方法,它属于StatefulPodControl类型。
// 该方法用于记录关于Pod的事件,该Pod属于一个StatefulSet。根据err参数的值,生成的事件会有不同的原因,
// 如果err为nil,则事件原因为v1.EventTypeNormal,否则为v1.EventTypeWarning。
// 根据verb参数,会生成成功或失败的事件消息,并通过spc.recorder.Event方法记录事件。
// recordClaimEvent records an event for verb applied to the PersistentVolumeClaim of a Pod in a StatefulSet. If err is
// nil the generated event will have a reason of v1.EventTypeNormal. If err is not nil the generated event will have a
// reason of v1.EventTypeWarning.
func (spc *StatefulPodControl) recordClaimEvent(verb string, set *apps.StatefulSet, pod *v1.Pod, claim *v1.PersistentVolumeClaim, err error) {
if err == nil {
reason := fmt.Sprintf("Successful%s", strings.Title(verb))
message := fmt.Sprintf("%s Claim %s Pod %s in StatefulSet %s success",
strings.ToLower(verb), claim.Name, pod.Name, set.Name)
spc.recorder.Event(set, v1.EventTypeNormal, reason, message)
} else {
reason := fmt.Sprintf("Failed%s", strings.Title(verb))
message := fmt.Sprintf("%s Claim %s for Pod %s in StatefulSet %s failed error: %s",
strings.ToLower(verb), claim.Name, pod.Name, set.Name, err)
spc.recorder.Event(set, v1.EventTypeWarning, reason, message)
}
}
// 该函数名为recordClaimEvent,用于记录StatefulSet中Pod的PersistentVolumeClaim的事件。
// 根据err是否为nil,生成的事件会有不同的原因,如果err为nil,则事件原因为v1.EventTypeNormal,否则为v1.EventTypeWarning。
// 函数通过spc.recorder.Event方法记录事件。
// createMissingPersistentVolumeClaims creates all of the required PersistentVolumeClaims for pod, and updates its retention policy
func (spc *StatefulPodControl) createMissingPersistentVolumeClaims(ctx context.Context, set *apps.StatefulSet, pod *v1.Pod) error {
if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
return err
}
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) {
// Set PVC policy as much as is possible at this point.
if err := spc.UpdatePodClaimForRetentionPolicy(ctx, set, pod); err != nil {
spc.recordPodEvent("update", set, pod, err)
return err
}
}
return nil
}
//该函数是一个Go语言函数,用于创建缺失的PersistentVolumeClaims(PVCs)并更新其保留策略,适用于StatefulPodControl类型。
//函数首先调用createPersistentVolumeClaims函数创建所需的PVCs,如果创建失败,则返回错误。
//接下来,如果启用了StatefulSetAutoDeletePVC功能,则调用UpdatePodClaimForRetentionPolicy函数来更新PVC的保留策略。
//如果更新失败,则记录事件并返回错误。最后,如果上述步骤都成功,则返回nil。
// createPersistentVolumeClaims creates all of the required PersistentVolumeClaims for pod, which must be a member of
// set. If all of the claims for Pod are successfully created, the returned error is nil. If creation fails, this method
// may be called again until no error is returned, indicating the PersistentVolumeClaims for pod are consistent with
// set's Spec.
func (spc *StatefulPodControl) createPersistentVolumeClaims(set *apps.StatefulSet, pod *v1.Pod) error {
var errs []error
for _, claim := range getPersistentVolumeClaims(set, pod) {
pvc, err := spc.objectMgr.GetClaim(claim.Namespace, claim.Name)
switch {
case apierrors.IsNotFound(err):
err := spc.objectMgr.CreateClaim(&claim)
if err != nil {
errs = append(errs, fmt.Errorf("failed to create PVC %s: %s", claim.Name, err))
}
if err == nil || !apierrors.IsAlreadyExists(err) {
spc.recordClaimEvent("create", set, pod, &claim, err)
}
case err != nil:
errs = append(errs, fmt.Errorf("failed to retrieve PVC %s: %s", claim.Name, err))
spc.recordClaimEvent("create", set, pod, &claim, err)
default:
if pvc.DeletionTimestamp != nil {
errs = append(errs, fmt.Errorf("pvc %s is being deleted", claim.Name))
}
}
// TODO: Check resource requirements and accessmodes, update if necessary
}
return errorutils.NewAggregate(errs)
}
//该函数用于创建StatefulSet中的PersistentVolumeClaims(PVCs)。
//1. 遍历获取需要创建的PVCs。
//2. 对于每个PVC,先尝试从objectMgr中获取,根据获取结果进行不同处理:
//- 如果未找到,则创建该PVC,并记录事件。
//- 如果获取发生错误,则将错误信息记录并返回。
//- 如果已存在该PVC,检查其DeletionTimestamp是否为空,若非空则将错误信息记录并返回。
//3. 返回所有错误的聚合。 注意:该函数未完成,最后还有一行TODO注释,提示需要检查资源需求和访问模式并进行更新。