open-nomad/plugins/drivers/driver.go

399 lines
9.8 KiB
Go
Raw Normal View History

package drivers
import (
"context"
"crypto/md5"
"fmt"
"io"
"path/filepath"
"sort"
"strconv"
"time"
"github.com/hashicorp/nomad/client/allocdir"
2018-10-11 00:07:52 +00:00
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/plugins/shared/hclspec"
pstructs "github.com/hashicorp/nomad/plugins/shared/structs"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/msgpack"
)
const DriverHealthy = "Healthy"
// DriverPlugin is the interface with drivers will implement. It is also
// implemented by a plugin client which proxies the calls to go-plugin. See
// the proto/driver.proto file for detailed information about each RPC and
// message structure.
type DriverPlugin interface {
base.BasePlugin
TaskConfigSchema() (*hclspec.Spec, error)
Capabilities() (*Capabilities, error)
Fingerprint(context.Context) (<-chan *Fingerprint, error)
RecoverTask(*TaskHandle) error
StartTask(*TaskConfig) (*TaskHandle, *DriverNetwork, error)
WaitTask(ctx context.Context, taskID string) (<-chan *ExitResult, error)
StopTask(taskID string, timeout time.Duration, signal string) error
DestroyTask(taskID string, force bool) error
InspectTask(taskID string) (*TaskStatus, error)
TaskStats(taskID string) (*TaskResourceUsage, error)
TaskEvents(context.Context) (<-chan *TaskEvent, error)
SignalTask(taskID string, signal string) error
ExecTask(taskID string, cmd []string, timeout time.Duration) (*ExecTaskResult, error)
}
// InternalDriverPlugin is an interface that exposes functions that are only
// implemented by internal driver plugins.
type InternalDriverPlugin interface {
// Shutdown allows the plugin to cleanup any running state to avoid leaking
// resources. It should not block.
Shutdown()
}
// DriverSignalTaskNotSupported can be embedded by drivers which don't support
// the SignalTask RPC. This satisfies the SignalTask func requirement for the
// DriverPlugin interface.
type DriverSignalTaskNotSupported struct{}
func (DriverSignalTaskNotSupported) SignalTask(taskID, signal string) error {
return fmt.Errorf("SignalTask is not supported by this driver")
}
// DriverExecTaskNotSupported can be embedded by drivers which don't support
// the ExecTask RPC. This satisfies the ExecTask func requirement of the
// DriverPlugin interface.
type DriverExecTaskNotSupported struct{}
func (_ DriverExecTaskNotSupported) ExecTask(taskID, signal string) error {
return fmt.Errorf("ExecTask is not supported by this driver")
}
type HealthState string
var (
HealthStateUndetected = HealthState("undetected")
HealthStateUnhealthy = HealthState("unhealthy")
HealthStateHealthy = HealthState("healthy")
)
type Fingerprint struct {
Attributes map[string]*pstructs.Attribute
Health HealthState
HealthDescription string
2018-10-05 02:36:40 +00:00
// Err is set by the plugin if an error occurred during fingerprinting
Err error
}
2019-01-04 21:11:25 +00:00
// FSIsolation is an enumeration to describe what kind of filesystem isolation
// a driver supports.
type FSIsolation string
var (
2019-01-04 21:11:25 +00:00
// FSIsolationNone means no isolation. The host filesystem is used.
FSIsolationNone = FSIsolation("none")
// FSIsolationChroot means the driver will use a chroot on the host
// filesystem.
FSIsolationChroot = FSIsolation("chroot")
2019-01-04 21:11:25 +00:00
// FSIsolationImage means the driver uses an image.
FSIsolationImage = FSIsolation("image")
)
type Capabilities struct {
// SendSignals marks the driver as being able to send signals
SendSignals bool
// Exec marks the driver as being able to execute arbitrary commands
// such as health checks. Used by the ScriptExecutor interface.
Exec bool
//FSIsolation indicates what kind of filesystem isolation the driver supports.
2019-01-04 21:11:25 +00:00
FSIsolation FSIsolation
}
type TaskConfig struct {
ID string
JobName string
TaskGroupName string
Name string
Env map[string]string
DeviceEnv map[string]string
Resources *Resources
Devices []*DeviceConfig
Mounts []*MountConfig
User string
AllocDir string
rawDriverConfig []byte
StdoutPath string
StderrPath string
AllocID string
}
2018-10-11 00:07:52 +00:00
func (tc *TaskConfig) Copy() *TaskConfig {
if tc == nil {
return nil
}
c := new(TaskConfig)
*c = *tc
c.Env = helper.CopyMapStringString(c.Env)
c.DeviceEnv = helper.CopyMapStringString(c.DeviceEnv)
2018-10-11 00:07:52 +00:00
c.Resources = tc.Resources.Copy()
if c.Devices != nil {
dc := make([]*DeviceConfig, len(c.Devices))
for i, c := range c.Devices {
dc[i] = c.Copy()
}
c.Devices = dc
}
if c.Mounts != nil {
mc := make([]*MountConfig, len(c.Mounts))
for i, m := range c.Mounts {
mc[i] = m.Copy()
}
c.Mounts = mc
}
2018-10-11 00:07:52 +00:00
return c
}
func (tc *TaskConfig) EnvList() []string {
l := make([]string, 0, len(tc.Env))
for k, v := range tc.Env {
l = append(l, k+"="+v)
}
sort.Strings(l)
return l
}
func (tc *TaskConfig) TaskDir() *allocdir.TaskDir {
taskDir := filepath.Join(tc.AllocDir, tc.Name)
return &allocdir.TaskDir{
Dir: taskDir,
SharedAllocDir: filepath.Join(tc.AllocDir, allocdir.SharedAllocName),
LogDir: filepath.Join(tc.AllocDir, allocdir.SharedAllocName, allocdir.LogDirName),
SharedTaskDir: filepath.Join(taskDir, allocdir.SharedAllocName),
LocalDir: filepath.Join(taskDir, allocdir.TaskLocal),
SecretsDir: filepath.Join(taskDir, allocdir.TaskSecrets),
}
}
func (tc *TaskConfig) DecodeDriverConfig(t interface{}) error {
2018-10-12 17:37:28 +00:00
return base.MsgPackDecode(tc.rawDriverConfig, t)
}
func (tc *TaskConfig) EncodeDriverConfig(val cty.Value) error {
data, err := msgpack.Marshal(val, val.Type())
if err != nil {
return err
}
tc.rawDriverConfig = data
return nil
}
func (tc *TaskConfig) EncodeConcreteDriverConfig(t interface{}) error {
data := []byte{}
err := base.MsgPackEncode(&data, t)
if err != nil {
return err
}
tc.rawDriverConfig = data
return nil
}
type Resources struct {
2018-12-13 23:06:48 +00:00
NomadResources *structs.AllocatedTaskResources
LinuxResources *LinuxResources
}
2018-10-11 00:07:52 +00:00
func (r *Resources) Copy() *Resources {
if r == nil {
return nil
}
res := new(Resources)
if r.NomadResources != nil {
res.NomadResources = r.NomadResources.Copy()
}
if r.LinuxResources != nil {
res.LinuxResources = r.LinuxResources.Copy()
}
return res
}
type LinuxResources struct {
CPUPeriod int64
CPUQuota int64
CPUShares int64
MemoryLimitBytes int64
OOMScoreAdj int64
CpusetCPUs string
CpusetMems string
// PrecentTicks is used to calculate the CPUQuota, currently the docker
// driver exposes cpu period and quota through the driver configuration
// and thus the calculation for CPUQuota cannot be done on the client.
// This is a capatability and should only be used by docker until the docker
// specific options are deprecated in favor of exposes CPUPeriod and
// CPUQuota at the task resource stanza.
PercentTicks float64
}
2018-10-11 00:07:52 +00:00
func (r *LinuxResources) Copy() *LinuxResources {
res := new(LinuxResources)
*res = *r
return res
}
type DeviceConfig struct {
TaskPath string
HostPath string
Permissions string
}
func (d *DeviceConfig) Copy() *DeviceConfig {
if d == nil {
return nil
}
dc := new(DeviceConfig)
*dc = *d
return dc
}
type MountConfig struct {
TaskPath string
HostPath string
Readonly bool
}
func (m *MountConfig) Copy() *MountConfig {
if m == nil {
return nil
}
mc := new(MountConfig)
*mc = *m
return mc
}
const (
TaskStateUnknown TaskState = "unknown"
TaskStateRunning TaskState = "running"
TaskStateExited TaskState = "exited"
)
type TaskState string
type ExitResult struct {
ExitCode int
Signal int
OOMKilled bool
Err error
}
func (r *ExitResult) Successful() bool {
return r.ExitCode == 0 && r.Signal == 0 && r.Err == nil
}
func (r *ExitResult) Copy() *ExitResult {
2018-11-26 21:53:15 +00:00
if r == nil {
return nil
}
res := new(ExitResult)
*res = *r
return res
}
type TaskStatus struct {
ID string
Name string
State TaskState
StartedAt time.Time
CompletedAt time.Time
ExitResult *ExitResult
DriverAttributes map[string]string
NetworkOverride *DriverNetwork
}
type TaskEvent struct {
TaskID string
TaskName string
AllocID string
Timestamp time.Time
Message string
Annotations map[string]string
2018-10-05 02:36:40 +00:00
// Err is only used if an error occurred while consuming the RPC stream
Err error
}
type ExecTaskResult struct {
Stdout []byte
Stderr []byte
ExitResult *ExitResult
}
// DriverNetwork is the network created by driver's (eg Docker's bridge
// network) during Prestart.
type DriverNetwork struct {
// PortMap can be set by drivers to replace ports in environment
// variables with driver-specific mappings.
PortMap map[string]int
// IP is the IP address for the task created by the driver.
IP string
// AutoAdvertise indicates whether the driver thinks services that
// choose to auto-advertise-addresses should use this IP instead of the
// host's. eg If a Docker network plugin is used
AutoAdvertise bool
}
// Advertise returns true if the driver suggests using the IP set. May be
// called on a nil Network in which case it returns false.
func (d *DriverNetwork) Advertise() bool {
return d != nil && d.AutoAdvertise
}
// Copy a DriverNetwork struct. If it is nil, nil is returned.
func (d *DriverNetwork) Copy() *DriverNetwork {
if d == nil {
return nil
}
pm := make(map[string]int, len(d.PortMap))
for k, v := range d.PortMap {
pm[k] = v
}
return &DriverNetwork{
PortMap: pm,
IP: d.IP,
AutoAdvertise: d.AutoAdvertise,
}
}
// Hash the contents of a DriverNetwork struct to detect changes. If it is nil,
// an empty slice is returned.
func (d *DriverNetwork) Hash() []byte {
if d == nil {
return []byte{}
}
h := md5.New()
io.WriteString(h, d.IP)
io.WriteString(h, strconv.FormatBool(d.AutoAdvertise))
for k, v := range d.PortMap {
io.WriteString(h, k)
io.WriteString(h, strconv.Itoa(v))
}
return h.Sum(nil)
}