mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-17 02:06:23 +00:00
Refactor
This commit is contained in:
@@ -82,7 +82,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
|
||||
@@ -74,7 +74,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/audit:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library",
|
||||
@@ -103,7 +103,7 @@ filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:all-srcs",
|
||||
],
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 apply
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// DecodeManagedFields converts ManagedFields from the wire format (api format)
|
||||
// to the format used by sigs.k8s.io/structured-merge-diff
|
||||
func DecodeManagedFields(encodedManagedFields map[string]metav1.VersionedFieldSet) (managedFields fieldpath.ManagedFields, err error) {
|
||||
managedFields = make(map[string]*fieldpath.VersionedSet, len(encodedManagedFields))
|
||||
for manager, encodedVersionedSet := range encodedManagedFields {
|
||||
managedFields[manager], err = decodeVersionedSet(&encodedVersionedSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding versioned set for %v: %v", manager, err)
|
||||
}
|
||||
}
|
||||
return managedFields, nil
|
||||
}
|
||||
|
||||
func decodeVersionedSet(encodedVersionedSet *metav1.VersionedFieldSet) (versionedSet *fieldpath.VersionedSet, err error) {
|
||||
versionedSet = &fieldpath.VersionedSet{}
|
||||
versionedSet.APIVersion = fieldpath.APIVersion(encodedVersionedSet.APIVersion)
|
||||
versionedSet.Set = &fieldpath.Set{}
|
||||
err = decodeSetInto(&encodedVersionedSet.Fields, versionedSet.Set)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding set: %v", err)
|
||||
}
|
||||
return versionedSet, nil
|
||||
}
|
||||
|
||||
func decodeSetInto(encodedSet *metav1.FieldSet, set *fieldpath.Set) (err error) {
|
||||
m, err := decodePathElementSet(encodedSet.Members)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding path element set: %v", err)
|
||||
}
|
||||
set.Members = *m
|
||||
c, err := decodeSetNodeMap(encodedSet.Children)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding children: %v", err)
|
||||
}
|
||||
set.Children = *c
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodePathElementSet(encodedPathElementSet []metav1.FieldPathElement) (pathElementSet *fieldpath.PathElementSet, err error) {
|
||||
pathElementSet = &fieldpath.PathElementSet{}
|
||||
for _, encodedPathElement := range encodedPathElementSet {
|
||||
pathElement, err := decodePathElement(&encodedPathElement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding path element: %v", err)
|
||||
}
|
||||
pathElementSet.Insert(*pathElement)
|
||||
}
|
||||
return pathElementSet, nil
|
||||
}
|
||||
|
||||
func decodeSetNodeMap(encodedSetNodeMap []metav1.FieldSetNode) (setNodeMap *fieldpath.SetNodeMap, err error) {
|
||||
setNodeMap = &fieldpath.SetNodeMap{}
|
||||
for _, encodedSetNode := range encodedSetNodeMap {
|
||||
pathElement, err := decodePathElement(&encodedSetNode.PathElement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding path element: %v", err)
|
||||
}
|
||||
set := setNodeMap.Descend(*pathElement)
|
||||
decodeSetInto(encodedSetNode.Set, set)
|
||||
}
|
||||
return setNodeMap, nil
|
||||
}
|
||||
|
||||
func decodePathElement(encodedPathElement *metav1.FieldPathElement) (pathElement *fieldpath.PathElement, err error) {
|
||||
if encodedPathElement == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !validateOneOf(encodedPathElement.FieldName, encodedPathElement.Key, encodedPathElement.Value, encodedPathElement.Index) {
|
||||
return nil, fmt.Errorf("too many fields set: %v", encodedPathElement)
|
||||
}
|
||||
|
||||
pathElement = &fieldpath.PathElement{}
|
||||
pathElement.FieldName = encodedPathElement.FieldName
|
||||
if encodedPathElement.Key != nil {
|
||||
pathElement.Key = make([]value.Field, len(encodedPathElement.Key))
|
||||
for i, encodedField := range encodedPathElement.Key {
|
||||
pathElement.Key[i], err = decodeField(&encodedField)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding field: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
pathElement.Value, err = decodeValue(encodedPathElement.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding value: %v", err)
|
||||
}
|
||||
if encodedPathElement.Index != nil {
|
||||
v := int(*encodedPathElement.Index)
|
||||
pathElement.Index = &v
|
||||
}
|
||||
return pathElement, nil
|
||||
}
|
||||
|
||||
func decodeField(encodedField *metav1.FieldNameValuePair) (field value.Field, err error) {
|
||||
field = value.Field{}
|
||||
if encodedField == nil {
|
||||
return field, fmt.Errorf("unexpected nil pointer")
|
||||
}
|
||||
field.Name = encodedField.Name
|
||||
v, err := decodeValue(&encodedField.Value)
|
||||
if err != nil {
|
||||
return field, fmt.Errorf("error decoding value: %v", err)
|
||||
}
|
||||
if v != nil {
|
||||
field.Value = *v
|
||||
}
|
||||
return field, nil
|
||||
}
|
||||
|
||||
func decodeValue(encodedValue *metav1.FieldValue) (v *value.Value, err error) {
|
||||
if encodedValue == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !validateOneOf(encodedValue.FloatValue, encodedValue.IntValue, encodedValue.StringValue, encodedValue.BooleanValue) {
|
||||
return nil, fmt.Errorf("too many fields set: %v", encodedValue)
|
||||
}
|
||||
|
||||
v = &value.Value{}
|
||||
if encodedValue.FloatValue != nil {
|
||||
f, err := strconv.ParseFloat(*encodedValue.FloatValue, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing float: %v", err)
|
||||
}
|
||||
v.FloatValue = (*value.Float)(&f)
|
||||
}
|
||||
if encodedValue.IntValue != nil {
|
||||
i := int64(*encodedValue.IntValue)
|
||||
v.IntValue = (*value.Int)(&i)
|
||||
}
|
||||
v.StringValue = (*value.String)(encodedValue.StringValue)
|
||||
v.BooleanValue = (*value.Boolean)(encodedValue.BooleanValue)
|
||||
v.Null = encodedValue.Null
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func validateOneOf(fields ...interface{}) bool {
|
||||
nonNilCount := 0
|
||||
for _, field := range fields {
|
||||
if !reflect.ValueOf(field).IsNil() {
|
||||
nonNilCount++
|
||||
}
|
||||
}
|
||||
return nonNilCount <= 1
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 apply
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestValidateOneOf tests that validateOneOf returns true if <= 1
|
||||
// non-nil fields are passed to it are and false otherwise
|
||||
func TestValidateOneOf(t *testing.T) {
|
||||
var nilValue *string
|
||||
nonNilValue := strPtr("test")
|
||||
|
||||
tests := []struct {
|
||||
fields []interface{}
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
fields: []interface{}{},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nilValue},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nonNilValue},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nilValue, nonNilValue, nilValue},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nonNilValue, nonNilValue},
|
||||
expected: false,
|
||||
}, {
|
||||
fields: []interface{}{nilValue, nonNilValue, nonNilValue, nilValue},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
actual := validateOneOf(tc.fields...)
|
||||
if actual != tc.expected {
|
||||
t.Errorf("[%v]expected validateOneOf(%v) to return:\n\t%+v\nbut got:\n\t%+v", i, tc.fields, tc.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func strPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 apply
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
)
|
||||
|
||||
// DecodeObjectManagedFields extracts and converts the objects ManagedFields into a fieldpath.ManagedFields.
|
||||
func DecodeObjectManagedFields(from runtime.Object) (fieldpath.ManagedFields, error) {
|
||||
if from == nil {
|
||||
return make(map[string]*fieldpath.VersionedSet), nil
|
||||
}
|
||||
accessor, err := meta.Accessor(from)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get accessor: %v", err)
|
||||
}
|
||||
accessor.SetManagedFields(nil)
|
||||
|
||||
managed, err := DecodeManagedFields(accessor.GetManagedFields())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert managed fields from API: %v", err)
|
||||
}
|
||||
return managed, err
|
||||
}
|
||||
|
||||
// EncodeObjectManagedFields converts and stores the fieldpathManagedFields into the objects ManagedFields
|
||||
func EncodeObjectManagedFields(obj runtime.Object, fields fieldpath.ManagedFields) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get accessor: %v", err)
|
||||
}
|
||||
|
||||
managed, err := EncodeManagedFields(fields)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert back managed fields to API: %v", err)
|
||||
}
|
||||
accessor.SetManagedFields(managed)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["fieldmanager.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager",
|
||||
importpath = "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//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/apiserver/pkg/endpoints/handlers/fieldmanager/internal:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/merge:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
package fieldmanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
|
||||
openapiproto "k8s.io/kube-openapi/pkg/util/proto"
|
||||
"sigs.k8s.io/structured-merge-diff/merge"
|
||||
)
|
||||
@@ -30,7 +31,7 @@ const applyManager = "apply"
|
||||
// FieldManager updates the managed fields and merge applied
|
||||
// configurations.
|
||||
type FieldManager struct {
|
||||
typeConverter TypeConverter
|
||||
typeConverter internal.TypeConverter
|
||||
objectConverter runtime.ObjectConvertor
|
||||
objectDefaulter runtime.ObjectDefaulter
|
||||
groupVersion schema.GroupVersion
|
||||
@@ -40,7 +41,7 @@ type FieldManager struct {
|
||||
// NewFieldManager creates a new FieldManager that merges apply requests
|
||||
// and update managed fields for other types of requests.
|
||||
func NewFieldManager(models openapiproto.Models, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, gv schema.GroupVersion) (*FieldManager, error) {
|
||||
typeConverter, err := NewTypeConverter(models)
|
||||
typeConverter, err := internal.NewTypeConverter(models)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -50,7 +51,7 @@ func NewFieldManager(models openapiproto.Models, objectConverter runtime.ObjectC
|
||||
objectDefaulter: objectDefaulter,
|
||||
groupVersion: gv,
|
||||
updater: merge.Updater{
|
||||
Converter: NewVersionConverter(typeConverter, objectConverter),
|
||||
Converter: internal.NewVersionConverter(typeConverter, objectConverter),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -68,7 +69,7 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (r
|
||||
return nil, fmt.Errorf("failed to convert live object to proper version: %v", err)
|
||||
}
|
||||
|
||||
managed, err := DecodeObjectManagedFields(newObj)
|
||||
managed, err := internal.DecodeObjectManagedFields(newObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode managed fields: %v", err)
|
||||
}
|
||||
@@ -87,7 +88,7 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (r
|
||||
return nil, fmt.Errorf("failed to update ManagedFields: %v", err)
|
||||
}
|
||||
|
||||
if err := EncodeObjectManagedFields(newObj, managed); err != nil {
|
||||
if err := internal.EncodeObjectManagedFields(newObj, managed); err != nil {
|
||||
return nil, fmt.Errorf("failed to encode managed fields: %v", err)
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ func (f *FieldManager) Apply(liveObj runtime.Object, patch []byte, force bool) (
|
||||
return nil, fmt.Errorf("failed to convert live object to proper version: %v", err)
|
||||
}
|
||||
|
||||
managed, err := DecodeObjectManagedFields(liveObj)
|
||||
managed, err := internal.DecodeObjectManagedFields(liveObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode managed fields: %v", err)
|
||||
}
|
||||
@@ -128,7 +129,7 @@ func (f *FieldManager) Apply(liveObj runtime.Object, patch []byte, force bool) (
|
||||
return nil, fmt.Errorf("failed to convert new typed object to object: %v", err)
|
||||
}
|
||||
|
||||
if err := EncodeObjectManagedFields(newObj, managed); err != nil {
|
||||
if err := internal.EncodeObjectManagedFields(newObj, managed); err != nil {
|
||||
return nil, fmt.Errorf("failed to encode managed fields: %v", err)
|
||||
}
|
||||
|
||||
@@ -3,16 +3,13 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"decode.go",
|
||||
"encode.go",
|
||||
"fieldmanager.go",
|
||||
"gvkparser.go",
|
||||
"managedfields.go",
|
||||
"typeconverter.go",
|
||||
"versionconverter.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/handlers/apply",
|
||||
importpath = "k8s.io/apiserver/pkg/endpoints/handlers/apply",
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal",
|
||||
importpath = "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
@@ -33,8 +30,7 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"decode_test.go",
|
||||
"roundtrip_test.go",
|
||||
"managedfields_test.go",
|
||||
"typeconverter_test.go",
|
||||
"versionconverter_test.go",
|
||||
],
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -14,22 +14,204 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EncodeManagedFields converts ManagedFields from the the format used by
|
||||
// DecodeObjectManagedFields extracts and converts the objects ManagedFields into a fieldpath.ManagedFields.
|
||||
func DecodeObjectManagedFields(from runtime.Object) (fieldpath.ManagedFields, error) {
|
||||
if from == nil {
|
||||
return make(map[string]*fieldpath.VersionedSet), nil
|
||||
}
|
||||
accessor, err := meta.Accessor(from)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get accessor: %v", err)
|
||||
}
|
||||
accessor.SetManagedFields(nil)
|
||||
|
||||
managed, err := decodeManagedFields(accessor.GetManagedFields())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert managed fields from API: %v", err)
|
||||
}
|
||||
return managed, err
|
||||
}
|
||||
|
||||
// EncodeObjectManagedFields converts and stores the fieldpathManagedFields into the objects ManagedFields
|
||||
func EncodeObjectManagedFields(obj runtime.Object, fields fieldpath.ManagedFields) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get accessor: %v", err)
|
||||
}
|
||||
|
||||
managed, err := encodeManagedFields(fields)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert back managed fields to API: %v", err)
|
||||
}
|
||||
accessor.SetManagedFields(managed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeManagedFields converts ManagedFields from the wire format (api format)
|
||||
// to the format used by sigs.k8s.io/structured-merge-diff
|
||||
func decodeManagedFields(encodedManagedFields map[string]metav1.VersionedFieldSet) (managedFields fieldpath.ManagedFields, err error) {
|
||||
managedFields = make(map[string]*fieldpath.VersionedSet, len(encodedManagedFields))
|
||||
for manager, encodedVersionedSet := range encodedManagedFields {
|
||||
managedFields[manager], err = decodeVersionedSet(&encodedVersionedSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding versioned set for %v: %v", manager, err)
|
||||
}
|
||||
}
|
||||
return managedFields, nil
|
||||
}
|
||||
|
||||
func decodeVersionedSet(encodedVersionedSet *metav1.VersionedFieldSet) (versionedSet *fieldpath.VersionedSet, err error) {
|
||||
versionedSet = &fieldpath.VersionedSet{}
|
||||
versionedSet.APIVersion = fieldpath.APIVersion(encodedVersionedSet.APIVersion)
|
||||
versionedSet.Set = &fieldpath.Set{}
|
||||
err = decodeSetInto(&encodedVersionedSet.Fields, versionedSet.Set)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding set: %v", err)
|
||||
}
|
||||
return versionedSet, nil
|
||||
}
|
||||
|
||||
func decodeSetInto(encodedSet *metav1.FieldSet, set *fieldpath.Set) (err error) {
|
||||
m, err := decodePathElementSet(encodedSet.Members)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding path element set: %v", err)
|
||||
}
|
||||
set.Members = *m
|
||||
c, err := decodeSetNodeMap(encodedSet.Children)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding children: %v", err)
|
||||
}
|
||||
set.Children = *c
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodePathElementSet(encodedPathElementSet []metav1.FieldPathElement) (pathElementSet *fieldpath.PathElementSet, err error) {
|
||||
pathElementSet = &fieldpath.PathElementSet{}
|
||||
for _, encodedPathElement := range encodedPathElementSet {
|
||||
pathElement, err := decodePathElement(&encodedPathElement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding path element: %v", err)
|
||||
}
|
||||
pathElementSet.Insert(*pathElement)
|
||||
}
|
||||
return pathElementSet, nil
|
||||
}
|
||||
|
||||
func decodeSetNodeMap(encodedSetNodeMap []metav1.FieldSetNode) (setNodeMap *fieldpath.SetNodeMap, err error) {
|
||||
setNodeMap = &fieldpath.SetNodeMap{}
|
||||
for _, encodedSetNode := range encodedSetNodeMap {
|
||||
pathElement, err := decodePathElement(&encodedSetNode.PathElement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding path element: %v", err)
|
||||
}
|
||||
set := setNodeMap.Descend(*pathElement)
|
||||
decodeSetInto(encodedSetNode.Set, set)
|
||||
}
|
||||
return setNodeMap, nil
|
||||
}
|
||||
|
||||
func decodePathElement(encodedPathElement *metav1.FieldPathElement) (pathElement *fieldpath.PathElement, err error) {
|
||||
if encodedPathElement == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !validateOneOf(encodedPathElement.FieldName, encodedPathElement.Key, encodedPathElement.Value, encodedPathElement.Index) {
|
||||
return nil, fmt.Errorf("too many fields set: %v", encodedPathElement)
|
||||
}
|
||||
|
||||
pathElement = &fieldpath.PathElement{}
|
||||
pathElement.FieldName = encodedPathElement.FieldName
|
||||
if encodedPathElement.Key != nil {
|
||||
pathElement.Key = make([]value.Field, len(encodedPathElement.Key))
|
||||
for i, encodedField := range encodedPathElement.Key {
|
||||
pathElement.Key[i], err = decodeField(&encodedField)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding field: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
pathElement.Value, err = decodeValue(encodedPathElement.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding value: %v", err)
|
||||
}
|
||||
if encodedPathElement.Index != nil {
|
||||
v := int(*encodedPathElement.Index)
|
||||
pathElement.Index = &v
|
||||
}
|
||||
return pathElement, nil
|
||||
}
|
||||
|
||||
func decodeField(encodedField *metav1.FieldNameValuePair) (field value.Field, err error) {
|
||||
field = value.Field{}
|
||||
if encodedField == nil {
|
||||
return field, fmt.Errorf("unexpected nil pointer")
|
||||
}
|
||||
field.Name = encodedField.Name
|
||||
v, err := decodeValue(&encodedField.Value)
|
||||
if err != nil {
|
||||
return field, fmt.Errorf("error decoding value: %v", err)
|
||||
}
|
||||
if v != nil {
|
||||
field.Value = *v
|
||||
}
|
||||
return field, nil
|
||||
}
|
||||
|
||||
func decodeValue(encodedValue *metav1.FieldValue) (v *value.Value, err error) {
|
||||
if encodedValue == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !validateOneOf(encodedValue.FloatValue, encodedValue.IntValue, encodedValue.StringValue, encodedValue.BooleanValue) {
|
||||
return nil, fmt.Errorf("too many fields set: %v", encodedValue)
|
||||
}
|
||||
|
||||
v = &value.Value{}
|
||||
if encodedValue.FloatValue != nil {
|
||||
f, err := strconv.ParseFloat(*encodedValue.FloatValue, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing float: %v", err)
|
||||
}
|
||||
v.FloatValue = (*value.Float)(&f)
|
||||
}
|
||||
if encodedValue.IntValue != nil {
|
||||
i := int64(*encodedValue.IntValue)
|
||||
v.IntValue = (*value.Int)(&i)
|
||||
}
|
||||
v.StringValue = (*value.String)(encodedValue.StringValue)
|
||||
v.BooleanValue = (*value.Boolean)(encodedValue.BooleanValue)
|
||||
v.Null = encodedValue.Null
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func validateOneOf(fields ...interface{}) bool {
|
||||
nonNilCount := 0
|
||||
for _, field := range fields {
|
||||
if !reflect.ValueOf(field).IsNil() {
|
||||
nonNilCount++
|
||||
}
|
||||
}
|
||||
return nonNilCount <= 1
|
||||
}
|
||||
|
||||
// encodeManagedFields converts ManagedFields from the the format used by
|
||||
// sigs.k8s.io/structured-merge-diff to the the wire format (api format)
|
||||
func EncodeManagedFields(managedFields fieldpath.ManagedFields) (encodedManagedFields map[string]metav1.VersionedFieldSet, err error) {
|
||||
func encodeManagedFields(managedFields fieldpath.ManagedFields) (encodedManagedFields map[string]metav1.VersionedFieldSet, err error) {
|
||||
encodedManagedFields = make(map[string]metav1.VersionedFieldSet, len(managedFields))
|
||||
for manager, versionedSet := range managedFields {
|
||||
v, err := encodeVersionedSet(versionedSet)
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
package internal
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -240,7 +240,7 @@ func TestRoundTripManagedFields(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
decoded, err := DecodeManagedFields(original)
|
||||
decoded, err := decodeManagedFields(original)
|
||||
if err == nil && len(tc.errString) > 0 {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
continue
|
||||
@@ -253,7 +253,7 @@ func TestRoundTripManagedFields(t *testing.T) {
|
||||
t.Errorf("[%v]expected error with %q but got: %v", i, tc.errString, err)
|
||||
continue
|
||||
}
|
||||
encoded, err := EncodeManagedFields(decoded)
|
||||
encoded, err := encodeManagedFields(decoded)
|
||||
if err != nil {
|
||||
t.Errorf("[%v]did not expect round trip error but got: %v", i, err)
|
||||
continue
|
||||
@@ -263,3 +263,46 @@ func TestRoundTripManagedFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateOneOf tests that validateOneOf returns true if <= 1
|
||||
// non-nil fields are passed to it are and false otherwise
|
||||
func TestValidateOneOf(t *testing.T) {
|
||||
var nilValue *string
|
||||
nonNilValue := strPtr("test")
|
||||
|
||||
tests := []struct {
|
||||
fields []interface{}
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
fields: []interface{}{},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nilValue},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nonNilValue},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nilValue, nonNilValue, nilValue},
|
||||
expected: true,
|
||||
}, {
|
||||
fields: []interface{}{nonNilValue, nonNilValue},
|
||||
expected: false,
|
||||
}, {
|
||||
fields: []interface{}{nilValue, nonNilValue, nonNilValue, nilValue},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
actual := validateOneOf(tc.fields...)
|
||||
if actual != tc.expected {
|
||||
t.Errorf("[%v]expected validateOneOf(%v) to return:\n\t%+v\nbut got:\n\t%+v", i, tc.fields, tc.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func strPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply_test
|
||||
package internal_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
@@ -23,14 +23,14 @@ import (
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/apply"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
prototesting "k8s.io/kube-openapi/pkg/util/proto/testing"
|
||||
)
|
||||
|
||||
var fakeSchema = prototesting.Fake{
|
||||
Path: filepath.Join(
|
||||
"..", "..", "..", "..", "..", "..", "..", "..",
|
||||
"..", "..", "..", "..", "..", "..", "..", "..", "..",
|
||||
"api", "openapi-spec", "swagger.json"),
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestTypeConverter(t *testing.T) {
|
||||
t.Fatalf("Failed to build OpenAPI models: %v", err)
|
||||
}
|
||||
|
||||
tc, err := apply.NewTypeConverter(m)
|
||||
tc, err := internal.NewTypeConverter(m)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build TypeConverter: %v", err)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
package internal
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -14,29 +14,22 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
package internal_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
prototesting "k8s.io/kube-openapi/pkg/util/proto/testing"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
)
|
||||
|
||||
var fakeSchema = prototesting.Fake{
|
||||
Path: filepath.Join(
|
||||
"..", "..", "..", "..", "..", "..", "..", "..",
|
||||
"api", "openapi-spec", "swagger.json"),
|
||||
}
|
||||
|
||||
// TestVersionConverter tests the version converter
|
||||
func TestVersionConverter(t *testing.T) {
|
||||
d, err := fakeSchema.OpenAPISchema()
|
||||
@@ -47,7 +40,7 @@ func TestVersionConverter(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build OpenAPI models: %v", err)
|
||||
}
|
||||
tc, err := NewTypeConverter(m)
|
||||
tc, err := internal.NewTypeConverter(m)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build TypeConverter: %v", err)
|
||||
}
|
||||
@@ -55,7 +48,7 @@ func TestVersionConverter(t *testing.T) {
|
||||
gvkForVersion("v1beta1"): objForGroupVersion("apps/v1beta1"),
|
||||
gvkForVersion("v1"): objForGroupVersion("apps/v1"),
|
||||
}
|
||||
vc := NewVersionConverter(tc, oc)
|
||||
vc := internal.NewVersionConverter(tc, oc)
|
||||
|
||||
input, err := tc.ObjectToTyped(objForGroupVersion("apps/v1beta1"))
|
||||
if err != nil {
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/apply"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
@@ -280,7 +280,7 @@ type patchMechanism interface {
|
||||
type jsonPatcher struct {
|
||||
*patcher
|
||||
|
||||
fieldManager *apply.FieldManager
|
||||
fieldManager *fieldmanager.FieldManager
|
||||
}
|
||||
|
||||
func (p *jsonPatcher) applyPatchToCurrentObject(currentObject runtime.Object) (runtime.Object, error) {
|
||||
@@ -339,7 +339,7 @@ type smpPatcher struct {
|
||||
|
||||
// Schema
|
||||
schemaReferenceObj runtime.Object
|
||||
fieldManager *apply.FieldManager
|
||||
fieldManager *fieldmanager.FieldManager
|
||||
}
|
||||
|
||||
func (p *smpPatcher) applyPatchToCurrentObject(currentObject runtime.Object) (runtime.Object, error) {
|
||||
@@ -377,7 +377,7 @@ type applyPatcher struct {
|
||||
options *metav1.PatchOptions
|
||||
creater runtime.ObjectCreater
|
||||
kind schema.GroupVersionKind
|
||||
fieldManager *apply.FieldManager
|
||||
fieldManager *fieldmanager.FieldManager
|
||||
}
|
||||
|
||||
func (p *applyPatcher) applyPatchToCurrentObject(obj runtime.Object) (runtime.Object, error) {
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/apply"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
@@ -60,7 +60,7 @@ type RequestScope struct {
|
||||
Trace *utiltrace.Trace
|
||||
|
||||
TableConvertor rest.TableConvertor
|
||||
FieldManager *apply.FieldManager
|
||||
FieldManager *fieldmanager.FieldManager
|
||||
|
||||
Resource schema.GroupVersionResource
|
||||
Kind schema.GroupVersionKind
|
||||
|
||||
@@ -34,7 +34,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/apply"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
@@ -516,7 +516,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
reqScope.MetaGroupVersion = *a.group.MetaGroupVersion
|
||||
}
|
||||
if a.group.OpenAPIModels != nil && utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
|
||||
fm, err := apply.NewFieldManager(
|
||||
fm, err := fieldmanager.NewFieldManager(
|
||||
a.group.OpenAPIModels,
|
||||
a.group.UnsafeConvertor,
|
||||
a.group.Defaulter,
|
||||
|
||||
Reference in New Issue
Block a user