Circuit Self-managed infrastructure, programmatic monitoring and orchestration

Using processes

You can start an OS process on any host in your cluster by creating a new process element at an anchor of your choosing that is a descendant of the host's server anchor. The created process element becomes your interface to the underlying OS process.

Creating a process

Suppose the variable anchor holds an Anchor object, corresponding to a path in the anchor hierarchy that has no element attached to it. For instance, say we obtained anchor like this:

	anchor := root.Walk([]string{"Xe2ac4c8c83976ce6", "job", "demo"})

This anchor corresponds to the path /Xe2ac4c8c83976ce6/job/demo. (Read more on navigating anchors here.)

To create a new process element and attach it to anchor, we use the anchor's MakeProc method:

	MakeProc(cmd Cmd) (Proc, error)

MakeProc will start a new process on the host /Xe2ac4c8c83976ce6, as specified by the command parameter cmd. If successful, it will create a corresponding process element and attach it to the anchor. MakeProc returns the newly created process element (of type Proc) as well as an application error (of type error), or it panics if a system error occurs.

An application error can occur in one of two cases. Either the anchor already has another element attached to it, or the process execution was rejected by the host OS (due to a missing binary or insufficient permissions, for example).

MakeProc never blocks.

The command parameter, of type Cmd, specifies the standard POSIX-level execution parameters and an additional parameter called Scrub:

type Cmd struct {
	Env []string
	Dir string
	Path string
	Args []string
	Scrub bool
}

If Scrub is set, the process element will automatically be detached from the anchor and discarded, as soon as the underlying OS process exits. If Scrub is not set, the process element will remain attached to the anchor even after the underlying OS process dies. The latter regime is useful when one wants to start a job and return at a later time to check if the job has already completed and what was its exit status. Furthermore, removing process elements explicitly (rather than automatically) is a way of explicit accounting on the user's side. Thus this regime is particularly well suited for applications that control circuit processes programmatically (as opposed to manually).

Regardless of the setting of the Scrub parameter, the user can use the Scrub method to discard the process element at any point:

	Scrub()

A call to Scrub will detach the process element from its anchor and discard it, thereby freeing the anchor to attach other elements. If the underlying OS process is still running, ‘scrubbing’ will not terminate the process. (If OS process termination is desired, the user must explicitly send a kill signal to the process, using a Signal which is described later.)

Example

For instance, the following code executes the GNU list command:

	proc, err := a.MakeProc(
		cli.Cmd{
			Env: []string{"TERM=xterm"},
			Dir: "/",
			Path: "/bin/ls",
			Args: []string{"-l", "/"},
			Scrub: true,
		},
	)

The following picture tries to illustrate the relationship between the process element and the underlying OS process itself.

Process elements execute OS processes on behalf of the user.

Controlling the standard file descriptors of a process

After its invocation, MakeProc returns immediately, while the underlying OS process is executing on the host machine.

After a successful execution the user is obligated, by the POSIX standard, to take care of the standard input, output and error streams of the underlying process. (For instance, if the standard input is not written to or closed, or if the output is not read from, some programs will pause in waiting.)

The standard streams of the executed process can be retrieved with the following methods of the process element:

	Stdin() io.WriteCloser
	Stdout() io.ReadCloser
	Stderr() io.ReadCloser

It is allowed to close the standard output and error at any point into the stream. This will result in discarding all remaining data in the stream, without blocking the underlying process.

Eventually, the user is responsible for closing all standard streams otherwise the underlying process will block and not exit.

Sending signals and killing processes

You can send a POSIX signal to the underlying process at any point (asynchronously) using:

	Signal(sig string) error

The sig string must be one of the following recognized signal names: ABRT, ALRM, BUS, CHLD, CONT, FPE, HUP, ILL, INT, IO, IOT, KILL, PIPE, PROF, QUIT, SEGV, STOP, SYS, TERM, TRAP, TSTP, TTIN, TTOU, URG, USR1, USR2, VTALRM, WINCH, XCPU, XFSZ.

Querying the status of a process asynchronously

You can query the status of a process asynchronously, using:

	Peek() ProcStat

The returned structure includes the command that started the process, a phase string describing the state of the process and, in the event that the process has exited, an exit error value or nil on successful exit.

	type ProcStat struct {
		Cmd Cmd
		Exit error
		Phase string
	}

The phase string takes on one of the following values: running, exited, stopped, signaled, continued.

Waiting until a process exits

Finally, you can call Wait asynchronously to block until the process ends:

	Wait() (ProcStat, error)

If you call Wait before the process has exited, the invocation will block until exit occurs. Otherwise, it will return immediately. In both cases, a process status structure (described earlier) is returned, which captures the exit state (successful or not) of the underlying OS process.

Wait can return an application error only in the event that it is interrupted by a concurring call to Scrub.