Improve fieldmanager tests and benchmarks
This commit is contained in:
@@ -48,6 +48,7 @@ go_test(
|
|||||||
"fieldmanager_test.go",
|
"fieldmanager_test.go",
|
||||||
"skipnonapplied_test.go",
|
"skipnonapplied_test.go",
|
||||||
],
|
],
|
||||||
|
data = ["//api/openapi-spec"],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
@@ -57,6 +58,8 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
|
||||||
|
"//vendor/k8s.io/kube-openapi/pkg/util/proto/testing:go_default_library",
|
||||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@@ -17,12 +17,16 @@ limitations under the License.
|
|||||||
package fieldmanager_test
|
package fieldmanager_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -30,9 +34,17 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||||
|
"k8s.io/kube-openapi/pkg/util/proto"
|
||||||
|
prototesting "k8s.io/kube-openapi/pkg/util/proto/testing"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var fakeSchema = prototesting.Fake{
|
||||||
|
Path: filepath.Join(
|
||||||
|
strings.Repeat(".."+string(filepath.Separator), 7),
|
||||||
|
"api", "openapi-spec", "swagger.json"),
|
||||||
|
}
|
||||||
|
|
||||||
type fakeObjectConvertor struct{}
|
type fakeObjectConvertor struct{}
|
||||||
|
|
||||||
func (c *fakeObjectConvertor) Convert(in, out, context interface{}) error {
|
func (c *fakeObjectConvertor) Convert(in, out, context interface{}) error {
|
||||||
@@ -54,34 +66,42 @@ func (d *fakeObjectDefaulter) Default(in runtime.Object) {}
|
|||||||
|
|
||||||
type TestFieldManager struct {
|
type TestFieldManager struct {
|
||||||
fieldManager fieldmanager.FieldManager
|
fieldManager fieldmanager.FieldManager
|
||||||
|
emptyObj runtime.Object
|
||||||
liveObj runtime.Object
|
liveObj runtime.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestFieldManager() TestFieldManager {
|
func NewTestFieldManager(gvk schema.GroupVersionKind) TestFieldManager {
|
||||||
gv := schema.GroupVersion{
|
d, err := fakeSchema.OpenAPISchema()
|
||||||
Group: "apps",
|
if err != nil {
|
||||||
Version: "v1",
|
panic(err)
|
||||||
|
}
|
||||||
|
m, err := proto.NewOpenAPIData(d)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := fieldmanager.NewCRDFieldManager(
|
f, err := fieldmanager.NewFieldManager(
|
||||||
nil,
|
m,
|
||||||
&fakeObjectConvertor{},
|
&fakeObjectConvertor{},
|
||||||
&fakeObjectDefaulter{},
|
&fakeObjectDefaulter{},
|
||||||
gv,
|
gvk.GroupVersion(),
|
||||||
gv,
|
gvk.GroupVersion(),
|
||||||
true,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
live := &unstructured.Unstructured{}
|
||||||
|
live.SetKind(gvk.Kind)
|
||||||
|
live.SetAPIVersion(gvk.GroupVersion().String())
|
||||||
return TestFieldManager{
|
return TestFieldManager{
|
||||||
fieldManager: f,
|
fieldManager: f,
|
||||||
liveObj: &unstructured.Unstructured{},
|
emptyObj: live,
|
||||||
|
liveObj: live.DeepCopyObject(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TestFieldManager) Reset() {
|
func (f *TestFieldManager) Reset() {
|
||||||
f.liveObj = &unstructured.Unstructured{}
|
f.liveObj = f.emptyObj.DeepCopyObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TestFieldManager) Apply(obj []byte, manager string, force bool) error {
|
func (f *TestFieldManager) Apply(obj []byte, manager string, force bool) error {
|
||||||
@@ -109,9 +129,10 @@ func (f *TestFieldManager) ManagedFields() []metav1.ManagedFieldsEntry {
|
|||||||
return accessor.GetManagedFields()
|
return accessor.GetManagedFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestUpdateApplyConflict tests that applying to an object, which wasn't created by apply, will give conflicts
|
// TestUpdateApplyConflict tests that applying to an object, which
|
||||||
|
// wasn't created by apply, will give conflicts
|
||||||
func TestUpdateApplyConflict(t *testing.T) {
|
func TestUpdateApplyConflict(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
|
||||||
|
|
||||||
patch := []byte(`{
|
patch := []byte(`{
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "apps/v1",
|
||||||
@@ -167,7 +188,7 @@ func TestUpdateApplyConflict(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyStripsFields(t *testing.T) {
|
func TestApplyStripsFields(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
|
||||||
|
|
||||||
newObj := &unstructured.Unstructured{
|
newObj := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]interface{}{
|
||||||
@@ -200,7 +221,7 @@ func TestApplyStripsFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestVersionCheck(t *testing.T) {
|
func TestVersionCheck(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
|
||||||
|
|
||||||
// patch has 'apiVersion: apps/v1' and live version is apps/v1 -> no errors
|
// patch has 'apiVersion: apps/v1' and live version is apps/v1 -> no errors
|
||||||
err := f.Apply([]byte(`{
|
err := f.Apply([]byte(`{
|
||||||
@@ -213,7 +234,7 @@ func TestVersionCheck(t *testing.T) {
|
|||||||
|
|
||||||
// patch has 'apiVersion: apps/v2' but live version is apps/v1 -> error
|
// patch has 'apiVersion: apps/v2' but live version is apps/v1 -> error
|
||||||
err = f.Apply([]byte(`{
|
err = f.Apply([]byte(`{
|
||||||
"apiVersion": "apps/v2",
|
"apiVersion": "apps/v1beta1",
|
||||||
"kind": "Deployment",
|
"kind": "Deployment",
|
||||||
}`), "fieldmanager_test", false)
|
}`), "fieldmanager_test", false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -231,10 +252,10 @@ func TestVersionCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyDoesNotStripLabels(t *testing.T) {
|
func TestApplyDoesNotStripLabels(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
|
|
||||||
err := f.Apply([]byte(`{
|
err := f.Apply([]byte(`{
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "v1",
|
||||||
"kind": "Pod",
|
"kind": "Pod",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -251,47 +272,200 @@ func TestApplyDoesNotStripLabels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var podBytes = []byte(`
|
||||||
|
{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"metadata": {
|
||||||
|
"labels": {
|
||||||
|
"app": "some-app",
|
||||||
|
"plugin1": "some-value",
|
||||||
|
"plugin2": "some-value",
|
||||||
|
"plugin3": "some-value",
|
||||||
|
"plugin4": "some-value"
|
||||||
|
},
|
||||||
|
"name": "some-name",
|
||||||
|
"namespace": "default",
|
||||||
|
"ownerReferences": [
|
||||||
|
{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"blockOwnerDeletion": true,
|
||||||
|
"controller": true,
|
||||||
|
"kind": "ReplicaSet",
|
||||||
|
"name": "some-name",
|
||||||
|
"uid": "0a9d2b9e-779e-11e7-b422-42010a8001be"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
"three",
|
||||||
|
"four",
|
||||||
|
"five",
|
||||||
|
"six",
|
||||||
|
"seven",
|
||||||
|
"eight",
|
||||||
|
"nine"
|
||||||
|
],
|
||||||
|
"env": [
|
||||||
|
{
|
||||||
|
"name": "VAR_3",
|
||||||
|
"valueFrom": {
|
||||||
|
"secretKeyRef": {
|
||||||
|
"key": "some-other-key",
|
||||||
|
"name": "some-oher-name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAR_2",
|
||||||
|
"valueFrom": {
|
||||||
|
"secretKeyRef": {
|
||||||
|
"key": "other-key",
|
||||||
|
"name": "other-name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAR_1",
|
||||||
|
"valueFrom": {
|
||||||
|
"secretKeyRef": {
|
||||||
|
"key": "some-key",
|
||||||
|
"name": "some-name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"image": "some-image-name",
|
||||||
|
"imagePullPolicy": "IfNotPresent",
|
||||||
|
"name": "some-name",
|
||||||
|
"resources": {
|
||||||
|
"requests": {
|
||||||
|
"cpu": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"terminationMessagePath": "/dev/termination-log",
|
||||||
|
"terminationMessagePolicy": "File",
|
||||||
|
"volumeMounts": [
|
||||||
|
{
|
||||||
|
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
|
||||||
|
"name": "default-token-hu5jz",
|
||||||
|
"readOnly": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dnsPolicy": "ClusterFirst",
|
||||||
|
"nodeName": "node-name",
|
||||||
|
"priority": 0,
|
||||||
|
"restartPolicy": "Always",
|
||||||
|
"schedulerName": "default-scheduler",
|
||||||
|
"securityContext": {},
|
||||||
|
"serviceAccount": "default",
|
||||||
|
"serviceAccountName": "default",
|
||||||
|
"terminationGracePeriodSeconds": 30,
|
||||||
|
"tolerations": [
|
||||||
|
{
|
||||||
|
"effect": "NoExecute",
|
||||||
|
"key": "node.kubernetes.io/not-ready",
|
||||||
|
"operator": "Exists",
|
||||||
|
"tolerationSeconds": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"effect": "NoExecute",
|
||||||
|
"key": "node.kubernetes.io/unreachable",
|
||||||
|
"operator": "Exists",
|
||||||
|
"tolerationSeconds": 300
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"volumes": [
|
||||||
|
{
|
||||||
|
"name": "default-token-hu5jz",
|
||||||
|
"secret": {
|
||||||
|
"defaultMode": 420,
|
||||||
|
"secretName": "default-token-hu5jz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"lastProbeTime": null,
|
||||||
|
"lastTransitionTime": "2019-07-08T09:31:18Z",
|
||||||
|
"status": "True",
|
||||||
|
"type": "Initialized"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lastProbeTime": null,
|
||||||
|
"lastTransitionTime": "2019-07-08T09:41:59Z",
|
||||||
|
"status": "True",
|
||||||
|
"type": "Ready"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lastProbeTime": null,
|
||||||
|
"lastTransitionTime": null,
|
||||||
|
"status": "True",
|
||||||
|
"type": "ContainersReady"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lastProbeTime": null,
|
||||||
|
"lastTransitionTime": "2019-07-08T09:31:18Z",
|
||||||
|
"status": "True",
|
||||||
|
"type": "PodScheduled"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"containerStatuses": [
|
||||||
|
{
|
||||||
|
"containerID": "docker://885e82a1ed0b7356541bb410a0126921ac42439607c09875cd8097dd5d7b5376",
|
||||||
|
"image": "some-image-name",
|
||||||
|
"imageID": "docker-pullable://some-image-id",
|
||||||
|
"lastState": {
|
||||||
|
"terminated": {
|
||||||
|
"containerID": "docker://d57290f9e00fad626b20d2dd87a3cf69bbc22edae07985374f86a8b2b4e39565",
|
||||||
|
"exitCode": 255,
|
||||||
|
"finishedAt": "2019-07-08T09:39:09Z",
|
||||||
|
"reason": "Error",
|
||||||
|
"startedAt": "2019-07-08T09:38:54Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "name",
|
||||||
|
"ready": true,
|
||||||
|
"restartCount": 6,
|
||||||
|
"state": {
|
||||||
|
"running": {
|
||||||
|
"startedAt": "2019-07-08T09:41:59Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hostIP": "10.0.0.1",
|
||||||
|
"phase": "Running",
|
||||||
|
"podIP": "10.0.0.1",
|
||||||
|
"qosClass": "BestEffort",
|
||||||
|
"startTime": "2019-07-08T09:31:18Z"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
func TestApplyNewObject(t *testing.T) {
|
||||||
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
|
|
||||||
|
if err := f.Apply(podBytes, "fieldmanager_test", false); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkApplyNewObject(b *testing.B) {
|
func BenchmarkApplyNewObject(b *testing.B) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
err := f.Apply([]byte(`{
|
err := f.Apply(podBytes, "fieldmanager_test", false)
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "Pod",
|
|
||||||
"metadata": {
|
|
||||||
"name": "b",
|
|
||||||
"namespace": "b",
|
|
||||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
|
||||||
},
|
|
||||||
"map": {
|
|
||||||
"fieldA": 1,
|
|
||||||
"fieldB": 1,
|
|
||||||
"fieldC": 1,
|
|
||||||
"fieldD": 1,
|
|
||||||
"fieldE": 1,
|
|
||||||
"fieldF": 1,
|
|
||||||
"fieldG": 1,
|
|
||||||
"fieldH": 1,
|
|
||||||
"fieldI": 1,
|
|
||||||
"fieldJ": 1,
|
|
||||||
"fieldK": 1,
|
|
||||||
"fieldL": 1,
|
|
||||||
"fieldM": 1,
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"value": true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}`), "fieldmanager_test", false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -300,48 +474,19 @@ func BenchmarkApplyNewObject(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkUpdateNewObject(b *testing.B) {
|
func BenchmarkUpdateNewObject(b *testing.B) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
|
|
||||||
y := `{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": {
|
|
||||||
"name": "b",
|
|
||||||
"namespace": "b",
|
|
||||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
|
||||||
},
|
|
||||||
"map": {
|
|
||||||
"fieldA": 1,
|
|
||||||
"fieldB": 1,
|
|
||||||
"fieldC": 1,
|
|
||||||
"fieldD": 1,
|
|
||||||
"fieldE": 1,
|
|
||||||
"fieldF": 1,
|
|
||||||
"fieldG": 1,
|
|
||||||
"fieldH": 1,
|
|
||||||
"fieldI": 1,
|
|
||||||
"fieldJ": 1,
|
|
||||||
"fieldK": 1,
|
|
||||||
"fieldL": 1,
|
|
||||||
"fieldM": 1,
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"value": true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
}`
|
|
||||||
newObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
newObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||||
if err := yaml.Unmarshal([]byte(y), &newObj.Object); err != nil {
|
if err := json.Unmarshal(podBytes, &newObj.Object); err != nil {
|
||||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
b.Fatalf("Failed to parse yaml object: %v", err)
|
||||||
}
|
}
|
||||||
|
newObj.SetManagedFields([]metav1.ManagedFieldsEntry{
|
||||||
|
{
|
||||||
|
Manager: "default",
|
||||||
|
Operation: "Update",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -355,137 +500,20 @@ func BenchmarkUpdateNewObject(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkRepeatedUpdate(b *testing.B) {
|
func BenchmarkRepeatedUpdate(b *testing.B) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
|
|
||||||
y1 := `{
|
var obj *corev1.Pod
|
||||||
"apiVersion": "apps/v1",
|
if err := json.Unmarshal(podBytes, &obj); err != nil {
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": {
|
|
||||||
"name": "b",
|
|
||||||
"namespace": "b",
|
|
||||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
|
||||||
},
|
|
||||||
"map": {
|
|
||||||
"fieldA": 1,
|
|
||||||
"fieldB": 1,
|
|
||||||
"fieldC": 1,
|
|
||||||
"fieldD": 1,
|
|
||||||
"fieldE": 1,
|
|
||||||
"fieldF": 1,
|
|
||||||
"fieldG": 1,
|
|
||||||
"fieldH": 1,
|
|
||||||
"fieldI": 1,
|
|
||||||
"fieldJ": 1,
|
|
||||||
"fieldK": 1,
|
|
||||||
"fieldL": 1,
|
|
||||||
"fieldM": 1,
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"value": true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
}`
|
|
||||||
obj1 := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
|
||||||
if err := yaml.Unmarshal([]byte(y1), &obj1.Object); err != nil {
|
|
||||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
b.Fatalf("Failed to parse yaml object: %v", err)
|
||||||
}
|
}
|
||||||
y2 := `{
|
obj.Spec.Containers[0].Image = "nginx:latest"
|
||||||
"apiVersion": "apps/v1",
|
objs := []*corev1.Pod{obj}
|
||||||
"kind": "Deployment",
|
obj = obj.DeepCopy()
|
||||||
"metadata": {
|
obj.Spec.Containers[0].Image = "nginx:4.3"
|
||||||
"name": "b",
|
objs = append(objs, obj)
|
||||||
"namespace": "b",
|
|
||||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
|
||||||
},
|
|
||||||
"map": {
|
|
||||||
"fieldA": 1,
|
|
||||||
"fieldB": 1,
|
|
||||||
"fieldC": 1,
|
|
||||||
"fieldD": 1,
|
|
||||||
"fieldE": 1,
|
|
||||||
"fieldF": 1,
|
|
||||||
"fieldG": 1,
|
|
||||||
"fieldH": 1,
|
|
||||||
"fieldI": 1,
|
|
||||||
"fieldJ": 1,
|
|
||||||
"fieldK": 1,
|
|
||||||
"fieldL": 1,
|
|
||||||
"fieldM": 1,
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"value": false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
}`
|
err := f.Apply(podBytes, "fieldmanager_apply", false)
|
||||||
obj2 := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
if err != nil {
|
||||||
if err := yaml.Unmarshal([]byte(y2), &obj2.Object); err != nil {
|
|
||||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
|
||||||
}
|
|
||||||
y3 := `{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": {
|
|
||||||
"name": "b",
|
|
||||||
"namespace": "b",
|
|
||||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
|
||||||
},
|
|
||||||
"map": {
|
|
||||||
"fieldA": 1,
|
|
||||||
"fieldB": 1,
|
|
||||||
"fieldC": 1,
|
|
||||||
"fieldD": 1,
|
|
||||||
"fieldE": 1,
|
|
||||||
"fieldF": 1,
|
|
||||||
"fieldG": 1,
|
|
||||||
"fieldH": 1,
|
|
||||||
"fieldI": 1,
|
|
||||||
"fieldJ": 1,
|
|
||||||
"fieldK": 1,
|
|
||||||
"fieldL": 1,
|
|
||||||
"fieldM": 1,
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"fieldN": {
|
|
||||||
"value": true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"fieldO": 1,
|
|
||||||
"fieldP": 1,
|
|
||||||
"fieldQ": 1,
|
|
||||||
"fieldR": 1,
|
|
||||||
"fieldS": 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
}`
|
|
||||||
obj3 := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
|
||||||
if err := yaml.Unmarshal([]byte(y3), &obj3.Object); err != nil {
|
|
||||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
objs := []*unstructured.Unstructured{obj1, obj2, obj3}
|
|
||||||
|
|
||||||
if err := f.Update(objs[0], "fieldmanager_0"); err != nil {
|
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,14 +521,10 @@ func BenchmarkRepeatedUpdate(b *testing.B) {
|
|||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := f.Update(objs[2], "fieldmanager_2"); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
err := f.Update(objs[n%3], fmt.Sprintf("fieldmanager_%d", n%3))
|
err := f.Update(objs[n%len(objs)], fmt.Sprintf("fieldmanager_%d", n%len(objs)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -509,10 +533,10 @@ func BenchmarkRepeatedUpdate(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyFailsWithManagedFields(t *testing.T) {
|
func TestApplyFailsWithManagedFields(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
|
|
||||||
err := f.Apply([]byte(`{
|
err := f.Apply([]byte(`{
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "v1",
|
||||||
"kind": "Pod",
|
"kind": "Pod",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"managedFields": [
|
"managedFields": [
|
||||||
@@ -529,10 +553,10 @@ func TestApplyFailsWithManagedFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestApplySuccessWithNoManagedFields(t *testing.T) {
|
func TestApplySuccessWithNoManagedFields(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
|
|
||||||
err := f.Apply([]byte(`{
|
err := f.Apply([]byte(`{
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "v1",
|
||||||
"kind": "Pod",
|
"kind": "Pod",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"labels": {
|
"labels": {
|
||||||
|
@@ -28,20 +28,29 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeObjectCreater struct{}
|
type fakeObjectCreater struct {
|
||||||
|
gvk schema.GroupVersionKind
|
||||||
|
}
|
||||||
|
|
||||||
var _ runtime.ObjectCreater = &fakeObjectCreater{}
|
var _ runtime.ObjectCreater = &fakeObjectCreater{}
|
||||||
|
|
||||||
func (*fakeObjectCreater) New(_ schema.GroupVersionKind) (runtime.Object, error) {
|
func (f *fakeObjectCreater) New(_ schema.GroupVersionKind) (runtime.Object, error) {
|
||||||
return &unstructured.Unstructured{Object: map[string]interface{}{}}, nil
|
u := unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||||
|
u.SetAPIVersion(f.gvk.GroupVersion().String())
|
||||||
|
u.SetKind(f.gvk.Kind)
|
||||||
|
return &u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNoUpdateBeforeFirstApply(t *testing.T) {
|
func TestNoUpdateBeforeFirstApply(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
f.fieldManager = fieldmanager.NewSkipNonAppliedManager(f.fieldManager, &fakeObjectCreater{}, schema.GroupVersionKind{})
|
f.fieldManager = fieldmanager.NewSkipNonAppliedManager(
|
||||||
|
f.fieldManager,
|
||||||
|
&fakeObjectCreater{gvk: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}},
|
||||||
|
schema.GroupVersionKind{},
|
||||||
|
)
|
||||||
|
|
||||||
if err := f.Apply([]byte(`{
|
if err := f.Apply([]byte(`{
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "v1",
|
||||||
"kind": "Pod",
|
"kind": "Pod",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "pod",
|
"name": "pod",
|
||||||
@@ -66,12 +75,18 @@ func TestNoUpdateBeforeFirstApply(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpateBeforeFirstApply(t *testing.T) {
|
func TestUpdateBeforeFirstApply(t *testing.T) {
|
||||||
f := NewTestFieldManager()
|
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||||
f.fieldManager = fieldmanager.NewSkipNonAppliedManager(f.fieldManager, &fakeObjectCreater{}, schema.GroupVersionKind{})
|
f.fieldManager = fieldmanager.NewSkipNonAppliedManager(
|
||||||
|
f.fieldManager,
|
||||||
|
&fakeObjectCreater{gvk: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}},
|
||||||
|
schema.GroupVersionKind{},
|
||||||
|
)
|
||||||
|
|
||||||
updatedObj := &corev1.Pod{}
|
updatedObj := &corev1.Pod{}
|
||||||
updatedObj.ObjectMeta.Labels = map[string]string{"app": "nginx"}
|
updatedObj.Kind = "Pod"
|
||||||
|
updatedObj.APIVersion = "v1"
|
||||||
|
updatedObj.ObjectMeta.Labels = map[string]string{"app": "my-nginx"}
|
||||||
|
|
||||||
if err := f.Update(updatedObj, "fieldmanager_test_update"); err != nil {
|
if err := f.Update(updatedObj, "fieldmanager_test_update"); err != nil {
|
||||||
t.Fatalf("failed to update object: %v", err)
|
t.Fatalf("failed to update object: %v", err)
|
||||||
@@ -82,7 +97,7 @@ func TestUpateBeforeFirstApply(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appliedBytes := []byte(`{
|
appliedBytes := []byte(`{
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "v1",
|
||||||
"kind": "Pod",
|
"kind": "Pod",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "pod",
|
"name": "pod",
|
||||||
@@ -102,7 +117,7 @@ func TestUpateBeforeFirstApply(t *testing.T) {
|
|||||||
t.Fatalf("Expecting to get one conflict but got %v", err)
|
t.Fatalf("Expecting to get one conflict but got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if e, a := ".spec.containers", apiStatus.Status().Details.Causes[0].Field; e != a {
|
if e, a := ".metadata.labels.app", apiStatus.Status().Details.Causes[0].Field; e != a {
|
||||||
t.Fatalf("Expecting to conflict on field %q but conflicted on field %q: %v", e, a, err)
|
t.Fatalf("Expecting to conflict on field %q but conflicted on field %q: %v", e, a, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user