diff --git a/cmd/crictl/constants.go b/cmd/crictl/constants.go new file mode 100644 index 0000000000..2bfb962300 --- /dev/null +++ b/cmd/crictl/constants.go @@ -0,0 +1,24 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +const ( + // This labels are copied from kubelet directly, and may change + // without warning in the future. + kubePodNameLabel = "io.kubernetes.pod.name" + kubePodNamespaceLabel = "io.kubernetes.pod.namespace" +) diff --git a/cmd/crictl/sandbox.go b/cmd/crictl/sandbox.go index 3250575c78..039980cba1 100644 --- a/cmd/crictl/sandbox.go +++ b/cmd/crictl/sandbox.go @@ -21,6 +21,7 @@ import ( "fmt" "log" "os" + "regexp" "sort" "strings" "text/tabwriter" @@ -163,12 +164,12 @@ var listPodCommand = cli.Command{ cli.StringFlag{ Name: "name", Value: "", - Usage: "filter by pod name", + Usage: "filter by pod name regular expression pattern", }, cli.StringFlag{ Name: "namespace", Value: "", - Usage: "filter by pod namespace", + Usage: "filter by pod namespace regular expression pattern", }, cli.StringFlag{ Name: "state, s", @@ -211,26 +212,21 @@ var listPodCommand = cli.Command{ } opts := listOptions{ - id: context.String("id"), - state: context.String("state"), - verbose: context.Bool("verbose"), - quiet: context.Bool("quiet"), - output: context.String("output"), - latest: context.Bool("latest"), - last: context.Int("last"), - noTrunc: context.Bool("no-trunc"), + id: context.String("id"), + state: context.String("state"), + verbose: context.Bool("verbose"), + quiet: context.Bool("quiet"), + output: context.String("output"), + latest: context.Bool("latest"), + last: context.Int("last"), + noTrunc: context.Bool("no-trunc"), + podNameRegexp: context.String("name"), + podNamespaceRegexp: context.String("namespace"), } opts.labels, err = parseLabelStringSlice(context.StringSlice("label")) if err != nil { return err } - if context.String("name") != "" { - opts.labels["io.kubernetes.pod.name"] = context.String("name") - } - if context.String("namespace") != "" { - opts.labels["io.kubernetes.pod.namespace"] = context.String("namespace") - } - if err = ListPodSandboxes(runtimeClient, opts); err != nil { return fmt.Errorf("listing pod sandboxes failed: %v", err) } @@ -377,6 +373,18 @@ func PodSandboxStatus(client pb.RuntimeServiceClient, ID, output string, quiet b return nil } +func podMatchesRegex(pattern, target string) bool { + if pattern == "" { + return true + } + matched, err := regexp.MatchString(pattern, target) + if err != nil { + // Assume it's not a match if an error occurs. + return false + } + return matched +} + // ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses // the returned ListPodSandboxResponse. func ListPodSandboxes(client pb.RuntimeServiceClient, opts listOptions) error { @@ -424,6 +432,14 @@ func ListPodSandboxes(client pb.RuntimeServiceClient, opts listOptions) error { fmt.Fprintln(w, "POD ID\tCREATED\tSTATE\tNAME\tNAMESPACE\tATTEMPT") } for _, pod := range r.Items { + // Filter by pod name/namespace regular expressions. + if !podMatchesRegex(opts.podNameRegexp, pod.Labels[kubePodNameLabel]) { + continue + } + if !podMatchesRegex(opts.podNamespaceRegexp, pod.Labels[kubePodNamespaceLabel]) { + continue + } + if opts.quiet { fmt.Printf("%s\n", pod.Id) continue diff --git a/cmd/crictl/sandbox_test.go b/cmd/crictl/sandbox_test.go new file mode 100644 index 0000000000..0bf29d5a82 --- /dev/null +++ b/cmd/crictl/sandbox_test.go @@ -0,0 +1,70 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" +) + +func TestPodFilterByRegex(t *testing.T) { + testCases := []struct { + desc string + pattern string + podString string + isMatch bool + }{ + { + "exact name should match", + "i_am_a_container", + "i_am_a_container", + true, + }, + { + "prefix should match", + "i_am", + "i_am_a_container", + true, + }, + { + "empty pattern should match", + "", + "i_am_a_container", + true, + }, + { + "unmatched pattern should not match", + "foo", + "i_am_a_container", + false, + }, + { + "reguar expression should match", + "iner$", + "i_am_a_container", + true, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + r := podMatchesRegex(tc.pattern, tc.podString) + if r != tc.isMatch { + t.Errorf("expected matched to be %v; actual result is %v", tc.isMatch, r) + } + + }) + } +} diff --git a/cmd/crictl/util.go b/cmd/crictl/util.go index 69c65b5af1..a58685cfa1 100644 --- a/cmd/crictl/util.go +++ b/cmd/crictl/util.go @@ -48,6 +48,10 @@ type listOptions struct { id string // podID of container podID string + // Regular expression pattern to match pod name + podNameRegexp string + // Regular expression pattern to match the pod namespace + podNamespaceRegexp string // state of the sandbox state string // show verbose info for the sandbox