diff --git a/nomad/schema.go b/nomad/schema.go index 04d6a5f78..2688ab218 100644 --- a/nomad/schema.go +++ b/nomad/schema.go @@ -1,6 +1,10 @@ package nomad -import "github.com/hashicorp/go-memdb" +import ( + "fmt" + + "github.com/hashicorp/go-memdb" +) // stateStoreSchema is used to return the schema for the state store func stateStoreSchema() *memdb.DBSchema { @@ -9,17 +13,29 @@ func stateStoreSchema() *memdb.DBSchema { Tables: make(map[string]*memdb.TableSchema), } - // Add each of the tables - nodeSchema := nodeTableSchema() - db.Tables[nodeSchema.Name] = nodeSchema + // Collect all the schemas that are needed + schemas := []func() *memdb.TableSchema{ + nodeTableSchema, + jobTableSchema, + taskGroupTableSchema, + taskTableSchema, + } + // Add each of the tables + for _, schemaFn := range schemas { + schema := schemaFn() + if _, ok := db.Tables[schema.Name]; ok { + panic(fmt.Sprintf("duplicate table name: %s", schema.Name)) + } + db.Tables[schema.Name] = schema + } return db } // nodeTableSchema returns the MemDB schema for the nodes table. // This table is used to store all the client nodes that are registered. func nodeTableSchema() *memdb.TableSchema { - table := &memdb.TableSchema{ + return &memdb.TableSchema{ Name: "nodes", Indexes: map[string]*memdb.IndexSchema{ // Primary index is used for node management @@ -51,7 +67,101 @@ func nodeTableSchema() *memdb.TableSchema { Lowercase: true, }, &memdb.StringFieldIndex{ - Field: "Status", + Field: "Status", + }, + }, + }, + }, + }, + } +} + +// jobTableSchema returns the MemDB schema for the jobs table. +// This table is used to store all the jobs that have been submitted. +func jobTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "jobs", + Indexes: map[string]*memdb.IndexSchema{ + // Primary index is used for job management + // and simple direct lookup. ID is required to be + // unique. + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.StringFieldIndex{ + Field: "Name", + Lowercase: true, + }, + }, + + // Status is used to scan for jobs that are in need + // of scheduling attention. + "status": &memdb.IndexSchema{ + Name: "status", + AllowMissing: false, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "Status", + }, + }, + }, + } +} + +// taskGroupTableSchema returns the MemDB schema for the task group table. +// This table is used to store all the task groups belonging to a job. +func taskGroupTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "taskGroups", + Indexes: map[string]*memdb.IndexSchema{ + // Primary index is compount of {Job, Name} + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.CompoundIndex{ + AllowMissing: false, + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "Job", + Lowercase: true, + }, + &memdb.StringFieldIndex{ + Field: "Name", + Lowercase: true, + }, + }, + }, + }, + }, + } +} + +// taskTableSchema returns the MemDB schema for the tasks table. +// This table is used to store all the task groups belonging to a job. +func taskTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "tasks", + Indexes: map[string]*memdb.IndexSchema{ + // Primary index is compount of {Job, TaskGroup, Name} + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.CompoundIndex{ + AllowMissing: false, + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "Job", + Lowercase: true, + }, + &memdb.StringFieldIndex{ + Field: "TaskGroup", + Lowercase: true, + }, + &memdb.StringFieldIndex{ + Field: "Name", Lowercase: true, }, }, @@ -59,5 +169,4 @@ func nodeTableSchema() *memdb.TableSchema { }, }, } - return table } diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 07e964840..6a780e2c4 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -106,13 +106,6 @@ type WriteMeta struct { Index uint64 } -const ( - StatusInit = "initializing" - StatusReady = "ready" - StatusMaint = "maintenance" - StatusDown = "down" -) - // RegisterRequest is used for Client.Register endpoint // to register a node as being a schedulable entity. type RegisterRequest struct { @@ -125,6 +118,13 @@ type RegisterResponse struct { WriteMeta } +const ( + NodeStatusInit = "initializing" + NodeStatusReady = "ready" + NodeStatusMaint = "maintenance" + NodeStatusDown = "down" +) + // Node is a representation of a schedulable client node type Node struct { // ID is a unique identifier for the node. It can be constructed @@ -148,6 +148,18 @@ type Node struct { // For example 'cpu=2' 'memory=2048' Resouces *Resources + // Reserved is the set of resources that are reserved, + // and should be subtracted from the total resources for + // the purposes of scheduling. This may be provide certain + // high-watermark tolerances or because of external schedulers + // consuming resources. + Reserved *Resources + + // Allocated is the set of resources that have been allocated + // as part of scheduling. They should also be excluded for the + // purposes of additional scheduling allocations. + Allocated *Resources + // Links are used to 'link' this client to external // systems. For example 'consul=foo.dc1' 'aws=i-83212' // 'ami=ami-123' @@ -164,26 +176,142 @@ type Node struct { // Resources is used to define the resources available // on a client type Resources struct { - CPU float64 - CPUReserved float64 - MemoryMB int - MemoryMBReserved int - DiskMB int - DiskMBReservered int - IOPS int - IOPSReserved int - Networks []*NetworkResource - Other map[string]interface{} + CPU float64 + MemoryMB int + DiskMB int + IOPS int + Networks []*NetworkResource + Other map[string]interface{} } // NetworkResource is used to represesent available network -// resources> +// resources type NetworkResource struct { Public bool // Is this a public address? CIDR string // CIDR block of addresses ReservedPorts []int // Reserved ports MBits int // Throughput - MBitsReserved int +} + +const ( + JobTypeService = "service" + JobTypeBatch = "batch" +) + +const ( + JobStatusPending = "pending" // Pending means the job is waiting on scheduling + JobStatusRunning = "running" // Running means the entire job is running + JobStatusComplete = "complete" // Complete means there was a clean termination + JobStatusDead = "dead" // Dead means there was abnormal termination +) + +// Job is the scope of a scheduling request to Nomad. It is the largest +// scoped object, and is a named collection of task groups. Each task group +// is further composed of tasks. A task group (TG) is the unit of scheduling +// however. +type Job struct { + // Name is the logical name of the job used to refer to it. This is unique + // per region, but not unique globally. + Name string + + // Type is used to control various behaviors about the job. Most jobs + // are service jobs, meaning they are expected to be long lived. + // Some jobs are batch oriented meaning they run and then terminate. + // This can be extended in the future to support custom schedulers. + Type string + + // Priority is used to control scheduling importance and if this job + // can preempt other jobs. + Priority int + + // AllAtOnce is used to control if incremental scheduling of task groups + // is allowed or if we must do a gang scheduling of the entire job. This + // can slow down larger jobs if resources are not available. + AllAtOnce bool + + // Constraints can be specified at a job level and apply to + // all the task groups and tasks. + Constraints []*Constraint + + // TaskGroups are the collections of task groups that this job needs + // to run. Each task group is an atomic unit of scheduling and placement. + TaskGroups []*TaskGroup + + // Meta is used to associate arbitrary metadata with this + // job. This is opaque to Nomad. + Meta map[string]string + + // Job status + Status string +} + +// TaskGroup is an atomic unit of placement. Each task group belongs to +// a job and may contain any number of tasks. A task group support running +// in many replicas using the same configuration.. +type TaskGroup struct { + // Name of the parent job + Job string + + // Name of the task group + Name string + + // Count is the number of replicas of this task group that should + // be scheduled. + Count int + + // Constraints can be specified at a task group level and apply to + // all the tasks contained. + Constraints []*Constraint + + // Tasks are the collection of tasks that this task group needs to run + Tasks []*Task + + // Meta is used to associate arbitrary metadata with this + // task group. This is opaque to Nomad. + Meta map[string]string + + // Task group status + Status string +} + +// Task is a single process typically that is executed as part of a task group. +type Task struct { + // Name of the parent job + Job string + + // Name of the partent task group + TaskGroup string + + // Name of the task + Name string + + // Driver is used to control which driver is used + Driver string + + // Config is provided to the driver to initialize + Config map[string]string + + // Constraints can be specified at a task level and apply only to + // the particular task. + Constraints []*Constraint + + // Resources is the resources needed by this task + Resources *Resources + + // Meta is used to associate arbitrary metadata with this + // task. This is opaque to Nomad. + Meta map[string]string +} + +// Constraints are used to restrict placement options in the case of +// a hard constraint, and used to prefer a placement in the case of +// a soft constraint. +type Constraint struct { + Hard bool // Hard or soft constraint + LTarget string // Left-hand target + RTarget string // Right-hand target + Operand string // Constraint operand (<=, <, =, !=, >, >=), contains, near + Weight int // Soft constraints can vary the weight } // msgpackHandle is a shared handle for encoding/decoding of structs