mirror of https://github.com/docker/docs.git
Merge pull request #2464 from askb/2310_filter_engine_label
Fixes issue #2310 - docker-machine ls --filter with engine label
This commit is contained in:
commit
a729e3e2f6
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/machine/libmachine"
|
"github.com/docker/machine/libmachine"
|
||||||
"github.com/docker/machine/libmachine/drivers"
|
"github.com/docker/machine/libmachine/drivers"
|
||||||
|
"github.com/docker/machine/libmachine/engine"
|
||||||
"github.com/docker/machine/libmachine/host"
|
"github.com/docker/machine/libmachine/host"
|
||||||
"github.com/docker/machine/libmachine/log"
|
"github.com/docker/machine/libmachine/log"
|
||||||
"github.com/docker/machine/libmachine/persist"
|
"github.com/docker/machine/libmachine/persist"
|
||||||
|
@ -30,6 +31,7 @@ type FilterOptions struct {
|
||||||
DriverName []string
|
DriverName []string
|
||||||
State []string
|
State []string
|
||||||
Name []string
|
Name []string
|
||||||
|
Labels []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type HostListItem struct {
|
type HostListItem struct {
|
||||||
|
@ -39,6 +41,7 @@ type HostListItem struct {
|
||||||
State state.State
|
State state.State
|
||||||
URL string
|
URL string
|
||||||
SwarmOptions *swarm.Options
|
SwarmOptions *swarm.Options
|
||||||
|
EngineOptions *engine.Options
|
||||||
Error string
|
Error string
|
||||||
DockerVersion string
|
DockerVersion string
|
||||||
}
|
}
|
||||||
|
@ -125,6 +128,8 @@ func parseFilters(filters []string) (FilterOptions, error) {
|
||||||
options.State = append(options.State, value)
|
options.State = append(options.State, value)
|
||||||
case "name":
|
case "name":
|
||||||
options.Name = append(options.Name, value)
|
options.Name = append(options.Name, value)
|
||||||
|
case "label":
|
||||||
|
options.Labels = append(options.Labels, value)
|
||||||
default:
|
default:
|
||||||
return options, fmt.Errorf("Unsupported filter key '%s'", key)
|
return options, fmt.Errorf("Unsupported filter key '%s'", key)
|
||||||
}
|
}
|
||||||
|
@ -136,7 +141,8 @@ func filterHosts(hosts []*host.Host, filters FilterOptions) []*host.Host {
|
||||||
if len(filters.SwarmName) == 0 &&
|
if len(filters.SwarmName) == 0 &&
|
||||||
len(filters.DriverName) == 0 &&
|
len(filters.DriverName) == 0 &&
|
||||||
len(filters.State) == 0 &&
|
len(filters.State) == 0 &&
|
||||||
len(filters.Name) == 0 {
|
len(filters.Name) == 0 &&
|
||||||
|
len(filters.Labels) == 0 {
|
||||||
return hosts
|
return hosts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,8 +173,9 @@ func filterHost(host *host.Host, filters FilterOptions, swarmMasters map[string]
|
||||||
driverMatches := matchesDriverName(host, filters.DriverName)
|
driverMatches := matchesDriverName(host, filters.DriverName)
|
||||||
stateMatches := matchesState(host, filters.State)
|
stateMatches := matchesState(host, filters.State)
|
||||||
nameMatches := matchesName(host, filters.Name)
|
nameMatches := matchesName(host, filters.Name)
|
||||||
|
labelMatches := matchesLabel(host, filters.Labels)
|
||||||
|
|
||||||
return swarmMatches && driverMatches && stateMatches && nameMatches
|
return swarmMatches && driverMatches && stateMatches && nameMatches && labelMatches
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchesSwarmName(host *host.Host, swarmNames []string, swarmMasters map[string]string) bool {
|
func matchesSwarmName(host *host.Host, swarmNames []string, swarmMasters map[string]string) bool {
|
||||||
|
@ -230,6 +237,29 @@ func matchesName(host *host.Host, names []string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func matchesLabel(host *host.Host, labels []string) bool {
|
||||||
|
if len(labels) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var englabels = make(map[string]string, len(host.HostOptions.EngineOptions.Labels))
|
||||||
|
|
||||||
|
if host.HostOptions != nil && host.HostOptions.EngineOptions.Labels != nil {
|
||||||
|
for _, s := range host.HostOptions.EngineOptions.Labels {
|
||||||
|
kv := strings.SplitN(s, "=", 2)
|
||||||
|
englabels[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range labels {
|
||||||
|
kv := strings.SplitN(l, "=", 2)
|
||||||
|
if val, exists := englabels[kv[0]]; exists && strings.EqualFold(val, kv[1]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
|
func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
|
||||||
url := ""
|
url := ""
|
||||||
hostError := ""
|
hostError := ""
|
||||||
|
@ -256,8 +286,10 @@ func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var swarmOptions *swarm.Options
|
var swarmOptions *swarm.Options
|
||||||
|
var engineOptions *engine.Options
|
||||||
if h.HostOptions != nil {
|
if h.HostOptions != nil {
|
||||||
swarmOptions = h.HostOptions.SwarmOptions
|
swarmOptions = h.HostOptions.SwarmOptions
|
||||||
|
engineOptions = h.HostOptions.EngineOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
stateQueryChan <- HostListItem{
|
stateQueryChan <- HostListItem{
|
||||||
|
@ -267,6 +299,7 @@ func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
|
||||||
State: currentState,
|
State: currentState,
|
||||||
URL: url,
|
URL: url,
|
||||||
SwarmOptions: swarmOptions,
|
SwarmOptions: swarmOptions,
|
||||||
|
EngineOptions: engineOptions,
|
||||||
DockerVersion: dockerVersion,
|
DockerVersion: dockerVersion,
|
||||||
Error: hostError,
|
Error: hostError,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/docker/machine/drivers/fakedriver"
|
"github.com/docker/machine/drivers/fakedriver"
|
||||||
|
"github.com/docker/machine/libmachine/engine"
|
||||||
"github.com/docker/machine/libmachine/host"
|
"github.com/docker/machine/libmachine/host"
|
||||||
"github.com/docker/machine/libmachine/mcndockerclient"
|
"github.com/docker/machine/libmachine/mcndockerclient"
|
||||||
"github.com/docker/machine/libmachine/state"
|
"github.com/docker/machine/libmachine/state"
|
||||||
|
@ -41,14 +42,20 @@ func TestParseFiltersName(t *testing.T) {
|
||||||
assert.Equal(t, actual, FilterOptions{Name: []string{"dev"}})
|
assert.Equal(t, actual, FilterOptions{Name: []string{"dev"}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseFiltersLabel(t *testing.T) {
|
||||||
|
actual, err := parseFilters([]string{"label=com.example.foo=bar"})
|
||||||
|
assert.EqualValues(t, actual, FilterOptions{Labels: []string{"com.example.foo=bar"}})
|
||||||
|
assert.Nil(t, err, "returned err value must be Nil")
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseFiltersAll(t *testing.T) {
|
func TestParseFiltersAll(t *testing.T) {
|
||||||
actual, _ := parseFilters([]string{"swarm=foo", "driver=bar", "state=Stopped", "name=dev"})
|
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"}})
|
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}, DriverName: []string{"bar"}, State: []string{"Stopped"}, Name: []string{"dev"}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseFiltersAllCase(t *testing.T) {
|
func TestParseFiltersAllCase(t *testing.T) {
|
||||||
actual, err := parseFilters([]string{"sWarM=foo", "DrIver=bar", "StaTe=Stopped", "NAMe=dev"})
|
actual, err := parseFilters([]string{"sWarM=foo", "DrIver=bar", "StaTe=Stopped", "NAMe=dev", "LABEL=com=foo"})
|
||||||
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}, DriverName: []string{"bar"}, State: []string{"Stopped"}, Name: []string{"dev"}})
|
assert.Equal(t, actual, FilterOptions{SwarmName: []string{"foo"}, DriverName: []string{"bar"}, State: []string{"Stopped"}, Name: []string{"dev"}, Labels: []string{"com=foo"}})
|
||||||
assert.Nil(t, err, "err should be nil")
|
assert.Nil(t, err, "err should be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +74,7 @@ func TestFilterHostsReturnsFiltersValuesCaseInsensitive(t *testing.T) {
|
||||||
SwarmName: []string{"fOo"},
|
SwarmName: []string{"fOo"},
|
||||||
DriverName: []string{"ViRtUaLboX"},
|
DriverName: []string{"ViRtUaLboX"},
|
||||||
State: []string{"StOPpeD"},
|
State: []string{"StOPpeD"},
|
||||||
|
Labels: []string{"com.EXAMPLE.app=FOO"},
|
||||||
}
|
}
|
||||||
hosts := []*host.Host{}
|
hosts := []*host.Host{}
|
||||||
actual := filterHosts(hosts, opts)
|
actual := filterHosts(hosts, opts)
|
||||||
|
@ -85,6 +93,25 @@ func TestFilterHostsReturnsSameGivenNoFilters(t *testing.T) {
|
||||||
assert.EqualValues(t, actual, hosts)
|
assert.EqualValues(t, actual, hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFilterHostsReturnSetLabel(t *testing.T) {
|
||||||
|
opts := FilterOptions{
|
||||||
|
Labels: []string{"com.class.foo=bar"},
|
||||||
|
}
|
||||||
|
hosts := []*host.Host{
|
||||||
|
{
|
||||||
|
Name: "testhost",
|
||||||
|
DriverName: "fakedriver",
|
||||||
|
HostOptions: &host.Options{
|
||||||
|
EngineOptions: &engine.Options{
|
||||||
|
Labels: []string{"com.class.foo=bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
actual := filterHosts(hosts, opts)
|
||||||
|
assert.EqualValues(t, actual, hosts)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFilterHostsReturnsEmptyGivenEmptyHosts(t *testing.T) {
|
func TestFilterHostsReturnsEmptyGivenEmptyHosts(t *testing.T) {
|
||||||
opts := FilterOptions{
|
opts := FilterOptions{
|
||||||
SwarmName: []string{"foo"},
|
SwarmName: []string{"foo"},
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
load ${BASE_TEST_DIR}/helpers.bash
|
load ${BASE_TEST_DIR}/helpers.bash
|
||||||
|
|
||||||
setup () {
|
setup () {
|
||||||
|
machine create -d none --url none --engine-label app=1 testmachine5
|
||||||
|
machine create -d none --url none --engine-label foo=bar --engine-label app=1 testmachine4
|
||||||
machine create -d none --url none testmachine3
|
machine create -d none --url none testmachine3
|
||||||
machine create -d none --url none testmachine2
|
machine create -d none --url none testmachine2
|
||||||
machine create -d none --url none testmachine
|
machine create -d none --url none testmachine
|
||||||
|
@ -19,19 +21,42 @@ bootstrap_swarm () {
|
||||||
machine create -d none --url tcp://127.0.0.1:2375 --swarm --swarm-discovery token://deadbeef testswarm3
|
machine create -d none --url tcp://127.0.0.1:2375 --swarm --swarm-discovery token://deadbeef testswarm3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "ls: filter on label 'machine ls --filter label=foo=bar'" {
|
||||||
|
run machine ls --filter label=foo=bar
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ ${#lines[@]} == 2 ]]
|
||||||
|
[[ ${lines[1]} =~ "testmachine4" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ls: mutiple filters on label 'machine ls --filter label=foo=bar --filter label=app=1'" {
|
||||||
|
run machine ls --filter label=foo=bar --filter label=app=1
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ ${#lines[@]} == 3 ]]
|
||||||
|
[[ ${lines[1]} =~ "testmachine4" ]]
|
||||||
|
[[ ${lines[2]} =~ "testmachine5" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ls: non-existing filter on label 'machine ls --filter label=invalid=filter'" {
|
||||||
|
run machine ls --filter label=invalid=filter
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ ${#lines[@]} == 1 ]]
|
||||||
|
}
|
||||||
|
|
||||||
@test "ls: filter on driver 'machine ls --filter driver=none'" {
|
@test "ls: filter on driver 'machine ls --filter driver=none'" {
|
||||||
run machine ls --filter driver=none
|
run machine ls --filter driver=none
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ ${#lines[@]} == 4 ]]
|
[[ ${#lines[@]} == 6 ]]
|
||||||
[[ ${lines[1]} =~ "testmachine" ]]
|
[[ ${lines[1]} =~ "testmachine" ]]
|
||||||
[[ ${lines[2]} =~ "testmachine2" ]]
|
[[ ${lines[2]} =~ "testmachine2" ]]
|
||||||
[[ ${lines[3]} =~ "testmachine3" ]]
|
[[ ${lines[3]} =~ "testmachine3" ]]
|
||||||
|
[[ ${lines[4]} =~ "testmachine4" ]]
|
||||||
|
[[ ${lines[5]} =~ "testmachine5" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "ls: filter on driver 'machine ls -q --filter driver=none'" {
|
@test "ls: filter on driver 'machine ls -q --filter driver=none'" {
|
||||||
run machine ls -q --filter driver=none
|
run machine ls -q --filter driver=none
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ ${#lines[@]} == 3 ]]
|
[[ ${#lines[@]} == 5 ]]
|
||||||
[[ ${lines[0]} == "testmachine" ]]
|
[[ ${lines[0]} == "testmachine" ]]
|
||||||
[[ ${lines[1]} == "testmachine2" ]]
|
[[ ${lines[1]} == "testmachine2" ]]
|
||||||
[[ ${lines[2]} == "testmachine3" ]]
|
[[ ${lines[2]} == "testmachine3" ]]
|
||||||
|
@ -41,7 +66,7 @@ bootstrap_swarm () {
|
||||||
# Default state for 'none' driver is "Running"
|
# Default state for 'none' driver is "Running"
|
||||||
run machine ls --filter state="Running"
|
run machine ls --filter state="Running"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ ${#lines[@]} == 4 ]]
|
[[ ${#lines[@]} == 6 ]]
|
||||||
[[ ${lines[1]} =~ "testmachine" ]]
|
[[ ${lines[1]} =~ "testmachine" ]]
|
||||||
[[ ${lines[2]} =~ "testmachine2" ]]
|
[[ ${lines[2]} =~ "testmachine2" ]]
|
||||||
[[ ${lines[3]} =~ "testmachine3" ]]
|
[[ ${lines[3]} =~ "testmachine3" ]]
|
||||||
|
@ -73,7 +98,7 @@ bootstrap_swarm () {
|
||||||
@test "ls: filter on state 'machine ls -q --filter state=\"Running\"'" {
|
@test "ls: filter on state 'machine ls -q --filter state=\"Running\"'" {
|
||||||
run machine ls -q --filter state="Running"
|
run machine ls -q --filter state="Running"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ ${#lines[@]} == 3 ]]
|
[[ ${#lines[@]} == 5 ]]
|
||||||
[[ ${lines[0]} == "testmachine" ]]
|
[[ ${lines[0]} == "testmachine" ]]
|
||||||
[[ ${lines[1]} == "testmachine2" ]]
|
[[ ${lines[1]} == "testmachine2" ]]
|
||||||
[[ ${lines[2]} == "testmachine3" ]]
|
[[ ${lines[2]} == "testmachine3" ]]
|
||||||
|
@ -96,7 +121,7 @@ bootstrap_swarm () {
|
||||||
@test "ls: filter on name with regex 'machine ls --filter name=\"^t.*e\"'" {
|
@test "ls: filter on name with regex 'machine ls --filter name=\"^t.*e\"'" {
|
||||||
run machine ls --filter name="^t.*e"
|
run machine ls --filter name="^t.*e"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ ${#lines[@]} == 4 ]]
|
[[ ${#lines[@]} == 6 ]]
|
||||||
[[ ${lines[1]} =~ "testmachine" ]]
|
[[ ${lines[1]} =~ "testmachine" ]]
|
||||||
[[ ${lines[2]} =~ "testmachine2" ]]
|
[[ ${lines[2]} =~ "testmachine2" ]]
|
||||||
[[ ${lines[3]} =~ "testmachine3" ]]
|
[[ ${lines[3]} =~ "testmachine3" ]]
|
||||||
|
@ -105,7 +130,7 @@ bootstrap_swarm () {
|
||||||
@test "ls: filter on name with regex 'machine ls -q --filter name=\"^t.*e\"'" {
|
@test "ls: filter on name with regex 'machine ls -q --filter name=\"^t.*e\"'" {
|
||||||
run machine ls -q --filter name="^t.*e"
|
run machine ls -q --filter name="^t.*e"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ ${#lines[@]} == 3 ]]
|
[[ ${#lines[@]} == 5 ]]
|
||||||
[[ ${lines[0]} == "testmachine" ]]
|
[[ ${lines[0]} == "testmachine" ]]
|
||||||
[[ ${lines[1]} == "testmachine2" ]]
|
[[ ${lines[1]} == "testmachine2" ]]
|
||||||
[[ ${lines[2]} == "testmachine3" ]]
|
[[ ${lines[2]} == "testmachine3" ]]
|
||||||
|
|
Loading…
Reference in New Issue