mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 10:19:50 +00:00
commit
b0d18b2af0
@ -68,6 +68,7 @@ func TestAPIObject(t *testing.T) {
|
|||||||
|
|
||||||
// Things that Decode would have done for us:
|
// Things that Decode would have done for us:
|
||||||
decodedViaJSON.Kind = ""
|
decodedViaJSON.Kind = ""
|
||||||
|
decodedViaJSON.APIVersion = ""
|
||||||
|
|
||||||
if e, a := outer, &decodedViaJSON; !reflect.DeepEqual(e, a) {
|
if e, a := outer, &decodedViaJSON; !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("Expected: %#v but got %#v", 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
|
||||||
|
}
|
1085
pkg/api/helper.go
1085
pkg/api/helper.go
File diff suppressed because it is too large
Load Diff
@ -17,12 +17,82 @@ limitations under the License.
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"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{}) {
|
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)
|
data, err := Encode(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%v: %v (%#v)", name, err, source)
|
t.Errorf("%v: %v (%#v)", name, err, source)
|
||||||
@ -32,58 +102,51 @@ func runTest(t *testing.T, source interface{}) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%v: %v", name, err)
|
t.Errorf("%v: %v", name, err)
|
||||||
return
|
return
|
||||||
}
|
} else {
|
||||||
if !reflect.DeepEqual(source, obj2) {
|
if !reflect.DeepEqual(source, obj2) {
|
||||||
t.Errorf("1: %v: wanted %#v, got %#v", name, source, obj2)
|
t.Errorf("1: %v: diff: %v", name, objDiff(source, obj2))
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface()
|
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface()
|
||||||
err = DecodeInto(data, obj3)
|
err = DecodeInto(data, obj3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("2: %v: %v", name, err)
|
t.Errorf("2: %v: %v", name, err)
|
||||||
return
|
return
|
||||||
}
|
} else {
|
||||||
if !reflect.DeepEqual(source, obj3) {
|
if !reflect.DeepEqual(source, obj3) {
|
||||||
t.Errorf("3: %v: wanted %#v, got %#v", name, source, obj3)
|
t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3))
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTypes(t *testing.T) {
|
func TestTypes(t *testing.T) {
|
||||||
// TODO: auto-fill all fields.
|
// TODO: auto-fill all fields.
|
||||||
table := []interface{}{
|
table := []interface{}{
|
||||||
&Pod{
|
&PodList{},
|
||||||
JSONBase: JSONBase{
|
&Pod{},
|
||||||
ID: "mylittlepod",
|
&ServiceList{},
|
||||||
},
|
|
||||||
Labels: map[string]string{
|
|
||||||
"name": "pinky",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Service{},
|
&Service{},
|
||||||
&ServiceList{
|
|
||||||
Items: []Service{
|
|
||||||
{
|
|
||||||
Labels: map[string]string{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
Labels: map[string]string{
|
|
||||||
"foo": "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&ReplicationControllerList{},
|
&ReplicationControllerList{},
|
||||||
&ReplicationController{},
|
&ReplicationController{},
|
||||||
&PodList{},
|
&MinionList{},
|
||||||
|
&Minion{},
|
||||||
|
&Status{},
|
||||||
|
&ServerOpList{},
|
||||||
|
&ServerOp{},
|
||||||
|
&ContainerManifestList{},
|
||||||
|
&Endpoints{},
|
||||||
}
|
}
|
||||||
for _, item := range table {
|
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{
|
pod := Pod{
|
||||||
Labels: map[string]string{"name": "foo"},
|
Labels: map[string]string{"name": "foo"},
|
||||||
}
|
}
|
||||||
@ -91,30 +154,30 @@ func TestNonPtr(t *testing.T) {
|
|||||||
data, err := Encode(obj)
|
data, err := Encode(obj)
|
||||||
obj2, err2 := Decode(data)
|
obj2, err2 := Decode(data)
|
||||||
if err != nil || err2 != nil {
|
if err != nil || err2 != nil {
|
||||||
t.Errorf("Failure: %v %v", err2, err2)
|
t.Fatalf("Failure: '%v' '%v'", err, err2)
|
||||||
}
|
}
|
||||||
if _, ok := obj2.(*Pod); !ok {
|
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)
|
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPtr(t *testing.T) {
|
func TestEncode_Ptr(t *testing.T) {
|
||||||
pod := Pod{
|
pod := &Pod{
|
||||||
Labels: map[string]string{"name": "foo"},
|
Labels: map[string]string{"name": "foo"},
|
||||||
}
|
}
|
||||||
obj := interface{}(&pod)
|
obj := interface{}(pod)
|
||||||
data, err := Encode(obj)
|
data, err := Encode(obj)
|
||||||
obj2, err2 := Decode(data)
|
obj2, err2 := Decode(data)
|
||||||
if err != nil || err2 != nil {
|
if err != nil || err2 != nil {
|
||||||
t.Errorf("Failure: %v %v", err2, err2)
|
t.Fatalf("Failure: '%v' '%v'", err, err2)
|
||||||
}
|
}
|
||||||
if _, ok := obj2.(*Pod); !ok {
|
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)
|
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:
|
// by the following regex:
|
||||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
|
// [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
|
// to the definition of a "subdomain" in RFCs 1035 and 1123. This is captured
|
||||||
// by the following regex:
|
// by the following regex:
|
||||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
|
// [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"`
|
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.
|
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
|
||||||
type Volume struct {
|
type Volume struct {
|
||||||
// Required: This must be a DNS_LABEL. Each volume in a pod must have
|
// Required: This must be a DNS_LABEL. Each volume in a pod must have
|
||||||
@ -217,7 +223,8 @@ type PodState struct {
|
|||||||
// entry per container in the manifest. The value of this map is currently the output
|
// 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
|
// of `docker inspect`. This output format is *not* final and should not be relied
|
||||||
// upon.
|
// 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"`
|
Info PodInfo `json:"info,omitempty" yaml:"info,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,8 +296,8 @@ type Service struct {
|
|||||||
// Endpoints is a collection of endpoints that implement the actual service, for example:
|
// 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"]
|
// Name: "mysql", Endpoints: ["10.10.1.1:1909", "10.10.2.2:8834"]
|
||||||
type Endpoints struct {
|
type Endpoints struct {
|
||||||
Name string
|
JSONBase `json:",inline" yaml:",inline"`
|
||||||
Endpoints []string
|
Endpoints []string `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minion is a worker node in Kubernetenes.
|
// Minion is a worker node in Kubernetenes.
|
||||||
|
@ -38,7 +38,7 @@ import (
|
|||||||
// by the following regex:
|
// by the following regex:
|
||||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
|
// [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
|
// to the definition of a "subdomain" in RFCs 1035 and 1123. This is captured
|
||||||
// by the following regex:
|
// by the following regex:
|
||||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
|
// [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"`
|
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.
|
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
|
||||||
type Volume struct {
|
type Volume struct {
|
||||||
// Required: This must be a DNS_LABEL. Each volume in a pod must have
|
// 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:
|
// 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"]
|
// Name: "mysql", Endpoints: ["10.10.1.1:1909", "10.10.2.2:8834"]
|
||||||
type Endpoints struct {
|
type Endpoints struct {
|
||||||
Name string
|
JSONBase `json:",inline" yaml:",inline"`
|
||||||
Endpoints []string
|
Endpoints []string `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minion is a worker node in Kubernetenes.
|
// Minion is a worker node in Kubernetenes.
|
||||||
|
@ -9,7 +9,7 @@ You may obtain a copy of the License at
|
|||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or sied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
@ -110,12 +110,12 @@ func responseToPods(response *etcd.Response) ([]kubelet.Pod, error) {
|
|||||||
return pods, fmt.Errorf("no nodes field: %v", response)
|
return pods, fmt.Errorf("no nodes field: %v", response)
|
||||||
}
|
}
|
||||||
|
|
||||||
manifests := []api.ContainerManifest{}
|
manifests := api.ContainerManifestList{}
|
||||||
if err := yaml.Unmarshal([]byte(response.Node.Value), &manifests); err != nil {
|
if err := yaml.Unmarshal([]byte(response.Node.Value), &manifests); err != nil {
|
||||||
return pods, fmt.Errorf("could not unmarshal manifests: %v", err)
|
return pods, fmt.Errorf("could not unmarshal manifests: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, manifest := range manifests {
|
for i, manifest := range manifests.Items {
|
||||||
name := manifest.ID
|
name := manifest.ID
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = fmt.Sprintf("_%d", i+1)
|
name = fmt.Sprintf("_%d", i+1)
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
"github.com/coreos/go-etcd/etcd"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,7 +35,9 @@ func TestGetEtcdData(t *testing.T) {
|
|||||||
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
||||||
R: &etcd.Response{
|
R: &etcd.Response{
|
||||||
Node: &etcd.Node{
|
Node: &etcd.Node{
|
||||||
Value: util.MakeJSONString([]api.ContainerManifest{api.ContainerManifest{ID: "foo"}}),
|
Value: api.EncodeOrDie(&api.ContainerManifestList{
|
||||||
|
Items: []api.ContainerManifest{{ID: "foo"}},
|
||||||
|
}),
|
||||||
ModifiedIndex: 1,
|
ModifiedIndex: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -76,7 +77,9 @@ func TestGetEtcd(t *testing.T) {
|
|||||||
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
||||||
R: &etcd.Response{
|
R: &etcd.Response{
|
||||||
Node: &etcd.Node{
|
Node: &etcd.Node{
|
||||||
Value: util.MakeJSONString([]api.ContainerManifest{api.ContainerManifest{ID: "foo"}}),
|
Value: api.EncodeOrDie(&api.ContainerManifestList{
|
||||||
|
Items: []api.ContainerManifest{{ID: "foo"}},
|
||||||
|
}),
|
||||||
ModifiedIndex: 1,
|
ModifiedIndex: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -103,7 +106,7 @@ func TestWatchEtcd(t *testing.T) {
|
|||||||
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
||||||
R: &etcd.Response{
|
R: &etcd.Response{
|
||||||
Node: &etcd.Node{
|
Node: &etcd.Node{
|
||||||
Value: util.MakeJSONString([]api.Container{}),
|
Value: api.EncodeOrDie(&api.ContainerManifestList{}),
|
||||||
ModifiedIndex: 2,
|
ModifiedIndex: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -127,19 +127,19 @@ func (s *endpointsStore) Merge(source string, change interface{}) error {
|
|||||||
case ADD:
|
case ADD:
|
||||||
glog.Infof("Adding new endpoint from source %s : %v", source, update.Endpoints)
|
glog.Infof("Adding new endpoint from source %s : %v", source, update.Endpoints)
|
||||||
for _, value := range update.Endpoints {
|
for _, value := range update.Endpoints {
|
||||||
endpoints[value.Name] = value
|
endpoints[value.ID] = value
|
||||||
}
|
}
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
glog.Infof("Removing an endpoint %v", update)
|
glog.Infof("Removing an endpoint %v", update)
|
||||||
for _, value := range update.Endpoints {
|
for _, value := range update.Endpoints {
|
||||||
delete(endpoints, value.Name)
|
delete(endpoints, value.ID)
|
||||||
}
|
}
|
||||||
case SET:
|
case SET:
|
||||||
glog.Infof("Setting endpoints %v", update)
|
glog.Infof("Setting endpoints %v", update)
|
||||||
// Clear the old map entries by just creating a new map
|
// Clear the old map entries by just creating a new map
|
||||||
endpoints = make(map[string]api.Endpoints)
|
endpoints = make(map[string]api.Endpoints)
|
||||||
for _, value := range update.Endpoints {
|
for _, value := range update.Endpoints {
|
||||||
endpoints[value.Name] = value
|
endpoints[value.ID] = value
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
glog.Infof("Received invalid update type: %v", update)
|
glog.Infof("Received invalid update type: %v", update)
|
||||||
|
@ -83,7 +83,7 @@ func (s sortedEndpoints) Swap(i, j int) {
|
|||||||
s[i], s[j] = s[j], s[i]
|
s[i], s[j] = s[j], s[i]
|
||||||
}
|
}
|
||||||
func (s sortedEndpoints) Less(i, j int) bool {
|
func (s sortedEndpoints) Less(i, j int) bool {
|
||||||
return s[i].Name < s[j].Name
|
return s[i].ID < s[j].ID
|
||||||
}
|
}
|
||||||
|
|
||||||
type EndpointsHandlerMock struct {
|
type EndpointsHandlerMock struct {
|
||||||
@ -216,8 +216,14 @@ func TestNewMultipleSourcesEndpointsMultipleHandlersAddedAndNotified(t *testing.
|
|||||||
handler2 := NewEndpointsHandlerMock()
|
handler2 := NewEndpointsHandlerMock()
|
||||||
config.RegisterHandler(handler)
|
config.RegisterHandler(handler)
|
||||||
config.RegisterHandler(handler2)
|
config.RegisterHandler(handler2)
|
||||||
endpointsUpdate1 := CreateEndpointsUpdate(ADD, api.Endpoints{Name: "foo", Endpoints: []string{"endpoint1", "endpoint2"}})
|
endpointsUpdate1 := CreateEndpointsUpdate(ADD, api.Endpoints{
|
||||||
endpointsUpdate2 := CreateEndpointsUpdate(ADD, api.Endpoints{Name: "bar", Endpoints: []string{"endpoint3", "endpoint4"}})
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint1", "endpoint2"},
|
||||||
|
})
|
||||||
|
endpointsUpdate2 := CreateEndpointsUpdate(ADD, api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "bar"},
|
||||||
|
Endpoints: []string{"endpoint3", "endpoint4"},
|
||||||
|
})
|
||||||
handler.Wait(2)
|
handler.Wait(2)
|
||||||
handler2.Wait(2)
|
handler2.Wait(2)
|
||||||
channelOne <- endpointsUpdate1
|
channelOne <- endpointsUpdate1
|
||||||
@ -236,8 +242,14 @@ func TestNewMultipleSourcesEndpointsMultipleHandlersAddRemoveSetAndNotified(t *t
|
|||||||
handler2 := NewEndpointsHandlerMock()
|
handler2 := NewEndpointsHandlerMock()
|
||||||
config.RegisterHandler(handler)
|
config.RegisterHandler(handler)
|
||||||
config.RegisterHandler(handler2)
|
config.RegisterHandler(handler2)
|
||||||
endpointsUpdate1 := CreateEndpointsUpdate(ADD, api.Endpoints{Name: "foo", Endpoints: []string{"endpoint1", "endpoint2"}})
|
endpointsUpdate1 := CreateEndpointsUpdate(ADD, api.Endpoints{
|
||||||
endpointsUpdate2 := CreateEndpointsUpdate(ADD, api.Endpoints{Name: "bar", Endpoints: []string{"endpoint3", "endpoint4"}})
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint1", "endpoint2"},
|
||||||
|
})
|
||||||
|
endpointsUpdate2 := CreateEndpointsUpdate(ADD, api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "bar"},
|
||||||
|
Endpoints: []string{"endpoint3", "endpoint4"},
|
||||||
|
})
|
||||||
handler.Wait(2)
|
handler.Wait(2)
|
||||||
handler2.Wait(2)
|
handler2.Wait(2)
|
||||||
channelOne <- endpointsUpdate1
|
channelOne <- endpointsUpdate1
|
||||||
@ -248,7 +260,10 @@ func TestNewMultipleSourcesEndpointsMultipleHandlersAddRemoveSetAndNotified(t *t
|
|||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
|
|
||||||
// Add one more
|
// Add one more
|
||||||
endpointsUpdate3 := CreateEndpointsUpdate(ADD, api.Endpoints{Name: "foobar", Endpoints: []string{"endpoint5", "endpoint6"}})
|
endpointsUpdate3 := CreateEndpointsUpdate(ADD, api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "foobar"},
|
||||||
|
Endpoints: []string{"endpoint5", "endpoint6"},
|
||||||
|
})
|
||||||
handler.Wait(1)
|
handler.Wait(1)
|
||||||
handler2.Wait(1)
|
handler2.Wait(1)
|
||||||
channelTwo <- endpointsUpdate3
|
channelTwo <- endpointsUpdate3
|
||||||
@ -257,7 +272,10 @@ func TestNewMultipleSourcesEndpointsMultipleHandlersAddRemoveSetAndNotified(t *t
|
|||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
|
|
||||||
// Update the "foo" service with new endpoints
|
// Update the "foo" service with new endpoints
|
||||||
endpointsUpdate1 = CreateEndpointsUpdate(ADD, api.Endpoints{Name: "foo", Endpoints: []string{"endpoint77"}})
|
endpointsUpdate1 = CreateEndpointsUpdate(ADD, api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint77"},
|
||||||
|
})
|
||||||
handler.Wait(1)
|
handler.Wait(1)
|
||||||
handler2.Wait(1)
|
handler2.Wait(1)
|
||||||
channelOne <- endpointsUpdate1
|
channelOne <- endpointsUpdate1
|
||||||
@ -266,7 +284,7 @@ func TestNewMultipleSourcesEndpointsMultipleHandlersAddRemoveSetAndNotified(t *t
|
|||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
|
|
||||||
// Remove "bar" service
|
// Remove "bar" service
|
||||||
endpointsUpdate2 = CreateEndpointsUpdate(REMOVE, api.Endpoints{Name: "bar"})
|
endpointsUpdate2 = CreateEndpointsUpdate(REMOVE, api.Endpoints{JSONBase: api.JSONBase{ID: "bar"}})
|
||||||
handler.Wait(1)
|
handler.Wait(1)
|
||||||
handler2.Wait(1)
|
handler2.Wait(1)
|
||||||
channelTwo <- endpointsUpdate2
|
channelTwo <- endpointsUpdate2
|
||||||
|
@ -34,7 +34,6 @@ limitations under the License.
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -127,7 +126,7 @@ func (s ConfigSourceEtcd) GetServices() ([]api.Service, []api.Endpoints, error)
|
|||||||
// and create a Service entry for it.
|
// and create a Service entry for it.
|
||||||
for i, node := range response.Node.Nodes {
|
for i, node := range response.Node.Nodes {
|
||||||
var svc api.Service
|
var svc api.Service
|
||||||
err = json.Unmarshal([]byte(node.Value), &svc)
|
err = api.DecodeInto([]byte(node.Value), &svc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Failed to load Service: %s (%#v)", node.Value, err)
|
glog.Errorf("Failed to load Service: %s (%#v)", node.Value, err)
|
||||||
continue
|
continue
|
||||||
@ -154,7 +153,9 @@ func (s ConfigSourceEtcd) GetEndpoints(service string) (api.Endpoints, error) {
|
|||||||
return api.Endpoints{}, err
|
return api.Endpoints{}, err
|
||||||
}
|
}
|
||||||
// Parse all the endpoint specifications in this value.
|
// Parse all the endpoint specifications in this value.
|
||||||
return parseEndpoints(response.Node.Value)
|
var e api.Endpoints
|
||||||
|
err = api.DecodeInto([]byte(response.Node.Value), &e)
|
||||||
|
return e, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// etcdResponseToService takes an etcd response and pulls it apart to find service.
|
// etcdResponseToService takes an etcd response and pulls it apart to find service.
|
||||||
@ -163,19 +164,13 @@ func etcdResponseToService(response *etcd.Response) (*api.Service, error) {
|
|||||||
return nil, fmt.Errorf("invalid response from etcd: %#v", response)
|
return nil, fmt.Errorf("invalid response from etcd: %#v", response)
|
||||||
}
|
}
|
||||||
var svc api.Service
|
var svc api.Service
|
||||||
err := json.Unmarshal([]byte(response.Node.Value), &svc)
|
err := api.DecodeInto([]byte(response.Node.Value), &svc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &svc, err
|
return &svc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEndpoints(jsonString string) (api.Endpoints, error) {
|
|
||||||
var e api.Endpoints
|
|
||||||
err := json.Unmarshal([]byte(jsonString), &e)
|
|
||||||
return e, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ConfigSourceEtcd) WatchForChanges() {
|
func (s ConfigSourceEtcd) WatchForChanges() {
|
||||||
glog.Info("Setting up a watch for new services")
|
glog.Info("Setting up a watch for new services")
|
||||||
watchChannel := make(chan *etcd.Response)
|
watchChannel := make(chan *etcd.Response)
|
||||||
@ -220,7 +215,7 @@ func (s ConfigSourceEtcd) ProcessChange(response *etcd.Response) {
|
|||||||
func (s ConfigSourceEtcd) ProcessEndpointResponse(response *etcd.Response) {
|
func (s ConfigSourceEtcd) ProcessEndpointResponse(response *etcd.Response) {
|
||||||
glog.Infof("Processing a change in endpoint configuration... %s", *response)
|
glog.Infof("Processing a change in endpoint configuration... %s", *response)
|
||||||
var endpoints api.Endpoints
|
var endpoints api.Endpoints
|
||||||
err := json.Unmarshal([]byte(response.Node.Value), &endpoints)
|
err := api.DecodeInto([]byte(response.Node.Value), &endpoints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Failed to parse service out of etcd key: %v : %+v", response.Node.Value, err)
|
glog.Errorf("Failed to parse service out of etcd key: %v : %+v", response.Node.Value, err)
|
||||||
return
|
return
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
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 config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
const TomcatContainerEtcdKey = "/registry/services/tomcat/endpoints/tomcat-3bd5af34"
|
|
||||||
const TomcatService = "tomcat"
|
|
||||||
const TomcatContainerID = "tomcat-3bd5af34"
|
|
||||||
|
|
||||||
func validateJSONParsing(t *testing.T, jsonString string, expectedEndpoints api.Endpoints, expectError bool) {
|
|
||||||
endpoints, err := parseEndpoints(jsonString)
|
|
||||||
if err == nil && expectError {
|
|
||||||
t.Errorf("validateJSONParsing did not get expected error when parsing %s", jsonString)
|
|
||||||
}
|
|
||||||
if err != nil && !expectError {
|
|
||||||
t.Errorf("validateJSONParsing got unexpected error %+v when parsing %s", err, jsonString)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(expectedEndpoints, endpoints) {
|
|
||||||
t.Errorf("Didn't get expected endpoints %+v got: %+v", expectedEndpoints, endpoints)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseJsonEndpoints(t *testing.T) {
|
|
||||||
validateJSONParsing(t, "", api.Endpoints{}, true)
|
|
||||||
endpoints := api.Endpoints{
|
|
||||||
Name: "foo",
|
|
||||||
Endpoints: []string{"foo", "bar", "baz"},
|
|
||||||
}
|
|
||||||
data, err := json.Marshal(endpoints)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %#v", err)
|
|
||||||
}
|
|
||||||
validateJSONParsing(t, string(data), endpoints, false)
|
|
||||||
// validateJSONParsing(t, "[{\"port\":8000,\"name\":\"mysql\",\"machine\":\"foo\"},{\"port\":9000,\"name\":\"mysql\",\"machine\":\"bar\"}]", []string{"foo:8000", "bar:9000"}, false)
|
|
||||||
}
|
|
@ -112,7 +112,7 @@ func (s ConfigSourceFile) Run() {
|
|||||||
newEndpoints := make([]api.Endpoints, len(config.Services))
|
newEndpoints := make([]api.Endpoints, len(config.Services))
|
||||||
for i, service := range config.Services {
|
for i, service := range config.Services {
|
||||||
newServices[i] = api.Service{JSONBase: api.JSONBase{ID: service.Name}, Port: service.Port}
|
newServices[i] = api.Service{JSONBase: api.JSONBase{ID: service.Name}, Port: service.Port}
|
||||||
newEndpoints[i] = api.Endpoints{Name: service.Name, Endpoints: service.Endpoints}
|
newEndpoints[i] = api.Endpoints{JSONBase: api.JSONBase{ID: service.Name}, Endpoints: service.Endpoints}
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(lastServices, newServices) {
|
if !reflect.DeepEqual(lastServices, newServices) {
|
||||||
serviceUpdate := ServiceUpdate{Op: SET, Services: newServices}
|
serviceUpdate := ServiceUpdate{Op: SET, Services: newServices}
|
||||||
|
@ -52,7 +52,8 @@ func TestProxy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lb := NewLoadBalancerRR()
|
lb := NewLoadBalancerRR()
|
||||||
lb.OnUpdate([]api.Endpoints{{"echo", []string{net.JoinHostPort("127.0.0.1", port)}}})
|
lb.OnUpdate([]api.Endpoints{
|
||||||
|
{JSONBase: api.JSONBase{ID: "echo"}, Endpoints: []string{net.JoinHostPort("127.0.0.1", port)}}})
|
||||||
|
|
||||||
p := NewProxier(lb)
|
p := NewProxier(lb)
|
||||||
|
|
||||||
|
@ -89,15 +89,15 @@ func (impl LoadBalancerRR) OnUpdate(endpoints []api.Endpoints) {
|
|||||||
defer impl.lock.Unlock()
|
defer impl.lock.Unlock()
|
||||||
// First update / add all new endpoints for services.
|
// First update / add all new endpoints for services.
|
||||||
for _, value := range endpoints {
|
for _, value := range endpoints {
|
||||||
existingEndpoints, exists := impl.endpointsMap[value.Name]
|
existingEndpoints, exists := impl.endpointsMap[value.ID]
|
||||||
validEndpoints := impl.filterValidEndpoints(value.Endpoints)
|
validEndpoints := impl.filterValidEndpoints(value.Endpoints)
|
||||||
if !exists || !reflect.DeepEqual(existingEndpoints, validEndpoints) {
|
if !exists || !reflect.DeepEqual(existingEndpoints, validEndpoints) {
|
||||||
glog.Infof("LoadBalancerRR: Setting endpoints for %s to %+v", value.Name, value.Endpoints)
|
glog.Infof("LoadBalancerRR: Setting endpoints for %s to %+v", value.ID, value.Endpoints)
|
||||||
impl.endpointsMap[value.Name] = validEndpoints
|
impl.endpointsMap[value.ID] = validEndpoints
|
||||||
// Start RR from the beginning if added or updated.
|
// Start RR from the beginning if added or updated.
|
||||||
impl.rrIndex[value.Name] = 0
|
impl.rrIndex[value.ID] = 0
|
||||||
}
|
}
|
||||||
tmp[value.Name] = true
|
tmp[value.ID] = true
|
||||||
}
|
}
|
||||||
// Then remove any endpoints no longer relevant
|
// Then remove any endpoints no longer relevant
|
||||||
for key, value := range impl.endpointsMap {
|
for key, value := range impl.endpointsMap {
|
||||||
|
@ -87,7 +87,10 @@ func TestLoadBalanceWorksWithSingleEndpoint(t *testing.T) {
|
|||||||
t.Errorf("Didn't fail with non-existent service")
|
t.Errorf("Didn't fail with non-existent service")
|
||||||
}
|
}
|
||||||
endpoints := make([]api.Endpoints, 1)
|
endpoints := make([]api.Endpoints, 1)
|
||||||
endpoints[0] = api.Endpoints{Name: "foo", Endpoints: []string{"endpoint1:40"}}
|
endpoints[0] = api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint1:40"},
|
||||||
|
}
|
||||||
loadBalancer.OnUpdate(endpoints)
|
loadBalancer.OnUpdate(endpoints)
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint1:40")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint1:40")
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint1:40")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint1:40")
|
||||||
@ -102,7 +105,10 @@ func TestLoadBalanceWorksWithMultipleEndpoints(t *testing.T) {
|
|||||||
t.Errorf("Didn't fail with non-existent service")
|
t.Errorf("Didn't fail with non-existent service")
|
||||||
}
|
}
|
||||||
endpoints := make([]api.Endpoints, 1)
|
endpoints := make([]api.Endpoints, 1)
|
||||||
endpoints[0] = api.Endpoints{Name: "foo", Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}}
|
endpoints[0] = api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||||
|
}
|
||||||
loadBalancer.OnUpdate(endpoints)
|
loadBalancer.OnUpdate(endpoints)
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:1")
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
||||||
@ -117,7 +123,10 @@ func TestLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) {
|
|||||||
t.Errorf("Didn't fail with non-existent service")
|
t.Errorf("Didn't fail with non-existent service")
|
||||||
}
|
}
|
||||||
endpoints := make([]api.Endpoints, 1)
|
endpoints := make([]api.Endpoints, 1)
|
||||||
endpoints[0] = api.Endpoints{Name: "foo", Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}}
|
endpoints[0] = api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||||
|
}
|
||||||
loadBalancer.OnUpdate(endpoints)
|
loadBalancer.OnUpdate(endpoints)
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:1")
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
||||||
@ -126,14 +135,16 @@ func TestLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) {
|
|||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
||||||
// Then update the configuration with one fewer endpoints, make sure
|
// Then update the configuration with one fewer endpoints, make sure
|
||||||
// we start in the beginning again
|
// we start in the beginning again
|
||||||
endpoints[0] = api.Endpoints{Name: "foo", Endpoints: []string{"endpoint:8", "endpoint:9"}}
|
endpoints[0] = api.Endpoints{JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint:8", "endpoint:9"},
|
||||||
|
}
|
||||||
loadBalancer.OnUpdate(endpoints)
|
loadBalancer.OnUpdate(endpoints)
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:8")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:8")
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:9")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:9")
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:8")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:8")
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:9")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:9")
|
||||||
// Clear endpoints
|
// Clear endpoints
|
||||||
endpoints[0] = api.Endpoints{Name: "foo", Endpoints: []string{}}
|
endpoints[0] = api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}}
|
||||||
loadBalancer.OnUpdate(endpoints)
|
loadBalancer.OnUpdate(endpoints)
|
||||||
|
|
||||||
endpoint, err = loadBalancer.LoadBalance("foo", nil)
|
endpoint, err = loadBalancer.LoadBalance("foo", nil)
|
||||||
@ -149,8 +160,14 @@ func TestLoadBalanceWorksWithServiceRemoval(t *testing.T) {
|
|||||||
t.Errorf("Didn't fail with non-existent service")
|
t.Errorf("Didn't fail with non-existent service")
|
||||||
}
|
}
|
||||||
endpoints := make([]api.Endpoints, 2)
|
endpoints := make([]api.Endpoints, 2)
|
||||||
endpoints[0] = api.Endpoints{Name: "foo", Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"}}
|
endpoints[0] = api.Endpoints{
|
||||||
endpoints[1] = api.Endpoints{Name: "bar", Endpoints: []string{"endpoint:4", "endpoint:5"}}
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
|
Endpoints: []string{"endpoint:1", "endpoint:2", "endpoint:3"},
|
||||||
|
}
|
||||||
|
endpoints[1] = api.Endpoints{
|
||||||
|
JSONBase: api.JSONBase{ID: "bar"},
|
||||||
|
Endpoints: []string{"endpoint:4", "endpoint:5"},
|
||||||
|
}
|
||||||
loadBalancer.OnUpdate(endpoints)
|
loadBalancer.OnUpdate(endpoints)
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:1")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:1")
|
||||||
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
expectEndpoint(t, loadBalancer, "foo", "endpoint:2")
|
||||||
|
@ -88,7 +88,7 @@ func (e *EndpointController) SyncServiceEndpoints() error {
|
|||||||
endpoints[ix] = net.JoinHostPort(pod.CurrentState.PodIP, strconv.Itoa(port))
|
endpoints[ix] = net.JoinHostPort(pod.CurrentState.PodIP, strconv.Itoa(port))
|
||||||
}
|
}
|
||||||
err = e.serviceRegistry.UpdateEndpoints(api.Endpoints{
|
err = e.serviceRegistry.UpdateEndpoints(api.Endpoints{
|
||||||
Name: service.ID,
|
JSONBase: api.JSONBase{ID: service.ID},
|
||||||
Endpoints: endpoints,
|
Endpoints: endpoints,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -112,9 +112,10 @@ func (registry *EtcdRegistry) runPod(pod api.Pod, machine string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contKey := makeContainerKey(machine)
|
contKey := makeContainerKey(machine)
|
||||||
err = registry.helper().AtomicUpdate(contKey, &[]api.ContainerManifest{}, func(in interface{}) (interface{}, error) {
|
err = registry.helper().AtomicUpdate(contKey, &api.ContainerManifestList{}, func(in interface{}) (interface{}, error) {
|
||||||
manifests := *in.(*[]api.ContainerManifest)
|
manifests := *in.(*api.ContainerManifestList)
|
||||||
return append(manifests, manifest), nil
|
manifests.Items = append(manifests.Items, manifest)
|
||||||
|
return manifests, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't strand stuff.
|
// Don't strand stuff.
|
||||||
@ -153,11 +154,11 @@ func (registry *EtcdRegistry) deletePodFromMachine(machine, podID string) error
|
|||||||
|
|
||||||
// Next, remove the pod from the machine atomically.
|
// Next, remove the pod from the machine atomically.
|
||||||
contKey := makeContainerKey(machine)
|
contKey := makeContainerKey(machine)
|
||||||
return registry.helper().AtomicUpdate(contKey, &[]api.ContainerManifest{}, func(in interface{}) (interface{}, error) {
|
return registry.helper().AtomicUpdate(contKey, &api.ContainerManifestList{}, func(in interface{}) (interface{}, error) {
|
||||||
manifests := *in.(*[]api.ContainerManifest)
|
manifests := in.(*api.ContainerManifestList)
|
||||||
newManifests := make([]api.ContainerManifest, 0, len(manifests))
|
newManifests := make([]api.ContainerManifest, 0, len(manifests.Items))
|
||||||
found := false
|
found := false
|
||||||
for _, manifest := range manifests {
|
for _, manifest := range manifests.Items {
|
||||||
if manifest.ID != podID {
|
if manifest.ID != podID {
|
||||||
newManifests = append(newManifests, manifest)
|
newManifests = append(newManifests, manifest)
|
||||||
} else {
|
} else {
|
||||||
@ -170,7 +171,8 @@ func (registry *EtcdRegistry) deletePodFromMachine(machine, podID string) error
|
|||||||
// However it is "deleted" so log it and move on
|
// However it is "deleted" so log it and move on
|
||||||
glog.Infof("Couldn't find: %s in %#v", podID, manifests)
|
glog.Infof("Couldn't find: %s in %#v", podID, manifests)
|
||||||
}
|
}
|
||||||
return newManifests, nil
|
manifests.Items = newManifests
|
||||||
|
return manifests, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,5 +306,5 @@ func (registry *EtcdRegistry) UpdateService(svc api.Service) error {
|
|||||||
|
|
||||||
// UpdateEndpoints update Endpoints of a Service.
|
// UpdateEndpoints update Endpoints of a Service.
|
||||||
func (registry *EtcdRegistry) UpdateEndpoints(e api.Endpoints) error {
|
func (registry *EtcdRegistry) UpdateEndpoints(e api.Endpoints) error {
|
||||||
return registry.helper().SetObj("/registry/services/endpoints/"+e.Name, e)
|
return registry.helper().SetObj("/registry/services/endpoints/"+e.ID, e)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -70,7 +69,7 @@ func TestEtcdCreatePod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
E: tools.EtcdErrorNotFound,
|
E: tools.EtcdErrorNotFound,
|
||||||
}
|
}
|
||||||
fakeClient.Set("/registry/hosts/machine/kubelet", util.MakeJSONString([]api.ContainerManifest{}), 0)
|
fakeClient.Set("/registry/hosts/machine/kubelet", api.EncodeOrDie(&api.ContainerManifestList{}), 0)
|
||||||
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
||||||
err := registry.CreatePod("machine", api.Pod{
|
err := registry.CreatePod("machine", api.Pod{
|
||||||
JSONBase: api.JSONBase{
|
JSONBase: api.JSONBase{
|
||||||
@ -88,18 +87,20 @@ func TestEtcdCreatePod(t *testing.T) {
|
|||||||
})
|
})
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
resp, err := fakeClient.Get("/registry/hosts/machine/pods/foo", false, false)
|
resp, err := fakeClient.Get("/registry/hosts/machine/pods/foo", false, false)
|
||||||
expectNoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
var pod api.Pod
|
var pod api.Pod
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &pod)
|
err = api.DecodeInto([]byte(resp.Node.Value), &pod)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
if pod.ID != "foo" {
|
if pod.ID != "foo" {
|
||||||
t.Errorf("Unexpected pod: %#v %s", pod, resp.Node.Value)
|
t.Errorf("Unexpected pod: %#v %s", pod, resp.Node.Value)
|
||||||
}
|
}
|
||||||
var manifests []api.ContainerManifest
|
var manifests api.ContainerManifestList
|
||||||
resp, err = fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
resp, err = fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &manifests)
|
err = api.DecodeInto([]byte(resp.Node.Value), &manifests)
|
||||||
if len(manifests) != 1 || manifests[0].ID != "foo" {
|
if len(manifests.Items) != 1 || manifests.Items[0].ID != "foo" {
|
||||||
t.Errorf("Unexpected manifest list: %#v", manifests)
|
t.Errorf("Unexpected manifest list: %#v", manifests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,18 +190,20 @@ func TestEtcdCreatePodWithContainersNotFound(t *testing.T) {
|
|||||||
})
|
})
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
resp, err := fakeClient.Get("/registry/hosts/machine/pods/foo", false, false)
|
resp, err := fakeClient.Get("/registry/hosts/machine/pods/foo", false, false)
|
||||||
expectNoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
var pod api.Pod
|
var pod api.Pod
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &pod)
|
err = api.DecodeInto([]byte(resp.Node.Value), &pod)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
if pod.ID != "foo" {
|
if pod.ID != "foo" {
|
||||||
t.Errorf("Unexpected pod: %#v %s", pod, resp.Node.Value)
|
t.Errorf("Unexpected pod: %#v %s", pod, resp.Node.Value)
|
||||||
}
|
}
|
||||||
var manifests []api.ContainerManifest
|
var manifests api.ContainerManifestList
|
||||||
resp, err = fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
resp, err = fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &manifests)
|
err = api.DecodeInto([]byte(resp.Node.Value), &manifests)
|
||||||
if len(manifests) != 1 || manifests[0].ID != "foo" {
|
if len(manifests.Items) != 1 || manifests.Items[0].ID != "foo" {
|
||||||
t.Errorf("Unexpected manifest list: %#v", manifests)
|
t.Errorf("Unexpected manifest list: %#v", manifests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,9 +216,9 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
E: tools.EtcdErrorNotFound,
|
E: tools.EtcdErrorNotFound,
|
||||||
}
|
}
|
||||||
fakeClient.Set("/registry/hosts/machine/kubelet", util.MakeJSONString([]api.ContainerManifest{
|
fakeClient.Set("/registry/hosts/machine/kubelet", api.EncodeOrDie(api.ContainerManifestList{
|
||||||
{
|
Items: []api.ContainerManifest{
|
||||||
ID: "bar",
|
{ID: "bar"},
|
||||||
},
|
},
|
||||||
}), 0)
|
}), 0)
|
||||||
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
||||||
@ -236,18 +239,20 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) {
|
|||||||
})
|
})
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
resp, err := fakeClient.Get("/registry/hosts/machine/pods/foo", false, false)
|
resp, err := fakeClient.Get("/registry/hosts/machine/pods/foo", false, false)
|
||||||
expectNoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
var pod api.Pod
|
var pod api.Pod
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &pod)
|
err = api.DecodeInto([]byte(resp.Node.Value), &pod)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
if pod.ID != "foo" {
|
if pod.ID != "foo" {
|
||||||
t.Errorf("Unexpected pod: %#v %s", pod, resp.Node.Value)
|
t.Errorf("Unexpected pod: %#v %s", pod, resp.Node.Value)
|
||||||
}
|
}
|
||||||
var manifests []api.ContainerManifest
|
var manifests api.ContainerManifestList
|
||||||
resp, err = fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
resp, err = fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &manifests)
|
err = api.DecodeInto([]byte(resp.Node.Value), &manifests)
|
||||||
if len(manifests) != 2 || manifests[1].ID != "foo" {
|
if len(manifests.Items) != 2 || manifests.Items[1].ID != "foo" {
|
||||||
t.Errorf("Unexpected manifest list: %#v", manifests)
|
t.Errorf("Unexpected manifest list: %#v", manifests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,9 +261,9 @@ func TestEtcdDeletePod(t *testing.T) {
|
|||||||
fakeClient := tools.MakeFakeEtcdClient(t)
|
fakeClient := tools.MakeFakeEtcdClient(t)
|
||||||
key := "/registry/hosts/machine/pods/foo"
|
key := "/registry/hosts/machine/pods/foo"
|
||||||
fakeClient.Set(key, util.MakeJSONString(api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0)
|
fakeClient.Set(key, util.MakeJSONString(api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0)
|
||||||
fakeClient.Set("/registry/hosts/machine/kubelet", util.MakeJSONString([]api.ContainerManifest{
|
fakeClient.Set("/registry/hosts/machine/kubelet", api.EncodeOrDie(&api.ContainerManifestList{
|
||||||
{
|
Items: []api.ContainerManifest{
|
||||||
ID: "foo",
|
{ID: "foo"},
|
||||||
},
|
},
|
||||||
}), 0)
|
}), 0)
|
||||||
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
||||||
@ -269,8 +274,13 @@ func TestEtcdDeletePod(t *testing.T) {
|
|||||||
} else if fakeClient.DeletedKeys[0] != key {
|
} else if fakeClient.DeletedKeys[0] != key {
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
||||||
}
|
}
|
||||||
response, _ := fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
response, err := fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
||||||
if response.Node.Value != "[]" {
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
|
var manifests api.ContainerManifestList
|
||||||
|
api.DecodeInto([]byte(response.Node.Value), &manifests)
|
||||||
|
if len(manifests.Items) != 0 {
|
||||||
t.Errorf("Unexpected container set: %s, expected empty", response.Node.Value)
|
t.Errorf("Unexpected container set: %s, expected empty", response.Node.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,9 +289,11 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) {
|
|||||||
fakeClient := tools.MakeFakeEtcdClient(t)
|
fakeClient := tools.MakeFakeEtcdClient(t)
|
||||||
key := "/registry/hosts/machine/pods/foo"
|
key := "/registry/hosts/machine/pods/foo"
|
||||||
fakeClient.Set(key, util.MakeJSONString(api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0)
|
fakeClient.Set(key, util.MakeJSONString(api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0)
|
||||||
fakeClient.Set("/registry/hosts/machine/kubelet", util.MakeJSONString([]api.ContainerManifest{
|
fakeClient.Set("/registry/hosts/machine/kubelet", api.EncodeOrDie(&api.ContainerManifestList{
|
||||||
{ID: "foo"},
|
Items: []api.ContainerManifest{
|
||||||
{ID: "bar"},
|
{ID: "foo"},
|
||||||
|
{ID: "bar"},
|
||||||
|
},
|
||||||
}), 0)
|
}), 0)
|
||||||
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
||||||
err := registry.DeletePod("foo")
|
err := registry.DeletePod("foo")
|
||||||
@ -292,13 +304,16 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) {
|
|||||||
if fakeClient.DeletedKeys[0] != key {
|
if fakeClient.DeletedKeys[0] != key {
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
||||||
}
|
}
|
||||||
response, _ := fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
response, err := fakeClient.Get("/registry/hosts/machine/kubelet", false, false)
|
||||||
var manifests []api.ContainerManifest
|
if err != nil {
|
||||||
json.Unmarshal([]byte(response.Node.Value), &manifests)
|
t.Fatalf("Unexpected error %v", err)
|
||||||
if len(manifests) != 1 {
|
|
||||||
t.Errorf("Unexpected manifest set: %#v, expected empty", manifests)
|
|
||||||
}
|
}
|
||||||
if manifests[0].ID != "bar" {
|
var manifests api.ContainerManifestList
|
||||||
|
api.DecodeInto([]byte(response.Node.Value), &manifests)
|
||||||
|
if len(manifests.Items) != 1 {
|
||||||
|
t.Fatalf("Unexpected manifest set: %#v, expected empty", manifests)
|
||||||
|
}
|
||||||
|
if manifests.Items[0].ID != "bar" {
|
||||||
t.Errorf("Deleted wrong manifest: %#v", manifests)
|
t.Errorf("Deleted wrong manifest: %#v", manifests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,9 +491,11 @@ func TestEtcdCreateController(t *testing.T) {
|
|||||||
})
|
})
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
resp, err := fakeClient.Get("/registry/controllers/foo", false, false)
|
resp, err := fakeClient.Get("/registry/controllers/foo", false, false)
|
||||||
expectNoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
var ctrl api.ReplicationController
|
var ctrl api.ReplicationController
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &ctrl)
|
err = api.DecodeInto([]byte(resp.Node.Value), &ctrl)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
if ctrl.ID != "foo" {
|
if ctrl.ID != "foo" {
|
||||||
t.Errorf("Unexpected pod: %#v %s", ctrl, resp.Node.Value)
|
t.Errorf("Unexpected pod: %#v %s", ctrl, resp.Node.Value)
|
||||||
@ -544,7 +561,7 @@ func TestEtcdCreateService(t *testing.T) {
|
|||||||
resp, err := fakeClient.Get("/registry/services/specs/foo", false, false)
|
resp, err := fakeClient.Get("/registry/services/specs/foo", false, false)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
var service api.Service
|
var service api.Service
|
||||||
err = json.Unmarshal([]byte(resp.Node.Value), &service)
|
err = api.DecodeInto([]byte(resp.Node.Value), &service)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
if service.ID != "foo" {
|
if service.ID != "foo" {
|
||||||
t.Errorf("Unexpected service: %#v %s", service, resp.Node.Value)
|
t.Errorf("Unexpected service: %#v %s", service, resp.Node.Value)
|
||||||
@ -621,15 +638,17 @@ func TestEtcdUpdateEndpoints(t *testing.T) {
|
|||||||
fakeClient := tools.MakeFakeEtcdClient(t)
|
fakeClient := tools.MakeFakeEtcdClient(t)
|
||||||
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
registry := MakeTestEtcdRegistry(fakeClient, []string{"machine"})
|
||||||
endpoints := api.Endpoints{
|
endpoints := api.Endpoints{
|
||||||
Name: "foo",
|
JSONBase: api.JSONBase{ID: "foo"},
|
||||||
Endpoints: []string{"baz", "bar"},
|
Endpoints: []string{"baz", "bar"},
|
||||||
}
|
}
|
||||||
err := registry.UpdateEndpoints(endpoints)
|
err := registry.UpdateEndpoints(endpoints)
|
||||||
expectNoError(t, err)
|
expectNoError(t, err)
|
||||||
response, err := fakeClient.Get("/registry/services/endpoints/foo", false, false)
|
response, err := fakeClient.Get("/registry/services/endpoints/foo", false, false)
|
||||||
expectNoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
var endpointsOut api.Endpoints
|
var endpointsOut api.Endpoints
|
||||||
err = json.Unmarshal([]byte(response.Node.Value), &endpointsOut)
|
err = api.DecodeInto([]byte(response.Node.Value), &endpointsOut)
|
||||||
if !reflect.DeepEqual(endpoints, endpointsOut) {
|
if !reflect.DeepEqual(endpoints, endpointsOut) {
|
||||||
t.Errorf("Unexpected endpoints: %#v, expected %#v", endpointsOut, endpoints)
|
t.Errorf("Unexpected endpoints: %#v, expected %#v", endpointsOut, endpoints)
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ func (h *EtcdHelper) ExtractList(key string, slicePtr interface{}) error {
|
|||||||
v := pv.Elem()
|
v := pv.Elem()
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
obj := reflect.New(v.Type().Elem())
|
obj := reflect.New(v.Type().Elem())
|
||||||
err = json.Unmarshal([]byte(node.Value), obj.Interface())
|
err = api.DecodeInto([]byte(node.Value), obj.Interface())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -146,9 +146,9 @@ func (h *EtcdHelper) bodyAndExtractObj(key string, objPtr interface{}, ignoreNot
|
|||||||
return "", 0, fmt.Errorf("key '%v' found no nodes field: %#v", key, response)
|
return "", 0, fmt.Errorf("key '%v' found no nodes field: %#v", key, response)
|
||||||
}
|
}
|
||||||
body = response.Node.Value
|
body = response.Node.Value
|
||||||
err = json.Unmarshal([]byte(body), objPtr)
|
err = api.DecodeInto([]byte(body), objPtr)
|
||||||
if jsonBase, err := api.FindJSONBase(objPtr); err == nil {
|
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
|
// Note that err shadows the err returned below, so we won't
|
||||||
// return an error just because we failed to find a JSONBase.
|
// return an error just because we failed to find a JSONBase.
|
||||||
// This is intentional.
|
// This is intentional.
|
||||||
@ -159,7 +159,7 @@ func (h *EtcdHelper) bodyAndExtractObj(key string, objPtr interface{}, ignoreNot
|
|||||||
// SetObj marshals obj via json, and stores under key. Will do an
|
// SetObj marshals obj via json, and stores under key. Will do an
|
||||||
// atomic update if obj's ResourceVersion field is set.
|
// atomic update if obj's ResourceVersion field is set.
|
||||||
func (h *EtcdHelper) SetObj(key string, obj interface{}) error {
|
func (h *EtcdHelper) SetObj(key string, obj interface{}) error {
|
||||||
data, err := json.Marshal(obj)
|
data, err := api.Encode(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -45,10 +45,6 @@ func TestIsNotFoundErr(t *testing.T) {
|
|||||||
try(fmt.Errorf("some other kind of error"), false)
|
try(fmt.Errorf("some other kind of error"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type testMarshalType struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtractList(t *testing.T) {
|
func TestExtractList(t *testing.T) {
|
||||||
fakeClient := MakeFakeEtcdClient(t)
|
fakeClient := MakeFakeEtcdClient(t)
|
||||||
fakeClient.Data["/some/key"] = EtcdResponseWithError{
|
fakeClient.Data["/some/key"] = EtcdResponseWithError{
|
||||||
@ -68,12 +64,12 @@ func TestExtractList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expect := []testMarshalType{
|
expect := []api.Pod{
|
||||||
{"foo"},
|
{JSONBase: api.JSONBase{ID: "foo"}},
|
||||||
{"bar"},
|
{JSONBase: api.JSONBase{ID: "bar"}},
|
||||||
{"baz"},
|
{JSONBase: api.JSONBase{ID: "baz"}},
|
||||||
}
|
}
|
||||||
var got []testMarshalType
|
var got []api.Pod
|
||||||
helper := EtcdHelper{fakeClient}
|
helper := EtcdHelper{fakeClient}
|
||||||
err := helper.ExtractList("/some/key", &got)
|
err := helper.ExtractList("/some/key", &got)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -86,10 +82,10 @@ func TestExtractList(t *testing.T) {
|
|||||||
|
|
||||||
func TestExtractObj(t *testing.T) {
|
func TestExtractObj(t *testing.T) {
|
||||||
fakeClient := MakeFakeEtcdClient(t)
|
fakeClient := MakeFakeEtcdClient(t)
|
||||||
expect := testMarshalType{ID: "foo"}
|
expect := api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
|
||||||
fakeClient.Set("/some/key", util.MakeJSONString(expect), 0)
|
fakeClient.Set("/some/key", util.MakeJSONString(expect), 0)
|
||||||
helper := EtcdHelper{fakeClient}
|
helper := EtcdHelper{fakeClient}
|
||||||
var got testMarshalType
|
var got api.Pod
|
||||||
err := helper.ExtractObj("/some/key", &got, false)
|
err := helper.ExtractObj("/some/key", &got, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %#v", err)
|
t.Errorf("Unexpected error %#v", err)
|
||||||
@ -123,7 +119,7 @@ func TestExtractObjNotFoundErr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
helper := EtcdHelper{fakeClient}
|
helper := EtcdHelper{fakeClient}
|
||||||
try := func(key string) {
|
try := func(key string) {
|
||||||
var got testMarshalType
|
var got api.Pod
|
||||||
err := helper.ExtractObj(key, &got, false)
|
err := helper.ExtractObj(key, &got, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("%s: wanted error but didn't get one", key)
|
t.Errorf("%s: wanted error but didn't get one", key)
|
||||||
@ -140,14 +136,18 @@ func TestExtractObjNotFoundErr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSetObj(t *testing.T) {
|
func TestSetObj(t *testing.T) {
|
||||||
obj := testMarshalType{ID: "foo"}
|
obj := api.Pod{JSONBase: api.JSONBase{ID: "foo"}}
|
||||||
fakeClient := MakeFakeEtcdClient(t)
|
fakeClient := MakeFakeEtcdClient(t)
|
||||||
helper := EtcdHelper{fakeClient}
|
helper := EtcdHelper{fakeClient}
|
||||||
err := helper.SetObj("/some/key", obj)
|
err := helper.SetObj("/some/key", obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %#v", err)
|
t.Errorf("Unexpected error %#v", err)
|
||||||
}
|
}
|
||||||
expect := util.MakeJSONString(obj)
|
data, err := api.Encode(obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error %#v", err)
|
||||||
|
}
|
||||||
|
expect := string(data)
|
||||||
got := fakeClient.Data["/some/key"].R.Node.Value
|
got := fakeClient.Data["/some/key"].R.Node.Value
|
||||||
if expect != got {
|
if expect != got {
|
||||||
t.Errorf("Wanted %v, got %v", expect, got)
|
t.Errorf("Wanted %v, got %v", expect, got)
|
||||||
|
Loading…
Reference in New Issue
Block a user