mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
Incorporate new types into versioned api system.
* Made externalize/internalize generic to prevent boilerplate. * Add fuzz testing. * All objects pass fuzz tests now. * This turned up some things we'll need to fix eventually. Left TODOs.
This commit is contained in:
parent
b3cc696486
commit
2396bdfa1b
@ -68,6 +68,7 @@ func TestAPIObject(t *testing.T) {
|
||||
|
||||
// Things that Decode would have done for us:
|
||||
decodedViaJSON.Kind = ""
|
||||
decodedViaJSON.APIVersion = ""
|
||||
|
||||
if e, a := outer, &decodedViaJSON; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected: %#v but got %#v", e, a)
|
||||
|
133
pkg/api/defaultcopy.go
Normal file
133
pkg/api/defaultcopy.go
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// DefaultCopy copies API objects to/from their corresponding types
|
||||
// in a versioned package (e.g., v1beta1). Only suitable for types
|
||||
// in which no fields changed.
|
||||
// dest and src must both be pointers to API objects.
|
||||
// Not safe for objects with cyclic references!
|
||||
// TODO: Allow overrides, using the same function mechanism that
|
||||
// util.Fuzzer allows.
|
||||
func DefaultCopy(src, dest interface{}) error {
|
||||
dv, sv := reflect.ValueOf(dest), reflect.ValueOf(src)
|
||||
if dv.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("Need pointer, but got %#v", dest)
|
||||
}
|
||||
if sv.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("Need pointer, but got %#v", src)
|
||||
}
|
||||
dv = dv.Elem()
|
||||
sv = sv.Elem()
|
||||
if !dv.CanAddr() {
|
||||
return fmt.Errorf("Can't write to dest")
|
||||
}
|
||||
|
||||
// Ensure there's no reversed src/dest bugs by making src unwriteable.
|
||||
sv = reflect.ValueOf(sv.Interface())
|
||||
if sv.CanAddr() {
|
||||
return fmt.Errorf("Can write to src, shouldn't be able to.")
|
||||
}
|
||||
|
||||
return copyValue(sv, dv)
|
||||
}
|
||||
|
||||
// Recursively copy sv into dv
|
||||
func copyValue(sv, dv reflect.Value) error {
|
||||
dt, st := dv.Type(), sv.Type()
|
||||
if dt.Name() != st.Name() {
|
||||
return fmt.Errorf("Type names don't match: %v, %v", dt.Name(), st.Name())
|
||||
}
|
||||
|
||||
// This should handle all simple types.
|
||||
if st.AssignableTo(dt) {
|
||||
dv.Set(sv)
|
||||
return nil
|
||||
} else if st.ConvertibleTo(dt) {
|
||||
dv.Set(sv.Convert(dt))
|
||||
return nil
|
||||
}
|
||||
|
||||
// For debugging, should you need to do that.
|
||||
if false {
|
||||
fmt.Printf("copyVal of %v.%v (%v) -> %v.%v (%v)\n",
|
||||
st.PkgPath(), st.Name(), st.Kind(),
|
||||
dt.PkgPath(), dt.Name(), dt.Kind())
|
||||
}
|
||||
|
||||
switch dv.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < dt.NumField(); i++ {
|
||||
f := dv.Type().Field(i)
|
||||
df := dv.FieldByName(f.Name)
|
||||
sf := sv.FieldByName(f.Name)
|
||||
if !df.IsValid() || !sf.IsValid() {
|
||||
return fmt.Errorf("%v not present in source and dest.", f.Name)
|
||||
}
|
||||
if err := copyValue(sf, df); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
if sv.IsNil() {
|
||||
// Don't make a zero-length slice.
|
||||
dv.Set(reflect.Zero(dt))
|
||||
return nil
|
||||
}
|
||||
dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
if err := copyValue(sv.Index(i), dv.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if sv.IsNil() {
|
||||
// Don't copy a nil ptr!
|
||||
dv.Set(reflect.Zero(dt))
|
||||
return nil
|
||||
}
|
||||
dv.Set(reflect.New(dt.Elem()))
|
||||
return copyValue(sv.Elem(), dv.Elem())
|
||||
case reflect.Map:
|
||||
if sv.IsNil() {
|
||||
// Don't copy a nil ptr!
|
||||
dv.Set(reflect.Zero(dt))
|
||||
return nil
|
||||
}
|
||||
dv.Set(reflect.MakeMap(dt))
|
||||
for _, sk := range sv.MapKeys() {
|
||||
dk := reflect.New(dt.Key()).Elem()
|
||||
if err := copyValue(sk, dk); err != nil {
|
||||
return err
|
||||
}
|
||||
dkv := reflect.New(dt.Elem()).Elem()
|
||||
if err := copyValue(sv.MapIndex(sk), dkv); err != nil {
|
||||
return err
|
||||
}
|
||||
dv.SetMapIndex(dk, dkv)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Couldn't copy %#v (%v) into %#v (%v)",
|
||||
sv.Interface(), sv.Kind(), dv.Interface(), dv.Kind())
|
||||
}
|
||||
return nil
|
||||
}
|
1074
pkg/api/helper.go
1074
pkg/api/helper.go
File diff suppressed because it is too large
Load Diff
@ -17,12 +17,82 @@ limitations under the License.
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
var fuzzIters = flag.Int("fuzz_iters", 3, "How many fuzzing iterations to do.")
|
||||
|
||||
// apiObjectFuzzer can randomly populate api objects.
|
||||
var apiObjectFuzzer = util.NewFuzzer(
|
||||
func(j *JSONBase) {
|
||||
// We have to customize the randomization of JSONBases because their
|
||||
// APIVersion and Kind must remain blank in memory.
|
||||
j.APIVersion = ""
|
||||
j.Kind = ""
|
||||
j.ID = util.RandString()
|
||||
// TODO: Fix JSON/YAML packages and/or write custom encoding
|
||||
// for uint64's. Somehow the LS *byte* of this is lost, but
|
||||
// only when all 8 bytes are set.
|
||||
j.ResourceVersion = util.RandUint64() >> 8
|
||||
j.SelfLink = util.RandString()
|
||||
j.CreationTimestamp = util.RandString()
|
||||
},
|
||||
func(intstr *util.IntOrString) {
|
||||
// util.IntOrString will panic if its kind is set wrong.
|
||||
if util.RandBool() {
|
||||
intstr.Kind = util.IntstrInt
|
||||
intstr.IntVal = int(util.RandUint64())
|
||||
intstr.StrVal = ""
|
||||
} else {
|
||||
intstr.Kind = util.IntstrString
|
||||
intstr.IntVal = 0
|
||||
intstr.StrVal = util.RandString()
|
||||
}
|
||||
},
|
||||
func(p *PodInfo) {
|
||||
// The docker container type doesn't survive fuzzing.
|
||||
// TODO: fix this.
|
||||
*p = nil
|
||||
},
|
||||
)
|
||||
|
||||
func objDiff(a, b interface{}) string {
|
||||
|
||||
// An alternate diff attempt, in case json isn't showing you
|
||||
// the difference. (reflect.DeepEqual makes a distinction between
|
||||
// nil and empty slices, for example.)
|
||||
return util.StringDiff(
|
||||
fmt.Sprintf("%#v", a),
|
||||
fmt.Sprintf("%#v", b),
|
||||
)
|
||||
|
||||
ab, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
panic("a")
|
||||
}
|
||||
bb, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
panic("b")
|
||||
}
|
||||
return util.StringDiff(string(ab), string(bb))
|
||||
}
|
||||
|
||||
func runTest(t *testing.T, source interface{}) {
|
||||
name := reflect.TypeOf(source).Name()
|
||||
name := reflect.TypeOf(source).Elem().Name()
|
||||
apiObjectFuzzer.Fuzz(source)
|
||||
j, err := FindJSONBase(source)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v for %#v", err, source)
|
||||
}
|
||||
j.SetKind("")
|
||||
j.SetAPIVersion("")
|
||||
|
||||
data, err := Encode(source)
|
||||
if err != nil {
|
||||
t.Errorf("%v: %v (%#v)", name, err, source)
|
||||
@ -32,58 +102,51 @@ func runTest(t *testing.T, source interface{}) {
|
||||
if err != nil {
|
||||
t.Errorf("%v: %v", name, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(source, obj2) {
|
||||
t.Errorf("1: %v: wanted %#v, got %#v", name, source, obj2)
|
||||
return
|
||||
} else {
|
||||
if !reflect.DeepEqual(source, obj2) {
|
||||
t.Errorf("1: %v: diff: %v", name, objDiff(source, obj2))
|
||||
return
|
||||
}
|
||||
}
|
||||
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface()
|
||||
err = DecodeInto(data, obj3)
|
||||
if err != nil {
|
||||
t.Errorf("2: %v: %v", name, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(source, obj3) {
|
||||
t.Errorf("3: %v: wanted %#v, got %#v", name, source, obj3)
|
||||
return
|
||||
} else {
|
||||
if !reflect.DeepEqual(source, obj3) {
|
||||
t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
// TODO: auto-fill all fields.
|
||||
table := []interface{}{
|
||||
&Pod{
|
||||
JSONBase: JSONBase{
|
||||
ID: "mylittlepod",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"name": "pinky",
|
||||
},
|
||||
},
|
||||
&PodList{},
|
||||
&Pod{},
|
||||
&ServiceList{},
|
||||
&Service{},
|
||||
&ServiceList{
|
||||
Items: []Service{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}, {
|
||||
Labels: map[string]string{
|
||||
"foo": "baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&ReplicationControllerList{},
|
||||
&ReplicationController{},
|
||||
&PodList{},
|
||||
&MinionList{},
|
||||
&Minion{},
|
||||
&Status{},
|
||||
&ServerOpList{},
|
||||
&ServerOp{},
|
||||
&ContainerManifestList{},
|
||||
&Endpoints{},
|
||||
}
|
||||
for _, item := range table {
|
||||
runTest(t, item)
|
||||
// Try a few times, since runTest uses random values.
|
||||
for i := 0; i < *fuzzIters; i++ {
|
||||
runTest(t, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonPtr(t *testing.T) {
|
||||
func TestEncode_NonPtr(t *testing.T) {
|
||||
pod := Pod{
|
||||
Labels: map[string]string{"name": "foo"},
|
||||
}
|
||||
@ -91,30 +154,30 @@ func TestNonPtr(t *testing.T) {
|
||||
data, err := Encode(obj)
|
||||
obj2, err2 := Decode(data)
|
||||
if err != nil || err2 != nil {
|
||||
t.Errorf("Failure: %v %v", err2, err2)
|
||||
t.Fatalf("Failure: '%v' '%v'", err, err2)
|
||||
}
|
||||
if _, ok := obj2.(*Pod); !ok {
|
||||
t.Errorf("Got wrong type")
|
||||
t.Fatalf("Got wrong type")
|
||||
}
|
||||
if !reflect.DeepEqual(obj2, &pod) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPtr(t *testing.T) {
|
||||
pod := Pod{
|
||||
func TestEncode_Ptr(t *testing.T) {
|
||||
pod := &Pod{
|
||||
Labels: map[string]string{"name": "foo"},
|
||||
}
|
||||
obj := interface{}(&pod)
|
||||
obj := interface{}(pod)
|
||||
data, err := Encode(obj)
|
||||
obj2, err2 := Decode(data)
|
||||
if err != nil || err2 != nil {
|
||||
t.Errorf("Failure: %v %v", err2, err2)
|
||||
t.Fatalf("Failure: '%v' '%v'", err, err2)
|
||||
}
|
||||
if _, ok := obj2.(*Pod); !ok {
|
||||
t.Errorf("Got wrong type")
|
||||
t.Fatalf("Got wrong type")
|
||||
}
|
||||
if !reflect.DeepEqual(obj2, &pod) {
|
||||
if !reflect.DeepEqual(obj2, pod) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
|
||||
}
|
||||
}
|
||||
|
108
pkg/api/jsonbase.go
Normal file
108
pkg/api/jsonbase.go
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// JSONBase lets you work with a JSONBase from any of the versioned or
|
||||
// internal APIObjects.
|
||||
type JSONBaseInterface interface {
|
||||
APIVersion() string
|
||||
SetAPIVersion(version string)
|
||||
Kind() string
|
||||
SetKind(kind string)
|
||||
ResourceVersion() uint64
|
||||
SetResourceVersion(version uint64)
|
||||
}
|
||||
|
||||
type genericJSONBase struct {
|
||||
apiVersion *string
|
||||
kind *string
|
||||
resourceVersion *uint64
|
||||
}
|
||||
|
||||
func (g genericJSONBase) APIVersion() string {
|
||||
return *g.apiVersion
|
||||
}
|
||||
|
||||
func (g genericJSONBase) SetAPIVersion(version string) {
|
||||
*g.apiVersion = version
|
||||
}
|
||||
|
||||
func (g genericJSONBase) Kind() string {
|
||||
return *g.kind
|
||||
}
|
||||
|
||||
func (g genericJSONBase) SetKind(kind string) {
|
||||
*g.kind = kind
|
||||
}
|
||||
|
||||
func (g genericJSONBase) ResourceVersion() uint64 {
|
||||
return *g.resourceVersion
|
||||
}
|
||||
|
||||
func (g genericJSONBase) SetResourceVersion(version uint64) {
|
||||
*g.resourceVersion = version
|
||||
}
|
||||
|
||||
// fieldPtr puts the address address of fieldName, which must be a member of v,
|
||||
// into dest, which must be an address of a variable to which this field's address
|
||||
// can be assigned.
|
||||
func fieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
|
||||
field := v.FieldByName(fieldName)
|
||||
if !field.IsValid() {
|
||||
return fmt.Errorf("Couldn't find %v field in %#v", fieldName, v.Interface())
|
||||
}
|
||||
v = reflect.ValueOf(dest)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("dest should be ptr")
|
||||
}
|
||||
v = v.Elem()
|
||||
field = field.Addr()
|
||||
if field.Type().AssignableTo(v.Type()) {
|
||||
v.Set(field)
|
||||
return nil
|
||||
}
|
||||
if field.Type().ConvertibleTo(v.Type()) {
|
||||
v.Set(field.Convert(v.Type()))
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Couldn't assign/convert %v to %v", field.Type(), v.Type())
|
||||
}
|
||||
|
||||
// newGenericJSONBase makes a new generic JSONBase from v, which must be an
|
||||
// addressable/setable reflect.Value having the same fields as api.JSONBase.
|
||||
// Returns an error if this isn't the case.
|
||||
func newGenericJSONBase(v reflect.Value) (genericJSONBase, error) {
|
||||
g := genericJSONBase{}
|
||||
err := fieldPtr(v, "APIVersion", &g.apiVersion)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
err = fieldPtr(v, "Kind", &g.kind)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
err = fieldPtr(v, "ResourceVersion", &g.resourceVersion)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
return g, nil
|
||||
}
|
59
pkg/api/jsonbase_test.go
Normal file
59
pkg/api/jsonbase_test.go
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 api
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenericJSONBase(t *testing.T) {
|
||||
j := JSONBase{
|
||||
APIVersion: "a",
|
||||
Kind: "b",
|
||||
ResourceVersion: 1,
|
||||
}
|
||||
g, err := newGenericJSONBase(reflect.ValueOf(&j).Elem())
|
||||
if err != nil {
|
||||
t.Fatalf("new err: %v", err)
|
||||
}
|
||||
// Proove g supports JSONBaseInterface.
|
||||
jbi := JSONBaseInterface(g)
|
||||
if e, a := "a", jbi.APIVersion(); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := "b", jbi.Kind(); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := uint64(1), jbi.ResourceVersion(); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
jbi.SetAPIVersion("c")
|
||||
jbi.SetKind("d")
|
||||
jbi.SetResourceVersion(2)
|
||||
|
||||
if e, a := "c", j.APIVersion; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := "d", j.Kind; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := uint64(2), j.ResourceVersion; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ import (
|
||||
// by the following regex:
|
||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
|
||||
//
|
||||
// DNS_SUBDOMAIN: This is a string, no more than 253 characters long, that conforms
|
||||
// DNS_SUBDOMAIN: This is a string, no more than 253 characters long, that conforms
|
||||
// to the definition of a "subdomain" in RFCs 1035 and 1123. This is captured
|
||||
// by the following regex:
|
||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
|
||||
@ -223,7 +223,8 @@ type PodState struct {
|
||||
// entry per container in the manifest. The value of this map is currently the output
|
||||
// of `docker inspect`. This output format is *not* final and should not be relied
|
||||
// upon.
|
||||
// TODO: Make real decisions about what our info should look like.
|
||||
// TODO: Make real decisions about what our info should look like. Re-enable fuzz test
|
||||
// when we have done this.
|
||||
Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ import (
|
||||
// by the following regex:
|
||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
|
||||
//
|
||||
// DNS_SUBDOMAIN: This is a string, no more than 253 characters long, that conforms
|
||||
// DNS_SUBDOMAIN: This is a string, no more than 253 characters long, that conforms
|
||||
// to the definition of a "subdomain" in RFCs 1035 and 1123. This is captured
|
||||
// by the following regex:
|
||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
|
||||
@ -58,6 +58,12 @@ type ContainerManifest struct {
|
||||
Containers []Container `yaml:"containers" json:"containers"`
|
||||
}
|
||||
|
||||
// ContainerManifestList is used to communicate container manifests to kubelet.
|
||||
type ContainerManifestList struct {
|
||||
JSONBase `json:",inline" yaml:",inline"`
|
||||
Items []ContainerManifest `json:"items,omitempty" yaml:"items,omitempty"`
|
||||
}
|
||||
|
||||
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
|
||||
type Volume struct {
|
||||
// Required: This must be a DNS_LABEL. Each volume in a pod must have
|
||||
@ -289,8 +295,8 @@ type Service struct {
|
||||
// Endpoints is a collection of endpoints that implement the actual service, for example:
|
||||
// Name: "mysql", Endpoints: ["10.10.1.1:1909", "10.10.2.2:8834"]
|
||||
type Endpoints struct {
|
||||
Name string
|
||||
Endpoints []string
|
||||
JSONBase `json:",inline" yaml:",inline"`
|
||||
Endpoints []string `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
|
||||
}
|
||||
|
||||
// Minion is a worker node in Kubernetenes.
|
||||
|
@ -148,7 +148,7 @@ func (h *EtcdHelper) bodyAndExtractObj(key string, objPtr interface{}, ignoreNot
|
||||
body = response.Node.Value
|
||||
err = api.DecodeInto([]byte(body), objPtr)
|
||||
if jsonBase, err := api.FindJSONBase(objPtr); err == nil {
|
||||
jsonBase.ResourceVersion = response.Node.ModifiedIndex
|
||||
jsonBase.SetResourceVersion(response.Node.ModifiedIndex)
|
||||
// Note that err shadows the err returned below, so we won't
|
||||
// return an error just because we failed to find a JSONBase.
|
||||
// This is intentional.
|
||||
|
Loading…
Reference in New Issue
Block a user