Signed-off-by: Derek McGowan <derek@mcg.dev>
| ... | ... |
@@ -4,9 +4,9 @@ import ( |
| 4 | 4 |
"runtime" |
| 5 | 5 |
|
| 6 | 6 |
"github.com/docker/docker/daemon/config" |
| 7 |
+ dopts "github.com/docker/docker/daemon/internal/opts" |
|
| 7 | 8 |
"github.com/docker/docker/daemon/pkg/opts" |
| 8 | 9 |
"github.com/docker/docker/daemon/pkg/registry" |
| 9 |
- dopts "github.com/docker/docker/internal/opts" |
|
| 10 | 10 |
"github.com/spf13/pflag" |
| 11 | 11 |
) |
| 12 | 12 |
|
| ... | ... |
@@ -13,9 +13,9 @@ import ( |
| 13 | 13 |
|
| 14 | 14 |
"dario.cat/mergo" |
| 15 | 15 |
"github.com/containerd/log" |
| 16 |
+ dopts "github.com/docker/docker/daemon/internal/opts" |
|
| 16 | 17 |
"github.com/docker/docker/daemon/pkg/opts" |
| 17 | 18 |
"github.com/docker/docker/daemon/pkg/registry" |
| 18 |
- dopts "github.com/docker/docker/internal/opts" |
|
| 19 | 19 |
"github.com/moby/moby/api" |
| 20 | 20 |
"github.com/moby/moby/api/types/versions" |
| 21 | 21 |
"github.com/pkg/errors" |
| ... | ... |
@@ -4,8 +4,8 @@ import ( |
| 4 | 4 |
"net/netip" |
| 5 | 5 |
"testing" |
| 6 | 6 |
|
| 7 |
+ dopts "github.com/docker/docker/daemon/internal/opts" |
|
| 7 | 8 |
"github.com/docker/docker/daemon/pkg/opts" |
| 8 |
- dopts "github.com/docker/docker/internal/opts" |
|
| 9 | 9 |
"github.com/google/go-cmp/cmp/cmpopts" |
| 10 | 10 |
"github.com/moby/moby/api/types/container" |
| 11 | 11 |
"github.com/spf13/pflag" |
| 12 | 12 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,25 @@ |
| 0 |
+package opts |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "net/netip" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// ValidateHostGatewayIPs makes sure the addresses are valid, and there's at-most one IPv4 and one IPv6 address. |
|
| 8 |
+func ValidateHostGatewayIPs(hostGatewayIPs []netip.Addr) error {
|
|
| 9 |
+ var have4, have6 bool |
|
| 10 |
+ for _, ip := range hostGatewayIPs {
|
|
| 11 |
+ if ip.Is4() {
|
|
| 12 |
+ if have4 {
|
|
| 13 |
+ return errors.New("only one IPv4 host gateway IP address can be specified")
|
|
| 14 |
+ } |
|
| 15 |
+ have4 = true |
|
| 16 |
+ } else {
|
|
| 17 |
+ if have6 {
|
|
| 18 |
+ return errors.New("only one IPv6 host gateway IP address can be specified")
|
|
| 19 |
+ } |
|
| 20 |
+ have6 = true |
|
| 21 |
+ } |
|
| 22 |
+ } |
|
| 23 |
+ return nil |
|
| 24 |
+} |
| 0 | 25 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,48 @@ |
| 0 |
+package opts |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net/netip" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// NamedIPListOpts appends to an underlying []netip.Addr. |
|
| 8 |
+type NamedIPListOpts struct {
|
|
| 9 |
+ name string |
|
| 10 |
+ ips *[]netip.Addr |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// NewNamedIPListOptsRef constructs a NamedIPListOpts and returns its address. |
|
| 14 |
+func NewNamedIPListOptsRef(name string, values *[]netip.Addr) *NamedIPListOpts {
|
|
| 15 |
+ return &NamedIPListOpts{
|
|
| 16 |
+ name: name, |
|
| 17 |
+ ips: values, |
|
| 18 |
+ } |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// String returns a string representation of the addresses in the underlying []netip.Addr. |
|
| 22 |
+func (o *NamedIPListOpts) String() string {
|
|
| 23 |
+ if len(*o.ips) == 0 {
|
|
| 24 |
+ return "" |
|
| 25 |
+ } |
|
| 26 |
+ return fmt.Sprintf("%v", *o.ips)
|
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// Set converts value to a netip.Addr and appends it to the underlying []netip.Addr. |
|
| 30 |
+func (o *NamedIPListOpts) Set(value string) error {
|
|
| 31 |
+ ip, err := netip.ParseAddr(value) |
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ return err |
|
| 34 |
+ } |
|
| 35 |
+ *o.ips = append(*o.ips, ip) |
|
| 36 |
+ return nil |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// Type returns a string name for this Option type |
|
| 40 |
+func (o *NamedIPListOpts) Type() string {
|
|
| 41 |
+ return "list" |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// Name returns the name of the NamedIPListOpts in the configuration. |
|
| 45 |
+func (o *NamedIPListOpts) Name() string {
|
|
| 46 |
+ return o.name |
|
| 47 |
+} |
| 0 | 48 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,83 @@ |
| 0 |
+package opts |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "strconv" |
|
| 6 |
+ "strings" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/docker/daemon/pkg/opts" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+// SetOpts holds a map of values and a validation function. |
|
| 12 |
+type SetOpts struct {
|
|
| 13 |
+ values map[string]bool |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// Set validates if needed the input value and add it to the |
|
| 17 |
+// internal map, by splitting on '='. |
|
| 18 |
+func (opts *SetOpts) Set(value string) error {
|
|
| 19 |
+ k, v, found := strings.Cut(value, "=") |
|
| 20 |
+ if k == "" {
|
|
| 21 |
+ return errors.New("invalid option name: " + value)
|
|
| 22 |
+ } |
|
| 23 |
+ var isSet bool |
|
| 24 |
+ if !found {
|
|
| 25 |
+ isSet = true |
|
| 26 |
+ } else {
|
|
| 27 |
+ var err error |
|
| 28 |
+ isSet, err = strconv.ParseBool(v) |
|
| 29 |
+ if err != nil {
|
|
| 30 |
+ return err |
|
| 31 |
+ } |
|
| 32 |
+ } |
|
| 33 |
+ opts.values[k] = isSet |
|
| 34 |
+ return nil |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// GetAll returns the values of SetOpts as a map. |
|
| 38 |
+func (opts *SetOpts) GetAll() map[string]bool {
|
|
| 39 |
+ return opts.values |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func (opts *SetOpts) String() string {
|
|
| 43 |
+ return fmt.Sprintf("%v", opts.values)
|
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+// Type returns a string name for this Option type |
|
| 47 |
+func (opts *SetOpts) Type() string {
|
|
| 48 |
+ return "map" |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// NewSetOpts creates a new SetOpts with the specified set of values as a map of string to bool. |
|
| 52 |
+func NewSetOpts(values map[string]bool) *SetOpts {
|
|
| 53 |
+ if values == nil {
|
|
| 54 |
+ values = make(map[string]bool) |
|
| 55 |
+ } |
|
| 56 |
+ return &SetOpts{
|
|
| 57 |
+ values: values, |
|
| 58 |
+ } |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// NamedSetOpts is a SetOpts struct with a configuration name. |
|
| 62 |
+// This struct is useful to keep reference to the assigned |
|
| 63 |
+// field name in the internal configuration struct. |
|
| 64 |
+type NamedSetOpts struct {
|
|
| 65 |
+ SetOpts |
|
| 66 |
+ name string |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+var _ opts.NamedOption = &NamedSetOpts{}
|
|
| 70 |
+ |
|
| 71 |
+// NewNamedSetOpts creates a reference to a new NamedSetOpts struct. |
|
| 72 |
+func NewNamedSetOpts(name string, values map[string]bool) *NamedSetOpts {
|
|
| 73 |
+ return &NamedSetOpts{
|
|
| 74 |
+ SetOpts: *NewSetOpts(values), |
|
| 75 |
+ name: name, |
|
| 76 |
+ } |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Name returns the name of the NamedSetOpts in the configuration. |
|
| 80 |
+func (o *NamedSetOpts) Name() string {
|
|
| 81 |
+ return o.name |
|
| 82 |
+} |
| 0 | 83 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package opts |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "gotest.tools/v3/assert" |
|
| 6 |
+ is "gotest.tools/v3/assert/cmp" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func TestSetOpts(t *testing.T) {
|
|
| 10 |
+ tmpMap := make(map[string]bool) |
|
| 11 |
+ o := NewSetOpts(tmpMap) |
|
| 12 |
+ assert.NilError(t, o.Set("feature-a=1"))
|
|
| 13 |
+ assert.NilError(t, o.Set("feature-b=true"))
|
|
| 14 |
+ assert.NilError(t, o.Set("feature-c=0"))
|
|
| 15 |
+ assert.NilError(t, o.Set("feature-d=false"))
|
|
| 16 |
+ assert.NilError(t, o.Set("feature-e"))
|
|
| 17 |
+ |
|
| 18 |
+ expected := "map[feature-a:true feature-b:true feature-c:false feature-d:false feature-e:true]" |
|
| 19 |
+ assert.Check(t, is.Equal(expected, o.String())) |
|
| 20 |
+ |
|
| 21 |
+ expectedValue := map[string]bool{"feature-a": true, "feature-b": true, "feature-c": false, "feature-d": false, "feature-e": true}
|
|
| 22 |
+ assert.Check(t, is.DeepEqual(expectedValue, o.GetAll())) |
|
| 23 |
+ |
|
| 24 |
+ err := o.Set("feature=not-a-bool")
|
|
| 25 |
+ assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "not-a-bool": invalid syntax`)) |
|
| 26 |
+ err = o.Set("feature=")
|
|
| 27 |
+ assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "": invalid syntax`)) |
|
| 28 |
+ err = o.Set("=true")
|
|
| 29 |
+ assert.Check(t, is.Error(err, `invalid option name: =true`)) |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func TestNamedSetOpts(t *testing.T) {
|
|
| 33 |
+ tmpMap := make(map[string]bool) |
|
| 34 |
+ o := NewNamedSetOpts("features", tmpMap)
|
|
| 35 |
+ assert.Check(t, is.Equal("features", o.Name()))
|
|
| 36 |
+ |
|
| 37 |
+ assert.NilError(t, o.Set("feature-a=1"))
|
|
| 38 |
+ assert.NilError(t, o.Set("feature-b=true"))
|
|
| 39 |
+ assert.NilError(t, o.Set("feature-c=0"))
|
|
| 40 |
+ assert.NilError(t, o.Set("feature-d=false"))
|
|
| 41 |
+ assert.NilError(t, o.Set("feature-e"))
|
|
| 42 |
+ |
|
| 43 |
+ expected := "map[feature-a:true feature-b:true feature-c:false feature-d:false feature-e:true]" |
|
| 44 |
+ assert.Check(t, is.Equal(expected, o.String())) |
|
| 45 |
+ |
|
| 46 |
+ expectedValue := map[string]bool{"feature-a": true, "feature-b": true, "feature-c": false, "feature-d": false, "feature-e": true}
|
|
| 47 |
+ assert.Check(t, is.DeepEqual(expectedValue, o.GetAll())) |
|
| 48 |
+ |
|
| 49 |
+ err := o.Set("feature=not-a-bool")
|
|
| 50 |
+ assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "not-a-bool": invalid syntax`)) |
|
| 51 |
+ err = o.Set("feature=")
|
|
| 52 |
+ assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "": invalid syntax`)) |
|
| 53 |
+ err = o.Set("=true")
|
|
| 54 |
+ assert.Check(t, is.Error(err, `invalid option name: =true`)) |
|
| 55 |
+} |
| 0 | 56 |
deleted file mode 100644 |
| ... | ... |
@@ -1,25 +0,0 @@ |
| 1 |
-package opts |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "net/netip" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-// ValidateHostGatewayIPs makes sure the addresses are valid, and there's at-most one IPv4 and one IPv6 address. |
|
| 9 |
-func ValidateHostGatewayIPs(hostGatewayIPs []netip.Addr) error {
|
|
| 10 |
- var have4, have6 bool |
|
| 11 |
- for _, ip := range hostGatewayIPs {
|
|
| 12 |
- if ip.Is4() {
|
|
| 13 |
- if have4 {
|
|
| 14 |
- return errors.New("only one IPv4 host gateway IP address can be specified")
|
|
| 15 |
- } |
|
| 16 |
- have4 = true |
|
| 17 |
- } else {
|
|
| 18 |
- if have6 {
|
|
| 19 |
- return errors.New("only one IPv6 host gateway IP address can be specified")
|
|
| 20 |
- } |
|
| 21 |
- have6 = true |
|
| 22 |
- } |
|
| 23 |
- } |
|
| 24 |
- return nil |
|
| 25 |
-} |
| 26 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,48 +0,0 @@ |
| 1 |
-package opts |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "net/netip" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-// NamedIPListOpts appends to an underlying []netip.Addr. |
|
| 9 |
-type NamedIPListOpts struct {
|
|
| 10 |
- name string |
|
| 11 |
- ips *[]netip.Addr |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-// NewNamedIPListOptsRef constructs a NamedIPListOpts and returns its address. |
|
| 15 |
-func NewNamedIPListOptsRef(name string, values *[]netip.Addr) *NamedIPListOpts {
|
|
| 16 |
- return &NamedIPListOpts{
|
|
| 17 |
- name: name, |
|
| 18 |
- ips: values, |
|
| 19 |
- } |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-// String returns a string representation of the addresses in the underlying []netip.Addr. |
|
| 23 |
-func (o *NamedIPListOpts) String() string {
|
|
| 24 |
- if len(*o.ips) == 0 {
|
|
| 25 |
- return "" |
|
| 26 |
- } |
|
| 27 |
- return fmt.Sprintf("%v", *o.ips)
|
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 |
-// Set converts value to a netip.Addr and appends it to the underlying []netip.Addr. |
|
| 31 |
-func (o *NamedIPListOpts) Set(value string) error {
|
|
| 32 |
- ip, err := netip.ParseAddr(value) |
|
| 33 |
- if err != nil {
|
|
| 34 |
- return err |
|
| 35 |
- } |
|
| 36 |
- *o.ips = append(*o.ips, ip) |
|
| 37 |
- return nil |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-// Type returns a string name for this Option type |
|
| 41 |
-func (o *NamedIPListOpts) Type() string {
|
|
| 42 |
- return "list" |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-// Name returns the name of the NamedIPListOpts in the configuration. |
|
| 46 |
-func (o *NamedIPListOpts) Name() string {
|
|
| 47 |
- return o.name |
|
| 48 |
-} |
| 49 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,83 +0,0 @@ |
| 1 |
-package opts |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "fmt" |
|
| 6 |
- "strconv" |
|
| 7 |
- "strings" |
|
| 8 |
- |
|
| 9 |
- "github.com/docker/docker/daemon/pkg/opts" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-// SetOpts holds a map of values and a validation function. |
|
| 13 |
-type SetOpts struct {
|
|
| 14 |
- values map[string]bool |
|
| 15 |
-} |
|
| 16 |
- |
|
| 17 |
-// Set validates if needed the input value and add it to the |
|
| 18 |
-// internal map, by splitting on '='. |
|
| 19 |
-func (opts *SetOpts) Set(value string) error {
|
|
| 20 |
- k, v, found := strings.Cut(value, "=") |
|
| 21 |
- if k == "" {
|
|
| 22 |
- return errors.New("invalid option name: " + value)
|
|
| 23 |
- } |
|
| 24 |
- var isSet bool |
|
| 25 |
- if !found {
|
|
| 26 |
- isSet = true |
|
| 27 |
- } else {
|
|
| 28 |
- var err error |
|
| 29 |
- isSet, err = strconv.ParseBool(v) |
|
| 30 |
- if err != nil {
|
|
| 31 |
- return err |
|
| 32 |
- } |
|
| 33 |
- } |
|
| 34 |
- opts.values[k] = isSet |
|
| 35 |
- return nil |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-// GetAll returns the values of SetOpts as a map. |
|
| 39 |
-func (opts *SetOpts) GetAll() map[string]bool {
|
|
| 40 |
- return opts.values |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-func (opts *SetOpts) String() string {
|
|
| 44 |
- return fmt.Sprintf("%v", opts.values)
|
|
| 45 |
-} |
|
| 46 |
- |
|
| 47 |
-// Type returns a string name for this Option type |
|
| 48 |
-func (opts *SetOpts) Type() string {
|
|
| 49 |
- return "map" |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-// NewSetOpts creates a new SetOpts with the specified set of values as a map of string to bool. |
|
| 53 |
-func NewSetOpts(values map[string]bool) *SetOpts {
|
|
| 54 |
- if values == nil {
|
|
| 55 |
- values = make(map[string]bool) |
|
| 56 |
- } |
|
| 57 |
- return &SetOpts{
|
|
| 58 |
- values: values, |
|
| 59 |
- } |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-// NamedSetOpts is a SetOpts struct with a configuration name. |
|
| 63 |
-// This struct is useful to keep reference to the assigned |
|
| 64 |
-// field name in the internal configuration struct. |
|
| 65 |
-type NamedSetOpts struct {
|
|
| 66 |
- SetOpts |
|
| 67 |
- name string |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-var _ opts.NamedOption = &NamedSetOpts{}
|
|
| 71 |
- |
|
| 72 |
-// NewNamedSetOpts creates a reference to a new NamedSetOpts struct. |
|
| 73 |
-func NewNamedSetOpts(name string, values map[string]bool) *NamedSetOpts {
|
|
| 74 |
- return &NamedSetOpts{
|
|
| 75 |
- SetOpts: *NewSetOpts(values), |
|
| 76 |
- name: name, |
|
| 77 |
- } |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-// Name returns the name of the NamedSetOpts in the configuration. |
|
| 81 |
-func (o *NamedSetOpts) Name() string {
|
|
| 82 |
- return o.name |
|
| 83 |
-} |
| 84 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,56 +0,0 @@ |
| 1 |
-package opts |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "testing" |
|
| 5 |
- |
|
| 6 |
- "gotest.tools/v3/assert" |
|
| 7 |
- is "gotest.tools/v3/assert/cmp" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func TestSetOpts(t *testing.T) {
|
|
| 11 |
- tmpMap := make(map[string]bool) |
|
| 12 |
- o := NewSetOpts(tmpMap) |
|
| 13 |
- assert.NilError(t, o.Set("feature-a=1"))
|
|
| 14 |
- assert.NilError(t, o.Set("feature-b=true"))
|
|
| 15 |
- assert.NilError(t, o.Set("feature-c=0"))
|
|
| 16 |
- assert.NilError(t, o.Set("feature-d=false"))
|
|
| 17 |
- assert.NilError(t, o.Set("feature-e"))
|
|
| 18 |
- |
|
| 19 |
- expected := "map[feature-a:true feature-b:true feature-c:false feature-d:false feature-e:true]" |
|
| 20 |
- assert.Check(t, is.Equal(expected, o.String())) |
|
| 21 |
- |
|
| 22 |
- expectedValue := map[string]bool{"feature-a": true, "feature-b": true, "feature-c": false, "feature-d": false, "feature-e": true}
|
|
| 23 |
- assert.Check(t, is.DeepEqual(expectedValue, o.GetAll())) |
|
| 24 |
- |
|
| 25 |
- err := o.Set("feature=not-a-bool")
|
|
| 26 |
- assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "not-a-bool": invalid syntax`)) |
|
| 27 |
- err = o.Set("feature=")
|
|
| 28 |
- assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "": invalid syntax`)) |
|
| 29 |
- err = o.Set("=true")
|
|
| 30 |
- assert.Check(t, is.Error(err, `invalid option name: =true`)) |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-func TestNamedSetOpts(t *testing.T) {
|
|
| 34 |
- tmpMap := make(map[string]bool) |
|
| 35 |
- o := NewNamedSetOpts("features", tmpMap)
|
|
| 36 |
- assert.Check(t, is.Equal("features", o.Name()))
|
|
| 37 |
- |
|
| 38 |
- assert.NilError(t, o.Set("feature-a=1"))
|
|
| 39 |
- assert.NilError(t, o.Set("feature-b=true"))
|
|
| 40 |
- assert.NilError(t, o.Set("feature-c=0"))
|
|
| 41 |
- assert.NilError(t, o.Set("feature-d=false"))
|
|
| 42 |
- assert.NilError(t, o.Set("feature-e"))
|
|
| 43 |
- |
|
| 44 |
- expected := "map[feature-a:true feature-b:true feature-c:false feature-d:false feature-e:true]" |
|
| 45 |
- assert.Check(t, is.Equal(expected, o.String())) |
|
| 46 |
- |
|
| 47 |
- expectedValue := map[string]bool{"feature-a": true, "feature-b": true, "feature-c": false, "feature-d": false, "feature-e": true}
|
|
| 48 |
- assert.Check(t, is.DeepEqual(expectedValue, o.GetAll())) |
|
| 49 |
- |
|
| 50 |
- err := o.Set("feature=not-a-bool")
|
|
| 51 |
- assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "not-a-bool": invalid syntax`)) |
|
| 52 |
- err = o.Set("feature=")
|
|
| 53 |
- assert.Check(t, is.Error(err, `strconv.ParseBool: parsing "": invalid syntax`)) |
|
| 54 |
- err = o.Set("=true")
|
|
| 55 |
- assert.Check(t, is.Error(err, `invalid option name: =true`)) |
|
| 56 |
-} |