Add regex based name filter to ls command.

Signed-off-by: Eric Sage <eric.david.sage@gmail.com>

Add regex support

Signed-off-by: Eric Sage <eric.david.sage@gmail.com>

Allow bad regex passthrough to reg string amtch

Signed-off-by: Eric Sage <eric.david.sage@gmail.com>

Add unit test

Signed-off-by: Eric Sage <eric.david.sage@gmail.com>

Add integration tests

Signed-off-by: Eric Sage <eric.david.sage@gmail.com>

Add documentation for name filter.

Signed-off-by: Eric Sage <eric.david.sage@gmail.com>
This commit is contained in:
Eric Sage 2015-07-11 01:34:01 -04:00
parent ad6d8d49a8
commit b333ea5294
5 changed files with 92 additions and 7 deletions

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
"regexp"
"strings"
"text/tabwriter"
@ -17,6 +18,7 @@ type FilterOptions struct {
SwarmName []string
DriverName []string
State []string
Name []string
}
func cmdLs(c *cli.Context) {
@ -100,6 +102,8 @@ func parseFilters(filters []string) (FilterOptions, error) {
options.DriverName = append(options.DriverName, value)
case "state":
options.State = append(options.State, value)
case "name":
options.Name = append(options.Name, value)
default:
return options, fmt.Errorf("Unsupported filter key '%s'", key)
}
@ -110,7 +114,8 @@ func parseFilters(filters []string) (FilterOptions, error) {
func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine.Host {
if len(filters.SwarmName) == 0 &&
len(filters.DriverName) == 0 &&
len(filters.State) == 0 {
len(filters.State) == 0 &&
len(filters.Name) == 0 {
return hosts
}
@ -140,8 +145,9 @@ func filterHost(host *libmachine.Host, filters FilterOptions, swarmMasters map[s
swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters)
driverMatches := matchesDriverName(host, filters.DriverName)
stateMatches := matchesState(host, filters.State)
nameMatches := matchesName(host, filters.Name)
return swarmMatches && driverMatches && stateMatches
return swarmMatches && driverMatches && stateMatches && nameMatches
}
func matchesSwarmName(host *libmachine.Host, swarmNames []string, swarmMasters map[string]string) bool {
@ -185,3 +191,19 @@ func matchesState(host *libmachine.Host, states []string) bool {
}
return false
}
func matchesName(host *libmachine.Host, names []string) bool {
if len(names) == 0 {
return true
}
for _, n := range names {
r, err := regexp.Compile(n)
if err != nil {
log.Fatal(err)
}
if r.MatchString(host.Driver.GetMachineName()) {
return true
}
}
return false
}

View File

@ -30,14 +30,19 @@ func TestParseFiltersState(t *testing.T) {
assert.Equal(t, actual, FilterOptions{State: []string{"Running"}})
}
func TestParseFiltersName(t *testing.T) {
actual, _ := parseFilters([]string{"name=dev"})
assert.Equal(t, actual, FilterOptions{Name: []string{"dev"}})
}
func TestParseFiltersAll(t *testing.T) {
actual, _ := parseFilters([]string{"swarm=foo", "driver=bar", "state=Stopped"})
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}, DriverName: []string{"bar"}, State: []string{"Stopped"}})
actual, _ := parseFilters([]string{"swarm=foo", "driver=bar", "state=Stopped", "name=dev"})
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}, DriverName: []string{"bar"}, State: []string{"Stopped"}, Name: []string{"dev"}})
}
func TestParseFiltersDuplicates(t *testing.T) {
actual, _ := parseFilters([]string{"swarm=foo", "driver=bar", "swarm=baz", "driver=qux", "state=Running", "state=Starting"})
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo", "baz"}, DriverName: []string{"bar", "qux"}, State: []string{"Running", "Starting"}})
actual, _ := parseFilters([]string{"swarm=foo", "driver=bar", "name=mark", "swarm=baz", "driver=qux", "state=Running", "state=Starting", "name=time"})
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo", "baz"}, DriverName: []string{"bar", "qux"}, State: []string{"Running", "Starting"}, Name: []string{"mark", "time"}})
}
func TestParseFiltersValueWithEqual(t *testing.T) {
@ -170,6 +175,44 @@ func TestFilterHostsByState(t *testing.T) {
assert.EqualValues(t, filterHosts(hosts, opts), expected)
}
func TestFilterHostsByName(t *testing.T) {
opts := FilterOptions{
Name: []string{"fire", "ice", "earth", "a.?r"},
}
node1 :=
&libmachine.Host{
Name: "fire",
DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "fire"},
}
node2 :=
&libmachine.Host{
Name: "ice",
DriverName: "adriver",
HostOptions: &libmachine.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "ice"},
}
node3 :=
&libmachine.Host{
Name: "air",
DriverName: "nodriver",
HostOptions: &libmachine.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "air"},
}
node4 :=
&libmachine.Host{
Name: "water",
DriverName: "falsedriver",
HostOptions: &libmachine.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "water"},
}
hosts := []*libmachine.Host{node1, node2, node3, node4}
expected := []*libmachine.Host{node1, node2, node3}
assert.EqualValues(t, filterHosts(hosts, opts), expected)
}
func TestFilterHostsMultiFlags(t *testing.T) {
opts := FilterOptions{
SwarmName: []string{},

View File

@ -31,6 +31,7 @@ The currently supported filters are:
* driver (driver name)
* swarm (swarm master's name)
* state (`Running|Paused|Saved|Stopped|Stopping|Starting|Error`)
* name (Machine name returned by driver, supports golang style (https://github.com/google/re2/wiki/Syntax) regular expressions in machine name)
## Examples

View File

@ -8,6 +8,7 @@ import (
type FakeDriver struct {
*drivers.BaseDriver
MockState state.State
MockName string
}
func (d *FakeDriver) DriverName() string {
@ -23,7 +24,7 @@ func (d *FakeDriver) GetURL() (string, error) {
}
func (d *FakeDriver) GetMachineName() string {
return ""
return d.MockName
}
func (d *FakeDriver) GetIP() (string, error) {

View File

@ -19,3 +19,21 @@ teardown () {
[ "$status" -eq 0 ]
[[ ${lines[1]} =~ "testmachine" ]]
}
@test "ls: filter on name" {
run mchine create -d none -url tcp://127.0.0.1:2375 testmachine
run mchine create -d none -url tcp://127.0.0.1:2375 testmachine2
run mchine create -d none -url tcp://127.0.0.1:2375 testmachine3
run mchine ls --filter name=testmachine2
[ "$status" -eq 0 ]
[[ ${lines[1]} =~ "testmachine2" ]]
}
@test "ls: filter on name with regex" {
run mchine create -d none -url tcp://127.0.0.1:2375 squirrel
run mchine create -d none -url tcp://127.0.0.1:2375 cat
run mchine create -d none -url tcp://127.0.0.1:2375 dog
run mchine ls --filter name=c.?t
[ "$status" -eq 0 ]
[[ ${lines[1]} =~ "cat" ]]
}