func parseSelectorOrDie(s string) fields.Selector { selector, err := fields.ParseSelector(s) if err != nil { panic(err) } return selector }
func init() { Scheme.AddDefaultingFuncs( func(obj *ListOptions) { obj.LabelSelector = labels.Everything() obj.FieldSelector = fields.Everything() }, func(obj *PodExecOptions) { obj.Stderr = true obj.Stdout = true }, ) Scheme.AddConversionFuncs( func(in *util.Time, out *util.Time, s conversion.Scope) error { // Cannot deep copy these, because time.Time has unexported fields. *out = *in return nil }, func(in *string, out *labels.Selector, s conversion.Scope) error { selector, err := labels.Parse(*in) if err != nil { return err } *out = selector return nil }, func(in *string, out *fields.Selector, s conversion.Scope) error { selector, err := fields.ParseSelector(*in) if err != nil { return err } *out = selector return nil }, func(in *labels.Selector, out *string, s conversion.Scope) error { if *in == nil { return nil } *out = (*in).String() return nil }, func(in *fields.Selector, out *string, s conversion.Scope) error { if *in == nil { return nil } *out = (*in).String() return nil }, func(in *resource.Quantity, out *resource.Quantity, s conversion.Scope) error { // Cannot deep copy these, because inf.Dec has unexported fields. *out = *in.Copy() return nil }, ) }
// WatchPod returns a ListWatch for watching a pod. The stopChannel is used // to close the reflector backing the watch. The caller is responsible for derring a close on the channel to // stop the reflector. func (c *realScrubberClient) WatchPod(name, namespace, resourceVersion string, stopChannel chan struct{}) func() *api.Pod { fieldSelector, _ := fields.ParseSelector("metadata.name=" + name) podLW := &cache.ListWatch{ ListFunc: func() (runtime.Object, error) { return c.client.Pods(namespace).List(labels.Everything(), fieldSelector) }, WatchFunc: func(resourceVersion string) (watch.Interface, error) { return c.client.Pods(namespace).Watch(labels.Everything(), fieldSelector, resourceVersion) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(podLW, &api.Pod{}, queue, 1*time.Minute).RunUntil(stopChannel) return func() *api.Pod { obj := queue.Pop() return obj.(*api.Pod) } }
// FuzzerFor can randomly populate api objects that are destined for version. func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { // Do nothing; this struct has only a Kind field and it must stay blank in memory. }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.ObjectMeta, c fuzz.Continue) { j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() j.UID = types.UID(c.RandString()) j.GenerateName = c.RandString() var sec, nsec int64 c.Fuzz(&sec) c.Fuzz(&nsec) j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy() }, func(j *api.ObjectReference, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = c.RandString() j.Kind = c.RandString() j.Namespace = c.RandString() j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.FieldPath = c.RandString() }, func(j *api.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.ListOptions, c fuzz.Continue) { // TODO: add some parsing j.LabelSelector, _ = labels.Parse("a=b") j.FieldSelector, _ = fields.ParseSelector("a=b") }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} *j = statuses[c.Rand.Intn(len(statuses))] }, func(j *api.PodTemplateSpec, c fuzz.Continue) { // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 // conversion compare converted object to nil via DeepEqual j.ObjectMeta = api.ObjectMeta{} c.Fuzz(&j.ObjectMeta) j.ObjectMeta = api.ObjectMeta{Labels: j.ObjectMeta.Labels} j.Spec = api.PodSpec{} c.Fuzz(&j.Spec) }, func(j *api.Binding, c fuzz.Continue) { c.Fuzz(&j.ObjectMeta) j.Target.Name = c.RandString() }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again //j.TemplateRef = nil // this is required for round trip }, func(j *api.ReplicationControllerStatus, c fuzz.Continue) { // only replicas round trips j.Replicas = int(c.RandUint64()) }, func(j *api.List, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again // TODO: uncomment when round trip starts from a versioned object if false { //j.Items == nil { j.Items = []runtime.Object{} } }, func(j *runtime.Object, c fuzz.Continue) { // TODO: uncomment when round trip starts from a versioned object if true { //c.RandBool() { *j = &runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *j = t } }, func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pb[docker.Port(c.RandString())] = []docker.PortBinding{ {c.RandString(), c.RandString()}, {c.RandString(), c.RandString()}, } }, func(pm map[string]docker.PortMapping, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pm[c.RandString()] = docker.PortMapping{ c.RandString(): c.RandString(), } }, func(q *resource.Quantity, c fuzz.Continue) { // Real Quantity fuzz testing is done elsewhere; // this limited subset of functionality survives // round-tripping to v1beta1/2. q.Amount = &inf.Dec{} q.Format = resource.DecimalExponent //q.Amount.SetScale(inf.Scale(-c.Intn(12))) q.Amount.SetUnscaled(c.Int63n(1000)) }, func(p *api.PullPolicy, c fuzz.Continue) { policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent} *p = policies[c.Rand.Intn(len(policies))] }, func(rp *api.RestartPolicy, c fuzz.Continue) { policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure} *rp = policies[c.Rand.Intn(len(policies))] }, func(vs *api.VolumeSource, c fuzz.Continue) { // Exactly one of the fields must be set. v := reflect.ValueOf(vs).Elem() i := int(c.RandUint64() % uint64(v.NumField())) v = v.Field(i).Addr() // Use a new fuzzer which cannot populate nil to ensure one field will be set. fuzz.New().NilChance(0).NumElements(1, 1).Fuzz(v.Interface()) }, func(d *api.DNSPolicy, c fuzz.Continue) { policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault} *d = policies[c.Rand.Intn(len(policies))] }, func(p *api.Protocol, c fuzz.Continue) { protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP} *p = protocols[c.Rand.Intn(len(protocols))] }, func(p *api.ServiceAffinity, c fuzz.Continue) { types := []api.ServiceAffinity{api.ServiceAffinityClientIP, api.ServiceAffinityNone} *p = types[c.Rand.Intn(len(types))] }, func(p *api.ServiceType, c fuzz.Continue) { types := []api.ServiceType{api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer} *p = types[c.Rand.Intn(len(types))] }, func(ct *api.Container, c fuzz.Continue) { c.FuzzNoCustom(ct) // fuzz self without calling this function again ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty }, func(ev *api.EnvVar, c fuzz.Continue) { ev.Name = c.RandString() if c.RandBool() { ev.Value = c.RandString() } else { ev.ValueFrom = &api.EnvVarSource{} ev.ValueFrom.FieldRef = &api.ObjectFieldSelector{} versions := []string{"v1beta1", "v1beta2", "v1beta3"} ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] ev.ValueFrom.FieldRef.FieldPath = c.RandString() } }, func(sc *api.SecurityContext, c fuzz.Continue) { c.FuzzNoCustom(sc) // fuzz self without calling this function again priv := c.RandBool() sc.Privileged = &priv sc.Capabilities = &api.Capabilities{ Add: make([]api.Capability, 0), Drop: make([]api.Capability, 0), } c.Fuzz(&sc.Capabilities.Add) c.Fuzz(&sc.Capabilities.Drop) }, func(e *api.Event, c fuzz.Continue) { c.FuzzNoCustom(e) // fuzz self without calling this function again // Fix event count to 1, otherwise, if a v1beta1 or v1beta2 event has a count set arbitrarily, it's count is ignored if e.FirstTimestamp.IsZero() { e.Count = 1 } else { c.Fuzz(&e.Count) } }, func(s *api.Secret, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again s.Type = api.SecretTypeOpaque }, func(pv *api.PersistentVolume, c fuzz.Continue) { c.FuzzNoCustom(pv) // fuzz self without calling this function again types := []api.PersistentVolumePhase{api.VolumeAvailable, api.VolumePending, api.VolumeBound, api.VolumeReleased, api.VolumeFailed} pv.Status.Phase = types[c.Rand.Intn(len(types))] pv.Status.Message = c.RandString() reclamationPolicies := []api.PersistentVolumeReclaimPolicy{api.PersistentVolumeReclaimRecycle, api.PersistentVolumeReclaimRetain} pv.Spec.PersistentVolumeReclaimPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))] }, func(pvc *api.PersistentVolumeClaim, c fuzz.Continue) { c.FuzzNoCustom(pvc) // fuzz self without calling this function again types := []api.PersistentVolumeClaimPhase{api.ClaimBound, api.ClaimPending} pvc.Status.Phase = types[c.Rand.Intn(len(types))] }, func(s *api.NamespaceSpec, c fuzz.Continue) { s.Finalizers = []api.FinalizerName{api.FinalizerQingYuan} }, func(s *api.NamespaceStatus, c fuzz.Continue) { s.Phase = api.NamespaceActive }, func(http *api.HTTPGetAction, c fuzz.Continue) { c.FuzzNoCustom(http) // fuzz self without calling this function again http.Path = "/" + http.Path // can't be blank }, func(ss *api.ServiceSpec, c fuzz.Continue) { c.FuzzNoCustom(ss) // fuzz self without calling this function again if len(ss.Ports) == 0 { // There must be at least 1 port. ss.Ports = append(ss.Ports, api.ServicePort{}) c.Fuzz(&ss.Ports[0]) } for i := range ss.Ports { switch ss.Ports[i].TargetPort.Kind { case util.IntstrInt: ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero case util.IntstrString: ss.Ports[i].TargetPort.StrVal = "x" + ss.Ports[i].TargetPort.StrVal // non-empty } } }, func(n *api.Node, c fuzz.Continue) { c.FuzzNoCustom(n) n.Spec.ExternalID = "external" }, ) return f }
func TestListNamespaceListSelection(t *testing.T) { fakeEtcdClient, helper := newHelper(t) key := etcdtest.AddPrefix("/namespaces") fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Nodes: []*etcd.Node{ {Value: runtime.EncodeOrDie(latest.Codec, &api.Namespace{ ObjectMeta: api.ObjectMeta{Name: "foo"}, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Namespace{ ObjectMeta: api.ObjectMeta{Name: "bar"}, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Namespace{ ObjectMeta: api.ObjectMeta{Name: "baz"}, Status: api.NamespaceStatus{Phase: api.NamespaceTerminating}, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Namespace{ ObjectMeta: api.ObjectMeta{ Name: "qux", Labels: map[string]string{"label": "qux"}, }, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Namespace{ ObjectMeta: api.ObjectMeta{Name: "zot"}, })}, }, }, }, } storage, _, _ := NewStorage(helper) ctx := api.NewContext() table := []struct { label, field string expectedIDs util.StringSet }{ { expectedIDs: util.NewStringSet("foo", "bar", "baz", "qux", "zot"), }, { field: "name=zot", expectedIDs: util.NewStringSet("zot"), }, { label: "label=qux", expectedIDs: util.NewStringSet("qux"), }, { field: "status.phase=Terminating", expectedIDs: util.NewStringSet("baz"), }, } for index, item := range table { label, err := labels.Parse(item.label) if err != nil { t.Errorf("unexpected error: %v", err) continue } field, err := fields.ParseSelector(item.field) if err != nil { t.Errorf("unexpected error: %v", err) continue } namespacesObj, err := storage.List(ctx, label, field) if err != nil { t.Errorf("unexpected error: %v", err) } namespaces := namespacesObj.(*api.NamespaceList) set := util.NewStringSet() for i := range namespaces.Items { set.Insert(namespaces.Items[i].Name) } if e, a := len(item.expectedIDs), len(set); e != a { t.Errorf("%v: Expected %v, got %v", index, item.expectedIDs, set) } } }
func TestListPodListSelection(t *testing.T) { fakeEtcdClient, helper := newHelper(t) ctx := api.NewDefaultContext() storage := NewStorage(helper, nil).Pod rootKey := etcdtest.AddPrefix("pods/default") key := etcdtest.AddPrefix("pods/default/zot") fakeEtcdClient.Data[rootKey] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Nodes: []*etcd.Node{ {Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo"}, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "bar"}, Spec: api.PodSpec{NodeName: "barhost"}, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "baz"}, Status: api.PodStatus{Phase: api.PodFailed}, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "qux", Labels: map[string]string{"label": "qux"}, }, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "zot"}, })}, }, }, }, } fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(latest.Codec, &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "zot"}, }), }, }, } table := []struct { label, field string expectedIDs util.StringSet }{ { expectedIDs: util.NewStringSet("foo", "bar", "baz", "qux", "zot"), }, { field: "metadata.name=zot", expectedIDs: util.NewStringSet("zot"), }, { label: "label=qux", expectedIDs: util.NewStringSet("qux"), }, { field: "status.phase=Failed", expectedIDs: util.NewStringSet("baz"), }, { field: "spec.nodeName=barhost", expectedIDs: util.NewStringSet("bar"), }, { field: "spec.nodeName=", expectedIDs: util.NewStringSet("foo", "baz", "qux", "zot"), }, { field: "spec.nodeName!=", expectedIDs: util.NewStringSet("bar"), }, } for index, item := range table { label, err := labels.Parse(item.label) if err != nil { t.Errorf("unexpected error: %v", err) continue } field, err := fields.ParseSelector(item.field) if err != nil { t.Errorf("unexpected error: %v", err) continue } podsObj, err := storage.List(ctx, label, field) if err != nil { t.Errorf("unexpected error: %v", err) } pods := podsObj.(*api.PodList) set := util.NewStringSet() for i := range pods.Items { set.Insert(pods.Items[i].Name) } if e, a := len(item.expectedIDs), len(set); e != a { t.Errorf("%v: Expected %v, got %v", index, item.expectedIDs, set) } /*for _, pod := range pods.Items { if !item.expectedIDs.Has(pod.Name) { t.Errorf("%v: Unexpected pod %v", index, pod.Name) } t.Logf("%v: Got pod Name: %v", index, pod.Name) }*/ } }
func validateFields(a, b string) bool { sA, _ := fields.ParseSelector(a) sB, _ := fields.ParseSelector(b) return sA.String() == sB.String() }
func TestListResourceQuotaListSelection(t *testing.T) { fakeEtcdClient, helper := newHelper(t) storage, _ := NewStorage(helper) ctx := api.NewDefaultContext() key := storage.Etcd.KeyRootFunc(ctx) key = etcdtest.AddPrefix(key) fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Nodes: []*etcd.Node{ {Value: runtime.EncodeOrDie(latest.Codec, &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{Name: "foo"}, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{ Name: "qux", Labels: map[string]string{"label": "qux"}, }, })}, {Value: runtime.EncodeOrDie(latest.Codec, &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{Name: "zot"}, })}, }, }, }, } table := []struct { label, field string expectedIDs util.StringSet }{ { expectedIDs: util.NewStringSet("foo", "qux", "zot"), }, { field: "name=zot", expectedIDs: util.NewStringSet("zot"), }, { label: "label=qux", expectedIDs: util.NewStringSet("qux"), }, } for index, item := range table { label, err := labels.Parse(item.label) if err != nil { t.Errorf("unexpected error: %v", err) continue } field, err := fields.ParseSelector(item.field) if err != nil { t.Errorf("unexpected error: %v", err) continue } resourcequotasObj, err := storage.List(ctx, label, field) if err != nil { t.Errorf("unexpected error: %v", err) } resourcequotas := resourcequotasObj.(*api.ResourceQuotaList) set := util.NewStringSet() for i := range resourcequotas.Items { set.Insert(resourcequotas.Items[i].Name) } if e, a := len(item.expectedIDs), len(set); e != a { t.Errorf("%v: Expected %v, got %v", index, item.expectedIDs, set) } } }
func TestSelectionPredicate(t *testing.T) { table := map[string]struct { labelSelector, fieldSelector string labels labels.Set fields fields.Set err error shouldMatch bool matchSingleKey string }{ "A": { labelSelector: "name=foo", fieldSelector: "uid=12345", labels: labels.Set{"name": "foo"}, fields: fields.Set{"uid": "12345"}, shouldMatch: true, }, "B": { labelSelector: "name=foo", fieldSelector: "uid=12345", labels: labels.Set{"name": "foo"}, fields: fields.Set{}, shouldMatch: false, }, "C": { labelSelector: "name=foo", fieldSelector: "uid=12345", labels: labels.Set{}, fields: fields.Set{"uid": "12345"}, shouldMatch: false, }, "D": { fieldSelector: "metadata.name=12345", labels: labels.Set{}, fields: fields.Set{"metadata.name": "12345"}, shouldMatch: true, matchSingleKey: "12345", }, "error": { labelSelector: "name=foo", fieldSelector: "uid=12345", err: errors.New("maybe this is a 'wrong object type' error"), shouldMatch: false, }, } for name, item := range table { parsedLabel, err := labels.Parse(item.labelSelector) if err != nil { panic(err) } parsedField, err := fields.ParseSelector(item.fieldSelector) if err != nil { panic(err) } sp := &SelectionPredicate{ Label: parsedLabel, Field: parsedField, GetAttrs: func(runtime.Object) (label labels.Set, field fields.Set, err error) { return item.labels, item.fields, item.err }, } got, err := sp.Matches(&Ignored{}) if e, a := item.err, err; e != a { t.Errorf("%v: expected %v, got %v", name, e, a) continue } if e, a := item.shouldMatch, got; e != a { t.Errorf("%v: expected %v, got %v", name, e, a) } if key := item.matchSingleKey; key != "" { got, ok := sp.MatchesSingle() if !ok { t.Errorf("%v: expected single match", name) } if e, a := key, got; e != a { t.Errorf("%v: expected %v, got %v", name, e, a) } } } }