services/device/device: allow specification of excluded states from glob filter
This cl introduces the option of prefixing the value of the instance-state and
installation-state flags by '!', which means that the states listed are to be
excluded (act as a blacklist rather than the default whilelist).
Change-Id: Ic013337164578e30cbf057a6fc736d502910761e
diff --git a/services/device/device/doc.go b/services/device/device/doc.go
index d25dc90..76fa353 100644
--- a/services/device/device/doc.go
+++ b/services/device/device/doc.go
@@ -262,11 +262,14 @@
-installation-state=
If non-empty, specifies allowed installation states (all others installations
get filtered out). The value of the flag is a comma-separated list of values
- from among: [Active Uninstalled].
+ from among: [Active Uninstalled]. If the value is prefixed by '!', the list
+ acts as a blacklist (all matching installations get filtered out).
-instance-state=
If non-empty, specifies allowed instance states (all other instances get
filtered out). The value of the flag is a comma-separated list of values from
- among: [Launching Running Dying NotRunning Updating Deleted].
+ among: [Launching Running Dying NotRunning Updating Deleted]. If the value is
+ prefixed by '!', the list acts as a blacklist (all matching instances get
+ filtered out).
-only-installations=false
If set, only consider installations.
-only-instances=false
@@ -289,11 +292,14 @@
-installation-state=
If non-empty, specifies allowed installation states (all others installations
get filtered out). The value of the flag is a comma-separated list of values
- from among: [Active Uninstalled].
+ from among: [Active Uninstalled]. If the value is prefixed by '!', the list
+ acts as a blacklist (all matching installations get filtered out).
-instance-state=
If non-empty, specifies allowed instance states (all other instances get
filtered out). The value of the flag is a comma-separated list of values from
- among: [Launching Running Dying NotRunning Updating Deleted].
+ among: [Launching Running Dying NotRunning Updating Deleted]. If the value is
+ prefixed by '!', the list acts as a blacklist (all matching instances get
+ filtered out).
-only-installations=false
If set, only consider installations.
-only-instances=false
@@ -316,11 +322,14 @@
-installation-state=
If non-empty, specifies allowed installation states (all others installations
get filtered out). The value of the flag is a comma-separated list of values
- from among: [Active Uninstalled].
+ from among: [Active Uninstalled]. If the value is prefixed by '!', the list
+ acts as a blacklist (all matching installations get filtered out).
-instance-state=
If non-empty, specifies allowed instance states (all other instances get
filtered out). The value of the flag is a comma-separated list of values from
- among: [Launching Running Dying NotRunning Updating Deleted].
+ among: [Launching Running Dying NotRunning Updating Deleted]. If the value is
+ prefixed by '!', the list acts as a blacklist (all matching instances get
+ filtered out).
-only-installations=false
If set, only consider installations.
-only-instances=false
@@ -343,11 +352,14 @@
-installation-state=
If non-empty, specifies allowed installation states (all others installations
get filtered out). The value of the flag is a comma-separated list of values
- from among: [Active Uninstalled].
+ from among: [Active Uninstalled]. If the value is prefixed by '!', the list
+ acts as a blacklist (all matching installations get filtered out).
-instance-state=
If non-empty, specifies allowed instance states (all other instances get
filtered out). The value of the flag is a comma-separated list of values from
- among: [Launching Running Dying NotRunning Updating Deleted].
+ among: [Launching Running Dying NotRunning Updating Deleted]. If the value is
+ prefixed by '!', the list acts as a blacklist (all matching instances get
+ filtered out).
-only-installations=false
If set, only consider installations.
-only-instances=false
@@ -458,11 +470,14 @@
-installation-state=
If non-empty, specifies allowed installation states (all others installations
get filtered out). The value of the flag is a comma-separated list of values
- from among: [Active Uninstalled].
+ from among: [Active Uninstalled]. If the value is prefixed by '!', the list
+ acts as a blacklist (all matching installations get filtered out).
-instance-state=
If non-empty, specifies allowed instance states (all other instances get
filtered out). The value of the flag is a comma-separated list of values from
- among: [Launching Running Dying NotRunning Updating Deleted].
+ among: [Launching Running Dying NotRunning Updating Deleted]. If the value is
+ prefixed by '!', the list acts as a blacklist (all matching instances get
+ filtered out).
-only-installations=false
If set, only consider installations.
-only-instances=false
diff --git a/services/device/device/glob.go b/services/device/device/glob.go
index 711e76d..9f4ee8b 100644
--- a/services/device/device/glob.go
+++ b/services/device/device/glob.go
@@ -316,7 +316,10 @@
return results
}
-type genericStateFlag map[genericState]bool
+type genericStateFlag struct {
+ set map[genericState]bool
+ exclude bool
+}
// genericState interface is meant to abstract device.InstanceState and
// device.InstallationState. We only make use of the String method, but we
@@ -327,22 +330,30 @@
type genericState fmt.Stringer
func (f *genericStateFlag) apply(state genericState) bool {
- if len(*f) == 0 {
+ if len(f.set) == 0 {
return true
}
- return (*f)[state]
+ return f.exclude != f.set[state]
}
func (f *genericStateFlag) String() string {
- states := make([]string, 0, len(*f))
- for s := range *f {
+ states := make([]string, 0, len(f.set))
+ for s := range f.set {
states = append(states, s.String())
}
sort.Strings(states)
- return strings.Join(states, ",")
+ statesStr := strings.Join(states, ",")
+ if f.exclude {
+ return "!" + statesStr
+ }
+ return statesStr
}
func (f *genericStateFlag) fromString(s string, stateConstructor func(string) (genericState, error)) error {
+ if len(s) > 0 && s[0] == '!' {
+ f.exclude = true
+ s = s[1:]
+ }
states := strings.Split(s, ",")
for _, s := range states {
state, err := stateConstructor(s)
@@ -355,10 +366,10 @@
}
func (f *genericStateFlag) add(s genericState) {
- if *f == nil {
- *f = make(genericStateFlag)
+ if f.set == nil {
+ f.set = make(map[genericState]bool)
}
- (*f)[s] = true
+ f.set[s] = true
}
type instanceStateFlag struct {
@@ -378,6 +389,12 @@
return
}
+func ExcludeInstanceStates(states ...device.InstanceState) instanceStateFlag {
+ f := InstanceStates(states...)
+ f.exclude = true
+ return f
+}
+
type installationStateFlag struct {
genericStateFlag
}
@@ -395,6 +412,12 @@
return
}
+func ExcludeInstallationStates(states ...device.InstallationState) installationStateFlag {
+ f := InstallationStates(states...)
+ f.exclude = true
+ return f
+}
+
type parallelismFlag int
const (
@@ -467,8 +490,8 @@
}
func defineGlobFlags(fs *flag.FlagSet, s *GlobSettings) {
- fs.Var(&s.InstanceStateFilter, "instance-state", fmt.Sprintf("If non-empty, specifies allowed instance states (all other instances get filtered out). The value of the flag is a comma-separated list of values from among: %v.", device.InstanceStateAll))
- fs.Var(&s.InstallationStateFilter, "installation-state", fmt.Sprintf("If non-empty, specifies allowed installation states (all others installations get filtered out). The value of the flag is a comma-separated list of values from among: %v.", device.InstallationStateAll))
+ fs.Var(&s.InstanceStateFilter, "instance-state", fmt.Sprintf("If non-empty, specifies allowed instance states (all other instances get filtered out). The value of the flag is a comma-separated list of values from among: %v. If the value is prefixed by '!', the list acts as a blacklist (all matching instances get filtered out).", device.InstanceStateAll))
+ fs.Var(&s.InstallationStateFilter, "installation-state", fmt.Sprintf("If non-empty, specifies allowed installation states (all others installations get filtered out). The value of the flag is a comma-separated list of values from among: %v. If the value is prefixed by '!', the list acts as a blacklist (all matching installations get filtered out).", device.InstallationStateAll))
fs.BoolVar(&s.OnlyInstances, "only-instances", false, "If set, only consider instances.")
fs.BoolVar(&s.OnlyInstallations, "only-installations", false, "If set, only consider installations.")
var parallelismValues []string
diff --git a/services/device/device/glob_test.go b/services/device/device/glob_test.go
index 084bd77..eceae7c 100644
--- a/services/device/device/glob_test.go
+++ b/services/device/device/glob_test.go
@@ -287,6 +287,39 @@
"",
"",
},
+ // Verifies "instance state" filter with more than 1 state.
+ {
+ simplePrintHandler,
+ allGlobResponses,
+ allStatusResponses,
+ cmd_device.GlobSettings{InstanceStateFilter: cmd_device.InstanceStates(device.InstanceStateUpdating, device.InstanceStateRunning)},
+ allGlobArgs,
+ joinLines(app2Out, app4Out, app7Out, app1Out, app3Out, app9Out, app6Out, app8Out),
+ "",
+ "",
+ },
+ // Verifies "instance state" filter with excluded state.
+ {
+ simplePrintHandler,
+ allGlobResponses,
+ allStatusResponses,
+ cmd_device.GlobSettings{InstanceStateFilter: cmd_device.ExcludeInstanceStates(device.InstanceStateUpdating)},
+ allGlobArgs,
+ joinLines(app2Out, app4Out, app7Out, app1Out, app5Out, app6Out, app8Out),
+ "",
+ "",
+ },
+ // Verifies "instance state" filter with more than 1 excluded state.
+ {
+ simplePrintHandler,
+ allGlobResponses,
+ allStatusResponses,
+ cmd_device.GlobSettings{InstanceStateFilter: cmd_device.ExcludeInstanceStates(device.InstanceStateUpdating, device.InstanceStateRunning)},
+ allGlobArgs,
+ joinLines(app2Out, app4Out, app7Out, app5Out, app6Out, app8Out),
+ "",
+ "",
+ },
// Verifies "installation state" filter.
{
simplePrintHandler,
@@ -298,6 +331,39 @@
"",
"",
},
+ // Verifies "installation state" filter with more than 1 state.
+ {
+ simplePrintHandler,
+ allGlobResponses,
+ allStatusResponses,
+ cmd_device.GlobSettings{InstallationStateFilter: cmd_device.InstallationStates(device.InstallationStateActive, device.InstallationStateUninstalled)},
+ allGlobArgs,
+ joinLines(app2Out, app4Out, app7Out, app1Out, app3Out, app5Out, app9Out, app6Out, app8Out),
+ "",
+ "",
+ },
+ // Verifies "installation state" filter with excluded state.
+ {
+ simplePrintHandler,
+ allGlobResponses,
+ allStatusResponses,
+ cmd_device.GlobSettings{InstallationStateFilter: cmd_device.ExcludeInstallationStates(device.InstallationStateActive)},
+ allGlobArgs,
+ joinLines(app2Out, app1Out, app3Out, app5Out, app9Out, app6Out, app8Out),
+ "",
+ "",
+ },
+ // Verifies "installation state" filter with more than 1 excluded state.
+ {
+ simplePrintHandler,
+ allGlobResponses,
+ allStatusResponses,
+ cmd_device.GlobSettings{InstallationStateFilter: cmd_device.ExcludeInstallationStates(device.InstallationStateActive, device.InstallationStateUninstalled)},
+ allGlobArgs,
+ joinLines(app1Out, app3Out, app5Out, app9Out, app6Out, app8Out),
+ "",
+ "",
+ },
// Verifies "installation state" filter + "only installations" filter.
{
simplePrintHandler,