func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { var driverConfig JavaDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Proceed to download an artifact to be executed. path, err := getter.GetArtifact( taskDir, driverConfig.ArtifactSource, driverConfig.Checksum, d.logger, ) if err != nil { return nil, err } jarName := filepath.Base(path) args := []string{} // Look for jvm options if len(driverConfig.JvmOpts) != 0 { d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", driverConfig.JvmOpts) args = append(args, driverConfig.JvmOpts...) } // Build the argument list. args = append(args, "-jar", jarName) if len(driverConfig.Args) != 0 { args = append(args, driverConfig.Args...) } // Setup the command // Assumes Java is in the $PATH, but could probably be detected execCtx := executor.NewExecutorContext(d.taskEnv) cmd := executor.Command(execCtx, "java", args...) // Populate environment variables cmd.Command().Env = d.taskEnv.EnvList() if err := cmd.Limit(task.Resources); err != nil { return nil, fmt.Errorf("failed to constrain resources: %s", err) } if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil { return nil, fmt.Errorf("failed to configure task directory: %v", err) } if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start source: %v", err) } // Return a driver handle h := &javaHandle{ cmd: cmd, killTimeout: d.DriverContext.KillTimeout(task), logger: d.logger, doneCh: make(chan struct{}), waitCh: make(chan *cstructs.WaitResult, 1), } go h.run() return h, nil }
func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { var driverConfig ExecDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } // Get the tasks local directory. taskName := d.DriverContext.taskName taskDir, ok := ctx.AllocDir.TaskDirs[taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Get the command to be ran command := driverConfig.Command if err := validateCommand(command, "args"); err != nil { return nil, err } // Check if an artificat is specified and attempt to download it source, ok := task.Config["artifact_source"] if ok && source != "" { // Proceed to download an artifact to be executed. _, err := getter.GetArtifact( taskDir, driverConfig.ArtifactSource, driverConfig.Checksum, d.logger, ) if err != nil { return nil, err } } bin, err := discover.NomadExecutable() if err != nil { return nil, fmt.Errorf("unable to find the nomad binary: %v", err) } pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name)) pluginConfig := &plugin.ClientConfig{ Cmd: exec.Command(bin, "executor", pluginLogFile), } exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) if err != nil { return nil, err } executorCtx := &executor.ExecutorContext{ TaskEnv: d.taskEnv, AllocDir: ctx.AllocDir, TaskName: task.Name, TaskResources: task.Resources, LogConfig: task.LogConfig, } ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx) if err != nil { pluginClient.Kill() return nil, fmt.Errorf("error starting process via the plugin: %v", err) } d.logger.Printf("[DEBUG] driver.raw_exec: started process with pid: %v", ps.Pid) // Return a driver handle h := &rawExecHandle{ pluginClient: pluginClient, executor: exec, userPid: ps.Pid, killTimeout: d.DriverContext.KillTimeout(task), allocDir: ctx.AllocDir, version: d.config.Version, logger: d.logger, doneCh: make(chan struct{}), waitCh: make(chan *cstructs.WaitResult, 1), } go h.run() return h, nil }
// Run an existing Qemu image. Start() will pull down an existing, valid Qemu // image and save it to the Drivers Allocation Dir func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { var driverConfig QemuDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } if len(driverConfig.PortMap) > 1 { return nil, fmt.Errorf("Only one port_map block is allowed in the qemu driver config") } // Get the image source source, ok := task.Config["artifact_source"] if !ok || source == "" { return nil, fmt.Errorf("Missing source image Qemu driver") } // Qemu defaults to 128M of RAM for a given VM. Instead, we force users to // supply a memory size in the tasks resources if task.Resources == nil || task.Resources.MemoryMB == 0 { return nil, fmt.Errorf("Missing required Task Resource: Memory") } // Get the tasks local directory. taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Proceed to download an artifact to be executed. vmPath, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), driverConfig.ArtifactSource, driverConfig.Checksum, d.logger, ) if err != nil { return nil, err } vmID := filepath.Base(vmPath) // Parse configuration arguments // Create the base arguments accelerator := "tcg" if driverConfig.Accelerator != "" { accelerator = driverConfig.Accelerator } // TODO: Check a lower bounds, e.g. the default 128 of Qemu mem := fmt.Sprintf("%dM", task.Resources.MemoryMB) args := []string{ "qemu-system-x86_64", "-machine", "type=pc,accel=" + accelerator, "-name", vmID, "-m", mem, "-drive", "file=" + vmPath, "-nodefconfig", "-nodefaults", "-nographic", } // Check the Resources required Networks to add port mappings. If no resources // are required, we assume the VM is a purely compute job and does not require // the outside world to be able to reach it. VMs ran without port mappings can // still reach out to the world, but without port mappings it is effectively // firewalled protocols := []string{"udp", "tcp"} if len(task.Resources.Networks) > 0 && len(driverConfig.PortMap) == 1 { // Loop through the port map and construct the hostfwd string, to map // reserved ports to the ports listenting in the VM // Ex: hostfwd=tcp::22000-:22,hostfwd=tcp::80-:8080 var forwarding []string taskPorts := task.Resources.Networks[0].MapLabelToValues(nil) for label, guest := range driverConfig.PortMap[0] { host, ok := taskPorts[label] if !ok { return nil, fmt.Errorf("Unknown port label %q", label) } for _, p := range protocols { forwarding = append(forwarding, fmt.Sprintf("hostfwd=%s::%d-:%d", p, host, guest)) } } if len(forwarding) != 0 { args = append(args, "-netdev", fmt.Sprintf("user,id=user.0,%s", strings.Join(forwarding, ",")), "-device", "virtio-net,netdev=user.0", ) } } // If using KVM, add optimization args if accelerator == "kvm" { args = append(args, "-enable-kvm", "-cpu", "host", // Do we have cores information available to the Driver? // "-smp", fmt.Sprintf("%d", cores), ) } // Setup the command cmd := executor.NewBasicExecutor() executor.SetCommand(cmd, args[0], args[1:]) if err := cmd.Limit(task.Resources); err != nil { return nil, fmt.Errorf("failed to constrain resources: %s", err) } if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil { return nil, fmt.Errorf("failed to configure task directory: %v", err) } d.logger.Printf("[DEBUG] Starting QemuVM command: %q", strings.Join(args, " ")) if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start command: %v", err) } d.logger.Printf("[INFO] Started new QemuVM: %s", vmID) // Create and Return Handle h := &qemuHandle{ cmd: cmd, killTimeout: d.DriverContext.KillTimeout(task), logger: d.logger, doneCh: make(chan struct{}), waitCh: make(chan *cstructs.WaitResult, 1), } go h.run() return h, nil }
func (r *TaskRunner) run() { // Predeclare things so we an jump to the RESTART var handleEmpty bool for { // Download the task's artifacts if !r.artifactsDownloaded && len(r.task.Artifacts) > 0 { r.setState(structs.TaskStatePending, structs.NewTaskEvent(structs.TaskDownloadingArtifacts)) taskDir, ok := r.ctx.AllocDir.TaskDirs[r.task.Name] if !ok { err := fmt.Errorf("task directory couldn't be found") r.setState(structs.TaskStateDead, structs.NewTaskEvent(structs.TaskDriverFailure).SetDriverError(err)) r.logger.Printf("[ERR] client: task directory for alloc %q task %q couldn't be found", r.alloc.ID, r.task.Name) r.restartTracker.SetStartError(err) goto RESTART } for _, artifact := range r.task.Artifacts { if err := getter.GetArtifact(r.taskEnv, artifact, taskDir, r.logger); err != nil { r.setState(structs.TaskStateDead, structs.NewTaskEvent(structs.TaskArtifactDownloadFailed).SetDownloadError(err)) r.restartTracker.SetStartError(cstructs.NewRecoverableError(err, true)) goto RESTART } } r.artifactsDownloaded = true } // Start the task if not yet started or it is being forced. This logic // is necessary because in the case of a restore the handle already // exists. r.handleLock.Lock() handleEmpty = r.handle == nil r.handleLock.Unlock() if handleEmpty { startErr := r.startTask() r.restartTracker.SetStartError(startErr) if startErr != nil { r.setState(structs.TaskStateDead, structs.NewTaskEvent(structs.TaskDriverFailure).SetDriverError(startErr)) goto RESTART } } // Mark the task as started r.setState(structs.TaskStateRunning, structs.NewTaskEvent(structs.TaskStarted)) // Wait for updates WAIT: for { select { case waitRes := <-r.handle.WaitCh(): if waitRes == nil { panic("nil wait") } // Log whether the task was successful or not. r.restartTracker.SetWaitResult(waitRes) r.setState(structs.TaskStateDead, r.waitErrorToEvent(waitRes)) if !waitRes.Successful() { r.logger.Printf("[INFO] client: task %q for alloc %q failed: %v", r.task.Name, r.alloc.ID, waitRes) } else { r.logger.Printf("[INFO] client: task %q for alloc %q completed successfully", r.task.Name, r.alloc.ID) } break WAIT case update := <-r.updateCh: if err := r.handleUpdate(update); err != nil { r.logger.Printf("[ERR] client: update to task %q failed: %v", r.task.Name, err) } case <-r.destroyCh: // Kill the task using an exponential backoff in-case of failures. destroySuccess, err := r.handleDestroy() if !destroySuccess { // We couldn't successfully destroy the resource created. r.logger.Printf("[ERR] client: failed to kill task %q. Resources may have been leaked: %v", r.task.Name, err) } // Store that the task has been destroyed and any associated error. r.setState(structs.TaskStateDead, structs.NewTaskEvent(structs.TaskKilled).SetKillError(err)) return } } RESTART: state, when := r.restartTracker.GetState() r.restartTracker.SetStartError(nil).SetWaitResult(nil) reason := r.restartTracker.GetReason() switch state { case structs.TaskNotRestarting, structs.TaskTerminated: r.logger.Printf("[INFO] client: Not restarting task: %v for alloc: %v ", r.task.Name, r.alloc.ID) if state == structs.TaskNotRestarting { r.setState(structs.TaskStateDead, structs.NewTaskEvent(structs.TaskNotRestarting). SetRestartReason(reason)) } return case structs.TaskRestarting: r.logger.Printf("[INFO] client: Restarting task %q for alloc %q in %v", r.task.Name, r.alloc.ID, when) r.setState(structs.TaskStatePending, structs.NewTaskEvent(structs.TaskRestarting). SetRestartDelay(when). SetRestartReason(reason)) default: r.logger.Printf("[ERR] client: restart tracker returned unknown state: %q", state) return } // Sleep but watch for destroy events. select { case <-time.After(when): case <-r.destroyCh: } // Destroyed while we were waiting to restart, so abort. r.destroyLock.Lock() destroyed := r.destroy r.destroyLock.Unlock() if destroyed { r.logger.Printf("[DEBUG] client: Not restarting task: %v because it's destroyed by user", r.task.Name) r.setState(structs.TaskStateDead, structs.NewTaskEvent(structs.TaskKilled)) return } // Clear the handle so a new driver will be created. r.handleLock.Lock() r.handle = nil r.handleLock.Unlock() } }
func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { var driverConfig ExecDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } // Get the tasks local directory. taskName := d.DriverContext.taskName taskDir, ok := ctx.AllocDir.TaskDirs[taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Get the command to be ran command := driverConfig.Command if command == "" { return nil, fmt.Errorf("missing command for Raw Exec driver") } // Check if an artificat is specified and attempt to download it source, ok := task.Config["artifact_source"] if ok && source != "" { // Proceed to download an artifact to be executed. _, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), driverConfig.ArtifactSource, driverConfig.Checksum, d.logger, ) if err != nil { return nil, err } } // Setup the command execCtx := executor.NewExecutorContext(d.taskEnv) cmd := executor.NewBasicExecutor(execCtx) executor.SetCommand(cmd, command, driverConfig.Args) if err := cmd.Limit(task.Resources); err != nil { return nil, fmt.Errorf("failed to constrain resources: %s", err) } // Populate environment variables cmd.Command().Env = d.taskEnv.EnvList() if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil { return nil, fmt.Errorf("failed to configure task directory: %v", err) } if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start command: %v", err) } // Return a driver handle h := &execHandle{ cmd: cmd, killTimeout: d.DriverContext.KillTimeout(task), logger: d.logger, doneCh: make(chan struct{}), waitCh: make(chan *cstructs.WaitResult, 1), } go h.run() return h, nil }
// prestart handles life-cycle tasks that occur before the task has started. func (r *TaskRunner) prestart(resultCh chan bool) { if r.task.Vault != nil { // Wait for the token r.logger.Printf("[DEBUG] client: waiting for Vault token for task %v in alloc %q", r.task.Name, r.alloc.ID) tokenCh := r.vaultFuture.Wait() select { case <-tokenCh: case <-r.waitCh: resultCh <- false return } r.logger.Printf("[DEBUG] client: retrieved Vault token for task %v in alloc %q", r.task.Name, r.alloc.ID) } if err := r.setTaskEnv(); err != nil { r.setState( structs.TaskStateDead, structs.NewTaskEvent(structs.TaskSetupFailure).SetSetupError(err).SetFailsTask()) resultCh <- false return } for { // Download the task's artifacts if !r.artifactsDownloaded && len(r.task.Artifacts) > 0 { r.setState(structs.TaskStatePending, structs.NewTaskEvent(structs.TaskDownloadingArtifacts)) for _, artifact := range r.task.Artifacts { if err := getter.GetArtifact(r.getTaskEnv(), artifact, r.taskDir); err != nil { wrapped := fmt.Errorf("failed to download artifact %q: %v", artifact.GetterSource, err) r.setState(structs.TaskStatePending, structs.NewTaskEvent(structs.TaskArtifactDownloadFailed).SetDownloadError(wrapped)) r.restartTracker.SetStartError(structs.NewRecoverableError(wrapped, true)) goto RESTART } } r.artifactsDownloaded = true } // We don't have to wait for any template if len(r.task.Templates) == 0 { // Send the start signal select { case r.startCh <- struct{}{}: default: } resultCh <- true return } // Build the template manager if r.templateManager == nil { var err error r.templateManager, err = NewTaskTemplateManager(r, r.task.Templates, r.config, r.vaultFuture.Get(), r.taskDir, r.getTaskEnv()) if err != nil { err := fmt.Errorf("failed to build task's template manager: %v", err) r.setState(structs.TaskStateDead, structs.NewTaskEvent(structs.TaskSetupFailure).SetSetupError(err).SetFailsTask()) r.logger.Printf("[ERR] client: alloc %q, task %q %v", r.alloc.ID, r.task.Name, err) resultCh <- false return } } // Block for consul-template // TODO Hooks should register themselves as blocking and then we can // perioidcally enumerate what we are still blocked on select { case <-r.unblockCh: // Send the start signal select { case r.startCh <- struct{}{}: default: } resultCh <- true return case <-r.waitCh: // The run loop has exited so exit too resultCh <- false return } RESTART: restart := r.shouldRestart() if !restart { resultCh <- false return } } }
func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Proceed to download an artifact to be executed. path, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), task.Config["artifact_source"], task.Config["checksum"], d.logger, ) if err != nil { return nil, err } jarName := filepath.Base(path) // Get the environment variables. envVars := TaskEnvironmentVariables(ctx, task) args := []string{} // Look for jvm options jvm_options, ok := task.Config["jvm_options"] if ok && jvm_options != "" { d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", jvm_options) args = append(args, jvm_options) } // Build the argument list. args = append(args, "-jar", filepath.Join(allocdir.TaskLocal, jarName)) if argRaw, ok := task.Config["args"]; ok { args = append(args, argRaw) } // Setup the command // Assumes Java is in the $PATH, but could probably be detected cmd := executor.Command("java", args...) // Populate environment variables cmd.Command().Env = envVars.List() if err := cmd.Limit(task.Resources); err != nil { return nil, fmt.Errorf("failed to constrain resources: %s", err) } if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil { return nil, fmt.Errorf("failed to configure task directory: %v", err) } if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start source: %v", err) } // Return a driver handle h := &javaHandle{ cmd: cmd, doneCh: make(chan struct{}), waitCh: make(chan error, 1), } go h.run() return h, nil }
func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { var driverConfig JavaDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Proceed to download an artifact to be executed. path, err := getter.GetArtifact( taskDir, driverConfig.ArtifactSource, driverConfig.Checksum, d.logger, ) if err != nil { return nil, err } jarName := filepath.Base(path) args := []string{} // Look for jvm options if len(driverConfig.JvmOpts) != 0 { d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", driverConfig.JvmOpts) args = append(args, driverConfig.JvmOpts...) } // Build the argument list. args = append(args, "-jar", jarName) if len(driverConfig.Args) != 0 { args = append(args, driverConfig.Args...) } bin, err := discover.NomadExecutable() if err != nil { return nil, fmt.Errorf("unable to find the nomad binary: %v", err) } pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name)) pluginConfig := &plugin.ClientConfig{ Cmd: exec.Command(bin, "executor", pluginLogFile), } exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) if err != nil { return nil, err } executorCtx := &executor.ExecutorContext{ TaskEnv: d.taskEnv, AllocDir: ctx.AllocDir, TaskName: task.Name, TaskResources: task.Resources, FSIsolation: true, ResourceLimits: true, UnprivilegedUser: true, } ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: "java", Args: args}, executorCtx) if err != nil { pluginClient.Kill() return nil, fmt.Errorf("error starting process via the plugin: %v", err) } d.logger.Printf("[DEBUG] driver.java: started process with pid: %v", ps.Pid) // Return a driver handle h := &javaHandle{ pluginClient: pluginClient, executor: exec, userPid: ps.Pid, isolationConfig: ps.IsolationConfig, taskDir: taskDir, allocDir: ctx.AllocDir, killTimeout: d.DriverContext.KillTimeout(task), logger: d.logger, doneCh: make(chan struct{}), waitCh: make(chan *cstructs.WaitResult, 1), } go h.run() return h, nil }
func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { // Get the command to be ran command, ok := task.Config["command"] if !ok || command == "" { return nil, fmt.Errorf("missing command for exec driver") } // Create a location to download the artifact. taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Check if an artificat is specified and attempt to download it source, ok := task.Config["artifact_source"] if ok && source != "" { // Proceed to download an artifact to be executed. _, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), task.Config["artifact_source"], task.Config["checksum"], d.logger, ) if err != nil { return nil, err } } // Get the environment variables. envVars := TaskEnvironmentVariables(ctx, task) // Look for arguments var args []string if argRaw, ok := task.Config["args"]; ok { args = append(args, argRaw) } // Setup the command cmd := executor.Command(command, args...) if err := cmd.Limit(task.Resources); err != nil { return nil, fmt.Errorf("failed to constrain resources: %s", err) } // Populate environment variables cmd.Command().Env = envVars.List() if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil { return nil, fmt.Errorf("failed to configure task directory: %v", err) } if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start command: %v", err) } // Return a driver handle h := &execHandle{ cmd: cmd, doneCh: make(chan struct{}), waitCh: make(chan error, 1), } go h.run() return h, nil }
// Run an existing Qemu image. Start() will pull down an existing, valid Qemu // image and save it to the Drivers Allocation Dir func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { // Get the image source source, ok := task.Config["artifact_source"] if !ok || source == "" { return nil, fmt.Errorf("Missing source image Qemu driver") } // Qemu defaults to 128M of RAM for a given VM. Instead, we force users to // supply a memory size in the tasks resources if task.Resources == nil || task.Resources.MemoryMB == 0 { return nil, fmt.Errorf("Missing required Task Resource: Memory") } // Get the tasks local directory. taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) } // Proceed to download an artifact to be executed. vmPath, err := getter.GetArtifact( filepath.Join(taskDir, allocdir.TaskLocal), task.Config["artifact_source"], task.Config["checksum"], d.logger, ) if err != nil { return nil, err } vmID := filepath.Base(vmPath) // Parse configuration arguments // Create the base arguments accelerator := "tcg" if acc, ok := task.Config["accelerator"]; ok { accelerator = acc } // TODO: Check a lower bounds, e.g. the default 128 of Qemu mem := fmt.Sprintf("%dM", task.Resources.MemoryMB) args := []string{ "qemu-system-x86_64", "-machine", "type=pc,accel=" + accelerator, "-name", vmID, "-m", mem, "-drive", "file=" + vmPath, "-nodefconfig", "-nodefaults", "-nographic", } // Check the Resources required Networks to add port mappings. If no resources // are required, we assume the VM is a purely compute job and does not require // the outside world to be able to reach it. VMs ran without port mappings can // still reach out to the world, but without port mappings it is effectively // firewalled if len(task.Resources.Networks) > 0 { // TODO: Consolidate these into map of host/guest port when we have HCL // Note: Host port must be open and available // Get and split guest ports. The guest_ports configuration must match up with // the Reserved ports in the Task Resources // Users can supply guest_hosts as a list of posts to map on the guest vm. // These map 1:1 with the requested Reserved Ports from the hostmachine. ports := strings.Split(task.Config["guest_ports"], ",") if len(ports) == 0 { return nil, fmt.Errorf("[ERR] driver.qemu: Error parsing required Guest Ports") } // TODO: support more than a single, default Network if len(ports) != len(task.Resources.Networks[0].ReservedPorts) { return nil, fmt.Errorf("[ERR] driver.qemu: Error matching Guest Ports with Reserved ports") } // Loop through the reserved ports and construct the hostfwd string, to map // reserved ports to the ports listenting in the VM // Ex: // hostfwd=tcp::22000-:22,hostfwd=tcp::80-:8080 reservedPorts := task.Resources.Networks[0].ReservedPorts var forwarding string for i, p := range ports { forwarding = fmt.Sprintf("%s,hostfwd=tcp::%s-:%s", forwarding, strconv.Itoa(reservedPorts[i]), p) } if "" == forwarding { return nil, fmt.Errorf("[ERR] driver.qemu: Error constructing port forwarding") } args = append(args, "-netdev", fmt.Sprintf("user,id=user.0%s", forwarding), "-device", "virtio-net,netdev=user.0", ) } // If using KVM, add optimization args if accelerator == "kvm" { args = append(args, "-enable-kvm", "-cpu", "host", // Do we have cores information available to the Driver? // "-smp", fmt.Sprintf("%d", cores), ) } // Setup the command cmd := executor.Command(args[0], args[1:]...) if err := cmd.Limit(task.Resources); err != nil { return nil, fmt.Errorf("failed to constrain resources: %s", err) } if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil { return nil, fmt.Errorf("failed to configure task directory: %v", err) } d.logger.Printf("[DEBUG] Starting QemuVM command: %q", strings.Join(args, " ")) if err := cmd.Start(); err != nil { return nil, fmt.Errorf("failed to start command: %v", err) } d.logger.Printf("[INFO] Started new QemuVM: %s", vmID) // Create and Return Handle h := &qemuHandle{ cmd: cmd, doneCh: make(chan struct{}), waitCh: make(chan error, 1), } go h.run() return h, nil }