diff --git a/client/driver/docker.go b/client/driver/docker.go index 2f0af7068f67..0874cd9703dd 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -29,6 +29,7 @@ import ( "github.com/hashicorp/nomad/client/driver/executor" dstructs "github.com/hashicorp/nomad/client/driver/structs" cstructs "github.com/hashicorp/nomad/client/structs" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/helper/fields" shelpers "github.com/hashicorp/nomad/helper/stats" "github.com/hashicorp/nomad/nomad/structs" @@ -108,6 +109,10 @@ type DockerDriver struct { driverConfig *DockerDriverConfig imageID string + + // A tri-state boolean to know if the fingerprinting has happened and + // whether it has been successful + fingerprintSuccess *bool } type DockerDriverAuth struct { @@ -262,6 +267,48 @@ func NewDockerDriver(ctx *DriverContext) Driver { return &DockerDriver{DriverContext: *ctx} } +func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { + // Initialize docker API clients + client, _, err := d.dockerClients() + if err != nil { + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { + d.logger.Printf("[INFO] driver.docker: failed to initialize client: %s", err) + } + delete(node.Attributes, dockerDriverAttr) + d.fingerprintSuccess = helper.BoolToPtr(false) + return false, nil + } + + // This is the first operation taken on the client so we'll try to + // establish a connection to the Docker daemon. If this fails it means + // Docker isn't available so we'll simply disable the docker driver. + env, err := client.Version() + if err != nil { + delete(node.Attributes, dockerDriverAttr) + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { + d.logger.Printf("[DEBUG] driver.docker: could not connect to docker daemon at %s: %s", client.Endpoint(), err) + } + d.fingerprintSuccess = helper.BoolToPtr(false) + return false, nil + } + + node.Attributes[dockerDriverAttr] = "1" + node.Attributes["driver.docker.version"] = env.Get("Version") + + privileged := d.config.ReadBoolDefault(dockerPrivilegedConfigOption, false) + if privileged { + node.Attributes[dockerPrivilegedConfigOption] = "1" + } + + // Advertise if this node supports Docker volumes + if d.config.ReadBoolDefault(dockerVolumesConfigOption, dockerVolumesConfigDefault) { + node.Attributes["driver."+dockerVolumesConfigOption] = "1" + } + + d.fingerprintSuccess = helper.BoolToPtr(true) + return true, nil +} + // Validate is used to validate the driver configuration func (d *DockerDriver) Validate(config map[string]interface{}) error { fd := &fields.FieldData{ @@ -618,49 +665,6 @@ func (d *DockerDriver) dockerClients() (*docker.Client, *docker.Client, error) { return client, waitClient, merr.ErrorOrNil() } -func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // Get the current status so that we can log any debug messages only if the - // state changes - _, currentlyEnabled := node.Attributes[dockerDriverAttr] - - // Initialize docker API clients - client, _, err := d.dockerClients() - if err != nil { - delete(node.Attributes, dockerDriverAttr) - if currentlyEnabled { - d.logger.Printf("[INFO] driver.docker: failed to initialize client: %s", err) - } - return false, nil - } - - privileged := d.config.ReadBoolDefault(dockerPrivilegedConfigOption, false) - if privileged { - node.Attributes[dockerPrivilegedConfigOption] = "1" - } - - // This is the first operation taken on the client so we'll try to - // establish a connection to the Docker daemon. If this fails it means - // Docker isn't available so we'll simply disable the docker driver. - env, err := client.Version() - if err != nil { - if currentlyEnabled { - d.logger.Printf("[DEBUG] driver.docker: could not connect to docker daemon at %s: %s", client.Endpoint(), err) - } - delete(node.Attributes, dockerDriverAttr) - return false, nil - } - - node.Attributes[dockerDriverAttr] = "1" - node.Attributes["driver.docker.version"] = env.Get("Version") - - // Advertise if this node supports Docker volumes - if d.config.ReadBoolDefault(dockerVolumesConfigOption, dockerVolumesConfigDefault) { - node.Attributes["driver."+dockerVolumesConfigOption] = "1" - } - - return true, nil -} - func (d *DockerDriver) containerBinds(driverConfig *DockerDriverConfig, taskDir *allocdir.TaskDir, task *structs.Task) ([]string, error) { diff --git a/client/driver/exec.go b/client/driver/exec.go index 1a1b7457fdf2..da1256be6edc 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -29,6 +29,10 @@ const ( // features. type ExecDriver struct { DriverContext + + // A tri-state boolean to know if the fingerprinting has happened and + // whether it has been successful + fingerprintSuccess *bool } type ExecDriverConfig struct { diff --git a/client/driver/exec_default.go b/client/driver/exec_default.go index 176938726587..2f1e267870fa 100644 --- a/client/driver/exec_default.go +++ b/client/driver/exec_default.go @@ -4,9 +4,11 @@ package driver import ( "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/nomad/structs" ) func (d *ExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { + d.fingerprintSuccess = helper.BoolToPtr(false) return false, nil } diff --git a/client/driver/exec_linux.go b/client/driver/exec_linux.go index 6c43b5119ab1..d48e0f57fdff 100644 --- a/client/driver/exec_linux.go +++ b/client/driver/exec_linux.go @@ -2,33 +2,33 @@ package driver import ( "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/nomad/structs" "golang.org/x/sys/unix" ) func (d *ExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // Get the current status so that we can log any debug messages only if the - // state changes - _, currentlyEnabled := node.Attributes[execDriverAttr] - // Only enable if cgroups are available and we are root - if _, ok := node.Attributes["unique.cgroup.mountpoint"]; !ok { - if currentlyEnabled { + if !cgroupsMounted(node) { + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { d.logger.Printf("[DEBUG] driver.exec: cgroups unavailable, disabling") } + d.fingerprintSuccess = helper.BoolToPtr(false) delete(node.Attributes, execDriverAttr) return false, nil } else if unix.Geteuid() != 0 { - if currentlyEnabled { + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { d.logger.Printf("[DEBUG] driver.exec: must run as root user, disabling") } delete(node.Attributes, execDriverAttr) + d.fingerprintSuccess = helper.BoolToPtr(false) return false, nil } - if !currentlyEnabled { + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { d.logger.Printf("[DEBUG] driver.exec: exec driver is enabled") } node.Attributes[execDriverAttr] = "1" + d.fingerprintSuccess = helper.BoolToPtr(true) return true, nil } diff --git a/client/driver/java.go b/client/driver/java.go index 0a5240f81360..bbe03f2e65b1 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -23,6 +23,7 @@ import ( dstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" cstructs "github.com/hashicorp/nomad/client/structs" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/helper/fields" "github.com/hashicorp/nomad/nomad/structs" ) @@ -38,6 +39,10 @@ const ( type JavaDriver struct { DriverContext fingerprint.StaticFingerprinter + + // A tri-state boolean to know if the fingerprinting has happened and + // whether it has been successful + fingerprintSuccess *bool } type JavaDriverConfig struct { @@ -105,16 +110,13 @@ func (d *JavaDriver) Abilities() DriverAbilities { } func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // Get the current status so that we can log any debug messages only if the - // state changes - _, currentlyEnabled := node.Attributes[javaDriverAttr] - // Only enable if we are root and cgroups are mounted when running on linux systems. - if runtime.GOOS == "linux" && (syscall.Geteuid() != 0 || !d.cgroupsMounted(node)) { - if currentlyEnabled { + if runtime.GOOS == "linux" && (syscall.Geteuid() != 0 || !cgroupsMounted(node)) { + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { d.logger.Printf("[DEBUG] driver.java: root priviledges and mounted cgroups required on linux, disabling") } delete(node.Attributes, "driver.java") + d.fingerprintSuccess = helper.BoolToPtr(false) return false, nil } @@ -128,6 +130,7 @@ func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, if err != nil { // assume Java wasn't found delete(node.Attributes, javaDriverAttr) + d.fingerprintSuccess = helper.BoolToPtr(false) return false, nil } @@ -143,10 +146,11 @@ func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, } if infoString == "" { - if currentlyEnabled { + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { d.logger.Println("[WARN] driver.java: error parsing Java version information, aborting") } delete(node.Attributes, javaDriverAttr) + d.fingerprintSuccess = helper.BoolToPtr(false) return false, nil } @@ -163,6 +167,7 @@ func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, node.Attributes["driver.java.version"] = versionString node.Attributes["driver.java.runtime"] = info[1] node.Attributes["driver.java.vm"] = info[2] + d.fingerprintSuccess = helper.BoolToPtr(true) return true, nil } @@ -295,13 +300,6 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, func (d *JavaDriver) Cleanup(*ExecContext, *CreatedResources) error { return nil } -// cgroupsMounted returns true if the cgroups are mounted on a system otherwise -// returns false -func (d *JavaDriver) cgroupsMounted(node *structs.Node) bool { - _, ok := node.Attributes["unique.cgroup.mountpoint"] - return ok -} - type javaId struct { Version string KillTimeout time.Duration diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 746740829208..f4d2c1d06b76 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -105,10 +105,6 @@ func (d *QemuDriver) FSIsolation() cstructs.FSIsolation { } func (d *QemuDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // Get the current status so that we can log any debug messages only if the - // state changes - _, currentlyEnabled := node.Attributes[qemuDriverAttr] - bin := "qemu-system-x86_64" if runtime.GOOS == "windows" { // On windows, the "qemu-system-x86_64" command does not respond to the @@ -128,9 +124,6 @@ func (d *QemuDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, return false, fmt.Errorf("Unable to parse Qemu version string: %#v", matches) } - if !currentlyEnabled { - d.logger.Printf("[DEBUG] driver.qemu: enabling driver") - } node.Attributes[qemuDriverAttr] = "1" node.Attributes["driver.qemu.version"] = matches[1] return true, nil diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index e7dbe2bb2f42..802de073cc69 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -87,17 +87,11 @@ func (d *RawExecDriver) FSIsolation() cstructs.FSIsolation { } func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // Get the current status so that we can log any debug messages only if the - // state changes - _, currentlyEnabled := node.Attributes[rawExecDriverAttr] - // Check that the user has explicitly enabled this executor. enabled := cfg.ReadBoolDefault(rawExecConfigOption, false) if enabled || cfg.DevMode { - if currentlyEnabled { - d.logger.Printf("[WARN] driver.raw_exec: raw exec is enabled. Only enable if needed") - } + d.logger.Printf("[WARN] driver.raw_exec: raw exec is enabled. Only enable if needed") node.Attributes[rawExecDriverAttr] = "1" return true, nil } diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 116659729302..2fdb00b863f1 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/nomad/client/driver/executor" dstructs "github.com/hashicorp/nomad/client/driver/structs" cstructs "github.com/hashicorp/nomad/client/structs" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/helper/fields" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/mapstructure" @@ -57,6 +58,10 @@ const ( // planned in the future type RktDriver struct { DriverContext + + // A tri-state boolean to know if the fingerprinting has happened and + // whether it has been successful + fingerprintSuccess *bool } type RktDriverConfig struct { @@ -157,22 +162,20 @@ func (d *RktDriver) Abilities() DriverAbilities { } func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // Get the current status so that we can log any debug messages only if the - // state changes - _, currentlyEnabled := node.Attributes[rktDriverAttr] - // Only enable if we are root when running on non-windows systems. if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { - if currentlyEnabled { + if d.fingerprintSuccess == nil || *d.fingerprintSuccess { d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling") } delete(node.Attributes, rktDriverAttr) + d.fingerprintSuccess = helper.BoolToPtr(false) return false, nil } outBytes, err := exec.Command(rktCmd, "version").Output() if err != nil { delete(node.Attributes, rktDriverAttr) + d.fingerprintSuccess = helper.BoolToPtr(false) return false, nil } out := strings.TrimSpace(string(outBytes)) @@ -181,6 +184,7 @@ func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, e appcMatches := reAppcVersion.FindStringSubmatch(out) if len(rktMatches) != 2 || len(appcMatches) != 2 { delete(node.Attributes, rktDriverAttr) + d.fingerprintSuccess = helper.BoolToPtr(false) return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) } @@ -200,6 +204,7 @@ func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, e if d.config.ReadBoolDefault(rktVolumesConfigOption, rktVolumesConfigDefault) { node.Attributes["driver."+rktVolumesConfigOption] = "1" } + d.fingerprintSuccess = helper.BoolToPtr(true) return true, nil } diff --git a/client/driver/utils.go b/client/driver/utils.go index 51de500a8b5c..7e1c79890bb7 100644 --- a/client/driver/utils.go +++ b/client/driver/utils.go @@ -19,6 +19,13 @@ import ( "github.com/hashicorp/nomad/nomad/structs" ) +// cgroupsMounted returns true if the cgroups are mounted on a system otherwise +// returns false +func cgroupsMounted(node *structs.Node) bool { + _, ok := node.Attributes["unique.cgroup.mountpoint"] + return ok +} + // createExecutor launches an executor plugin and returns an instance of the // Executor interface func createExecutor(w io.Writer, clientConfig *config.Config,