// 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 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 := runtime.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 = runtime.SetList(list, filteredItems) if err != nil { return nil, err } return list, nil }
// 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 !runtime.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 := runtime.ExtractList(obj) if err != nil { return err } for i := range items { if err := setSelfLink(items[i], req, namer); err != nil { return err } } return runtime.SetList(obj, items) }
func (r *Reflector) listAndWatch(stopCh <-chan struct{}) { var resourceVersion string resyncCh, cleanup := r.resyncChan() defer cleanup() list, err := r.listerWatcher.List() if err != nil { util.HandleError(fmt.Errorf("%s: Failed to list %v: %v", r.name, r.expectedType, err)) return } meta, err := meta.Accessor(list) if err != nil { util.HandleError(fmt.Errorf("%s: Unable to understand list result %#v", r.name, list)) return } resourceVersion = meta.ResourceVersion() items, err := runtime.ExtractList(list) if err != nil { util.HandleError(fmt.Errorf("%s: Unable to understand list result %#v (%v)", r.name, list, err)) return } if err := r.syncWith(items); err != nil { util.HandleError(fmt.Errorf("%s: Unable to sync list result: %v", r.name, err)) return } r.setLastSyncResourceVersion(resourceVersion) for { w, err := r.listerWatcher.Watch(resourceVersion) 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: util.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 } 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 } } }
func (s *SortingPrinter) sortObj(obj runtime.Object) error { objs, err := runtime.ExtractList(obj) if err != nil { return err } if len(objs) == 0 { return nil } parser := jsonpath.New("sorting") parser.Parse(s.SortField) values, err := parser.FindResults(reflect.ValueOf(objs[0]).Elem().Interface()) if err != nil { return err } if len(values) == 0 { return fmt.Errorf("couldn't find any field with path: %s", s.SortField) } sorter := &RuntimeSort{ field: s.SortField, objs: objs, } sort.Sort(sorter) runtime.SetList(obj, sorter.objs) return nil }
func (s *SortingPrinter) sortObj(obj runtime.Object) error { objs, err := runtime.ExtractList(obj) if err != nil { return err } if len(objs) == 0 { return nil } sorter, err := SortObjects(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 runtime.SetList(obj, objs) }
func (o objects) Add(obj runtime.Object) error { _, kind, err := o.scheme.ObjectVersionAndKind(obj) if err != nil { return err } switch { case runtime.IsListType(obj): if kind != "List" { o.types[kind] = append(o.types[kind], obj) } list, err := runtime.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.(*api.Status); ok && status.Details != nil { kind = status.Details.Kind } o.types[kind] = append(o.types[kind], obj) } return nil }
func filterListInTenant(obj runtime.Object, tenant string, kind string, namer ScopeNamer) error { var ( result = []runtime.Object{} ) if !runtime.IsListType(obj) { return nil } items, err := runtime.ExtractList(obj) if err != nil { return err } for i := range items { if name, err := namer.ObjectTenant(items[i]); err == nil { if tenant == name { result = append(result, items[i]) continue } if name == "" { result = append(result, items[i]) } } } return runtime.SetList(obj, result) }
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 runtime.IsListType(obj) { objs, err := runtime.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() }
func (v FlattenListVisitor) Visit(fn VisitorFunc) error { return v.Visitor.Visit(func(info *Info) error { if info.Object == nil { return fn(info) } items, err := runtime.ExtractList(info.Object) if err != nil { return fn(info) } if errs := runtime.DecodeList(items, struct { runtime.ObjectTyper runtime.Decoder }{v.Mapper, info.Mapping.Codec}); len(errs) > 0 { return errors.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); err != nil { return err } } return nil }) }
func TestGetMultipleTypeObjectsAsList(t *testing.T) { pods, svc, _ := testData() f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { case "/namespaces/test/pods": return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil case "/namespaces/test/services": return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" tf.ClientConfig = &client.Config{Version: testapi.Version()} buf := bytes.NewBuffer([]byte{}) cmd := NewCmdGet(f, buf) 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 := codec.Decode(buf.Bytes()) if err != nil { t.Fatalf("unexpected error: %v", err) } list, err := runtime.ExtractList(out) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list, api.Scheme); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := runtime.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) } }
// CreateList will properly create a list using the storage interface func CreateList(prefix string, helper storage.Interface, list runtime.Object) error { items, err := runtime.ExtractList(list) if err != nil { return err } err = CreateObjList(prefix, helper, items) if err != nil { return err } return runtime.SetList(list, items) }
func TestExtractListOfInterfacePtrs(t *testing.T) { pl := &fakePtrInterfaceList{ Items: &[]runtime.Object{}, } list, err := runtime.ExtractList(pl) if err != nil { t.Fatalf("Unexpected error %v", err) } if len(list) > 0 { t.Fatalf("Expected empty list, got %#v", list) } }
func extractResourceList(objs []runtime.Object) ([]runtime.Object, error) { finalObjs := []runtime.Object{} for _, obj := range objs { items, err := runtime.ExtractList(obj) if err != nil { return nil, err } for _, item := range items { finalObjs = append(finalObjs, item) } } return finalObjs, nil }
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, err := kapi.ObjectMetaFor(obj) if err != nil { t.Errorf("Expected no error, Got %v", err) 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 := runtime.ExtractList(typedObj); err == nil { runtime.DecodeList(list, kapi.Scheme) 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) } } }
// CreateList will properly create a list using the storage interface func CreateList(t *testing.T, prefix string, helper storage.Interface, list runtime.Object) error { items, err := runtime.ExtractList(list) if err != nil { return err } for i := range items { obj := items[i] meta, err := meta.Accessor(obj) if err != nil { return err } err = CreateObj(t, helper, path.Join(prefix, meta.Name()), obj, obj, 0) if err != nil { return err } items[i] = obj } return runtime.SetList(list, items) }
func TestExtractListOfValuePtrs(t *testing.T) { pl := &fakePtrValueList{ Items: []*api.Pod{ {ObjectMeta: api.ObjectMeta{Name: "1"}}, {ObjectMeta: api.ObjectMeta{Name: "2"}}, }, } list, err := runtime.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 TestExtractListGenericV1(t *testing.T) { pl := &v1.List{ Items: []runtime.RawExtension{ {RawJSON: []byte("foo")}, {RawJSON: []byte("bar")}, }, } list, err := runtime.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) } }
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 := runtime.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) } } }
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 := runtime.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 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 := runtime.ExtractList(start) if err != nil { t.Errorf("Unexpected error %v", err) continue } got := &api.PodList{} err = runtime.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) } } }
func (s *SortingPrinter) sortObj(obj runtime.Object) error { objs, err := runtime.ExtractList(obj) if err != nil { return err } if len(objs) == 0 { return nil } parser := jsonpath.New("sorting") parser.Parse(s.SortField) for ix := range objs { item := objs[ix] switch u := item.(type) { case *runtime.Unknown: var err error if objs[ix], err = api.Codec.Decode(u.RawJSON); err != nil { return err } } } values, err := parser.FindResults(reflect.ValueOf(objs[0]).Elem().Interface()) if err != nil { return err } if len(values) == 0 { return fmt.Errorf("couldn't find any field with path: %s", s.SortField) } sorter := &RuntimeSort{ field: s.SortField, objs: objs, } sort.Sort(sorter) runtime.SetList(obj, sorter.objs) return nil }
func TestArrayOfRuntimeObject(t *testing.T) { s := runtime.NewScheme() s.AddKnownTypes("", &EmbeddedTest{}) s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) s.AddKnownTypes("", &ObjectTest{}) s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{}) internal := &ObjectTest{ 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{RawJSON: []byte(`{"apiVersion":"unknown","foo":"bar","kind":"OtherTest"}`)}, &ObjectTest{ Items: []runtime.Object{ &EmbeddedTest{ID: "baz"}, }, }, }, } wire, err := s.EncodeToVersion(internal, "v1test") 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].RawJSON)) decoded, err := s.Decode(wire) if err != nil { t.Fatalf("unexpected error: %v", err) } list, err := runtime.ExtractList(decoded) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list, s); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } list2, err := runtime.ExtractList(list[3]) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list2, s); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := runtime.SetList(list[3], list2); err != nil { t.Fatalf("unexpected error: %v", err) } internal.Items[2].(*runtime.Unknown).Kind = "OtherTest" internal.Items[2].(*runtime.Unknown).APIVersion = "unknown" if e, a := internal.Items, list; !reflect.DeepEqual(e, a) { t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a)) } }
// Returns error if ListAndWatch didn't even tried to initialize watch. func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error { var resourceVersion string resyncCh, cleanup := r.resyncChan() defer cleanup() list, err := r.listerWatcher.List() if err != nil { return fmt.Errorf("%s: Failed to list %v: %v", r.name, r.expectedType, err) } meta, err := meta.Accessor(list) if err != nil { return fmt.Errorf("%s: Unable to understand list result %#v", r.name, list) } resourceVersion = meta.ResourceVersion() items, err := runtime.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: util.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 } } }