diff --git a/driver.go b/driver.go index 6fdb8dbcfdf00543d882b0a6a5a22de6c552cd51..02f4b82015b4f08b85e77b378b48d759c3af1c40 100644 --- a/driver.go +++ b/driver.go @@ -243,6 +243,10 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { // SetConfigFromFlags handles additional driver arguments as retrieved by [Driver.GetCreateFlags]; // see [drivers.Driver.SetConfigFromFlags] func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error { + return d.setConfigFromFlags(opts) +} + +func (d *Driver) setConfigFromFlagsImpl(opts drivers.DriverOptions) error { d.AccessToken = opts.String(flagAPIToken) d.Image = opts.String(flagImage) d.ImageID = opts.Int(flagImageID) @@ -267,7 +271,7 @@ func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error { d.placementGroup = opts.String(flagPlacementGroup) if opts.Bool(flagAutoSpread) { if d.placementGroup != "" { - return errors.Errorf(flagAutoSpread + " and " + flagPlacementGroup + " are mutually exclusive") + return d.flagFailure("%v and %v are mutually exclusive", flagAutoSpread, flagPlacementGroup) } d.placementGroup = autoSpreadPgName } @@ -280,17 +284,17 @@ func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error { d.SetSwarmConfigFromFlags(opts) if d.AccessToken == "" { - return errors.Errorf("hetzner requires --%v to be set", flagAPIToken) + return d.flagFailure("hetzner requires --%v to be set", flagAPIToken) } if d.ImageID != 0 && d.Image != "" && d.Image != defaultImage /* support legacy behaviour */ { - return errors.Errorf("--%v and --%v are mutually exclusive", flagImage, flagImageID) + return d.flagFailure("--%v and --%v are mutually exclusive", flagImage, flagImageID) } else if d.ImageID == 0 && d.Image == "" { d.Image = defaultImage } if d.DisablePublic4 && d.DisablePublic6 && !d.UsePrivateNetwork { - return errors.Errorf("--%v must be used if public networking is disabled (hint: implicitly set by --%v)", + return d.flagFailure("--%v must be used if public networking is disabled (hint: implicitly set by --%v)", flagUsePrivateNetwork, flagDisablePublic) } @@ -312,7 +316,7 @@ func (d *Driver) setLabelsFromFlags(opts drivers.DriverOptions) error { for _, label := range opts.StringSlice(flagServerLabel) { split := strings.SplitN(label, "=", 2) if len(split) != 2 { - return errors.Errorf("server label %v is not in key=value format", label) + return d.flagFailure("server label %v is not in key=value format", label) } d.ServerLabels[split[0]] = split[1] } @@ -331,7 +335,7 @@ func (d *Driver) setLabelsFromFlags(opts drivers.DriverOptions) error { func (d *Driver) PreCreateCheck() error { if d.IsExistingKey { if d.originalKey == "" { - return errors.New("specifying an existing key ID requires the existing key path to be set as well") + return d.flagFailure("specifying an existing key ID requires the existing key path to be set as well") } key, err := d.getKey() diff --git a/flag_failure.go b/flag_failure.go new file mode 100644 index 0000000000000000000000000000000000000000..b58ded33caf3b300aaa3172b5798823e16bc4f20 --- /dev/null +++ b/flag_failure.go @@ -0,0 +1,16 @@ +//go:build !flag_debug + +package main + +import ( + "github.com/docker/machine/libmachine/drivers" + "github.com/pkg/errors" +) + +func (d *Driver) flagFailure(format string, args ...interface{}) error { + return errors.Errorf(format, args...) +} + +func (d *Driver) setConfigFromFlags(opts drivers.DriverOptions) error { + return d.setConfigFromFlagsImpl(opts) +} diff --git a/flag_failure_debug.go b/flag_failure_debug.go new file mode 100644 index 0000000000000000000000000000000000000000..3d25f404d222bcc61fcf981c722912b9f9396644 --- /dev/null +++ b/flag_failure_debug.go @@ -0,0 +1,32 @@ +//go:build flag_debug + +package main + +import ( + "encoding/json" + "fmt" + + "github.com/docker/machine/libmachine/drivers" + "github.com/pkg/errors" +) + +var lastOpts drivers.DriverOptions + +func (d *Driver) flagFailure(format string, args ...interface{}) error { + // machine driver may not flush logs received when getting an RPC error, so we have to resort to this terribleness + line1 := fmt.Sprintf("Flag failure detected:\n -> last opts: %v\n -> driver state %v", lastOpts, d) + var line2 string + if out, err := json.MarshalIndent(d, "", " "); err == nil { + line2 = fmt.Sprintf(" -> driver json:\n%s", out) + } else { + line2 = fmt.Sprintf("could not encode driver json: %v", err) + } + + combined := append([]interface{}{line1, line2}, args...) + return errors.Errorf("%s\n%s\n"+format, combined...) +} + +func (d *Driver) setConfigFromFlags(opts drivers.DriverOptions) error { + lastOpts = opts + return d.setConfigFromFlagsImpl(opts) +}