mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	SetSelfLink is inefficient
Generating self links, especially for lists, is inefficient. Replace use of net.URL.String() with direct encoding that reduces number of allocations. Switch from calling meta.ExtractList|SetList to a function that iterates over each object in the list. In steady state for nodes performing frequently small get/list operations, and for larger LISTs significantly reduces CPU and allocations.
This commit is contained in:
		@@ -57,6 +57,57 @@ func GetItemsPtr(list runtime.Object) (interface{}, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EachListItem invokes fn on each runtime.Object in the list. Any error immediately terminates
 | 
				
			||||||
 | 
					// the loop.
 | 
				
			||||||
 | 
					func EachListItem(obj runtime.Object, fn func(runtime.Object) error) error {
 | 
				
			||||||
 | 
						// TODO: Change to an interface call?
 | 
				
			||||||
 | 
						itemsPtr, err := GetItemsPtr(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						items, err := conversion.EnforcePtr(itemsPtr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						len := items.Len()
 | 
				
			||||||
 | 
						if len == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						takeAddr := false
 | 
				
			||||||
 | 
						if elemType := items.Type().Elem(); elemType.Kind() != reflect.Ptr && elemType.Kind() != reflect.Interface {
 | 
				
			||||||
 | 
							if !items.Index(0).CanAddr() {
 | 
				
			||||||
 | 
								return fmt.Errorf("unable to take address of items in %T for EachListItem", obj)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							takeAddr = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < len; i++ {
 | 
				
			||||||
 | 
							raw := items.Index(i)
 | 
				
			||||||
 | 
							if takeAddr {
 | 
				
			||||||
 | 
								raw = raw.Addr()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							switch item := raw.Interface().(type) {
 | 
				
			||||||
 | 
							case *runtime.RawExtension:
 | 
				
			||||||
 | 
								if err := fn(item.Object); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case runtime.Object:
 | 
				
			||||||
 | 
								if err := fn(item); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								obj, ok := item.(runtime.Object)
 | 
				
			||||||
 | 
								if !ok {
 | 
				
			||||||
 | 
									return fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err := fn(obj); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ExtractList returns obj's Items element as an array of runtime.Objects.
 | 
					// ExtractList returns obj's Items element as an array of runtime.Objects.
 | 
				
			||||||
// Returns an error if obj is not a List type (does not have an Items member).
 | 
					// Returns an error if obj is not a List type (does not have an Items member).
 | 
				
			||||||
func ExtractList(obj runtime.Object) ([]runtime.Object, error) {
 | 
					func ExtractList(obj runtime.Object) ([]runtime.Object, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,94 +46,185 @@ func TestIsList(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestExtractList(t *testing.T) {
 | 
					func TestExtractList(t *testing.T) {
 | 
				
			||||||
	pl := &api.PodList{
 | 
						list1 := []runtime.Object{
 | 
				
			||||||
		Items: []api.Pod{
 | 
							&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
			{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
							&api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
				
			||||||
			{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
					 | 
				
			||||||
			{ObjectMeta: api.ObjectMeta{Name: "3"}},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	list, err := meta.ExtractList(pl)
 | 
						list2 := &v1.List{
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("Unexpected error %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if e, a := len(list), len(pl.Items); e != a {
 | 
					 | 
				
			||||||
		t.Fatalf("Expected %v, got %v", e, a)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for i := range list {
 | 
					 | 
				
			||||||
		if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a {
 | 
					 | 
				
			||||||
			t.Fatalf("Expected %v, got %v", e, a)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestExtractListV1(t *testing.T) {
 | 
					 | 
				
			||||||
	pl := &v1.PodList{
 | 
					 | 
				
			||||||
		Items: []v1.Pod{
 | 
					 | 
				
			||||||
			{ObjectMeta: v1.ObjectMeta{Name: "1"}},
 | 
					 | 
				
			||||||
			{ObjectMeta: v1.ObjectMeta{Name: "2"}},
 | 
					 | 
				
			||||||
			{ObjectMeta: v1.ObjectMeta{Name: "3"}},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	list, err := meta.ExtractList(pl)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("Unexpected error %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if e, a := len(list), len(pl.Items); e != a {
 | 
					 | 
				
			||||||
		t.Fatalf("Expected %v, got %v", e, a)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for i := range list {
 | 
					 | 
				
			||||||
		if e, a := list[i].(*v1.Pod).Name, pl.Items[i].Name; e != a {
 | 
					 | 
				
			||||||
			t.Fatalf("Expected %v, got %v", e, a)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestExtractListGeneric(t *testing.T) {
 | 
					 | 
				
			||||||
	pl := &api.List{
 | 
					 | 
				
			||||||
		Items: []runtime.Object{
 | 
					 | 
				
			||||||
			&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
					 | 
				
			||||||
			&api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	list, err := meta.ExtractList(pl)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("Unexpected error %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if e, a := len(list), len(pl.Items); e != a {
 | 
					 | 
				
			||||||
		t.Fatalf("Expected %v, got %v", e, a)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if obj, ok := list[0].(*api.Pod); !ok {
 | 
					 | 
				
			||||||
		t.Fatalf("Expected list[0] to be *api.Pod, it is %#v", obj)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if obj, ok := list[1].(*api.Service); !ok {
 | 
					 | 
				
			||||||
		t.Fatalf("Expected list[1] to be *api.Service, it is %#v", obj)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestExtractListGenericV1(t *testing.T) {
 | 
					 | 
				
			||||||
	pl := &v1.List{
 | 
					 | 
				
			||||||
		Items: []runtime.RawExtension{
 | 
							Items: []runtime.RawExtension{
 | 
				
			||||||
			{Raw: []byte("foo")},
 | 
								{Raw: []byte("foo")},
 | 
				
			||||||
			{Raw: []byte("bar")},
 | 
								{Raw: []byte("bar")},
 | 
				
			||||||
			{Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}},
 | 
								{Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	list, err := meta.ExtractList(pl)
 | 
						list3 := &fakePtrValueList{
 | 
				
			||||||
	if err != nil {
 | 
							Items: []*api.Pod{
 | 
				
			||||||
		t.Fatalf("Unexpected error %v", err)
 | 
								{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if e, a := len(list), len(pl.Items); e != a {
 | 
						list4 := &api.PodList{
 | 
				
			||||||
		t.Fatalf("Expected %v, got %v", e, a)
 | 
							Items: []api.Pod{
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "3"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if obj, ok := list[0].(*runtime.Unknown); !ok {
 | 
						list5 := &v1.PodList{
 | 
				
			||||||
		t.Fatalf("Expected list[0] to be *runtime.Unknown, it is %#v", obj)
 | 
							Items: []v1.Pod{
 | 
				
			||||||
 | 
								{ObjectMeta: v1.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								{ObjectMeta: v1.ObjectMeta{Name: "2"}},
 | 
				
			||||||
 | 
								{ObjectMeta: v1.ObjectMeta{Name: "3"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if obj, ok := list[1].(*runtime.Unknown); !ok {
 | 
					
 | 
				
			||||||
		t.Fatalf("Expected list[1] to be *runtime.Unknown, it is %#v", obj)
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							in    runtime.Object
 | 
				
			||||||
 | 
							out   []interface{}
 | 
				
			||||||
 | 
							equal bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &api.List{},
 | 
				
			||||||
 | 
								out: []interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &v1.List{},
 | 
				
			||||||
 | 
								out: []interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &v1.PodList{},
 | 
				
			||||||
 | 
								out: []interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &api.List{Items: list1},
 | 
				
			||||||
 | 
								out: []interface{}{list1[0], list1[1]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:    list2,
 | 
				
			||||||
 | 
								out:   []interface{}{&runtime.Unknown{Raw: list2.Items[0].Raw}, &runtime.Unknown{Raw: list2.Items[1].Raw}, list2.Items[2].Object},
 | 
				
			||||||
 | 
								equal: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  list3,
 | 
				
			||||||
 | 
								out: []interface{}{list3.Items[0], list3.Items[1]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  list4,
 | 
				
			||||||
 | 
								out: []interface{}{&list4.Items[0], &list4.Items[1], &list4.Items[2]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  list5,
 | 
				
			||||||
 | 
								out: []interface{}{&list5.Items[0], &list5.Items[1], &list5.Items[2]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if obj, ok := list[2].(*v1.Pod); !ok {
 | 
						for i, test := range testCases {
 | 
				
			||||||
		t.Fatalf("Expected list[2] to be *runtime.Unknown, it is %#v", obj)
 | 
							list, err := meta.ExtractList(test.in)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatalf("%d: extract: Unexpected error %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if e, a := len(test.out), len(list); e != a {
 | 
				
			||||||
 | 
								t.Fatalf("%d: extract: Expected %v, got %v", i, e, a)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for j, e := range test.out {
 | 
				
			||||||
 | 
								if e != list[j] {
 | 
				
			||||||
 | 
									if !test.equal {
 | 
				
			||||||
 | 
										t.Fatalf("%d: extract: Expected list[%d] to be %#v, but found %#v", i, j, e, list[j])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if !reflect.DeepEqual(e, list[j]) {
 | 
				
			||||||
 | 
										t.Fatalf("%d: extract: Expected list[%d] to be %#v, but found %#v", i, j, e, list[j])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEachListItem(t *testing.T) {
 | 
				
			||||||
 | 
						list1 := []runtime.Object{
 | 
				
			||||||
 | 
							&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
							&api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						list2 := &v1.List{
 | 
				
			||||||
 | 
							Items: []runtime.RawExtension{
 | 
				
			||||||
 | 
								{Raw: []byte("foo")},
 | 
				
			||||||
 | 
								{Raw: []byte("bar")},
 | 
				
			||||||
 | 
								{Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						list3 := &fakePtrValueList{
 | 
				
			||||||
 | 
							Items: []*api.Pod{
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						list4 := &api.PodList{
 | 
				
			||||||
 | 
							Items: []api.Pod{
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
				
			||||||
 | 
								{ObjectMeta: api.ObjectMeta{Name: "3"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						list5 := &v1.PodList{
 | 
				
			||||||
 | 
							Items: []v1.Pod{
 | 
				
			||||||
 | 
								{ObjectMeta: v1.ObjectMeta{Name: "1"}},
 | 
				
			||||||
 | 
								{ObjectMeta: v1.ObjectMeta{Name: "2"}},
 | 
				
			||||||
 | 
								{ObjectMeta: v1.ObjectMeta{Name: "3"}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							in  runtime.Object
 | 
				
			||||||
 | 
							out []interface{}
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &api.List{},
 | 
				
			||||||
 | 
								out: []interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &v1.List{},
 | 
				
			||||||
 | 
								out: []interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &v1.PodList{},
 | 
				
			||||||
 | 
								out: []interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  &api.List{Items: list1},
 | 
				
			||||||
 | 
								out: []interface{}{list1[0], list1[1]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  list2,
 | 
				
			||||||
 | 
								out: []interface{}{nil, nil, list2.Items[2].Object},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  list3,
 | 
				
			||||||
 | 
								out: []interface{}{list3.Items[0], list3.Items[1]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  list4,
 | 
				
			||||||
 | 
								out: []interface{}{&list4.Items[0], &list4.Items[1], &list4.Items[2]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								in:  list5,
 | 
				
			||||||
 | 
								out: []interface{}{&list5.Items[0], &list5.Items[1], &list5.Items[2]},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i, test := range testCases {
 | 
				
			||||||
 | 
							list := []runtime.Object{}
 | 
				
			||||||
 | 
							err := meta.EachListItem(test.in, func(obj runtime.Object) error {
 | 
				
			||||||
 | 
								list = append(list, obj)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatalf("%d: each: Unexpected error %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if e, a := len(test.out), len(list); e != a {
 | 
				
			||||||
 | 
								t.Fatalf("%d: each: Expected %v, got %v", i, e, a)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for j, e := range test.out {
 | 
				
			||||||
 | 
								if e != list[j] {
 | 
				
			||||||
 | 
									t.Fatalf("%d: each: Expected list[%d] to be %#v, but found %#v", i, j, e, list[j])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -166,27 +257,6 @@ func (obj fakePtrValueList) GetObjectKind() unversioned.ObjectKind {
 | 
				
			|||||||
	return unversioned.EmptyObjectKind
 | 
						return unversioned.EmptyObjectKind
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestExtractListOfValuePtrs(t *testing.T) {
 | 
					 | 
				
			||||||
	pl := &fakePtrValueList{
 | 
					 | 
				
			||||||
		Items: []*api.Pod{
 | 
					 | 
				
			||||||
			{ObjectMeta: api.ObjectMeta{Name: "1"}},
 | 
					 | 
				
			||||||
			{ObjectMeta: api.ObjectMeta{Name: "2"}},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	list, err := meta.ExtractList(pl)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("Unexpected error %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if e, a := len(list), len(pl.Items); e != a {
 | 
					 | 
				
			||||||
		t.Fatalf("Expected %v, got %v", e, a)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for i := range list {
 | 
					 | 
				
			||||||
		if obj, ok := list[i].(*api.Pod); !ok {
 | 
					 | 
				
			||||||
			t.Fatalf("Expected list[%d] to be *api.Pod, it is %#v", i, obj)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestSetList(t *testing.T) {
 | 
					func TestSetList(t *testing.T) {
 | 
				
			||||||
	pl := &api.PodList{}
 | 
						pl := &api.PodList{}
 | 
				
			||||||
	list := []runtime.Object{
 | 
						list := []runtime.Object{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,8 +17,10 @@ limitations under the License.
 | 
				
			|||||||
package apiserver
 | 
					package apiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	gpath "path"
 | 
						gpath "path"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
@@ -356,15 +358,17 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
		itemPath := resourcePath + "/{name}"
 | 
							itemPath := resourcePath + "/{name}"
 | 
				
			||||||
		nameParams := append(params, nameParam)
 | 
							nameParams := append(params, nameParam)
 | 
				
			||||||
		proxyParams := append(nameParams, pathParam)
 | 
							proxyParams := append(nameParams, pathParam)
 | 
				
			||||||
 | 
							suffix := ""
 | 
				
			||||||
		if hasSubresource {
 | 
							if hasSubresource {
 | 
				
			||||||
			itemPath = itemPath + "/" + subresource
 | 
								suffix = "/" + subresource
 | 
				
			||||||
 | 
								itemPath = itemPath + suffix
 | 
				
			||||||
			resourcePath = itemPath
 | 
								resourcePath = itemPath
 | 
				
			||||||
			resourceParams = nameParams
 | 
								resourceParams = nameParams
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		apiResource.Name = path
 | 
							apiResource.Name = path
 | 
				
			||||||
		apiResource.Namespaced = false
 | 
							apiResource.Namespaced = false
 | 
				
			||||||
		apiResource.Kind = resourceKind
 | 
							apiResource.Kind = resourceKind
 | 
				
			||||||
		namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)}
 | 
							namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, resourcePath, "/"), suffix}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Handler for standard REST verbs (GET, PUT, POST and DELETE).
 | 
							// Handler for standard REST verbs (GET, PUT, POST and DELETE).
 | 
				
			||||||
		// Add actions at the resource path: /api/apiVersion/resource
 | 
							// Add actions at the resource path: /api/apiVersion/resource
 | 
				
			||||||
@@ -416,8 +420,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
		apiResource.Namespaced = true
 | 
							apiResource.Namespaced = true
 | 
				
			||||||
		apiResource.Kind = resourceKind
 | 
							apiResource.Kind = resourceKind
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		itemPathFn := func(name, namespace string) string {
 | 
							itemPathFn := func(name, namespace string) bytes.Buffer {
 | 
				
			||||||
			return itemPathPrefix + namespace + itemPathMiddle + name + itemPathSuffix
 | 
								var buf bytes.Buffer
 | 
				
			||||||
 | 
								buf.WriteString(itemPathPrefix)
 | 
				
			||||||
 | 
								buf.WriteString(url.QueryEscape(namespace))
 | 
				
			||||||
 | 
								buf.WriteString(itemPathMiddle)
 | 
				
			||||||
 | 
								buf.WriteString(url.QueryEscape(name))
 | 
				
			||||||
 | 
								buf.WriteString(itemPathSuffix)
 | 
				
			||||||
 | 
								return buf
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		namer := scopeNaming{scope, a.group.Linker, itemPathFn, false}
 | 
							namer := scopeNaming{scope, a.group.Linker, itemPathFn, false}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -747,7 +757,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
 | 
				
			|||||||
type rootScopeNaming struct {
 | 
					type rootScopeNaming struct {
 | 
				
			||||||
	scope meta.RESTScope
 | 
						scope meta.RESTScope
 | 
				
			||||||
	runtime.SelfLinker
 | 
						runtime.SelfLinker
 | 
				
			||||||
	itemPath string
 | 
						pathPrefix string
 | 
				
			||||||
 | 
						pathSuffix string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// rootScopeNaming implements ScopeNamer
 | 
					// rootScopeNaming implements ScopeNamer
 | 
				
			||||||
@@ -769,25 +780,26 @@ func (n rootScopeNaming) Name(req *restful.Request) (namespace, name string, err
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateLink returns the appropriate path and query to locate an object by its canonical path.
 | 
					// GenerateLink returns the appropriate path and query to locate an object by its canonical path.
 | 
				
			||||||
func (n rootScopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (path, query string, err error) {
 | 
					func (n rootScopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) {
 | 
				
			||||||
	_, name, err := n.ObjectName(obj)
 | 
						_, name, err := n.ObjectName(obj)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(name) == 0 {
 | 
						if len(name) == 0 {
 | 
				
			||||||
		_, name, err = n.Name(req)
 | 
							_, name, err = n.Name(req)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return "", "", err
 | 
								return "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	path = strings.Replace(n.itemPath, "{name}", name, 1)
 | 
						return n.pathPrefix + url.QueryEscape(name) + n.pathSuffix, nil
 | 
				
			||||||
	return path, "", nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateListLink returns the appropriate path and query to locate a list by its canonical path.
 | 
					// GenerateListLink returns the appropriate path and query to locate a list by its canonical path.
 | 
				
			||||||
func (n rootScopeNaming) GenerateListLink(req *restful.Request) (path, query string, err error) {
 | 
					func (n rootScopeNaming) GenerateListLink(req *restful.Request) (uri string, err error) {
 | 
				
			||||||
	path = req.Request.URL.Path
 | 
						if len(req.Request.URL.RawPath) > 0 {
 | 
				
			||||||
	return path, "", nil
 | 
							return req.Request.URL.RawPath, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return req.Request.URL.EscapedPath(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ObjectName returns the name set on the object, or an error if the
 | 
					// ObjectName returns the name set on the object, or an error if the
 | 
				
			||||||
@@ -809,7 +821,7 @@ func (n rootScopeNaming) ObjectName(obj runtime.Object) (namespace, name string,
 | 
				
			|||||||
type scopeNaming struct {
 | 
					type scopeNaming struct {
 | 
				
			||||||
	scope meta.RESTScope
 | 
						scope meta.RESTScope
 | 
				
			||||||
	runtime.SelfLinker
 | 
						runtime.SelfLinker
 | 
				
			||||||
	itemPathFn    func(name, namespace string) string
 | 
						itemPathFn    func(name, namespace string) bytes.Buffer
 | 
				
			||||||
	allNamespaces bool
 | 
						allNamespaces bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -842,28 +854,30 @@ func (n scopeNaming) Name(req *restful.Request) (namespace, name string, err err
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateLink returns the appropriate path and query to locate an object by its canonical path.
 | 
					// GenerateLink returns the appropriate path and query to locate an object by its canonical path.
 | 
				
			||||||
func (n scopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (path, query string, err error) {
 | 
					func (n scopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) {
 | 
				
			||||||
	namespace, name, err := n.ObjectName(obj)
 | 
						namespace, name, err := n.ObjectName(obj)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(namespace) == 0 && len(name) == 0 {
 | 
						if len(namespace) == 0 && len(name) == 0 {
 | 
				
			||||||
		namespace, name, err = n.Name(req)
 | 
							namespace, name, err = n.Name(req)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return "", "", err
 | 
								return "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(name) == 0 {
 | 
						if len(name) == 0 {
 | 
				
			||||||
		return "", "", errEmptyName
 | 
							return "", errEmptyName
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						result := n.itemPathFn(name, namespace)
 | 
				
			||||||
	return n.itemPathFn(name, namespace), "", nil
 | 
						return result.String(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateListLink returns the appropriate path and query to locate a list by its canonical path.
 | 
					// GenerateListLink returns the appropriate path and query to locate a list by its canonical path.
 | 
				
			||||||
func (n scopeNaming) GenerateListLink(req *restful.Request) (path, query string, err error) {
 | 
					func (n scopeNaming) GenerateListLink(req *restful.Request) (uri string, err error) {
 | 
				
			||||||
	path = req.Request.URL.Path
 | 
						if len(req.Request.URL.RawPath) > 0 {
 | 
				
			||||||
	return path, "", nil
 | 
							return req.Request.URL.RawPath, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return req.Request.URL.EscapedPath(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ObjectName returns the name and namespace set on the object, or an error if the
 | 
					// ObjectName returns the name and namespace set on the object, or an error if the
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ limitations under the License.
 | 
				
			|||||||
package apiserver
 | 
					package apiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
@@ -36,8 +37,8 @@ func TestScopeNamingGenerateLink(t *testing.T) {
 | 
				
			|||||||
	s := scopeNaming{
 | 
						s := scopeNaming{
 | 
				
			||||||
		meta.RESTScopeNamespace,
 | 
							meta.RESTScopeNamespace,
 | 
				
			||||||
		selfLinker,
 | 
							selfLinker,
 | 
				
			||||||
		func(name, namespace string) string {
 | 
							func(name, namespace string) bytes.Buffer {
 | 
				
			||||||
			return "/api/v1/namespaces/" + namespace + "/services/" + name
 | 
								return *bytes.NewBufferString("/api/v1/namespaces/" + namespace + "/services/" + name)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		true,
 | 
							true,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -50,7 +51,7 @@ func TestScopeNamingGenerateLink(t *testing.T) {
 | 
				
			|||||||
			Kind: "Service",
 | 
								Kind: "Service",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, _, err := s.GenerateLink(&restful.Request{}, service)
 | 
						_, err := s.GenerateLink(&restful.Request{}, service)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("Unexpected error %v", err)
 | 
							t.Errorf("Unexpected error %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,10 +60,11 @@ type ScopeNamer interface {
 | 
				
			|||||||
	// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
 | 
						// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
 | 
				
			||||||
	// does not support selfLinks.
 | 
						// does not support selfLinks.
 | 
				
			||||||
	SetSelfLink(obj runtime.Object, url string) error
 | 
						SetSelfLink(obj runtime.Object, url string) error
 | 
				
			||||||
	// GenerateLink creates a path and query for a given runtime object that represents the canonical path.
 | 
						// GenerateLink creates an encoded URI for a given runtime object that represents the canonical path
 | 
				
			||||||
	GenerateLink(req *restful.Request, obj runtime.Object) (path, query string, err error)
 | 
						// and query.
 | 
				
			||||||
	// GenerateLink creates a path and query for a list that represents the canonical path.
 | 
						GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error)
 | 
				
			||||||
	GenerateListLink(req *restful.Request) (path, query string, err error)
 | 
						// GenerateLink creates an encoded URI for a list that represents the canonical path and query.
 | 
				
			||||||
 | 
						GenerateListLink(req *restful.Request) (uri string, err error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RequestScope encapsulates common fields across all RESTful handler methods.
 | 
					// RequestScope encapsulates common fields across all RESTful handler methods.
 | 
				
			||||||
@@ -990,18 +991,12 @@ func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime
 | 
				
			|||||||
// plus the path and query generated by the provided linkFunc
 | 
					// plus the path and query generated by the provided linkFunc
 | 
				
			||||||
func setSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) error {
 | 
					func setSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) error {
 | 
				
			||||||
	// TODO: SelfLink generation should return a full URL?
 | 
						// TODO: SelfLink generation should return a full URL?
 | 
				
			||||||
	path, query, err := namer.GenerateLink(req, obj)
 | 
						uri, err := namer.GenerateLink(req, obj)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newURL := *req.Request.URL
 | 
						return namer.SetSelfLink(obj, uri)
 | 
				
			||||||
	// use only canonical paths
 | 
					 | 
				
			||||||
	newURL.Path = path
 | 
					 | 
				
			||||||
	newURL.RawQuery = query
 | 
					 | 
				
			||||||
	newURL.Fragment = ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return namer.SetSelfLink(obj, newURL.String())
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func hasUID(obj runtime.Object) (bool, error) {
 | 
					func hasUID(obj runtime.Object) (bool, error) {
 | 
				
			||||||
@@ -1045,31 +1040,20 @@ func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer)
 | 
				
			|||||||
		return 0, nil
 | 
							return 0, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: List SelfLink generation should return a full URL?
 | 
						uri, err := namer.GenerateListLink(req)
 | 
				
			||||||
	path, query, err := namer.GenerateListLink(req)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	newURL := *req.Request.URL
 | 
						if err := namer.SetSelfLink(obj, uri); err != nil {
 | 
				
			||||||
	newURL.Path = path
 | 
					 | 
				
			||||||
	newURL.RawQuery = query
 | 
					 | 
				
			||||||
	// use the path that got us here
 | 
					 | 
				
			||||||
	newURL.Fragment = ""
 | 
					 | 
				
			||||||
	if err := namer.SetSelfLink(obj, newURL.String()); err != nil {
 | 
					 | 
				
			||||||
		glog.V(4).Infof("Unable to set self link on object: %v", err)
 | 
							glog.V(4).Infof("Unable to set self link on object: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set self-link of objects in the list.
 | 
						count := 0
 | 
				
			||||||
	items, err := meta.ExtractList(obj)
 | 
						err = meta.EachListItem(obj, func(obj runtime.Object) error {
 | 
				
			||||||
	if err != nil {
 | 
							count++
 | 
				
			||||||
		return 0, err
 | 
							return setSelfLink(obj, req, namer)
 | 
				
			||||||
	}
 | 
						})
 | 
				
			||||||
	for i := range items {
 | 
						return count, err
 | 
				
			||||||
		if err := setSelfLink(items[i], req, namer); err != nil {
 | 
					 | 
				
			||||||
			return len(items), err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return len(items), meta.SetList(obj, items)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getPatchedJS(patchType api.PatchType, originalJS, patchJS []byte, obj runtime.Object) ([]byte, error) {
 | 
					func getPatchedJS(patchType api.PatchType, originalJS, patchJS []byte, obj runtime.Object) ([]byte, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,13 +134,13 @@ func (p *testNamer) SetSelfLink(obj runtime.Object, url string) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateLink creates a path and query for a given runtime object that represents the canonical path.
 | 
					// GenerateLink creates a path and query for a given runtime object that represents the canonical path.
 | 
				
			||||||
func (p *testNamer) GenerateLink(req *restful.Request, obj runtime.Object) (path, query string, err error) {
 | 
					func (p *testNamer) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) {
 | 
				
			||||||
	return "", "", errors.New("not implemented")
 | 
						return "", errors.New("not implemented")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateLink creates a path and query for a list that represents the canonical path.
 | 
					// GenerateLink creates a path and query for a list that represents the canonical path.
 | 
				
			||||||
func (p *testNamer) GenerateListLink(req *restful.Request) (path, query string, err error) {
 | 
					func (p *testNamer) GenerateListLink(req *restful.Request) (uri string, err error) {
 | 
				
			||||||
	return "", "", errors.New("not implemented")
 | 
						return "", errors.New("not implemented")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type patchTestCase struct {
 | 
					type patchTestCase struct {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user