k8s源码设计模式之Strategy

Strategy也就是策略模式

k8s中策略模式的实现

k8s中为不同的资源对象实现了不同的策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go
// Store实现了k8s.io/apiserver/pkg/registry/rest.StandardStorage接口,
// 它允许使用者实现任何需要的非通用函数
type Store struct {

NewFunc func() runtime.Object
NewListFunc func() runtime.Object
...
// 对应的策略
CreateStrategy rest.RESTCreateStrategy
UpdateStrategy rest.RESTUpdateStrategy
DeleteStrategy rest.RESTDeleteStrategy
...

}
Deployment实现的策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//pkg/registry/apps/deployment/strategy.go
type deploymentStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}

// Strategy is the default logic that applies when creating and updating Deployment
// objects via the REST API.
var Strategy = deploymentStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}


// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (deploymentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
deployment := obj.(*apps.Deployment)
deployment.Status = apps.DeploymentStatus{}
deployment.Generation = 1

pod.DropDisabledTemplateFields(&deployment.Spec.Template, nil)
}

// Validate validates a new deployment.
func (deploymentStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
deployment := obj.(*apps.Deployment)
opts := pod.GetValidationOptionsFromPodTemplate(&deployment.Spec.Template, nil)
return appsvalidation.ValidateDeployment(deployment, opts)
}
ReplicaSet实现的策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// pkg/registry/apps/replicaset/strategy.go
type rsStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}

// Strategy is the default logic that applies when creating and updating ReplicaSet objects.
var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}

func (rsStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
rs := obj.(*apps.ReplicaSet)
opts := pod.GetValidationOptionsFromPodTemplate(&rs.Spec.Template, nil)
return appsvalidation.ValidateReplicaSet(rs, opts)
}
https://groups.google.com/g/kubernetes-sig-apps/c/IGuguCg-vv8rnings for the creation of the given object.
func (rsStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newRS := obj.(*apps.ReplicaSet)
var warnings []string
if msgs := utilvalidation.IsDNS1123Label(newRS.Name); len(msgs) != 0 {
warnings = append(warnings, fmt.Sprintf("metadata.name: this is used in Pod names and hostnames, which can result in surprising behavior; a DNS label is recommended: %v", msgs))
}
warnings = append(warnings, pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newRS.Spec.Template, nil)...)
return warnings
}
Store如何与对应的Strategy关联
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 创建对应store时指定了Deployment对应的strategy
// pkg/registry/apps/deployment/storage/storage.go
// NewREST returns a RESTStorage object that will work against deployments.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *RollbackREST, error) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &apps.Deployment{} },
NewListFunc: func() runtime.Object { return &apps.DeploymentList{} },
DefaultQualifiedResource: apps.Resource("deployments"),
SingularQualifiedResource: apps.Resource("deployment"),

CreateStrategy: deployment.Strategy,
UpdateStrategy: deployment.Strategy,
DeleteStrategy: deployment.Strategy,
ResetFieldsStrategy: deployment.Strategy,

TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
return nil, nil, nil, err
}

statusStore := *store
statusStore.UpdateStrategy = deployment.StatusStrategy
statusStore.ResetFieldsStrategy = deployment.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, &RollbackREST{store: store}, nil
}


// 创建对应store时指定了ReplicaSet对应的strategy
// pkg/registry/apps/replicaset/storage/storage.go
// NewREST returns a RESTStorage object that will work against ReplicaSet.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &apps.ReplicaSet{} },
NewListFunc: func() runtime.Object { return &apps.ReplicaSetList{} },
PredicateFunc: replicaset.MatchReplicaSet,
DefaultQualifiedResource: apps.Resource("replicasets"),
SingularQualifiedResource: apps.Resource("replicaset"),

CreateStrategy: replicaset.Strategy,
UpdateStrategy: replicaset.Strategy,
DeleteStrategy: replicaset.Strategy,
ResetFieldsStrategy: replicaset.Strategy,

TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: replicaset.GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
return nil, nil, err
}

statusStore := *store
statusStore.UpdateStrategy = replicaset.StatusStrategy
statusStore.ResetFieldsStrategy = replicaset.StatusStrategy

return &REST{store}, &StatusREST{store: &statusStore}, nil
}
何时调用Strategy

例如CreateStrategy,在创建Deployment的时候会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go
func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
var finishCreate FinishFunc = finishNothing

// Init metadata as early as possible.
if objectMeta, err := meta.Accessor(obj); err != nil {
return nil, err
} else {
rest.FillObjectMetaSystemFields(objectMeta)
if len(objectMeta.GetGenerateName()) > 0 && len(objectMeta.GetName()) == 0 {
objectMeta.SetName(e.CreateStrategy.GenerateName(objectMeta.GetGenerateName()))
}
}

...
if err := rest.BeforeCreate(e.CreateStrategy, ctx, obj); err != nil {
return nil, err
}
...
return out, nil
}


REF:
1.pkg/registry/generic/registry/store.go
2.pkg/registry/apps/replicaset/strategy.go
3.pkg/registry/apps/replicaset/storage/storage.go
4.pkg/registry/apps/deployment/strategy.go
5.pkg/registry/apps/deployment/storage/storage.go