Skip to content

Commit f751f89

Browse files
Merge pull request #51321 from mengqiy/kubectl_apply_openapi
Automatic merge from submit-queue (batch tested with PRs 51321, 55969, 55039, 56183, 55976). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Kubectl apply and strategic merge patch using openapi - [x] support openapi in strategic merge patch - [x] test openapi in strategic merge patch - [x] kubectl apply use openapi to calculate diff be default. It will fall back to use baked-in types when openapi is not available. - [x] test openapi in kubectl apply Fixes: kubernetes/kubectl#55 ```release-note kubectl apply use openapi to calculate diff be default. It will fall back to use baked-in types when openapi is not available. ``` /assign @apelisse Kubernetes-commit: e412ad5393b8c949474b904616fc411c3aa478a9
2 parents 8b356a9 + 86331ff commit f751f89

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+19493
-637
lines changed

Godeps/Godeps.json

Lines changed: 252 additions & 220 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/util/strategicpatch/BUILD

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,38 @@ load(
99
go_test(
1010
name = "go_default_test",
1111
srcs = ["patch_test.go"],
12+
data = [
13+
"testdata/swagger-merge-item.json",
14+
"testdata/swagger-precision-item.json",
15+
],
1216
importpath = "k8s.io/apimachinery/pkg/util/strategicpatch",
1317
library = ":go_default_library",
1418
deps = [
1519
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
1620
"//vendor/github.com/ghodss/yaml:go_default_library",
1721
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
22+
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
1823
"//vendor/k8s.io/apimachinery/pkg/util/mergepatch:go_default_library",
1924
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
25+
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch/testing:go_default_library",
2026
],
2127
)
2228

2329
go_library(
2430
name = "go_default_library",
25-
srcs = ["patch.go"],
31+
srcs = [
32+
"errors.go",
33+
"meta.go",
34+
"patch.go",
35+
"types.go",
36+
],
2637
importpath = "k8s.io/apimachinery/pkg/util/strategicpatch",
2738
deps = [
2839
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
2940
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
3041
"//vendor/k8s.io/apimachinery/pkg/util/mergepatch:go_default_library",
3142
"//vendor/k8s.io/apimachinery/third_party/forked/golang/json:go_default_library",
43+
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
3244
],
3345
)
3446

@@ -41,6 +53,9 @@ filegroup(
4153

4254
filegroup(
4355
name = "all-srcs",
44-
srcs = [":package-srcs"],
56+
srcs = [
57+
":package-srcs",
58+
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/testing:all-srcs",
59+
],
4560
tags = ["automanaged"],
4661
)

pkg/util/strategicpatch/errors.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package strategicpatch
18+
19+
import (
20+
"fmt"
21+
)
22+
23+
type LookupPatchMetaError struct {
24+
Path string
25+
Err error
26+
}
27+
28+
func (e LookupPatchMetaError) Error() string {
29+
return fmt.Sprintf("LookupPatchMetaError(%s): %v", e.Path, e.Err)
30+
}
31+
32+
type FieldNotFoundError struct {
33+
Path string
34+
Field string
35+
}
36+
37+
func (e FieldNotFoundError) Error() string {
38+
return fmt.Sprintf("unable to find api field %q in %s", e.Field, e.Path)
39+
}
40+
41+
type InvalidTypeError struct {
42+
Path string
43+
Expected string
44+
Actual string
45+
}
46+
47+
func (e InvalidTypeError) Error() string {
48+
return fmt.Sprintf("invalid type for %s: got %q, expected %q", e.Path, e.Actual, e.Expected)
49+
}

pkg/util/strategicpatch/meta.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package strategicpatch
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
"reflect"
23+
24+
"k8s.io/apimachinery/pkg/util/mergepatch"
25+
forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
26+
openapi "k8s.io/kube-openapi/pkg/util/proto"
27+
)
28+
29+
type PatchMeta struct {
30+
patchStrategies []string
31+
patchMergeKey string
32+
}
33+
34+
func (pm PatchMeta) GetPatchStrategies() []string {
35+
if pm.patchStrategies == nil {
36+
return []string{}
37+
}
38+
return pm.patchStrategies
39+
}
40+
41+
func (pm PatchMeta) SetPatchStrategies(ps []string) {
42+
pm.patchStrategies = ps
43+
}
44+
45+
func (pm PatchMeta) GetPatchMergeKey() string {
46+
return pm.patchMergeKey
47+
}
48+
49+
func (pm PatchMeta) SetPatchMergeKey(pmk string) {
50+
pm.patchMergeKey = pmk
51+
}
52+
53+
type LookupPatchMeta interface {
54+
// LookupPatchMetadataForStruct gets subschema and the patch metadata (e.g. patch strategy and merge key) for map.
55+
LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error)
56+
// LookupPatchMetadataForSlice get subschema and the patch metadata for slice.
57+
LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error)
58+
// Get the type name of the field
59+
Name() string
60+
}
61+
62+
type PatchMetaFromStruct struct {
63+
T reflect.Type
64+
}
65+
66+
func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) {
67+
t, err := getTagStructType(dataStruct)
68+
return PatchMetaFromStruct{T: t}, err
69+
}
70+
71+
var _ LookupPatchMeta = PatchMetaFromStruct{}
72+
73+
func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
74+
fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key)
75+
if err != nil {
76+
return nil, PatchMeta{}, err
77+
}
78+
79+
return PatchMetaFromStruct{T: fieldType},
80+
PatchMeta{
81+
patchStrategies: fieldPatchStrategies,
82+
patchMergeKey: fieldPatchMergeKey,
83+
}, nil
84+
}
85+
86+
func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
87+
subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key)
88+
if err != nil {
89+
return nil, PatchMeta{}, err
90+
}
91+
elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct)
92+
t := elemPatchMetaFromStruct.T
93+
94+
var elemType reflect.Type
95+
switch t.Kind() {
96+
// If t is an array or a slice, get the element type.
97+
// If element is still an array or a slice, return an error.
98+
// Otherwise, return element type.
99+
case reflect.Array, reflect.Slice:
100+
elemType = t.Elem()
101+
if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice {
102+
return nil, PatchMeta{}, errors.New("unexpected slice of slice")
103+
}
104+
// If t is an pointer, get the underlying element.
105+
// If the underlying element is neither an array nor a slice, the pointer is pointing to a slice,
106+
// e.g. https://github.com/kubernetes/kubernetes/blob/bc22e206c79282487ea0bf5696d5ccec7e839a76/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go#L2782-L2822
107+
// If the underlying element is either an array or a slice, return its element type.
108+
case reflect.Ptr:
109+
t = t.Elem()
110+
if t.Kind() == reflect.Array || t.Kind() == reflect.Slice {
111+
t = t.Elem()
112+
}
113+
elemType = t
114+
default:
115+
return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String())
116+
}
117+
118+
return PatchMetaFromStruct{T: elemType}, patchMeta, nil
119+
}
120+
121+
func (s PatchMetaFromStruct) Name() string {
122+
return s.T.Kind().String()
123+
}
124+
125+
func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
126+
if dataStruct == nil {
127+
return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
128+
}
129+
130+
t := reflect.TypeOf(dataStruct)
131+
// Get the underlying type for pointers
132+
if t.Kind() == reflect.Ptr {
133+
t = t.Elem()
134+
}
135+
136+
if t.Kind() != reflect.Struct {
137+
return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
138+
}
139+
140+
return t, nil
141+
}
142+
143+
func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type {
144+
t, err := getTagStructType(dataStruct)
145+
if err != nil {
146+
panic(err)
147+
}
148+
return t
149+
}
150+
151+
type PatchMetaFromOpenAPI struct {
152+
Schema openapi.Schema
153+
}
154+
155+
func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI {
156+
return PatchMetaFromOpenAPI{Schema: s}
157+
}
158+
159+
var _ LookupPatchMeta = PatchMetaFromOpenAPI{}
160+
161+
func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
162+
if s.Schema == nil {
163+
return nil, PatchMeta{}, nil
164+
}
165+
kindItem := NewKindItem(key, s.Schema.GetPath())
166+
s.Schema.Accept(kindItem)
167+
168+
err := kindItem.Error()
169+
if err != nil {
170+
return nil, PatchMeta{}, err
171+
}
172+
return PatchMetaFromOpenAPI{Schema: kindItem.subschema},
173+
kindItem.patchmeta, nil
174+
}
175+
176+
func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
177+
if s.Schema == nil {
178+
return nil, PatchMeta{}, nil
179+
}
180+
sliceItem := NewSliceItem(key, s.Schema.GetPath())
181+
s.Schema.Accept(sliceItem)
182+
183+
err := sliceItem.Error()
184+
if err != nil {
185+
return nil, PatchMeta{}, err
186+
}
187+
return PatchMetaFromOpenAPI{Schema: sliceItem.subschema},
188+
sliceItem.patchmeta, nil
189+
}
190+
191+
func (s PatchMetaFromOpenAPI) Name() string {
192+
schema := s.Schema
193+
return schema.GetName()
194+
}

0 commit comments

Comments
 (0)