Пример #1
0
func TestGetMultipleTypeObjectsAsList(t *testing.T) {
	pods, svc, _ := testData()

	f, tf, codec, _ := cmdtesting.NewAPIFactory()
	tf.Printer = &testPrinter{}
	tf.Client = &fake.RESTClient{
		NegotiatedSerializer: unstructuredSerializer,
		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
			switch req.URL.Path {
			case "/namespaces/test/pods":
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
			case "/namespaces/test/services":
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil
			default:
				t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
				return nil, nil
			}
		}),
	}
	tf.Namespace = "test"
	tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &registered.GroupOrDie(api.GroupName).GroupVersion}}
	buf := bytes.NewBuffer([]byte{})
	errBuf := bytes.NewBuffer([]byte{})

	cmd := NewCmdGet(f, buf, errBuf)
	cmd.SetOutput(buf)

	cmd.Flags().Set("output", "json")
	cmd.Run(cmd, []string{"pods,services"})

	if tf.Printer.(*testPrinter).Objects != nil {
		t.Errorf("unexpected print to default printer")
	}

	out, err := runtime.Decode(codec, buf.Bytes())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	list, err := meta.ExtractList(out)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
		t.Fatalf("unexpected error: %v", errs)
	}
	if err := meta.SetList(out, list); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	expected := &api.List{
		Items: []runtime.Object{
			&pods.Items[0],
			&pods.Items[1],
			&svc.Items[0],
		},
	}
	if !reflect.DeepEqual(expected, out) {
		t.Errorf("unexpected output: %#v", out)
	}
}
Пример #2
0
func (s *SortingPrinter) sortObj(obj runtime.Object) error {
	objs, err := meta.ExtractList(obj)
	if err != nil {
		return err
	}
	if len(objs) == 0 {
		return nil
	}

	sorter, err := SortObjects(s.Decoder, objs, s.SortField)
	if err != nil {
		return err
	}

	switch list := obj.(type) {
	case *v1.List:
		outputList := make([]runtime.RawExtension, len(objs))
		for ix := range objs {
			outputList[ix] = list.Items[sorter.OriginalPosition(ix)]
		}
		list.Items = outputList
		return nil
	}
	return meta.SetList(obj, objs)
}
Пример #3
0
// UsageStats calculates latest observed usage stats for all objects
func (g *GenericEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.UsageStats, error) {
	// default each tracked resource to zero
	result := quota.UsageStats{Used: api.ResourceList{}}
	for _, resourceName := range g.MatchedResourceNames {
		result.Used[resourceName] = resource.MustParse("0")
	}
	list, err := g.ListFuncByNamespace(options.Namespace, api.ListOptions{})
	if err != nil {
		return result, fmt.Errorf("%s: Failed to list %v: %v", g.Name, g.GroupKind(), err)
	}
	_, err = meta.Accessor(list)
	if err != nil {
		return result, fmt.Errorf("%s: Unable to understand list result %#v", g.Name, list)
	}
	items, err := meta.ExtractList(list)
	if err != nil {
		return result, fmt.Errorf("%s: Unable to understand list result %#v (%v)", g.Name, list, err)
	}
	for _, item := range items {
		// need to verify that the item matches the set of scopes
		matchesScopes := true
		for _, scope := range options.Scopes {
			if !g.MatchesScope(scope, item) {
				matchesScopes = false
			}
		}
		// only count usage if there was a match
		if matchesScopes {
			result.Used = quota.Add(result.Used, g.Usage(item))
		}
	}
	return result, nil
}
Пример #4
0
func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
	return v.Visitor.Visit(func(info *Info, err error) error {
		if err != nil {
			return err
		}
		if info.Object == nil {
			return fn(info, nil)
		}
		items, err := meta.ExtractList(info.Object)
		if err != nil {
			return fn(info, nil)
		}
		if errs := runtime.DecodeList(items, struct {
			runtime.ObjectTyper
			runtime.Decoder
		}{v.Mapper, info.Mapping.Codec}); len(errs) > 0 {
			return utilerrors.NewAggregate(errs)
		}
		for i := range items {
			item, err := v.InfoForObject(items[i])
			if err != nil {
				return err
			}
			if len(info.ResourceVersion) != 0 {
				item.ResourceVersion = info.ResourceVersion
			}
			if err := fn(item, nil); err != nil {
				return err
			}
		}
		return nil
	})
}
Пример #5
0
func (o objects) Add(obj runtime.Object) error {
	gvk, err := o.scheme.ObjectKind(obj)
	if err != nil {
		return err
	}
	kind := gvk.Kind

	switch {
	case meta.IsListType(obj):
		if kind != "List" {
			o.types[kind] = append(o.types[kind], obj)
		}

		list, err := meta.ExtractList(obj)
		if err != nil {
			return err
		}
		if errs := runtime.DecodeList(list, o.decoder); len(errs) > 0 {
			return errs[0]
		}
		for _, obj := range list {
			if err := o.Add(obj); err != nil {
				return err
			}
		}
	default:
		if status, ok := obj.(*unversioned.Status); ok && status.Details != nil {
			kind = status.Details.Kind
		}
		o.types[kind] = append(o.types[kind], obj)
	}

	return nil
}
Пример #6
0
// ReadObjectsFromPath reads objects from the specified file for testing.
func ReadObjectsFromPath(path, namespace string, decoder runtime.Decoder, typer runtime.ObjectTyper) ([]runtime.Object, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	data, err = yaml.ToJSON(data)
	if err != nil {
		return nil, err
	}
	obj, err := runtime.Decode(decoder, data)
	if err != nil {
		return nil, err
	}
	if !meta.IsListType(obj) {
		if err := setNamespace(typer, obj, namespace); err != nil {
			return nil, err
		}
		return []runtime.Object{obj}, nil
	}
	list, err := meta.ExtractList(obj)
	if err != nil {
		return nil, err
	}
	errs := runtime.DecodeList(list, decoder)
	if len(errs) > 0 {
		return nil, errs[0]
	}
	for _, o := range list {
		if err := setNamespace(typer, o, namespace); err != nil {
			return nil, err
		}
	}
	return list, nil
}
Пример #7
0
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags)
	headers := make([]string, len(s.Columns))
	for ix := range s.Columns {
		headers[ix] = s.Columns[ix].Header
	}
	fmt.Fprintln(w, strings.Join(headers, "\t"))
	parsers := make([]*jsonpath.JSONPath, len(s.Columns))
	for ix := range s.Columns {
		parsers[ix] = jsonpath.New(fmt.Sprintf("column%d", ix))
		if err := parsers[ix].Parse(s.Columns[ix].FieldSpec); err != nil {
			return err
		}
	}

	if meta.IsListType(obj) {
		objs, err := meta.ExtractList(obj)
		if err != nil {
			return err
		}
		for ix := range objs {
			if err := s.printOneObject(objs[ix], parsers, w); err != nil {
				return err
			}
		}
	} else {
		if err := s.printOneObject(obj, parsers, w); err != nil {
			return err
		}
	}
	return w.Flush()
}
Пример #8
0
// filterList filters any list object that conforms to the api conventions,
// provided that 'm' works with the concrete type of list. d is an optional
// decorator for the returned functions. Only matching items are decorated.
func filterList(list runtime.Object, m generic.Matcher, d decoratorFunc) (filtered runtime.Object, err error) {
	// TODO: push a matcher down into tools.etcdHelper to avoid all this
	// nonsense. This is a lot of unnecessary copies.
	items, err := meta.ExtractList(list)
	if err != nil {
		return nil, err
	}
	var filteredItems []runtime.Object
	for _, obj := range items {
		match, err := m.Matches(obj)
		if err != nil {
			return nil, err
		}
		if match {
			if d != nil {
				if err := d(obj); err != nil {
					return nil, err
				}
			}
			filteredItems = append(filteredItems, obj)
		}
	}
	err = meta.SetList(list, filteredItems)
	if err != nil {
		return nil, err
	}
	return list, nil
}
Пример #9
0
// setListSelfLink sets the self link of a list to the base URL, then sets the self links
// on all child objects returned.
func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) error {
	if !meta.IsListType(obj) {
		return nil
	}

	// TODO: List SelfLink generation should return a full URL?
	path, query, err := namer.GenerateListLink(req)
	if err != nil {
		return err
	}
	newURL := *req.Request.URL
	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)
	}

	// Set self-link of objects in the list.
	items, err := meta.ExtractList(obj)
	if err != nil {
		return err
	}
	for i := range items {
		if err := setSelfLink(items[i], req, namer); err != nil {
			return err
		}
	}
	return meta.SetList(obj, items)

}
Пример #10
0
func TestExtractListGenericV1(t *testing.T) {
	pl := &v1.List{
		Items: []runtime.RawExtension{
			{Raw: []byte("foo")},
			{Raw: []byte("bar")},
			{Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}},
		},
	}
	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].(*runtime.Unknown); !ok {
		t.Fatalf("Expected list[0] to be *runtime.Unknown, it is %#v", obj)
	}
	if obj, ok := list[1].(*runtime.Unknown); !ok {
		t.Fatalf("Expected list[1] to be *runtime.Unknown, it is %#v", obj)
	}
	if obj, ok := list[2].(*v1.Pod); !ok {
		t.Fatalf("Expected list[2] to be *runtime.Unknown, it is %#v", obj)
	}
}
Пример #11
0
// CreateList will properly create a list using the storage interface
func CreateList(prefix string, helper storage.Interface, list runtime.Object) error {
	items, err := meta.ExtractList(list)
	if err != nil {
		return err
	}
	err = CreateObjList(prefix, helper, items)
	if err != nil {
		return err
	}
	return meta.SetList(list, items)
}
Пример #12
0
func TestExtractListOfInterfacePtrs(t *testing.T) {
	pl := &fakePtrInterfaceList{
		Items: &[]runtime.Object{},
	}
	list, err := meta.ExtractList(pl)
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}
	if len(list) > 0 {
		t.Fatalf("Expected empty list, got %#v", list)
	}
}
Пример #13
0
func extractResourceList(objs []runtime.Object) ([]runtime.Object, error) {
	finalObjs := []runtime.Object{}
	for _, obj := range objs {
		items, err := meta.ExtractList(obj)
		if err != nil {
			return nil, err
		}
		for _, item := range items {
			finalObjs = append(finalObjs, item)
		}
	}
	return finalObjs, nil
}
Пример #14
0
func verifyListMetadata(t *testing.T, metaOnlyList *MetadataOnlyObjectList) {
	items, err := meta.ExtractList(metaOnlyList)
	if err != nil {
		t.Fatal(err)
	}
	for _, item := range items {
		metaOnly, ok := item.(*MetadataOnlyObject)
		if !ok {
			t.Fatalf("expected item to be *MetadataOnlyObject")
		}
		verfiyMetadata("check list", t, metaOnly)
	}
}
Пример #15
0
func validateObject(path string, obj runtime.Object, t *testing.T) {
	// if an object requires a namespace server side, be sure that it is filled in for validation
	if validation.HasObjectMeta(obj) {
		namespaceRequired, err := validation.GetRequiresNamespace(obj)
		if err != nil {
			t.Errorf("Expected no error, Got %v", err)
			return
		}

		if namespaceRequired {
			objectMeta, objectMetaErr := kapi.ObjectMetaFor(obj)
			if objectMetaErr != nil {
				t.Errorf("Expected no error, Got %v", objectMetaErr)
				return
			}

			objectMeta.Namespace = kapi.NamespaceDefault
		}
	}

	switch typedObj := obj.(type) {
	case *kapi.Pod:
		if errors := kvalidation.ValidatePod(typedObj); len(errors) > 0 {
			t.Errorf("%s did not validate correctly: %v", path, errors)
		}

	case *kapi.Service:
		if errors := kvalidation.ValidateService(typedObj); len(errors) > 0 {
			t.Errorf("%s did not validate correctly: %v", path, errors)
		}

	case *kapi.List, *imageapi.ImageStreamList:
		if list, err := meta.ExtractList(typedObj); err == nil {
			runtime.DecodeList(list, kapi.Codecs.UniversalDecoder())
			for i := range list {
				validateObject(path, list[i], t)
			}

		} else {
			t.Errorf("Expected no error, Got %v", err)

		}

	default:
		if errors := validation.Validator.Validate(obj); len(errors) > 0 {
			t.Errorf("%s with %v did not validate correctly: %v", path, reflect.TypeOf(obj), errors)
		}
	}

}
Пример #16
0
func (t *tracker) addList(obj runtime.Object, replaceExisting bool) error {
	list, err := meta.ExtractList(obj)
	if err != nil {
		return err
	}
	errs := runtime.DecodeList(list, t.decoder)
	if len(errs) > 0 {
		return errs[0]
	}
	for _, obj := range list {
		err := t.add(obj, replaceExisting)
		if err != nil {
			return err
		}
	}
	return nil
}
Пример #17
0
func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput bool) error {
	c, err := p.clientFunc(mapping)
	if err != nil {
		return err
	}

	objList, err := resource.NewHelper(c, mapping).List(namespace, mapping.GroupVersionKind.Version, p.selector, false)
	if err != nil {
		return err
	}
	objs, err := meta.ExtractList(objList)
	if err != nil {
		return err
	}

	for _, obj := range objs {
		annots, err := mapping.MetadataAccessor.Annotations(obj)
		if err != nil {
			return err
		}
		if _, ok := annots[annotations.LastAppliedConfigAnnotation]; !ok {
			// don't prune resources not created with apply
			continue
		}
		uid, err := mapping.UID(obj)
		if err != nil {
			return err
		}
		if p.visitedUids.Has(string(uid)) {
			continue
		}

		name, err := mapping.Name(obj)
		if err != nil {
			return err
		}
		if !p.dryRun {
			if err := p.delete(namespace, name, mapping, c); err != nil {
				return err
			}
		}
		cmdutil.PrintSuccess(p.mapper, shortOutput, p.out, mapping.Resource, name, p.dryRun, "pruned")
	}
	return nil
}
Пример #18
0
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)
		}
	}
}
Пример #19
0
// DeleteCollection remove all items returned by List with a given ListOptions from etcd.
//
// DeleteCollection is currently NOT atomic. It can happen that only subset of objects
// will be deleted from etcd, and then an error will be returned.
// In case of success, the list of deleted objects will be returned.
//
// TODO: Currently, there is no easy way to remove 'directory' entry from etcd (if we
// are removing all objects of a given type) with the current API (it's technically
// possibly with etcd API, but watch is not delivered correctly then).
// It will be possible to fix it with v3 etcd API.
func (e *Etcd) DeleteCollection(ctx api.Context, options *api.DeleteOptions, listOptions *unversioned.ListOptions) (runtime.Object, error) {
	listObj, err := e.List(ctx, listOptions)
	if err != nil {
		return nil, err
	}
	items, err := meta.ExtractList(listObj)
	if err != nil {
		return nil, err
	}
	for _, item := range items {
		accessor, err := meta.Accessor(item)
		if err != nil {
			return nil, err
		}
		if _, err := e.Delete(ctx, accessor.GetName(), options); err != nil {
			return nil, err
		}
	}
	return listObj, nil
}
Пример #20
0
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)
	}
}
Пример #21
0
func TestExtractList(t *testing.T) {
	pl := &api.PodList{
		Items: []api.Pod{
			{ObjectMeta: api.ObjectMeta{Name: "1"}},
			{ObjectMeta: api.ObjectMeta{Name: "2"}},
			{ObjectMeta: api.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].(*api.Pod).Name, pl.Items[i].Name; e != a {
			t.Fatalf("Expected %v, got %v", e, a)
		}
	}
}
Пример #22
0
func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
	return v.Visitor.Visit(func(info *Info, err error) error {
		if err != nil {
			return err
		}
		if info.Object == nil {
			return fn(info, nil)
		}
		items, err := meta.ExtractList(info.Object)
		if err != nil {
			return fn(info, nil)
		}
		if errs := runtime.DecodeList(items, struct {
			runtime.ObjectTyper
			runtime.Decoder
		}{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 {
			return utilerrors.NewAggregate(errs)
		}

		// If we have a GroupVersionKind on the list, prioritize that when asking for info on the objects contained in the list
		var preferredGVKs []unversioned.GroupVersionKind
		if info.Mapping != nil && !info.Mapping.GroupVersionKind.Empty() {
			preferredGVKs = append(preferredGVKs, info.Mapping.GroupVersionKind)
		}

		for i := range items {
			item, err := v.InfoForObject(items[i], preferredGVKs)
			if err != nil {
				return err
			}
			if len(info.ResourceVersion) != 0 {
				item.ResourceVersion = info.ResourceVersion
			}
			if err := fn(item, nil); err != nil {
				return err
			}
		}
		return nil
	})
}
Пример #23
0
func TestSetExtractListRoundTrip(t *testing.T) {
	fuzzer := fuzz.New().NilChance(0).NumElements(1, 5)
	for i := 0; i < 5; i++ {
		start := &api.PodList{}
		fuzzer.Fuzz(&start.Items)

		list, err := meta.ExtractList(start)
		if err != nil {
			t.Errorf("Unexpected error %v", err)
			continue
		}
		got := &api.PodList{}
		err = meta.SetList(got, list)
		if err != nil {
			t.Errorf("Unexpected error %v", err)
			continue
		}
		if e, a := start, got; !reflect.DeepEqual(e, a) {
			t.Fatalf("Expected %#v, got %#v", e, a)
		}
	}
}
Пример #24
0
// FilterResourceList receives a list of runtime objects.
// If any objects are filtered, that number is returned along with a modified list.
func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *kubectl.PrintOptions) (int, []runtime.Object, error) {
	items, err := meta.ExtractList(obj)
	if err != nil {
		return 0, []runtime.Object{obj}, utilerrors.NewAggregate([]error{err})
	}
	if errs := runtime.DecodeList(items, api.Codecs.UniversalDecoder(), runtime.UnstructuredJSONScheme); len(errs) > 0 {
		return 0, []runtime.Object{obj}, utilerrors.NewAggregate(errs)
	}

	filterCount := 0
	list := make([]runtime.Object, 0, len(items))
	for _, obj := range items {
		if isFiltered, err := filterFuncs.Filter(obj, filterOpts); !isFiltered {
			if err != nil {
				glog.V(2).Infof("Unable to filter resource: %v", err)
				continue
			}
			list = append(list, obj)
		} else if isFiltered {
			filterCount++
		}
	}
	return filterCount, list, nil
}
Пример #25
0
// DeleteCollection remove all items returned by List with a given ListOptions from storage.
//
// DeleteCollection is currently NOT atomic. It can happen that only subset of objects
// will be deleted from storage, and then an error will be returned.
// In case of success, the list of deleted objects will be returned.
//
// TODO: Currently, there is no easy way to remove 'directory' entry from storage (if we
// are removing all objects of a given type) with the current API (it's technically
// possibly with storage API, but watch is not delivered correctly then).
// It will be possible to fix it with v3 etcd API.
func (e *Store) DeleteCollection(ctx api.Context, options *api.DeleteOptions, listOptions *api.ListOptions) (runtime.Object, error) {
	listObj, err := e.List(ctx, listOptions)
	if err != nil {
		return nil, err
	}
	items, err := meta.ExtractList(listObj)
	if err != nil {
		return nil, err
	}
	// Spawn a number of goroutines, so that we can issue requests to storage
	// in parallel to speed up deletion.
	// TODO: Make this proportional to the number of items to delete, up to
	// DeleteCollectionWorkers (it doesn't make much sense to spawn 16
	// workers to delete 10 items).
	workersNumber := e.DeleteCollectionWorkers
	if workersNumber < 1 {
		workersNumber = 1
	}
	wg := sync.WaitGroup{}
	toProcess := make(chan int, 2*workersNumber)
	errs := make(chan error, workersNumber+1)

	go func() {
		defer utilruntime.HandleCrash(func(panicReason interface{}) {
			errs <- fmt.Errorf("DeleteCollection distributor panicked: %v", panicReason)
		})
		for i := 0; i < len(items); i++ {
			toProcess <- i
		}
		close(toProcess)
	}()

	wg.Add(workersNumber)
	for i := 0; i < workersNumber; i++ {
		go func() {
			// panics don't cross goroutine boundaries
			defer utilruntime.HandleCrash(func(panicReason interface{}) {
				errs <- fmt.Errorf("DeleteCollection goroutine panicked: %v", panicReason)
			})
			defer wg.Done()

			for {
				index, ok := <-toProcess
				if !ok {
					return
				}
				accessor, err := meta.Accessor(items[index])
				if err != nil {
					errs <- err
					return
				}
				if _, err := e.Delete(ctx, accessor.GetName(), options); err != nil && !kubeerr.IsNotFound(err) {
					glog.V(4).Infof("Delete %s in DeleteCollection failed: %v", accessor.GetName(), err)
					errs <- err
					return
				}
			}
		}()
	}
	wg.Wait()
	select {
	case err := <-errs:
		return nil, err
	default:
		return listObj, nil
	}
}
Пример #26
0
func TestArrayOfRuntimeObject(t *testing.T) {
	internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
	externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}

	s := runtime.NewScheme()
	s.AddKnownTypes(internalGV, &EmbeddedTest{})
	s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{})
	s.AddKnownTypes(internalGV, &ObjectTest{})
	s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{})

	codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)

	innerItems := []runtime.Object{
		&EmbeddedTest{ID: "baz"},
	}
	items := []runtime.Object{
		&EmbeddedTest{ID: "foo"},
		&EmbeddedTest{ID: "bar"},
		// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
		&runtime.Unknown{
			Raw:         []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`),
			ContentType: runtime.ContentTypeJSON,
		},
		&ObjectTest{
			Items: runtime.NewEncodableList(codec, innerItems),
		},
	}
	internal := &ObjectTest{
		Items: runtime.NewEncodableList(codec, items),
	}
	wire, err := runtime.Encode(codec, internal)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	t.Logf("Wire format is:\n%s\n", string(wire))

	obj := &ObjectTestExternal{}
	if err := json.Unmarshal(wire, obj); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	t.Logf("exact wire is: %s", string(obj.Items[0].Raw))

	items[3] = &ObjectTest{Items: innerItems}
	internal.Items = items

	decoded, err := runtime.Decode(codec, wire)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	list, err := meta.ExtractList(decoded)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
		t.Fatalf("unexpected error: %v", errs)
	}

	list2, err := meta.ExtractList(list[3])
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if errs := runtime.DecodeList(list2, codec); len(errs) > 0 {
		t.Fatalf("unexpected error: %v", errs)
	}
	if err := meta.SetList(list[3], list2); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// we want DecodeList to set type meta if possible, even on runtime.Unknown objects
	internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"}
	if e, a := internal.Items, list; !reflect.DeepEqual(e, a) {
		t.Errorf("mismatched decoded: %s", diff.ObjectGoPrintSideBySide(e, a))
	}
}
Пример #27
0
// ListAndWatch first lists all items and get the resource version at the moment of call,
// and then use the resource version to watch.
// It returns error if ListAndWatch didn't even try to initialize watch.
func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
	glog.V(3).Infof("Listing and watching %v from %s", r.expectedType, r.name)
	var resourceVersion string
	resyncCh, cleanup := r.resyncChan()
	defer cleanup()

	// Explicitly set "0" as resource version - it's fine for the List()
	// to be served from cache and potentially be delayed relative to
	// etcd contents. Reflector framework will catch up via Watch() eventually.
	options := api.ListOptions{ResourceVersion: "0"}
	list, err := r.listerWatcher.List(options)
	if err != nil {
		return fmt.Errorf("%s: Failed to list %v: %v", r.name, r.expectedType, err)
	}
	listMetaInterface, err := meta.ListAccessor(list)
	if err != nil {
		return fmt.Errorf("%s: Unable to understand list result %#v: %v", r.name, list, err)
	}
	resourceVersion = listMetaInterface.GetResourceVersion()
	items, err := meta.ExtractList(list)
	if err != nil {
		return fmt.Errorf("%s: Unable to understand list result %#v (%v)", r.name, list, err)
	}
	if err := r.syncWith(items, resourceVersion); err != nil {
		return fmt.Errorf("%s: Unable to sync list result: %v", r.name, err)
	}
	r.setLastSyncResourceVersion(resourceVersion)

	for {
		options := api.ListOptions{
			ResourceVersion: resourceVersion,
			// We want to avoid situations when resyncing is breaking the TCP connection
			// - see comment for 'timeoutForWatch()' for more details.
			TimeoutSeconds: r.timeoutForWatch(),
		}
		w, err := r.listerWatcher.Watch(options)
		if err != nil {
			switch err {
			case io.EOF:
				// watch closed normally
			case io.ErrUnexpectedEOF:
				glog.V(1).Infof("%s: Watch for %v closed with unexpected EOF: %v", r.name, r.expectedType, err)
			default:
				utilruntime.HandleError(fmt.Errorf("%s: Failed to watch %v: %v", r.name, r.expectedType, err))
			}
			// If this is "connection refused" error, it means that most likely apiserver is not responsive.
			// It doesn't make sense to re-list all objects because most likely we will be able to restart
			// watch where we ended.
			// If that's the case wait and resend watch request.
			if urlError, ok := err.(*url.Error); ok {
				if opError, ok := urlError.Err.(*net.OpError); ok {
					if errno, ok := opError.Err.(syscall.Errno); ok && errno == syscall.ECONNREFUSED {
						time.Sleep(time.Second)
						continue
					}
				}
			}
			return nil
		}
		if err := r.watchHandler(w, &resourceVersion, resyncCh, stopCh); err != nil {
			if err != errorResyncRequested && err != errorStopRequested {
				glog.Warningf("%s: watch of %v ended with: %v", r.name, r.expectedType, err)
			}
			return nil
		}
		if r.canForceResyncNow() {
			glog.V(4).Infof("%s: next resync planned for %#v, forcing now", r.name, r.nextResync)
			return nil
		}
	}
}
Пример #28
0
func RunEdit(f *cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *EditOptions) error {
	var printer kubectl.ResourcePrinter
	var ext string
	switch format := cmdutil.GetFlagString(cmd, "output"); format {
	case "json":
		printer = &kubectl.JSONPrinter{}
		ext = ".json"
	case "yaml":
		printer = &kubectl.YAMLPrinter{}
		ext = ".yaml"
	default:
		return cmdutil.UsageError(cmd, "The flag 'output' must be one of yaml|json")
	}

	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
	resourceMapper := &resource.Mapper{
		ObjectTyper:  typer,
		RESTMapper:   mapper,
		ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),

		// NB: we use `f.Decoder(false)` to get a plain deserializer for
		// the resourceMapper, since it's used to read in edits and
		// we don't want to convert into the internal version when
		// reading in edits (this would cause us to potentially try to
		// compare two different GroupVersions).
		Decoder: f.Decoder(false),
	}

	r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options.Recursive, options.Filenames...).
		ResourceTypeOrNameArgs(true, args...).
		Flatten().
		Latest().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	infos, err := r.Infos()
	if err != nil {
		return err
	}

	clientConfig, err := f.ClientConfig()
	if err != nil {
		return err
	}

	encoder := f.JSONEncoder()
	defaultVersion, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion)
	if err != nil {
		return err
	}
	originalObj, err := resource.AsVersionedObject(infos, false, defaultVersion, encoder)
	if err != nil {
		return err
	}

	var (
		windowsLineEndings = cmdutil.GetFlagBool(cmd, "windows-line-endings")
		edit               = editor.NewDefaultEditor(f.EditorEnvs())
		results            = editResults{}
		original           = []byte{}
		edited             = []byte{}
		file               string
	)

	containsError := false

	for {
		// infos mutates over time to be the list of things we've tried and failed to edit
		// this means that our overall list changes over time.
		objToEdit, err := resource.AsVersionedObject(infos, false, defaultVersion, encoder)
		if err != nil {
			return err
		}

		// generate the file to edit
		buf := &bytes.Buffer{}
		var w io.Writer = buf
		if windowsLineEndings {
			w = crlf.NewCRLFWriter(w)
		}
		if err := results.header.writeTo(w); err != nil {
			return preservedFile(err, results.file, errOut)
		}
		if !containsError {
			if err := printer.PrintObj(objToEdit, w); err != nil {
				return preservedFile(err, results.file, errOut)
			}
			original = buf.Bytes()
		} else {
			// In case of an error, preserve the edited file.
			// Remove the comments (header) from it since we already
			// have included the latest header in the buffer above.
			buf.Write(manualStrip(edited))
		}

		// launch the editor
		editedDiff := edited
		edited, file, err = edit.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), ext, buf)
		if err != nil {
			return preservedFile(err, results.file, errOut)
		}
		if bytes.Equal(stripComments(editedDiff), stripComments(edited)) {
			// Ugly hack right here. We will hit this either (1) when we try to
			// save the same changes we tried to save in the previous iteration
			// which means our changes are invalid or (2) when we exit the second
			// time. The second case is more usual so we can probably live with it.
			// TODO: A less hacky fix would be welcome :)
			fmt.Fprintln(errOut, "Edit cancelled, no valid changes were saved.")
			return nil
		}

		// cleanup any file from the previous pass
		if len(results.file) > 0 {
			os.Remove(results.file)
		}
		glog.V(4).Infof("User edited:\n%s", string(edited))

		// Compare content without comments
		if bytes.Equal(stripComments(original), stripComments(edited)) {
			os.Remove(file)
			fmt.Fprintln(errOut, "Edit cancelled, no changes made.")
			return nil
		}
		lines, err := hasLines(bytes.NewBuffer(edited))
		if err != nil {
			return preservedFile(err, file, errOut)
		}
		if !lines {
			os.Remove(file)
			fmt.Fprintln(errOut, "Edit cancelled, saved file was empty.")
			return nil
		}

		results = editResults{
			file: file,
		}

		// parse the edited file
		updates, err := resourceMapper.InfoForData(edited, "edited-file")
		if err != nil {
			// syntax error
			containsError = true
			results.header.reasons = append(results.header.reasons, editReason{head: fmt.Sprintf("The edited file had a syntax error: %v", err)})
			continue
		}
		// not a syntax error as it turns out...
		containsError = false

		namespaceVisitor := resource.NewFlattenListVisitor(updates, resourceMapper)
		// need to make sure the original namespace wasn't changed while editing
		if err = namespaceVisitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil {
			return preservedFile(err, file, errOut)
		}

		mutatedObjects := []runtime.Object{}
		annotationVisitor := resource.NewFlattenListVisitor(updates, resourceMapper)
		// iterate through all items to apply annotations
		if err = annotationVisitor.Visit(func(info *resource.Info, incomingErr error) error {
			// put configuration annotation in "updates"
			if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, encoder); err != nil {
				return err
			}
			if cmdutil.ShouldRecord(cmd, info) {
				if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil {
					return err
				}
			}
			mutatedObjects = append(mutatedObjects, info.Object)

			return nil

		}); err != nil {
			return preservedFile(err, file, errOut)
		}

		// if we mutated a list in the visitor, persist the changes on the overall object
		if meta.IsListType(updates.Object) {
			meta.SetList(updates.Object, mutatedObjects)
		}

		patchVisitor := resource.NewFlattenListVisitor(updates, resourceMapper)
		err = patchVisitor.Visit(func(info *resource.Info, incomingErr error) error {
			currOriginalObj := originalObj

			// if we're editing a list, then navigate the list to find the item that we're currently trying to edit
			if meta.IsListType(originalObj) {
				currOriginalObj = nil
				editObjUID, err := meta.NewAccessor().UID(info.Object)
				if err != nil {
					return err
				}

				listItems, err := meta.ExtractList(originalObj)
				if err != nil {
					return err
				}

				// iterate through the list to find the item with the matching UID
				for i := range listItems {
					originalObjUID, err := meta.NewAccessor().UID(listItems[i])
					if err != nil {
						return err
					}
					if editObjUID == originalObjUID {
						currOriginalObj = listItems[i]
						break
					}
				}
				if currOriginalObj == nil {
					return fmt.Errorf("no original object found for %#v", info.Object)
				}

			}

			originalSerialization, err := runtime.Encode(encoder, currOriginalObj)
			if err != nil {
				return err
			}
			editedSerialization, err := runtime.Encode(encoder, info.Object)
			if err != nil {
				return err
			}

			// compute the patch on a per-item basis
			// use strategic merge to create a patch
			originalJS, err := yaml.ToJSON(originalSerialization)
			if err != nil {
				return err
			}
			editedJS, err := yaml.ToJSON(editedSerialization)
			if err != nil {
				return err
			}

			if reflect.DeepEqual(originalJS, editedJS) {
				// no edit, so just skip it.
				cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "skipped")
				return nil
			}

			patch, err := strategicpatch.CreateStrategicMergePatch(originalJS, editedJS, currOriginalObj)
			// TODO: change all jsonmerge to strategicpatch
			// for checking preconditions
			preconditions := []jsonmerge.PreconditionFunc{}
			if err != nil {
				glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err)
				return err
			} else {
				preconditions = append(preconditions, jsonmerge.RequireKeyUnchanged("apiVersion"))
				preconditions = append(preconditions, jsonmerge.RequireKeyUnchanged("kind"))
				preconditions = append(preconditions, jsonmerge.RequireMetadataKeyUnchanged("name"))
				results.version = defaultVersion
			}

			if hold, msg := jsonmerge.TestPreconditionsHold(patch, preconditions); !hold {
				fmt.Fprintf(errOut, "error: %s", msg)
				return preservedFile(nil, file, errOut)
			}

			patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch)
			if err != nil {
				fmt.Fprintln(out, results.addError(err, info))
				return nil
			}
			info.Refresh(patched, true)
			cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "edited")
			return nil
		})
		if err != nil {
			return preservedFile(err, results.file, errOut)
		}

		// Handle all possible errors
		//
		// 1. retryable: propose kubectl replace -f
		// 2. notfound: indicate the location of the saved configuration of the deleted resource
		// 3. invalid: retry those on the spot by looping ie. reloading the editor
		if results.retryable > 0 {
			fmt.Fprintf(errOut, "You can run `%s replace -f %s` to try this update again.\n", filepath.Base(os.Args[0]), file)
			return errExit
		}
		if results.notfound > 0 {
			fmt.Fprintf(errOut, "The edits you made on deleted resources have been saved to %q\n", file)
			return errExit
		}

		if len(results.edit) == 0 {
			if results.notfound == 0 {
				os.Remove(file)
			} else {
				fmt.Fprintf(out, "The edits you made on deleted resources have been saved to %q\n", file)
			}
			return nil
		}

		// loop again and edit the remaining items
		infos = results.edit
	}
}
Пример #29
0
// ListAndWatch first lists all items and get the resource version at the moment of call,
// and then use the resource version to watch.
// It returns error if ListAndWatch didn't even try to initialize watch.
func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
	glog.V(3).Infof("Listing and watching %v from %s", r.expectedType, r.name)
	var resourceVersion string
	resyncCh, cleanup := r.resyncChan()
	defer cleanup()

	// Explicitly set "0" as resource version - it's fine for the List()
	// to be served from cache and potentially be delayed relative to
	// etcd contents. Reflector framework will catch up via Watch() eventually.
	options := api.ListOptions{ResourceVersion: "0"}
	list, err := r.listerWatcher.List(options)
	if err != nil {
		return fmt.Errorf("%s: Failed to list %v: %v", r.name, r.expectedType, err)
	}
	listMetaInterface, err := meta.ListAccessor(list)
	if err != nil {
		return fmt.Errorf("%s: Unable to understand list result %#v: %v", r.name, list, err)
	}
	resourceVersion = listMetaInterface.GetResourceVersion()
	items, err := meta.ExtractList(list)
	if err != nil {
		return fmt.Errorf("%s: Unable to understand list result %#v (%v)", r.name, list, err)
	}
	if err := r.syncWith(items, resourceVersion); err != nil {
		return fmt.Errorf("%s: Unable to sync list result: %v", r.name, err)
	}
	r.setLastSyncResourceVersion(resourceVersion)

	resyncerrc := make(chan error, 1)
	go func() {
		for {
			select {
			case <-resyncCh:
			case <-stopCh:
				return
			}
			glog.V(4).Infof("%s: forcing resync", r.name)
			if err := r.store.Resync(); err != nil {
				resyncerrc <- err
				return
			}
			cleanup()
			resyncCh, cleanup = r.resyncChan()
		}
	}()

	for {
		timemoutseconds := int64(minWatchTimeout.Seconds() * (rand.Float64() + 1.0))
		options = api.ListOptions{
			ResourceVersion: resourceVersion,
			// We want to avoid situations of hanging watchers. Stop any wachers that do not
			// receive any events within the timeout window.
			TimeoutSeconds: &timemoutseconds,
		}

		w, err := r.listerWatcher.Watch(options)
		if err != nil {
			switch err {
			case io.EOF:
				// watch closed normally
			case io.ErrUnexpectedEOF:
				glog.V(1).Infof("%s: Watch for %v closed with unexpected EOF: %v", r.name, r.expectedType, err)
			default:
				utilruntime.HandleError(fmt.Errorf("%s: Failed to watch %v: %v", r.name, r.expectedType, err))
			}
			// If this is "connection refused" error, it means that most likely apiserver is not responsive.
			// It doesn't make sense to re-list all objects because most likely we will be able to restart
			// watch where we ended.
			// If that's the case wait and resend watch request.
			if urlError, ok := err.(*url.Error); ok {
				if opError, ok := urlError.Err.(*net.OpError); ok {
					if errno, ok := opError.Err.(syscall.Errno); ok && errno == syscall.ECONNREFUSED {
						time.Sleep(time.Second)
						continue
					}
				}
			}
			return nil
		}

		if err := r.watchHandler(w, &resourceVersion, resyncerrc, stopCh); err != nil {
			if err != errorStopRequested {
				glog.Warningf("%s: watch of %v ended with: %v", r.name, r.expectedType, err)
			}
			return nil
		}
	}
}
Пример #30
0
// TODO: check for watch expired error and retry watch from latest point?  Same issue exists for Until.
func ListWatchUntil(timeout time.Duration, lw ListerWatcher, conditions ...watch.ConditionFunc) (*watch.Event, error) {
	if len(conditions) == 0 {
		return nil, nil
	}

	list, err := lw.List(v1.ListOptions{})
	if err != nil {
		return nil, err
	}
	initialItems, err := meta.ExtractList(list)
	if err != nil {
		return nil, err
	}

	// use the initial items as simulated "adds"
	var lastEvent *watch.Event
	currIndex := 0
	passedConditions := 0
	for _, condition := range conditions {
		// check the next condition against the previous event and short circuit waiting for the next watch
		if lastEvent != nil {
			done, err := condition(*lastEvent)
			if err != nil {
				return lastEvent, err
			}
			if done {
				passedConditions = passedConditions + 1
				continue
			}
		}

	ConditionSucceeded:
		for currIndex < len(initialItems) {
			lastEvent = &watch.Event{Type: watch.Added, Object: initialItems[currIndex]}
			currIndex++

			done, err := condition(*lastEvent)
			if err != nil {
				return lastEvent, err
			}
			if done {
				passedConditions = passedConditions + 1
				break ConditionSucceeded
			}
		}
	}
	if passedConditions == len(conditions) {
		return lastEvent, nil
	}
	remainingConditions := conditions[passedConditions:]

	metaObj, err := meta.ListAccessor(list)
	if err != nil {
		return nil, err
	}
	currResourceVersion := metaObj.GetResourceVersion()

	watchInterface, err := lw.Watch(v1.ListOptions{ResourceVersion: currResourceVersion})
	if err != nil {
		return nil, err
	}

	return watch.Until(timeout, watchInterface, remainingConditions...)
}