Add HashiCorp Nomad provider (#483)
* provider: adding Nomad provider * updating CONTRIBUTING.md with Nomad provider * updated README.md by adding the Nomad provider * fix typo * adding nomad/api and nomad/testutil deps * adding Nomad binary dependency for provider tests * fixed the nomad binary download command step and added tolerations to the nomad provider. * adding nomad provider demo gif * adding my name to authors * adding two missing go-rootcerts files after dep ensure * delete pod comment
This commit is contained in:
committed by
Robbie Zhang
parent
5796be449b
commit
a46e1dd2ce
196
vendor/github.com/hashicorp/nomad/api/acl.go
generated
vendored
Normal file
196
vendor/github.com/hashicorp/nomad/api/acl.go
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ACLPolicies is used to query the ACL Policy endpoints.
|
||||
type ACLPolicies struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ACLPolicies returns a new handle on the ACL policies.
|
||||
func (c *Client) ACLPolicies() *ACLPolicies {
|
||||
return &ACLPolicies{client: c}
|
||||
}
|
||||
|
||||
// List is used to dump all of the policies.
|
||||
func (a *ACLPolicies) List(q *QueryOptions) ([]*ACLPolicyListStub, *QueryMeta, error) {
|
||||
var resp []*ACLPolicyListStub
|
||||
qm, err := a.client.query("/v1/acl/policies", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// Upsert is used to create or update a policy
|
||||
func (a *ACLPolicies) Upsert(policy *ACLPolicy, q *WriteOptions) (*WriteMeta, error) {
|
||||
if policy == nil || policy.Name == "" {
|
||||
return nil, fmt.Errorf("missing policy name")
|
||||
}
|
||||
wm, err := a.client.write("/v1/acl/policy/"+policy.Name, policy, nil, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Delete is used to delete a policy
|
||||
func (a *ACLPolicies) Delete(policyName string, q *WriteOptions) (*WriteMeta, error) {
|
||||
if policyName == "" {
|
||||
return nil, fmt.Errorf("missing policy name")
|
||||
}
|
||||
wm, err := a.client.delete("/v1/acl/policy/"+policyName, nil, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Info is used to query a specific policy
|
||||
func (a *ACLPolicies) Info(policyName string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
|
||||
if policyName == "" {
|
||||
return nil, nil, fmt.Errorf("missing policy name")
|
||||
}
|
||||
var resp ACLPolicy
|
||||
wm, err := a.client.query("/v1/acl/policy/"+policyName, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// ACLTokens is used to query the ACL token endpoints.
|
||||
type ACLTokens struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ACLTokens returns a new handle on the ACL tokens.
|
||||
func (c *Client) ACLTokens() *ACLTokens {
|
||||
return &ACLTokens{client: c}
|
||||
}
|
||||
|
||||
// Bootstrap is used to get the initial bootstrap token
|
||||
func (a *ACLTokens) Bootstrap(q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
||||
var resp ACLToken
|
||||
wm, err := a.client.write("/v1/acl/bootstrap", nil, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// List is used to dump all of the tokens.
|
||||
func (a *ACLTokens) List(q *QueryOptions) ([]*ACLTokenListStub, *QueryMeta, error) {
|
||||
var resp []*ACLTokenListStub
|
||||
qm, err := a.client.query("/v1/acl/tokens", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// Create is used to create a token
|
||||
func (a *ACLTokens) Create(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
||||
if token.AccessorID != "" {
|
||||
return nil, nil, fmt.Errorf("cannot specify Accessor ID")
|
||||
}
|
||||
var resp ACLToken
|
||||
wm, err := a.client.write("/v1/acl/token", token, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// Update is used to update an existing token
|
||||
func (a *ACLTokens) Update(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
|
||||
if token.AccessorID == "" {
|
||||
return nil, nil, fmt.Errorf("missing accessor ID")
|
||||
}
|
||||
var resp ACLToken
|
||||
wm, err := a.client.write("/v1/acl/token/"+token.AccessorID,
|
||||
token, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// Delete is used to delete a token
|
||||
func (a *ACLTokens) Delete(accessorID string, q *WriteOptions) (*WriteMeta, error) {
|
||||
if accessorID == "" {
|
||||
return nil, fmt.Errorf("missing accessor ID")
|
||||
}
|
||||
wm, err := a.client.delete("/v1/acl/token/"+accessorID, nil, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Info is used to query a token
|
||||
func (a *ACLTokens) Info(accessorID string, q *QueryOptions) (*ACLToken, *QueryMeta, error) {
|
||||
if accessorID == "" {
|
||||
return nil, nil, fmt.Errorf("missing accessor ID")
|
||||
}
|
||||
var resp ACLToken
|
||||
wm, err := a.client.query("/v1/acl/token/"+accessorID, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// Self is used to query our own token
|
||||
func (a *ACLTokens) Self(q *QueryOptions) (*ACLToken, *QueryMeta, error) {
|
||||
var resp ACLToken
|
||||
wm, err := a.client.query("/v1/acl/token/self", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// ACLPolicyListStub is used to for listing ACL policies
|
||||
type ACLPolicyListStub struct {
|
||||
Name string
|
||||
Description string
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// ACLPolicy is used to represent an ACL policy
|
||||
type ACLPolicy struct {
|
||||
Name string
|
||||
Description string
|
||||
Rules string
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// ACLToken represents a client token which is used to Authenticate
|
||||
type ACLToken struct {
|
||||
AccessorID string
|
||||
SecretID string
|
||||
Name string
|
||||
Type string
|
||||
Policies []string
|
||||
Global bool
|
||||
CreateTime time.Time
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
type ACLTokenListStub struct {
|
||||
AccessorID string
|
||||
Name string
|
||||
Type string
|
||||
Policies []string
|
||||
Global bool
|
||||
CreateTime time.Time
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
308
vendor/github.com/hashicorp/nomad/api/agent.go
generated
vendored
Normal file
308
vendor/github.com/hashicorp/nomad/api/agent.go
generated
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Agent encapsulates an API client which talks to Nomad's
|
||||
// agent endpoints for a specific node.
|
||||
type Agent struct {
|
||||
client *Client
|
||||
|
||||
// Cache static agent info
|
||||
nodeName string
|
||||
datacenter string
|
||||
region string
|
||||
}
|
||||
|
||||
// KeyringResponse is a unified key response and can be used for install,
|
||||
// remove, use, as well as listing key queries.
|
||||
type KeyringResponse struct {
|
||||
Messages map[string]string
|
||||
Keys map[string]int
|
||||
NumNodes int
|
||||
}
|
||||
|
||||
// KeyringRequest is request objects for serf key operations.
|
||||
type KeyringRequest struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// Agent returns a new agent which can be used to query
|
||||
// the agent-specific endpoints.
|
||||
func (c *Client) Agent() *Agent {
|
||||
return &Agent{client: c}
|
||||
}
|
||||
|
||||
// Self is used to query the /v1/agent/self endpoint and
|
||||
// returns information specific to the running agent.
|
||||
func (a *Agent) Self() (*AgentSelf, error) {
|
||||
var out *AgentSelf
|
||||
|
||||
// Query the self endpoint on the agent
|
||||
_, err := a.client.query("/v1/agent/self", &out, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed querying self endpoint: %s", err)
|
||||
}
|
||||
|
||||
// Populate the cache for faster queries
|
||||
a.populateCache(out)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// populateCache is used to insert various pieces of static
|
||||
// data into the agent handle. This is used during subsequent
|
||||
// lookups for the same data later on to save the round trip.
|
||||
func (a *Agent) populateCache(self *AgentSelf) {
|
||||
if a.nodeName == "" {
|
||||
a.nodeName = self.Member.Name
|
||||
}
|
||||
if a.datacenter == "" {
|
||||
if val, ok := self.Config["Datacenter"]; ok {
|
||||
a.datacenter, _ = val.(string)
|
||||
}
|
||||
}
|
||||
if a.region == "" {
|
||||
if val, ok := self.Config["Region"]; ok {
|
||||
a.region, _ = val.(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NodeName is used to query the Nomad agent for its node name.
|
||||
func (a *Agent) NodeName() (string, error) {
|
||||
// Return from cache if we have it
|
||||
if a.nodeName != "" {
|
||||
return a.nodeName, nil
|
||||
}
|
||||
|
||||
// Query the node name
|
||||
_, err := a.Self()
|
||||
return a.nodeName, err
|
||||
}
|
||||
|
||||
// Datacenter is used to return the name of the datacenter which
|
||||
// the agent is a member of.
|
||||
func (a *Agent) Datacenter() (string, error) {
|
||||
// Return from cache if we have it
|
||||
if a.datacenter != "" {
|
||||
return a.datacenter, nil
|
||||
}
|
||||
|
||||
// Query the agent for the DC
|
||||
_, err := a.Self()
|
||||
return a.datacenter, err
|
||||
}
|
||||
|
||||
// Region is used to look up the region the agent is in.
|
||||
func (a *Agent) Region() (string, error) {
|
||||
// Return from cache if we have it
|
||||
if a.region != "" {
|
||||
return a.region, nil
|
||||
}
|
||||
|
||||
// Query the agent for the region
|
||||
_, err := a.Self()
|
||||
return a.region, err
|
||||
}
|
||||
|
||||
// Join is used to instruct a server node to join another server
|
||||
// via the gossip protocol. Multiple addresses may be specified.
|
||||
// We attempt to join all of the hosts in the list. Returns the
|
||||
// number of nodes successfully joined and any error. If one or
|
||||
// more nodes have a successful result, no error is returned.
|
||||
func (a *Agent) Join(addrs ...string) (int, error) {
|
||||
// Accumulate the addresses
|
||||
v := url.Values{}
|
||||
for _, addr := range addrs {
|
||||
v.Add("address", addr)
|
||||
}
|
||||
|
||||
// Send the join request
|
||||
var resp joinResponse
|
||||
_, err := a.client.write("/v1/agent/join?"+v.Encode(), nil, &resp, nil)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed joining: %s", err)
|
||||
}
|
||||
if resp.Error != "" {
|
||||
return 0, fmt.Errorf("failed joining: %s", resp.Error)
|
||||
}
|
||||
return resp.NumJoined, nil
|
||||
}
|
||||
|
||||
// Members is used to query all of the known server members
|
||||
func (a *Agent) Members() (*ServerMembers, error) {
|
||||
var resp *ServerMembers
|
||||
|
||||
// Query the known members
|
||||
_, err := a.client.query("/v1/agent/members", &resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ForceLeave is used to eject an existing node from the cluster.
|
||||
func (a *Agent) ForceLeave(node string) error {
|
||||
_, err := a.client.write("/v1/agent/force-leave?node="+node, nil, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Servers is used to query the list of servers on a client node.
|
||||
func (a *Agent) Servers() ([]string, error) {
|
||||
var resp []string
|
||||
_, err := a.client.query("/v1/agent/servers", &resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SetServers is used to update the list of servers on a client node.
|
||||
func (a *Agent) SetServers(addrs []string) error {
|
||||
// Accumulate the addresses
|
||||
v := url.Values{}
|
||||
for _, addr := range addrs {
|
||||
v.Add("address", addr)
|
||||
}
|
||||
|
||||
_, err := a.client.write("/v1/agent/servers?"+v.Encode(), nil, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// ListKeys returns the list of installed keys
|
||||
func (a *Agent) ListKeys() (*KeyringResponse, error) {
|
||||
var resp KeyringResponse
|
||||
_, err := a.client.query("/v1/agent/keyring/list", &resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// InstallKey installs a key in the keyrings of all the serf members
|
||||
func (a *Agent) InstallKey(key string) (*KeyringResponse, error) {
|
||||
args := KeyringRequest{
|
||||
Key: key,
|
||||
}
|
||||
var resp KeyringResponse
|
||||
_, err := a.client.write("/v1/agent/keyring/install", &args, &resp, nil)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
// UseKey uses a key from the keyring of serf members
|
||||
func (a *Agent) UseKey(key string) (*KeyringResponse, error) {
|
||||
args := KeyringRequest{
|
||||
Key: key,
|
||||
}
|
||||
var resp KeyringResponse
|
||||
_, err := a.client.write("/v1/agent/keyring/use", &args, &resp, nil)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
// RemoveKey removes a particular key from keyrings of serf members
|
||||
func (a *Agent) RemoveKey(key string) (*KeyringResponse, error) {
|
||||
args := KeyringRequest{
|
||||
Key: key,
|
||||
}
|
||||
var resp KeyringResponse
|
||||
_, err := a.client.write("/v1/agent/keyring/remove", &args, &resp, nil)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
// Health queries the agent's health
|
||||
func (a *Agent) Health() (*AgentHealthResponse, error) {
|
||||
req, err := a.client.newRequest("GET", "/v1/agent/health")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var health AgentHealthResponse
|
||||
_, resp, err := a.client.doRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Always try to decode the response as JSON
|
||||
err = json.NewDecoder(resp.Body).Decode(&health)
|
||||
if err == nil {
|
||||
return &health, nil
|
||||
}
|
||||
|
||||
// Return custom error when response is not expected JSON format
|
||||
return nil, fmt.Errorf("unable to unmarshal response with status %d: %v", resp.StatusCode, err)
|
||||
}
|
||||
|
||||
// joinResponse is used to decode the response we get while
|
||||
// sending a member join request.
|
||||
type joinResponse struct {
|
||||
NumJoined int `json:"num_joined"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type ServerMembers struct {
|
||||
ServerName string
|
||||
ServerRegion string
|
||||
ServerDC string
|
||||
Members []*AgentMember
|
||||
}
|
||||
|
||||
type AgentSelf struct {
|
||||
Config map[string]interface{} `json:"config"`
|
||||
Member AgentMember `json:"member"`
|
||||
Stats map[string]map[string]string `json:"stats"`
|
||||
}
|
||||
|
||||
// AgentMember represents a cluster member known to the agent
|
||||
type AgentMember struct {
|
||||
Name string
|
||||
Addr string
|
||||
Port uint16
|
||||
Tags map[string]string
|
||||
Status string
|
||||
ProtocolMin uint8
|
||||
ProtocolMax uint8
|
||||
ProtocolCur uint8
|
||||
DelegateMin uint8
|
||||
DelegateMax uint8
|
||||
DelegateCur uint8
|
||||
}
|
||||
|
||||
// AgentMembersNameSort implements sort.Interface for []*AgentMembersNameSort
|
||||
// based on the Name, DC and Region
|
||||
type AgentMembersNameSort []*AgentMember
|
||||
|
||||
func (a AgentMembersNameSort) Len() int { return len(a) }
|
||||
func (a AgentMembersNameSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a AgentMembersNameSort) Less(i, j int) bool {
|
||||
if a[i].Tags["region"] != a[j].Tags["region"] {
|
||||
return a[i].Tags["region"] < a[j].Tags["region"]
|
||||
}
|
||||
|
||||
if a[i].Tags["dc"] != a[j].Tags["dc"] {
|
||||
return a[i].Tags["dc"] < a[j].Tags["dc"]
|
||||
}
|
||||
|
||||
return a[i].Name < a[j].Name
|
||||
|
||||
}
|
||||
|
||||
// AgentHealthResponse is the response from the Health endpoint describing an
|
||||
// agent's health.
|
||||
type AgentHealthResponse struct {
|
||||
Client *AgentHealth `json:"client,omitempty"`
|
||||
Server *AgentHealth `json:"server,omitempty"`
|
||||
}
|
||||
|
||||
// AgentHealth describes the Client or Server's health in a Health request.
|
||||
type AgentHealth struct {
|
||||
// Ok is false if the agent is unhealthy
|
||||
Ok bool `json:"ok"`
|
||||
|
||||
// Message describes why the agent is unhealthy
|
||||
Message string `json:"message"`
|
||||
}
|
||||
228
vendor/github.com/hashicorp/nomad/api/allocations.go
generated
vendored
Normal file
228
vendor/github.com/hashicorp/nomad/api/allocations.go
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// NodeDownErr marks an operation as not able to complete since the node is
|
||||
// down.
|
||||
NodeDownErr = fmt.Errorf("node down")
|
||||
)
|
||||
|
||||
// Allocations is used to query the alloc-related endpoints.
|
||||
type Allocations struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Allocations returns a handle on the allocs endpoints.
|
||||
func (c *Client) Allocations() *Allocations {
|
||||
return &Allocations{client: c}
|
||||
}
|
||||
|
||||
// List returns a list of all of the allocations.
|
||||
func (a *Allocations) List(q *QueryOptions) ([]*AllocationListStub, *QueryMeta, error) {
|
||||
var resp []*AllocationListStub
|
||||
qm, err := a.client.query("/v1/allocations", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(AllocIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
func (a *Allocations) PrefixList(prefix string) ([]*AllocationListStub, *QueryMeta, error) {
|
||||
return a.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to retrieve a single allocation.
|
||||
func (a *Allocations) Info(allocID string, q *QueryOptions) (*Allocation, *QueryMeta, error) {
|
||||
var resp Allocation
|
||||
qm, err := a.client.query("/v1/allocation/"+allocID, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
func (a *Allocations) Stats(alloc *Allocation, q *QueryOptions) (*AllocResourceUsage, error) {
|
||||
var resp AllocResourceUsage
|
||||
path := fmt.Sprintf("/v1/client/allocation/%s/stats", alloc.ID)
|
||||
_, err := a.client.query(path, &resp, q)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (a *Allocations) GC(alloc *Allocation, q *QueryOptions) error {
|
||||
nodeClient, err := a.client.GetNodeClient(alloc.NodeID, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp struct{}
|
||||
_, err = nodeClient.query("/v1/client/allocation/"+alloc.ID+"/gc", &resp, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Allocation is used for serialization of allocations.
|
||||
type Allocation struct {
|
||||
ID string
|
||||
Namespace string
|
||||
EvalID string
|
||||
Name string
|
||||
NodeID string
|
||||
JobID string
|
||||
Job *Job
|
||||
TaskGroup string
|
||||
Resources *Resources
|
||||
TaskResources map[string]*Resources
|
||||
Services map[string]string
|
||||
Metrics *AllocationMetric
|
||||
DesiredStatus string
|
||||
DesiredDescription string
|
||||
DesiredTransition DesiredTransition
|
||||
ClientStatus string
|
||||
ClientDescription string
|
||||
TaskStates map[string]*TaskState
|
||||
DeploymentID string
|
||||
DeploymentStatus *AllocDeploymentStatus
|
||||
FollowupEvalID string
|
||||
PreviousAllocation string
|
||||
NextAllocation string
|
||||
RescheduleTracker *RescheduleTracker
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
AllocModifyIndex uint64
|
||||
CreateTime int64
|
||||
ModifyTime int64
|
||||
}
|
||||
|
||||
// AllocationMetric is used to deserialize allocation metrics.
|
||||
type AllocationMetric struct {
|
||||
NodesEvaluated int
|
||||
NodesFiltered int
|
||||
NodesAvailable map[string]int
|
||||
ClassFiltered map[string]int
|
||||
ConstraintFiltered map[string]int
|
||||
NodesExhausted int
|
||||
ClassExhausted map[string]int
|
||||
DimensionExhausted map[string]int
|
||||
QuotaExhausted []string
|
||||
Scores map[string]float64
|
||||
AllocationTime time.Duration
|
||||
CoalescedFailures int
|
||||
}
|
||||
|
||||
// AllocationListStub is used to return a subset of an allocation
|
||||
// during list operations.
|
||||
type AllocationListStub struct {
|
||||
ID string
|
||||
EvalID string
|
||||
Name string
|
||||
NodeID string
|
||||
JobID string
|
||||
JobVersion uint64
|
||||
TaskGroup string
|
||||
DesiredStatus string
|
||||
DesiredDescription string
|
||||
ClientStatus string
|
||||
ClientDescription string
|
||||
TaskStates map[string]*TaskState
|
||||
DeploymentStatus *AllocDeploymentStatus
|
||||
FollowupEvalID string
|
||||
RescheduleTracker *RescheduleTracker
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
CreateTime int64
|
||||
ModifyTime int64
|
||||
}
|
||||
|
||||
// AllocDeploymentStatus captures the status of the allocation as part of the
|
||||
// deployment. This can include things like if the allocation has been marked as
|
||||
// healthy.
|
||||
type AllocDeploymentStatus struct {
|
||||
Healthy *bool
|
||||
Timestamp time.Time
|
||||
Canary bool
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// AllocIndexSort reverse sorts allocs by CreateIndex.
|
||||
type AllocIndexSort []*AllocationListStub
|
||||
|
||||
func (a AllocIndexSort) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a AllocIndexSort) Less(i, j int) bool {
|
||||
return a[i].CreateIndex > a[j].CreateIndex
|
||||
}
|
||||
|
||||
func (a AllocIndexSort) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
// RescheduleInfo is used to calculate remaining reschedule attempts
|
||||
// according to the given time and the task groups reschedule policy
|
||||
func (a Allocation) RescheduleInfo(t time.Time) (int, int) {
|
||||
var reschedulePolicy *ReschedulePolicy
|
||||
for _, tg := range a.Job.TaskGroups {
|
||||
if *tg.Name == a.TaskGroup {
|
||||
reschedulePolicy = tg.ReschedulePolicy
|
||||
}
|
||||
}
|
||||
if reschedulePolicy == nil {
|
||||
return 0, 0
|
||||
}
|
||||
availableAttempts := *reschedulePolicy.Attempts
|
||||
interval := *reschedulePolicy.Interval
|
||||
attempted := 0
|
||||
|
||||
// Loop over reschedule tracker to find attempts within the restart policy's interval
|
||||
if a.RescheduleTracker != nil && availableAttempts > 0 && interval > 0 {
|
||||
for j := len(a.RescheduleTracker.Events) - 1; j >= 0; j-- {
|
||||
lastAttempt := a.RescheduleTracker.Events[j].RescheduleTime
|
||||
timeDiff := t.UTC().UnixNano() - lastAttempt
|
||||
if timeDiff < interval.Nanoseconds() {
|
||||
attempted += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return attempted, availableAttempts
|
||||
}
|
||||
|
||||
// RescheduleTracker encapsulates previous reschedule events
|
||||
type RescheduleTracker struct {
|
||||
Events []*RescheduleEvent
|
||||
}
|
||||
|
||||
// RescheduleEvent is used to keep track of previous attempts at rescheduling an allocation
|
||||
type RescheduleEvent struct {
|
||||
// RescheduleTime is the timestamp of a reschedule attempt
|
||||
RescheduleTime int64
|
||||
|
||||
// PrevAllocID is the ID of the previous allocation being restarted
|
||||
PrevAllocID string
|
||||
|
||||
// PrevNodeID is the node ID of the previous allocation
|
||||
PrevNodeID string
|
||||
}
|
||||
|
||||
// DesiredTransition is used to mark an allocation as having a desired state
|
||||
// transition. This information can be used by the scheduler to make the
|
||||
// correct decision.
|
||||
type DesiredTransition struct {
|
||||
// Migrate is used to indicate that this allocation should be stopped and
|
||||
// migrated to another node.
|
||||
Migrate *bool
|
||||
|
||||
// Reschedule is used to indicate that this allocation is eligible to be
|
||||
// rescheduled.
|
||||
Reschedule *bool
|
||||
}
|
||||
|
||||
// ShouldMigrate returns whether the transition object dictates a migration.
|
||||
func (d DesiredTransition) ShouldMigrate() bool {
|
||||
return d.Migrate != nil && *d.Migrate
|
||||
}
|
||||
832
vendor/github.com/hashicorp/nomad/api/api.go
generated
vendored
Normal file
832
vendor/github.com/hashicorp/nomad/api/api.go
generated
vendored
Normal file
@@ -0,0 +1,832 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
rootcerts "github.com/hashicorp/go-rootcerts"
|
||||
)
|
||||
|
||||
var (
|
||||
// ClientConnTimeout is the timeout applied when attempting to contact a
|
||||
// client directly before switching to a connection through the Nomad
|
||||
// server.
|
||||
ClientConnTimeout = 1 * time.Second
|
||||
)
|
||||
|
||||
// QueryOptions are used to parameterize a query
|
||||
type QueryOptions struct {
|
||||
// Providing a datacenter overwrites the region provided
|
||||
// by the Config
|
||||
Region string
|
||||
|
||||
// Namespace is the target namespace for the query.
|
||||
Namespace string
|
||||
|
||||
// AllowStale allows any Nomad server (non-leader) to service
|
||||
// a read. This allows for lower latency and higher throughput
|
||||
AllowStale bool
|
||||
|
||||
// WaitIndex is used to enable a blocking query. Waits
|
||||
// until the timeout or the next index is reached
|
||||
WaitIndex uint64
|
||||
|
||||
// WaitTime is used to bound the duration of a wait.
|
||||
// Defaults to that of the Config, but can be overridden.
|
||||
WaitTime time.Duration
|
||||
|
||||
// If set, used as prefix for resource list searches
|
||||
Prefix string
|
||||
|
||||
// Set HTTP parameters on the query.
|
||||
Params map[string]string
|
||||
|
||||
// AuthToken is the secret ID of an ACL token
|
||||
AuthToken string
|
||||
}
|
||||
|
||||
// WriteOptions are used to parameterize a write
|
||||
type WriteOptions struct {
|
||||
// Providing a datacenter overwrites the region provided
|
||||
// by the Config
|
||||
Region string
|
||||
|
||||
// Namespace is the target namespace for the write.
|
||||
Namespace string
|
||||
|
||||
// AuthToken is the secret ID of an ACL token
|
||||
AuthToken string
|
||||
}
|
||||
|
||||
// QueryMeta is used to return meta data about a query
|
||||
type QueryMeta struct {
|
||||
// LastIndex. This can be used as a WaitIndex to perform
|
||||
// a blocking query
|
||||
LastIndex uint64
|
||||
|
||||
// Time of last contact from the leader for the
|
||||
// server servicing the request
|
||||
LastContact time.Duration
|
||||
|
||||
// Is there a known leader
|
||||
KnownLeader bool
|
||||
|
||||
// How long did the request take
|
||||
RequestTime time.Duration
|
||||
}
|
||||
|
||||
// WriteMeta is used to return meta data about a write
|
||||
type WriteMeta struct {
|
||||
// LastIndex. This can be used as a WaitIndex to perform
|
||||
// a blocking query
|
||||
LastIndex uint64
|
||||
|
||||
// How long did the request take
|
||||
RequestTime time.Duration
|
||||
}
|
||||
|
||||
// HttpBasicAuth is used to authenticate http client with HTTP Basic Authentication
|
||||
type HttpBasicAuth struct {
|
||||
// Username to use for HTTP Basic Authentication
|
||||
Username string
|
||||
|
||||
// Password to use for HTTP Basic Authentication
|
||||
Password string
|
||||
}
|
||||
|
||||
// Config is used to configure the creation of a client
|
||||
type Config struct {
|
||||
// Address is the address of the Nomad agent
|
||||
Address string
|
||||
|
||||
// Region to use. If not provided, the default agent region is used.
|
||||
Region string
|
||||
|
||||
// SecretID to use. This can be overwritten per request.
|
||||
SecretID string
|
||||
|
||||
// Namespace to use. If not provided the default namespace is used.
|
||||
Namespace string
|
||||
|
||||
// httpClient is the client to use. Default will be used if not provided.
|
||||
httpClient *http.Client
|
||||
|
||||
// HttpAuth is the auth info to use for http access.
|
||||
HttpAuth *HttpBasicAuth
|
||||
|
||||
// WaitTime limits how long a Watch will block. If not provided,
|
||||
// the agent default values will be used.
|
||||
WaitTime time.Duration
|
||||
|
||||
// TLSConfig provides the various TLS related configurations for the http
|
||||
// client
|
||||
TLSConfig *TLSConfig
|
||||
}
|
||||
|
||||
// ClientConfig copies the configuration with a new client address, region, and
|
||||
// whether the client has TLS enabled.
|
||||
func (c *Config) ClientConfig(region, address string, tlsEnabled bool) *Config {
|
||||
scheme := "http"
|
||||
if tlsEnabled {
|
||||
scheme = "https"
|
||||
}
|
||||
defaultConfig := DefaultConfig()
|
||||
config := &Config{
|
||||
Address: fmt.Sprintf("%s://%s", scheme, address),
|
||||
Region: region,
|
||||
Namespace: c.Namespace,
|
||||
httpClient: defaultConfig.httpClient,
|
||||
SecretID: c.SecretID,
|
||||
HttpAuth: c.HttpAuth,
|
||||
WaitTime: c.WaitTime,
|
||||
TLSConfig: c.TLSConfig.Copy(),
|
||||
}
|
||||
|
||||
// Update the tls server name for connecting to a client
|
||||
if tlsEnabled && config.TLSConfig != nil {
|
||||
config.TLSConfig.TLSServerName = fmt.Sprintf("client.%s.nomad", region)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// TLSConfig contains the parameters needed to configure TLS on the HTTP client
|
||||
// used to communicate with Nomad.
|
||||
type TLSConfig struct {
|
||||
// CACert is the path to a PEM-encoded CA cert file to use to verify the
|
||||
// Nomad server SSL certificate.
|
||||
CACert string
|
||||
|
||||
// CAPath is the path to a directory of PEM-encoded CA cert files to verify
|
||||
// the Nomad server SSL certificate.
|
||||
CAPath string
|
||||
|
||||
// ClientCert is the path to the certificate for Nomad communication
|
||||
ClientCert string
|
||||
|
||||
// ClientKey is the path to the private key for Nomad communication
|
||||
ClientKey string
|
||||
|
||||
// TLSServerName, if set, is used to set the SNI host when connecting via
|
||||
// TLS.
|
||||
TLSServerName string
|
||||
|
||||
// Insecure enables or disables SSL verification
|
||||
Insecure bool
|
||||
}
|
||||
|
||||
func (t *TLSConfig) Copy() *TLSConfig {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nt := new(TLSConfig)
|
||||
*nt = *t
|
||||
return nt
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration for the client
|
||||
func DefaultConfig() *Config {
|
||||
config := &Config{
|
||||
Address: "http://127.0.0.1:4646",
|
||||
httpClient: cleanhttp.DefaultClient(),
|
||||
TLSConfig: &TLSConfig{},
|
||||
}
|
||||
transport := config.httpClient.Transport.(*http.Transport)
|
||||
transport.TLSHandshakeTimeout = 10 * time.Second
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
if addr := os.Getenv("NOMAD_ADDR"); addr != "" {
|
||||
config.Address = addr
|
||||
}
|
||||
if v := os.Getenv("NOMAD_REGION"); v != "" {
|
||||
config.Region = v
|
||||
}
|
||||
if v := os.Getenv("NOMAD_NAMESPACE"); v != "" {
|
||||
config.Namespace = v
|
||||
}
|
||||
if auth := os.Getenv("NOMAD_HTTP_AUTH"); auth != "" {
|
||||
var username, password string
|
||||
if strings.Contains(auth, ":") {
|
||||
split := strings.SplitN(auth, ":", 2)
|
||||
username = split[0]
|
||||
password = split[1]
|
||||
} else {
|
||||
username = auth
|
||||
}
|
||||
|
||||
config.HttpAuth = &HttpBasicAuth{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
|
||||
// Read TLS specific env vars
|
||||
if v := os.Getenv("NOMAD_CACERT"); v != "" {
|
||||
config.TLSConfig.CACert = v
|
||||
}
|
||||
if v := os.Getenv("NOMAD_CAPATH"); v != "" {
|
||||
config.TLSConfig.CAPath = v
|
||||
}
|
||||
if v := os.Getenv("NOMAD_CLIENT_CERT"); v != "" {
|
||||
config.TLSConfig.ClientCert = v
|
||||
}
|
||||
if v := os.Getenv("NOMAD_CLIENT_KEY"); v != "" {
|
||||
config.TLSConfig.ClientKey = v
|
||||
}
|
||||
if v := os.Getenv("NOMAD_SKIP_VERIFY"); v != "" {
|
||||
if insecure, err := strconv.ParseBool(v); err == nil {
|
||||
config.TLSConfig.Insecure = insecure
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("NOMAD_TOKEN"); v != "" {
|
||||
config.SecretID = v
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// SetTimeout is used to place a timeout for connecting to Nomad. A negative
|
||||
// duration is ignored, a duration of zero means no timeout, and any other value
|
||||
// will add a timeout.
|
||||
func (c *Config) SetTimeout(t time.Duration) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("nil config")
|
||||
} else if c.httpClient == nil {
|
||||
return fmt.Errorf("nil HTTP client")
|
||||
} else if c.httpClient.Transport == nil {
|
||||
return fmt.Errorf("nil HTTP client transport")
|
||||
}
|
||||
|
||||
// Apply a timeout.
|
||||
if t.Nanoseconds() >= 0 {
|
||||
transport, ok := c.httpClient.Transport.(*http.Transport)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected HTTP transport: %T", c.httpClient.Transport)
|
||||
}
|
||||
|
||||
transport.DialContext = (&net.Dialer{
|
||||
Timeout: t,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureTLS applies a set of TLS configurations to the the HTTP client.
|
||||
func (c *Config) ConfigureTLS() error {
|
||||
if c.TLSConfig == nil {
|
||||
return nil
|
||||
}
|
||||
if c.httpClient == nil {
|
||||
return fmt.Errorf("config HTTP Client must be set")
|
||||
}
|
||||
|
||||
var clientCert tls.Certificate
|
||||
foundClientCert := false
|
||||
if c.TLSConfig.ClientCert != "" || c.TLSConfig.ClientKey != "" {
|
||||
if c.TLSConfig.ClientCert != "" && c.TLSConfig.ClientKey != "" {
|
||||
var err error
|
||||
clientCert, err = tls.LoadX509KeyPair(c.TLSConfig.ClientCert, c.TLSConfig.ClientKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
foundClientCert = true
|
||||
} else {
|
||||
return fmt.Errorf("Both client cert and client key must be provided")
|
||||
}
|
||||
}
|
||||
|
||||
clientTLSConfig := c.httpClient.Transport.(*http.Transport).TLSClientConfig
|
||||
rootConfig := &rootcerts.Config{
|
||||
CAFile: c.TLSConfig.CACert,
|
||||
CAPath: c.TLSConfig.CAPath,
|
||||
}
|
||||
if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientTLSConfig.InsecureSkipVerify = c.TLSConfig.Insecure
|
||||
|
||||
if foundClientCert {
|
||||
clientTLSConfig.Certificates = []tls.Certificate{clientCert}
|
||||
}
|
||||
if c.TLSConfig.TLSServerName != "" {
|
||||
clientTLSConfig.ServerName = c.TLSConfig.TLSServerName
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Client provides a client to the Nomad API
|
||||
type Client struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
// NewClient returns a new client
|
||||
func NewClient(config *Config) (*Client, error) {
|
||||
// bootstrap the config
|
||||
defConfig := DefaultConfig()
|
||||
|
||||
if config.Address == "" {
|
||||
config.Address = defConfig.Address
|
||||
} else if _, err := url.Parse(config.Address); err != nil {
|
||||
return nil, fmt.Errorf("invalid address '%s': %v", config.Address, err)
|
||||
}
|
||||
|
||||
if config.httpClient == nil {
|
||||
config.httpClient = defConfig.httpClient
|
||||
}
|
||||
|
||||
// Configure the TLS configurations
|
||||
if err := config.ConfigureTLS(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
config: *config,
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Address return the address of the Nomad agent
|
||||
func (c *Client) Address() string {
|
||||
return c.config.Address
|
||||
}
|
||||
|
||||
// SetRegion sets the region to forward API requests to.
|
||||
func (c *Client) SetRegion(region string) {
|
||||
c.config.Region = region
|
||||
}
|
||||
|
||||
// SetNamespace sets the namespace to forward API requests to.
|
||||
func (c *Client) SetNamespace(namespace string) {
|
||||
c.config.Namespace = namespace
|
||||
}
|
||||
|
||||
// GetNodeClient returns a new Client that will dial the specified node. If the
|
||||
// QueryOptions is set, its region will be used.
|
||||
func (c *Client) GetNodeClient(nodeID string, q *QueryOptions) (*Client, error) {
|
||||
return c.getNodeClientImpl(nodeID, -1, q, c.Nodes().Info)
|
||||
}
|
||||
|
||||
// GetNodeClientWithTimeout returns a new Client that will dial the specified
|
||||
// node using the specified timeout. If the QueryOptions is set, its region will
|
||||
// be used.
|
||||
func (c *Client) GetNodeClientWithTimeout(
|
||||
nodeID string, timeout time.Duration, q *QueryOptions) (*Client, error) {
|
||||
return c.getNodeClientImpl(nodeID, timeout, q, c.Nodes().Info)
|
||||
}
|
||||
|
||||
// nodeLookup is the definition of a function used to lookup a node. This is
|
||||
// largely used to mock the lookup in tests.
|
||||
type nodeLookup func(nodeID string, q *QueryOptions) (*Node, *QueryMeta, error)
|
||||
|
||||
// getNodeClientImpl is the implementation of creating a API client for
|
||||
// contacting a node. It takes a function to lookup the node such that it can be
|
||||
// mocked during tests.
|
||||
func (c *Client) getNodeClientImpl(nodeID string, timeout time.Duration, q *QueryOptions, lookup nodeLookup) (*Client, error) {
|
||||
node, _, err := lookup(nodeID, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node.Status == "down" {
|
||||
return nil, NodeDownErr
|
||||
}
|
||||
if node.HTTPAddr == "" {
|
||||
return nil, fmt.Errorf("http addr of node %q (%s) is not advertised", node.Name, nodeID)
|
||||
}
|
||||
|
||||
var region string
|
||||
switch {
|
||||
case q != nil && q.Region != "":
|
||||
// Prefer the region set in the query parameter
|
||||
region = q.Region
|
||||
case c.config.Region != "":
|
||||
// If the client is configured for a particular region use that
|
||||
region = c.config.Region
|
||||
default:
|
||||
// No region information is given so use the default.
|
||||
region = "global"
|
||||
}
|
||||
|
||||
// Get an API client for the node
|
||||
conf := c.config.ClientConfig(region, node.HTTPAddr, node.TLSEnabled)
|
||||
|
||||
// Set the timeout
|
||||
conf.SetTimeout(timeout)
|
||||
|
||||
return NewClient(conf)
|
||||
}
|
||||
|
||||
// SetSecretID sets the ACL token secret for API requests.
|
||||
func (c *Client) SetSecretID(secretID string) {
|
||||
c.config.SecretID = secretID
|
||||
}
|
||||
|
||||
// request is used to help build up a request
|
||||
type request struct {
|
||||
config *Config
|
||||
method string
|
||||
url *url.URL
|
||||
params url.Values
|
||||
token string
|
||||
body io.Reader
|
||||
obj interface{}
|
||||
}
|
||||
|
||||
// setQueryOptions is used to annotate the request with
|
||||
// additional query options
|
||||
func (r *request) setQueryOptions(q *QueryOptions) {
|
||||
if q == nil {
|
||||
return
|
||||
}
|
||||
if q.Region != "" {
|
||||
r.params.Set("region", q.Region)
|
||||
}
|
||||
if q.Namespace != "" {
|
||||
r.params.Set("namespace", q.Namespace)
|
||||
}
|
||||
if q.AuthToken != "" {
|
||||
r.token = q.AuthToken
|
||||
}
|
||||
if q.AllowStale {
|
||||
r.params.Set("stale", "")
|
||||
}
|
||||
if q.WaitIndex != 0 {
|
||||
r.params.Set("index", strconv.FormatUint(q.WaitIndex, 10))
|
||||
}
|
||||
if q.WaitTime != 0 {
|
||||
r.params.Set("wait", durToMsec(q.WaitTime))
|
||||
}
|
||||
if q.Prefix != "" {
|
||||
r.params.Set("prefix", q.Prefix)
|
||||
}
|
||||
for k, v := range q.Params {
|
||||
r.params.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// durToMsec converts a duration to a millisecond specified string
|
||||
func durToMsec(dur time.Duration) string {
|
||||
return fmt.Sprintf("%dms", dur/time.Millisecond)
|
||||
}
|
||||
|
||||
// setWriteOptions is used to annotate the request with
|
||||
// additional write options
|
||||
func (r *request) setWriteOptions(q *WriteOptions) {
|
||||
if q == nil {
|
||||
return
|
||||
}
|
||||
if q.Region != "" {
|
||||
r.params.Set("region", q.Region)
|
||||
}
|
||||
if q.Namespace != "" {
|
||||
r.params.Set("namespace", q.Namespace)
|
||||
}
|
||||
if q.AuthToken != "" {
|
||||
r.token = q.AuthToken
|
||||
}
|
||||
}
|
||||
|
||||
// toHTTP converts the request to an HTTP request
|
||||
func (r *request) toHTTP() (*http.Request, error) {
|
||||
// Encode the query parameters
|
||||
r.url.RawQuery = r.params.Encode()
|
||||
|
||||
// Check if we should encode the body
|
||||
if r.body == nil && r.obj != nil {
|
||||
if b, err := encodeBody(r.obj); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
r.body = b
|
||||
}
|
||||
}
|
||||
|
||||
// Create the HTTP request
|
||||
req, err := http.NewRequest(r.method, r.url.RequestURI(), r.body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Optionally configure HTTP basic authentication
|
||||
if r.url.User != nil {
|
||||
username := r.url.User.Username()
|
||||
password, _ := r.url.User.Password()
|
||||
req.SetBasicAuth(username, password)
|
||||
} else if r.config.HttpAuth != nil {
|
||||
req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password)
|
||||
}
|
||||
|
||||
req.Header.Add("Accept-Encoding", "gzip")
|
||||
if r.token != "" {
|
||||
req.Header.Set("X-Nomad-Token", r.token)
|
||||
}
|
||||
|
||||
req.URL.Host = r.url.Host
|
||||
req.URL.Scheme = r.url.Scheme
|
||||
req.Host = r.url.Host
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// newRequest is used to create a new request
|
||||
func (c *Client) newRequest(method, path string) (*request, error) {
|
||||
base, _ := url.Parse(c.config.Address)
|
||||
u, err := url.Parse(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &request{
|
||||
config: &c.config,
|
||||
method: method,
|
||||
url: &url.URL{
|
||||
Scheme: base.Scheme,
|
||||
User: base.User,
|
||||
Host: base.Host,
|
||||
Path: u.Path,
|
||||
},
|
||||
params: make(map[string][]string),
|
||||
}
|
||||
if c.config.Region != "" {
|
||||
r.params.Set("region", c.config.Region)
|
||||
}
|
||||
if c.config.Namespace != "" {
|
||||
r.params.Set("namespace", c.config.Namespace)
|
||||
}
|
||||
if c.config.WaitTime != 0 {
|
||||
r.params.Set("wait", durToMsec(r.config.WaitTime))
|
||||
}
|
||||
if c.config.SecretID != "" {
|
||||
r.token = r.config.SecretID
|
||||
}
|
||||
|
||||
// Add in the query parameters, if any
|
||||
for key, values := range u.Query() {
|
||||
for _, value := range values {
|
||||
r.params.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// multiCloser is to wrap a ReadCloser such that when close is called, multiple
|
||||
// Closes occur.
|
||||
type multiCloser struct {
|
||||
reader io.Reader
|
||||
inorderClose []io.Closer
|
||||
}
|
||||
|
||||
func (m *multiCloser) Close() error {
|
||||
for _, c := range m.inorderClose {
|
||||
if err := c.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *multiCloser) Read(p []byte) (int, error) {
|
||||
return m.reader.Read(p)
|
||||
}
|
||||
|
||||
// doRequest runs a request with our client
|
||||
func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
|
||||
req, err := r.toHTTP()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
start := time.Now()
|
||||
resp, err := c.config.httpClient.Do(req)
|
||||
diff := time.Now().Sub(start)
|
||||
|
||||
// If the response is compressed, we swap the body's reader.
|
||||
if resp != nil && resp.Header != nil {
|
||||
var reader io.ReadCloser
|
||||
switch resp.Header.Get("Content-Encoding") {
|
||||
case "gzip":
|
||||
greader, err := gzip.NewReader(resp.Body)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// The gzip reader doesn't close the wrapped reader so we use
|
||||
// multiCloser.
|
||||
reader = &multiCloser{
|
||||
reader: greader,
|
||||
inorderClose: []io.Closer{greader, resp.Body},
|
||||
}
|
||||
default:
|
||||
reader = resp.Body
|
||||
}
|
||||
resp.Body = reader
|
||||
}
|
||||
|
||||
return diff, resp, err
|
||||
}
|
||||
|
||||
// rawQuery makes a GET request to the specified endpoint but returns just the
|
||||
// response body.
|
||||
func (c *Client) rawQuery(endpoint string, q *QueryOptions) (io.ReadCloser, error) {
|
||||
r, err := c.newRequest("GET", endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.setQueryOptions(q)
|
||||
_, resp, err := requireOK(c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
// query is used to do a GET request against an endpoint
|
||||
// and deserialize the response into an interface using
|
||||
// standard Nomad conventions.
|
||||
func (c *Client) query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) {
|
||||
r, err := c.newRequest("GET", endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.setQueryOptions(q)
|
||||
rtt, resp, err := requireOK(c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
qm := &QueryMeta{}
|
||||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
if err := decodeBody(resp, out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return qm, nil
|
||||
}
|
||||
|
||||
// putQuery is used to do a PUT request when doing a read against an endpoint
|
||||
// and deserialize the response into an interface using standard Nomad
|
||||
// conventions.
|
||||
func (c *Client) putQuery(endpoint string, in, out interface{}, q *QueryOptions) (*QueryMeta, error) {
|
||||
r, err := c.newRequest("PUT", endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.setQueryOptions(q)
|
||||
r.obj = in
|
||||
rtt, resp, err := requireOK(c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
qm := &QueryMeta{}
|
||||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
|
||||
if err := decodeBody(resp, out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return qm, nil
|
||||
}
|
||||
|
||||
// write is used to do a PUT request against an endpoint
|
||||
// and serialize/deserialized using the standard Nomad conventions.
|
||||
func (c *Client) write(endpoint string, in, out interface{}, q *WriteOptions) (*WriteMeta, error) {
|
||||
r, err := c.newRequest("PUT", endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.setWriteOptions(q)
|
||||
r.obj = in
|
||||
rtt, resp, err := requireOK(c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
wm := &WriteMeta{RequestTime: rtt}
|
||||
parseWriteMeta(resp, wm)
|
||||
|
||||
if out != nil {
|
||||
if err := decodeBody(resp, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// delete is used to do a DELETE request against an endpoint
|
||||
// and serialize/deserialized using the standard Nomad conventions.
|
||||
func (c *Client) delete(endpoint string, out interface{}, q *WriteOptions) (*WriteMeta, error) {
|
||||
r, err := c.newRequest("DELETE", endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.setWriteOptions(q)
|
||||
rtt, resp, err := requireOK(c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
wm := &WriteMeta{RequestTime: rtt}
|
||||
parseWriteMeta(resp, wm)
|
||||
|
||||
if out != nil {
|
||||
if err := decodeBody(resp, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// parseQueryMeta is used to help parse query meta-data
|
||||
func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
|
||||
header := resp.Header
|
||||
|
||||
// Parse the X-Nomad-Index
|
||||
index, err := strconv.ParseUint(header.Get("X-Nomad-Index"), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse X-Nomad-Index: %v", err)
|
||||
}
|
||||
q.LastIndex = index
|
||||
|
||||
// Parse the X-Nomad-LastContact
|
||||
last, err := strconv.ParseUint(header.Get("X-Nomad-LastContact"), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse X-Nomad-LastContact: %v", err)
|
||||
}
|
||||
q.LastContact = time.Duration(last) * time.Millisecond
|
||||
|
||||
// Parse the X-Nomad-KnownLeader
|
||||
switch header.Get("X-Nomad-KnownLeader") {
|
||||
case "true":
|
||||
q.KnownLeader = true
|
||||
default:
|
||||
q.KnownLeader = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseWriteMeta is used to help parse write meta-data
|
||||
func parseWriteMeta(resp *http.Response, q *WriteMeta) error {
|
||||
header := resp.Header
|
||||
|
||||
// Parse the X-Nomad-Index
|
||||
index, err := strconv.ParseUint(header.Get("X-Nomad-Index"), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse X-Nomad-Index: %v", err)
|
||||
}
|
||||
q.LastIndex = index
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeBody is used to JSON decode a body
|
||||
func decodeBody(resp *http.Response, out interface{}) error {
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
return dec.Decode(out)
|
||||
}
|
||||
|
||||
// encodeBody is used to encode a request body
|
||||
func encodeBody(obj interface{}) (io.Reader, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
enc := json.NewEncoder(buf)
|
||||
if err := enc.Encode(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// requireOK is used to wrap doRequest and check for a 200
|
||||
func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
|
||||
if e != nil {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return d, nil, e
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, resp.Body)
|
||||
resp.Body.Close()
|
||||
return d, nil, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
|
||||
}
|
||||
return d, resp, nil
|
||||
}
|
||||
17
vendor/github.com/hashicorp/nomad/api/constraint.go
generated
vendored
Normal file
17
vendor/github.com/hashicorp/nomad/api/constraint.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package api
|
||||
|
||||
// Constraint is used to serialize a job placement constraint.
|
||||
type Constraint struct {
|
||||
LTarget string
|
||||
RTarget string
|
||||
Operand string
|
||||
}
|
||||
|
||||
// NewConstraint generates a new job placement constraint.
|
||||
func NewConstraint(left, operand, right string) *Constraint {
|
||||
return &Constraint{
|
||||
LTarget: left,
|
||||
RTarget: right,
|
||||
Operand: operand,
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/hashicorp/nomad/api/contexts/contexts.go
generated
vendored
Normal file
15
vendor/github.com/hashicorp/nomad/api/contexts/contexts.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package contexts
|
||||
|
||||
// Context defines the scope in which a search for Nomad object operates
|
||||
type Context string
|
||||
|
||||
const (
|
||||
Allocs Context = "allocs"
|
||||
Deployments Context = "deployment"
|
||||
Evals Context = "evals"
|
||||
Jobs Context = "jobs"
|
||||
Nodes Context = "nodes"
|
||||
Namespaces Context = "namespaces"
|
||||
Quotas Context = "quotas"
|
||||
All Context = "all"
|
||||
)
|
||||
264
vendor/github.com/hashicorp/nomad/api/deployments.go
generated
vendored
Normal file
264
vendor/github.com/hashicorp/nomad/api/deployments.go
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Deployments is used to query the deployments endpoints.
|
||||
type Deployments struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Deployments returns a new handle on the deployments.
|
||||
func (c *Client) Deployments() *Deployments {
|
||||
return &Deployments{client: c}
|
||||
}
|
||||
|
||||
// List is used to dump all of the deployments.
|
||||
func (d *Deployments) List(q *QueryOptions) ([]*Deployment, *QueryMeta, error) {
|
||||
var resp []*Deployment
|
||||
qm, err := d.client.query("/v1/deployments", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(DeploymentIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
func (d *Deployments) PrefixList(prefix string) ([]*Deployment, *QueryMeta, error) {
|
||||
return d.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to query a single deployment by its ID.
|
||||
func (d *Deployments) Info(deploymentID string, q *QueryOptions) (*Deployment, *QueryMeta, error) {
|
||||
var resp Deployment
|
||||
qm, err := d.client.query("/v1/deployment/"+deploymentID, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// Allocations is used to retrieve a set of allocations that are part of the
|
||||
// deployment
|
||||
func (d *Deployments) Allocations(deploymentID string, q *QueryOptions) ([]*AllocationListStub, *QueryMeta, error) {
|
||||
var resp []*AllocationListStub
|
||||
qm, err := d.client.query("/v1/deployment/allocations/"+deploymentID, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(AllocIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// Fail is used to fail the given deployment.
|
||||
func (d *Deployments) Fail(deploymentID string, q *WriteOptions) (*DeploymentUpdateResponse, *WriteMeta, error) {
|
||||
var resp DeploymentUpdateResponse
|
||||
req := &DeploymentFailRequest{
|
||||
DeploymentID: deploymentID,
|
||||
}
|
||||
wm, err := d.client.write("/v1/deployment/fail/"+deploymentID, req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// Pause is used to pause or unpause the given deployment.
|
||||
func (d *Deployments) Pause(deploymentID string, pause bool, q *WriteOptions) (*DeploymentUpdateResponse, *WriteMeta, error) {
|
||||
var resp DeploymentUpdateResponse
|
||||
req := &DeploymentPauseRequest{
|
||||
DeploymentID: deploymentID,
|
||||
Pause: pause,
|
||||
}
|
||||
wm, err := d.client.write("/v1/deployment/pause/"+deploymentID, req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// PromoteAll is used to promote all canaries in the given deployment
|
||||
func (d *Deployments) PromoteAll(deploymentID string, q *WriteOptions) (*DeploymentUpdateResponse, *WriteMeta, error) {
|
||||
var resp DeploymentUpdateResponse
|
||||
req := &DeploymentPromoteRequest{
|
||||
DeploymentID: deploymentID,
|
||||
All: true,
|
||||
}
|
||||
wm, err := d.client.write("/v1/deployment/promote/"+deploymentID, req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// PromoteGroups is used to promote canaries in the passed groups in the given deployment
|
||||
func (d *Deployments) PromoteGroups(deploymentID string, groups []string, q *WriteOptions) (*DeploymentUpdateResponse, *WriteMeta, error) {
|
||||
var resp DeploymentUpdateResponse
|
||||
req := &DeploymentPromoteRequest{
|
||||
DeploymentID: deploymentID,
|
||||
Groups: groups,
|
||||
}
|
||||
wm, err := d.client.write("/v1/deployment/promote/"+deploymentID, req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// SetAllocHealth is used to set allocation health for allocs that are part of
|
||||
// the given deployment
|
||||
func (d *Deployments) SetAllocHealth(deploymentID string, healthy, unhealthy []string, q *WriteOptions) (*DeploymentUpdateResponse, *WriteMeta, error) {
|
||||
var resp DeploymentUpdateResponse
|
||||
req := &DeploymentAllocHealthRequest{
|
||||
DeploymentID: deploymentID,
|
||||
HealthyAllocationIDs: healthy,
|
||||
UnhealthyAllocationIDs: unhealthy,
|
||||
}
|
||||
wm, err := d.client.write("/v1/deployment/allocation-health/"+deploymentID, req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
// Deployment is used to serialize an deployment.
|
||||
type Deployment struct {
|
||||
// ID is a generated UUID for the deployment
|
||||
ID string
|
||||
|
||||
// Namespace is the namespace the deployment is created in
|
||||
Namespace string
|
||||
|
||||
// JobID is the job the deployment is created for
|
||||
JobID string
|
||||
|
||||
// JobVersion is the version of the job at which the deployment is tracking
|
||||
JobVersion uint64
|
||||
|
||||
// JobModifyIndex is the ModifyIndex of the job which the deployment is
|
||||
// tracking.
|
||||
JobModifyIndex uint64
|
||||
|
||||
// JobSpecModifyIndex is the JobModifyIndex of the job which the
|
||||
// deployment is tracking.
|
||||
JobSpecModifyIndex uint64
|
||||
|
||||
// JobCreateIndex is the create index of the job which the deployment is
|
||||
// tracking. It is needed so that if the job gets stopped and reran we can
|
||||
// present the correct list of deployments for the job and not old ones.
|
||||
JobCreateIndex uint64
|
||||
|
||||
// TaskGroups is the set of task groups effected by the deployment and their
|
||||
// current deployment status.
|
||||
TaskGroups map[string]*DeploymentState
|
||||
|
||||
// The status of the deployment
|
||||
Status string
|
||||
|
||||
// StatusDescription allows a human readable description of the deployment
|
||||
// status.
|
||||
StatusDescription string
|
||||
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// DeploymentState tracks the state of a deployment for a given task group.
|
||||
type DeploymentState struct {
|
||||
PlacedCanaries []string
|
||||
AutoRevert bool
|
||||
ProgressDeadline time.Duration
|
||||
RequireProgressBy time.Time
|
||||
Promoted bool
|
||||
DesiredCanaries int
|
||||
DesiredTotal int
|
||||
PlacedAllocs int
|
||||
HealthyAllocs int
|
||||
UnhealthyAllocs int
|
||||
}
|
||||
|
||||
// DeploymentIndexSort is a wrapper to sort deployments by CreateIndex. We
|
||||
// reverse the test so that we get the highest index first.
|
||||
type DeploymentIndexSort []*Deployment
|
||||
|
||||
func (d DeploymentIndexSort) Len() int {
|
||||
return len(d)
|
||||
}
|
||||
|
||||
func (d DeploymentIndexSort) Less(i, j int) bool {
|
||||
return d[i].CreateIndex > d[j].CreateIndex
|
||||
}
|
||||
|
||||
func (d DeploymentIndexSort) Swap(i, j int) {
|
||||
d[i], d[j] = d[j], d[i]
|
||||
}
|
||||
|
||||
// DeploymentUpdateResponse is used to respond to a deployment change. The
|
||||
// response will include the modify index of the deployment as well as details
|
||||
// of any triggered evaluation.
|
||||
type DeploymentUpdateResponse struct {
|
||||
EvalID string
|
||||
EvalCreateIndex uint64
|
||||
DeploymentModifyIndex uint64
|
||||
RevertedJobVersion *uint64
|
||||
WriteMeta
|
||||
}
|
||||
|
||||
// DeploymentAllocHealthRequest is used to set the health of a set of
|
||||
// allocations as part of a deployment.
|
||||
type DeploymentAllocHealthRequest struct {
|
||||
DeploymentID string
|
||||
|
||||
// Marks these allocations as healthy, allow further allocations
|
||||
// to be rolled.
|
||||
HealthyAllocationIDs []string
|
||||
|
||||
// Any unhealthy allocations fail the deployment
|
||||
UnhealthyAllocationIDs []string
|
||||
|
||||
WriteRequest
|
||||
}
|
||||
|
||||
// DeploymentPromoteRequest is used to promote task groups in a deployment
|
||||
type DeploymentPromoteRequest struct {
|
||||
DeploymentID string
|
||||
|
||||
// All is to promote all task groups
|
||||
All bool
|
||||
|
||||
// Groups is used to set the promotion status per task group
|
||||
Groups []string
|
||||
|
||||
WriteRequest
|
||||
}
|
||||
|
||||
// DeploymentPauseRequest is used to pause a deployment
|
||||
type DeploymentPauseRequest struct {
|
||||
DeploymentID string
|
||||
|
||||
// Pause sets the pause status
|
||||
Pause bool
|
||||
|
||||
WriteRequest
|
||||
}
|
||||
|
||||
// DeploymentSpecificRequest is used to make a request specific to a particular
|
||||
// deployment
|
||||
type DeploymentSpecificRequest struct {
|
||||
DeploymentID string
|
||||
QueryOptions
|
||||
}
|
||||
|
||||
// DeploymentFailRequest is used to fail a particular deployment
|
||||
type DeploymentFailRequest struct {
|
||||
DeploymentID string
|
||||
WriteRequest
|
||||
}
|
||||
|
||||
// SingleDeploymentResponse is used to respond with a single deployment
|
||||
type SingleDeploymentResponse struct {
|
||||
Deployment *Deployment
|
||||
QueryMeta
|
||||
}
|
||||
99
vendor/github.com/hashicorp/nomad/api/evaluations.go
generated
vendored
Normal file
99
vendor/github.com/hashicorp/nomad/api/evaluations.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Evaluations is used to query the evaluation endpoints.
|
||||
type Evaluations struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Evaluations returns a new handle on the evaluations.
|
||||
func (c *Client) Evaluations() *Evaluations {
|
||||
return &Evaluations{client: c}
|
||||
}
|
||||
|
||||
// List is used to dump all of the evaluations.
|
||||
func (e *Evaluations) List(q *QueryOptions) ([]*Evaluation, *QueryMeta, error) {
|
||||
var resp []*Evaluation
|
||||
qm, err := e.client.query("/v1/evaluations", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(EvalIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
func (e *Evaluations) PrefixList(prefix string) ([]*Evaluation, *QueryMeta, error) {
|
||||
return e.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to query a single evaluation by its ID.
|
||||
func (e *Evaluations) Info(evalID string, q *QueryOptions) (*Evaluation, *QueryMeta, error) {
|
||||
var resp Evaluation
|
||||
qm, err := e.client.query("/v1/evaluation/"+evalID, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// Allocations is used to retrieve a set of allocations given
|
||||
// an evaluation ID.
|
||||
func (e *Evaluations) Allocations(evalID string, q *QueryOptions) ([]*AllocationListStub, *QueryMeta, error) {
|
||||
var resp []*AllocationListStub
|
||||
qm, err := e.client.query("/v1/evaluation/"+evalID+"/allocations", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(AllocIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// Evaluation is used to serialize an evaluation.
|
||||
type Evaluation struct {
|
||||
ID string
|
||||
Priority int
|
||||
Type string
|
||||
TriggeredBy string
|
||||
Namespace string
|
||||
JobID string
|
||||
JobModifyIndex uint64
|
||||
NodeID string
|
||||
NodeModifyIndex uint64
|
||||
DeploymentID string
|
||||
Status string
|
||||
StatusDescription string
|
||||
Wait time.Duration
|
||||
WaitUntil time.Time
|
||||
NextEval string
|
||||
PreviousEval string
|
||||
BlockedEval string
|
||||
FailedTGAllocs map[string]*AllocationMetric
|
||||
ClassEligibility map[string]bool
|
||||
EscapedComputedClass bool
|
||||
QuotaLimitReached string
|
||||
AnnotatePlan bool
|
||||
QueuedAllocations map[string]int
|
||||
SnapshotIndex uint64
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// EvalIndexSort is a wrapper to sort evaluations by CreateIndex.
|
||||
// We reverse the test so that we get the highest index first.
|
||||
type EvalIndexSort []*Evaluation
|
||||
|
||||
func (e EvalIndexSort) Len() int {
|
||||
return len(e)
|
||||
}
|
||||
|
||||
func (e EvalIndexSort) Less(i, j int) bool {
|
||||
return e[i].CreateIndex > e[j].CreateIndex
|
||||
}
|
||||
|
||||
func (e EvalIndexSort) Swap(i, j int) {
|
||||
e[i], e[j] = e[j], e[i]
|
||||
}
|
||||
439
vendor/github.com/hashicorp/nomad/api/fs.go
generated
vendored
Normal file
439
vendor/github.com/hashicorp/nomad/api/fs.go
generated
vendored
Normal file
@@ -0,0 +1,439 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// OriginStart and OriginEnd are the available parameters for the origin
|
||||
// argument when streaming a file. They respectively offset from the start
|
||||
// and end of a file.
|
||||
OriginStart = "start"
|
||||
OriginEnd = "end"
|
||||
)
|
||||
|
||||
// AllocFileInfo holds information about a file inside the AllocDir
|
||||
type AllocFileInfo struct {
|
||||
Name string
|
||||
IsDir bool
|
||||
Size int64
|
||||
FileMode string
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
// StreamFrame is used to frame data of a file when streaming
|
||||
type StreamFrame struct {
|
||||
Offset int64 `json:",omitempty"`
|
||||
Data []byte `json:",omitempty"`
|
||||
File string `json:",omitempty"`
|
||||
FileEvent string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// IsHeartbeat returns if the frame is a heartbeat frame
|
||||
func (s *StreamFrame) IsHeartbeat() bool {
|
||||
return len(s.Data) == 0 && s.FileEvent == "" && s.File == "" && s.Offset == 0
|
||||
}
|
||||
|
||||
// AllocFS is used to introspect an allocation directory on a Nomad client
|
||||
type AllocFS struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// AllocFS returns an handle to the AllocFS endpoints
|
||||
func (c *Client) AllocFS() *AllocFS {
|
||||
return &AllocFS{client: c}
|
||||
}
|
||||
|
||||
// List is used to list the files at a given path of an allocation directory
|
||||
func (a *AllocFS) List(alloc *Allocation, path string, q *QueryOptions) ([]*AllocFileInfo, *QueryMeta, error) {
|
||||
if q == nil {
|
||||
q = &QueryOptions{}
|
||||
}
|
||||
if q.Params == nil {
|
||||
q.Params = make(map[string]string)
|
||||
}
|
||||
q.Params["path"] = path
|
||||
|
||||
var resp []*AllocFileInfo
|
||||
qm, err := a.client.query(fmt.Sprintf("/v1/client/fs/ls/%s", alloc.ID), &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// Stat is used to stat a file at a given path of an allocation directory
|
||||
func (a *AllocFS) Stat(alloc *Allocation, path string, q *QueryOptions) (*AllocFileInfo, *QueryMeta, error) {
|
||||
if q == nil {
|
||||
q = &QueryOptions{}
|
||||
}
|
||||
if q.Params == nil {
|
||||
q.Params = make(map[string]string)
|
||||
}
|
||||
|
||||
q.Params["path"] = path
|
||||
|
||||
var resp AllocFileInfo
|
||||
qm, err := a.client.query(fmt.Sprintf("/v1/client/fs/stat/%s", alloc.ID), &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// ReadAt is used to read bytes at a given offset until limit at the given path
|
||||
// in an allocation directory. If limit is <= 0, there is no limit.
|
||||
func (a *AllocFS) ReadAt(alloc *Allocation, path string, offset int64, limit int64, q *QueryOptions) (io.ReadCloser, error) {
|
||||
nodeClient, err := a.client.GetNodeClientWithTimeout(alloc.NodeID, ClientConnTimeout, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if q == nil {
|
||||
q = &QueryOptions{}
|
||||
}
|
||||
if q.Params == nil {
|
||||
q.Params = make(map[string]string)
|
||||
}
|
||||
|
||||
q.Params["path"] = path
|
||||
q.Params["offset"] = strconv.FormatInt(offset, 10)
|
||||
q.Params["limit"] = strconv.FormatInt(limit, 10)
|
||||
|
||||
reqPath := fmt.Sprintf("/v1/client/fs/readat/%s", alloc.ID)
|
||||
r, err := nodeClient.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
// There was a networking error when talking directly to the client.
|
||||
if _, ok := err.(net.Error); !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Try via the server
|
||||
r, err = a.client.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Cat is used to read contents of a file at the given path in an allocation
|
||||
// directory
|
||||
func (a *AllocFS) Cat(alloc *Allocation, path string, q *QueryOptions) (io.ReadCloser, error) {
|
||||
nodeClient, err := a.client.GetNodeClientWithTimeout(alloc.NodeID, ClientConnTimeout, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if q == nil {
|
||||
q = &QueryOptions{}
|
||||
}
|
||||
if q.Params == nil {
|
||||
q.Params = make(map[string]string)
|
||||
}
|
||||
|
||||
q.Params["path"] = path
|
||||
reqPath := fmt.Sprintf("/v1/client/fs/cat/%s", alloc.ID)
|
||||
r, err := nodeClient.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
// There was a networking error when talking directly to the client.
|
||||
if _, ok := err.(net.Error); !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Try via the server
|
||||
r, err = a.client.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Stream streams the content of a file blocking on EOF.
|
||||
// The parameters are:
|
||||
// * path: path to file to stream.
|
||||
// * offset: The offset to start streaming data at.
|
||||
// * origin: Either "start" or "end" and defines from where the offset is applied.
|
||||
// * cancel: A channel that when closed, streaming will end.
|
||||
//
|
||||
// The return value is a channel that will emit StreamFrames as they are read.
|
||||
func (a *AllocFS) Stream(alloc *Allocation, path, origin string, offset int64,
|
||||
cancel <-chan struct{}, q *QueryOptions) (<-chan *StreamFrame, <-chan error) {
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
nodeClient, err := a.client.GetNodeClientWithTimeout(alloc.NodeID, ClientConnTimeout, q)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return nil, errCh
|
||||
}
|
||||
|
||||
if q == nil {
|
||||
q = &QueryOptions{}
|
||||
}
|
||||
if q.Params == nil {
|
||||
q.Params = make(map[string]string)
|
||||
}
|
||||
|
||||
q.Params["path"] = path
|
||||
q.Params["offset"] = strconv.FormatInt(offset, 10)
|
||||
q.Params["origin"] = origin
|
||||
|
||||
reqPath := fmt.Sprintf("/v1/client/fs/stream/%s", alloc.ID)
|
||||
r, err := nodeClient.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
// There was a networking error when talking directly to the client.
|
||||
if _, ok := err.(net.Error); !ok {
|
||||
errCh <- err
|
||||
return nil, errCh
|
||||
}
|
||||
|
||||
// Try via the server
|
||||
r, err = a.client.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return nil, errCh
|
||||
}
|
||||
}
|
||||
|
||||
// Create the output channel
|
||||
frames := make(chan *StreamFrame, 10)
|
||||
|
||||
go func() {
|
||||
// Close the body
|
||||
defer r.Close()
|
||||
|
||||
// Create a decoder
|
||||
dec := json.NewDecoder(r)
|
||||
|
||||
for {
|
||||
// Check if we have been cancelled
|
||||
select {
|
||||
case <-cancel:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// Decode the next frame
|
||||
var frame StreamFrame
|
||||
if err := dec.Decode(&frame); err != nil {
|
||||
errCh <- err
|
||||
close(frames)
|
||||
return
|
||||
}
|
||||
|
||||
// Discard heartbeat frames
|
||||
if frame.IsHeartbeat() {
|
||||
continue
|
||||
}
|
||||
|
||||
frames <- &frame
|
||||
}
|
||||
}()
|
||||
|
||||
return frames, errCh
|
||||
}
|
||||
|
||||
// Logs streams the content of a tasks logs blocking on EOF.
|
||||
// The parameters are:
|
||||
// * allocation: the allocation to stream from.
|
||||
// * follow: Whether the logs should be followed.
|
||||
// * task: the tasks name to stream logs for.
|
||||
// * logType: Either "stdout" or "stderr"
|
||||
// * origin: Either "start" or "end" and defines from where the offset is applied.
|
||||
// * offset: The offset to start streaming data at.
|
||||
// * cancel: A channel that when closed, streaming will end.
|
||||
//
|
||||
// The return value is a channel that will emit StreamFrames as they are read.
|
||||
// The chan will be closed when follow=false and the end of the file is
|
||||
// reached.
|
||||
//
|
||||
// Unexpected (non-EOF) errors will be sent on the error chan.
|
||||
func (a *AllocFS) Logs(alloc *Allocation, follow bool, task, logType, origin string,
|
||||
offset int64, cancel <-chan struct{}, q *QueryOptions) (<-chan *StreamFrame, <-chan error) {
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
nodeClient, err := a.client.GetNodeClientWithTimeout(alloc.NodeID, ClientConnTimeout, q)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return nil, errCh
|
||||
}
|
||||
|
||||
if q == nil {
|
||||
q = &QueryOptions{}
|
||||
}
|
||||
if q.Params == nil {
|
||||
q.Params = make(map[string]string)
|
||||
}
|
||||
|
||||
q.Params["follow"] = strconv.FormatBool(follow)
|
||||
q.Params["task"] = task
|
||||
q.Params["type"] = logType
|
||||
q.Params["origin"] = origin
|
||||
q.Params["offset"] = strconv.FormatInt(offset, 10)
|
||||
|
||||
reqPath := fmt.Sprintf("/v1/client/fs/logs/%s", alloc.ID)
|
||||
r, err := nodeClient.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
// There was a networking error when talking directly to the client.
|
||||
if _, ok := err.(net.Error); !ok {
|
||||
errCh <- err
|
||||
return nil, errCh
|
||||
}
|
||||
|
||||
// Try via the server
|
||||
r, err = a.client.rawQuery(reqPath, q)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return nil, errCh
|
||||
}
|
||||
}
|
||||
|
||||
// Create the output channel
|
||||
frames := make(chan *StreamFrame, 10)
|
||||
|
||||
go func() {
|
||||
// Close the body
|
||||
defer r.Close()
|
||||
|
||||
// Create a decoder
|
||||
dec := json.NewDecoder(r)
|
||||
|
||||
for {
|
||||
// Check if we have been cancelled
|
||||
select {
|
||||
case <-cancel:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// Decode the next frame
|
||||
var frame StreamFrame
|
||||
if err := dec.Decode(&frame); err != nil {
|
||||
if err == io.EOF || err == io.ErrClosedPipe {
|
||||
close(frames)
|
||||
} else {
|
||||
errCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Discard heartbeat frames
|
||||
if frame.IsHeartbeat() {
|
||||
continue
|
||||
}
|
||||
|
||||
frames <- &frame
|
||||
}
|
||||
}()
|
||||
|
||||
return frames, errCh
|
||||
}
|
||||
|
||||
// FrameReader is used to convert a stream of frames into a read closer.
|
||||
type FrameReader struct {
|
||||
frames <-chan *StreamFrame
|
||||
errCh <-chan error
|
||||
cancelCh chan struct{}
|
||||
|
||||
closedLock sync.Mutex
|
||||
closed bool
|
||||
|
||||
unblockTime time.Duration
|
||||
|
||||
frame *StreamFrame
|
||||
frameOffset int
|
||||
|
||||
byteOffset int
|
||||
}
|
||||
|
||||
// NewFrameReader takes a channel of frames and returns a FrameReader which
|
||||
// implements io.ReadCloser
|
||||
func NewFrameReader(frames <-chan *StreamFrame, errCh <-chan error, cancelCh chan struct{}) *FrameReader {
|
||||
return &FrameReader{
|
||||
frames: frames,
|
||||
errCh: errCh,
|
||||
cancelCh: cancelCh,
|
||||
}
|
||||
}
|
||||
|
||||
// SetUnblockTime sets the time to unblock and return zero bytes read. If the
|
||||
// duration is unset or is zero or less, the read will block until data is read.
|
||||
func (f *FrameReader) SetUnblockTime(d time.Duration) {
|
||||
f.unblockTime = d
|
||||
}
|
||||
|
||||
// Offset returns the offset into the stream.
|
||||
func (f *FrameReader) Offset() int {
|
||||
return f.byteOffset
|
||||
}
|
||||
|
||||
// Read reads the data of the incoming frames into the bytes buffer. Returns EOF
|
||||
// when there are no more frames.
|
||||
func (f *FrameReader) Read(p []byte) (n int, err error) {
|
||||
f.closedLock.Lock()
|
||||
closed := f.closed
|
||||
f.closedLock.Unlock()
|
||||
if closed {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if f.frame == nil {
|
||||
var unblock <-chan time.Time
|
||||
if f.unblockTime.Nanoseconds() > 0 {
|
||||
unblock = time.After(f.unblockTime)
|
||||
}
|
||||
|
||||
select {
|
||||
case frame, ok := <-f.frames:
|
||||
if !ok {
|
||||
return 0, io.EOF
|
||||
}
|
||||
f.frame = frame
|
||||
|
||||
// Store the total offset into the file
|
||||
f.byteOffset = int(f.frame.Offset)
|
||||
case <-unblock:
|
||||
return 0, nil
|
||||
case err := <-f.errCh:
|
||||
return 0, err
|
||||
case <-f.cancelCh:
|
||||
return 0, io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the data out of the frame and update our offset
|
||||
n = copy(p, f.frame.Data[f.frameOffset:])
|
||||
f.frameOffset += n
|
||||
|
||||
// Clear the frame and its offset once we have read everything
|
||||
if len(f.frame.Data) == f.frameOffset {
|
||||
f.frame = nil
|
||||
f.frameOffset = 0
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Close cancels the stream of frames
|
||||
func (f *FrameReader) Close() error {
|
||||
f.closedLock.Lock()
|
||||
defer f.closedLock.Unlock()
|
||||
if f.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
close(f.cancelCh)
|
||||
f.closed = true
|
||||
return nil
|
||||
}
|
||||
1063
vendor/github.com/hashicorp/nomad/api/jobs.go
generated
vendored
Normal file
1063
vendor/github.com/hashicorp/nomad/api/jobs.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
110
vendor/github.com/hashicorp/nomad/api/jobs_testing.go
generated
vendored
Normal file
110
vendor/github.com/hashicorp/nomad/api/jobs_testing.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
)
|
||||
|
||||
func MockJob() *Job {
|
||||
job := &Job{
|
||||
Region: helper.StringToPtr("global"),
|
||||
ID: helper.StringToPtr(uuid.Generate()),
|
||||
Name: helper.StringToPtr("my-job"),
|
||||
Type: helper.StringToPtr("service"),
|
||||
Priority: helper.IntToPtr(50),
|
||||
AllAtOnce: helper.BoolToPtr(false),
|
||||
Datacenters: []string{"dc1"},
|
||||
Constraints: []*Constraint{
|
||||
{
|
||||
LTarget: "${attr.kernel.name}",
|
||||
RTarget: "linux",
|
||||
Operand: "=",
|
||||
},
|
||||
},
|
||||
TaskGroups: []*TaskGroup{
|
||||
{
|
||||
Name: helper.StringToPtr("web"),
|
||||
Count: helper.IntToPtr(10),
|
||||
EphemeralDisk: &EphemeralDisk{
|
||||
SizeMB: helper.IntToPtr(150),
|
||||
},
|
||||
RestartPolicy: &RestartPolicy{
|
||||
Attempts: helper.IntToPtr(3),
|
||||
Interval: helper.TimeToPtr(10 * time.Minute),
|
||||
Delay: helper.TimeToPtr(1 * time.Minute),
|
||||
Mode: helper.StringToPtr("delay"),
|
||||
},
|
||||
Tasks: []*Task{
|
||||
{
|
||||
Name: "web",
|
||||
Driver: "exec",
|
||||
Config: map[string]interface{}{
|
||||
"command": "/bin/date",
|
||||
},
|
||||
Env: map[string]string{
|
||||
"FOO": "bar",
|
||||
},
|
||||
Services: []*Service{
|
||||
{
|
||||
Name: "${TASK}-frontend",
|
||||
PortLabel: "http",
|
||||
Tags: []string{"pci:${meta.pci-dss}", "datacenter:${node.datacenter}"},
|
||||
Checks: []ServiceCheck{
|
||||
{
|
||||
Name: "check-table",
|
||||
Type: "script",
|
||||
Command: "/usr/local/check-table-${meta.database}",
|
||||
Args: []string{"${meta.version}"},
|
||||
Interval: 30 * time.Second,
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "${TASK}-admin",
|
||||
PortLabel: "admin",
|
||||
},
|
||||
},
|
||||
LogConfig: DefaultLogConfig(),
|
||||
Resources: &Resources{
|
||||
CPU: helper.IntToPtr(500),
|
||||
MemoryMB: helper.IntToPtr(256),
|
||||
Networks: []*NetworkResource{
|
||||
{
|
||||
MBits: helper.IntToPtr(50),
|
||||
DynamicPorts: []Port{{Label: "http"}, {Label: "admin"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Meta: map[string]string{
|
||||
"elb_check_type": "http",
|
||||
"elb_check_interval": "30s",
|
||||
"elb_check_min": "3",
|
||||
},
|
||||
},
|
||||
},
|
||||
Meta: map[string]string{
|
||||
"owner": "armon",
|
||||
},
|
||||
}
|
||||
job.Canonicalize()
|
||||
return job
|
||||
}
|
||||
|
||||
func MockPeriodicJob() *Job {
|
||||
j := MockJob()
|
||||
j.Type = helper.StringToPtr("batch")
|
||||
j.Periodic = &PeriodicConfig{
|
||||
Enabled: helper.BoolToPtr(true),
|
||||
SpecType: helper.StringToPtr("cron"),
|
||||
Spec: helper.StringToPtr("*/30 * * * *"),
|
||||
}
|
||||
return j
|
||||
}
|
||||
91
vendor/github.com/hashicorp/nomad/api/namespace.go
generated
vendored
Normal file
91
vendor/github.com/hashicorp/nomad/api/namespace.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Namespaces is used to query the namespace endpoints.
|
||||
type Namespaces struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Namespaces returns a new handle on the namespaces.
|
||||
func (c *Client) Namespaces() *Namespaces {
|
||||
return &Namespaces{client: c}
|
||||
}
|
||||
|
||||
// List is used to dump all of the namespaces.
|
||||
func (n *Namespaces) List(q *QueryOptions) ([]*Namespace, *QueryMeta, error) {
|
||||
var resp []*Namespace
|
||||
qm, err := n.client.query("/v1/namespaces", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(NamespaceIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// PrefixList is used to do a PrefixList search over namespaces
|
||||
func (n *Namespaces) PrefixList(prefix string, q *QueryOptions) ([]*Namespace, *QueryMeta, error) {
|
||||
if q == nil {
|
||||
q = &QueryOptions{Prefix: prefix}
|
||||
} else {
|
||||
q.Prefix = prefix
|
||||
}
|
||||
|
||||
return n.List(q)
|
||||
}
|
||||
|
||||
// Info is used to query a single namespace by its name.
|
||||
func (n *Namespaces) Info(name string, q *QueryOptions) (*Namespace, *QueryMeta, error) {
|
||||
var resp Namespace
|
||||
qm, err := n.client.query("/v1/namespace/"+name, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// Register is used to register a namespace.
|
||||
func (n *Namespaces) Register(namespace *Namespace, q *WriteOptions) (*WriteMeta, error) {
|
||||
wm, err := n.client.write("/v1/namespace", namespace, nil, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Delete is used to delete a namespace
|
||||
func (n *Namespaces) Delete(namespace string, q *WriteOptions) (*WriteMeta, error) {
|
||||
wm, err := n.client.delete(fmt.Sprintf("/v1/namespace/%s", namespace), nil, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Namespace is used to serialize a namespace.
|
||||
type Namespace struct {
|
||||
Name string
|
||||
Description string
|
||||
Quota string
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// NamespaceIndexSort is a wrapper to sort Namespaces by CreateIndex. We
|
||||
// reverse the test so that we get the highest index first.
|
||||
type NamespaceIndexSort []*Namespace
|
||||
|
||||
func (n NamespaceIndexSort) Len() int {
|
||||
return len(n)
|
||||
}
|
||||
|
||||
func (n NamespaceIndexSort) Less(i, j int) bool {
|
||||
return n[i].CreateIndex > n[j].CreateIndex
|
||||
}
|
||||
|
||||
func (n NamespaceIndexSort) Swap(i, j int) {
|
||||
n[i], n[j] = n[j], n[i]
|
||||
}
|
||||
611
vendor/github.com/hashicorp/nomad/api/nodes.go
generated
vendored
Normal file
611
vendor/github.com/hashicorp/nomad/api/nodes.go
generated
vendored
Normal file
@@ -0,0 +1,611 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
// Nodes is used to query node-related API endpoints
|
||||
type Nodes struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Nodes returns a handle on the node endpoints.
|
||||
func (c *Client) Nodes() *Nodes {
|
||||
return &Nodes{client: c}
|
||||
}
|
||||
|
||||
// List is used to list out all of the nodes
|
||||
func (n *Nodes) List(q *QueryOptions) ([]*NodeListStub, *QueryMeta, error) {
|
||||
var resp NodeIndexSort
|
||||
qm, err := n.client.query("/v1/nodes", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(resp)
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
func (n *Nodes) PrefixList(prefix string) ([]*NodeListStub, *QueryMeta, error) {
|
||||
return n.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to query a specific node by its ID.
|
||||
func (n *Nodes) Info(nodeID string, q *QueryOptions) (*Node, *QueryMeta, error) {
|
||||
var resp Node
|
||||
qm, err := n.client.query("/v1/node/"+nodeID, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// NodeUpdateDrainRequest is used to update the drain specification for a node.
|
||||
type NodeUpdateDrainRequest struct {
|
||||
// NodeID is the node to update the drain specification for.
|
||||
NodeID string
|
||||
|
||||
// DrainSpec is the drain specification to set for the node. A nil DrainSpec
|
||||
// will disable draining.
|
||||
DrainSpec *DrainSpec
|
||||
|
||||
// MarkEligible marks the node as eligible for scheduling if removing
|
||||
// the drain strategy.
|
||||
MarkEligible bool
|
||||
}
|
||||
|
||||
// NodeDrainUpdateResponse is used to respond to a node drain update
|
||||
type NodeDrainUpdateResponse struct {
|
||||
NodeModifyIndex uint64
|
||||
EvalIDs []string
|
||||
EvalCreateIndex uint64
|
||||
WriteMeta
|
||||
}
|
||||
|
||||
// UpdateDrain is used to update the drain strategy for a given node. If
|
||||
// markEligible is true and the drain is being removed, the node will be marked
|
||||
// as having its scheduling being eligible
|
||||
func (n *Nodes) UpdateDrain(nodeID string, spec *DrainSpec, markEligible bool, q *WriteOptions) (*NodeDrainUpdateResponse, error) {
|
||||
req := &NodeUpdateDrainRequest{
|
||||
NodeID: nodeID,
|
||||
DrainSpec: spec,
|
||||
MarkEligible: markEligible,
|
||||
}
|
||||
|
||||
var resp NodeDrainUpdateResponse
|
||||
wm, err := n.client.write("/v1/node/"+nodeID+"/drain", req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.WriteMeta = *wm
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// MonitorMsgLevels represents the severity log level of a MonitorMessage.
|
||||
type MonitorMsgLevel int
|
||||
|
||||
const (
|
||||
MonitorMsgLevelNormal MonitorMsgLevel = 0
|
||||
MonitorMsgLevelInfo MonitorMsgLevel = 1
|
||||
MonitorMsgLevelWarn MonitorMsgLevel = 2
|
||||
MonitorMsgLevelError MonitorMsgLevel = 3
|
||||
)
|
||||
|
||||
// MonitorMessage contains a message and log level.
|
||||
type MonitorMessage struct {
|
||||
Level MonitorMsgLevel
|
||||
Message string
|
||||
}
|
||||
|
||||
// Messagef formats a new MonitorMessage.
|
||||
func Messagef(lvl MonitorMsgLevel, msg string, args ...interface{}) *MonitorMessage {
|
||||
return &MonitorMessage{
|
||||
Level: lvl,
|
||||
Message: fmt.Sprintf(msg, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MonitorMessage) String() string {
|
||||
return m.Message
|
||||
}
|
||||
|
||||
// MonitorDrain emits drain related events on the returned string channel. The
|
||||
// channel will be closed when all allocations on the draining node have
|
||||
// stopped or the context is canceled.
|
||||
func (n *Nodes) MonitorDrain(ctx context.Context, nodeID string, index uint64, ignoreSys bool) <-chan *MonitorMessage {
|
||||
outCh := make(chan *MonitorMessage, 8)
|
||||
nodeCh := make(chan *MonitorMessage, 1)
|
||||
allocCh := make(chan *MonitorMessage, 8)
|
||||
|
||||
// Multiplex node and alloc chans onto outCh. This goroutine closes
|
||||
// outCh when other chans have been closed or context canceled.
|
||||
multiplexCtx, cancel := context.WithCancel(ctx)
|
||||
go n.monitorDrainMultiplex(multiplexCtx, cancel, outCh, nodeCh, allocCh)
|
||||
|
||||
// Monitor node for updates
|
||||
go n.monitorDrainNode(multiplexCtx, cancel, nodeID, index, nodeCh)
|
||||
|
||||
// Monitor allocs on node for updates
|
||||
go n.monitorDrainAllocs(multiplexCtx, nodeID, ignoreSys, allocCh)
|
||||
|
||||
return outCh
|
||||
}
|
||||
|
||||
// monitorDrainMultiplex multiplexes node and alloc updates onto the out chan.
|
||||
// Closes out chan when either the context is canceled, both update chans are
|
||||
// closed, or an error occurs.
|
||||
func (n *Nodes) monitorDrainMultiplex(ctx context.Context, cancel func(),
|
||||
outCh chan<- *MonitorMessage, nodeCh, allocCh <-chan *MonitorMessage) {
|
||||
|
||||
defer cancel()
|
||||
defer close(outCh)
|
||||
|
||||
nodeOk := true
|
||||
allocOk := true
|
||||
var msg *MonitorMessage
|
||||
for {
|
||||
// If both chans have been closed, close the output chan
|
||||
if !nodeOk && !allocOk {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case msg, nodeOk = <-nodeCh:
|
||||
if !nodeOk {
|
||||
// nil chan to prevent further recvs
|
||||
nodeCh = nil
|
||||
}
|
||||
|
||||
case msg, allocOk = <-allocCh:
|
||||
if !allocOk {
|
||||
// nil chan to prevent further recvs
|
||||
allocCh = nil
|
||||
}
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
if msg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case outCh <- msg:
|
||||
case <-ctx.Done():
|
||||
|
||||
// If we are exiting but we have a message, attempt to send it
|
||||
// so we don't lose a message but do not block.
|
||||
select {
|
||||
case outCh <- msg:
|
||||
default:
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Abort on error messages
|
||||
if msg.Level == MonitorMsgLevelError {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// monitorDrainNode emits node updates on nodeCh and closes the channel when
|
||||
// the node has finished draining.
|
||||
func (n *Nodes) monitorDrainNode(ctx context.Context, cancel func(),
|
||||
nodeID string, index uint64, nodeCh chan<- *MonitorMessage) {
|
||||
defer close(nodeCh)
|
||||
|
||||
var lastStrategy *DrainStrategy
|
||||
var strategyChanged bool
|
||||
q := QueryOptions{
|
||||
AllowStale: true,
|
||||
WaitIndex: index,
|
||||
}
|
||||
for {
|
||||
node, meta, err := n.Info(nodeID, &q)
|
||||
if err != nil {
|
||||
msg := Messagef(MonitorMsgLevelError, "Error monitoring node: %v", err)
|
||||
select {
|
||||
case nodeCh <- msg:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if node.DrainStrategy == nil {
|
||||
var msg *MonitorMessage
|
||||
if strategyChanged {
|
||||
msg = Messagef(MonitorMsgLevelInfo, "Node %q has marked all allocations for migration", nodeID)
|
||||
} else {
|
||||
msg = Messagef(MonitorMsgLevelInfo, "No drain strategy set for node %s", nodeID)
|
||||
defer cancel()
|
||||
}
|
||||
select {
|
||||
case nodeCh <- msg:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if node.Status == structs.NodeStatusDown {
|
||||
msg := Messagef(MonitorMsgLevelWarn, "Node %q down", nodeID)
|
||||
select {
|
||||
case nodeCh <- msg:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
// DrainStrategy changed
|
||||
if lastStrategy != nil && !node.DrainStrategy.Equal(lastStrategy) {
|
||||
msg := Messagef(MonitorMsgLevelInfo, "Node %q drain updated: %s", nodeID, node.DrainStrategy)
|
||||
select {
|
||||
case nodeCh <- msg:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
lastStrategy = node.DrainStrategy
|
||||
strategyChanged = true
|
||||
|
||||
// Drain still ongoing, update index and block for updates
|
||||
q.WaitIndex = meta.LastIndex
|
||||
}
|
||||
}
|
||||
|
||||
// monitorDrainAllocs emits alloc updates on allocCh and closes the channel
|
||||
// when the node has finished draining.
|
||||
func (n *Nodes) monitorDrainAllocs(ctx context.Context, nodeID string, ignoreSys bool, allocCh chan<- *MonitorMessage) {
|
||||
defer close(allocCh)
|
||||
|
||||
q := QueryOptions{AllowStale: true}
|
||||
initial := make(map[string]*Allocation, 4)
|
||||
|
||||
for {
|
||||
allocs, meta, err := n.Allocations(nodeID, &q)
|
||||
if err != nil {
|
||||
msg := Messagef(MonitorMsgLevelError, "Error monitoring allocations: %v", err)
|
||||
select {
|
||||
case allocCh <- msg:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
q.WaitIndex = meta.LastIndex
|
||||
|
||||
runningAllocs := 0
|
||||
for _, a := range allocs {
|
||||
// Get previous version of alloc
|
||||
orig, existing := initial[a.ID]
|
||||
|
||||
// Update local alloc state
|
||||
initial[a.ID] = a
|
||||
|
||||
migrating := a.DesiredTransition.ShouldMigrate()
|
||||
|
||||
var msg string
|
||||
switch {
|
||||
case !existing:
|
||||
// Should only be possible if response
|
||||
// from initial Allocations call was
|
||||
// stale. No need to output
|
||||
|
||||
case orig.ClientStatus != a.ClientStatus:
|
||||
// Alloc status has changed; output
|
||||
msg = fmt.Sprintf("status %s -> %s", orig.ClientStatus, a.ClientStatus)
|
||||
|
||||
case migrating && !orig.DesiredTransition.ShouldMigrate():
|
||||
// Alloc was marked for migration
|
||||
msg = "marked for migration"
|
||||
|
||||
case migrating && (orig.DesiredStatus != a.DesiredStatus) && a.DesiredStatus == structs.AllocDesiredStatusStop:
|
||||
// Alloc has already been marked for migration and is now being stopped
|
||||
msg = "draining"
|
||||
}
|
||||
|
||||
if msg != "" {
|
||||
select {
|
||||
case allocCh <- Messagef(MonitorMsgLevelNormal, "Alloc %q %s", a.ID, msg):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore malformed allocs
|
||||
if a.Job == nil || a.Job.Type == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Track how many allocs are still running
|
||||
if ignoreSys && a.Job.Type != nil && *a.Job.Type == structs.JobTypeSystem {
|
||||
continue
|
||||
}
|
||||
|
||||
switch a.ClientStatus {
|
||||
case structs.AllocClientStatusPending, structs.AllocClientStatusRunning:
|
||||
runningAllocs++
|
||||
}
|
||||
}
|
||||
|
||||
// Exit if all allocs are terminal
|
||||
if runningAllocs == 0 {
|
||||
msg := Messagef(MonitorMsgLevelInfo, "All allocations on node %q have stopped.", nodeID)
|
||||
select {
|
||||
case allocCh <- msg:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NodeUpdateEligibilityRequest is used to update the drain specification for a node.
|
||||
type NodeUpdateEligibilityRequest struct {
|
||||
// NodeID is the node to update the drain specification for.
|
||||
NodeID string
|
||||
Eligibility string
|
||||
}
|
||||
|
||||
// NodeEligibilityUpdateResponse is used to respond to a node eligibility update
|
||||
type NodeEligibilityUpdateResponse struct {
|
||||
NodeModifyIndex uint64
|
||||
EvalIDs []string
|
||||
EvalCreateIndex uint64
|
||||
WriteMeta
|
||||
}
|
||||
|
||||
// ToggleEligibility is used to update the scheduling eligibility of the node
|
||||
func (n *Nodes) ToggleEligibility(nodeID string, eligible bool, q *WriteOptions) (*NodeEligibilityUpdateResponse, error) {
|
||||
e := structs.NodeSchedulingEligible
|
||||
if !eligible {
|
||||
e = structs.NodeSchedulingIneligible
|
||||
}
|
||||
|
||||
req := &NodeUpdateEligibilityRequest{
|
||||
NodeID: nodeID,
|
||||
Eligibility: e,
|
||||
}
|
||||
|
||||
var resp NodeEligibilityUpdateResponse
|
||||
wm, err := n.client.write("/v1/node/"+nodeID+"/eligibility", req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.WriteMeta = *wm
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// Allocations is used to return the allocations associated with a node.
|
||||
func (n *Nodes) Allocations(nodeID string, q *QueryOptions) ([]*Allocation, *QueryMeta, error) {
|
||||
var resp []*Allocation
|
||||
qm, err := n.client.query("/v1/node/"+nodeID+"/allocations", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(AllocationSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// ForceEvaluate is used to force-evaluate an existing node.
|
||||
func (n *Nodes) ForceEvaluate(nodeID string, q *WriteOptions) (string, *WriteMeta, error) {
|
||||
var resp nodeEvalResponse
|
||||
wm, err := n.client.write("/v1/node/"+nodeID+"/evaluate", nil, &resp, q)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return resp.EvalID, wm, nil
|
||||
}
|
||||
|
||||
func (n *Nodes) Stats(nodeID string, q *QueryOptions) (*HostStats, error) {
|
||||
var resp HostStats
|
||||
path := fmt.Sprintf("/v1/client/stats?node_id=%s", nodeID)
|
||||
if _, err := n.client.query(path, &resp, q); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (n *Nodes) GC(nodeID string, q *QueryOptions) error {
|
||||
var resp struct{}
|
||||
path := fmt.Sprintf("/v1/client/gc?node_id=%s", nodeID)
|
||||
_, err := n.client.query(path, &resp, q)
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO Add tests
|
||||
func (n *Nodes) GcAlloc(allocID string, q *QueryOptions) error {
|
||||
var resp struct{}
|
||||
path := fmt.Sprintf("/v1/client/allocation/%s/gc", allocID)
|
||||
_, err := n.client.query(path, &resp, q)
|
||||
return err
|
||||
}
|
||||
|
||||
// DriverInfo is used to deserialize a DriverInfo entry
|
||||
type DriverInfo struct {
|
||||
Attributes map[string]string
|
||||
Detected bool
|
||||
Healthy bool
|
||||
HealthDescription string
|
||||
UpdateTime time.Time
|
||||
}
|
||||
|
||||
// Node is used to deserialize a node entry.
|
||||
type Node struct {
|
||||
ID string
|
||||
Datacenter string
|
||||
Name string
|
||||
HTTPAddr string
|
||||
TLSEnabled bool
|
||||
Attributes map[string]string
|
||||
Resources *Resources
|
||||
Reserved *Resources
|
||||
Links map[string]string
|
||||
Meta map[string]string
|
||||
NodeClass string
|
||||
Drain bool
|
||||
DrainStrategy *DrainStrategy
|
||||
SchedulingEligibility string
|
||||
Status string
|
||||
StatusDescription string
|
||||
StatusUpdatedAt int64
|
||||
Events []*NodeEvent
|
||||
Drivers map[string]*DriverInfo
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// DrainStrategy describes a Node's drain behavior.
|
||||
type DrainStrategy struct {
|
||||
// DrainSpec is the user declared drain specification
|
||||
DrainSpec
|
||||
|
||||
// ForceDeadline is the deadline time for the drain after which drains will
|
||||
// be forced
|
||||
ForceDeadline time.Time
|
||||
}
|
||||
|
||||
// DrainSpec describes a Node's drain behavior.
|
||||
type DrainSpec struct {
|
||||
// Deadline is the duration after StartTime when the remaining
|
||||
// allocations on a draining Node should be told to stop.
|
||||
Deadline time.Duration
|
||||
|
||||
// IgnoreSystemJobs allows systems jobs to remain on the node even though it
|
||||
// has been marked for draining.
|
||||
IgnoreSystemJobs bool
|
||||
}
|
||||
|
||||
func (d *DrainStrategy) Equal(o *DrainStrategy) bool {
|
||||
if d == nil || o == nil {
|
||||
return d == o
|
||||
}
|
||||
|
||||
if d.ForceDeadline != o.ForceDeadline {
|
||||
return false
|
||||
}
|
||||
if d.Deadline != o.Deadline {
|
||||
return false
|
||||
}
|
||||
if d.IgnoreSystemJobs != o.IgnoreSystemJobs {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns a human readable version of the drain strategy.
|
||||
func (d *DrainStrategy) String() string {
|
||||
if d.IgnoreSystemJobs {
|
||||
return fmt.Sprintf("drain ignoring system jobs and deadline at %s", d.ForceDeadline)
|
||||
}
|
||||
return fmt.Sprintf("drain with deadline at %s", d.ForceDeadline)
|
||||
}
|
||||
|
||||
const (
|
||||
NodeEventSubsystemDrain = "Drain"
|
||||
NodeEventSubsystemDriver = "Driver"
|
||||
NodeEventSubsystemHeartbeat = "Heartbeat"
|
||||
NodeEventSubsystemCluster = "Cluster"
|
||||
)
|
||||
|
||||
// NodeEvent is a single unit representing a node’s state change
|
||||
type NodeEvent struct {
|
||||
Message string
|
||||
Subsystem string
|
||||
Details map[string]string
|
||||
Timestamp time.Time
|
||||
CreateIndex uint64
|
||||
}
|
||||
|
||||
// HostStats represents resource usage stats of the host running a Nomad client
|
||||
type HostStats struct {
|
||||
Memory *HostMemoryStats
|
||||
CPU []*HostCPUStats
|
||||
DiskStats []*HostDiskStats
|
||||
Uptime uint64
|
||||
CPUTicksConsumed float64
|
||||
}
|
||||
|
||||
type HostMemoryStats struct {
|
||||
Total uint64
|
||||
Available uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
}
|
||||
|
||||
type HostCPUStats struct {
|
||||
CPU string
|
||||
User float64
|
||||
System float64
|
||||
Idle float64
|
||||
}
|
||||
|
||||
type HostDiskStats struct {
|
||||
Device string
|
||||
Mountpoint string
|
||||
Size uint64
|
||||
Used uint64
|
||||
Available uint64
|
||||
UsedPercent float64
|
||||
InodesUsedPercent float64
|
||||
}
|
||||
|
||||
// NodeListStub is a subset of information returned during
|
||||
// node list operations.
|
||||
type NodeListStub struct {
|
||||
Address string
|
||||
ID string
|
||||
Datacenter string
|
||||
Name string
|
||||
NodeClass string
|
||||
Version string
|
||||
Drain bool
|
||||
SchedulingEligibility string
|
||||
Status string
|
||||
StatusDescription string
|
||||
Drivers map[string]*DriverInfo
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// NodeIndexSort reverse sorts nodes by CreateIndex
|
||||
type NodeIndexSort []*NodeListStub
|
||||
|
||||
func (n NodeIndexSort) Len() int {
|
||||
return len(n)
|
||||
}
|
||||
|
||||
func (n NodeIndexSort) Less(i, j int) bool {
|
||||
return n[i].CreateIndex > n[j].CreateIndex
|
||||
}
|
||||
|
||||
func (n NodeIndexSort) Swap(i, j int) {
|
||||
n[i], n[j] = n[j], n[i]
|
||||
}
|
||||
|
||||
// nodeEvalResponse is used to decode a force-eval.
|
||||
type nodeEvalResponse struct {
|
||||
EvalID string
|
||||
}
|
||||
|
||||
// AllocationSort reverse sorts allocs by CreateIndex.
|
||||
type AllocationSort []*Allocation
|
||||
|
||||
func (a AllocationSort) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a AllocationSort) Less(i, j int) bool {
|
||||
return a[i].CreateIndex > a[j].CreateIndex
|
||||
}
|
||||
|
||||
func (a AllocationSort) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
108
vendor/github.com/hashicorp/nomad/api/operator.go
generated
vendored
Normal file
108
vendor/github.com/hashicorp/nomad/api/operator.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package api
|
||||
|
||||
// Operator can be used to perform low-level operator tasks for Nomad.
|
||||
type Operator struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Operator returns a handle to the operator endpoints.
|
||||
func (c *Client) Operator() *Operator {
|
||||
return &Operator{c}
|
||||
}
|
||||
|
||||
// RaftServer has information about a server in the Raft configuration.
|
||||
type RaftServer struct {
|
||||
// ID is the unique ID for the server. These are currently the same
|
||||
// as the address, but they will be changed to a real GUID in a future
|
||||
// release of Nomad.
|
||||
ID string
|
||||
|
||||
// Node is the node name of the server, as known by Nomad, or this
|
||||
// will be set to "(unknown)" otherwise.
|
||||
Node string
|
||||
|
||||
// Address is the IP:port of the server, used for Raft communications.
|
||||
Address string
|
||||
|
||||
// Leader is true if this server is the current cluster leader.
|
||||
Leader bool
|
||||
|
||||
// Voter is true if this server has a vote in the cluster. This might
|
||||
// be false if the server is staging and still coming online, or if
|
||||
// it's a non-voting server, which will be added in a future release of
|
||||
// Nomad.
|
||||
Voter bool
|
||||
|
||||
// RaftProtocol is the version of the Raft protocol spoken by this server.
|
||||
RaftProtocol string
|
||||
}
|
||||
|
||||
// RaftConfiguration is returned when querying for the current Raft configuration.
|
||||
type RaftConfiguration struct {
|
||||
// Servers has the list of servers in the Raft configuration.
|
||||
Servers []*RaftServer
|
||||
|
||||
// Index has the Raft index of this configuration.
|
||||
Index uint64
|
||||
}
|
||||
|
||||
// RaftGetConfiguration is used to query the current Raft peer set.
|
||||
func (op *Operator) RaftGetConfiguration(q *QueryOptions) (*RaftConfiguration, error) {
|
||||
r, err := op.c.newRequest("GET", "/v1/operator/raft/configuration")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.setQueryOptions(q)
|
||||
_, resp, err := requireOK(op.c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var out RaftConfiguration
|
||||
if err := decodeBody(resp, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// RaftRemovePeerByAddress is used to kick a stale peer (one that it in the Raft
|
||||
// quorum but no longer known to Serf or the catalog) by address in the form of
|
||||
// "IP:port".
|
||||
func (op *Operator) RaftRemovePeerByAddress(address string, q *WriteOptions) error {
|
||||
r, err := op.c.newRequest("DELETE", "/v1/operator/raft/peer")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.setWriteOptions(q)
|
||||
|
||||
r.params.Set("address", address)
|
||||
|
||||
_, resp, err := requireOK(op.c.doRequest(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RaftRemovePeerByID is used to kick a stale peer (one that is in the Raft
|
||||
// quorum but no longer known to Serf or the catalog) by ID.
|
||||
func (op *Operator) RaftRemovePeerByID(id string, q *WriteOptions) error {
|
||||
r, err := op.c.newRequest("DELETE", "/v1/operator/raft/peer")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.setWriteOptions(q)
|
||||
|
||||
r.params.Set("id", id)
|
||||
|
||||
_, resp, err := requireOK(op.c.doRequest(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
218
vendor/github.com/hashicorp/nomad/api/operator_autopilot.go
generated
vendored
Normal file
218
vendor/github.com/hashicorp/nomad/api/operator_autopilot.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AutopilotConfiguration is used for querying/setting the Autopilot configuration.
|
||||
// Autopilot helps manage operator tasks related to Nomad servers like removing
|
||||
// failed servers from the Raft quorum.
|
||||
type AutopilotConfiguration struct {
|
||||
// CleanupDeadServers controls whether to remove dead servers from the Raft
|
||||
// peer list when a new server joins
|
||||
CleanupDeadServers bool
|
||||
|
||||
// LastContactThreshold is the limit on the amount of time a server can go
|
||||
// without leader contact before being considered unhealthy.
|
||||
LastContactThreshold time.Duration
|
||||
|
||||
// MaxTrailingLogs is the amount of entries in the Raft Log that a server can
|
||||
// be behind before being considered unhealthy.
|
||||
MaxTrailingLogs uint64
|
||||
|
||||
// ServerStabilizationTime is the minimum amount of time a server must be
|
||||
// in a stable, healthy state before it can be added to the cluster. Only
|
||||
// applicable with Raft protocol version 3 or higher.
|
||||
ServerStabilizationTime time.Duration
|
||||
|
||||
// (Enterprise-only) EnableRedundancyZones specifies whether to enable redundancy zones.
|
||||
EnableRedundancyZones bool
|
||||
|
||||
// (Enterprise-only) DisableUpgradeMigration will disable Autopilot's upgrade migration
|
||||
// strategy of waiting until enough newer-versioned servers have been added to the
|
||||
// cluster before promoting them to voters.
|
||||
DisableUpgradeMigration bool
|
||||
|
||||
// (Enterprise-only) EnableCustomUpgrades specifies whether to enable using custom
|
||||
// upgrade versions when performing migrations.
|
||||
EnableCustomUpgrades bool
|
||||
|
||||
// CreateIndex holds the index corresponding the creation of this configuration.
|
||||
// This is a read-only field.
|
||||
CreateIndex uint64
|
||||
|
||||
// ModifyIndex will be set to the index of the last update when retrieving the
|
||||
// Autopilot configuration. Resubmitting a configuration with
|
||||
// AutopilotCASConfiguration will perform a check-and-set operation which ensures
|
||||
// there hasn't been a subsequent update since the configuration was retrieved.
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
func (u *AutopilotConfiguration) MarshalJSON() ([]byte, error) {
|
||||
type Alias AutopilotConfiguration
|
||||
return json.Marshal(&struct {
|
||||
LastContactThreshold string
|
||||
ServerStabilizationTime string
|
||||
*Alias
|
||||
}{
|
||||
LastContactThreshold: u.LastContactThreshold.String(),
|
||||
ServerStabilizationTime: u.ServerStabilizationTime.String(),
|
||||
Alias: (*Alias)(u),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *AutopilotConfiguration) UnmarshalJSON(data []byte) error {
|
||||
type Alias AutopilotConfiguration
|
||||
aux := &struct {
|
||||
LastContactThreshold string
|
||||
ServerStabilizationTime string
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(u),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
if aux.LastContactThreshold != "" {
|
||||
if u.LastContactThreshold, err = time.ParseDuration(aux.LastContactThreshold); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if aux.ServerStabilizationTime != "" {
|
||||
if u.ServerStabilizationTime, err = time.ParseDuration(aux.ServerStabilizationTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServerHealth is the health (from the leader's point of view) of a server.
|
||||
type ServerHealth struct {
|
||||
// ID is the raft ID of the server.
|
||||
ID string
|
||||
|
||||
// Name is the node name of the server.
|
||||
Name string
|
||||
|
||||
// Address is the address of the server.
|
||||
Address string
|
||||
|
||||
// The status of the SerfHealth check for the server.
|
||||
SerfStatus string
|
||||
|
||||
// Version is the Nomad version of the server.
|
||||
Version string
|
||||
|
||||
// Leader is whether this server is currently the leader.
|
||||
Leader bool
|
||||
|
||||
// LastContact is the time since this node's last contact with the leader.
|
||||
LastContact time.Duration
|
||||
|
||||
// LastTerm is the highest leader term this server has a record of in its Raft log.
|
||||
LastTerm uint64
|
||||
|
||||
// LastIndex is the last log index this server has a record of in its Raft log.
|
||||
LastIndex uint64
|
||||
|
||||
// Healthy is whether or not the server is healthy according to the current
|
||||
// Autopilot config.
|
||||
Healthy bool
|
||||
|
||||
// Voter is whether this is a voting server.
|
||||
Voter bool
|
||||
|
||||
// StableSince is the last time this server's Healthy value changed.
|
||||
StableSince time.Time
|
||||
}
|
||||
|
||||
func (u *ServerHealth) MarshalJSON() ([]byte, error) {
|
||||
type Alias ServerHealth
|
||||
return json.Marshal(&struct {
|
||||
LastContact string
|
||||
*Alias
|
||||
}{
|
||||
LastContact: u.LastContact.String(),
|
||||
Alias: (*Alias)(u),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *ServerHealth) UnmarshalJSON(data []byte) error {
|
||||
type Alias ServerHealth
|
||||
aux := &struct {
|
||||
LastContact string
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(u),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
if aux.LastContact != "" {
|
||||
if u.LastContact, err = time.ParseDuration(aux.LastContact); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OperatorHealthReply is a representation of the overall health of the cluster
|
||||
type OperatorHealthReply struct {
|
||||
// Healthy is true if all the servers in the cluster are healthy.
|
||||
Healthy bool
|
||||
|
||||
// FailureTolerance is the number of healthy servers that could be lost without
|
||||
// an outage occurring.
|
||||
FailureTolerance int
|
||||
|
||||
// Servers holds the health of each server.
|
||||
Servers []ServerHealth
|
||||
}
|
||||
|
||||
// AutopilotGetConfiguration is used to query the current Autopilot configuration.
|
||||
func (op *Operator) AutopilotGetConfiguration(q *QueryOptions) (*AutopilotConfiguration, *QueryMeta, error) {
|
||||
var resp AutopilotConfiguration
|
||||
qm, err := op.c.query("/v1/operator/autopilot/configuration", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// AutopilotSetConfiguration is used to set the current Autopilot configuration.
|
||||
func (op *Operator) AutopilotSetConfiguration(conf *AutopilotConfiguration, q *WriteOptions) (*WriteMeta, error) {
|
||||
var out bool
|
||||
wm, err := op.c.write("/v1/operator/autopilot/configuration", conf, &out, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// AutopilotCASConfiguration is used to perform a Check-And-Set update on the
|
||||
// Autopilot configuration. The ModifyIndex value will be respected. Returns
|
||||
// true on success or false on failures.
|
||||
func (op *Operator) AutopilotCASConfiguration(conf *AutopilotConfiguration, q *WriteOptions) (bool, *WriteMeta, error) {
|
||||
var out bool
|
||||
wm, err := op.c.write("/v1/operator/autopilot/configuration?cas="+strconv.FormatUint(conf.ModifyIndex, 10), conf, &out, q)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
return out, wm, nil
|
||||
}
|
||||
|
||||
// AutopilotServerHealth is used to query Autopilot's top-level view of the health
|
||||
// of each Nomad server.
|
||||
func (op *Operator) AutopilotServerHealth(q *QueryOptions) (*OperatorHealthReply, *QueryMeta, error) {
|
||||
var out OperatorHealthReply
|
||||
qm, err := op.c.query("/v1/operator/autopilot/health", &out, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &out, qm, nil
|
||||
}
|
||||
186
vendor/github.com/hashicorp/nomad/api/quota.go
generated
vendored
Normal file
186
vendor/github.com/hashicorp/nomad/api/quota.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Quotas is used to query the quotas endpoints.
|
||||
type Quotas struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Quotas returns a new handle on the quotas.
|
||||
func (c *Client) Quotas() *Quotas {
|
||||
return &Quotas{client: c}
|
||||
}
|
||||
|
||||
// List is used to dump all of the quota specs
|
||||
func (q *Quotas) List(qo *QueryOptions) ([]*QuotaSpec, *QueryMeta, error) {
|
||||
var resp []*QuotaSpec
|
||||
qm, err := q.client.query("/v1/quotas", &resp, qo)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(QuotaSpecIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// PrefixList is used to do a PrefixList search over quota specs
|
||||
func (q *Quotas) PrefixList(prefix string, qo *QueryOptions) ([]*QuotaSpec, *QueryMeta, error) {
|
||||
if qo == nil {
|
||||
qo = &QueryOptions{Prefix: prefix}
|
||||
} else {
|
||||
qo.Prefix = prefix
|
||||
}
|
||||
|
||||
return q.List(qo)
|
||||
}
|
||||
|
||||
// ListUsage is used to dump all of the quota usages
|
||||
func (q *Quotas) ListUsage(qo *QueryOptions) ([]*QuotaUsage, *QueryMeta, error) {
|
||||
var resp []*QuotaUsage
|
||||
qm, err := q.client.query("/v1/quota-usages", &resp, qo)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sort.Sort(QuotaUsageIndexSort(resp))
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// PrefixList is used to do a PrefixList search over quota usages
|
||||
func (q *Quotas) PrefixListUsage(prefix string, qo *QueryOptions) ([]*QuotaUsage, *QueryMeta, error) {
|
||||
if qo == nil {
|
||||
qo = &QueryOptions{Prefix: prefix}
|
||||
} else {
|
||||
qo.Prefix = prefix
|
||||
}
|
||||
|
||||
return q.ListUsage(qo)
|
||||
}
|
||||
|
||||
// Info is used to query a single quota spec by its name.
|
||||
func (q *Quotas) Info(name string, qo *QueryOptions) (*QuotaSpec, *QueryMeta, error) {
|
||||
var resp QuotaSpec
|
||||
qm, err := q.client.query("/v1/quota/"+name, &resp, qo)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// Usage is used to query a single quota usage by its name.
|
||||
func (q *Quotas) Usage(name string, qo *QueryOptions) (*QuotaUsage, *QueryMeta, error) {
|
||||
var resp QuotaUsage
|
||||
qm, err := q.client.query("/v1/quota/usage/"+name, &resp, qo)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
// Register is used to register a quota spec.
|
||||
func (q *Quotas) Register(spec *QuotaSpec, qo *WriteOptions) (*WriteMeta, error) {
|
||||
wm, err := q.client.write("/v1/quota", spec, nil, qo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Delete is used to delete a quota spec
|
||||
func (q *Quotas) Delete(quota string, qo *WriteOptions) (*WriteMeta, error) {
|
||||
wm, err := q.client.delete(fmt.Sprintf("/v1/quota/%s", quota), nil, qo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// QuotaSpec specifies the allowed resource usage across regions.
|
||||
type QuotaSpec struct {
|
||||
// Name is the name for the quota object
|
||||
Name string
|
||||
|
||||
// Description is an optional description for the quota object
|
||||
Description string
|
||||
|
||||
// Limits is the set of quota limits encapsulated by this quota object. Each
|
||||
// limit applies quota in a particular region and in the future over a
|
||||
// particular priority range and datacenter set.
|
||||
Limits []*QuotaLimit
|
||||
|
||||
// Raft indexes to track creation and modification
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// QuotaLimit describes the resource limit in a particular region.
|
||||
type QuotaLimit struct {
|
||||
// Region is the region in which this limit has affect
|
||||
Region string
|
||||
|
||||
// RegionLimit is the quota limit that applies to any allocation within a
|
||||
// referencing namespace in the region. A value of zero is treated as
|
||||
// unlimited and a negative value is treated as fully disallowed. This is
|
||||
// useful for once we support GPUs
|
||||
RegionLimit *Resources
|
||||
|
||||
// Hash is the hash of the object and is used to make replication efficient.
|
||||
Hash []byte
|
||||
}
|
||||
|
||||
// QuotaUsage is the resource usage of a Quota
|
||||
type QuotaUsage struct {
|
||||
Name string
|
||||
Used map[string]*QuotaLimit
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
// QuotaSpecIndexSort is a wrapper to sort QuotaSpecs by CreateIndex. We
|
||||
// reverse the test so that we get the highest index first.
|
||||
type QuotaSpecIndexSort []*QuotaSpec
|
||||
|
||||
func (q QuotaSpecIndexSort) Len() int {
|
||||
return len(q)
|
||||
}
|
||||
|
||||
func (q QuotaSpecIndexSort) Less(i, j int) bool {
|
||||
return q[i].CreateIndex > q[j].CreateIndex
|
||||
}
|
||||
|
||||
func (q QuotaSpecIndexSort) Swap(i, j int) {
|
||||
q[i], q[j] = q[j], q[i]
|
||||
}
|
||||
|
||||
// QuotaUsageIndexSort is a wrapper to sort QuotaUsages by CreateIndex. We
|
||||
// reverse the test so that we get the highest index first.
|
||||
type QuotaUsageIndexSort []*QuotaUsage
|
||||
|
||||
func (q QuotaUsageIndexSort) Len() int {
|
||||
return len(q)
|
||||
}
|
||||
|
||||
func (q QuotaUsageIndexSort) Less(i, j int) bool {
|
||||
return q[i].CreateIndex > q[j].CreateIndex
|
||||
}
|
||||
|
||||
func (q QuotaUsageIndexSort) Swap(i, j int) {
|
||||
q[i], q[j] = q[j], q[i]
|
||||
}
|
||||
|
||||
// QuotaLimitSort is a wrapper to sort QuotaLimits
|
||||
type QuotaLimitSort []*QuotaLimit
|
||||
|
||||
func (q QuotaLimitSort) Len() int {
|
||||
return len(q)
|
||||
}
|
||||
|
||||
func (q QuotaLimitSort) Less(i, j int) bool {
|
||||
return q[i].Region < q[j].Region
|
||||
}
|
||||
|
||||
func (q QuotaLimitSort) Swap(i, j int) {
|
||||
q[i], q[j] = q[j], q[i]
|
||||
}
|
||||
38
vendor/github.com/hashicorp/nomad/api/raw.go
generated
vendored
Normal file
38
vendor/github.com/hashicorp/nomad/api/raw.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package api
|
||||
|
||||
import "io"
|
||||
|
||||
// Raw can be used to do raw queries against custom endpoints
|
||||
type Raw struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Raw returns a handle to query endpoints
|
||||
func (c *Client) Raw() *Raw {
|
||||
return &Raw{c}
|
||||
}
|
||||
|
||||
// Query is used to do a GET request against an endpoint
|
||||
// and deserialize the response into an interface using
|
||||
// standard Nomad conventions.
|
||||
func (raw *Raw) Query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) {
|
||||
return raw.c.query(endpoint, out, q)
|
||||
}
|
||||
|
||||
// Response is used to make a GET request against an endpoint and returns the
|
||||
// response body
|
||||
func (raw *Raw) Response(endpoint string, q *QueryOptions) (io.ReadCloser, error) {
|
||||
return raw.c.rawQuery(endpoint, q)
|
||||
}
|
||||
|
||||
// Write is used to do a PUT request against an endpoint
|
||||
// and serialize/deserialized using the standard Nomad conventions.
|
||||
func (raw *Raw) Write(endpoint string, in, out interface{}, q *WriteOptions) (*WriteMeta, error) {
|
||||
return raw.c.write(endpoint, in, out, q)
|
||||
}
|
||||
|
||||
// Delete is used to do a DELETE request against an endpoint
|
||||
// and serialize/deserialized using the standard Nomad conventions.
|
||||
func (raw *Raw) Delete(endpoint string, out interface{}, q *WriteOptions) (*WriteMeta, error) {
|
||||
return raw.c.delete(endpoint, out, q)
|
||||
}
|
||||
23
vendor/github.com/hashicorp/nomad/api/regions.go
generated
vendored
Normal file
23
vendor/github.com/hashicorp/nomad/api/regions.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package api
|
||||
|
||||
import "sort"
|
||||
|
||||
// Regions is used to query the regions in the cluster.
|
||||
type Regions struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Regions returns a handle on the regions endpoints.
|
||||
func (c *Client) Regions() *Regions {
|
||||
return &Regions{client: c}
|
||||
}
|
||||
|
||||
// List returns a list of all of the regions.
|
||||
func (r *Regions) List() ([]string, error) {
|
||||
var resp []string
|
||||
if _, err := r.client.query("/v1/regions", &resp, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Strings(resp)
|
||||
return resp, nil
|
||||
}
|
||||
100
vendor/github.com/hashicorp/nomad/api/resources.go
generated
vendored
Normal file
100
vendor/github.com/hashicorp/nomad/api/resources.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package api
|
||||
|
||||
import "github.com/hashicorp/nomad/helper"
|
||||
|
||||
// Resources encapsulates the required resources of
|
||||
// a given task or task group.
|
||||
type Resources struct {
|
||||
CPU *int
|
||||
MemoryMB *int `mapstructure:"memory"`
|
||||
DiskMB *int `mapstructure:"disk"`
|
||||
IOPS *int
|
||||
Networks []*NetworkResource
|
||||
}
|
||||
|
||||
// Canonicalize will supply missing values in the cases
|
||||
// where they are not provided.
|
||||
func (r *Resources) Canonicalize() {
|
||||
defaultResources := DefaultResources()
|
||||
if r.CPU == nil {
|
||||
r.CPU = defaultResources.CPU
|
||||
}
|
||||
if r.MemoryMB == nil {
|
||||
r.MemoryMB = defaultResources.MemoryMB
|
||||
}
|
||||
if r.IOPS == nil {
|
||||
r.IOPS = defaultResources.IOPS
|
||||
}
|
||||
for _, n := range r.Networks {
|
||||
n.Canonicalize()
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultResources is a small resources object that contains the
|
||||
// default resources requests that we will provide to an object.
|
||||
// --- THIS FUNCTION IS REPLICATED IN nomad/structs/structs.go
|
||||
// and should be kept in sync.
|
||||
func DefaultResources() *Resources {
|
||||
return &Resources{
|
||||
CPU: helper.IntToPtr(100),
|
||||
MemoryMB: helper.IntToPtr(300),
|
||||
IOPS: helper.IntToPtr(0),
|
||||
}
|
||||
}
|
||||
|
||||
// MinResources is a small resources object that contains the
|
||||
// absolute minimum resources that we will provide to an object.
|
||||
// This should not be confused with the defaults which are
|
||||
// provided in DefaultResources() --- THIS LOGIC IS REPLICATED
|
||||
// IN nomad/structs/structs.go and should be kept in sync.
|
||||
func MinResources() *Resources {
|
||||
return &Resources{
|
||||
CPU: helper.IntToPtr(20),
|
||||
MemoryMB: helper.IntToPtr(10),
|
||||
IOPS: helper.IntToPtr(0),
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges this resource with another resource.
|
||||
func (r *Resources) Merge(other *Resources) {
|
||||
if other == nil {
|
||||
return
|
||||
}
|
||||
if other.CPU != nil {
|
||||
r.CPU = other.CPU
|
||||
}
|
||||
if other.MemoryMB != nil {
|
||||
r.MemoryMB = other.MemoryMB
|
||||
}
|
||||
if other.DiskMB != nil {
|
||||
r.DiskMB = other.DiskMB
|
||||
}
|
||||
if other.IOPS != nil {
|
||||
r.IOPS = other.IOPS
|
||||
}
|
||||
if len(other.Networks) != 0 {
|
||||
r.Networks = other.Networks
|
||||
}
|
||||
}
|
||||
|
||||
type Port struct {
|
||||
Label string
|
||||
Value int `mapstructure:"static"`
|
||||
}
|
||||
|
||||
// NetworkResource is used to describe required network
|
||||
// resources of a given task.
|
||||
type NetworkResource struct {
|
||||
Device string
|
||||
CIDR string
|
||||
IP string
|
||||
MBits *int
|
||||
ReservedPorts []Port
|
||||
DynamicPorts []Port
|
||||
}
|
||||
|
||||
func (n *NetworkResource) Canonicalize() {
|
||||
if n.MBits == nil {
|
||||
n.MBits = helper.IntToPtr(10)
|
||||
}
|
||||
}
|
||||
39
vendor/github.com/hashicorp/nomad/api/search.go
generated
vendored
Normal file
39
vendor/github.com/hashicorp/nomad/api/search.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/nomad/api/contexts"
|
||||
)
|
||||
|
||||
type Search struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Search returns a handle on the Search endpoints
|
||||
func (c *Client) Search() *Search {
|
||||
return &Search{client: c}
|
||||
}
|
||||
|
||||
// PrefixSearch returns a list of matches for a particular context and prefix.
|
||||
func (s *Search) PrefixSearch(prefix string, context contexts.Context, q *QueryOptions) (*SearchResponse, *QueryMeta, error) {
|
||||
var resp SearchResponse
|
||||
req := &SearchRequest{Prefix: prefix, Context: context}
|
||||
|
||||
qm, err := s.client.putQuery("/v1/search", req, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &resp, qm, nil
|
||||
}
|
||||
|
||||
type SearchRequest struct {
|
||||
Prefix string
|
||||
Context contexts.Context
|
||||
QueryOptions
|
||||
}
|
||||
|
||||
type SearchResponse struct {
|
||||
Matches map[contexts.Context][]string
|
||||
Truncations map[contexts.Context]bool
|
||||
QueryMeta
|
||||
}
|
||||
79
vendor/github.com/hashicorp/nomad/api/sentinel.go
generated
vendored
Normal file
79
vendor/github.com/hashicorp/nomad/api/sentinel.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package api
|
||||
|
||||
import "fmt"
|
||||
|
||||
// SentinelPolicies is used to query the Sentinel Policy endpoints.
|
||||
type SentinelPolicies struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// SentinelPolicies returns a new handle on the Sentinel policies.
|
||||
func (c *Client) SentinelPolicies() *SentinelPolicies {
|
||||
return &SentinelPolicies{client: c}
|
||||
}
|
||||
|
||||
// List is used to dump all of the policies.
|
||||
func (a *SentinelPolicies) List(q *QueryOptions) ([]*SentinelPolicyListStub, *QueryMeta, error) {
|
||||
var resp []*SentinelPolicyListStub
|
||||
qm, err := a.client.query("/v1/sentinel/policies", &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// Upsert is used to create or update a policy
|
||||
func (a *SentinelPolicies) Upsert(policy *SentinelPolicy, q *WriteOptions) (*WriteMeta, error) {
|
||||
if policy == nil || policy.Name == "" {
|
||||
return nil, fmt.Errorf("missing policy name")
|
||||
}
|
||||
wm, err := a.client.write("/v1/sentinel/policy/"+policy.Name, policy, nil, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Delete is used to delete a policy
|
||||
func (a *SentinelPolicies) Delete(policyName string, q *WriteOptions) (*WriteMeta, error) {
|
||||
if policyName == "" {
|
||||
return nil, fmt.Errorf("missing policy name")
|
||||
}
|
||||
wm, err := a.client.delete("/v1/sentinel/policy/"+policyName, nil, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Info is used to query a specific policy
|
||||
func (a *SentinelPolicies) Info(policyName string, q *QueryOptions) (*SentinelPolicy, *QueryMeta, error) {
|
||||
if policyName == "" {
|
||||
return nil, nil, fmt.Errorf("missing policy name")
|
||||
}
|
||||
var resp SentinelPolicy
|
||||
wm, err := a.client.query("/v1/sentinel/policy/"+policyName, &resp, q)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &resp, wm, nil
|
||||
}
|
||||
|
||||
type SentinelPolicy struct {
|
||||
Name string
|
||||
Description string
|
||||
Scope string
|
||||
EnforcementLevel string
|
||||
Policy string
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
type SentinelPolicyListStub struct {
|
||||
Name string
|
||||
Description string
|
||||
Scope string
|
||||
EnforcementLevel string
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
43
vendor/github.com/hashicorp/nomad/api/status.go
generated
vendored
Normal file
43
vendor/github.com/hashicorp/nomad/api/status.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package api
|
||||
|
||||
// Status is used to query the status-related endpoints.
|
||||
type Status struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Status returns a handle on the status endpoints.
|
||||
func (c *Client) Status() *Status {
|
||||
return &Status{client: c}
|
||||
}
|
||||
|
||||
// Leader is used to query for the current cluster leader.
|
||||
func (s *Status) Leader() (string, error) {
|
||||
var resp string
|
||||
_, err := s.client.query("/v1/status/leader", &resp, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// RegionLeader is used to query for the leader in the passed region.
|
||||
func (s *Status) RegionLeader(region string) (string, error) {
|
||||
var resp string
|
||||
q := QueryOptions{Region: region}
|
||||
_, err := s.client.query("/v1/status/leader", &resp, &q)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Peers is used to query the addresses of the server peers
|
||||
// in the cluster.
|
||||
func (s *Status) Peers() ([]string, error) {
|
||||
var resp []string
|
||||
_, err := s.client.query("/v1/status/peers", &resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
23
vendor/github.com/hashicorp/nomad/api/system.go
generated
vendored
Normal file
23
vendor/github.com/hashicorp/nomad/api/system.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package api
|
||||
|
||||
// Status is used to query the status-related endpoints.
|
||||
type System struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// System returns a handle on the system endpoints.
|
||||
func (c *Client) System() *System {
|
||||
return &System{client: c}
|
||||
}
|
||||
|
||||
func (s *System) GarbageCollect() error {
|
||||
var req struct{}
|
||||
_, err := s.client.write("/v1/system/gc", &req, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *System) ReconcileSummaries() error {
|
||||
var req struct{}
|
||||
_, err := s.client.write("/v1/system/reconcile/summaries", &req, nil, nil)
|
||||
return err
|
||||
}
|
||||
843
vendor/github.com/hashicorp/nomad/api/tasks.go
generated
vendored
Normal file
843
vendor/github.com/hashicorp/nomad/api/tasks.go
generated
vendored
Normal file
@@ -0,0 +1,843 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
// MemoryStats holds memory usage related stats
|
||||
type MemoryStats struct {
|
||||
RSS uint64
|
||||
Cache uint64
|
||||
Swap uint64
|
||||
MaxUsage uint64
|
||||
KernelUsage uint64
|
||||
KernelMaxUsage uint64
|
||||
Measured []string
|
||||
}
|
||||
|
||||
// CpuStats holds cpu usage related stats
|
||||
type CpuStats struct {
|
||||
SystemMode float64
|
||||
UserMode float64
|
||||
TotalTicks float64
|
||||
ThrottledPeriods uint64
|
||||
ThrottledTime uint64
|
||||
Percent float64
|
||||
Measured []string
|
||||
}
|
||||
|
||||
// ResourceUsage holds information related to cpu and memory stats
|
||||
type ResourceUsage struct {
|
||||
MemoryStats *MemoryStats
|
||||
CpuStats *CpuStats
|
||||
}
|
||||
|
||||
// TaskResourceUsage holds aggregated resource usage of all processes in a Task
|
||||
// and the resource usage of the individual pids
|
||||
type TaskResourceUsage struct {
|
||||
ResourceUsage *ResourceUsage
|
||||
Timestamp int64
|
||||
Pids map[string]*ResourceUsage
|
||||
}
|
||||
|
||||
// AllocResourceUsage holds the aggregated task resource usage of the
|
||||
// allocation.
|
||||
type AllocResourceUsage struct {
|
||||
ResourceUsage *ResourceUsage
|
||||
Tasks map[string]*TaskResourceUsage
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
// RestartPolicy defines how the Nomad client restarts
|
||||
// tasks in a taskgroup when they fail
|
||||
type RestartPolicy struct {
|
||||
Interval *time.Duration
|
||||
Attempts *int
|
||||
Delay *time.Duration
|
||||
Mode *string
|
||||
}
|
||||
|
||||
func (r *RestartPolicy) Merge(rp *RestartPolicy) {
|
||||
if rp.Interval != nil {
|
||||
r.Interval = rp.Interval
|
||||
}
|
||||
if rp.Attempts != nil {
|
||||
r.Attempts = rp.Attempts
|
||||
}
|
||||
if rp.Delay != nil {
|
||||
r.Delay = rp.Delay
|
||||
}
|
||||
if rp.Mode != nil {
|
||||
r.Mode = rp.Mode
|
||||
}
|
||||
}
|
||||
|
||||
// Reschedule configures how Tasks are rescheduled when they crash or fail.
|
||||
type ReschedulePolicy struct {
|
||||
// Attempts limits the number of rescheduling attempts that can occur in an interval.
|
||||
Attempts *int `mapstructure:"attempts"`
|
||||
|
||||
// Interval is a duration in which we can limit the number of reschedule attempts.
|
||||
Interval *time.Duration `mapstructure:"interval"`
|
||||
|
||||
// Delay is a minimum duration to wait between reschedule attempts.
|
||||
// The delay function determines how much subsequent reschedule attempts are delayed by.
|
||||
Delay *time.Duration `mapstructure:"delay"`
|
||||
|
||||
// DelayFunction determines how the delay progressively changes on subsequent reschedule
|
||||
// attempts. Valid values are "exponential", "constant", and "fibonacci".
|
||||
DelayFunction *string `mapstructure:"delay_function"`
|
||||
|
||||
// MaxDelay is an upper bound on the delay.
|
||||
MaxDelay *time.Duration `mapstructure:"max_delay"`
|
||||
|
||||
// Unlimited allows rescheduling attempts until they succeed
|
||||
Unlimited *bool `mapstructure:"unlimited"`
|
||||
}
|
||||
|
||||
func (r *ReschedulePolicy) Merge(rp *ReschedulePolicy) {
|
||||
if rp == nil {
|
||||
return
|
||||
}
|
||||
if rp.Interval != nil {
|
||||
r.Interval = rp.Interval
|
||||
}
|
||||
if rp.Attempts != nil {
|
||||
r.Attempts = rp.Attempts
|
||||
}
|
||||
if rp.Delay != nil {
|
||||
r.Delay = rp.Delay
|
||||
}
|
||||
if rp.DelayFunction != nil {
|
||||
r.DelayFunction = rp.DelayFunction
|
||||
}
|
||||
if rp.MaxDelay != nil {
|
||||
r.MaxDelay = rp.MaxDelay
|
||||
}
|
||||
if rp.Unlimited != nil {
|
||||
r.Unlimited = rp.Unlimited
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReschedulePolicy) Canonicalize(jobType string) {
|
||||
dp := NewDefaultReschedulePolicy(jobType)
|
||||
if r.Interval == nil {
|
||||
r.Interval = dp.Interval
|
||||
}
|
||||
if r.Attempts == nil {
|
||||
r.Attempts = dp.Attempts
|
||||
}
|
||||
if r.Delay == nil {
|
||||
r.Delay = dp.Delay
|
||||
}
|
||||
if r.DelayFunction == nil {
|
||||
r.DelayFunction = dp.DelayFunction
|
||||
}
|
||||
if r.MaxDelay == nil {
|
||||
r.MaxDelay = dp.MaxDelay
|
||||
}
|
||||
if r.Unlimited == nil {
|
||||
r.Unlimited = dp.Unlimited
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultReschedulePolicy(jobType string) *ReschedulePolicy {
|
||||
var dp *ReschedulePolicy
|
||||
switch jobType {
|
||||
case "service":
|
||||
dp = &ReschedulePolicy{
|
||||
Attempts: helper.IntToPtr(structs.DefaultServiceJobReschedulePolicy.Attempts),
|
||||
Interval: helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.Interval),
|
||||
Delay: helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.Delay),
|
||||
DelayFunction: helper.StringToPtr(structs.DefaultServiceJobReschedulePolicy.DelayFunction),
|
||||
MaxDelay: helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.MaxDelay),
|
||||
Unlimited: helper.BoolToPtr(structs.DefaultServiceJobReschedulePolicy.Unlimited),
|
||||
}
|
||||
case "batch":
|
||||
dp = &ReschedulePolicy{
|
||||
Attempts: helper.IntToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts),
|
||||
Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
|
||||
Delay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
|
||||
DelayFunction: helper.StringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
|
||||
MaxDelay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
|
||||
Unlimited: helper.BoolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
|
||||
}
|
||||
|
||||
case "system":
|
||||
dp = &ReschedulePolicy{
|
||||
Attempts: helper.IntToPtr(0),
|
||||
Interval: helper.TimeToPtr(0),
|
||||
Delay: helper.TimeToPtr(0),
|
||||
DelayFunction: helper.StringToPtr(""),
|
||||
MaxDelay: helper.TimeToPtr(0),
|
||||
Unlimited: helper.BoolToPtr(false),
|
||||
}
|
||||
}
|
||||
return dp
|
||||
}
|
||||
|
||||
func (r *ReschedulePolicy) Copy() *ReschedulePolicy {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
nrp := new(ReschedulePolicy)
|
||||
*nrp = *r
|
||||
return nrp
|
||||
}
|
||||
|
||||
func (p *ReschedulePolicy) String() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
if *p.Unlimited {
|
||||
return fmt.Sprintf("unlimited with %v delay, max_delay = %v", *p.DelayFunction, *p.MaxDelay)
|
||||
}
|
||||
return fmt.Sprintf("%v in %v with %v delay, max_delay = %v", *p.Attempts, *p.Interval, *p.DelayFunction, *p.MaxDelay)
|
||||
}
|
||||
|
||||
// CheckRestart describes if and when a task should be restarted based on
|
||||
// failing health checks.
|
||||
type CheckRestart struct {
|
||||
Limit int `mapstructure:"limit"`
|
||||
Grace *time.Duration `mapstructure:"grace"`
|
||||
IgnoreWarnings bool `mapstructure:"ignore_warnings"`
|
||||
}
|
||||
|
||||
// Canonicalize CheckRestart fields if not nil.
|
||||
func (c *CheckRestart) Canonicalize() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Grace == nil {
|
||||
c.Grace = helper.TimeToPtr(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy returns a copy of CheckRestart or nil if unset.
|
||||
func (c *CheckRestart) Copy() *CheckRestart {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nc := new(CheckRestart)
|
||||
nc.Limit = c.Limit
|
||||
if c.Grace != nil {
|
||||
g := *c.Grace
|
||||
nc.Grace = &g
|
||||
}
|
||||
nc.IgnoreWarnings = c.IgnoreWarnings
|
||||
return nc
|
||||
}
|
||||
|
||||
// Merge values from other CheckRestart over default values on this
|
||||
// CheckRestart and return merged copy.
|
||||
func (c *CheckRestart) Merge(o *CheckRestart) *CheckRestart {
|
||||
if c == nil {
|
||||
// Just return other
|
||||
return o
|
||||
}
|
||||
|
||||
nc := c.Copy()
|
||||
|
||||
if o == nil {
|
||||
// Nothing to merge
|
||||
return nc
|
||||
}
|
||||
|
||||
if o.Limit > 0 {
|
||||
nc.Limit = o.Limit
|
||||
}
|
||||
|
||||
if o.Grace != nil {
|
||||
nc.Grace = o.Grace
|
||||
}
|
||||
|
||||
if o.IgnoreWarnings {
|
||||
nc.IgnoreWarnings = o.IgnoreWarnings
|
||||
}
|
||||
|
||||
return nc
|
||||
}
|
||||
|
||||
// The ServiceCheck data model represents the consul health check that
|
||||
// Nomad registers for a Task
|
||||
type ServiceCheck struct {
|
||||
Id string
|
||||
Name string
|
||||
Type string
|
||||
Command string
|
||||
Args []string
|
||||
Path string
|
||||
Protocol string
|
||||
PortLabel string `mapstructure:"port"`
|
||||
AddressMode string `mapstructure:"address_mode"`
|
||||
Interval time.Duration
|
||||
Timeout time.Duration
|
||||
InitialStatus string `mapstructure:"initial_status"`
|
||||
TLSSkipVerify bool `mapstructure:"tls_skip_verify"`
|
||||
Header map[string][]string
|
||||
Method string
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart"`
|
||||
GRPCService string `mapstructure:"grpc_service"`
|
||||
GRPCUseTLS bool `mapstructure:"grpc_use_tls"`
|
||||
}
|
||||
|
||||
// The Service model represents a Consul service definition
|
||||
type Service struct {
|
||||
Id string
|
||||
Name string
|
||||
Tags []string
|
||||
CanaryTags []string `mapstructure:"canary_tags"`
|
||||
PortLabel string `mapstructure:"port"`
|
||||
AddressMode string `mapstructure:"address_mode"`
|
||||
Checks []ServiceCheck
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart"`
|
||||
}
|
||||
|
||||
func (s *Service) Canonicalize(t *Task, tg *TaskGroup, job *Job) {
|
||||
if s.Name == "" {
|
||||
s.Name = fmt.Sprintf("%s-%s-%s", *job.Name, *tg.Name, t.Name)
|
||||
}
|
||||
|
||||
// Default to AddressModeAuto
|
||||
if s.AddressMode == "" {
|
||||
s.AddressMode = "auto"
|
||||
}
|
||||
|
||||
// Canonicalize CheckRestart on Checks and merge Service.CheckRestart
|
||||
// into each check.
|
||||
for i, check := range s.Checks {
|
||||
s.Checks[i].CheckRestart = s.CheckRestart.Merge(check.CheckRestart)
|
||||
s.Checks[i].CheckRestart.Canonicalize()
|
||||
}
|
||||
}
|
||||
|
||||
// EphemeralDisk is an ephemeral disk object
|
||||
type EphemeralDisk struct {
|
||||
Sticky *bool
|
||||
Migrate *bool
|
||||
SizeMB *int `mapstructure:"size"`
|
||||
}
|
||||
|
||||
func DefaultEphemeralDisk() *EphemeralDisk {
|
||||
return &EphemeralDisk{
|
||||
Sticky: helper.BoolToPtr(false),
|
||||
Migrate: helper.BoolToPtr(false),
|
||||
SizeMB: helper.IntToPtr(300),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EphemeralDisk) Canonicalize() {
|
||||
if e.Sticky == nil {
|
||||
e.Sticky = helper.BoolToPtr(false)
|
||||
}
|
||||
if e.Migrate == nil {
|
||||
e.Migrate = helper.BoolToPtr(false)
|
||||
}
|
||||
if e.SizeMB == nil {
|
||||
e.SizeMB = helper.IntToPtr(300)
|
||||
}
|
||||
}
|
||||
|
||||
// MigrateStrategy describes how allocations for a task group should be
|
||||
// migrated between nodes (eg when draining).
|
||||
type MigrateStrategy struct {
|
||||
MaxParallel *int `mapstructure:"max_parallel"`
|
||||
HealthCheck *string `mapstructure:"health_check"`
|
||||
MinHealthyTime *time.Duration `mapstructure:"min_healthy_time"`
|
||||
HealthyDeadline *time.Duration `mapstructure:"healthy_deadline"`
|
||||
}
|
||||
|
||||
func DefaultMigrateStrategy() *MigrateStrategy {
|
||||
return &MigrateStrategy{
|
||||
MaxParallel: helper.IntToPtr(1),
|
||||
HealthCheck: helper.StringToPtr("checks"),
|
||||
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
|
||||
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MigrateStrategy) Canonicalize() {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
defaults := DefaultMigrateStrategy()
|
||||
if m.MaxParallel == nil {
|
||||
m.MaxParallel = defaults.MaxParallel
|
||||
}
|
||||
if m.HealthCheck == nil {
|
||||
m.HealthCheck = defaults.HealthCheck
|
||||
}
|
||||
if m.MinHealthyTime == nil {
|
||||
m.MinHealthyTime = defaults.MinHealthyTime
|
||||
}
|
||||
if m.HealthyDeadline == nil {
|
||||
m.HealthyDeadline = defaults.HealthyDeadline
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MigrateStrategy) Merge(o *MigrateStrategy) {
|
||||
if o.MaxParallel != nil {
|
||||
m.MaxParallel = o.MaxParallel
|
||||
}
|
||||
if o.HealthCheck != nil {
|
||||
m.HealthCheck = o.HealthCheck
|
||||
}
|
||||
if o.MinHealthyTime != nil {
|
||||
m.MinHealthyTime = o.MinHealthyTime
|
||||
}
|
||||
if o.HealthyDeadline != nil {
|
||||
m.HealthyDeadline = o.HealthyDeadline
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MigrateStrategy) Copy() *MigrateStrategy {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
nm := new(MigrateStrategy)
|
||||
*nm = *m
|
||||
return nm
|
||||
}
|
||||
|
||||
// TaskGroup is the unit of scheduling.
|
||||
type TaskGroup struct {
|
||||
Name *string
|
||||
Count *int
|
||||
Constraints []*Constraint
|
||||
Tasks []*Task
|
||||
RestartPolicy *RestartPolicy
|
||||
ReschedulePolicy *ReschedulePolicy
|
||||
EphemeralDisk *EphemeralDisk
|
||||
Update *UpdateStrategy
|
||||
Migrate *MigrateStrategy
|
||||
Meta map[string]string
|
||||
}
|
||||
|
||||
// NewTaskGroup creates a new TaskGroup.
|
||||
func NewTaskGroup(name string, count int) *TaskGroup {
|
||||
return &TaskGroup{
|
||||
Name: helper.StringToPtr(name),
|
||||
Count: helper.IntToPtr(count),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *TaskGroup) Canonicalize(job *Job) {
|
||||
if g.Name == nil {
|
||||
g.Name = helper.StringToPtr("")
|
||||
}
|
||||
if g.Count == nil {
|
||||
g.Count = helper.IntToPtr(1)
|
||||
}
|
||||
for _, t := range g.Tasks {
|
||||
t.Canonicalize(g, job)
|
||||
}
|
||||
if g.EphemeralDisk == nil {
|
||||
g.EphemeralDisk = DefaultEphemeralDisk()
|
||||
} else {
|
||||
g.EphemeralDisk.Canonicalize()
|
||||
}
|
||||
|
||||
// Merge the update policy from the job
|
||||
if ju, tu := job.Update != nil, g.Update != nil; ju && tu {
|
||||
// Merge the jobs and task groups definition of the update strategy
|
||||
jc := job.Update.Copy()
|
||||
jc.Merge(g.Update)
|
||||
g.Update = jc
|
||||
} else if ju && !job.Update.Empty() {
|
||||
// Inherit the jobs as long as it is non-empty.
|
||||
jc := job.Update.Copy()
|
||||
g.Update = jc
|
||||
}
|
||||
|
||||
if g.Update != nil {
|
||||
g.Update.Canonicalize()
|
||||
}
|
||||
|
||||
// Merge the reschedule policy from the job
|
||||
if jr, tr := job.Reschedule != nil, g.ReschedulePolicy != nil; jr && tr {
|
||||
jobReschedule := job.Reschedule.Copy()
|
||||
jobReschedule.Merge(g.ReschedulePolicy)
|
||||
g.ReschedulePolicy = jobReschedule
|
||||
} else if jr {
|
||||
jobReschedule := job.Reschedule.Copy()
|
||||
g.ReschedulePolicy = jobReschedule
|
||||
}
|
||||
// Only use default reschedule policy for non system jobs
|
||||
if g.ReschedulePolicy == nil && *job.Type != "system" {
|
||||
g.ReschedulePolicy = NewDefaultReschedulePolicy(*job.Type)
|
||||
}
|
||||
if g.ReschedulePolicy != nil {
|
||||
g.ReschedulePolicy.Canonicalize(*job.Type)
|
||||
}
|
||||
// Merge the migrate strategy from the job
|
||||
if jm, tm := job.Migrate != nil, g.Migrate != nil; jm && tm {
|
||||
jobMigrate := job.Migrate.Copy()
|
||||
jobMigrate.Merge(g.Migrate)
|
||||
g.Migrate = jobMigrate
|
||||
} else if jm {
|
||||
jobMigrate := job.Migrate.Copy()
|
||||
g.Migrate = jobMigrate
|
||||
}
|
||||
|
||||
// Merge with default reschedule policy
|
||||
if *job.Type == "service" {
|
||||
defaultMigrateStrategy := &MigrateStrategy{}
|
||||
defaultMigrateStrategy.Canonicalize()
|
||||
if g.Migrate != nil {
|
||||
defaultMigrateStrategy.Merge(g.Migrate)
|
||||
}
|
||||
g.Migrate = defaultMigrateStrategy
|
||||
}
|
||||
|
||||
var defaultRestartPolicy *RestartPolicy
|
||||
switch *job.Type {
|
||||
case "service", "system":
|
||||
defaultRestartPolicy = &RestartPolicy{
|
||||
Delay: helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Delay),
|
||||
Attempts: helper.IntToPtr(structs.DefaultServiceJobRestartPolicy.Attempts),
|
||||
Interval: helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Interval),
|
||||
Mode: helper.StringToPtr(structs.DefaultServiceJobRestartPolicy.Mode),
|
||||
}
|
||||
default:
|
||||
defaultRestartPolicy = &RestartPolicy{
|
||||
Delay: helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Delay),
|
||||
Attempts: helper.IntToPtr(structs.DefaultBatchJobRestartPolicy.Attempts),
|
||||
Interval: helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Interval),
|
||||
Mode: helper.StringToPtr(structs.DefaultBatchJobRestartPolicy.Mode),
|
||||
}
|
||||
}
|
||||
|
||||
if g.RestartPolicy != nil {
|
||||
defaultRestartPolicy.Merge(g.RestartPolicy)
|
||||
}
|
||||
g.RestartPolicy = defaultRestartPolicy
|
||||
}
|
||||
|
||||
// Constrain is used to add a constraint to a task group.
|
||||
func (g *TaskGroup) Constrain(c *Constraint) *TaskGroup {
|
||||
g.Constraints = append(g.Constraints, c)
|
||||
return g
|
||||
}
|
||||
|
||||
// AddMeta is used to add a meta k/v pair to a task group
|
||||
func (g *TaskGroup) SetMeta(key, val string) *TaskGroup {
|
||||
if g.Meta == nil {
|
||||
g.Meta = make(map[string]string)
|
||||
}
|
||||
g.Meta[key] = val
|
||||
return g
|
||||
}
|
||||
|
||||
// AddTask is used to add a new task to a task group.
|
||||
func (g *TaskGroup) AddTask(t *Task) *TaskGroup {
|
||||
g.Tasks = append(g.Tasks, t)
|
||||
return g
|
||||
}
|
||||
|
||||
// RequireDisk adds a ephemeral disk to the task group
|
||||
func (g *TaskGroup) RequireDisk(disk *EphemeralDisk) *TaskGroup {
|
||||
g.EphemeralDisk = disk
|
||||
return g
|
||||
}
|
||||
|
||||
// LogConfig provides configuration for log rotation
|
||||
type LogConfig struct {
|
||||
MaxFiles *int `mapstructure:"max_files"`
|
||||
MaxFileSizeMB *int `mapstructure:"max_file_size"`
|
||||
}
|
||||
|
||||
func DefaultLogConfig() *LogConfig {
|
||||
return &LogConfig{
|
||||
MaxFiles: helper.IntToPtr(10),
|
||||
MaxFileSizeMB: helper.IntToPtr(10),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LogConfig) Canonicalize() {
|
||||
if l.MaxFiles == nil {
|
||||
l.MaxFiles = helper.IntToPtr(10)
|
||||
}
|
||||
if l.MaxFileSizeMB == nil {
|
||||
l.MaxFileSizeMB = helper.IntToPtr(10)
|
||||
}
|
||||
}
|
||||
|
||||
// DispatchPayloadConfig configures how a task gets its input from a job dispatch
|
||||
type DispatchPayloadConfig struct {
|
||||
File string
|
||||
}
|
||||
|
||||
// Task is a single process in a task group.
|
||||
type Task struct {
|
||||
Name string
|
||||
Driver string
|
||||
User string
|
||||
Config map[string]interface{}
|
||||
Constraints []*Constraint
|
||||
Env map[string]string
|
||||
Services []*Service
|
||||
Resources *Resources
|
||||
Meta map[string]string
|
||||
KillTimeout *time.Duration `mapstructure:"kill_timeout"`
|
||||
LogConfig *LogConfig `mapstructure:"logs"`
|
||||
Artifacts []*TaskArtifact
|
||||
Vault *Vault
|
||||
Templates []*Template
|
||||
DispatchPayload *DispatchPayloadConfig
|
||||
Leader bool
|
||||
ShutdownDelay time.Duration `mapstructure:"shutdown_delay"`
|
||||
KillSignal string `mapstructure:"kill_signal"`
|
||||
}
|
||||
|
||||
func (t *Task) Canonicalize(tg *TaskGroup, job *Job) {
|
||||
if t.Resources == nil {
|
||||
t.Resources = &Resources{}
|
||||
}
|
||||
t.Resources.Canonicalize()
|
||||
if t.KillTimeout == nil {
|
||||
t.KillTimeout = helper.TimeToPtr(5 * time.Second)
|
||||
}
|
||||
if t.LogConfig == nil {
|
||||
t.LogConfig = DefaultLogConfig()
|
||||
} else {
|
||||
t.LogConfig.Canonicalize()
|
||||
}
|
||||
for _, artifact := range t.Artifacts {
|
||||
artifact.Canonicalize()
|
||||
}
|
||||
if t.Vault != nil {
|
||||
t.Vault.Canonicalize()
|
||||
}
|
||||
for _, tmpl := range t.Templates {
|
||||
tmpl.Canonicalize()
|
||||
}
|
||||
for _, s := range t.Services {
|
||||
s.Canonicalize(t, tg, job)
|
||||
}
|
||||
}
|
||||
|
||||
// TaskArtifact is used to download artifacts before running a task.
|
||||
type TaskArtifact struct {
|
||||
GetterSource *string `mapstructure:"source"`
|
||||
GetterOptions map[string]string `mapstructure:"options"`
|
||||
GetterMode *string `mapstructure:"mode"`
|
||||
RelativeDest *string `mapstructure:"destination"`
|
||||
}
|
||||
|
||||
func (a *TaskArtifact) Canonicalize() {
|
||||
if a.GetterMode == nil {
|
||||
a.GetterMode = helper.StringToPtr("any")
|
||||
}
|
||||
if a.GetterSource == nil {
|
||||
// Shouldn't be possible, but we don't want to panic
|
||||
a.GetterSource = helper.StringToPtr("")
|
||||
}
|
||||
if a.RelativeDest == nil {
|
||||
switch *a.GetterMode {
|
||||
case "file":
|
||||
// File mode should default to local/filename
|
||||
dest := *a.GetterSource
|
||||
dest = path.Base(dest)
|
||||
dest = filepath.Join("local", dest)
|
||||
a.RelativeDest = &dest
|
||||
default:
|
||||
// Default to a directory
|
||||
a.RelativeDest = helper.StringToPtr("local/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
SourcePath *string `mapstructure:"source"`
|
||||
DestPath *string `mapstructure:"destination"`
|
||||
EmbeddedTmpl *string `mapstructure:"data"`
|
||||
ChangeMode *string `mapstructure:"change_mode"`
|
||||
ChangeSignal *string `mapstructure:"change_signal"`
|
||||
Splay *time.Duration `mapstructure:"splay"`
|
||||
Perms *string `mapstructure:"perms"`
|
||||
LeftDelim *string `mapstructure:"left_delimiter"`
|
||||
RightDelim *string `mapstructure:"right_delimiter"`
|
||||
Envvars *bool `mapstructure:"env"`
|
||||
VaultGrace *time.Duration `mapstructure:"vault_grace"`
|
||||
}
|
||||
|
||||
func (tmpl *Template) Canonicalize() {
|
||||
if tmpl.SourcePath == nil {
|
||||
tmpl.SourcePath = helper.StringToPtr("")
|
||||
}
|
||||
if tmpl.DestPath == nil {
|
||||
tmpl.DestPath = helper.StringToPtr("")
|
||||
}
|
||||
if tmpl.EmbeddedTmpl == nil {
|
||||
tmpl.EmbeddedTmpl = helper.StringToPtr("")
|
||||
}
|
||||
if tmpl.ChangeMode == nil {
|
||||
tmpl.ChangeMode = helper.StringToPtr("restart")
|
||||
}
|
||||
if tmpl.ChangeSignal == nil {
|
||||
if *tmpl.ChangeMode == "signal" {
|
||||
tmpl.ChangeSignal = helper.StringToPtr("SIGHUP")
|
||||
} else {
|
||||
tmpl.ChangeSignal = helper.StringToPtr("")
|
||||
}
|
||||
} else {
|
||||
sig := *tmpl.ChangeSignal
|
||||
tmpl.ChangeSignal = helper.StringToPtr(strings.ToUpper(sig))
|
||||
}
|
||||
if tmpl.Splay == nil {
|
||||
tmpl.Splay = helper.TimeToPtr(5 * time.Second)
|
||||
}
|
||||
if tmpl.Perms == nil {
|
||||
tmpl.Perms = helper.StringToPtr("0644")
|
||||
}
|
||||
if tmpl.LeftDelim == nil {
|
||||
tmpl.LeftDelim = helper.StringToPtr("{{")
|
||||
}
|
||||
if tmpl.RightDelim == nil {
|
||||
tmpl.RightDelim = helper.StringToPtr("}}")
|
||||
}
|
||||
if tmpl.Envvars == nil {
|
||||
tmpl.Envvars = helper.BoolToPtr(false)
|
||||
}
|
||||
if tmpl.VaultGrace == nil {
|
||||
tmpl.VaultGrace = helper.TimeToPtr(15 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
type Vault struct {
|
||||
Policies []string
|
||||
Env *bool
|
||||
ChangeMode *string `mapstructure:"change_mode"`
|
||||
ChangeSignal *string `mapstructure:"change_signal"`
|
||||
}
|
||||
|
||||
func (v *Vault) Canonicalize() {
|
||||
if v.Env == nil {
|
||||
v.Env = helper.BoolToPtr(true)
|
||||
}
|
||||
if v.ChangeMode == nil {
|
||||
v.ChangeMode = helper.StringToPtr("restart")
|
||||
}
|
||||
if v.ChangeSignal == nil {
|
||||
v.ChangeSignal = helper.StringToPtr("SIGHUP")
|
||||
}
|
||||
}
|
||||
|
||||
// NewTask creates and initializes a new Task.
|
||||
func NewTask(name, driver string) *Task {
|
||||
return &Task{
|
||||
Name: name,
|
||||
Driver: driver,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure is used to configure a single k/v pair on
|
||||
// the task.
|
||||
func (t *Task) SetConfig(key string, val interface{}) *Task {
|
||||
if t.Config == nil {
|
||||
t.Config = make(map[string]interface{})
|
||||
}
|
||||
t.Config[key] = val
|
||||
return t
|
||||
}
|
||||
|
||||
// SetMeta is used to add metadata k/v pairs to the task.
|
||||
func (t *Task) SetMeta(key, val string) *Task {
|
||||
if t.Meta == nil {
|
||||
t.Meta = make(map[string]string)
|
||||
}
|
||||
t.Meta[key] = val
|
||||
return t
|
||||
}
|
||||
|
||||
// Require is used to add resource requirements to a task.
|
||||
func (t *Task) Require(r *Resources) *Task {
|
||||
t.Resources = r
|
||||
return t
|
||||
}
|
||||
|
||||
// Constraint adds a new constraints to a single task.
|
||||
func (t *Task) Constrain(c *Constraint) *Task {
|
||||
t.Constraints = append(t.Constraints, c)
|
||||
return t
|
||||
}
|
||||
|
||||
// SetLogConfig sets a log config to a task
|
||||
func (t *Task) SetLogConfig(l *LogConfig) *Task {
|
||||
t.LogConfig = l
|
||||
return t
|
||||
}
|
||||
|
||||
// TaskState tracks the current state of a task and events that caused state
|
||||
// transitions.
|
||||
type TaskState struct {
|
||||
State string
|
||||
Failed bool
|
||||
Restarts uint64
|
||||
LastRestart time.Time
|
||||
StartedAt time.Time
|
||||
FinishedAt time.Time
|
||||
Events []*TaskEvent
|
||||
}
|
||||
|
||||
const (
|
||||
TaskSetup = "Task Setup"
|
||||
TaskSetupFailure = "Setup Failure"
|
||||
TaskDriverFailure = "Driver Failure"
|
||||
TaskDriverMessage = "Driver"
|
||||
TaskReceived = "Received"
|
||||
TaskFailedValidation = "Failed Validation"
|
||||
TaskStarted = "Started"
|
||||
TaskTerminated = "Terminated"
|
||||
TaskKilling = "Killing"
|
||||
TaskKilled = "Killed"
|
||||
TaskRestarting = "Restarting"
|
||||
TaskNotRestarting = "Not Restarting"
|
||||
TaskDownloadingArtifacts = "Downloading Artifacts"
|
||||
TaskArtifactDownloadFailed = "Failed Artifact Download"
|
||||
TaskSiblingFailed = "Sibling Task Failed"
|
||||
TaskSignaling = "Signaling"
|
||||
TaskRestartSignal = "Restart Signaled"
|
||||
TaskLeaderDead = "Leader Task Dead"
|
||||
TaskBuildingTaskDir = "Building Task Directory"
|
||||
)
|
||||
|
||||
// TaskEvent is an event that effects the state of a task and contains meta-data
|
||||
// appropriate to the events type.
|
||||
type TaskEvent struct {
|
||||
Type string
|
||||
Time int64
|
||||
DisplayMessage string
|
||||
Details map[string]string
|
||||
// DEPRECATION NOTICE: The following fields are all deprecated. see TaskEvent struct in structs.go for details.
|
||||
FailsTask bool
|
||||
RestartReason string
|
||||
SetupError string
|
||||
DriverError string
|
||||
DriverMessage string
|
||||
ExitCode int
|
||||
Signal int
|
||||
Message string
|
||||
KillReason string
|
||||
KillTimeout time.Duration
|
||||
KillError string
|
||||
StartDelay int64
|
||||
DownloadError string
|
||||
ValidationError string
|
||||
DiskLimit int64
|
||||
DiskSize int64
|
||||
FailedSibling string
|
||||
VaultError string
|
||||
TaskSignalReason string
|
||||
TaskSignal string
|
||||
GenericSource string
|
||||
}
|
||||
Reference in New Issue
Block a user