func getWatchParams(query url.Values) (label, field labels.Selector, resourceVersion string) { if s, err := labels.ParseSelector(query.Get("labels")); err != nil { label = labels.Everything() } else { label = s } if s, err := labels.ParseSelector(query.Get("fields")); err != nil { field = labels.Everything() } else { field = s } resourceVersion = query.Get("resourceVersion") return }
// ResourcesFromArgsOrFile computes a list of Resources by extracting info from filename or args. It will // handle label selectors provided. func ResourcesFromArgsOrFile( cmd *cobra.Command, args []string, filename, selector string, typer runtime.ObjectTyper, mapper meta.RESTMapper, clientBuilder func(cmd *cobra.Command, mapping *meta.RESTMapping) (resource.RESTClient, error), schema validation.Schema, requireNames bool, cmdNamespace, cmdVersion string, ) resource.Visitor { // handling filename & resource id if len(selector) == 0 { if requireNames || len(filename) > 0 { mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, typer, mapper, schema, cmdNamespace, cmdVersion) client, err := clientBuilder(cmd, mapping) checkErr(err) return resource.NewInfo(client, mapping, namespace, name) } if len(args) == 2 { mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, mapper, cmdNamespace, cmdVersion) client, err := clientBuilder(cmd, mapping) checkErr(err) return resource.NewInfo(client, mapping, namespace, name) } } labelSelector, err := labels.ParseSelector(selector) checkErr(err) namespace := cmdNamespace visitors := resource.VisitorList{} if len(args) < 1 { usageError(cmd, "Must specify the type of resource") } if len(args) > 1 { usageError(cmd, "Too many arguments") } types := SplitResourceArgument(args[0]) for _, arg := range types { resourceName := arg if len(resourceName) == 0 { usageError(cmd, "Unknown resource %s", resourceName) } version, kind, err := mapper.VersionAndKindForResource(resourceName) checkErr(err) mapping, err := mapper.RESTMapping(kind, version) checkErr(err) client, err := clientBuilder(cmd, mapping) checkErr(err) visitors = append(visitors, resource.NewSelector(client, mapping, namespace, labelSelector)) } return visitors }
// ParseSelector parses the given string as a resource label selector. Optional. func (r *Request) ParseSelector(item string) *Request { if r.err != nil { return r } r.selector, r.err = labels.ParseSelector(item) return r }
func getWatchParams(query url.Values) (label, field labels.Selector, resourceVersion uint64) { if s, err := labels.ParseSelector(query.Get("labels")); err != nil { label = labels.Everything() } else { label = s } if s, err := labels.ParseSelector(query.Get("fields")); err != nil { field = labels.Everything() } else { field = s } if rv, err := strconv.ParseUint(query.Get("resourceVersion"), 10, 64); err == nil { resourceVersion = rv } return label, field, resourceVersion }
func parseSelectorOrDie(s string) labels.Selector { selector, err := labels.ParseSelector(s) if err != nil { panic(err) } return selector }
func (self *realPodsApi) getNodeSelector(nodeList *nodes.NodeList) (labels.Selector, error) { nodeLabels := []string{} for host := range nodeList.Items { nodeLabels = append(nodeLabels, fmt.Sprintf("DesiredState.Host==%s", host)) } glog.V(2).Infof("using labels %v to find pods", nodeLabels) return labels.ParseSelector(strings.Join(nodeLabels, ",")) }
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|...] <resource> [<id>]", Short: "Display one or many resources", Long: `Display one or many resources. Possible resources include pods (po), replication controllers (rc), services (se), minions (mi), or events (ev). If you specify a Go template, you can use any fields defined for the API version you are connecting to the server with. Examples: $ kubectl get pods <list all pods in ps output format> $ kubectl get replicationController 1234-56-7890-234234-456456 <list single replication controller in ps output format> $ kubectl get -f json pod 1234-56-7890-234234-456456 <list single pod in json output format>`, Run: func(cmd *cobra.Command, args []string) { mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, f.Mapper) selector := GetFlagString(cmd, "selector") labels, err := labels.ParseSelector(selector) checkErr(err) client, err := f.Client(cmd, mapping) checkErr(err) outputFormat := GetFlagString(cmd, "output") templateFile := GetFlagString(cmd, "template") defaultPrinter, err := f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers")) checkErr(err) outputVersion := GetFlagString(cmd, "output-version") if len(outputVersion) == 0 { outputVersion = mapping.APIVersion } printer, err := kubectl.GetPrinter(outputVersion, outputFormat, templateFile, defaultPrinter) checkErr(err) obj, err := kubectl.NewRESTHelper(client, mapping).Get(namespace, name, labels) checkErr(err) if err := printer.PrintObj(obj, out); err != nil { checkErr(fmt.Errorf("Unable to output the provided object: %v", err)) } }, } cmd.Flags().StringP("output", "o", "", "Output format: json|yaml|template|templatefile") cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version)") cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers") cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when --output=template or --output=templatefile") cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") return cmd }
func getWatchParams(query url.Values) (label, field labels.Selector, resourceVersion string, err error) { s, perr := labels.ParseSelector(query.Get("labels")) if perr != nil { err = perr return } label = s s, perr = labels.ParseSelector(query.Get("fields")) if perr != nil { err = perr return } field = s resourceVersion = query.Get("resourceVersion") return }
// SelectorParam defines a selector that should be applied to the object types to load. // This will not affect files loaded from disk or URL. If the parameter is empty it is // a no-op - to select all resources invoke `b.Selector(labels.Everything)`. func (b *Builder) SelectorParam(s string) *Builder { selector, err := labels.ParseSelector(s) if err != nil { b.errs = append(b.errs, fmt.Errorf("the provided selector %q is not valid: %v", s, err)) } if selector.Empty() { return b } return b.Selector(selector) }
// ParseSelectorParam parses the given string as a resource label selector. // This is a convenience function so you don't have to first check that it's a // validly formatted selector. func (r *Request) ParseSelectorParam(paramName, item string) *Request { if r.err != nil { return r } sel, err := labels.ParseSelector(item) if err != nil { r.err = err return r } return r.setParam(paramName, sel.String()) }
func validateLabels(a, b string) bool { sA, _ := labels.ParseSelector(a) sB, _ := labels.ParseSelector(b) return sA.String() == sB.String() }
// handleRESTStorage is the main dispatcher for a storage object. It switches on the HTTP method, and then // on path length, according to the following table: // Method Path Action // GET /foo list // GET /foo/bar get 'bar' // POST /foo create // PUT /foo/bar update 'bar' // DELETE /foo/bar delete 'bar' // Returns 404 if the method/pattern doesn't match one of these entries // The s accepts several query parameters: // sync=[false|true] Synchronous request (only applies to create, update, delete operations) // timeout=<duration> Timeout for synchronous requests, only applies if sync=true // labels=<label-selector> Used for filtering list operations func (s *APIServer) handleRESTStorage(parts []string, req *http.Request, w http.ResponseWriter, storage RESTStorage) { sync := req.URL.Query().Get("sync") == "true" timeout := parseTimeout(req.URL.Query().Get("timeout")) switch req.Method { case "GET": switch len(parts) { case 1: selector, err := labels.ParseSelector(req.URL.Query().Get("labels")) if err != nil { internalError(err, w) return } list, err := storage.List(selector) if err != nil { internalError(err, w) return } writeJSON(http.StatusOK, list, w) case 2: item, err := storage.Get(parts[1]) if IsNotFound(err) { notFound(w, req) return } if err != nil { internalError(err, w) return } writeJSON(http.StatusOK, item, w) default: notFound(w, req) } case "POST": if len(parts) != 1 { notFound(w, req) return } body, err := readBody(req) if err != nil { internalError(err, w) return } obj, err := storage.Extract(body) if IsNotFound(err) { notFound(w, req) return } if err != nil { internalError(err, w) return } out, err := storage.Create(obj) if IsNotFound(err) { notFound(w, req) return } if err != nil { internalError(err, w) return } op := s.createOperation(out, sync, timeout) s.finishReq(op, w) case "DELETE": if len(parts) != 2 { notFound(w, req) return } out, err := storage.Delete(parts[1]) if IsNotFound(err) { notFound(w, req) return } if err != nil { internalError(err, w) return } op := s.createOperation(out, sync, timeout) s.finishReq(op, w) case "PUT": if len(parts) != 2 { notFound(w, req) return } body, err := readBody(req) if err != nil { internalError(err, w) return } obj, err := storage.Extract(body) if IsNotFound(err) { notFound(w, req) return } if err != nil { internalError(err, w) return } out, err := storage.Update(obj) if IsNotFound(err) { notFound(w, req) return } if err != nil { internalError(err, w) return } op := s.createOperation(out, sync, timeout) s.finishReq(op, w) default: notFound(w, req) } }
// handleRESTStorage is the main dispatcher for a storage object. It switches on the HTTP method, and then // on path length, according to the following table: // Method Path Action // GET /foo list // GET /foo/bar get 'bar' // POST /foo create // PUT /foo/bar update 'bar' // DELETE /foo/bar delete 'bar' // Returns 404 if the method/pattern doesn't match one of these entries // The s accepts several query parameters: // sync=[false|true] Synchronous request (only applies to create, update, delete operations) // timeout=<duration> Timeout for synchronous requests, only applies if sync=true // labels=<label-selector> Used for filtering list operations func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w http.ResponseWriter, storage RESTStorage) { ctx := api.NewContext() sync := req.URL.Query().Get("sync") == "true" timeout := parseTimeout(req.URL.Query().Get("timeout")) // TODO for now, we pull namespace from query parameter, but according to spec, it must go in resource path in future PR // if a namespace if specified, it's always used. // for list/watch operations, a namespace is not required if omitted. // for all other operations, if namespace is omitted, we will default to default namespace. namespace := req.URL.Query().Get("namespace") if len(namespace) > 0 { ctx = api.WithNamespace(ctx, namespace) } switch req.Method { case "GET": switch len(parts) { case 1: label, err := labels.ParseSelector(req.URL.Query().Get("labels")) if err != nil { errorJSON(err, h.codec, w) return } field, err := labels.ParseSelector(req.URL.Query().Get("fields")) if err != nil { errorJSON(err, h.codec, w) return } list, err := storage.List(ctx, label, field) if err != nil { errorJSON(err, h.codec, w) return } if err := h.setSelfLink(list, req); err != nil { errorJSON(err, h.codec, w) return } writeJSON(http.StatusOK, h.codec, list, w) case 2: item, err := storage.Get(api.WithNamespaceDefaultIfNone(ctx), parts[1]) if err != nil { errorJSON(err, h.codec, w) return } if err := h.setSelfLink(item, req); err != nil { errorJSON(err, h.codec, w) return } writeJSON(http.StatusOK, h.codec, item, w) default: notFound(w, req) } case "POST": if len(parts) != 1 { notFound(w, req) return } body, err := readBody(req) if err != nil { errorJSON(err, h.codec, w) return } obj := storage.New() err = h.codec.DecodeInto(body, obj) if err != nil { errorJSON(err, h.codec, w) return } out, err := storage.Create(api.WithNamespaceDefaultIfNone(ctx), obj) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout, curry(h.setSelfLinkAddName, req)) h.finishReq(op, req, w) case "DELETE": if len(parts) != 2 { notFound(w, req) return } out, err := storage.Delete(api.WithNamespaceDefaultIfNone(ctx), parts[1]) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout, nil) h.finishReq(op, req, w) case "PUT": if len(parts) != 2 { notFound(w, req) return } body, err := readBody(req) if err != nil { errorJSON(err, h.codec, w) return } obj := storage.New() err = h.codec.DecodeInto(body, obj) if err != nil { errorJSON(err, h.codec, w) return } out, err := storage.Update(api.WithNamespaceDefaultIfNone(ctx), obj) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout, curry(h.setSelfLink, req)) h.finishReq(op, req, w) default: notFound(w, req) } }
// handleREST is the main dispatcher for the server. It switches on the HTTP method, and then // on path length, according to the following table: // Method Path Action // GET /foo list // GET /foo/bar get 'bar' // POST /foo create // PUT /foo/bar update 'bar' // DELETE /foo/bar delete 'bar' // Returns 404 if the method/pattern doesn't match one of these entries // The server accepts several query parameters: // sync=[false|true] Synchronous request (only applies to create, update, delete operations) // timeout=<duration> Timeout for synchronous requests, only applies if sync=true // labels=<label-selector> Used for filtering list operations func (server *ApiServer) handleREST(parts []string, requestUrl *url.URL, req *http.Request, w http.ResponseWriter, storage RESTStorage) { sync := requestUrl.Query().Get("sync") == "true" timeout, err := time.ParseDuration(requestUrl.Query().Get("timeout")) if err != nil && len(requestUrl.Query().Get("timeout")) > 0 { log.Printf("Failed to parse: %#v '%s'", err, requestUrl.Query().Get("timeout")) timeout = time.Second * 30 } switch req.Method { case "GET": switch len(parts) { case 1: selector, err := labels.ParseSelector(requestUrl.Query().Get("labels")) if err != nil { server.error(err, w) return } controllers, err := storage.List(selector) if err != nil { server.error(err, w) return } server.write(http.StatusOK, controllers, w) case 2: item, err := storage.Get(parts[1]) if err != nil { server.error(err, w) return } if item == nil { server.notFound(req, w) return } server.write(200, item, w) default: server.notFound(req, w) } return case "POST": if len(parts) != 1 { server.notFound(req, w) return } body, err := server.readBody(req) if err != nil { server.error(err, w) return } obj, err := storage.Extract(body) if err != nil { server.error(err, w) return } out, err := storage.Create(obj) if err == nil && sync { obj, err = server.waitForObject(out, timeout) } if err != nil { server.error(err, w) return } var statusCode int if sync { statusCode = http.StatusOK } else { statusCode = http.StatusAccepted } server.write(statusCode, obj, w) return case "DELETE": if len(parts) != 2 { server.notFound(req, w) return } out, err := storage.Delete(parts[1]) var obj interface{} obj = Status{Success: true} if err == nil && sync { obj, err = server.waitForObject(out, timeout) } if err != nil { server.error(err, w) return } var statusCode int if sync { statusCode = http.StatusOK } else { statusCode = http.StatusAccepted } server.write(statusCode, obj, w) return case "PUT": if len(parts) != 2 { server.notFound(req, w) return } body, err := server.readBody(req) if err != nil { server.error(err, w) } obj, err := storage.Extract(body) if err != nil { server.error(err, w) return } out, err := storage.Update(obj) if err == nil && sync { obj, err = server.waitForObject(out, timeout) } if err != nil { server.error(err, w) return } var statusCode int if sync { statusCode = http.StatusOK } else { statusCode = http.StatusAccepted } server.write(statusCode, obj, w) return default: server.notFound(req, w) } }
func TestListPodListSelection(t *testing.T) { podRegistry := registrytest.NewPodRegistry(nil) podRegistry.Pods = &api.PodList{ Items: []api.Pod{ { JSONBase: api.JSONBase{ID: "foo"}, }, { JSONBase: api.JSONBase{ID: "bar"}, DesiredState: api.PodState{Host: "barhost"}, }, { JSONBase: api.JSONBase{ID: "baz"}, DesiredState: api.PodState{Status: "bazstatus"}, }, { JSONBase: api.JSONBase{ID: "qux"}, Labels: map[string]string{"label": "qux"}, }, { JSONBase: api.JSONBase{ID: "zot"}, }, }, } storage := REST{ registry: podRegistry, } table := []struct { label, field string expectedIDs util.StringSet }{ { expectedIDs: util.NewStringSet("foo", "bar", "baz", "qux", "zot"), }, { field: "ID=zot", expectedIDs: util.NewStringSet("zot"), }, { label: "label=qux", expectedIDs: util.NewStringSet("qux"), }, { field: "DesiredState.Status=bazstatus", expectedIDs: util.NewStringSet("baz"), }, { field: "DesiredState.Host=barhost", expectedIDs: util.NewStringSet("bar"), }, { field: "DesiredState.Host=", expectedIDs: util.NewStringSet("foo", "baz", "qux", "zot"), }, { field: "DesiredState.Host!=", expectedIDs: util.NewStringSet("bar"), }, } for index, item := range table { label, err := labels.ParseSelector(item.label) if err != nil { t.Errorf("unexpected error: %v", err) continue } field, err := labels.ParseSelector(item.field) if err != nil { t.Errorf("unexpected error: %v", err) continue } podsObj, err := storage.List(label, field) if err != nil { t.Errorf("unexpected error: %v", err) } pods := podsObj.(*api.PodList) if e, a := len(item.expectedIDs), len(pods.Items); e != a { t.Errorf("%v: Expected %v, got %v", index, e, a) } for _, pod := range pods.Items { if !item.expectedIDs.Has(pod.ID) { t.Errorf("%v: Unexpected pod %v", index, pod.ID) } t.Logf("%v: Got pod ID: %v", index, pod.ID) } } }
func TestListPodListSelection(t *testing.T) { podRegistry := registrytest.NewPodRegistry(nil) podRegistry.Pods = &api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "foo"}, }, { ObjectMeta: api.ObjectMeta{Name: "bar"}, Status: api.PodStatus{Host: "barhost"}, }, { ObjectMeta: api.ObjectMeta{Name: "baz"}, Status: api.PodStatus{Phase: "bazstatus"}, }, { ObjectMeta: api.ObjectMeta{ Name: "qux", Labels: map[string]string{"label": "qux"}, }, }, { ObjectMeta: api.ObjectMeta{Name: "zot"}, }, }, } storage := REST{ registry: podRegistry, podCache: &fakeCache{statusToReturn: &api.PodStatus{}}, } 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=bazstatus", expectedIDs: util.NewStringSet("baz"), }, { field: "Status.Host=barhost", expectedIDs: util.NewStringSet("bar"), }, { field: "Status.Host=", expectedIDs: util.NewStringSet("foo", "baz", "qux", "zot"), }, { field: "Status.Host!=", expectedIDs: util.NewStringSet("bar"), }, } for index, item := range table { label, err := labels.ParseSelector(item.label) if err != nil { t.Errorf("unexpected error: %v", err) continue } field, err := labels.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) if e, a := len(item.expectedIDs), len(pods.Items); e != a { t.Errorf("%v: Expected %v, got %v", index, e, a) } 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) } } }
// handleREST is the main dispatcher for the server. It switches on the HTTP method, and then // on path length, according to the following table: // Method Path Action // GET /foo list // GET /foo/bar get 'bar' // POST /foo create // PUT /foo/bar update 'bar' // DELETE /foo/bar delete 'bar' // Returns 404 if the method/pattern doesn't match one of these entries // The server accepts several query parameters: // sync=[false|true] Synchronous request (only applies to create, update, delete operations) // timeout=<duration> Timeout for synchronous requests, only applies if sync=true // labels=<label-selector> Used for filtering list operations func (server *ApiServer) handleREST(parts []string, requestUrl *url.URL, req *http.Request, w http.ResponseWriter, storage RESTStorage) { sync := requestUrl.Query().Get("sync") == "true" timeout := parseTimeout(requestUrl.Query().Get("timeout")) switch req.Method { case "GET": switch len(parts) { case 1: selector, err := labels.ParseSelector(requestUrl.Query().Get("labels")) if err != nil { server.error(err, w) return } list, err := storage.List(selector) if err != nil { server.error(err, w) return } server.write(http.StatusOK, list, w) case 2: item, err := storage.Get(parts[1]) if err != nil { server.error(err, w) return } if item == nil { server.notFound(req, w) return } server.write(http.StatusOK, item, w) default: server.notFound(req, w) } case "POST": if len(parts) != 1 { server.notFound(req, w) return } body, err := server.readBody(req) if err != nil { server.error(err, w) return } obj, err := storage.Extract(body) if err != nil { server.error(err, w) return } out, err := storage.Create(obj) if err != nil { server.error(err, w) return } server.finishReq(out, sync, timeout, w) case "DELETE": if len(parts) != 2 { server.notFound(req, w) return } out, err := storage.Delete(parts[1]) if err != nil { server.error(err, w) return } server.finishReq(out, sync, timeout, w) case "PUT": if len(parts) != 2 { server.notFound(req, w) return } body, err := server.readBody(req) if err != nil { server.error(err, w) } obj, err := storage.Extract(body) if err != nil { server.error(err, w) return } out, err := storage.Update(obj) if err != nil { server.error(err, w) return } server.finishReq(out, sync, timeout, w) default: server.notFound(req, w) } }
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|...] <resource> [<id>]", Short: "Display one or many resources", Long: `Display one or many resources. Possible resources include pods (po), replication controllers (rc), services (se), minions (mi), or events (ev). If you specify a Go template, you can use any fields defined for the API version you are connecting to the server with. Examples: $ kubectl get pods <list all pods in ps output format> $ kubectl get replicationController 1234-56-7890-234234-456456 <list single replication controller in ps output format> $ kubectl get -o json pod 1234-56-7890-234234-456456 <list single pod in json output format>`, Run: func(cmd *cobra.Command, args []string) { mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, f.Mapper) selector := GetFlagString(cmd, "selector") labelSelector, err := labels.ParseSelector(selector) checkErr(err) client, err := f.RESTClient(cmd, mapping) checkErr(err) outputFormat := GetFlagString(cmd, "output") templateFile := GetFlagString(cmd, "template") defaultPrinter, err := f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers")) checkErr(err) outputVersion := GetFlagString(cmd, "output-version") if len(outputVersion) == 0 { outputVersion = mapping.APIVersion } printer, err := kubectl.GetPrinter(outputFormat, templateFile, outputVersion, mapping.ObjectConvertor, defaultPrinter) checkErr(err) restHelper := resource.NewHelper(client, mapping) var obj runtime.Object if len(name) == 0 { obj, err = restHelper.List(namespace, labelSelector) } else { obj, err = restHelper.Get(namespace, name) } checkErr(err) isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only") // print the current object if !isWatchOnly { if err := printer.PrintObj(obj, out); err != nil { checkErr(fmt.Errorf("unable to output the provided object: %v", err)) } } // print watched changes if isWatch || isWatchOnly { rv, err := mapping.MetadataAccessor.ResourceVersion(obj) checkErr(err) w, err := restHelper.Watch(namespace, rv, labelSelector, labels.Everything()) checkErr(err) kubectl.WatchLoop(w, printer, out) } }, } cmd.Flags().StringP("output", "o", "", "Output format: json|yaml|template|templatefile") cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version)") cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers") cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when --output=template or --output=templatefile") cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") cmd.Flags().Bool("watch-only", false, "Watch for changes to the requseted object(s), without listing/getting first.") return cmd }
func TestSelectionPredicate(t *testing.T) { table := map[string]struct { labelSelector, fieldSelector string labels, fields labels.Set err error shouldMatch bool }{ "A": { labelSelector: "name=foo", fieldSelector: "uid=12345", labels: labels.Set{"name": "foo"}, fields: labels.Set{"uid": "12345"}, shouldMatch: true, }, "B": { labelSelector: "name=foo", fieldSelector: "uid=12345", labels: labels.Set{"name": "foo"}, fields: labels.Set{}, shouldMatch: false, }, "C": { labelSelector: "name=foo", fieldSelector: "uid=12345", labels: labels.Set{}, fields: labels.Set{"uid": "12345"}, shouldMatch: false, }, "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.ParseSelector(item.labelSelector) if err != nil { panic(err) } parsedField, err := labels.ParseSelector(item.fieldSelector) if err != nil { panic(err) } sp := &SelectionPredicate{ Label: parsedLabel, Field: parsedField, GetAttrs: func(runtime.Object) (label, field labels.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) } } }
// handleRESTStorage is the main dispatcher for a storage object. It switches on the HTTP method, and then // on path length, according to the following table: // Method Path Action // GET /foo list // GET /foo/bar get 'bar' // POST /foo create // PUT /foo/bar update 'bar' // DELETE /foo/bar delete 'bar' // Returns 404 if the method/pattern doesn't match one of these entries // The s accepts several query parameters: // sync=[false|true] Synchronous request (only applies to create, update, delete operations) // timeout=<duration> Timeout for synchronous requests, only applies if sync=true // labels=<label-selector> Used for filtering list operations func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w http.ResponseWriter, storage RESTStorage, namespace, kind string) { ctx := api.WithNamespace(api.NewContext(), namespace) sync := req.URL.Query().Get("sync") == "true" timeout := parseTimeout(req.URL.Query().Get("timeout")) switch req.Method { case "GET": switch len(parts) { case 1: label, err := labels.ParseSelector(req.URL.Query().Get("labels")) if err != nil { errorJSON(err, h.codec, w) return } field, err := labels.ParseSelector(req.URL.Query().Get("fields")) if err != nil { errorJSON(err, h.codec, w) return } lister, ok := storage.(RESTLister) if !ok { errorJSON(errors.NewMethodNotSupported(kind, "list"), h.codec, w) return } list, err := lister.List(ctx, label, field) if err != nil { errorJSON(err, h.codec, w) return } if err := h.setSelfLink(list, req); err != nil { errorJSON(err, h.codec, w) return } writeJSON(http.StatusOK, h.codec, list, w) case 2: getter, ok := storage.(RESTGetter) if !ok { errorJSON(errors.NewMethodNotSupported(kind, "get"), h.codec, w) return } item, err := getter.Get(ctx, parts[1]) if err != nil { errorJSON(err, h.codec, w) return } if err := h.setSelfLink(item, req); err != nil { errorJSON(err, h.codec, w) return } writeJSON(http.StatusOK, h.codec, item, w) default: notFound(w, req) } case "POST": if len(parts) != 1 { notFound(w, req) return } creater, ok := storage.(RESTCreater) if !ok { errorJSON(errors.NewMethodNotSupported(kind, "create"), h.codec, w) return } body, err := readBody(req) if err != nil { errorJSON(err, h.codec, w) return } obj := storage.New() err = h.codec.DecodeInto(body, obj) if err != nil { errorJSON(err, h.codec, w) return } // invoke admission control err = h.admissionControl.Admit(admission.NewAttributesRecord(obj, namespace, parts[0], "CREATE")) if err != nil { errorJSON(err, h.codec, w) return } out, err := creater.Create(ctx, obj) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout, curry(h.setSelfLinkAddName, req)) h.finishReq(op, req, w) case "DELETE": if len(parts) != 2 { notFound(w, req) return } deleter, ok := storage.(RESTDeleter) if !ok { errorJSON(errors.NewMethodNotSupported(kind, "delete"), h.codec, w) return } // invoke admission control err := h.admissionControl.Admit(admission.NewAttributesRecord(nil, namespace, parts[0], "DELETE")) if err != nil { errorJSON(err, h.codec, w) return } out, err := deleter.Delete(ctx, parts[1]) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout, nil) h.finishReq(op, req, w) case "PUT": if len(parts) != 2 { notFound(w, req) return } updater, ok := storage.(RESTUpdater) if !ok { errorJSON(errors.NewMethodNotSupported(kind, "create"), h.codec, w) return } body, err := readBody(req) if err != nil { errorJSON(err, h.codec, w) return } obj := storage.New() err = h.codec.DecodeInto(body, obj) if err != nil { errorJSON(err, h.codec, w) return } // invoke admission control err = h.admissionControl.Admit(admission.NewAttributesRecord(obj, namespace, parts[0], "UPDATE")) if err != nil { errorJSON(err, h.codec, w) return } out, err := updater.Update(ctx, obj) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout, curry(h.setSelfLink, req)) h.finishReq(op, req, w) default: notFound(w, req) } }
// handleRESTStorage is the main dispatcher for a storage object. It switches on the HTTP method, and then // on path length, according to the following table: // Method Path Action // GET /foo list // GET /foo/bar get 'bar' // POST /foo create // PUT /foo/bar update 'bar' // DELETE /foo/bar delete 'bar' // Returns 404 if the method/pattern doesn't match one of these entries // The s accepts several query parameters: // sync=[false|true] Synchronous request (only applies to create, update, delete operations) // timeout=<duration> Timeout for synchronous requests, only applies if sync=true // labels=<label-selector> Used for filtering list operations func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w http.ResponseWriter, storage RESTStorage) { sync := req.URL.Query().Get("sync") == "true" timeout := parseTimeout(req.URL.Query().Get("timeout")) switch req.Method { case "GET": switch len(parts) { case 1: label, err := labels.ParseSelector(req.URL.Query().Get("labels")) if err != nil { errorJSON(err, h.codec, w) return } field, err := labels.ParseSelector(req.URL.Query().Get("fields")) if err != nil { errorJSON(err, h.codec, w) return } list, err := storage.List(label, field) if err != nil { errorJSON(err, h.codec, w) return } writeJSON(http.StatusOK, h.codec, list, w) case 2: item, err := storage.Get(parts[1]) if err != nil { errorJSON(err, h.codec, w) return } writeJSON(http.StatusOK, h.codec, item, w) default: notFound(w, req) } case "POST": if len(parts) != 1 { notFound(w, req) return } body, err := readBody(req) if err != nil { errorJSON(err, h.codec, w) return } obj := storage.New() err = h.codec.DecodeInto(body, obj) if err != nil { errorJSON(err, h.codec, w) return } out, err := storage.Create(obj) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout) h.finishReq(op, req, w) case "DELETE": if len(parts) != 2 { notFound(w, req) return } out, err := storage.Delete(parts[1]) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout) h.finishReq(op, req, w) case "PUT": if len(parts) != 2 { notFound(w, req) return } body, err := readBody(req) if err != nil { errorJSON(err, h.codec, w) return } obj := storage.New() err = h.codec.DecodeInto(body, obj) if err != nil { errorJSON(err, h.codec, w) return } out, err := storage.Update(obj) if err != nil { errorJSON(err, h.codec, w) return } op := h.createOperation(out, sync, timeout) h.finishReq(op, req, w) default: notFound(w, req) } }