kubernetes lastappliedmanager_test 源码
kubernetes lastappliedmanager_test 代码
文件路径:/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/lastappliedmanager_test.go
/*
Copyright 2020 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 fieldmanager
import (
"fmt"
"reflect"
"testing"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
"sigs.k8s.io/structured-merge-diff/v4/merge"
"sigs.k8s.io/yaml"
)
type testArgs struct {
lastApplied []byte
original []byte
applied []byte
fieldManager string
expectConflictSet *fieldpath.Set
}
// TestApplyUsingLastAppliedAnnotation tests that applying to an object
// created with the client-side apply last-applied annotation
// will not give conflicts
func TestApplyUsingLastAppliedAnnotation(t *testing.T) {
f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
tests := []testArgs{
{
fieldManager: "kubectl",
lastApplied: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image-v1
- name: my-c2
image: my-image2
`),
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app # missing from last-applied
spec:
replicas: 100 # does not match last-applied
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image-v2 # does no match last-applied
# note that second container in last-applied is missing
`),
applied: []byte(`
# test conflicts due to fields not allowed by last-applied
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-new-label # NOT allowed: update label
spec:
replicas: 333 # NOT allowed: update replicas
selector:
matchLabels:
app: my-new-label # allowed: update label
template:
metadata:
labels:
app: my-new-label # allowed: update-label
spec:
containers:
- name: my-c
image: my-image-new # NOT allowed: update image
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata", "labels", "app"),
fieldpath.MakePathOrDie("spec", "replicas"),
fieldpath.MakePathOrDie("spec", "template", "spec", "containers", fieldpath.KeyByFields("name", "my-c"), "image"),
),
},
{
fieldManager: "kubectl",
lastApplied: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 100 # does not match last applied
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
applied: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-new-label
spec:
replicas: 3 # expect conflict
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "replicas"),
),
},
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 100
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
applied: []byte(`
# applied object matches original
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 100
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
},
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
applied: []byte(`
# test allowed update with no conflicts
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-new-label # update label
spec:
replicas: 333 # update replicas
selector:
matchLabels:
app: my-new-label # update label
template:
metadata:
labels:
app: my-new-label # update-label
spec:
containers:
- name: my-c
image: my-image
`),
},
{
fieldManager: "not_kubectl",
lastApplied: []byte(`
# expect conflicts because field manager is NOT kubectl
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image-v1
`),
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 100 # does not match last-applied
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image-v2 # does no match last-applied
`),
applied: []byte(`
# test conflicts due to fields not allowed by last-applied
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-new-label # update label
spec:
replicas: 333 # update replicas
selector:
matchLabels:
app: my-new-label # update label
template:
metadata:
labels:
app: my-new-label # update-label
spec:
containers:
- name: my-c
image: my-image-new # update image
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata", "labels", "app"),
fieldpath.MakePathOrDie("spec", "replicas"),
fieldpath.MakePathOrDie("spec", "selector"), // selector is atomic
fieldpath.MakePathOrDie("spec", "template", "metadata", "labels", "app"),
fieldpath.MakePathOrDie("spec", "template", "spec", "containers", fieldpath.KeyByFields("name", "my-c"), "image"),
),
},
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
applied: []byte(`
# test allowed update with no conflicts
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-new-label
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-new-image # update image
`),
},
{
fieldManager: "not_kubectl",
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 100
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-image
`),
applied: []byte(`
# expect changes to fail because field manager is not kubectl
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-new-label # update label
spec:
replicas: 3 # update replicas
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-c
image: my-new-image # update image
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata", "labels", "app"),
fieldpath.MakePathOrDie("spec", "replicas"),
fieldpath.MakePathOrDie("spec", "template", "spec", "containers", fieldpath.KeyByFields("name", "my-c"), "image"),
),
},
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
`),
applied: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 100 # update replicas
`),
},
{
fieldManager: "kubectl",
lastApplied: []byte(`
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
`),
original: []byte(`
apiVersion: apps/v1 # expect conflict due to apiVersion mismatch with last-applied
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
`),
applied: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 100 # update replicas
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "replicas"),
),
},
{
fieldManager: "kubectl",
lastApplied: []byte(`
apiVerison: foo
kind: bar
spec: expect conflict due to invalid object
`),
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
`),
applied: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 100 # update replicas
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "replicas"),
),
},
{
fieldManager: "kubectl",
// last-applied is empty
lastApplied: []byte{},
original: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
`),
applied: []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 100 # update replicas
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "replicas"),
),
},
}
testConflicts(t, f, tests)
}
func TestServiceApply(t *testing.T) {
f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Service"))
tests := []testArgs{
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: v1
kind: Service
metadata:
name: test
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: 8443
selector:
old: test
`),
applied: []byte(`
# All accepted while using the same field manager
apiVersion: v1
kind: Service
metadata:
name: test
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: 8444
selector:
new: test
`),
},
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: v1
kind: Service
metadata:
name: test
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: 8443
selector:
old: test
`),
applied: []byte(`
# Allowed to remove selectors while using the same field manager
apiVersion: v1
kind: Service
metadata:
name: test
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: 8444
selector: {}
`),
},
{
fieldManager: "not_kubectl",
original: []byte(`
apiVersion: v1
kind: Service
metadata:
name: test
spec:
ports:
- name: https
port: 443
protocol: TCP # TODO: issue - this is a defaulted field, should not be required in a new spec
targetPort: 8443
selector:
old: test
`),
applied: []byte(`
# test selector update not allowed by last-applied
apiVersion: v1
kind: Service
metadata:
name: test
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: 8444
selector:
new: test
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "selector"), // selector is atomic
fieldpath.MakePathOrDie("spec", "ports", fieldpath.KeyByFields("port", 443, "protocol", "TCP"), "targetPort"),
),
},
}
testConflicts(t, f, tests)
}
func TestReplicationControllerApply(t *testing.T) {
f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "ReplicationController"))
tests := []testArgs{
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: v1
kind: ReplicationController
metadata:
name: test
spec:
replicas: 0
selector:
old: test
`),
applied: []byte(`
# All accepted while using the same field manager
apiVersion: v1
kind: ReplicationController
metadata:
name: test
spec:
replicas: 3
selector:
new: test
`),
},
{
fieldManager: "not_kubectl",
original: []byte(`
apiVersion: v1
kind: ReplicationController
metadata:
name: test
spec:
replicas: 0
selector:
old: test
`),
applied: []byte(`
# test selector update not allowed by last-applied
apiVersion: v1
kind: ReplicationController
metadata:
name: test
spec:
replicas: 3
selector:
new: test
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "selector"), // selector is atomic
fieldpath.MakePathOrDie("spec", "replicas"),
),
},
}
testConflicts(t, f, tests)
}
func TestPodApply(t *testing.T) {
f := NewDefaultTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
tests := []testArgs{
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeName: definetlyControlPlane
nodeSelector:
node-role.kubernetes.io/master: ""
`),
applied: []byte(`
# All accepted while using the same field manager
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeSelector:
node-role.kubernetes.io/worker: ""
`),
},
{
fieldManager: "not_kubectl",
original: []byte(`
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeName: definetlyControlPlane
nodeSelector:
node-role.kubernetes.io/master: ""
`),
applied: []byte(`
# test selector update not allowed by last-applied
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeName: definetlyControlPlane
nodeSelector:
node-role.kubernetes.io/master: ""
otherNodeType: ""
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "nodeSelector"), // selector is atomic
),
},
{
fieldManager: "not_kubectl",
original: []byte(`
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeName: definetlyControlPlane
nodeSelector:
node-role.kubernetes.io/master: ""
`),
applied: []byte(`
# purging selector not allowed for different manager
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeName: another
nodeSelector: {}
`),
expectConflictSet: fieldpath.NewSet(
fieldpath.MakePathOrDie("spec", "nodeSelector"), // selector is atomic
fieldpath.MakePathOrDie("spec", "nodeName"),
),
},
{
fieldManager: "kubectl",
original: []byte(`
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeName: definetlyControlPlane
nodeSelector:
node-role.kubernetes.io/master: ""
`),
applied: []byte(`
# same manager could purge nodeSelector
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: test
spec:
containers:
- args:
- -v=2
command:
- controller
image: some.registry/app:latest
name: doJob
nodeName: another
nodeSelector: {}
`),
},
}
testConflicts(t, f, tests)
}
func testConflicts(t *testing.T, f TestFieldManager, tests []testArgs) {
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
f.Reset()
originalObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
if err := yaml.Unmarshal(test.original, &originalObj.Object); err != nil {
t.Errorf("error decoding YAML: %v", err)
}
if test.lastApplied == nil {
test.lastApplied = test.original
}
if err := setLastAppliedFromEncoded(originalObj, test.lastApplied); err != nil {
t.Errorf("failed to set last applied: %v", err)
}
if err := f.Update(originalObj, "test_client_side_apply"); err != nil {
t.Errorf("failed to apply object: %v", err)
}
appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
if err := yaml.Unmarshal(test.applied, &appliedObj.Object); err != nil {
t.Errorf("error decoding YAML: %v", err)
}
err := f.Apply(appliedObj, test.fieldManager, false)
if test.expectConflictSet == nil {
if err != nil {
t.Errorf("expected no error but got %v", err)
}
} else {
if err == nil || !apierrors.IsConflict(err) {
t.Errorf("expected to get conflicts but got %v", err)
}
expectedConflicts := merge.Conflicts{}
test.expectConflictSet.Iterate(func(p fieldpath.Path) {
expectedConflicts = append(expectedConflicts, merge.Conflict{
Manager: fmt.Sprintf(`{"manager":"test_client_side_apply","operation":"Update","apiVersion":"%s"}`, f.APIVersion()),
Path: p,
})
})
expectedConflictErr := internal.NewConflictError(expectedConflicts)
if !reflect.DeepEqual(expectedConflictErr, err) {
t.Errorf("expected to get\n%+v\nbut got\n%+v", expectedConflictErr, err)
}
// Yet force should resolve all conflicts
err = f.Apply(appliedObj, test.fieldManager, true)
if err != nil {
t.Errorf("unexpected error during force ownership apply: %v", err)
}
}
// Eventually resource should contain applied changes
if !apiequality.Semantic.DeepDerivative(appliedObj, f.Get()) {
t.Errorf("expected equal resource: \n%#v, got: \n%#v", appliedObj, f.Get())
}
})
}
}
相关信息
相关文章
kubernetes buildmanagerinfo 源码
kubernetes capmanagers_test 源码
kubernetes fieldmanager_test 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦