Sync namespace changes
This commit is contained in:
parent
187c1568aa
commit
84d06f6abe
|
@ -72,6 +72,7 @@ func (a *Allocations) GC(alloc *Allocation, q *QueryOptions) error {
|
||||||
// Allocation is used for serialization of allocations.
|
// Allocation is used for serialization of allocations.
|
||||||
type Allocation struct {
|
type Allocation struct {
|
||||||
ID string
|
ID string
|
||||||
|
Namespace string
|
||||||
EvalID string
|
EvalID string
|
||||||
Name string
|
Name string
|
||||||
NodeID string
|
NodeID string
|
||||||
|
|
30
api/api.go
30
api/api.go
|
@ -24,6 +24,9 @@ type QueryOptions struct {
|
||||||
// by the Config
|
// by the Config
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Namespace is the target namespace for the query.
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// AllowStale allows any Nomad server (non-leader) to service
|
// AllowStale allows any Nomad server (non-leader) to service
|
||||||
// a read. This allows for lower latency and higher throughput
|
// a read. This allows for lower latency and higher throughput
|
||||||
AllowStale bool
|
AllowStale bool
|
||||||
|
@ -52,6 +55,9 @@ type WriteOptions struct {
|
||||||
// by the Config
|
// by the Config
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Namespace is the target namespace for the write.
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// SecretID is the secret ID of an ACL token
|
// SecretID is the secret ID of an ACL token
|
||||||
SecretID string
|
SecretID string
|
||||||
}
|
}
|
||||||
|
@ -100,6 +106,9 @@ type Config struct {
|
||||||
// Region to use. If not provided, the default agent region is used.
|
// Region to use. If not provided, the default agent region is used.
|
||||||
Region string
|
Region 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 is the client to use. Default will be used if not provided.
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
|
||||||
|
@ -129,6 +138,7 @@ func (c *Config) ClientConfig(region, address string, tlsEnabled bool) *Config {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
Address: fmt.Sprintf("%s://%s", scheme, address),
|
Address: fmt.Sprintf("%s://%s", scheme, address),
|
||||||
Region: region,
|
Region: region,
|
||||||
|
Namespace: c.Namespace,
|
||||||
httpClient: defaultConfig.httpClient,
|
httpClient: defaultConfig.httpClient,
|
||||||
SecretID: c.SecretID,
|
SecretID: c.SecretID,
|
||||||
HttpAuth: c.HttpAuth,
|
HttpAuth: c.HttpAuth,
|
||||||
|
@ -193,6 +203,12 @@ func DefaultConfig() *Config {
|
||||||
if addr := os.Getenv("NOMAD_ADDR"); addr != "" {
|
if addr := os.Getenv("NOMAD_ADDR"); addr != "" {
|
||||||
config.Address = 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 != "" {
|
if auth := os.Getenv("NOMAD_HTTP_AUTH"); auth != "" {
|
||||||
var username, password string
|
var username, password string
|
||||||
if strings.Contains(auth, ":") {
|
if strings.Contains(auth, ":") {
|
||||||
|
@ -314,6 +330,11 @@ func (c *Client) SetRegion(region string) {
|
||||||
c.config.Region = region
|
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
|
// GetNodeClient returns a new Client that will dial the specified node. If the
|
||||||
// QueryOptions is set, its region will be used.
|
// QueryOptions is set, its region will be used.
|
||||||
func (c *Client) GetNodeClient(nodeID string, q *QueryOptions) (*Client, error) {
|
func (c *Client) GetNodeClient(nodeID string, q *QueryOptions) (*Client, error) {
|
||||||
|
@ -382,6 +403,9 @@ func (r *request) setQueryOptions(q *QueryOptions) {
|
||||||
if q.Region != "" {
|
if q.Region != "" {
|
||||||
r.params.Set("region", q.Region)
|
r.params.Set("region", q.Region)
|
||||||
}
|
}
|
||||||
|
if q.Namespace != "" {
|
||||||
|
r.params.Set("namespace", q.Namespace)
|
||||||
|
}
|
||||||
if q.SecretID != "" {
|
if q.SecretID != "" {
|
||||||
r.token = q.SecretID
|
r.token = q.SecretID
|
||||||
}
|
}
|
||||||
|
@ -416,6 +440,9 @@ func (r *request) setWriteOptions(q *WriteOptions) {
|
||||||
if q.Region != "" {
|
if q.Region != "" {
|
||||||
r.params.Set("region", q.Region)
|
r.params.Set("region", q.Region)
|
||||||
}
|
}
|
||||||
|
if q.Namespace != "" {
|
||||||
|
r.params.Set("namespace", q.Namespace)
|
||||||
|
}
|
||||||
if q.SecretID != "" {
|
if q.SecretID != "" {
|
||||||
r.token = q.SecretID
|
r.token = q.SecretID
|
||||||
}
|
}
|
||||||
|
@ -482,6 +509,9 @@ func (c *Client) newRequest(method, path string) (*request, error) {
|
||||||
if c.config.Region != "" {
|
if c.config.Region != "" {
|
||||||
r.params.Set("region", 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 {
|
if c.config.WaitTime != 0 {
|
||||||
r.params.Set("wait", durToMsec(r.config.WaitTime))
|
r.params.Set("wait", durToMsec(r.config.WaitTime))
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,11 +115,19 @@ func TestDefaultConfig_env(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
url := "http://1.2.3.4:5678"
|
url := "http://1.2.3.4:5678"
|
||||||
auth := []string{"nomaduser", "12345"}
|
auth := []string{"nomaduser", "12345"}
|
||||||
|
region := "test"
|
||||||
|
namespace := "dev"
|
||||||
token := "foobar"
|
token := "foobar"
|
||||||
|
|
||||||
os.Setenv("NOMAD_ADDR", url)
|
os.Setenv("NOMAD_ADDR", url)
|
||||||
defer os.Setenv("NOMAD_ADDR", "")
|
defer os.Setenv("NOMAD_ADDR", "")
|
||||||
|
|
||||||
|
os.Setenv("NOMAD_REGION", region)
|
||||||
|
defer os.Setenv("NOMAD_REGION", "")
|
||||||
|
|
||||||
|
os.Setenv("NOMAD_NAMESPACE", namespace)
|
||||||
|
defer os.Setenv("NOMAD_NAMESPACE", "")
|
||||||
|
|
||||||
os.Setenv("NOMAD_HTTP_AUTH", strings.Join(auth, ":"))
|
os.Setenv("NOMAD_HTTP_AUTH", strings.Join(auth, ":"))
|
||||||
defer os.Setenv("NOMAD_HTTP_AUTH", "")
|
defer os.Setenv("NOMAD_HTTP_AUTH", "")
|
||||||
|
|
||||||
|
@ -132,6 +140,14 @@ func TestDefaultConfig_env(t *testing.T) {
|
||||||
t.Errorf("expected %q to be %q", config.Address, url)
|
t.Errorf("expected %q to be %q", config.Address, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Region != region {
|
||||||
|
t.Errorf("expected %q to be %q", config.Region, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Namespace != namespace {
|
||||||
|
t.Errorf("expected %q to be %q", config.Namespace, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
if config.HttpAuth.Username != auth[0] {
|
if config.HttpAuth.Username != auth[0] {
|
||||||
t.Errorf("expected %q to be %q", config.HttpAuth.Username, auth[0])
|
t.Errorf("expected %q to be %q", config.HttpAuth.Username, auth[0])
|
||||||
}
|
}
|
||||||
|
@ -153,6 +169,7 @@ func TestSetQueryOptions(t *testing.T) {
|
||||||
r, _ := c.newRequest("GET", "/v1/jobs")
|
r, _ := c.newRequest("GET", "/v1/jobs")
|
||||||
q := &QueryOptions{
|
q := &QueryOptions{
|
||||||
Region: "foo",
|
Region: "foo",
|
||||||
|
Namespace: "bar",
|
||||||
AllowStale: true,
|
AllowStale: true,
|
||||||
WaitIndex: 1000,
|
WaitIndex: 1000,
|
||||||
WaitTime: 100 * time.Second,
|
WaitTime: 100 * time.Second,
|
||||||
|
@ -163,6 +180,9 @@ func TestSetQueryOptions(t *testing.T) {
|
||||||
if r.params.Get("region") != "foo" {
|
if r.params.Get("region") != "foo" {
|
||||||
t.Fatalf("bad: %v", r.params)
|
t.Fatalf("bad: %v", r.params)
|
||||||
}
|
}
|
||||||
|
if r.params.Get("namespace") != "bar" {
|
||||||
|
t.Fatalf("bad: %v", r.params)
|
||||||
|
}
|
||||||
if _, ok := r.params["stale"]; !ok {
|
if _, ok := r.params["stale"]; !ok {
|
||||||
t.Fatalf("bad: %v", r.params)
|
t.Fatalf("bad: %v", r.params)
|
||||||
}
|
}
|
||||||
|
@ -184,14 +204,18 @@ func TestSetWriteOptions(t *testing.T) {
|
||||||
|
|
||||||
r, _ := c.newRequest("GET", "/v1/jobs")
|
r, _ := c.newRequest("GET", "/v1/jobs")
|
||||||
q := &WriteOptions{
|
q := &WriteOptions{
|
||||||
Region: "foo",
|
Region: "foo",
|
||||||
SecretID: "foobar",
|
Namespace: "bar",
|
||||||
|
SecretID: "foobar",
|
||||||
}
|
}
|
||||||
r.setWriteOptions(q)
|
r.setWriteOptions(q)
|
||||||
|
|
||||||
if r.params.Get("region") != "foo" {
|
if r.params.Get("region") != "foo" {
|
||||||
t.Fatalf("bad: %v", r.params)
|
t.Fatalf("bad: %v", r.params)
|
||||||
}
|
}
|
||||||
|
if r.params.Get("namespace") != "bar" {
|
||||||
|
t.Fatalf("bad: %v", r.params)
|
||||||
|
}
|
||||||
if r.token != "foobar" {
|
if r.token != "foobar" {
|
||||||
t.Fatalf("bad: %v", r.token)
|
t.Fatalf("bad: %v", r.token)
|
||||||
}
|
}
|
||||||
|
@ -204,8 +228,9 @@ func TestRequestToHTTP(t *testing.T) {
|
||||||
|
|
||||||
r, _ := c.newRequest("DELETE", "/v1/jobs/foo")
|
r, _ := c.newRequest("DELETE", "/v1/jobs/foo")
|
||||||
q := &QueryOptions{
|
q := &QueryOptions{
|
||||||
Region: "foo",
|
Region: "foo",
|
||||||
SecretID: "foobar",
|
Namespace: "bar",
|
||||||
|
SecretID: "foobar",
|
||||||
}
|
}
|
||||||
r.setQueryOptions(q)
|
r.setQueryOptions(q)
|
||||||
req, err := r.toHTTP()
|
req, err := r.toHTTP()
|
||||||
|
@ -216,7 +241,7 @@ func TestRequestToHTTP(t *testing.T) {
|
||||||
if req.Method != "DELETE" {
|
if req.Method != "DELETE" {
|
||||||
t.Fatalf("bad: %v", req)
|
t.Fatalf("bad: %v", req)
|
||||||
}
|
}
|
||||||
if req.URL.RequestURI() != "/v1/jobs/foo?region=foo" {
|
if req.URL.RequestURI() != "/v1/jobs/foo?namespace=bar®ion=foo" {
|
||||||
t.Fatalf("bad: %v", req)
|
t.Fatalf("bad: %v", req)
|
||||||
}
|
}
|
||||||
if req.Header.Get("X-Nomad-Token") != "foobar" {
|
if req.Header.Get("X-Nomad-Token") != "foobar" {
|
||||||
|
@ -272,7 +297,10 @@ func TestQueryString(t *testing.T) {
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
r, _ := c.newRequest("PUT", "/v1/abc?foo=bar&baz=zip")
|
r, _ := c.newRequest("PUT", "/v1/abc?foo=bar&baz=zip")
|
||||||
q := &WriteOptions{Region: "foo"}
|
q := &WriteOptions{
|
||||||
|
Region: "foo",
|
||||||
|
Namespace: "bar",
|
||||||
|
}
|
||||||
r.setWriteOptions(q)
|
r.setWriteOptions(q)
|
||||||
|
|
||||||
req, err := r.toHTTP()
|
req, err := r.toHTTP()
|
||||||
|
@ -280,7 +308,7 @@ func TestQueryString(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if uri := req.URL.RequestURI(); uri != "/v1/abc?baz=zip&foo=bar®ion=foo" {
|
if uri := req.URL.RequestURI(); uri != "/v1/abc?baz=zip&foo=bar&namespace=bar®ion=foo" {
|
||||||
t.Fatalf("bad uri: %q", uri)
|
t.Fatalf("bad uri: %q", uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,6 @@ const (
|
||||||
Evals Context = "evals"
|
Evals Context = "evals"
|
||||||
Jobs Context = "jobs"
|
Jobs Context = "jobs"
|
||||||
Nodes Context = "nodes"
|
Nodes Context = "nodes"
|
||||||
|
Namespaces Context = "namespaces"
|
||||||
All Context = "all"
|
All Context = "all"
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,7 +14,7 @@ func (c *Client) Deployments() *Deployments {
|
||||||
return &Deployments{client: c}
|
return &Deployments{client: c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List is used to dump all of the evaluations.
|
// List is used to dump all of the deployments.
|
||||||
func (d *Deployments) List(q *QueryOptions) ([]*Deployment, *QueryMeta, error) {
|
func (d *Deployments) List(q *QueryOptions) ([]*Deployment, *QueryMeta, error) {
|
||||||
var resp []*Deployment
|
var resp []*Deployment
|
||||||
qm, err := d.client.query("/v1/deployments", &resp, q)
|
qm, err := d.client.query("/v1/deployments", &resp, q)
|
||||||
|
@ -29,7 +29,7 @@ func (d *Deployments) PrefixList(prefix string) ([]*Deployment, *QueryMeta, erro
|
||||||
return d.List(&QueryOptions{Prefix: prefix})
|
return d.List(&QueryOptions{Prefix: prefix})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info is used to query a single evaluation by its ID.
|
// Info is used to query a single deployment by its ID.
|
||||||
func (d *Deployments) Info(deploymentID string, q *QueryOptions) (*Deployment, *QueryMeta, error) {
|
func (d *Deployments) Info(deploymentID string, q *QueryOptions) (*Deployment, *QueryMeta, error) {
|
||||||
var resp Deployment
|
var resp Deployment
|
||||||
qm, err := d.client.query("/v1/deployment/"+deploymentID, &resp, q)
|
qm, err := d.client.query("/v1/deployment/"+deploymentID, &resp, q)
|
||||||
|
@ -125,6 +125,7 @@ func (d *Deployments) SetAllocHealth(deploymentID string, healthy, unhealthy []s
|
||||||
// Deployment is used to serialize an deployment.
|
// Deployment is used to serialize an deployment.
|
||||||
type Deployment struct {
|
type Deployment struct {
|
||||||
ID string
|
ID string
|
||||||
|
Namespace string
|
||||||
JobID string
|
JobID string
|
||||||
JobVersion uint64
|
JobVersion uint64
|
||||||
JobModifyIndex uint64
|
JobModifyIndex uint64
|
||||||
|
|
|
@ -58,6 +58,7 @@ type Evaluation struct {
|
||||||
Priority int
|
Priority int
|
||||||
Type string
|
Type string
|
||||||
TriggeredBy string
|
TriggeredBy string
|
||||||
|
Namespace string
|
||||||
JobID string
|
JobID string
|
||||||
JobModifyIndex uint64
|
JobModifyIndex uint64
|
||||||
NodeID string
|
NodeID string
|
||||||
|
|
20
api/jobs.go
20
api/jobs.go
|
@ -21,6 +21,9 @@ const (
|
||||||
|
|
||||||
// PeriodicSpecCron is used for a cron spec.
|
// PeriodicSpecCron is used for a cron spec.
|
||||||
PeriodicSpecCron = "cron"
|
PeriodicSpecCron = "cron"
|
||||||
|
|
||||||
|
// DefaultNamespace is the default namespace.
|
||||||
|
DefaultNamespace = "default"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -500,6 +503,7 @@ type ParameterizedJobConfig struct {
|
||||||
type Job struct {
|
type Job struct {
|
||||||
Stop *bool
|
Stop *bool
|
||||||
Region *string
|
Region *string
|
||||||
|
Namespace *string
|
||||||
ID *string
|
ID *string
|
||||||
ParentID *string
|
ParentID *string
|
||||||
Name *string
|
Name *string
|
||||||
|
@ -545,6 +549,9 @@ func (j *Job) Canonicalize() {
|
||||||
if j.ParentID == nil {
|
if j.ParentID == nil {
|
||||||
j.ParentID = helper.StringToPtr("")
|
j.ParentID = helper.StringToPtr("")
|
||||||
}
|
}
|
||||||
|
if j.Namespace == nil {
|
||||||
|
j.Namespace = helper.StringToPtr(DefaultNamespace)
|
||||||
|
}
|
||||||
if j.Priority == nil {
|
if j.Priority == nil {
|
||||||
j.Priority = helper.IntToPtr(50)
|
j.Priority = helper.IntToPtr(50)
|
||||||
}
|
}
|
||||||
|
@ -554,6 +561,9 @@ func (j *Job) Canonicalize() {
|
||||||
if j.Region == nil {
|
if j.Region == nil {
|
||||||
j.Region = helper.StringToPtr("global")
|
j.Region = helper.StringToPtr("global")
|
||||||
}
|
}
|
||||||
|
if j.Namespace == nil {
|
||||||
|
j.Namespace = helper.StringToPtr("default")
|
||||||
|
}
|
||||||
if j.Type == nil {
|
if j.Type == nil {
|
||||||
j.Type = helper.StringToPtr("service")
|
j.Type = helper.StringToPtr("service")
|
||||||
}
|
}
|
||||||
|
@ -598,9 +608,10 @@ func (j *Job) Canonicalize() {
|
||||||
|
|
||||||
// JobSummary summarizes the state of the allocations of a job
|
// JobSummary summarizes the state of the allocations of a job
|
||||||
type JobSummary struct {
|
type JobSummary struct {
|
||||||
JobID string
|
JobID string
|
||||||
Summary map[string]TaskGroupSummary
|
Namespace string
|
||||||
Children *JobChildrenSummary
|
Summary map[string]TaskGroupSummary
|
||||||
|
Children *JobChildrenSummary
|
||||||
|
|
||||||
// Raft Indexes
|
// Raft Indexes
|
||||||
CreateIndex uint64
|
CreateIndex uint64
|
||||||
|
@ -730,6 +741,9 @@ type WriteRequest struct {
|
||||||
// The target region for this write
|
// The target region for this write
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Namespace is the target namespace for this write
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// SecretID is the secret ID of an ACL token
|
// SecretID is the secret ID of an ACL token
|
||||||
SecretID string
|
SecretID string
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ func TestJobs_Canonicalize(t *testing.T) {
|
||||||
ID: helper.StringToPtr(""),
|
ID: helper.StringToPtr(""),
|
||||||
Name: helper.StringToPtr(""),
|
Name: helper.StringToPtr(""),
|
||||||
Region: helper.StringToPtr("global"),
|
Region: helper.StringToPtr("global"),
|
||||||
|
Namespace: helper.StringToPtr(DefaultNamespace),
|
||||||
Type: helper.StringToPtr("service"),
|
Type: helper.StringToPtr("service"),
|
||||||
ParentID: helper.StringToPtr(""),
|
ParentID: helper.StringToPtr(""),
|
||||||
Priority: helper.IntToPtr(50),
|
Priority: helper.IntToPtr(50),
|
||||||
|
@ -146,9 +147,10 @@ func TestJobs_Canonicalize(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "partial",
|
name: "partial",
|
||||||
input: &Job{
|
input: &Job{
|
||||||
Name: helper.StringToPtr("foo"),
|
Name: helper.StringToPtr("foo"),
|
||||||
ID: helper.StringToPtr("bar"),
|
Namespace: helper.StringToPtr("bar"),
|
||||||
ParentID: helper.StringToPtr("lol"),
|
ID: helper.StringToPtr("bar"),
|
||||||
|
ParentID: helper.StringToPtr("lol"),
|
||||||
TaskGroups: []*TaskGroup{
|
TaskGroups: []*TaskGroup{
|
||||||
{
|
{
|
||||||
Name: helper.StringToPtr("bar"),
|
Name: helper.StringToPtr("bar"),
|
||||||
|
@ -161,6 +163,7 @@ func TestJobs_Canonicalize(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: &Job{
|
expected: &Job{
|
||||||
|
Namespace: helper.StringToPtr("bar"),
|
||||||
ID: helper.StringToPtr("bar"),
|
ID: helper.StringToPtr("bar"),
|
||||||
Name: helper.StringToPtr("foo"),
|
Name: helper.StringToPtr("foo"),
|
||||||
Region: helper.StringToPtr("global"),
|
Region: helper.StringToPtr("global"),
|
||||||
|
@ -284,6 +287,7 @@ func TestJobs_Canonicalize(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: &Job{
|
expected: &Job{
|
||||||
|
Namespace: helper.StringToPtr(DefaultNamespace),
|
||||||
ID: helper.StringToPtr("example_template"),
|
ID: helper.StringToPtr("example_template"),
|
||||||
Name: helper.StringToPtr("example_template"),
|
Name: helper.StringToPtr("example_template"),
|
||||||
ParentID: helper.StringToPtr(""),
|
ParentID: helper.StringToPtr(""),
|
||||||
|
@ -420,6 +424,7 @@ func TestJobs_Canonicalize(t *testing.T) {
|
||||||
Periodic: &PeriodicConfig{},
|
Periodic: &PeriodicConfig{},
|
||||||
},
|
},
|
||||||
expected: &Job{
|
expected: &Job{
|
||||||
|
Namespace: helper.StringToPtr(DefaultNamespace),
|
||||||
ID: helper.StringToPtr("bar"),
|
ID: helper.StringToPtr("bar"),
|
||||||
ParentID: helper.StringToPtr(""),
|
ParentID: helper.StringToPtr(""),
|
||||||
Name: helper.StringToPtr("bar"),
|
Name: helper.StringToPtr("bar"),
|
||||||
|
@ -489,6 +494,7 @@ func TestJobs_Canonicalize(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: &Job{
|
expected: &Job{
|
||||||
|
Namespace: helper.StringToPtr(DefaultNamespace),
|
||||||
ID: helper.StringToPtr("bar"),
|
ID: helper.StringToPtr("bar"),
|
||||||
Name: helper.StringToPtr("foo"),
|
Name: helper.StringToPtr("foo"),
|
||||||
Region: helper.StringToPtr("global"),
|
Region: helper.StringToPtr("global"),
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
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
|
||||||
|
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]
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
// +build pro ent
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNamespaces_Register(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := assert.New(t)
|
||||||
|
c, s := makeClient(t, nil, nil)
|
||||||
|
defer s.Stop()
|
||||||
|
namespaces := c.Namespaces()
|
||||||
|
|
||||||
|
// Create a namespace and register it
|
||||||
|
ns := testNamespace()
|
||||||
|
wm, err := namespaces.Register(ns, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertWriteMeta(t, wm)
|
||||||
|
|
||||||
|
// Query the jobs back out again
|
||||||
|
resp, qm, err := namespaces.List(nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertQueryMeta(t, qm)
|
||||||
|
assert.Len(resp, 2)
|
||||||
|
assert.Equal(ns.Name, resp[0].Name)
|
||||||
|
assert.Equal("default", resp[1].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaces_Register_Invalid(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := assert.New(t)
|
||||||
|
c, s := makeClient(t, nil, nil)
|
||||||
|
defer s.Stop()
|
||||||
|
namespaces := c.Namespaces()
|
||||||
|
|
||||||
|
// Create an invalid namespace and register it
|
||||||
|
ns := testNamespace()
|
||||||
|
ns.Name = "*"
|
||||||
|
_, err := namespaces.Register(ns, nil)
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespace_Info(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := assert.New(t)
|
||||||
|
c, s := makeClient(t, nil, nil)
|
||||||
|
defer s.Stop()
|
||||||
|
namespaces := c.Namespaces()
|
||||||
|
|
||||||
|
// Trying to retrieve a namespace before it exists returns an error
|
||||||
|
_, _, err := namespaces.Info("foo", nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Contains("not found", err.Error())
|
||||||
|
|
||||||
|
// Register the namespace
|
||||||
|
ns := testNamespace()
|
||||||
|
wm, err := namespaces.Register(ns, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertWriteMeta(t, wm)
|
||||||
|
|
||||||
|
// Query the namespace again and ensure it exists
|
||||||
|
result, qm, err := namespaces.Info(ns.Name, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertQueryMeta(t, qm)
|
||||||
|
assert.NotNil(result)
|
||||||
|
assert.Equal(ns.Name, result.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaces_Delete(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := assert.New(t)
|
||||||
|
c, s := makeClient(t, nil, nil)
|
||||||
|
defer s.Stop()
|
||||||
|
namespaces := c.Namespaces()
|
||||||
|
|
||||||
|
// Create a namespace and register it
|
||||||
|
ns := testNamespace()
|
||||||
|
wm, err := namespaces.Register(ns, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertWriteMeta(t, wm)
|
||||||
|
|
||||||
|
// Query the namespace back out again
|
||||||
|
resp, qm, err := namespaces.List(nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertQueryMeta(t, qm)
|
||||||
|
assert.Len(resp, 2)
|
||||||
|
assert.Equal(ns.Name, resp[0].Name)
|
||||||
|
assert.Equal("default", resp[1].Name)
|
||||||
|
|
||||||
|
// Delete the namespace
|
||||||
|
wm, err = namespaces.Delete(ns.Name, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertWriteMeta(t, wm)
|
||||||
|
|
||||||
|
// Query the namespaces back out again
|
||||||
|
resp, qm, err = namespaces.List(nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertQueryMeta(t, qm)
|
||||||
|
assert.Len(resp, 1)
|
||||||
|
assert.Equal("default", resp[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaces_List(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := assert.New(t)
|
||||||
|
c, s := makeClient(t, nil, nil)
|
||||||
|
defer s.Stop()
|
||||||
|
namespaces := c.Namespaces()
|
||||||
|
|
||||||
|
// Create two namespaces and register them
|
||||||
|
ns1 := testNamespace()
|
||||||
|
ns2 := testNamespace()
|
||||||
|
ns1.Name = "fooaaa"
|
||||||
|
ns2.Name = "foobbb"
|
||||||
|
wm, err := namespaces.Register(ns1, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertWriteMeta(t, wm)
|
||||||
|
|
||||||
|
wm, err = namespaces.Register(ns2, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertWriteMeta(t, wm)
|
||||||
|
|
||||||
|
// Query the namespaces
|
||||||
|
resp, qm, err := namespaces.List(nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertQueryMeta(t, qm)
|
||||||
|
assert.Len(resp, 3)
|
||||||
|
|
||||||
|
// Query the namespaces using a prefix
|
||||||
|
resp, qm, err = namespaces.PrefixList("foo", nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertQueryMeta(t, qm)
|
||||||
|
assert.Len(resp, 2)
|
||||||
|
|
||||||
|
// Query the namespaces using a prefix
|
||||||
|
resp, qm, err = namespaces.PrefixList("foob", nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assertQueryMeta(t, qm)
|
||||||
|
assert.Len(resp, 1)
|
||||||
|
assert.Equal(ns2.Name, resp[0].Name)
|
||||||
|
}
|
|
@ -55,3 +55,10 @@ func testPeriodicJob() *Job {
|
||||||
})
|
})
|
||||||
return job
|
return job
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testNamespace() *Namespace {
|
||||||
|
return &Namespace{
|
||||||
|
Name: "test-namespace",
|
||||||
|
Description: "Testing namespaces",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (s *HTTPServer) aclPolicyUpdate(resp http.ResponseWriter, req *http.Request
|
||||||
args := structs.ACLPolicyUpsertRequest{
|
args := structs.ACLPolicyUpsertRequest{
|
||||||
Policies: []*structs.ACLPolicy{&policy},
|
Policies: []*structs.ACLPolicy{&policy},
|
||||||
}
|
}
|
||||||
s.parseWrite(req, &args.WriteRequest)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.GenericResponse
|
var out structs.GenericResponse
|
||||||
if err := s.agent.RPC("ACL.UpsertPolicies", &args, &out); err != nil {
|
if err := s.agent.RPC("ACL.UpsertPolicies", &args, &out); err != nil {
|
||||||
|
@ -100,7 +100,7 @@ func (s *HTTPServer) aclPolicyDelete(resp http.ResponseWriter, req *http.Request
|
||||||
args := structs.ACLPolicyDeleteRequest{
|
args := structs.ACLPolicyDeleteRequest{
|
||||||
Names: []string{policyName},
|
Names: []string{policyName},
|
||||||
}
|
}
|
||||||
s.parseWrite(req, &args.WriteRequest)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.GenericResponse
|
var out structs.GenericResponse
|
||||||
if err := s.agent.RPC("ACL.DeletePolicies", &args, &out); err != nil {
|
if err := s.agent.RPC("ACL.DeletePolicies", &args, &out); err != nil {
|
||||||
|
@ -140,7 +140,7 @@ func (s *HTTPServer) ACLTokenBootstrap(resp http.ResponseWriter, req *http.Reque
|
||||||
|
|
||||||
// Format the request
|
// Format the request
|
||||||
args := structs.ACLTokenBootstrapRequest{}
|
args := structs.ACLTokenBootstrapRequest{}
|
||||||
s.parseWrite(req, &args.WriteRequest)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.ACLTokenUpsertResponse
|
var out structs.ACLTokenUpsertResponse
|
||||||
if err := s.agent.RPC("ACL.Bootstrap", &args, &out); err != nil {
|
if err := s.agent.RPC("ACL.Bootstrap", &args, &out); err != nil {
|
||||||
|
@ -220,7 +220,7 @@ func (s *HTTPServer) aclTokenUpdate(resp http.ResponseWriter, req *http.Request,
|
||||||
args := structs.ACLTokenUpsertRequest{
|
args := structs.ACLTokenUpsertRequest{
|
||||||
Tokens: []*structs.ACLToken{&token},
|
Tokens: []*structs.ACLToken{&token},
|
||||||
}
|
}
|
||||||
s.parseWrite(req, &args.WriteRequest)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.ACLTokenUpsertResponse
|
var out structs.ACLTokenUpsertResponse
|
||||||
if err := s.agent.RPC("ACL.UpsertTokens", &args, &out); err != nil {
|
if err := s.agent.RPC("ACL.UpsertTokens", &args, &out); err != nil {
|
||||||
|
@ -239,7 +239,7 @@ func (s *HTTPServer) aclTokenDelete(resp http.ResponseWriter, req *http.Request,
|
||||||
args := structs.ACLTokenDeleteRequest{
|
args := structs.ACLTokenDeleteRequest{
|
||||||
AccessorIDs: []string{tokenAccessor},
|
AccessorIDs: []string{tokenAccessor},
|
||||||
}
|
}
|
||||||
s.parseWrite(req, &args.WriteRequest)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.GenericResponse
|
var out structs.GenericResponse
|
||||||
if err := s.agent.RPC("ACL.DeleteTokens", &args, &out); err != nil {
|
if err := s.agent.RPC("ACL.DeleteTokens", &args, &out); err != nil {
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (s *HTTPServer) deploymentFail(resp http.ResponseWriter, req *http.Request,
|
||||||
args := structs.DeploymentFailRequest{
|
args := structs.DeploymentFailRequest{
|
||||||
DeploymentID: deploymentID,
|
DeploymentID: deploymentID,
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.DeploymentUpdateResponse
|
var out structs.DeploymentUpdateResponse
|
||||||
if err := s.agent.RPC("Deployment.Fail", &args, &out); err != nil {
|
if err := s.agent.RPC("Deployment.Fail", &args, &out); err != nil {
|
||||||
|
@ -85,7 +85,7 @@ func (s *HTTPServer) deploymentPause(resp http.ResponseWriter, req *http.Request
|
||||||
if pauseRequest.DeploymentID != deploymentID {
|
if pauseRequest.DeploymentID != deploymentID {
|
||||||
return nil, CodedError(400, "Deployment ID does not match")
|
return nil, CodedError(400, "Deployment ID does not match")
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &pauseRequest.Region)
|
s.parseWriteRequest(req, &pauseRequest.WriteRequest)
|
||||||
|
|
||||||
var out structs.DeploymentUpdateResponse
|
var out structs.DeploymentUpdateResponse
|
||||||
if err := s.agent.RPC("Deployment.Pause", &pauseRequest, &out); err != nil {
|
if err := s.agent.RPC("Deployment.Pause", &pauseRequest, &out); err != nil {
|
||||||
|
@ -110,7 +110,7 @@ func (s *HTTPServer) deploymentPromote(resp http.ResponseWriter, req *http.Reque
|
||||||
if promoteRequest.DeploymentID != deploymentID {
|
if promoteRequest.DeploymentID != deploymentID {
|
||||||
return nil, CodedError(400, "Deployment ID does not match")
|
return nil, CodedError(400, "Deployment ID does not match")
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &promoteRequest.Region)
|
s.parseWriteRequest(req, &promoteRequest.WriteRequest)
|
||||||
|
|
||||||
var out structs.DeploymentUpdateResponse
|
var out structs.DeploymentUpdateResponse
|
||||||
if err := s.agent.RPC("Deployment.Promote", &promoteRequest, &out); err != nil {
|
if err := s.agent.RPC("Deployment.Promote", &promoteRequest, &out); err != nil {
|
||||||
|
@ -135,7 +135,7 @@ func (s *HTTPServer) deploymentSetAllocHealth(resp http.ResponseWriter, req *htt
|
||||||
if healthRequest.DeploymentID != deploymentID {
|
if healthRequest.DeploymentID != deploymentID {
|
||||||
return nil, CodedError(400, "Deployment ID does not match")
|
return nil, CodedError(400, "Deployment ID does not match")
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &healthRequest.Region)
|
s.parseWriteRequest(req, &healthRequest.WriteRequest)
|
||||||
|
|
||||||
var out structs.DeploymentUpdateResponse
|
var out structs.DeploymentUpdateResponse
|
||||||
if err := s.agent.RPC("Deployment.SetAllocHealth", &healthRequest, &out); err != nil {
|
if err := s.agent.RPC("Deployment.SetAllocHealth", &healthRequest, &out); err != nil {
|
||||||
|
|
|
@ -159,7 +159,10 @@ func TestHTTP_DeploymentPause(t *testing.T) {
|
||||||
args := structs.DeploymentPauseRequest{
|
args := structs.DeploymentPauseRequest{
|
||||||
DeploymentID: d.ID,
|
DeploymentID: d.ID,
|
||||||
Pause: false,
|
Pause: false,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args)
|
buf := encodeReq(args)
|
||||||
|
|
||||||
|
@ -197,7 +200,10 @@ func TestHTTP_DeploymentPromote(t *testing.T) {
|
||||||
args := structs.DeploymentPromoteRequest{
|
args := structs.DeploymentPromoteRequest{
|
||||||
DeploymentID: d.ID,
|
DeploymentID: d.ID,
|
||||||
All: true,
|
All: true,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args)
|
buf := encodeReq(args)
|
||||||
|
|
||||||
|
@ -239,7 +245,10 @@ func TestHTTP_DeploymentAllocHealth(t *testing.T) {
|
||||||
args := structs.DeploymentAllocHealthRequest{
|
args := structs.DeploymentAllocHealthRequest{
|
||||||
DeploymentID: d.ID,
|
DeploymentID: d.ID,
|
||||||
HealthyAllocationIDs: []string{a.ID},
|
HealthyAllocationIDs: []string{a.ID},
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args)
|
buf := encodeReq(args)
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,9 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
|
||||||
s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
s.mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
s.mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register enterprise endpoints.
|
||||||
|
s.registerEnterpriseHandlers()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPCodedError is used to provide the HTTP error code
|
// HTTPCodedError is used to provide the HTTP error code
|
||||||
|
@ -361,6 +364,15 @@ func (s *HTTPServer) parseRegion(req *http.Request, r *string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseNamespace is used to parse the ?namespace parameter
|
||||||
|
func parseNamespace(req *http.Request, n *string) {
|
||||||
|
if other := req.URL.Query().Get("namespace"); other != "" {
|
||||||
|
*n = other
|
||||||
|
} else if *n == "" {
|
||||||
|
*n = structs.DefaultNamespace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parseToken is used to parse the X-Nomad-Token param
|
// parseToken is used to parse the X-Nomad-Token param
|
||||||
func (s *HTTPServer) parseToken(req *http.Request, token *string) {
|
func (s *HTTPServer) parseToken(req *http.Request, token *string) {
|
||||||
if other := req.Header.Get("X-Nomad-Token"); other != "" {
|
if other := req.Header.Get("X-Nomad-Token"); other != "" {
|
||||||
|
@ -369,17 +381,20 @@ func (s *HTTPServer) parseToken(req *http.Request, token *string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseWrite is a convenience method for endpoints that call write methods
|
|
||||||
func (s *HTTPServer) parseWrite(req *http.Request, b *structs.WriteRequest) {
|
|
||||||
s.parseRegion(req, &b.Region)
|
|
||||||
s.parseToken(req, &b.SecretID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse is a convenience method for endpoints that need to parse multiple flags
|
// parse is a convenience method for endpoints that need to parse multiple flags
|
||||||
func (s *HTTPServer) parse(resp http.ResponseWriter, req *http.Request, r *string, b *structs.QueryOptions) bool {
|
func (s *HTTPServer) parse(resp http.ResponseWriter, req *http.Request, r *string, b *structs.QueryOptions) bool {
|
||||||
s.parseRegion(req, r)
|
s.parseRegion(req, r)
|
||||||
s.parseToken(req, &b.SecretID)
|
s.parseToken(req, &b.SecretID)
|
||||||
parseConsistency(req, b)
|
parseConsistency(req, b)
|
||||||
parsePrefix(req, b)
|
parsePrefix(req, b)
|
||||||
|
parseNamespace(req, &b.Namespace)
|
||||||
return parseWait(resp, req, b)
|
return parseWait(resp, req, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseWriteRequest is a convience method for endpoints that need to parse a
|
||||||
|
// write request.
|
||||||
|
func (s *HTTPServer) parseWriteRequest(req *http.Request, w *structs.WriteRequest) {
|
||||||
|
parseNamespace(req, &w.Namespace)
|
||||||
|
s.parseToken(req, &w.SecretID)
|
||||||
|
s.parseRegion(req, &w.Region)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
// +build !pro,!ent
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
// registerEnterpriseHandlers is a no-op for the oss release
|
||||||
|
func (s *HTTPServer) registerEnterpriseHandlers() {}
|
|
@ -92,7 +92,7 @@ func (s *HTTPServer) jobForceEvaluate(resp http.ResponseWriter, req *http.Reques
|
||||||
args := structs.JobEvaluateRequest{
|
args := structs.JobEvaluateRequest{
|
||||||
JobID: jobName,
|
JobID: jobName,
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.JobRegisterResponse
|
var out structs.JobRegisterResponse
|
||||||
if err := s.agent.RPC("Job.Evaluate", &args, &out); err != nil {
|
if err := s.agent.RPC("Job.Evaluate", &args, &out); err != nil {
|
||||||
|
@ -121,7 +121,6 @@ func (s *HTTPServer) jobPlan(resp http.ResponseWriter, req *http.Request,
|
||||||
if jobName != "" && *args.Job.ID != jobName {
|
if jobName != "" && *args.Job.ID != jobName {
|
||||||
return nil, CodedError(400, "Job ID does not match")
|
return nil, CodedError(400, "Job ID does not match")
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
|
||||||
|
|
||||||
sJob := ApiJobToStructJob(args.Job)
|
sJob := ApiJobToStructJob(args.Job)
|
||||||
planReq := structs.JobPlanRequest{
|
planReq := structs.JobPlanRequest{
|
||||||
|
@ -131,6 +130,7 @@ func (s *HTTPServer) jobPlan(resp http.ResponseWriter, req *http.Request,
|
||||||
Region: args.WriteRequest.Region,
|
Region: args.WriteRequest.Region,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
s.parseWriteRequest(req, &planReq.WriteRequest)
|
||||||
var out structs.JobPlanResponse
|
var out structs.JobPlanResponse
|
||||||
if err := s.agent.RPC("Job.Plan", &planReq, &out); err != nil {
|
if err := s.agent.RPC("Job.Plan", &planReq, &out); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -160,7 +160,7 @@ func (s *HTTPServer) ValidateJobRequest(resp http.ResponseWriter, req *http.Requ
|
||||||
Region: validateRequest.Region,
|
Region: validateRequest.Region,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.JobValidateResponse
|
var out structs.JobValidateResponse
|
||||||
if err := s.agent.RPC("Job.Validate", &args, &out); err != nil {
|
if err := s.agent.RPC("Job.Validate", &args, &out); err != nil {
|
||||||
|
@ -179,7 +179,7 @@ func (s *HTTPServer) periodicForceRequest(resp http.ResponseWriter, req *http.Re
|
||||||
args := structs.PeriodicForceRequest{
|
args := structs.PeriodicForceRequest{
|
||||||
JobID: jobName,
|
JobID: jobName,
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.PeriodicForceResponse
|
var out structs.PeriodicForceResponse
|
||||||
if err := s.agent.RPC("Periodic.Force", &args, &out); err != nil {
|
if err := s.agent.RPC("Periodic.Force", &args, &out); err != nil {
|
||||||
|
@ -348,8 +348,6 @@ func (s *HTTPServer) jobUpdate(resp http.ResponseWriter, req *http.Request,
|
||||||
if jobName != "" && *args.Job.ID != jobName {
|
if jobName != "" && *args.Job.ID != jobName {
|
||||||
return nil, CodedError(400, "Job ID does not match name")
|
return nil, CodedError(400, "Job ID does not match name")
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
|
||||||
s.parseToken(req, &args.SecretID)
|
|
||||||
|
|
||||||
sJob := ApiJobToStructJob(args.Job)
|
sJob := ApiJobToStructJob(args.Job)
|
||||||
|
|
||||||
|
@ -362,6 +360,7 @@ func (s *HTTPServer) jobUpdate(resp http.ResponseWriter, req *http.Request,
|
||||||
SecretID: args.WriteRequest.SecretID,
|
SecretID: args.WriteRequest.SecretID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
s.parseWriteRequest(req, ®Req.WriteRequest)
|
||||||
var out structs.JobRegisterResponse
|
var out structs.JobRegisterResponse
|
||||||
if err := s.agent.RPC("Job.Register", ®Req, &out); err != nil {
|
if err := s.agent.RPC("Job.Register", ®Req, &out); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -387,7 +386,7 @@ func (s *HTTPServer) jobDelete(resp http.ResponseWriter, req *http.Request,
|
||||||
JobID: jobName,
|
JobID: jobName,
|
||||||
Purge: purgeBool,
|
Purge: purgeBool,
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.JobDeregisterResponse
|
var out structs.JobDeregisterResponse
|
||||||
if err := s.agent.RPC("Job.Deregister", &args, &out); err != nil {
|
if err := s.agent.RPC("Job.Deregister", &args, &out); err != nil {
|
||||||
|
@ -449,7 +448,7 @@ func (s *HTTPServer) jobRevert(resp http.ResponseWriter, req *http.Request,
|
||||||
return nil, CodedError(400, "Job ID does not match")
|
return nil, CodedError(400, "Job ID does not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.parseRegion(req, &revertRequest.Region)
|
s.parseWriteRequest(req, &revertRequest.WriteRequest)
|
||||||
|
|
||||||
var out structs.JobRegisterResponse
|
var out structs.JobRegisterResponse
|
||||||
if err := s.agent.RPC("Job.Revert", &revertRequest, &out); err != nil {
|
if err := s.agent.RPC("Job.Revert", &revertRequest, &out); err != nil {
|
||||||
|
@ -478,7 +477,7 @@ func (s *HTTPServer) jobStable(resp http.ResponseWriter, req *http.Request,
|
||||||
return nil, CodedError(400, "Job ID does not match")
|
return nil, CodedError(400, "Job ID does not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.parseRegion(req, &stableRequest.Region)
|
s.parseWriteRequest(req, &stableRequest.WriteRequest)
|
||||||
|
|
||||||
var out structs.JobStabilityResponse
|
var out structs.JobStabilityResponse
|
||||||
if err := s.agent.RPC("Job.Stable", &stableRequest, &out); err != nil {
|
if err := s.agent.RPC("Job.Stable", &stableRequest, &out); err != nil {
|
||||||
|
@ -525,7 +524,7 @@ func (s *HTTPServer) jobDispatchRequest(resp http.ResponseWriter, req *http.Requ
|
||||||
args.JobID = name
|
args.JobID = name
|
||||||
}
|
}
|
||||||
|
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.JobDispatchResponse
|
var out structs.JobDispatchResponse
|
||||||
if err := s.agent.RPC("Job.Dispatch", &args, &out); err != nil {
|
if err := s.agent.RPC("Job.Dispatch", &args, &out); err != nil {
|
||||||
|
@ -541,6 +540,7 @@ func ApiJobToStructJob(job *api.Job) *structs.Job {
|
||||||
j := &structs.Job{
|
j := &structs.Job{
|
||||||
Stop: *job.Stop,
|
Stop: *job.Stop,
|
||||||
Region: *job.Region,
|
Region: *job.Region,
|
||||||
|
Namespace: *job.Namespace,
|
||||||
ID: *job.ID,
|
ID: *job.ID,
|
||||||
ParentID: *job.ParentID,
|
ParentID: *job.ParentID,
|
||||||
Name: *job.Name,
|
Name: *job.Name,
|
||||||
|
|
|
@ -24,8 +24,11 @@ func TestHTTP_JobsList(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -79,8 +82,11 @@ func TestHTTP_PrefixJobsList(t *testing.T) {
|
||||||
job.ID = ids[i]
|
job.ID = ids[i]
|
||||||
job.TaskGroups[0].Count = 1
|
job.TaskGroups[0].Count = 1
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -157,8 +163,11 @@ func TestHTTP_JobsRegister(t *testing.T) {
|
||||||
|
|
||||||
// Check the job is registered
|
// Check the job is registered
|
||||||
getReq := structs.JobSpecificRequest{
|
getReq := structs.JobSpecificRequest{
|
||||||
JobID: *job.ID,
|
JobID: *job.ID,
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var getResp structs.SingleJobResponse
|
var getResp structs.SingleJobResponse
|
||||||
if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil {
|
if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil {
|
||||||
|
@ -243,8 +252,11 @@ func TestHTTP_JobsRegister_Defaulting(t *testing.T) {
|
||||||
|
|
||||||
// Check the job is registered
|
// Check the job is registered
|
||||||
getReq := structs.JobSpecificRequest{
|
getReq := structs.JobSpecificRequest{
|
||||||
JobID: *job.ID,
|
JobID: *job.ID,
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var getResp structs.SingleJobResponse
|
var getResp structs.SingleJobResponse
|
||||||
if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil {
|
if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil {
|
||||||
|
@ -266,8 +278,11 @@ func TestHTTP_JobQuery(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -366,8 +381,11 @@ func TestHTTP_JobUpdate(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := api.MockJob()
|
job := api.MockJob()
|
||||||
args := api.JobRegisterRequest{
|
args := api.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: api.WriteRequest{Region: "global"},
|
WriteRequest: api.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: api.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args)
|
buf := encodeReq(args)
|
||||||
|
|
||||||
|
@ -397,8 +415,11 @@ func TestHTTP_JobUpdate(t *testing.T) {
|
||||||
|
|
||||||
// Check the job is registered
|
// Check the job is registered
|
||||||
getReq := structs.JobSpecificRequest{
|
getReq := structs.JobSpecificRequest{
|
||||||
JobID: *job.ID,
|
JobID: *job.ID,
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var getResp structs.SingleJobResponse
|
var getResp structs.SingleJobResponse
|
||||||
if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil {
|
if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil {
|
||||||
|
@ -417,8 +438,11 @@ func TestHTTP_JobDelete(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -451,8 +475,11 @@ func TestHTTP_JobDelete(t *testing.T) {
|
||||||
|
|
||||||
// Check the job is still queryable
|
// Check the job is still queryable
|
||||||
getReq1 := structs.JobSpecificRequest{
|
getReq1 := structs.JobSpecificRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var getResp1 structs.SingleJobResponse
|
var getResp1 structs.SingleJobResponse
|
||||||
if err := s.Agent.RPC("Job.GetJob", &getReq1, &getResp1); err != nil {
|
if err := s.Agent.RPC("Job.GetJob", &getReq1, &getResp1); err != nil {
|
||||||
|
@ -491,8 +518,11 @@ func TestHTTP_JobDelete(t *testing.T) {
|
||||||
|
|
||||||
// Check the job is gone
|
// Check the job is gone
|
||||||
getReq2 := structs.JobSpecificRequest{
|
getReq2 := structs.JobSpecificRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var getResp2 structs.SingleJobResponse
|
var getResp2 structs.SingleJobResponse
|
||||||
if err := s.Agent.RPC("Job.GetJob", &getReq2, &getResp2); err != nil {
|
if err := s.Agent.RPC("Job.GetJob", &getReq2, &getResp2); err != nil {
|
||||||
|
@ -510,8 +540,11 @@ func TestHTTP_JobForceEvaluate(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -550,8 +583,11 @@ func TestHTTP_JobEvaluations(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -598,8 +634,11 @@ func TestHTTP_JobAllocations(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
alloc1 := mock.Alloc()
|
alloc1 := mock.Alloc()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: alloc1.Job,
|
Job: alloc1.Job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -652,8 +691,11 @@ func TestHTTP_JobDeployments(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
j := mock.Job()
|
j := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: j,
|
Job: j,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
assert.Nil(s.Agent.RPC("Job.Register", &args, &resp), "JobRegister")
|
assert.Nil(s.Agent.RPC("Job.Register", &args, &resp), "JobRegister")
|
||||||
|
@ -691,8 +733,11 @@ func TestHTTP_JobDeployment(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
j := mock.Job()
|
j := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: j,
|
Job: j,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
assert.Nil(s.Agent.RPC("Job.Register", &args, &resp), "JobRegister")
|
assert.Nil(s.Agent.RPC("Job.Register", &args, &resp), "JobRegister")
|
||||||
|
@ -729,8 +774,11 @@ func TestHTTP_JobVersions(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -742,8 +790,11 @@ func TestHTTP_JobVersions(t *testing.T) {
|
||||||
job2.Priority = 100
|
job2.Priority = 100
|
||||||
|
|
||||||
args2 := structs.JobRegisterRequest{
|
args2 := structs.JobRegisterRequest{
|
||||||
Job: job2,
|
Job: job2,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp2 structs.JobRegisterResponse
|
var resp2 structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args2, &resp2); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args2, &resp2); err != nil {
|
||||||
|
@ -801,8 +852,11 @@ func TestHTTP_PeriodicForce(t *testing.T) {
|
||||||
// Create and register a periodic job.
|
// Create and register a periodic job.
|
||||||
job := mock.PeriodicJob()
|
job := mock.PeriodicJob()
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -841,9 +895,12 @@ func TestHTTP_JobPlan(t *testing.T) {
|
||||||
// Create the job
|
// Create the job
|
||||||
job := api.MockJob()
|
job := api.MockJob()
|
||||||
args := api.JobPlanRequest{
|
args := api.JobPlanRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
Diff: true,
|
Diff: true,
|
||||||
WriteRequest: api.WriteRequest{Region: "global"},
|
WriteRequest: api.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: api.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args)
|
buf := encodeReq(args)
|
||||||
|
|
||||||
|
@ -881,8 +938,11 @@ func TestHTTP_JobDispatch(t *testing.T) {
|
||||||
job.ParameterizedJob = &structs.ParameterizedJobConfig{}
|
job.ParameterizedJob = &structs.ParameterizedJobConfig{}
|
||||||
|
|
||||||
args := structs.JobRegisterRequest{
|
args := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobRegisterResponse
|
var resp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
|
||||||
|
@ -892,7 +952,10 @@ func TestHTTP_JobDispatch(t *testing.T) {
|
||||||
// Make the request
|
// Make the request
|
||||||
respW := httptest.NewRecorder()
|
respW := httptest.NewRecorder()
|
||||||
args2 := structs.JobDispatchRequest{
|
args2 := structs.JobDispatchRequest{
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args2)
|
buf := encodeReq(args2)
|
||||||
|
|
||||||
|
@ -927,8 +990,11 @@ func TestHTTP_JobRevert(t *testing.T) {
|
||||||
// Create the job and register it twice
|
// Create the job and register it twice
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
regReq := structs.JobRegisterRequest{
|
regReq := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var regResp structs.JobRegisterResponse
|
var regResp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil {
|
||||||
|
@ -942,9 +1008,12 @@ func TestHTTP_JobRevert(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
args := structs.JobRevertRequest{
|
args := structs.JobRevertRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
JobVersion: 0,
|
JobVersion: 0,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args)
|
buf := encodeReq(args)
|
||||||
|
|
||||||
|
@ -980,8 +1049,11 @@ func TestHTTP_JobStable(t *testing.T) {
|
||||||
// Create the job and register it twice
|
// Create the job and register it twice
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
regReq := structs.JobRegisterRequest{
|
regReq := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var regResp structs.JobRegisterResponse
|
var regResp structs.JobRegisterResponse
|
||||||
if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil {
|
if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil {
|
||||||
|
@ -993,10 +1065,13 @@ func TestHTTP_JobStable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
args := structs.JobStabilityRequest{
|
args := structs.JobStabilityRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
JobVersion: 0,
|
JobVersion: 0,
|
||||||
Stable: true,
|
Stable: true,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf := encodeReq(args)
|
buf := encodeReq(args)
|
||||||
|
|
||||||
|
@ -1030,6 +1105,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
||||||
apiJob := &api.Job{
|
apiJob := &api.Job{
|
||||||
Stop: helper.BoolToPtr(true),
|
Stop: helper.BoolToPtr(true),
|
||||||
Region: helper.StringToPtr("global"),
|
Region: helper.StringToPtr("global"),
|
||||||
|
Namespace: helper.StringToPtr("foo"),
|
||||||
ID: helper.StringToPtr("foo"),
|
ID: helper.StringToPtr("foo"),
|
||||||
ParentID: helper.StringToPtr("lol"),
|
ParentID: helper.StringToPtr("lol"),
|
||||||
Name: helper.StringToPtr("name"),
|
Name: helper.StringToPtr("name"),
|
||||||
|
@ -1224,6 +1300,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
||||||
expected := &structs.Job{
|
expected := &structs.Job{
|
||||||
Stop: true,
|
Stop: true,
|
||||||
Region: "global",
|
Region: "global",
|
||||||
|
Namespace: "foo",
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
ParentID: "lol",
|
ParentID: "lol",
|
||||||
Name: "name",
|
Name: "name",
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (s *HTTPServer) nodeForceEvaluate(resp http.ResponseWriter, req *http.Reque
|
||||||
args := structs.NodeEvaluateRequest{
|
args := structs.NodeEvaluateRequest{
|
||||||
NodeID: nodeID,
|
NodeID: nodeID,
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.NodeUpdateResponse
|
var out structs.NodeUpdateResponse
|
||||||
if err := s.agent.RPC("Node.Evaluate", &args, &out); err != nil {
|
if err := s.agent.RPC("Node.Evaluate", &args, &out); err != nil {
|
||||||
|
@ -109,7 +109,7 @@ func (s *HTTPServer) nodeToggleDrain(resp http.ResponseWriter, req *http.Request
|
||||||
NodeID: nodeID,
|
NodeID: nodeID,
|
||||||
Drain: enable,
|
Drain: enable,
|
||||||
}
|
}
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
var out structs.NodeDrainUpdateResponse
|
var out structs.NodeDrainUpdateResponse
|
||||||
if err := s.agent.RPC("Node.UpdateDrain", &args, &out); err != nil {
|
if err := s.agent.RPC("Node.UpdateDrain", &args, &out); err != nil {
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (s *HTTPServer) OperatorRaftPeer(resp http.ResponseWriter, req *http.Reques
|
||||||
}
|
}
|
||||||
|
|
||||||
var args structs.RaftPeerByAddressRequest
|
var args structs.RaftPeerByAddressRequest
|
||||||
s.parseRegion(req, &args.Region)
|
s.parseWriteRequest(req, &args.WriteRequest)
|
||||||
|
|
||||||
params := req.URL.Query()
|
params := req.URL.Query()
|
||||||
if _, ok := params["address"]; ok {
|
if _, ok := params["address"]; ok {
|
||||||
|
|
|
@ -22,6 +22,10 @@ func (s *HTTPServer) newSearchRequest(resp http.ResponseWriter, req *http.Reques
|
||||||
return nil, CodedError(400, err.Error())
|
return nil, CodedError(400, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
var out structs.SearchResponse
|
var out structs.SearchResponse
|
||||||
if err := s.agent.RPC("Search.PrefixSearch", &args, &out); err != nil {
|
if err := s.agent.RPC("Search.PrefixSearch", &args, &out); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -15,11 +15,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Names of environment variables used to supply various
|
|
||||||
// config options to the Nomad CLI.
|
|
||||||
EnvNomadAddress = "NOMAD_ADDR"
|
|
||||||
EnvNomadRegion = "NOMAD_REGION"
|
|
||||||
|
|
||||||
// Constants for CLI identifier length
|
// Constants for CLI identifier length
|
||||||
shortId = 8
|
shortId = 8
|
||||||
fullId = 36
|
fullId = 36
|
||||||
|
@ -49,6 +44,9 @@ type Meta struct {
|
||||||
// The region to send API requests
|
// The region to send API requests
|
||||||
region string
|
region string
|
||||||
|
|
||||||
|
// namespace to send API requests
|
||||||
|
namespace string
|
||||||
|
|
||||||
caCert string
|
caCert string
|
||||||
caPath string
|
caPath string
|
||||||
clientCert string
|
clientCert string
|
||||||
|
@ -68,6 +66,7 @@ func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
|
||||||
if fs&FlagSetClient != 0 {
|
if fs&FlagSetClient != 0 {
|
||||||
f.StringVar(&m.flagAddress, "address", "", "")
|
f.StringVar(&m.flagAddress, "address", "", "")
|
||||||
f.StringVar(&m.region, "region", "", "")
|
f.StringVar(&m.region, "region", "", "")
|
||||||
|
f.StringVar(&m.namespace, "namespace", "", "")
|
||||||
f.BoolVar(&m.noColor, "no-color", false, "")
|
f.BoolVar(&m.noColor, "no-color", false, "")
|
||||||
f.StringVar(&m.caCert, "ca-cert", "", "")
|
f.StringVar(&m.caCert, "ca-cert", "", "")
|
||||||
f.StringVar(&m.caPath, "ca-path", "", "")
|
f.StringVar(&m.caPath, "ca-path", "", "")
|
||||||
|
@ -103,6 +102,7 @@ func (m *Meta) AutocompleteFlags(fs FlagSetFlags) complete.Flags {
|
||||||
return complete.Flags{
|
return complete.Flags{
|
||||||
"-address": complete.PredictAnything,
|
"-address": complete.PredictAnything,
|
||||||
"-region": complete.PredictAnything,
|
"-region": complete.PredictAnything,
|
||||||
|
"-namespace": NamespacePredictor(m.Client, nil),
|
||||||
"-no-color": complete.PredictNothing,
|
"-no-color": complete.PredictNothing,
|
||||||
"-ca-cert": complete.PredictFiles("*"),
|
"-ca-cert": complete.PredictFiles("*"),
|
||||||
"-ca-path": complete.PredictDirs("*"),
|
"-ca-path": complete.PredictDirs("*"),
|
||||||
|
@ -113,22 +113,23 @@ func (m *Meta) AutocompleteFlags(fs FlagSetFlags) complete.Flags {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApiClientFactory is the signature of a API client factory
|
||||||
|
type ApiClientFactory func() (*api.Client, error)
|
||||||
|
|
||||||
// Client is used to initialize and return a new API client using
|
// Client is used to initialize and return a new API client using
|
||||||
// the default command line arguments and env vars.
|
// the default command line arguments and env vars.
|
||||||
func (m *Meta) Client() (*api.Client, error) {
|
func (m *Meta) Client() (*api.Client, error) {
|
||||||
config := api.DefaultConfig()
|
config := api.DefaultConfig()
|
||||||
if v := os.Getenv(EnvNomadAddress); v != "" {
|
|
||||||
config.Address = v
|
|
||||||
}
|
|
||||||
if m.flagAddress != "" {
|
if m.flagAddress != "" {
|
||||||
config.Address = m.flagAddress
|
config.Address = m.flagAddress
|
||||||
}
|
}
|
||||||
if v := os.Getenv(EnvNomadRegion); v != "" {
|
|
||||||
config.Region = v
|
|
||||||
}
|
|
||||||
if m.region != "" {
|
if m.region != "" {
|
||||||
config.Region = m.region
|
config.Region = m.region
|
||||||
}
|
}
|
||||||
|
if m.namespace != "" {
|
||||||
|
config.Namespace = m.namespace
|
||||||
|
}
|
||||||
|
|
||||||
// If we need custom TLS configuration, then set it
|
// If we need custom TLS configuration, then set it
|
||||||
if m.caCert != "" || m.caPath != "" || m.clientCert != "" || m.clientKey != "" || m.insecure {
|
if m.caCert != "" || m.caPath != "" || m.clientCert != "" || m.clientKey != "" || m.insecure {
|
||||||
t := &api.TLSConfig{
|
t := &api.TLSConfig{
|
||||||
|
@ -165,6 +166,11 @@ func generalOptionsUsage() string {
|
||||||
Overrides the NOMAD_REGION environment variable if set.
|
Overrides the NOMAD_REGION environment variable if set.
|
||||||
Defaults to the Agent's local region.
|
Defaults to the Agent's local region.
|
||||||
|
|
||||||
|
-namespace=<namespace>
|
||||||
|
The target namespace for queries and actions bound to a namespace.
|
||||||
|
Overrides the NOMAD_NAMESPACE environment variable if set.
|
||||||
|
Defaults to the "default" namespace.
|
||||||
|
|
||||||
-no-color
|
-no-color
|
||||||
Disables colored command output.
|
Disables colored command output.
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ func TestMeta_FlagSet(t *testing.T) {
|
||||||
"address",
|
"address",
|
||||||
"no-color",
|
"no-color",
|
||||||
"region",
|
"region",
|
||||||
|
"namespace",
|
||||||
"ca-cert",
|
"ca-cert",
|
||||||
"ca-path",
|
"ca-path",
|
||||||
"client-cert",
|
"client-cert",
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/nomad/api/contexts"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NamespaceCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *NamespaceCommand) Help() string {
|
||||||
|
return "This command is accessed by using one of the subcommands below."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *NamespaceCommand) Synopsis() string {
|
||||||
|
return "Interact with namespaces"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *NamespaceCommand) Run(args []string) int {
|
||||||
|
return cli.RunResultHelp
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamespacePredictor returns a namespace predictor that can optionally filter
|
||||||
|
// specific namespaces
|
||||||
|
func NamespacePredictor(factory ApiClientFactory, filter map[string]struct{}) complete.Predictor {
|
||||||
|
return complete.PredictFunc(func(a complete.Args) []string {
|
||||||
|
client, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Namespaces, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter the returned namespaces. We assign the unfiltered slice to the
|
||||||
|
// filtered slice but with no elements. This causes the slices to share
|
||||||
|
// the underlying array and makes the filtering allocation free.
|
||||||
|
unfiltered := resp.Matches[contexts.Namespaces]
|
||||||
|
filtered := unfiltered[:0]
|
||||||
|
for _, ns := range unfiltered {
|
||||||
|
if _, ok := filter[ns]; !ok {
|
||||||
|
filtered = append(filtered, ns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/api"
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NamespaceApplyCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceApplyCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: nomad namespace apply [options]
|
||||||
|
|
||||||
|
Apply is used to create or update a namespace.
|
||||||
|
|
||||||
|
General Options:
|
||||||
|
|
||||||
|
` + generalOptionsUsage() + `
|
||||||
|
|
||||||
|
Apply Options:
|
||||||
|
|
||||||
|
-name
|
||||||
|
The name of the namespace.
|
||||||
|
|
||||||
|
-description
|
||||||
|
An optional description for the namespace.
|
||||||
|
`
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceApplyCommand) AutocompleteFlags() complete.Flags {
|
||||||
|
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
|
||||||
|
complete.Flags{
|
||||||
|
"-name": complete.PredictAnything,
|
||||||
|
"-description": complete.PredictAnything,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceApplyCommand) AutocompleteArgs() complete.Predictor {
|
||||||
|
return complete.PredictNothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceApplyCommand) Synopsis() string {
|
||||||
|
return "Create or update a namespace"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceApplyCommand) Run(args []string) int {
|
||||||
|
var name, description string
|
||||||
|
|
||||||
|
flags := c.Meta.FlagSet("namespace apply", FlagSetClient)
|
||||||
|
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||||
|
flags.StringVar(&name, "name", "", "")
|
||||||
|
flags.StringVar(&description, "description", "", "")
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we got no arguments
|
||||||
|
args = flags.Args()
|
||||||
|
if l := len(args); l != 0 {
|
||||||
|
c.Ui.Error(c.Help())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate we have at-least a name
|
||||||
|
if name == "" {
|
||||||
|
c.Ui.Error("Namespace name required")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the HTTP client
|
||||||
|
client, err := c.Meta.Client()
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the request object.
|
||||||
|
ns := &api.Namespace{
|
||||||
|
Name: name,
|
||||||
|
Description: description,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Namespaces().Register(ns, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error applying namespace: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// +build pro ent
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNamespaceApplyCommand_Implements(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var _ cli.Command = &NamespaceApplyCommand{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceApplyCommand_Fails(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
cmd := &NamespaceApplyCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// Fails on misuse
|
||||||
|
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
|
||||||
|
t.Fatalf("expected exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, cmd.Help()) {
|
||||||
|
t.Fatalf("expected help output, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.ErrorWriter.Reset()
|
||||||
|
|
||||||
|
if code := cmd.Run([]string{"-address=nope"}); code != 1 {
|
||||||
|
t.Fatalf("expected exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, "name required") {
|
||||||
|
t.Fatalf("name required error, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.ErrorWriter.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceApplyCommand_Good(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create a server
|
||||||
|
srv, client, url := testServer(t, true, nil)
|
||||||
|
defer srv.Shutdown()
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
cmd := &NamespaceApplyCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// Create a namespace
|
||||||
|
name, desc := "foo", "bar"
|
||||||
|
if code := cmd.Run([]string{"-address=" + url, "-name=" + name, "-description=" + desc}); code != 0 {
|
||||||
|
t.Fatalf("expected exit 0, got: %d; %v", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaces, _, err := client.Namespaces().List(nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Len(t, namespaces, 2)
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NamespaceDeleteCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceDeleteCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: nomad namespace delete [options] <namespace>
|
||||||
|
|
||||||
|
Delete is used to remove a namespace.
|
||||||
|
|
||||||
|
General Options:
|
||||||
|
|
||||||
|
` + generalOptionsUsage()
|
||||||
|
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceDeleteCommand) AutocompleteFlags() complete.Flags {
|
||||||
|
return c.Meta.AutocompleteFlags(FlagSetClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceDeleteCommand) AutocompleteArgs() complete.Predictor {
|
||||||
|
filter := map[string]struct{}{"default": struct{}{}}
|
||||||
|
return NamespacePredictor(c.Meta.Client, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceDeleteCommand) Synopsis() string {
|
||||||
|
return "Delete a namespace"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceDeleteCommand) Run(args []string) int {
|
||||||
|
flags := c.Meta.FlagSet("namespace delete", FlagSetClient)
|
||||||
|
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we got one argument
|
||||||
|
args = flags.Args()
|
||||||
|
if l := len(args); l != 1 {
|
||||||
|
c.Ui.Error(c.Help())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := args[0]
|
||||||
|
|
||||||
|
// Get the HTTP client
|
||||||
|
client, err := c.Meta.Client()
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Namespaces().Delete(namespace, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error deleting namespace: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// +build pro ent
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/api"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/posener/complete"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNamespaceDeleteCommand_Implements(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var _ cli.Command = &NamespaceDeleteCommand{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceDeleteCommand_Fails(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
cmd := &NamespaceDeleteCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// Fails on misuse
|
||||||
|
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
|
||||||
|
t.Fatalf("expected exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, cmd.Help()) {
|
||||||
|
t.Fatalf("expected help output, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.ErrorWriter.Reset()
|
||||||
|
|
||||||
|
if code := cmd.Run([]string{"-address=nope", "foo"}); code != 1 {
|
||||||
|
t.Fatalf("expected exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, "deleting namespace") {
|
||||||
|
t.Fatalf("connection error, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.ErrorWriter.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceDeleteCommand_Good(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create a server
|
||||||
|
srv, client, url := testServer(t, true, nil)
|
||||||
|
defer srv.Shutdown()
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
cmd := &NamespaceDeleteCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// Create a namespace to delete
|
||||||
|
ns := &api.Namespace{
|
||||||
|
Name: "foo",
|
||||||
|
}
|
||||||
|
_, err := client.Namespaces().Register(ns, nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// Delete a namespace
|
||||||
|
if code := cmd.Run([]string{"-address=" + url, ns.Name}); code != 0 {
|
||||||
|
t.Fatalf("expected exit 0, got: %d; %v", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaces, _, err := client.Namespaces().List(nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Len(t, namespaces, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceDeleteCommand_AutocompleteArgs(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
srv, client, url := testServer(t, true, nil)
|
||||||
|
defer srv.Shutdown()
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
cmd := &NamespaceDeleteCommand{Meta: Meta{Ui: ui, flagAddress: url}}
|
||||||
|
|
||||||
|
// Create a namespace other than default
|
||||||
|
ns := &api.Namespace{
|
||||||
|
Name: "diddo",
|
||||||
|
}
|
||||||
|
_, err := client.Namespaces().Register(ns, nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
args := complete.Args{Last: "d"}
|
||||||
|
predictor := cmd.AutocompleteArgs()
|
||||||
|
|
||||||
|
res := predictor.Predict(args)
|
||||||
|
assert.Equal(1, len(res))
|
||||||
|
assert.Equal(ns.Name, res[0])
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/api"
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NamespaceListCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceListCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: nomad namespace list [options]
|
||||||
|
|
||||||
|
List is used to list available namespaces.
|
||||||
|
|
||||||
|
General Options:
|
||||||
|
|
||||||
|
` + generalOptionsUsage() + `
|
||||||
|
|
||||||
|
List Options:
|
||||||
|
|
||||||
|
-json
|
||||||
|
Output the namespaces in a JSON format.
|
||||||
|
|
||||||
|
-t
|
||||||
|
Format and display the namespaces using a Go template.
|
||||||
|
`
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceListCommand) AutocompleteFlags() complete.Flags {
|
||||||
|
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
|
||||||
|
complete.Flags{
|
||||||
|
"-json": complete.PredictNothing,
|
||||||
|
"-t": complete.PredictAnything,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceListCommand) AutocompleteArgs() complete.Predictor {
|
||||||
|
return complete.PredictNothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceListCommand) Synopsis() string {
|
||||||
|
return "List namespaces"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NamespaceListCommand) Run(args []string) int {
|
||||||
|
var json bool
|
||||||
|
var tmpl string
|
||||||
|
|
||||||
|
flags := c.Meta.FlagSet("namespace list", FlagSetClient)
|
||||||
|
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||||
|
flags.BoolVar(&json, "json", false, "")
|
||||||
|
flags.StringVar(&tmpl, "t", "", "")
|
||||||
|
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we got no arguments
|
||||||
|
args = flags.Args()
|
||||||
|
if l := len(args); l != 0 {
|
||||||
|
c.Ui.Error(c.Help())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the HTTP client
|
||||||
|
client, err := c.Meta.Client()
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaces, _, err := client.Namespaces().List(nil)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error retrieving namespaces: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if json || len(tmpl) > 0 {
|
||||||
|
out, err := Format(json, tmpl, namespaces)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Ui.Output(out)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Ui.Output(formatNamespaces(namespaces))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatNamespaces(namespaces []*api.Namespace) string {
|
||||||
|
if len(namespaces) == 0 {
|
||||||
|
return "No namespaces found"
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := make([]string, len(namespaces)+1)
|
||||||
|
rows[0] = "Name|Description"
|
||||||
|
for i, ns := range namespaces {
|
||||||
|
rows[i+1] = fmt.Sprintf("%s|%s",
|
||||||
|
ns.Name,
|
||||||
|
ns.Description)
|
||||||
|
}
|
||||||
|
return formatList(rows)
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// +build pro ent
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNamespaceListCommand_Implements(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var _ cli.Command = &NamespaceListCommand{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceListCommand_Fails(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
cmd := &NamespaceListCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// Fails on misuse
|
||||||
|
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
|
||||||
|
t.Fatalf("expected exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, cmd.Help()) {
|
||||||
|
t.Fatalf("expected help output, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.ErrorWriter.Reset()
|
||||||
|
|
||||||
|
if code := cmd.Run([]string{"-address=nope"}); code != 1 {
|
||||||
|
t.Fatalf("expected exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error retrieving namespaces") {
|
||||||
|
t.Fatalf("expected failed query error, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.ErrorWriter.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceListCommand_List(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create a server
|
||||||
|
srv, _, url := testServer(t, true, nil)
|
||||||
|
defer srv.Shutdown()
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
cmd := &NamespaceListCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// List should contain default deployment
|
||||||
|
if code := cmd.Run([]string{"-address=" + url}); code != 0 {
|
||||||
|
t.Fatalf("expected exit 0, got: %d; %v", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
out := ui.OutputWriter.String()
|
||||||
|
if !strings.Contains(out, "default") || !strings.Contains(out, "Default shared namespace") {
|
||||||
|
t.Fatalf("expected default namespace, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.OutputWriter.Reset()
|
||||||
|
|
||||||
|
// List json
|
||||||
|
t.Log(url)
|
||||||
|
if code := cmd.Run([]string{"-address=" + url, "-json"}); code != 0 {
|
||||||
|
t.Fatalf("expected exit 0, got: %d; %v", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
out = ui.OutputWriter.String()
|
||||||
|
if !strings.Contains(out, "CreateIndex") {
|
||||||
|
t.Fatalf("expected json output, got: %s", out)
|
||||||
|
}
|
||||||
|
ui.OutputWriter.Reset()
|
||||||
|
}
|
|
@ -129,6 +129,11 @@ func (c *PlanCommand) Run(args []string) int {
|
||||||
client.SetRegion(*r)
|
client.SetRegion(*r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force the namespace to be that of the job.
|
||||||
|
if n := job.Namespace; n != nil {
|
||||||
|
client.SetNamespace(*n)
|
||||||
|
}
|
||||||
|
|
||||||
// Submit the job
|
// Submit the job
|
||||||
resp, _, err := client.Jobs().Plan(job, diff, nil)
|
resp, _, err := client.Jobs().Plan(job, diff, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -167,6 +167,11 @@ func (c *RunCommand) Run(args []string) int {
|
||||||
client.SetRegion(*r)
|
client.SetRegion(*r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force the namespace to be that of the job.
|
||||||
|
if n := job.Namespace; n != nil {
|
||||||
|
client.SetNamespace(*n)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the job is periodic or is a parameterized job
|
// Check if the job is periodic or is a parameterized job
|
||||||
periodic := job.IsPeriodic()
|
periodic := job.IsPeriodic()
|
||||||
paramjob := job.IsParameterized()
|
paramjob := job.IsParameterized()
|
||||||
|
|
20
commands.go
20
commands.go
|
@ -163,6 +163,26 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
"namespace": func() (cli.Command, error) {
|
||||||
|
return &command.NamespaceCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
"namespace apply": func() (cli.Command, error) {
|
||||||
|
return &command.NamespaceApplyCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
"namespace delete": func() (cli.Command, error) {
|
||||||
|
return &command.NamespaceDeleteCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
"namespace list": func() (cli.Command, error) {
|
||||||
|
return &command.NamespaceListCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
"node-drain": func() (cli.Command, error) {
|
"node-drain": func() (cli.Command, error) {
|
||||||
return &command.NodeDrainCommand{
|
return &command.NodeDrainCommand{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
|
|
|
@ -136,6 +136,7 @@ func parseJob(result *api.Job, list *ast.ObjectList) error {
|
||||||
"id",
|
"id",
|
||||||
"meta",
|
"meta",
|
||||||
"name",
|
"name",
|
||||||
|
"namespace",
|
||||||
"periodic",
|
"periodic",
|
||||||
"priority",
|
"priority",
|
||||||
"region",
|
"region",
|
||||||
|
|
|
@ -31,6 +31,7 @@ func TestParse(t *testing.T) {
|
||||||
AllAtOnce: helper.BoolToPtr(true),
|
AllAtOnce: helper.BoolToPtr(true),
|
||||||
Datacenters: []string{"us2", "eu1"},
|
Datacenters: []string{"us2", "eu1"},
|
||||||
Region: helper.StringToPtr("fooregion"),
|
Region: helper.StringToPtr("fooregion"),
|
||||||
|
Namespace: helper.StringToPtr("foonamespace"),
|
||||||
VaultToken: helper.StringToPtr("foo"),
|
VaultToken: helper.StringToPtr("foo"),
|
||||||
|
|
||||||
Meta: map[string]string{
|
Meta: map[string]string{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
job "binstore-storagelocker" {
|
job "binstore-storagelocker" {
|
||||||
region = "fooregion"
|
region = "fooregion"
|
||||||
|
namespace = "foonamespace"
|
||||||
type = "batch"
|
type = "batch"
|
||||||
priority = 52
|
priority = 52
|
||||||
all_at_once = true
|
all_at_once = true
|
||||||
|
|
1
main.go
1
main.go
|
@ -35,6 +35,7 @@ func RunCustom(args []string, commands map[string]cli.CommandFactory) int {
|
||||||
case "executor":
|
case "executor":
|
||||||
case "fs ls", "fs cat", "fs stat":
|
case "fs ls", "fs cat", "fs stat":
|
||||||
case "job deployments", "job dispatch", "job history", "job promote", "job revert":
|
case "job deployments", "job dispatch", "job history", "job promote", "job revert":
|
||||||
|
case "namespace list", "namespace delete", "namespace apply":
|
||||||
case "operator raft", "operator raft list-peers", "operator raft remove-peer":
|
case "operator raft", "operator raft list-peers", "operator raft remove-peer":
|
||||||
case "syslog":
|
case "syslog":
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -30,9 +30,9 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
|
||||||
var err error
|
var err error
|
||||||
var iter memdb.ResultIterator
|
var iter memdb.ResultIterator
|
||||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||||
iter, err = state.AllocsByIDPrefix(ws, prefix)
|
iter, err = state.AllocsByIDPrefix(ws, args.RequestNamespace(), prefix)
|
||||||
} else {
|
} else {
|
||||||
iter, err = state.Allocs(ws)
|
iter, err = state.AllocsByNamespace(ws, args.RequestNamespace())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -32,7 +32,10 @@ func TestAllocEndpoint_List(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations
|
// Lookup the allocations
|
||||||
get := &structs.AllocListRequest{
|
get := &structs.AllocListRequest{
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.AllocListResponse
|
var resp structs.AllocListResponse
|
||||||
if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp); err != nil {
|
if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp); err != nil {
|
||||||
|
@ -51,7 +54,11 @@ func TestAllocEndpoint_List(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by prefix
|
// Lookup the allocations by prefix
|
||||||
get = &structs.AllocListRequest{
|
get = &structs.AllocListRequest{
|
||||||
QueryOptions: structs.QueryOptions{Region: "global", Prefix: alloc.ID[:4]},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
Prefix: alloc.ID[:4],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp2 structs.AllocListResponse
|
var resp2 structs.AllocListResponse
|
||||||
|
@ -95,6 +102,7 @@ func TestAllocEndpoint_List_Blocking(t *testing.T) {
|
||||||
req := &structs.AllocListRequest{
|
req := &structs.AllocListRequest{
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
MinQueryIndex: 1,
|
MinQueryIndex: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,8 @@ func (c *CoreScheduler) jobGC(eval *structs.Evaluation) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the allocations, evaluations and jobs to GC
|
// Collect the allocations, evaluations and jobs to GC
|
||||||
var gcAlloc, gcEval, gcJob []string
|
var gcAlloc, gcEval []string
|
||||||
|
var gcJob []*structs.Job
|
||||||
|
|
||||||
OUTER:
|
OUTER:
|
||||||
for i := iter.Next(); i != nil; i = iter.Next() {
|
for i := iter.Next(); i != nil; i = iter.Next() {
|
||||||
|
@ -107,7 +108,7 @@ OUTER:
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
evals, err := c.snap.EvalsByJob(ws, job.ID)
|
evals, err := c.snap.EvalsByJob(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.srv.logger.Printf("[ERR] sched.core: failed to get evals for job %s: %v", job.ID, err)
|
c.srv.logger.Printf("[ERR] sched.core: failed to get evals for job %s: %v", job.ID, err)
|
||||||
continue
|
continue
|
||||||
|
@ -132,7 +133,7 @@ OUTER:
|
||||||
|
|
||||||
// Job is eligible for garbage collection
|
// Job is eligible for garbage collection
|
||||||
if allEvalsGC {
|
if allEvalsGC {
|
||||||
gcJob = append(gcJob, job.ID)
|
gcJob = append(gcJob, job)
|
||||||
gcAlloc = append(gcAlloc, jobAlloc...)
|
gcAlloc = append(gcAlloc, jobAlloc...)
|
||||||
gcEval = append(gcEval, jobEval...)
|
gcEval = append(gcEval, jobEval...)
|
||||||
}
|
}
|
||||||
|
@ -153,10 +154,11 @@ OUTER:
|
||||||
// Call to the leader to deregister the jobs.
|
// Call to the leader to deregister the jobs.
|
||||||
for _, job := range gcJob {
|
for _, job := range gcJob {
|
||||||
req := structs.JobDeregisterRequest{
|
req := structs.JobDeregisterRequest{
|
||||||
JobID: job,
|
JobID: job.ID,
|
||||||
Purge: true,
|
Purge: true,
|
||||||
WriteRequest: structs.WriteRequest{
|
WriteRequest: structs.WriteRequest{
|
||||||
Region: c.srv.config.Region,
|
Region: c.srv.config.Region,
|
||||||
|
Namespace: job.Namespace,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
var resp structs.JobDeregisterResponse
|
var resp structs.JobDeregisterResponse
|
||||||
|
@ -244,7 +246,7 @@ func (c *CoreScheduler) gcEval(eval *structs.Evaluation, thresholdIndex uint64,
|
||||||
// allocations.
|
// allocations.
|
||||||
if eval.Type == structs.JobTypeBatch {
|
if eval.Type == structs.JobTypeBatch {
|
||||||
// Check if the job is running
|
// Check if the job is running
|
||||||
job, err := c.snap.JobByID(ws, eval.JobID)
|
job, err := c.snap.JobByID(ws, eval.Namespace, eval.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ func TestCoreScheduler_EvalGC_Batch(t *testing.T) {
|
||||||
t.Fatalf("bad: %v", outA2)
|
t.Fatalf("bad: %v", outA2)
|
||||||
}
|
}
|
||||||
|
|
||||||
outB, err := state.JobByID(ws, job.ID)
|
outB, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -698,7 +698,7 @@ func TestCoreScheduler_JobGC_OutstandingEvals(t *testing.T) {
|
||||||
|
|
||||||
// Should still exist
|
// Should still exist
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := state.JobByID(ws, job.ID)
|
out, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -744,7 +744,7 @@ func TestCoreScheduler_JobGC_OutstandingEvals(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should not still exist
|
// Should not still exist
|
||||||
out, err = state.JobByID(ws, job.ID)
|
out, err = state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -835,7 +835,7 @@ func TestCoreScheduler_JobGC_OutstandingAllocs(t *testing.T) {
|
||||||
|
|
||||||
// Should still exist
|
// Should still exist
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := state.JobByID(ws, job.ID)
|
out, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -881,7 +881,7 @@ func TestCoreScheduler_JobGC_OutstandingAllocs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should not still exist
|
// Should not still exist
|
||||||
out, err = state.JobByID(ws, job.ID)
|
out, err = state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -979,7 +979,7 @@ func TestCoreScheduler_JobGC_OneShot(t *testing.T) {
|
||||||
|
|
||||||
// Should still exist
|
// Should still exist
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := state.JobByID(ws, job.ID)
|
out, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1084,7 +1084,7 @@ func TestCoreScheduler_JobGC_Stopped(t *testing.T) {
|
||||||
|
|
||||||
// Shouldn't still exist
|
// Shouldn't still exist
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := state.JobByID(ws, job.ID)
|
out, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1161,7 +1161,7 @@ func TestCoreScheduler_JobGC_Force(t *testing.T) {
|
||||||
|
|
||||||
// Shouldn't still exist
|
// Shouldn't still exist
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := state.JobByID(ws, job.ID)
|
out, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1217,7 +1217,7 @@ func TestCoreScheduler_JobGC_Parameterized(t *testing.T) {
|
||||||
|
|
||||||
// Should still exist
|
// Should still exist
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := state.JobByID(ws, job.ID)
|
out, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1248,7 +1248,7 @@ func TestCoreScheduler_JobGC_Parameterized(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should not exist
|
// Should not exist
|
||||||
out, err = state.JobByID(ws, job.ID)
|
out, err = state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1292,7 +1292,7 @@ func TestCoreScheduler_JobGC_Periodic(t *testing.T) {
|
||||||
|
|
||||||
// Should still exist
|
// Should still exist
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := state.JobByID(ws, job.ID)
|
out, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1323,7 +1323,7 @@ func TestCoreScheduler_JobGC_Periodic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should not exist
|
// Should not exist
|
||||||
out, err = state.JobByID(ws, job.ID)
|
out, err = state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,9 +224,9 @@ func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.De
|
||||||
var err error
|
var err error
|
||||||
var iter memdb.ResultIterator
|
var iter memdb.ResultIterator
|
||||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||||
iter, err = state.DeploymentsByIDPrefix(ws, prefix)
|
iter, err = state.DeploymentsByIDPrefix(ws, args.RequestNamespace(), prefix)
|
||||||
} else {
|
} else {
|
||||||
iter, err = state.Deployments(ws)
|
iter, err = state.DeploymentsByNamespace(ws, args.RequestNamespace())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -33,7 +33,10 @@ func TestDeploymentEndpoint_GetDeployment(t *testing.T) {
|
||||||
// Lookup the deployments
|
// Lookup the deployments
|
||||||
get := &structs.DeploymentSpecificRequest{
|
get := &structs.DeploymentSpecificRequest{
|
||||||
DeploymentID: d.ID,
|
DeploymentID: d.ID,
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.SingleDeploymentResponse
|
var resp structs.SingleDeploymentResponse
|
||||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.GetDeployment", get, &resp), "RPC")
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.GetDeployment", get, &resp), "RPC")
|
||||||
|
@ -76,6 +79,7 @@ func TestDeploymentEndpoint_GetDeployment_Blocking(t *testing.T) {
|
||||||
DeploymentID: d2.ID,
|
DeploymentID: d2.ID,
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
MinQueryIndex: 150,
|
MinQueryIndex: 150,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -207,7 +211,7 @@ func TestDeploymentEndpoint_Fail_Rollback(t *testing.T) {
|
||||||
assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index")
|
assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index")
|
||||||
|
|
||||||
// Lookup the job
|
// Lookup the job
|
||||||
jout, err := state.JobByID(ws, j.ID)
|
jout, err := state.JobByID(ws, j.Namespace, j.ID)
|
||||||
assert.Nil(err, "JobByID")
|
assert.Nil(err, "JobByID")
|
||||||
assert.NotNil(jout, "job")
|
assert.NotNil(jout, "job")
|
||||||
assert.EqualValues(2, jout.Version, "reverted job version")
|
assert.EqualValues(2, jout.Version, "reverted job version")
|
||||||
|
@ -467,7 +471,7 @@ func TestDeploymentEndpoint_SetAllocHealth_Rollback(t *testing.T) {
|
||||||
assert.False(*aout.DeploymentStatus.Healthy, "alloc deployment healthy")
|
assert.False(*aout.DeploymentStatus.Healthy, "alloc deployment healthy")
|
||||||
|
|
||||||
// Lookup the job
|
// Lookup the job
|
||||||
jout, err := state.JobByID(ws, j.ID)
|
jout, err := state.JobByID(ws, j.Namespace, j.ID)
|
||||||
assert.Nil(err, "JobByID")
|
assert.Nil(err, "JobByID")
|
||||||
assert.NotNil(jout, "job")
|
assert.NotNil(jout, "job")
|
||||||
assert.EqualValues(2, jout.Version, "reverted job version")
|
assert.EqualValues(2, jout.Version, "reverted job version")
|
||||||
|
@ -492,7 +496,10 @@ func TestDeploymentEndpoint_List(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the deployments
|
// Lookup the deployments
|
||||||
get := &structs.DeploymentListRequest{
|
get := &structs.DeploymentListRequest{
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.DeploymentListResponse
|
var resp structs.DeploymentListResponse
|
||||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp), "RPC")
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp), "RPC")
|
||||||
|
@ -502,7 +509,11 @@ func TestDeploymentEndpoint_List(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the deploys by prefix
|
// Lookup the deploys by prefix
|
||||||
get = &structs.DeploymentListRequest{
|
get = &structs.DeploymentListRequest{
|
||||||
QueryOptions: structs.QueryOptions{Region: "global", Prefix: d.ID[:4]},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
Prefix: d.ID[:4],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp2 structs.DeploymentListResponse
|
var resp2 structs.DeploymentListResponse
|
||||||
|
@ -536,6 +547,7 @@ func TestDeploymentEndpoint_List_Blocking(t *testing.T) {
|
||||||
req := &structs.DeploymentListRequest{
|
req := &structs.DeploymentListRequest{
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
MinQueryIndex: 1,
|
MinQueryIndex: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -593,7 +605,10 @@ func TestDeploymentEndpoint_Allocations(t *testing.T) {
|
||||||
// Lookup the allocations
|
// Lookup the allocations
|
||||||
get := &structs.DeploymentSpecificRequest{
|
get := &structs.DeploymentSpecificRequest{
|
||||||
DeploymentID: d.ID,
|
DeploymentID: d.ID,
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.AllocListResponse
|
var resp structs.AllocListResponse
|
||||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", get, &resp), "RPC")
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", get, &resp), "RPC")
|
||||||
|
@ -632,6 +647,7 @@ func TestDeploymentEndpoint_Allocations_Blocking(t *testing.T) {
|
||||||
DeploymentID: d.ID,
|
DeploymentID: d.ID,
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
MinQueryIndex: 1,
|
MinQueryIndex: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,9 @@ func (w *deploymentWatcher) SetAllocHealth(
|
||||||
u = w.getDeploymentStatusUpdate(structs.DeploymentStatusFailed, desc)
|
u = w.getDeploymentStatusUpdate(structs.DeploymentStatusFailed, desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Canonicalize the job in case it doesn't have namespace set
|
||||||
|
j.Canonicalize()
|
||||||
|
|
||||||
// Create the request
|
// Create the request
|
||||||
areq := &structs.ApplyDeploymentAllocHealthRequest{
|
areq := &structs.ApplyDeploymentAllocHealthRequest{
|
||||||
DeploymentAllocHealthRequest: *req,
|
DeploymentAllocHealthRequest: *req,
|
||||||
|
@ -396,7 +399,7 @@ func (w *deploymentWatcher) latestStableJob() (*structs.Job, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
versions, err := snap.JobVersionsByID(nil, w.d.JobID)
|
versions, err := snap.JobVersionsByID(nil, w.d.Namespace, w.d.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -443,6 +446,7 @@ func (w *deploymentWatcher) createEvalBatched(forIndex uint64) {
|
||||||
func (w *deploymentWatcher) getEval() *structs.Evaluation {
|
func (w *deploymentWatcher) getEval() *structs.Evaluation {
|
||||||
return &structs.Evaluation{
|
return &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: w.j.Namespace,
|
||||||
Priority: w.j.Priority,
|
Priority: w.j.Priority,
|
||||||
Type: w.j.Type,
|
Type: w.j.Type,
|
||||||
TriggeredBy: structs.EvalTriggerDeploymentWatcher,
|
TriggeredBy: structs.EvalTriggerDeploymentWatcher,
|
||||||
|
@ -514,7 +518,7 @@ func (w *deploymentWatcher) latestEvalIndex() (uint64, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
evals, err := snap.EvalsByJob(nil, w.d.JobID)
|
evals, err := snap.EvalsByJob(nil, w.d.Namespace, w.d.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,7 +241,7 @@ func (w *Watcher) addLocked(d *structs.Deployment) (*deploymentWatcher, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
job, err := snap.JobByID(nil, d.JobID)
|
job, err := snap.JobByID(nil, d.Namespace, d.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -664,7 +664,7 @@ func TestDeploymentWatcher_Watch(t *testing.T) {
|
||||||
// Wait for there to be one eval
|
// Wait for there to be one eval
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
evals, err := m.state.EvalsByJob(ws, j.ID)
|
evals, err := m.state.EvalsByJob(ws, j.Namespace, j.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -702,7 +702,7 @@ func TestDeploymentWatcher_Watch(t *testing.T) {
|
||||||
// Wait for there to be one eval
|
// Wait for there to be one eval
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
evals, err := m.state.EvalsByJob(ws, j.ID)
|
evals, err := m.state.EvalsByJob(ws, j.Namespace, j.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -789,12 +789,12 @@ func TestWatcher_BatchEvals(t *testing.T) {
|
||||||
// Wait for there to be one eval for each job
|
// Wait for there to be one eval for each job
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
evals1, err := m.state.EvalsByJob(ws, j1.ID)
|
evals1, err := m.state.EvalsByJob(ws, j1.Namespace, j1.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
evals2, err := m.state.EvalsByJob(ws, j2.ID)
|
evals2, err := m.state.EvalsByJob(ws, j2.Namespace, j2.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// +build !pro,!ent
|
||||||
|
|
||||||
|
package nomad
|
||||||
|
|
||||||
|
// EnterpriseEndpoints holds the set of enterprise only endpoints to register
|
||||||
|
type EnterpriseEndpoints struct{}
|
||||||
|
|
||||||
|
// NewEnterpriseEndpoints returns a stub of the enterprise endpoints since there
|
||||||
|
// are none in oss
|
||||||
|
func NewEnterpriseEndpoints(s *Server) *EnterpriseEndpoints {
|
||||||
|
return &EnterpriseEndpoints{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register is a no-op in oss.
|
||||||
|
func (e *EnterpriseEndpoints) Register(s *Server) {}
|
|
@ -52,8 +52,8 @@ type EvalBroker struct {
|
||||||
// and is used to eventually fail an evaluation.
|
// and is used to eventually fail an evaluation.
|
||||||
evals map[string]int
|
evals map[string]int
|
||||||
|
|
||||||
// jobEvals tracks queued evaluations by JobID to serialize them
|
// jobEvals tracks queued evaluations by a job's ID and namespace to serialize them
|
||||||
jobEvals map[string]string
|
jobEvals map[structs.NamespacedID]string
|
||||||
|
|
||||||
// blocked tracks the blocked evaluations by JobID in a priority queue
|
// blocked tracks the blocked evaluations by JobID in a priority queue
|
||||||
blocked map[string]PendingEvaluations
|
blocked map[string]PendingEvaluations
|
||||||
|
@ -117,7 +117,7 @@ func NewEvalBroker(timeout, initialNackDelay, subsequentNackDelay time.Duration,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
stats: new(BrokerStats),
|
stats: new(BrokerStats),
|
||||||
evals: make(map[string]int),
|
evals: make(map[string]int),
|
||||||
jobEvals: make(map[string]string),
|
jobEvals: make(map[structs.NamespacedID]string),
|
||||||
blocked: make(map[string]PendingEvaluations),
|
blocked: make(map[string]PendingEvaluations),
|
||||||
ready: make(map[string]PendingEvaluations),
|
ready: make(map[string]PendingEvaluations),
|
||||||
unack: make(map[string]*unackEval),
|
unack: make(map[string]*unackEval),
|
||||||
|
@ -235,9 +235,13 @@ func (b *EvalBroker) enqueueLocked(eval *structs.Evaluation, queue string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there is an evaluation for this JobID pending
|
// Check if there is an evaluation for this JobID pending
|
||||||
pendingEval := b.jobEvals[eval.JobID]
|
tuple := structs.NamespacedID{
|
||||||
|
ID: eval.JobID,
|
||||||
|
Namespace: eval.Namespace,
|
||||||
|
}
|
||||||
|
pendingEval := b.jobEvals[tuple]
|
||||||
if pendingEval == "" {
|
if pendingEval == "" {
|
||||||
b.jobEvals[eval.JobID] = eval.ID
|
b.jobEvals[tuple] = eval.ID
|
||||||
} else if pendingEval != eval.ID {
|
} else if pendingEval != eval.ID {
|
||||||
blocked := b.blocked[eval.JobID]
|
blocked := b.blocked[eval.JobID]
|
||||||
heap.Push(&blocked, eval)
|
heap.Push(&blocked, eval)
|
||||||
|
@ -513,7 +517,12 @@ func (b *EvalBroker) Ack(evalID, token string) error {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
delete(b.unack, evalID)
|
delete(b.unack, evalID)
|
||||||
delete(b.evals, evalID)
|
delete(b.evals, evalID)
|
||||||
delete(b.jobEvals, jobID)
|
|
||||||
|
tuple := structs.NamespacedID{
|
||||||
|
ID: jobID,
|
||||||
|
Namespace: unack.Eval.Namespace,
|
||||||
|
}
|
||||||
|
delete(b.jobEvals, tuple)
|
||||||
|
|
||||||
// Check if there are any blocked evaluations
|
// Check if there are any blocked evaluations
|
||||||
if blocked := b.blocked[jobID]; len(blocked) != 0 {
|
if blocked := b.blocked[jobID]; len(blocked) != 0 {
|
||||||
|
@ -660,7 +669,7 @@ func (b *EvalBroker) Flush() {
|
||||||
b.stats.TotalWaiting = 0
|
b.stats.TotalWaiting = 0
|
||||||
b.stats.ByScheduler = make(map[string]*SchedulerStats)
|
b.stats.ByScheduler = make(map[string]*SchedulerStats)
|
||||||
b.evals = make(map[string]int)
|
b.evals = make(map[string]int)
|
||||||
b.jobEvals = make(map[string]string)
|
b.jobEvals = make(map[structs.NamespacedID]string)
|
||||||
b.blocked = make(map[string]PendingEvaluations)
|
b.blocked = make(map[string]PendingEvaluations)
|
||||||
b.ready = make(map[string]PendingEvaluations)
|
b.ready = make(map[string]PendingEvaluations)
|
||||||
b.unack = make(map[string]*unackEval)
|
b.unack = make(map[string]*unackEval)
|
||||||
|
|
|
@ -387,24 +387,41 @@ func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
|
||||||
b := testBroker(t, 0)
|
b := testBroker(t, 0)
|
||||||
b.SetEnabled(true)
|
b.SetEnabled(true)
|
||||||
|
|
||||||
|
ns1 := "namespace-one"
|
||||||
|
ns2 := "namespace-two"
|
||||||
eval := mock.Eval()
|
eval := mock.Eval()
|
||||||
|
eval.Namespace = ns1
|
||||||
b.Enqueue(eval)
|
b.Enqueue(eval)
|
||||||
|
|
||||||
eval2 := mock.Eval()
|
eval2 := mock.Eval()
|
||||||
eval2.JobID = eval.JobID
|
eval2.JobID = eval.JobID
|
||||||
|
eval2.Namespace = ns1
|
||||||
eval2.CreateIndex = eval.CreateIndex + 1
|
eval2.CreateIndex = eval.CreateIndex + 1
|
||||||
b.Enqueue(eval2)
|
b.Enqueue(eval2)
|
||||||
|
|
||||||
eval3 := mock.Eval()
|
eval3 := mock.Eval()
|
||||||
eval3.JobID = eval.JobID
|
eval3.JobID = eval.JobID
|
||||||
|
eval3.Namespace = ns1
|
||||||
eval3.CreateIndex = eval.CreateIndex + 2
|
eval3.CreateIndex = eval.CreateIndex + 2
|
||||||
b.Enqueue(eval3)
|
b.Enqueue(eval3)
|
||||||
|
|
||||||
|
eval4 := mock.Eval()
|
||||||
|
eval4.JobID = eval.JobID
|
||||||
|
eval4.Namespace = ns2
|
||||||
|
eval4.CreateIndex = eval.CreateIndex + 3
|
||||||
|
b.Enqueue(eval4)
|
||||||
|
|
||||||
|
eval5 := mock.Eval()
|
||||||
|
eval5.JobID = eval.JobID
|
||||||
|
eval5.Namespace = ns2
|
||||||
|
eval5.CreateIndex = eval.CreateIndex + 4
|
||||||
|
b.Enqueue(eval5)
|
||||||
|
|
||||||
stats := b.Stats()
|
stats := b.Stats()
|
||||||
if stats.TotalReady != 1 {
|
if stats.TotalReady != 2 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
if stats.TotalBlocked != 2 {
|
if stats.TotalBlocked != 3 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,13 +436,13 @@ func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
|
||||||
|
|
||||||
// Check the stats
|
// Check the stats
|
||||||
stats = b.Stats()
|
stats = b.Stats()
|
||||||
if stats.TotalReady != 0 {
|
if stats.TotalReady != 1 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
if stats.TotalUnacked != 1 {
|
if stats.TotalUnacked != 1 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
if stats.TotalBlocked != 2 {
|
if stats.TotalBlocked != 3 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,13 +454,13 @@ func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
|
||||||
|
|
||||||
// Check the stats
|
// Check the stats
|
||||||
stats = b.Stats()
|
stats = b.Stats()
|
||||||
if stats.TotalReady != 1 {
|
if stats.TotalReady != 2 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
if stats.TotalUnacked != 0 {
|
if stats.TotalUnacked != 0 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
if stats.TotalBlocked != 1 {
|
if stats.TotalBlocked != 2 {
|
||||||
t.Fatalf("bad: %#v", stats)
|
t.Fatalf("bad: %#v", stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,6 +473,84 @@ func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
|
||||||
t.Fatalf("bad : %#v", out)
|
t.Fatalf("bad : %#v", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the stats
|
||||||
|
stats = b.Stats()
|
||||||
|
if stats.TotalReady != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalUnacked != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalBlocked != 2 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ack out
|
||||||
|
err = b.Ack(eval2.ID, token)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the stats
|
||||||
|
stats = b.Stats()
|
||||||
|
if stats.TotalReady != 2 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalUnacked != 0 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalBlocked != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequeue should work
|
||||||
|
out, token, err = b.Dequeue(defaultSched, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if out != eval3 {
|
||||||
|
t.Fatalf("bad : %#v", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the stats
|
||||||
|
stats = b.Stats()
|
||||||
|
if stats.TotalReady != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalUnacked != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalBlocked != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ack out
|
||||||
|
err = b.Ack(eval3.ID, token)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the stats
|
||||||
|
stats = b.Stats()
|
||||||
|
if stats.TotalReady != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalUnacked != 0 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
if stats.TotalBlocked != 1 {
|
||||||
|
t.Fatalf("bad: %#v", stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequeue should work
|
||||||
|
out, token, err = b.Dequeue(defaultSched, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if out != eval4 {
|
||||||
|
t.Fatalf("bad : %#v", out)
|
||||||
|
}
|
||||||
|
|
||||||
// Check the stats
|
// Check the stats
|
||||||
stats = b.Stats()
|
stats = b.Stats()
|
||||||
if stats.TotalReady != 0 {
|
if stats.TotalReady != 0 {
|
||||||
|
@ -469,7 +564,7 @@ func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ack out
|
// Ack out
|
||||||
err = b.Ack(eval2.ID, token)
|
err = b.Ack(eval4.ID, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -491,7 +586,7 @@ func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if out != eval3 {
|
if out != eval5 {
|
||||||
t.Fatalf("bad : %#v", out)
|
t.Fatalf("bad : %#v", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,7 +603,7 @@ func TestEvalBroker_Serialize_DuplicateJobID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ack out
|
// Ack out
|
||||||
err = b.Ack(eval3.ID, token)
|
err = b.Ack(eval5.ID, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,9 +284,9 @@ func (e *Eval) List(args *structs.EvalListRequest,
|
||||||
var err error
|
var err error
|
||||||
var iter memdb.ResultIterator
|
var iter memdb.ResultIterator
|
||||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||||
iter, err = state.EvalsByIDPrefix(ws, prefix)
|
iter, err = state.EvalsByIDPrefix(ws, args.RequestNamespace(), prefix)
|
||||||
} else {
|
} else {
|
||||||
iter, err = state.Evals(ws)
|
iter, err = state.EvalsByNamespace(ws, args.RequestNamespace())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -442,7 +442,10 @@ func TestEvalEndpoint_List(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the eval
|
// Lookup the eval
|
||||||
get := &structs.EvalListRequest{
|
get := &structs.EvalListRequest{
|
||||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp structs.EvalListResponse
|
var resp structs.EvalListResponse
|
||||||
if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp); err != nil {
|
if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp); err != nil {
|
||||||
|
@ -458,7 +461,11 @@ func TestEvalEndpoint_List(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the eval by prefix
|
// Lookup the eval by prefix
|
||||||
get = &structs.EvalListRequest{
|
get = &structs.EvalListRequest{
|
||||||
QueryOptions: structs.QueryOptions{Region: "global", Prefix: "aaaabb"},
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
Prefix: "aaaabb",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var resp2 structs.EvalListResponse
|
var resp2 structs.EvalListResponse
|
||||||
if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp2); err != nil {
|
if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp2); err != nil {
|
||||||
|
@ -495,6 +502,7 @@ func TestEvalEndpoint_List_Blocking(t *testing.T) {
|
||||||
req := &structs.EvalListRequest{
|
req := &structs.EvalListRequest{
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
MinQueryIndex: 1,
|
MinQueryIndex: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
149
nomad/fsm.go
149
nomad/fsm.go
|
@ -45,6 +45,20 @@ const (
|
||||||
ACLTokenSnapshot
|
ACLTokenSnapshot
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LogApplier is the definition of a function that can apply a Raft log
|
||||||
|
type LogApplier func(buf []byte, index uint64) interface{}
|
||||||
|
|
||||||
|
// LogAppliers is a mapping of the Raft MessageType to the appropriate log
|
||||||
|
// applier
|
||||||
|
type LogAppliers map[structs.MessageType]LogApplier
|
||||||
|
|
||||||
|
// SnapshotRestorer is the definition of a function that can apply a Raft log
|
||||||
|
type SnapshotRestorer func(restore *state.StateRestore, dec *codec.Decoder) error
|
||||||
|
|
||||||
|
// SnapshotRestorers is a mapping of the SnapshotType to the appropriate
|
||||||
|
// snapshot restorer.
|
||||||
|
type SnapshotRestorers map[SnapshotType]SnapshotRestorer
|
||||||
|
|
||||||
// nomadFSM implements a finite state machine that is used
|
// nomadFSM implements a finite state machine that is used
|
||||||
// along with Raft to provide strong consistency. We implement
|
// along with Raft to provide strong consistency. We implement
|
||||||
// this outside the Server to avoid exposing this outside the package.
|
// this outside the Server to avoid exposing this outside the package.
|
||||||
|
@ -57,6 +71,12 @@ type nomadFSM struct {
|
||||||
state *state.StateStore
|
state *state.StateStore
|
||||||
timetable *TimeTable
|
timetable *TimeTable
|
||||||
|
|
||||||
|
// enterpriseAppliers holds the set of enterprise only LogAppliers
|
||||||
|
enterpriseAppliers LogAppliers
|
||||||
|
|
||||||
|
// enterpriseRestorers holds the set of enterprise only snapshot restorers
|
||||||
|
enterpriseRestorers SnapshotRestorers
|
||||||
|
|
||||||
// stateLock is only used to protect outside callers to State() from
|
// stateLock is only used to protect outside callers to State() from
|
||||||
// racing with Restore(), which is called by Raft (it puts in a totally
|
// racing with Restore(), which is called by Raft (it puts in a totally
|
||||||
// new state store). Everything internal here is synchronized by the
|
// new state store). Everything internal here is synchronized by the
|
||||||
|
@ -86,14 +106,23 @@ func NewFSM(evalBroker *EvalBroker, periodic *PeriodicDispatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
fsm := &nomadFSM{
|
fsm := &nomadFSM{
|
||||||
evalBroker: evalBroker,
|
evalBroker: evalBroker,
|
||||||
periodicDispatcher: periodic,
|
periodicDispatcher: periodic,
|
||||||
blockedEvals: blocked,
|
blockedEvals: blocked,
|
||||||
logOutput: logOutput,
|
logOutput: logOutput,
|
||||||
logger: log.New(logOutput, "", log.LstdFlags),
|
logger: log.New(logOutput, "", log.LstdFlags),
|
||||||
state: state,
|
state: state,
|
||||||
timetable: NewTimeTable(timeTableGranularity, timeTableLimit),
|
timetable: NewTimeTable(timeTableGranularity, timeTableLimit),
|
||||||
|
enterpriseAppliers: make(map[structs.MessageType]LogApplier, 8),
|
||||||
|
enterpriseRestorers: make(map[SnapshotType]SnapshotRestorer, 8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register all the log applier functions
|
||||||
|
fsm.registerLogAppliers()
|
||||||
|
|
||||||
|
// Register all the snapshot restorer functions
|
||||||
|
fsm.registerSnapshotRestorers()
|
||||||
|
|
||||||
return fsm, nil
|
return fsm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,14 +208,20 @@ func (n *nomadFSM) Apply(log *raft.Log) interface{} {
|
||||||
return n.applyACLTokenDelete(buf[1:], log.Index)
|
return n.applyACLTokenDelete(buf[1:], log.Index)
|
||||||
case structs.ACLTokenBootstrapRequestType:
|
case structs.ACLTokenBootstrapRequestType:
|
||||||
return n.applyACLTokenBootstrap(buf[1:], log.Index)
|
return n.applyACLTokenBootstrap(buf[1:], log.Index)
|
||||||
default:
|
|
||||||
if ignoreUnknown {
|
|
||||||
n.logger.Printf("[WARN] nomad.fsm: ignoring unknown message type (%d), upgrade to newer version", msgType)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
panic(fmt.Errorf("failed to apply request: %#v", buf))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check enterprise only message types.
|
||||||
|
if applier, ok := n.enterpriseAppliers[msgType]; ok {
|
||||||
|
return applier(buf[1:], log.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We didn't match anything, either panic or ignore
|
||||||
|
if ignoreUnknown {
|
||||||
|
n.logger.Printf("[WARN] nomad.fsm: ignoring unknown message type (%d), upgrade to newer version", msgType)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Errorf("failed to apply request: %#v", buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *nomadFSM) applyUpsertNode(buf []byte, index uint64) interface{} {
|
func (n *nomadFSM) applyUpsertNode(buf []byte, index uint64) interface{} {
|
||||||
|
@ -304,7 +339,7 @@ func (n *nomadFSM) applyUpsertJob(buf []byte, index uint64) interface{} {
|
||||||
// job was not launched. In this case, we use the insertion time to
|
// job was not launched. In this case, we use the insertion time to
|
||||||
// determine if a launch was missed.
|
// determine if a launch was missed.
|
||||||
if req.Job.IsPeriodic() {
|
if req.Job.IsPeriodic() {
|
||||||
prevLaunch, err := n.state.PeriodicLaunchByID(ws, req.Job.ID)
|
prevLaunch, err := n.state.PeriodicLaunchByID(ws, req.Namespace, req.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: PeriodicLaunchByID failed: %v", err)
|
n.logger.Printf("[ERR] nomad.fsm: PeriodicLaunchByID failed: %v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -313,7 +348,11 @@ func (n *nomadFSM) applyUpsertJob(buf []byte, index uint64) interface{} {
|
||||||
// Record the insertion time as a launch. We overload the launch table
|
// Record the insertion time as a launch. We overload the launch table
|
||||||
// such that the first entry is the insertion time.
|
// such that the first entry is the insertion time.
|
||||||
if prevLaunch == nil {
|
if prevLaunch == nil {
|
||||||
launch := &structs.PeriodicLaunch{ID: req.Job.ID, Launch: time.Now()}
|
launch := &structs.PeriodicLaunch{
|
||||||
|
ID: req.Job.ID,
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Launch: time.Now(),
|
||||||
|
}
|
||||||
if err := n.state.UpsertPeriodicLaunch(index, launch); err != nil {
|
if err := n.state.UpsertPeriodicLaunch(index, launch); err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: UpsertPeriodicLaunch failed: %v", err)
|
n.logger.Printf("[ERR] nomad.fsm: UpsertPeriodicLaunch failed: %v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -324,7 +363,7 @@ func (n *nomadFSM) applyUpsertJob(buf []byte, index uint64) interface{} {
|
||||||
// Check if the parent job is periodic and mark the launch time.
|
// Check if the parent job is periodic and mark the launch time.
|
||||||
parentID := req.Job.ParentID
|
parentID := req.Job.ParentID
|
||||||
if parentID != "" {
|
if parentID != "" {
|
||||||
parent, err := n.state.JobByID(ws, parentID)
|
parent, err := n.state.JobByID(ws, req.Namespace, parentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: JobByID(%v) lookup for parent failed: %v", parentID, err)
|
n.logger.Printf("[ERR] nomad.fsm: JobByID(%v) lookup for parent failed: %v", parentID, err)
|
||||||
return err
|
return err
|
||||||
|
@ -340,7 +379,11 @@ func (n *nomadFSM) applyUpsertJob(buf []byte, index uint64) interface{} {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
launch := &structs.PeriodicLaunch{ID: parentID, Launch: t}
|
launch := &structs.PeriodicLaunch{
|
||||||
|
ID: parentID,
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Launch: t,
|
||||||
|
}
|
||||||
if err := n.state.UpsertPeriodicLaunch(index, launch); err != nil {
|
if err := n.state.UpsertPeriodicLaunch(index, launch); err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: UpsertPeriodicLaunch failed: %v", err)
|
n.logger.Printf("[ERR] nomad.fsm: UpsertPeriodicLaunch failed: %v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -359,13 +402,13 @@ func (n *nomadFSM) applyDeregisterJob(buf []byte, index uint64) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is periodic remove it from the dispatcher
|
// If it is periodic remove it from the dispatcher
|
||||||
if err := n.periodicDispatcher.Remove(req.JobID); err != nil {
|
if err := n.periodicDispatcher.Remove(req.Namespace, req.JobID); err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: periodicDispatcher.Remove failed: %v", err)
|
n.logger.Printf("[ERR] nomad.fsm: periodicDispatcher.Remove failed: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Purge {
|
if req.Purge {
|
||||||
if err := n.state.DeleteJob(index, req.JobID); err != nil {
|
if err := n.state.DeleteJob(index, req.Namespace, req.JobID); err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: DeleteJob failed: %v", err)
|
n.logger.Printf("[ERR] nomad.fsm: DeleteJob failed: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -373,18 +416,18 @@ func (n *nomadFSM) applyDeregisterJob(buf []byte, index uint64) interface{} {
|
||||||
// We always delete from the periodic launch table because it is possible that
|
// We always delete from the periodic launch table because it is possible that
|
||||||
// the job was updated to be non-perioidic, thus checking if it is periodic
|
// the job was updated to be non-perioidic, thus checking if it is periodic
|
||||||
// doesn't ensure we clean it up properly.
|
// doesn't ensure we clean it up properly.
|
||||||
n.state.DeletePeriodicLaunch(index, req.JobID)
|
n.state.DeletePeriodicLaunch(index, req.Namespace, req.JobID)
|
||||||
} else {
|
} else {
|
||||||
// Get the current job and mark it as stopped and re-insert it.
|
// Get the current job and mark it as stopped and re-insert it.
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
current, err := n.state.JobByID(ws, req.JobID)
|
current, err := n.state.JobByID(ws, req.Namespace, req.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: JobByID lookup failed: %v", err)
|
n.logger.Printf("[ERR] nomad.fsm: JobByID lookup failed: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if current == nil {
|
if current == nil {
|
||||||
return fmt.Errorf("job %q doesn't exist to be deregistered", req.JobID)
|
return fmt.Errorf("job %q in namespace %q doesn't exist to be deregistered", req.JobID, req.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopped := current.Copy()
|
stopped := current.Copy()
|
||||||
|
@ -673,7 +716,7 @@ func (n *nomadFSM) applyJobStability(buf []byte, index uint64) interface{} {
|
||||||
panic(fmt.Errorf("failed to decode request: %v", err))
|
panic(fmt.Errorf("failed to decode request: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := n.state.UpdateJobStability(index, req.JobID, req.JobVersion, req.Stable); err != nil {
|
if err := n.state.UpdateJobStability(index, req.Namespace, req.JobID, req.JobVersion, req.Stable); err != nil {
|
||||||
n.logger.Printf("[ERR] nomad.fsm: UpdateJobStability failed: %v", err)
|
n.logger.Printf("[ERR] nomad.fsm: UpdateJobStability failed: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -807,7 +850,8 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode
|
// Decode
|
||||||
switch SnapshotType(msgType[0]) {
|
snapType := SnapshotType(msgType[0])
|
||||||
|
switch snapType {
|
||||||
case TimeTableSnapshot:
|
case TimeTableSnapshot:
|
||||||
if err := n.timetable.Deserialize(dec); err != nil {
|
if err := n.timetable.Deserialize(dec); err != nil {
|
||||||
return fmt.Errorf("time table deserialize failed: %v", err)
|
return fmt.Errorf("time table deserialize failed: %v", err)
|
||||||
|
@ -846,6 +890,12 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
if err := dec.Decode(eval); err != nil {
|
if err := dec.Decode(eval); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COMPAT: Handle upgrade to v0.7.0
|
||||||
|
if eval.Namespace == "" {
|
||||||
|
eval.Namespace = structs.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if err := restore.EvalRestore(eval); err != nil {
|
if err := restore.EvalRestore(eval); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -855,6 +905,12 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
if err := dec.Decode(alloc); err != nil {
|
if err := dec.Decode(alloc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COMPAT: Handle upgrade to v0.7.0
|
||||||
|
if alloc.Namespace == "" {
|
||||||
|
alloc.Namespace = structs.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if err := restore.AllocRestore(alloc); err != nil {
|
if err := restore.AllocRestore(alloc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -873,6 +929,12 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
if err := dec.Decode(launch); err != nil {
|
if err := dec.Decode(launch); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COMPAT: Handle upgrade to v0.7.0
|
||||||
|
if launch.Namespace == "" {
|
||||||
|
launch.Namespace = structs.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if err := restore.PeriodicLaunchRestore(launch); err != nil {
|
if err := restore.PeriodicLaunchRestore(launch); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -882,6 +944,12 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
if err := dec.Decode(summary); err != nil {
|
if err := dec.Decode(summary); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COMPAT: Handle upgrade to v0.7.0
|
||||||
|
if summary.Namespace == "" {
|
||||||
|
summary.Namespace = structs.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if err := restore.JobSummaryRestore(summary); err != nil {
|
if err := restore.JobSummaryRestore(summary); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -900,6 +968,12 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
if err := dec.Decode(version); err != nil {
|
if err := dec.Decode(version); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COMPAT: Handle upgrade to v0.7.0
|
||||||
|
if version.Namespace == "" {
|
||||||
|
version.Namespace = structs.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if err := restore.JobVersionRestore(version); err != nil {
|
if err := restore.JobVersionRestore(version); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -909,6 +983,12 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
if err := dec.Decode(deployment); err != nil {
|
if err := dec.Decode(deployment); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COMPAT: Handle upgrade to v0.7.0
|
||||||
|
if deployment.Namespace == "" {
|
||||||
|
deployment.Namespace = structs.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if err := restore.DeploymentRestore(deployment); err != nil {
|
if err := restore.DeploymentRestore(deployment); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -932,7 +1012,16 @@ func (n *nomadFSM) Restore(old io.ReadCloser) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unrecognized snapshot type: %v", msgType)
|
// Check if this is an enterprise only object being restored
|
||||||
|
restorer, ok := n.enterpriseRestorers[snapType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Unrecognized snapshot type: %v", msgType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the enterprise only object
|
||||||
|
if err := restorer(restore, dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1006,6 +1095,7 @@ func (n *nomadFSM) reconcileQueuedAllocations(index uint64) error {
|
||||||
// Create an eval and mark it as requiring annotations and insert that as well
|
// Create an eval and mark it as requiring annotations and insert that as well
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: job.Namespace,
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
Type: job.Type,
|
Type: job.Type,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1026,7 +1116,7 @@ func (n *nomadFSM) reconcileQueuedAllocations(index uint64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the job summary from the fsm state store
|
// Get the job summary from the fsm state store
|
||||||
originalSummary, err := n.state.JobSummaryByID(ws, job.ID)
|
originalSummary, err := n.state.JobSummaryByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1145,6 +1235,11 @@ func (s *nomadSnapshot) Persist(sink raft.SnapshotSink) error {
|
||||||
sink.Cancel()
|
sink.Cancel()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.persistEnterpriseTables(sink, encoder); err != nil {
|
||||||
|
sink.Cancel()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// +build !pro,!ent
|
||||||
|
|
||||||
|
package nomad
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/raft"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// registerLogAppliers is a no-op for open-source only FSMs.
|
||||||
|
func (n *nomadFSM) registerLogAppliers() {}
|
||||||
|
|
||||||
|
// registerSnapshotRestorers is a no-op for open-source only FSMs.
|
||||||
|
func (n *nomadFSM) registerSnapshotRestorers() {}
|
||||||
|
|
||||||
|
// persistEnterpriseTables is a no-op for open-source only FSMs.
|
||||||
|
func (s *nomadSnapshot) persistEnterpriseTables(sink raft.SnapshotSink, encoder *codec.Encoder) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -281,6 +281,9 @@ func TestFSM_RegisterJob(t *testing.T) {
|
||||||
job := mock.PeriodicJob()
|
job := mock.PeriodicJob()
|
||||||
req := structs.JobRegisterRequest{
|
req := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf, err := structs.Encode(structs.JobRegisterRequestType, req)
|
buf, err := structs.Encode(structs.JobRegisterRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -294,7 +297,7 @@ func TestFSM_RegisterJob(t *testing.T) {
|
||||||
|
|
||||||
// Verify we are registered
|
// Verify we are registered
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
|
jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -306,12 +309,16 @@ func TestFSM_RegisterJob(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was added to the periodic runner.
|
// Verify it was added to the periodic runner.
|
||||||
if _, ok := fsm.periodicDispatcher.tracked[job.ID]; !ok {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, ok := fsm.periodicDispatcher.tracked[tuple]; !ok {
|
||||||
t.Fatal("job not added to periodic runner")
|
t.Fatal("job not added to periodic runner")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the launch time was tracked.
|
// Verify the launch time was tracked.
|
||||||
launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
|
launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -330,6 +337,9 @@ func TestFSM_DeregisterJob_Purge(t *testing.T) {
|
||||||
job := mock.PeriodicJob()
|
job := mock.PeriodicJob()
|
||||||
req := structs.JobRegisterRequest{
|
req := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf, err := structs.Encode(structs.JobRegisterRequestType, req)
|
buf, err := structs.Encode(structs.JobRegisterRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -344,6 +354,9 @@ func TestFSM_DeregisterJob_Purge(t *testing.T) {
|
||||||
req2 := structs.JobDeregisterRequest{
|
req2 := structs.JobDeregisterRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Purge: true,
|
Purge: true,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
|
buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -357,7 +370,7 @@ func TestFSM_DeregisterJob_Purge(t *testing.T) {
|
||||||
|
|
||||||
// Verify we are NOT registered
|
// Verify we are NOT registered
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
|
jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -366,12 +379,16 @@ func TestFSM_DeregisterJob_Purge(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was removed from the periodic runner.
|
// Verify it was removed from the periodic runner.
|
||||||
if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok {
|
||||||
t.Fatal("job not removed from periodic runner")
|
t.Fatal("job not removed from periodic runner")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was removed from the periodic launch table.
|
// Verify it was removed from the periodic launch table.
|
||||||
launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
|
launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -387,6 +404,9 @@ func TestFSM_DeregisterJob_NoPurge(t *testing.T) {
|
||||||
job := mock.PeriodicJob()
|
job := mock.PeriodicJob()
|
||||||
req := structs.JobRegisterRequest{
|
req := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf, err := structs.Encode(structs.JobRegisterRequestType, req)
|
buf, err := structs.Encode(structs.JobRegisterRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -401,6 +421,9 @@ func TestFSM_DeregisterJob_NoPurge(t *testing.T) {
|
||||||
req2 := structs.JobDeregisterRequest{
|
req2 := structs.JobDeregisterRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Purge: false,
|
Purge: false,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
|
buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -414,7 +437,7 @@ func TestFSM_DeregisterJob_NoPurge(t *testing.T) {
|
||||||
|
|
||||||
// Verify we are NOT registered
|
// Verify we are NOT registered
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
|
jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -426,12 +449,16 @@ func TestFSM_DeregisterJob_NoPurge(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was removed from the periodic runner.
|
// Verify it was removed from the periodic runner.
|
||||||
if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok {
|
||||||
t.Fatal("job not removed from periodic runner")
|
t.Fatal("job not removed from periodic runner")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was removed from the periodic launch table.
|
// Verify it was removed from the periodic launch table.
|
||||||
launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
|
launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1218,7 +1245,7 @@ func TestFSM_DeploymentStatusUpdate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the job was created
|
// Check that the job was created
|
||||||
jout, _ := state.JobByID(ws, j.ID)
|
jout, _ := state.JobByID(ws, j.Namespace, j.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("bad: %v", err)
|
t.Fatalf("bad: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1250,6 +1277,9 @@ func TestFSM_JobStabilityUpdate(t *testing.T) {
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
JobVersion: job.Version,
|
JobVersion: job.Version,
|
||||||
Stable: true,
|
Stable: true,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
buf, err := structs.Encode(structs.JobStabilityRequestType, req)
|
buf, err := structs.Encode(structs.JobStabilityRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1262,7 +1292,7 @@ func TestFSM_JobStabilityUpdate(t *testing.T) {
|
||||||
|
|
||||||
// Check that the stability was updated properly
|
// Check that the stability was updated properly
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
jout, _ := state.JobByIDAndVersion(ws, job.ID, job.Version)
|
jout, _ := state.JobByIDAndVersion(ws, job.Namespace, job.ID, job.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("bad: %v", err)
|
t.Fatalf("bad: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1451,7 +1481,7 @@ func TestFSM_DeploymentAllocHealth(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the job was created
|
// Check that the job was created
|
||||||
jout, _ := state.JobByID(ws, j.ID)
|
jout, _ := state.JobByID(ws, j.Namespace, j.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("bad: %v", err)
|
t.Fatalf("bad: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1725,8 +1755,8 @@ func TestFSM_SnapshotRestore_Jobs(t *testing.T) {
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
fsm2 := testSnapshotRestore(t, fsm)
|
fsm2 := testSnapshotRestore(t, fsm)
|
||||||
state2 := fsm2.State()
|
state2 := fsm2.State()
|
||||||
out1, _ := state2.JobByID(ws, job1.ID)
|
out1, _ := state2.JobByID(ws, job1.Namespace, job1.ID)
|
||||||
out2, _ := state2.JobByID(ws, job2.ID)
|
out2, _ := state2.JobByID(ws, job2.Namespace, job2.ID)
|
||||||
if !reflect.DeepEqual(job1, out1) {
|
if !reflect.DeepEqual(job1, out1) {
|
||||||
t.Fatalf("bad: \n%#v\n%#v", out1, job1)
|
t.Fatalf("bad: \n%#v\n%#v", out1, job1)
|
||||||
}
|
}
|
||||||
|
@ -1865,18 +1895,26 @@ func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) {
|
||||||
fsm := testFSM(t)
|
fsm := testFSM(t)
|
||||||
state := fsm.State()
|
state := fsm.State()
|
||||||
job1 := mock.Job()
|
job1 := mock.Job()
|
||||||
launch1 := &structs.PeriodicLaunch{ID: job1.ID, Launch: time.Now()}
|
launch1 := &structs.PeriodicLaunch{
|
||||||
|
ID: job1.ID,
|
||||||
|
Namespace: job1.Namespace,
|
||||||
|
Launch: time.Now(),
|
||||||
|
}
|
||||||
state.UpsertPeriodicLaunch(1000, launch1)
|
state.UpsertPeriodicLaunch(1000, launch1)
|
||||||
job2 := mock.Job()
|
job2 := mock.Job()
|
||||||
launch2 := &structs.PeriodicLaunch{ID: job2.ID, Launch: time.Now()}
|
launch2 := &structs.PeriodicLaunch{
|
||||||
|
ID: job2.ID,
|
||||||
|
Namespace: job2.Namespace,
|
||||||
|
Launch: time.Now(),
|
||||||
|
}
|
||||||
state.UpsertPeriodicLaunch(1001, launch2)
|
state.UpsertPeriodicLaunch(1001, launch2)
|
||||||
|
|
||||||
// Verify the contents
|
// Verify the contents
|
||||||
fsm2 := testSnapshotRestore(t, fsm)
|
fsm2 := testSnapshotRestore(t, fsm)
|
||||||
state2 := fsm2.State()
|
state2 := fsm2.State()
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out1, _ := state2.PeriodicLaunchByID(ws, launch1.ID)
|
out1, _ := state2.PeriodicLaunchByID(ws, launch1.Namespace, launch1.ID)
|
||||||
out2, _ := state2.PeriodicLaunchByID(ws, launch2.ID)
|
out2, _ := state2.PeriodicLaunchByID(ws, launch2.Namespace, launch2.ID)
|
||||||
|
|
||||||
if !cmp.Equal(launch1, out1) {
|
if !cmp.Equal(launch1, out1) {
|
||||||
t.Fatalf("bad: %v", cmp.Diff(launch1, out1))
|
t.Fatalf("bad: %v", cmp.Diff(launch1, out1))
|
||||||
|
@ -1895,17 +1933,17 @@ func TestFSM_SnapshotRestore_JobSummary(t *testing.T) {
|
||||||
job1 := mock.Job()
|
job1 := mock.Job()
|
||||||
state.UpsertJob(1000, job1)
|
state.UpsertJob(1000, job1)
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
js1, _ := state.JobSummaryByID(ws, job1.ID)
|
js1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID)
|
||||||
|
|
||||||
job2 := mock.Job()
|
job2 := mock.Job()
|
||||||
state.UpsertJob(1001, job2)
|
state.UpsertJob(1001, job2)
|
||||||
js2, _ := state.JobSummaryByID(ws, job2.ID)
|
js2, _ := state.JobSummaryByID(ws, job2.Namespace, job2.ID)
|
||||||
|
|
||||||
// Verify the contents
|
// Verify the contents
|
||||||
fsm2 := testSnapshotRestore(t, fsm)
|
fsm2 := testSnapshotRestore(t, fsm)
|
||||||
state2 := fsm2.State()
|
state2 := fsm2.State()
|
||||||
out1, _ := state2.JobSummaryByID(ws, job1.ID)
|
out1, _ := state2.JobSummaryByID(ws, job1.Namespace, job1.ID)
|
||||||
out2, _ := state2.JobSummaryByID(ws, job2.ID)
|
out2, _ := state2.JobSummaryByID(ws, job2.Namespace, job2.ID)
|
||||||
if !reflect.DeepEqual(js1, out1) {
|
if !reflect.DeepEqual(js1, out1) {
|
||||||
t.Fatalf("bad: \n%#v\n%#v", js1, out1)
|
t.Fatalf("bad: \n%#v\n%#v", js1, out1)
|
||||||
}
|
}
|
||||||
|
@ -1952,8 +1990,8 @@ func TestFSM_SnapshotRestore_JobVersions(t *testing.T) {
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
fsm2 := testSnapshotRestore(t, fsm)
|
fsm2 := testSnapshotRestore(t, fsm)
|
||||||
state2 := fsm2.State()
|
state2 := fsm2.State()
|
||||||
out1, _ := state2.JobByIDAndVersion(ws, job1.ID, job1.Version)
|
out1, _ := state2.JobByIDAndVersion(ws, job1.Namespace, job1.ID, job1.Version)
|
||||||
out2, _ := state2.JobByIDAndVersion(ws, job2.ID, job2.Version)
|
out2, _ := state2.JobByIDAndVersion(ws, job2.Namespace, job2.ID, job2.Version)
|
||||||
if !reflect.DeepEqual(job1, out1) {
|
if !reflect.DeepEqual(job1, out1) {
|
||||||
t.Fatalf("bad: \n%#v\n%#v", out1, job1)
|
t.Fatalf("bad: \n%#v\n%#v", out1, job1)
|
||||||
}
|
}
|
||||||
|
@ -2039,7 +2077,7 @@ func TestFSM_SnapshotRestore_AddMissingSummary(t *testing.T) {
|
||||||
state.UpsertAllocs(1011, []*structs.Allocation{alloc})
|
state.UpsertAllocs(1011, []*structs.Allocation{alloc})
|
||||||
|
|
||||||
// Delete the summary
|
// Delete the summary
|
||||||
state.DeleteJobSummary(1040, alloc.Job.ID)
|
state.DeleteJobSummary(1040, alloc.Namespace, alloc.Job.ID)
|
||||||
|
|
||||||
// Delete the index
|
// Delete the index
|
||||||
if err := state.RemoveIndex("job_summary"); err != nil {
|
if err := state.RemoveIndex("job_summary"); err != nil {
|
||||||
|
@ -2051,9 +2089,10 @@ func TestFSM_SnapshotRestore_AddMissingSummary(t *testing.T) {
|
||||||
latestIndex, _ := state.LatestIndex()
|
latestIndex, _ := state.LatestIndex()
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, _ := state2.JobSummaryByID(ws, alloc.Job.ID)
|
out, _ := state2.JobSummaryByID(ws, alloc.Namespace, alloc.Job.ID)
|
||||||
expected := structs.JobSummary{
|
expected := structs.JobSummary{
|
||||||
JobID: alloc.Job.ID,
|
JobID: alloc.Job.ID,
|
||||||
|
Namespace: alloc.Job.Namespace,
|
||||||
Summary: map[string]structs.TaskGroupSummary{
|
Summary: map[string]structs.TaskGroupSummary{
|
||||||
"web": structs.TaskGroupSummary{
|
"web": structs.TaskGroupSummary{
|
||||||
Starting: 1,
|
Starting: 1,
|
||||||
|
@ -2089,8 +2128,8 @@ func TestFSM_ReconcileSummaries(t *testing.T) {
|
||||||
state.UpsertAllocs(1011, []*structs.Allocation{alloc})
|
state.UpsertAllocs(1011, []*structs.Allocation{alloc})
|
||||||
|
|
||||||
// Delete the summaries
|
// Delete the summaries
|
||||||
state.DeleteJobSummary(1030, job1.ID)
|
state.DeleteJobSummary(1030, job1.Namespace, job1.ID)
|
||||||
state.DeleteJobSummary(1040, alloc.Job.ID)
|
state.DeleteJobSummary(1040, alloc.Namespace, alloc.Job.ID)
|
||||||
|
|
||||||
req := structs.GenericRequest{}
|
req := structs.GenericRequest{}
|
||||||
buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req)
|
buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req)
|
||||||
|
@ -2104,9 +2143,10 @@ func TestFSM_ReconcileSummaries(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out1, _ := state.JobSummaryByID(ws, job1.ID)
|
out1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID)
|
||||||
expected := structs.JobSummary{
|
expected := structs.JobSummary{
|
||||||
JobID: job1.ID,
|
JobID: job1.ID,
|
||||||
|
Namespace: job1.Namespace,
|
||||||
Summary: map[string]structs.TaskGroupSummary{
|
Summary: map[string]structs.TaskGroupSummary{
|
||||||
"web": structs.TaskGroupSummary{
|
"web": structs.TaskGroupSummary{
|
||||||
Queued: 10,
|
Queued: 10,
|
||||||
|
@ -2122,9 +2162,10 @@ func TestFSM_ReconcileSummaries(t *testing.T) {
|
||||||
// This exercises the code path which adds the allocations made by the
|
// This exercises the code path which adds the allocations made by the
|
||||||
// planner and the number of unplaced allocations in the reconcile summaries
|
// planner and the number of unplaced allocations in the reconcile summaries
|
||||||
// codepath
|
// codepath
|
||||||
out2, _ := state.JobSummaryByID(ws, alloc.Job.ID)
|
out2, _ := state.JobSummaryByID(ws, alloc.Namespace, alloc.Job.ID)
|
||||||
expected = structs.JobSummary{
|
expected = structs.JobSummary{
|
||||||
JobID: alloc.Job.ID,
|
JobID: alloc.Job.ID,
|
||||||
|
Namespace: alloc.Job.Namespace,
|
||||||
Summary: map[string]structs.TaskGroupSummary{
|
Summary: map[string]structs.TaskGroupSummary{
|
||||||
"web": structs.TaskGroupSummary{
|
"web": structs.TaskGroupSummary{
|
||||||
Queued: 9,
|
Queued: 9,
|
||||||
|
|
|
@ -85,7 +85,7 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
existingJob, err := snap.JobByID(ws, args.Job.ID)
|
existingJob, err := snap.JobByID(ws, args.RequestNamespace(), args.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,7 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
|
||||||
// Create a new evaluation
|
// Create a new evaluation
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: args.RequestNamespace(),
|
||||||
Priority: args.Job.Priority,
|
Priority: args.Job.Priority,
|
||||||
Type: args.Job.Type,
|
Type: args.Job.Type,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -290,7 +291,7 @@ func (j *Job) Summary(args *structs.JobSummaryRequest,
|
||||||
queryMeta: &reply.QueryMeta,
|
queryMeta: &reply.QueryMeta,
|
||||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||||
// Look for job summary
|
// Look for job summary
|
||||||
out, err := state.JobSummaryByID(ws, args.JobID)
|
out, err := state.JobSummaryByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -364,7 +365,7 @@ func (j *Job) Revert(args *structs.JobRevertRequest, reply *structs.JobRegisterR
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
cur, err := snap.JobByID(ws, args.JobID)
|
cur, err := snap.JobByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -375,12 +376,12 @@ func (j *Job) Revert(args *structs.JobRevertRequest, reply *structs.JobRegisterR
|
||||||
return fmt.Errorf("can't revert to current version")
|
return fmt.Errorf("can't revert to current version")
|
||||||
}
|
}
|
||||||
|
|
||||||
jobV, err := snap.JobByIDAndVersion(ws, args.JobID, args.JobVersion)
|
jobV, err := snap.JobByIDAndVersion(ws, args.RequestNamespace(), args.JobID, args.JobVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if jobV == nil {
|
if jobV == nil {
|
||||||
return fmt.Errorf("job %q at version %d not found", args.JobID, args.JobVersion)
|
return fmt.Errorf("job %q in namespace %q at version %d not found", args.JobID, args.RequestNamespace(), args.JobVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the register request
|
// Build the register request
|
||||||
|
@ -422,12 +423,12 @@ func (j *Job) Stable(args *structs.JobStabilityRequest, reply *structs.JobStabil
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
jobV, err := snap.JobByIDAndVersion(ws, args.JobID, args.JobVersion)
|
jobV, err := snap.JobByIDAndVersion(ws, args.RequestNamespace(), args.JobID, args.JobVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if jobV == nil {
|
if jobV == nil {
|
||||||
return fmt.Errorf("job %q at version %d not found", args.JobID, args.JobVersion)
|
return fmt.Errorf("job %q in namespace %q at version %d not found", args.JobID, args.RequestNamespace(), args.JobVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit this stability request via Raft
|
// Commit this stability request via Raft
|
||||||
|
@ -460,7 +461,7 @@ func (j *Job) Evaluate(args *structs.JobEvaluateRequest, reply *structs.JobRegis
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
job, err := snap.JobByID(ws, args.JobID)
|
job, err := snap.JobByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -477,6 +478,7 @@ func (j *Job) Evaluate(args *structs.JobEvaluateRequest, reply *structs.JobRegis
|
||||||
// Create a new evaluation
|
// Create a new evaluation
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: args.RequestNamespace(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
Type: job.Type,
|
Type: job.Type,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -522,7 +524,7 @@ func (j *Job) Deregister(args *structs.JobDeregisterRequest, reply *structs.JobD
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
job, err := snap.JobByID(ws, args.JobID)
|
job, err := snap.JobByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -548,6 +550,7 @@ func (j *Job) Deregister(args *structs.JobDeregisterRequest, reply *structs.JobD
|
||||||
// since all should be able to handle deregistration in the same way.
|
// since all should be able to handle deregistration in the same way.
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: args.RequestNamespace(),
|
||||||
Priority: structs.JobDefaultPriority,
|
Priority: structs.JobDefaultPriority,
|
||||||
Type: structs.JobTypeService,
|
Type: structs.JobTypeService,
|
||||||
TriggeredBy: structs.EvalTriggerJobDeregister,
|
TriggeredBy: structs.EvalTriggerJobDeregister,
|
||||||
|
@ -588,7 +591,7 @@ func (j *Job) GetJob(args *structs.JobSpecificRequest,
|
||||||
queryMeta: &reply.QueryMeta,
|
queryMeta: &reply.QueryMeta,
|
||||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||||
// Look for the job
|
// Look for the job
|
||||||
out, err := state.JobByID(ws, args.JobID)
|
out, err := state.JobByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -627,7 +630,7 @@ func (j *Job) GetJobVersions(args *structs.JobVersionsRequest,
|
||||||
queryMeta: &reply.QueryMeta,
|
queryMeta: &reply.QueryMeta,
|
||||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||||
// Look for the job
|
// Look for the job
|
||||||
out, err := state.JobVersionsByID(ws, args.JobID)
|
out, err := state.JobVersionsByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -681,9 +684,9 @@ func (j *Job) List(args *structs.JobListRequest,
|
||||||
var err error
|
var err error
|
||||||
var iter memdb.ResultIterator
|
var iter memdb.ResultIterator
|
||||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||||
iter, err = state.JobsByIDPrefix(ws, prefix)
|
iter, err = state.JobsByIDPrefix(ws, args.RequestNamespace(), prefix)
|
||||||
} else {
|
} else {
|
||||||
iter, err = state.Jobs(ws)
|
iter, err = state.JobsByNamespace(ws, args.RequestNamespace())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -696,7 +699,7 @@ func (j *Job) List(args *structs.JobListRequest,
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
job := raw.(*structs.Job)
|
job := raw.(*structs.Job)
|
||||||
summary, err := state.JobSummaryByID(ws, job.ID)
|
summary, err := state.JobSummaryByID(ws, args.RequestNamespace(), job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to look up summary for job: %v", job.ID)
|
return fmt.Errorf("unable to look up summary for job: %v", job.ID)
|
||||||
}
|
}
|
||||||
|
@ -732,7 +735,7 @@ func (j *Job) Allocations(args *structs.JobSpecificRequest,
|
||||||
queryMeta: &reply.QueryMeta,
|
queryMeta: &reply.QueryMeta,
|
||||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||||
// Capture the allocations
|
// Capture the allocations
|
||||||
allocs, err := state.AllocsByJob(ws, args.JobID, args.AllAllocs)
|
allocs, err := state.AllocsByJob(ws, args.RequestNamespace(), args.JobID, args.AllAllocs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -775,7 +778,7 @@ func (j *Job) Evaluations(args *structs.JobSpecificRequest,
|
||||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||||
// Capture the evals
|
// Capture the evals
|
||||||
var err error
|
var err error
|
||||||
reply.Evaluations, err = state.EvalsByJob(ws, args.JobID)
|
reply.Evaluations, err = state.EvalsByJob(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -809,7 +812,7 @@ func (j *Job) Deployments(args *structs.JobSpecificRequest,
|
||||||
queryMeta: &reply.QueryMeta,
|
queryMeta: &reply.QueryMeta,
|
||||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||||
// Capture the deployments
|
// Capture the deployments
|
||||||
deploys, err := state.DeploymentsByJobID(ws, args.JobID)
|
deploys, err := state.DeploymentsByJobID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -844,7 +847,7 @@ func (j *Job) LatestDeployment(args *structs.JobSpecificRequest,
|
||||||
queryMeta: &reply.QueryMeta,
|
queryMeta: &reply.QueryMeta,
|
||||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||||
// Capture the deployments
|
// Capture the deployments
|
||||||
deploys, err := state.DeploymentsByJobID(ws, args.JobID)
|
deploys, err := state.DeploymentsByJobID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -906,7 +909,7 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse)
|
||||||
|
|
||||||
// Get the original job
|
// Get the original job
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
oldJob, err := snap.JobByID(ws, args.Job.ID)
|
oldJob, err := snap.JobByID(ws, args.RequestNamespace(), args.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -932,6 +935,7 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse)
|
||||||
// Create an eval and mark it as requiring annotations and insert that as well
|
// Create an eval and mark it as requiring annotations and insert that as well
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: args.RequestNamespace(),
|
||||||
Priority: args.Job.Priority,
|
Priority: args.Job.Priority,
|
||||||
Type: args.Job.Type,
|
Type: args.Job.Type,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1100,7 +1104,7 @@ func (j *Job) Dispatch(args *structs.JobDispatchRequest, reply *structs.JobDispa
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
parameterizedJob, err := snap.JobByID(ws, args.JobID)
|
parameterizedJob, err := snap.JobByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1161,6 +1165,7 @@ func (j *Job) Dispatch(args *structs.JobDispatchRequest, reply *structs.JobDispa
|
||||||
// Create a new evaluation
|
// Create a new evaluation
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: args.RequestNamespace(),
|
||||||
Priority: dispatchJob.Priority,
|
Priority: dispatchJob.Priority,
|
||||||
Type: dispatchJob.Type,
|
Type: dispatchJob.Type,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -203,6 +203,12 @@ func (s *Server) establishLeadership(stopCh chan struct{}) error {
|
||||||
go s.replicateACLPolicies(stopCh)
|
go s.replicateACLPolicies(stopCh)
|
||||||
go s.replicateACLTokens(stopCh)
|
go s.replicateACLTokens(stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup any enterprise systems required.
|
||||||
|
if err := s.establishEnterpriseLeadership(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +319,7 @@ func (s *Server) restorePeriodicDispatcher() error {
|
||||||
// If the periodic job has never been launched before, launch will hold
|
// If the periodic job has never been launched before, launch will hold
|
||||||
// the time the periodic job was added. Otherwise it has the last launch
|
// the time the periodic job was added. Otherwise it has the last launch
|
||||||
// time of the periodic job.
|
// time of the periodic job.
|
||||||
launch, err := s.fsm.State().PeriodicLaunchByID(ws, job.ID)
|
launch, err := s.fsm.State().PeriodicLaunchByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil || launch == nil {
|
if err != nil || launch == nil {
|
||||||
return fmt.Errorf("failed to get periodic launch time: %v", err)
|
return fmt.Errorf("failed to get periodic launch time: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -328,7 +334,7 @@ func (s *Server) restorePeriodicDispatcher() error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := s.periodicDispatcher.ForceRun(job.ID); err != nil {
|
if _, err := s.periodicDispatcher.ForceRun(job.Namespace, job.ID); err != nil {
|
||||||
msg := fmt.Sprintf("force run of periodic job %q failed: %v", job.ID, err)
|
msg := fmt.Sprintf("force run of periodic job %q failed: %v", job.ID, err)
|
||||||
s.logger.Printf("[ERR] nomad.periodic: %s", msg)
|
s.logger.Printf("[ERR] nomad.periodic: %s", msg)
|
||||||
return errors.New(msg)
|
return errors.New(msg)
|
||||||
|
@ -386,6 +392,7 @@ func (s *Server) schedulePeriodic(stopCh chan struct{}) {
|
||||||
func (s *Server) coreJobEval(job string, modifyIndex uint64) *structs.Evaluation {
|
func (s *Server) coreJobEval(job string, modifyIndex uint64) *structs.Evaluation {
|
||||||
return &structs.Evaluation{
|
return &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: "-",
|
||||||
Priority: structs.CoreJobPriority,
|
Priority: structs.CoreJobPriority,
|
||||||
Type: structs.JobTypeCore,
|
Type: structs.JobTypeCore,
|
||||||
TriggeredBy: structs.EvalTriggerScheduled,
|
TriggeredBy: structs.EvalTriggerScheduled,
|
||||||
|
@ -513,6 +520,11 @@ func (s *Server) revokeLeadership() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable any enterprise systems required.
|
||||||
|
if err := s.revokeEnterpriseLeadership(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the heartbeat timers on either shutdown or step down,
|
// Clear the heartbeat timers on either shutdown or step down,
|
||||||
// since we are no longer responsible for TTL expirations.
|
// since we are no longer responsible for TTL expirations.
|
||||||
if err := s.clearAllHeartbeatTimers(); err != nil {
|
if err := s.clearAllHeartbeatTimers(); err != nil {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build !pro,!ent
|
||||||
|
|
||||||
|
package nomad
|
||||||
|
|
||||||
|
// establishEnterpriseLeadership is a no-op on OSS.
|
||||||
|
func (s *Server) establishEnterpriseLeadership() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// revokeEnterpriseLeadership is a no-op on OSS>
|
||||||
|
func (s *Server) revokeEnterpriseLeadership() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -339,6 +339,9 @@ func TestLeader_PeriodicDispatcher_Restore_Adds(t *testing.T) {
|
||||||
for _, job := range []*structs.Job{nonPeriodic, periodic, parameterizedPeriodic} {
|
for _, job := range []*structs.Job{nonPeriodic, periodic, parameterizedPeriodic} {
|
||||||
req := structs.JobRegisterRequest{
|
req := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
_, _, err := leader.raftApply(structs.JobRegisterRequestType, req)
|
_, _, err := leader.raftApply(structs.JobRegisterRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -364,15 +367,28 @@ func TestLeader_PeriodicDispatcher_Restore_Adds(t *testing.T) {
|
||||||
t.Fatalf("should have leader")
|
t.Fatalf("should have leader")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
tuplePeriodic := structs.NamespacedID{
|
||||||
|
ID: periodic.ID,
|
||||||
|
Namespace: periodic.Namespace,
|
||||||
|
}
|
||||||
|
tupleNonPeriodic := structs.NamespacedID{
|
||||||
|
ID: nonPeriodic.ID,
|
||||||
|
Namespace: nonPeriodic.Namespace,
|
||||||
|
}
|
||||||
|
tupleParameterized := structs.NamespacedID{
|
||||||
|
ID: parameterizedPeriodic.ID,
|
||||||
|
Namespace: parameterizedPeriodic.Namespace,
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the new leader is tracking the periodic job only
|
// Check that the new leader is tracking the periodic job only
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
if _, tracked := leader.periodicDispatcher.tracked[periodic.ID]; !tracked {
|
if _, tracked := leader.periodicDispatcher.tracked[tuplePeriodic]; !tracked {
|
||||||
return false, fmt.Errorf("periodic job not tracked")
|
return false, fmt.Errorf("periodic job not tracked")
|
||||||
}
|
}
|
||||||
if _, tracked := leader.periodicDispatcher.tracked[nonPeriodic.ID]; tracked {
|
if _, tracked := leader.periodicDispatcher.tracked[tupleNonPeriodic]; tracked {
|
||||||
return false, fmt.Errorf("non periodic job tracked")
|
return false, fmt.Errorf("non periodic job tracked")
|
||||||
}
|
}
|
||||||
if _, tracked := leader.periodicDispatcher.tracked[parameterizedPeriodic.ID]; tracked {
|
if _, tracked := leader.periodicDispatcher.tracked[tupleParameterized]; tracked {
|
||||||
return false, fmt.Errorf("parameterized periodic job tracked")
|
return false, fmt.Errorf("parameterized periodic job tracked")
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -393,6 +409,9 @@ func TestLeader_PeriodicDispatcher_Restore_NoEvals(t *testing.T) {
|
||||||
job := testPeriodicJob(launch)
|
job := testPeriodicJob(launch)
|
||||||
req := structs.JobRegisterRequest{
|
req := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
_, _, err := s1.raftApply(structs.JobRegisterRequestType, req)
|
_, _, err := s1.raftApply(structs.JobRegisterRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -414,13 +433,17 @@ func TestLeader_PeriodicDispatcher_Restore_NoEvals(t *testing.T) {
|
||||||
s1.restorePeriodicDispatcher()
|
s1.restorePeriodicDispatcher()
|
||||||
|
|
||||||
// Ensure the job is tracked.
|
// Ensure the job is tracked.
|
||||||
if _, tracked := s1.periodicDispatcher.tracked[job.ID]; !tracked {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, tracked := s1.periodicDispatcher.tracked[tuple]; !tracked {
|
||||||
t.Fatalf("periodic job not restored")
|
t.Fatalf("periodic job not restored")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that an eval was made.
|
// Check that an eval was made.
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
last, err := s1.fsm.State().PeriodicLaunchByID(ws, job.ID)
|
last, err := s1.fsm.State().PeriodicLaunchByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil || last == nil {
|
if err != nil || last == nil {
|
||||||
t.Fatalf("failed to get periodic launch time: %v", err)
|
t.Fatalf("failed to get periodic launch time: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -445,6 +468,9 @@ func TestLeader_PeriodicDispatcher_Restore_Evals(t *testing.T) {
|
||||||
job := testPeriodicJob(past, now, future)
|
job := testPeriodicJob(past, now, future)
|
||||||
req := structs.JobRegisterRequest{
|
req := structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
_, _, err := s1.raftApply(structs.JobRegisterRequestType, req)
|
_, _, err := s1.raftApply(structs.JobRegisterRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -465,13 +491,17 @@ func TestLeader_PeriodicDispatcher_Restore_Evals(t *testing.T) {
|
||||||
s1.restorePeriodicDispatcher()
|
s1.restorePeriodicDispatcher()
|
||||||
|
|
||||||
// Ensure the job is tracked.
|
// Ensure the job is tracked.
|
||||||
if _, tracked := s1.periodicDispatcher.tracked[job.ID]; !tracked {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, tracked := s1.periodicDispatcher.tracked[tuple]; !tracked {
|
||||||
t.Fatalf("periodic job not restored")
|
t.Fatalf("periodic job not restored")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that an eval was made.
|
// Check that an eval was made.
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
last, err := s1.fsm.State().PeriodicLaunchByID(ws, job.ID)
|
last, err := s1.fsm.State().PeriodicLaunchByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil || last == nil {
|
if err != nil || last == nil {
|
||||||
t.Fatalf("failed to get periodic launch time: %v", err)
|
t.Fatalf("failed to get periodic launch time: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -535,7 +565,7 @@ func TestLeader_ReapFailedEval(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if there is a followup
|
// See if there is a followup
|
||||||
evals, err := state.EvalsByJob(ws, eval.JobID)
|
evals, err := state.EvalsByJob(ws, eval.Namespace, eval.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ func Job() *structs.Job {
|
||||||
Region: "global",
|
Region: "global",
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Name: "my-job",
|
Name: "my-job",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
Type: structs.JobTypeService,
|
Type: structs.JobTypeService,
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
AllAtOnce: false,
|
AllAtOnce: false,
|
||||||
|
@ -159,6 +160,7 @@ func Job() *structs.Job {
|
||||||
func SystemJob() *structs.Job {
|
func SystemJob() *structs.Job {
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Name: "my-job",
|
Name: "my-job",
|
||||||
Type: structs.JobTypeSystem,
|
Type: structs.JobTypeSystem,
|
||||||
|
@ -231,18 +233,20 @@ func PeriodicJob() *structs.Job {
|
||||||
|
|
||||||
func Eval() *structs.Evaluation {
|
func Eval() *structs.Evaluation {
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Namespace: structs.DefaultNamespace,
|
||||||
Type: structs.JobTypeService,
|
Priority: 50,
|
||||||
JobID: structs.GenerateUUID(),
|
Type: structs.JobTypeService,
|
||||||
Status: structs.EvalStatusPending,
|
JobID: structs.GenerateUUID(),
|
||||||
|
Status: structs.EvalStatusPending,
|
||||||
}
|
}
|
||||||
return eval
|
return eval
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobSummary(jobID string) *structs.JobSummary {
|
func JobSummary(jobID string) *structs.JobSummary {
|
||||||
js := &structs.JobSummary{
|
js := &structs.JobSummary{
|
||||||
JobID: jobID,
|
JobID: jobID,
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
Summary: map[string]structs.TaskGroupSummary{
|
Summary: map[string]structs.TaskGroupSummary{
|
||||||
"web": {
|
"web": {
|
||||||
Queued: 0,
|
Queued: 0,
|
||||||
|
@ -258,6 +262,7 @@ func Alloc() *structs.Allocation {
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
EvalID: structs.GenerateUUID(),
|
EvalID: structs.GenerateUUID(),
|
||||||
NodeID: "12345678-abcd-efab-cdef-123456789abc",
|
NodeID: "12345678-abcd-efab-cdef-123456789abc",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: "web",
|
TaskGroup: "web",
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 500,
|
CPU: 500,
|
||||||
|
@ -313,6 +318,7 @@ func Deployment() *structs.Deployment {
|
||||||
return &structs.Deployment{
|
return &structs.Deployment{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
JobID: structs.GenerateUUID(),
|
JobID: structs.GenerateUUID(),
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
JobVersion: 2,
|
JobVersion: 2,
|
||||||
JobModifyIndex: 20,
|
JobModifyIndex: 20,
|
||||||
JobCreateIndex: 18,
|
JobCreateIndex: 18,
|
||||||
|
|
|
@ -844,6 +844,7 @@ func (n *Node) createNodeEvals(nodeID string, nodeIndex uint64) ([]string, uint6
|
||||||
// Create a new eval
|
// Create a new eval
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: alloc.Namespace,
|
||||||
Priority: alloc.Job.Priority,
|
Priority: alloc.Job.Priority,
|
||||||
Type: alloc.Job.Type,
|
Type: alloc.Job.Type,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -867,6 +868,7 @@ func (n *Node) createNodeEvals(nodeID string, nodeIndex uint64) ([]string, uint6
|
||||||
// Create a new eval
|
// Create a new eval
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: job.Namespace,
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
Type: job.Type,
|
Type: job.Type,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
|
|
@ -682,8 +682,11 @@ func TestClientEndpoint_Drain_Down(t *testing.T) {
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
job.TaskGroups[0].Count = 1
|
job.TaskGroups[0].Count = 1
|
||||||
jobReq := &structs.JobRegisterRequest{
|
jobReq := &structs.JobRegisterRequest{
|
||||||
Job: job,
|
Job: job,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := msgpackrpc.CallWithCodec(codec, "Job.Register", jobReq, &jobResp); err != nil {
|
if err := msgpackrpc.CallWithCodec(codec, "Job.Register", jobReq, &jobResp); err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
|
@ -695,8 +698,11 @@ func TestClientEndpoint_Drain_Down(t *testing.T) {
|
||||||
job1.TaskGroups[0].Count = 1
|
job1.TaskGroups[0].Count = 1
|
||||||
job1.Type = structs.JobTypeSystem
|
job1.Type = structs.JobTypeSystem
|
||||||
jobReq1 := &structs.JobRegisterRequest{
|
jobReq1 := &structs.JobRegisterRequest{
|
||||||
Job: job1,
|
Job: job1,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job1.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := msgpackrpc.CallWithCodec(codec, "Job.Register", jobReq1, &jobResp1); err != nil {
|
if err := msgpackrpc.CallWithCodec(codec, "Job.Register", jobReq1, &jobResp1); err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
|
@ -705,11 +711,11 @@ func TestClientEndpoint_Drain_Down(t *testing.T) {
|
||||||
// Wait for the scheduler to create an allocation
|
// Wait for the scheduler to create an allocation
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
allocs, err := s1.fsm.state.AllocsByJob(ws, job.ID, true)
|
allocs, err := s1.fsm.state.AllocsByJob(ws, job.Namespace, job.ID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
allocs1, err := s1.fsm.state.AllocsByJob(ws, job1.ID, true)
|
allocs1, err := s1.fsm.state.AllocsByJob(ws, job1.Namespace, job1.ID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -742,12 +748,13 @@ func TestClientEndpoint_Drain_Down(t *testing.T) {
|
||||||
// Ensure that the allocation has transitioned to lost
|
// Ensure that the allocation has transitioned to lost
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
summary, err := s1.fsm.state.JobSummaryByID(ws, job.ID)
|
summary, err := s1.fsm.state.JobSummaryByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
expectedSummary := &structs.JobSummary{
|
expectedSummary := &structs.JobSummary{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
Summary: map[string]structs.TaskGroupSummary{
|
Summary: map[string]structs.TaskGroupSummary{
|
||||||
"web": structs.TaskGroupSummary{
|
"web": structs.TaskGroupSummary{
|
||||||
Queued: 1,
|
Queued: 1,
|
||||||
|
@ -762,12 +769,13 @@ func TestClientEndpoint_Drain_Down(t *testing.T) {
|
||||||
return false, fmt.Errorf("expected: %#v, actual: %#v", expectedSummary, summary)
|
return false, fmt.Errorf("expected: %#v, actual: %#v", expectedSummary, summary)
|
||||||
}
|
}
|
||||||
|
|
||||||
summary1, err := s1.fsm.state.JobSummaryByID(ws, job1.ID)
|
summary1, err := s1.fsm.state.JobSummaryByID(ws, job1.Namespace, job1.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
expectedSummary1 := &structs.JobSummary{
|
expectedSummary1 := &structs.JobSummary{
|
||||||
JobID: job1.ID,
|
JobID: job1.ID,
|
||||||
|
Namespace: job1.Namespace,
|
||||||
Summary: map[string]structs.TaskGroupSummary{
|
Summary: map[string]structs.TaskGroupSummary{
|
||||||
"web": structs.TaskGroupSummary{
|
"web": structs.TaskGroupSummary{
|
||||||
Lost: 1,
|
Lost: 1,
|
||||||
|
|
|
@ -21,7 +21,7 @@ type PeriodicDispatch struct {
|
||||||
dispatcher JobEvalDispatcher
|
dispatcher JobEvalDispatcher
|
||||||
enabled bool
|
enabled bool
|
||||||
|
|
||||||
tracked map[string]*structs.Job
|
tracked map[structs.NamespacedID]*structs.Job
|
||||||
heap *periodicHeap
|
heap *periodicHeap
|
||||||
|
|
||||||
updateCh chan struct{}
|
updateCh chan struct{}
|
||||||
|
@ -46,7 +46,12 @@ type JobEvalDispatcher interface {
|
||||||
func (s *Server) DispatchJob(job *structs.Job) (*structs.Evaluation, error) {
|
func (s *Server) DispatchJob(job *structs.Job) (*structs.Evaluation, error) {
|
||||||
// Commit this update via Raft
|
// Commit this update via Raft
|
||||||
job.SetSubmitTime()
|
job.SetSubmitTime()
|
||||||
req := structs.JobRegisterRequest{Job: job}
|
req := structs.JobRegisterRequest{
|
||||||
|
Job: job,
|
||||||
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
|
}
|
||||||
_, index, err := s.raftApply(structs.JobRegisterRequestType, req)
|
_, index, err := s.raftApply(structs.JobRegisterRequestType, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -55,6 +60,7 @@ func (s *Server) DispatchJob(job *structs.Job) (*structs.Evaluation, error) {
|
||||||
// Create a new evaluation
|
// Create a new evaluation
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: job.Namespace,
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
Type: job.Type,
|
Type: job.Type,
|
||||||
TriggeredBy: structs.EvalTriggerPeriodicJob,
|
TriggeredBy: structs.EvalTriggerPeriodicJob,
|
||||||
|
@ -89,7 +95,7 @@ func (s *Server) RunningChildren(job *structs.Job) (bool, error) {
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
prefix := fmt.Sprintf("%s%s", job.ID, structs.PeriodicLaunchSuffix)
|
prefix := fmt.Sprintf("%s%s", job.ID, structs.PeriodicLaunchSuffix)
|
||||||
iter, err := state.JobsByIDPrefix(ws, prefix)
|
iter, err := state.JobsByIDPrefix(ws, job.Namespace, prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -104,7 +110,7 @@ func (s *Server) RunningChildren(job *structs.Job) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the childs evaluations.
|
// Get the childs evaluations.
|
||||||
evals, err := state.EvalsByJob(ws, child.ID)
|
evals, err := state.EvalsByJob(ws, child.Namespace, child.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -137,7 +143,7 @@ func (s *Server) RunningChildren(job *structs.Job) (bool, error) {
|
||||||
func NewPeriodicDispatch(logger *log.Logger, dispatcher JobEvalDispatcher) *PeriodicDispatch {
|
func NewPeriodicDispatch(logger *log.Logger, dispatcher JobEvalDispatcher) *PeriodicDispatch {
|
||||||
return &PeriodicDispatch{
|
return &PeriodicDispatch{
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
tracked: make(map[string]*structs.Job),
|
tracked: make(map[structs.NamespacedID]*structs.Job),
|
||||||
heap: NewPeriodicHeap(),
|
heap: NewPeriodicHeap(),
|
||||||
updateCh: make(chan struct{}, 1),
|
updateCh: make(chan struct{}, 1),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
@ -192,10 +198,15 @@ func (p *PeriodicDispatch) Add(job *structs.Job) error {
|
||||||
|
|
||||||
// If we were tracking a job and it has been disabled or made non-periodic remove it.
|
// If we were tracking a job and it has been disabled or made non-periodic remove it.
|
||||||
disabled := !job.IsPeriodic() || !job.Periodic.Enabled
|
disabled := !job.IsPeriodic() || !job.Periodic.Enabled
|
||||||
_, tracked := p.tracked[job.ID]
|
|
||||||
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
_, tracked := p.tracked[tuple]
|
||||||
if disabled {
|
if disabled {
|
||||||
if tracked {
|
if tracked {
|
||||||
p.removeLocked(job.ID)
|
p.removeLocked(tuple)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the job is disabled and we aren't tracking it, do nothing.
|
// If the job is disabled and we aren't tracking it, do nothing.
|
||||||
|
@ -209,18 +220,18 @@ func (p *PeriodicDispatch) Add(job *structs.Job) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add or update the job.
|
// Add or update the job.
|
||||||
p.tracked[job.ID] = job
|
p.tracked[tuple] = job
|
||||||
next := job.Periodic.Next(time.Now().In(job.Periodic.GetLocation()))
|
next := job.Periodic.Next(time.Now().In(job.Periodic.GetLocation()))
|
||||||
if tracked {
|
if tracked {
|
||||||
if err := p.heap.Update(job, next); err != nil {
|
if err := p.heap.Update(job, next); err != nil {
|
||||||
return fmt.Errorf("failed to update job %v launch time: %v", job.ID, err)
|
return fmt.Errorf("failed to update job %q (%s) launch time: %v", job.ID, job.Namespace, err)
|
||||||
}
|
}
|
||||||
p.logger.Printf("[DEBUG] nomad.periodic: updated periodic job %q", job.ID)
|
p.logger.Printf("[DEBUG] nomad.periodic: updated periodic job %q (%s)", job.ID, job.Namespace)
|
||||||
} else {
|
} else {
|
||||||
if err := p.heap.Push(job, next); err != nil {
|
if err := p.heap.Push(job, next); err != nil {
|
||||||
return fmt.Errorf("failed to add job %v: %v", job.ID, err)
|
return fmt.Errorf("failed to add job %v: %v", job.ID, err)
|
||||||
}
|
}
|
||||||
p.logger.Printf("[DEBUG] nomad.periodic: registered periodic job %q", job.ID)
|
p.logger.Printf("[DEBUG] nomad.periodic: registered periodic job %q (%s)", job.ID, job.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal an update.
|
// Signal an update.
|
||||||
|
@ -234,15 +245,18 @@ func (p *PeriodicDispatch) Add(job *structs.Job) error {
|
||||||
|
|
||||||
// Remove stops tracking the passed job. If the job is not tracked, it is a
|
// Remove stops tracking the passed job. If the job is not tracked, it is a
|
||||||
// no-op.
|
// no-op.
|
||||||
func (p *PeriodicDispatch) Remove(jobID string) error {
|
func (p *PeriodicDispatch) Remove(namespace, jobID string) error {
|
||||||
p.l.Lock()
|
p.l.Lock()
|
||||||
defer p.l.Unlock()
|
defer p.l.Unlock()
|
||||||
return p.removeLocked(jobID)
|
return p.removeLocked(structs.NamespacedID{
|
||||||
|
ID: jobID,
|
||||||
|
Namespace: namespace,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove stops tracking the passed job. If the job is not tracked, it is a
|
// Remove stops tracking the passed job. If the job is not tracked, it is a
|
||||||
// no-op. It assumes this is called while a lock is held.
|
// no-op. It assumes this is called while a lock is held.
|
||||||
func (p *PeriodicDispatch) removeLocked(jobID string) error {
|
func (p *PeriodicDispatch) removeLocked(jobID structs.NamespacedID) error {
|
||||||
// Do nothing if not enabled
|
// Do nothing if not enabled
|
||||||
if !p.enabled {
|
if !p.enabled {
|
||||||
return nil
|
return nil
|
||||||
|
@ -255,7 +269,7 @@ func (p *PeriodicDispatch) removeLocked(jobID string) error {
|
||||||
|
|
||||||
delete(p.tracked, jobID)
|
delete(p.tracked, jobID)
|
||||||
if err := p.heap.Remove(job); err != nil {
|
if err := p.heap.Remove(job); err != nil {
|
||||||
return fmt.Errorf("failed to remove tracked job %v: %v", jobID, err)
|
return fmt.Errorf("failed to remove tracked job %q (%s): %v", jobID.ID, jobID.Namespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal an update.
|
// Signal an update.
|
||||||
|
@ -264,13 +278,13 @@ func (p *PeriodicDispatch) removeLocked(jobID string) error {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
p.logger.Printf("[DEBUG] nomad.periodic: deregistered periodic job %q", jobID)
|
p.logger.Printf("[DEBUG] nomad.periodic: deregistered periodic job %q (%s)", jobID.ID, jobID.Namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForceRun causes the periodic job to be evaluated immediately and returns the
|
// ForceRun causes the periodic job to be evaluated immediately and returns the
|
||||||
// subsequent eval.
|
// subsequent eval.
|
||||||
func (p *PeriodicDispatch) ForceRun(jobID string) (*structs.Evaluation, error) {
|
func (p *PeriodicDispatch) ForceRun(namespace, jobID string) (*structs.Evaluation, error) {
|
||||||
p.l.Lock()
|
p.l.Lock()
|
||||||
|
|
||||||
// Do nothing if not enabled
|
// Do nothing if not enabled
|
||||||
|
@ -279,10 +293,14 @@ func (p *PeriodicDispatch) ForceRun(jobID string) (*structs.Evaluation, error) {
|
||||||
return nil, fmt.Errorf("periodic dispatch disabled")
|
return nil, fmt.Errorf("periodic dispatch disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
job, tracked := p.tracked[jobID]
|
tuple := structs.NamespacedID{
|
||||||
|
ID: jobID,
|
||||||
|
Namespace: namespace,
|
||||||
|
}
|
||||||
|
job, tracked := p.tracked[tuple]
|
||||||
if !tracked {
|
if !tracked {
|
||||||
p.l.Unlock()
|
p.l.Unlock()
|
||||||
return nil, fmt.Errorf("can't force run non-tracked job %v", jobID)
|
return nil, fmt.Errorf("can't force run non-tracked job %q (%s)", jobID, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.l.Unlock()
|
p.l.Unlock()
|
||||||
|
@ -307,7 +325,7 @@ func (p *PeriodicDispatch) run(ctx context.Context) {
|
||||||
} else {
|
} else {
|
||||||
launchDur := launch.Sub(time.Now().In(job.Periodic.GetLocation()))
|
launchDur := launch.Sub(time.Now().In(job.Periodic.GetLocation()))
|
||||||
launchCh = time.After(launchDur)
|
launchCh = time.After(launchDur)
|
||||||
p.logger.Printf("[DEBUG] nomad.periodic: launching job %q in %s", job.ID, launchDur)
|
p.logger.Printf("[DEBUG] nomad.periodic: launching job %q (%s) in %s", job.ID, job.Namespace, launchDur)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
@ -328,7 +346,7 @@ func (p *PeriodicDispatch) dispatch(job *structs.Job, launchTime time.Time) {
|
||||||
|
|
||||||
nextLaunch := job.Periodic.Next(launchTime)
|
nextLaunch := job.Periodic.Next(launchTime)
|
||||||
if err := p.heap.Update(job, nextLaunch); err != nil {
|
if err := p.heap.Update(job, nextLaunch); err != nil {
|
||||||
p.logger.Printf("[ERR] nomad.periodic: failed to update next launch of periodic job %q: %v", job.ID, err)
|
p.logger.Printf("[ERR] nomad.periodic: failed to update next launch of periodic job %q (%s): %v", job.ID, job.Namespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the job prohibits overlapping and there are running children, we skip
|
// If the job prohibits overlapping and there are running children, we skip
|
||||||
|
@ -337,7 +355,7 @@ func (p *PeriodicDispatch) dispatch(job *structs.Job, launchTime time.Time) {
|
||||||
running, err := p.dispatcher.RunningChildren(job)
|
running, err := p.dispatcher.RunningChildren(job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("[ERR] nomad.periodic: failed to determine if"+
|
msg := fmt.Sprintf("[ERR] nomad.periodic: failed to determine if"+
|
||||||
" periodic job %q has running children: %v", job.ID, err)
|
" periodic job %q (%s) has running children: %v", job.ID, job.Namespace, err)
|
||||||
p.logger.Println(msg)
|
p.logger.Println(msg)
|
||||||
p.l.Unlock()
|
p.l.Unlock()
|
||||||
return
|
return
|
||||||
|
@ -345,14 +363,14 @@ func (p *PeriodicDispatch) dispatch(job *structs.Job, launchTime time.Time) {
|
||||||
|
|
||||||
if running {
|
if running {
|
||||||
msg := fmt.Sprintf("[DEBUG] nomad.periodic: skipping launch of"+
|
msg := fmt.Sprintf("[DEBUG] nomad.periodic: skipping launch of"+
|
||||||
" periodic job %q because job prohibits overlap", job.ID)
|
" periodic job %q (%s) because job prohibits overlap", job.ID, job.Namespace)
|
||||||
p.logger.Println(msg)
|
p.logger.Println(msg)
|
||||||
p.l.Unlock()
|
p.l.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.logger.Printf("[DEBUG] nomad.periodic: launching job %v at %v", job.ID, launchTime)
|
p.logger.Printf("[DEBUG] nomad.periodic: launching job %q (%v) at %v", job.ID, job.Namespace, launchTime)
|
||||||
p.l.Unlock()
|
p.l.Unlock()
|
||||||
p.createEval(job, launchTime)
|
p.createEval(job, launchTime)
|
||||||
}
|
}
|
||||||
|
@ -386,7 +404,8 @@ func (p *PeriodicDispatch) createEval(periodicJob *structs.Job, time time.Time)
|
||||||
|
|
||||||
eval, err := p.dispatcher.DispatchJob(derived)
|
eval, err := p.dispatcher.DispatchJob(derived)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Printf("[ERR] nomad.periodic: failed to dispatch job %q: %v", periodicJob.ID, err)
|
p.logger.Printf("[ERR] nomad.periodic: failed to dispatch job %q (%s): %v",
|
||||||
|
periodicJob.ID, periodicJob.Namespace, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,11 +421,13 @@ func (p *PeriodicDispatch) deriveJob(periodicJob *structs.Job, time time.Time) (
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
p.logger.Printf("[ERR] nomad.periodic: deriving job from"+
|
p.logger.Printf("[ERR] nomad.periodic: deriving job from"+
|
||||||
" periodic job %v failed; deregistering from periodic runner: %v",
|
" periodic job %q (%s) failed; deregistering from periodic runner: %v",
|
||||||
periodicJob.ID, r)
|
periodicJob.ID, periodicJob.Namespace, r)
|
||||||
p.Remove(periodicJob.ID)
|
|
||||||
|
p.Remove(periodicJob.Namespace, periodicJob.ID)
|
||||||
derived = nil
|
derived = nil
|
||||||
err = fmt.Errorf("Failed to create a copy of the periodic job %v: %v", periodicJob.ID, r)
|
err = fmt.Errorf("Failed to create a copy of the periodic job %q (%s): %v",
|
||||||
|
periodicJob.ID, periodicJob.Namespace, r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -445,14 +466,14 @@ func (p *PeriodicDispatch) LaunchTime(jobID string) (time.Time, error) {
|
||||||
// flush clears the state of the PeriodicDispatcher
|
// flush clears the state of the PeriodicDispatcher
|
||||||
func (p *PeriodicDispatch) flush() {
|
func (p *PeriodicDispatch) flush() {
|
||||||
p.updateCh = make(chan struct{}, 1)
|
p.updateCh = make(chan struct{}, 1)
|
||||||
p.tracked = make(map[string]*structs.Job)
|
p.tracked = make(map[structs.NamespacedID]*structs.Job)
|
||||||
p.heap = NewPeriodicHeap()
|
p.heap = NewPeriodicHeap()
|
||||||
p.stopFn = nil
|
p.stopFn = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// periodicHeap wraps a heap and gives operations other than Push/Pop.
|
// periodicHeap wraps a heap and gives operations other than Push/Pop.
|
||||||
type periodicHeap struct {
|
type periodicHeap struct {
|
||||||
index map[string]*periodicJob
|
index map[structs.NamespacedID]*periodicJob
|
||||||
heap periodicHeapImp
|
heap periodicHeapImp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,18 +485,22 @@ type periodicJob struct {
|
||||||
|
|
||||||
func NewPeriodicHeap() *periodicHeap {
|
func NewPeriodicHeap() *periodicHeap {
|
||||||
return &periodicHeap{
|
return &periodicHeap{
|
||||||
index: make(map[string]*periodicJob),
|
index: make(map[structs.NamespacedID]*periodicJob),
|
||||||
heap: make(periodicHeapImp, 0),
|
heap: make(periodicHeapImp, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *periodicHeap) Push(job *structs.Job, next time.Time) error {
|
func (p *periodicHeap) Push(job *structs.Job, next time.Time) error {
|
||||||
if _, ok := p.index[job.ID]; ok {
|
tuple := structs.NamespacedID{
|
||||||
return fmt.Errorf("job %v already exists", job.ID)
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, ok := p.index[tuple]; ok {
|
||||||
|
return fmt.Errorf("job %q (%s) already exists", job.ID, job.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
pJob := &periodicJob{job, next, 0}
|
pJob := &periodicJob{job, next, 0}
|
||||||
p.index[job.ID] = pJob
|
p.index[tuple] = pJob
|
||||||
heap.Push(&p.heap, pJob)
|
heap.Push(&p.heap, pJob)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -486,7 +511,11 @@ func (p *periodicHeap) Pop() *periodicJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
pJob := heap.Pop(&p.heap).(*periodicJob)
|
pJob := heap.Pop(&p.heap).(*periodicJob)
|
||||||
delete(p.index, pJob.job.ID)
|
tuple := structs.NamespacedID{
|
||||||
|
ID: pJob.job.ID,
|
||||||
|
Namespace: pJob.job.Namespace,
|
||||||
|
}
|
||||||
|
delete(p.index, tuple)
|
||||||
return pJob
|
return pJob
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,12 +528,20 @@ func (p *periodicHeap) Peek() *periodicJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *periodicHeap) Contains(job *structs.Job) bool {
|
func (p *periodicHeap) Contains(job *structs.Job) bool {
|
||||||
_, ok := p.index[job.ID]
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
_, ok := p.index[tuple]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *periodicHeap) Update(job *structs.Job, next time.Time) error {
|
func (p *periodicHeap) Update(job *structs.Job, next time.Time) error {
|
||||||
if pJob, ok := p.index[job.ID]; ok {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if pJob, ok := p.index[tuple]; ok {
|
||||||
// Need to update the job as well because its spec can change.
|
// Need to update the job as well because its spec can change.
|
||||||
pJob.job = job
|
pJob.job = job
|
||||||
pJob.next = next
|
pJob.next = next
|
||||||
|
@ -512,17 +549,21 @@ func (p *periodicHeap) Update(job *structs.Job, next time.Time) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("heap doesn't contain job %v", job.ID)
|
return fmt.Errorf("heap doesn't contain job %q (%s)", job.ID, job.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *periodicHeap) Remove(job *structs.Job) error {
|
func (p *periodicHeap) Remove(job *structs.Job) error {
|
||||||
if pJob, ok := p.index[job.ID]; ok {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if pJob, ok := p.index[tuple]; ok {
|
||||||
heap.Remove(&p.heap, pJob.index)
|
heap.Remove(&p.heap, pJob.index)
|
||||||
delete(p.index, job.ID)
|
delete(p.index, tuple)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("heap doesn't contain job %v", job.ID)
|
return fmt.Errorf("heap doesn't contain job %q (%s)", job.ID, job.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *periodicHeap) Length() int {
|
func (p *periodicHeap) Length() int {
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (p *Periodic) Force(args *structs.PeriodicForceRequest, reply *structs.Peri
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
job, err := snap.JobByID(ws, args.JobID)
|
job, err := snap.JobByID(ws, args.RequestNamespace(), args.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func (p *Periodic) Force(args *structs.PeriodicForceRequest, reply *structs.Peri
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force run the job.
|
// Force run the job.
|
||||||
eval, err := p.srv.periodicDispatcher.ForceRun(job.ID)
|
eval, err := p.srv.periodicDispatcher.ForceRun(args.RequestNamespace(), job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("force launch for job %q failed: %v", job.ID, err)
|
return fmt.Errorf("force launch for job %q failed: %v", job.ID, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,11 @@ func TestPeriodicEndpoint_Force(t *testing.T) {
|
||||||
|
|
||||||
// Force launch it.
|
// Force launch it.
|
||||||
req := &structs.PeriodicForceRequest{
|
req := &structs.PeriodicForceRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the response
|
// Fetch the response
|
||||||
|
@ -75,8 +78,11 @@ func TestPeriodicEndpoint_Force_NonPeriodic(t *testing.T) {
|
||||||
|
|
||||||
// Force launch it.
|
// Force launch it.
|
||||||
req := &structs.PeriodicForceRequest{
|
req := &structs.PeriodicForceRequest{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
WriteRequest: structs.WriteRequest{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the response
|
// Fetch the response
|
||||||
|
|
|
@ -16,21 +16,26 @@ import (
|
||||||
"github.com/hashicorp/nomad/nomad/mock"
|
"github.com/hashicorp/nomad/nomad/mock"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
"github.com/hashicorp/nomad/testutil"
|
"github.com/hashicorp/nomad/testutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockJobEvalDispatcher struct {
|
type MockJobEvalDispatcher struct {
|
||||||
Jobs map[string]*structs.Job
|
Jobs map[structs.NamespacedID]*structs.Job
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockJobEvalDispatcher() *MockJobEvalDispatcher {
|
func NewMockJobEvalDispatcher() *MockJobEvalDispatcher {
|
||||||
return &MockJobEvalDispatcher{Jobs: make(map[string]*structs.Job)}
|
return &MockJobEvalDispatcher{Jobs: make(map[structs.NamespacedID]*structs.Job)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockJobEvalDispatcher) DispatchJob(job *structs.Job) (*structs.Evaluation, error) {
|
func (m *MockJobEvalDispatcher) DispatchJob(job *structs.Job) (*structs.Evaluation, error) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
m.Jobs[job.ID] = job
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
m.Jobs[tuple] = job
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +43,7 @@ func (m *MockJobEvalDispatcher) RunningChildren(parent *structs.Job) (bool, erro
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
for _, job := range m.Jobs {
|
for _, job := range m.Jobs {
|
||||||
if job.ParentID == parent.ID {
|
if job.ParentID == parent.ID && job.Namespace == parent.Namespace {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,12 +51,12 @@ func (m *MockJobEvalDispatcher) RunningChildren(parent *structs.Job) (bool, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// LaunchTimes returns the launch times of child jobs in sorted order.
|
// LaunchTimes returns the launch times of child jobs in sorted order.
|
||||||
func (m *MockJobEvalDispatcher) LaunchTimes(p *PeriodicDispatch, parentID string) ([]time.Time, error) {
|
func (m *MockJobEvalDispatcher) LaunchTimes(p *PeriodicDispatch, namespace, parentID string) ([]time.Time, error) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
var launches []time.Time
|
var launches []time.Time
|
||||||
for _, job := range m.Jobs {
|
for _, job := range m.Jobs {
|
||||||
if job.ParentID != parentID {
|
if job.ParentID != parentID || job.Namespace != namespace {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +184,22 @@ func TestPeriodicDispatch_Add_UpdateJob(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPeriodicDispatch_Add_Remove_Namespaced(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
t.Parallel()
|
||||||
|
p, _ := testPeriodicDispatcher()
|
||||||
|
job := mock.PeriodicJob()
|
||||||
|
job2 := mock.PeriodicJob()
|
||||||
|
job2.Namespace = "test"
|
||||||
|
assert.Nil(p.Add(job))
|
||||||
|
assert.Nil(p.Add(job2))
|
||||||
|
assert.Len(p.Tracked(), 2)
|
||||||
|
|
||||||
|
assert.Nil(p.Remove(job2.Namespace, job2.ID))
|
||||||
|
assert.Len(p.Tracked(), 1)
|
||||||
|
assert.Equal(p.Tracked()[0], job)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPeriodicDispatch_Add_RemoveJob(t *testing.T) {
|
func TestPeriodicDispatch_Add_RemoveJob(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
p, _ := testPeriodicDispatcher()
|
p, _ := testPeriodicDispatcher()
|
||||||
|
@ -224,14 +245,18 @@ func TestPeriodicDispatch_Add_TriggersUpdate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that nothing is created.
|
// Check that nothing is created.
|
||||||
if _, ok := m.Jobs[job.ID]; ok {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, ok := m.Jobs[tuple]; ok {
|
||||||
t.Fatalf("periodic dispatcher created eval at the wrong time")
|
t.Fatalf("periodic dispatcher created eval at the wrong time")
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
// Check that job was launched correctly.
|
// Check that job was launched correctly.
|
||||||
times, err := m.LaunchTimes(p, job.ID)
|
times, err := m.LaunchTimes(p, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get launch times for job %q", job.ID)
|
t.Fatalf("failed to get launch times for job %q", job.ID)
|
||||||
}
|
}
|
||||||
|
@ -246,7 +271,7 @@ func TestPeriodicDispatch_Add_TriggersUpdate(t *testing.T) {
|
||||||
func TestPeriodicDispatch_Remove_Untracked(t *testing.T) {
|
func TestPeriodicDispatch_Remove_Untracked(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
p, _ := testPeriodicDispatcher()
|
p, _ := testPeriodicDispatcher()
|
||||||
if err := p.Remove("foo"); err != nil {
|
if err := p.Remove("ns", "foo"); err != nil {
|
||||||
t.Fatalf("Remove failed %v; expected a no-op", err)
|
t.Fatalf("Remove failed %v; expected a no-op", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,7 +290,7 @@ func TestPeriodicDispatch_Remove_Tracked(t *testing.T) {
|
||||||
t.Fatalf("Add didn't track the job: %v", tracked)
|
t.Fatalf("Add didn't track the job: %v", tracked)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.Remove(job.ID); err != nil {
|
if err := p.Remove(job.Namespace, job.ID); err != nil {
|
||||||
t.Fatalf("Remove failed %v", err)
|
t.Fatalf("Remove failed %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +313,7 @@ func TestPeriodicDispatch_Remove_TriggersUpdate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the job.
|
// Remove the job.
|
||||||
if err := p.Remove(job.ID); err != nil {
|
if err := p.Remove(job.Namespace, job.ID); err != nil {
|
||||||
t.Fatalf("Add failed %v", err)
|
t.Fatalf("Add failed %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +321,11 @@ func TestPeriodicDispatch_Remove_TriggersUpdate(t *testing.T) {
|
||||||
|
|
||||||
// Check that an eval wasn't created.
|
// Check that an eval wasn't created.
|
||||||
d := p.dispatcher.(*MockJobEvalDispatcher)
|
d := p.dispatcher.(*MockJobEvalDispatcher)
|
||||||
if _, ok := d.Jobs[job.ID]; ok {
|
tuple := structs.NamespacedID{
|
||||||
|
ID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
}
|
||||||
|
if _, ok := d.Jobs[tuple]; ok {
|
||||||
t.Fatalf("Remove didn't cancel creation of an eval")
|
t.Fatalf("Remove didn't cancel creation of an eval")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,7 +334,7 @@ func TestPeriodicDispatch_ForceRun_Untracked(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
p, _ := testPeriodicDispatcher()
|
p, _ := testPeriodicDispatcher()
|
||||||
|
|
||||||
if _, err := p.ForceRun("foo"); err == nil {
|
if _, err := p.ForceRun("ns", "foo"); err == nil {
|
||||||
t.Fatal("ForceRun of untracked job should fail")
|
t.Fatal("ForceRun of untracked job should fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,12 +352,12 @@ func TestPeriodicDispatch_ForceRun_Tracked(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForceRun the job
|
// ForceRun the job
|
||||||
if _, err := p.ForceRun(job.ID); err != nil {
|
if _, err := p.ForceRun(job.Namespace, job.ID); err != nil {
|
||||||
t.Fatalf("ForceRun failed %v", err)
|
t.Fatalf("ForceRun failed %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that job was launched correctly.
|
// Check that job was launched correctly.
|
||||||
launches, err := m.LaunchTimes(p, job.ID)
|
launches, err := m.LaunchTimes(p, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get launch times for job %q: %v", job.ID, err)
|
t.Fatalf("failed to get launch times for job %q: %v", job.ID, err)
|
||||||
}
|
}
|
||||||
|
@ -357,7 +386,7 @@ func TestPeriodicDispatch_Run_DisallowOverlaps(t *testing.T) {
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
// Check that only one job was launched.
|
// Check that only one job was launched.
|
||||||
times, err := m.LaunchTimes(p, job.ID)
|
times, err := m.LaunchTimes(p, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get launch times for job %q", job.ID)
|
t.Fatalf("failed to get launch times for job %q", job.ID)
|
||||||
}
|
}
|
||||||
|
@ -386,7 +415,7 @@ func TestPeriodicDispatch_Run_Multiple(t *testing.T) {
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
// Check that job was launched correctly.
|
// Check that job was launched correctly.
|
||||||
times, err := m.LaunchTimes(p, job.ID)
|
times, err := m.LaunchTimes(p, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get launch times for job %q", job.ID)
|
t.Fatalf("failed to get launch times for job %q", job.ID)
|
||||||
}
|
}
|
||||||
|
@ -418,11 +447,55 @@ func TestPeriodicDispatch_Run_SameTime(t *testing.T) {
|
||||||
t.Fatalf("Add failed %v", err)
|
t.Fatalf("Add failed %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if l := len(p.Tracked()); l != 2 {
|
||||||
|
t.Fatalf("got %d tracked; want 2", l)
|
||||||
|
}
|
||||||
|
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
// Check that the jobs were launched correctly.
|
// Check that the jobs were launched correctly.
|
||||||
for _, job := range []*structs.Job{job, job2} {
|
for _, job := range []*structs.Job{job, job2} {
|
||||||
times, err := m.LaunchTimes(p, job.ID)
|
times, err := m.LaunchTimes(p, job.Namespace, job.ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get launch times for job %q", job.ID)
|
||||||
|
}
|
||||||
|
if len(times) != 1 {
|
||||||
|
t.Fatalf("incorrect number of launch times for job %q; got %d; want 1", job.ID, len(times))
|
||||||
|
}
|
||||||
|
if times[0] != launch {
|
||||||
|
t.Fatalf("periodic dispatcher created eval for time %v; want %v", times[0], launch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeriodicDispatch_Run_SameID_Different_Namespace(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
p, m := testPeriodicDispatcher()
|
||||||
|
|
||||||
|
// Create two job that will be launched at the same time.
|
||||||
|
launch := time.Now().Round(1 * time.Second).Add(1 * time.Second)
|
||||||
|
job := testPeriodicJob(launch)
|
||||||
|
job2 := testPeriodicJob(launch)
|
||||||
|
job2.ID = job.ID
|
||||||
|
job2.Namespace = "test"
|
||||||
|
|
||||||
|
// Add them.
|
||||||
|
if err := p.Add(job); err != nil {
|
||||||
|
t.Fatalf("Add failed %v", err)
|
||||||
|
}
|
||||||
|
if err := p.Add(job2); err != nil {
|
||||||
|
t.Fatalf("Add failed %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := len(p.Tracked()); l != 2 {
|
||||||
|
t.Fatalf("got %d tracked; want 2", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
// Check that the jobs were launched correctly.
|
||||||
|
for _, job := range []*structs.Job{job, job2} {
|
||||||
|
times, err := m.LaunchTimes(p, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get launch times for job %q", job.ID)
|
t.Fatalf("failed to get launch times for job %q", job.ID)
|
||||||
}
|
}
|
||||||
|
@ -493,7 +566,7 @@ func TestPeriodicDispatch_Complex(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, job := range toDelete {
|
for _, job := range toDelete {
|
||||||
if err := p.Remove(job.ID); err != nil {
|
if err := p.Remove(job.Namespace, job.ID); err != nil {
|
||||||
t.Fatalf("Remove failed %v", err)
|
t.Fatalf("Remove failed %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,9 +574,9 @@ func TestPeriodicDispatch_Complex(t *testing.T) {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
actual := make(map[string][]time.Time, len(expected))
|
actual := make(map[string][]time.Time, len(expected))
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
launches, err := m.LaunchTimes(p, job.ID)
|
launches, err := m.LaunchTimes(p, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("LaunchTimes(%v) failed %v", job.ID, err)
|
t.Fatalf("LaunchTimes(%v, %v) failed %v", job.Namespace, job.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual[job.ID] = launches
|
actual[job.ID] = launches
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package nomad
|
package nomad
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
memdb "github.com/hashicorp/go-memdb"
|
memdb "github.com/hashicorp/go-memdb"
|
||||||
|
@ -19,6 +18,11 @@ var (
|
||||||
// allContexts are the available contexts which are searched to find matches
|
// allContexts are the available contexts which are searched to find matches
|
||||||
// for a given prefix
|
// for a given prefix
|
||||||
allContexts = []structs.Context{structs.Allocs, structs.Jobs, structs.Nodes,
|
allContexts = []structs.Context{structs.Allocs, structs.Jobs, structs.Nodes,
|
||||||
|
structs.Evals, structs.Deployments, structs.Namespaces}
|
||||||
|
|
||||||
|
// ossContexts are the oss contexts which are searched to find matches
|
||||||
|
// for a given prefix
|
||||||
|
ossContexts = []structs.Context{structs.Allocs, structs.Jobs, structs.Nodes,
|
||||||
structs.Evals, structs.Deployments}
|
structs.Evals, structs.Deployments}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,8 +55,13 @@ func (s *Search) getMatches(iter memdb.ResultIterator, prefix string) ([]string,
|
||||||
case *structs.Deployment:
|
case *structs.Deployment:
|
||||||
id = raw.(*structs.Deployment).ID
|
id = raw.(*structs.Deployment).ID
|
||||||
default:
|
default:
|
||||||
s.srv.logger.Printf("[ERR] nomad.resources: unexpected type for resources context: %T", t)
|
matchID, ok := getEnterpriseMatch(raw)
|
||||||
continue
|
if !ok {
|
||||||
|
s.srv.logger.Printf("[ERR] nomad.resources: unexpected type for resources context: %T", t)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id = matchID
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(id, prefix) {
|
if !strings.HasPrefix(id, prefix) {
|
||||||
|
@ -67,20 +76,20 @@ func (s *Search) getMatches(iter memdb.ResultIterator, prefix string) ([]string,
|
||||||
|
|
||||||
// getResourceIter takes a context and returns a memdb iterator specific to
|
// getResourceIter takes a context and returns a memdb iterator specific to
|
||||||
// that context
|
// that context
|
||||||
func getResourceIter(context structs.Context, prefix string, ws memdb.WatchSet, state *state.StateStore) (memdb.ResultIterator, error) {
|
func getResourceIter(context structs.Context, namespace, prefix string, ws memdb.WatchSet, state *state.StateStore) (memdb.ResultIterator, error) {
|
||||||
switch context {
|
switch context {
|
||||||
case structs.Jobs:
|
case structs.Jobs:
|
||||||
return state.JobsByIDPrefix(ws, prefix)
|
return state.JobsByIDPrefix(ws, namespace, prefix)
|
||||||
case structs.Evals:
|
case structs.Evals:
|
||||||
return state.EvalsByIDPrefix(ws, prefix)
|
return state.EvalsByIDPrefix(ws, namespace, prefix)
|
||||||
case structs.Allocs:
|
case structs.Allocs:
|
||||||
return state.AllocsByIDPrefix(ws, prefix)
|
return state.AllocsByIDPrefix(ws, namespace, prefix)
|
||||||
case structs.Nodes:
|
case structs.Nodes:
|
||||||
return state.NodesByIDPrefix(ws, prefix)
|
return state.NodesByIDPrefix(ws, prefix)
|
||||||
case structs.Deployments:
|
case structs.Deployments:
|
||||||
return state.DeploymentsByIDPrefix(ws, prefix)
|
return state.DeploymentsByIDPrefix(ws, namespace, prefix)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("context must be one of %v or 'all' for all contexts; got %q", allContexts, context)
|
return getEnterpriseResourceIter(context, namespace, prefix, ws, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +131,7 @@ func (s *Search) PrefixSearch(args *structs.SearchRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ctx := range contexts {
|
for _, ctx := range contexts {
|
||||||
iter, err := getResourceIter(ctx, roundUUIDDownIfOdd(args.Prefix, args.Context), ws, state)
|
iter, err := getResourceIter(ctx, args.RequestNamespace(), roundUUIDDownIfOdd(args.Prefix, args.Context), ws, state)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := err.Error()
|
e := err.Error()
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// +build !pro,!ent
|
||||||
|
|
||||||
|
package nomad
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
memdb "github.com/hashicorp/go-memdb"
|
||||||
|
"github.com/hashicorp/nomad/nomad/state"
|
||||||
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getEnterpriseMatch is a no-op in oss since there are no enterprise objects.
|
||||||
|
func getEnterpriseMatch(match interface{}) (id string, ok bool) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnterpriseResourceIter is used to retrieve an iterator over an enterprise
|
||||||
|
// only table.
|
||||||
|
func getEnterpriseResourceIter(context structs.Context, namespace, prefix string, ws memdb.WatchSet, state *state.StateStore) (memdb.ResultIterator, error) {
|
||||||
|
// If we have made it here then it is an error since we have exhausted all
|
||||||
|
// open source contexts.
|
||||||
|
return nil, fmt.Errorf("context must be one of %v or 'all' for all contexts; got %q", ossContexts, context)
|
||||||
|
}
|
|
@ -14,16 +14,15 @@ import (
|
||||||
|
|
||||||
const jobIndex = 1000
|
const jobIndex = 1000
|
||||||
|
|
||||||
func registerAndVerifyJob(s *Server, t *testing.T, prefix string, counter int) string {
|
func registerAndVerifyJob(s *Server, t *testing.T, prefix string, counter int) *structs.Job {
|
||||||
job := mock.Job()
|
job := mock.Job()
|
||||||
|
|
||||||
job.ID = prefix + strconv.Itoa(counter)
|
job.ID = prefix + strconv.Itoa(counter)
|
||||||
state := s.fsm.State()
|
state := s.fsm.State()
|
||||||
if err := state.UpsertJob(jobIndex, job); err != nil {
|
if err := state.UpsertJob(jobIndex, job); err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return job.ID
|
return job
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearch_PrefixSearch_Job(t *testing.T) {
|
func TestSearch_PrefixSearch_Job(t *testing.T) {
|
||||||
|
@ -39,11 +38,15 @@ func TestSearch_PrefixSearch_Job(t *testing.T) {
|
||||||
codec := rpcClient(t, s)
|
codec := rpcClient(t, s)
|
||||||
testutil.WaitForLeader(t, s.RPC)
|
testutil.WaitForLeader(t, s.RPC)
|
||||||
|
|
||||||
jobID := registerAndVerifyJob(s, t, prefix, 0)
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
||||||
|
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Jobs,
|
Context: structs.Jobs,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -52,7 +55,7 @@ func TestSearch_PrefixSearch_Job(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
||||||
assert.Equal(jobID, resp.Matches[structs.Jobs][0])
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
||||||
assert.Equal(uint64(jobIndex), resp.Index)
|
assert.Equal(uint64(jobIndex), resp.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,9 +73,10 @@ func TestSearch_PrefixSearch_All_JobWithHyphen(t *testing.T) {
|
||||||
testutil.WaitForLeader(t, s.RPC)
|
testutil.WaitForLeader(t, s.RPC)
|
||||||
|
|
||||||
// Register a job and an allocation
|
// Register a job and an allocation
|
||||||
jobID := registerAndVerifyJob(s, t, prefix, 0)
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
||||||
alloc := mock.Alloc()
|
alloc := mock.Alloc()
|
||||||
alloc.JobID = jobID
|
alloc.JobID = job.ID
|
||||||
|
alloc.Namespace = job.Namespace
|
||||||
summary := mock.JobSummary(alloc.JobID)
|
summary := mock.JobSummary(alloc.JobID)
|
||||||
state := s.fsm.State()
|
state := s.fsm.State()
|
||||||
|
|
||||||
|
@ -86,6 +90,10 @@ func TestSearch_PrefixSearch_All_JobWithHyphen(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: "example-",
|
Prefix: "example-",
|
||||||
Context: structs.All,
|
Context: structs.All,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -94,7 +102,7 @@ func TestSearch_PrefixSearch_All_JobWithHyphen(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
||||||
assert.Equal(jobID, resp.Matches[structs.Jobs][0])
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
||||||
assert.EqualValues(jobIndex, resp.Index)
|
assert.EqualValues(jobIndex, resp.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,9 +120,9 @@ func TestSearch_PrefixSearch_All_LongJob(t *testing.T) {
|
||||||
testutil.WaitForLeader(t, s.RPC)
|
testutil.WaitForLeader(t, s.RPC)
|
||||||
|
|
||||||
// Register a job and an allocation
|
// Register a job and an allocation
|
||||||
jobID := registerAndVerifyJob(s, t, prefix, 0)
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
||||||
alloc := mock.Alloc()
|
alloc := mock.Alloc()
|
||||||
alloc.JobID = jobID
|
alloc.JobID = job.ID
|
||||||
summary := mock.JobSummary(alloc.JobID)
|
summary := mock.JobSummary(alloc.JobID)
|
||||||
state := s.fsm.State()
|
state := s.fsm.State()
|
||||||
|
|
||||||
|
@ -128,6 +136,10 @@ func TestSearch_PrefixSearch_All_LongJob(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.All,
|
Context: structs.All,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -136,7 +148,7 @@ func TestSearch_PrefixSearch_All_LongJob(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
||||||
assert.Equal(jobID, resp.Matches[structs.Jobs][0])
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
||||||
assert.EqualValues(jobIndex, resp.Index)
|
assert.EqualValues(jobIndex, resp.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,13 +166,18 @@ func TestSearch_PrefixSearch_Truncate(t *testing.T) {
|
||||||
codec := rpcClient(t, s)
|
codec := rpcClient(t, s)
|
||||||
testutil.WaitForLeader(t, s.RPC)
|
testutil.WaitForLeader(t, s.RPC)
|
||||||
|
|
||||||
|
var job *structs.Job
|
||||||
for counter := 0; counter < 25; counter++ {
|
for counter := 0; counter < 25; counter++ {
|
||||||
registerAndVerifyJob(s, t, prefix, counter)
|
job = registerAndVerifyJob(s, t, prefix, counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Jobs,
|
Context: structs.Jobs,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -186,15 +203,19 @@ func TestSearch_PrefixSearch_AllWithJob(t *testing.T) {
|
||||||
codec := rpcClient(t, s)
|
codec := rpcClient(t, s)
|
||||||
testutil.WaitForLeader(t, s.RPC)
|
testutil.WaitForLeader(t, s.RPC)
|
||||||
|
|
||||||
jobID := registerAndVerifyJob(s, t, prefix, 0)
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
||||||
|
|
||||||
eval1 := mock.Eval()
|
eval1 := mock.Eval()
|
||||||
eval1.ID = jobID
|
eval1.ID = job.ID
|
||||||
s.fsm.State().UpsertEvals(2000, []*structs.Evaluation{eval1})
|
s.fsm.State().UpsertEvals(2000, []*structs.Evaluation{eval1})
|
||||||
|
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.All,
|
Context: structs.All,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -203,7 +224,7 @@ func TestSearch_PrefixSearch_AllWithJob(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
||||||
assert.Equal(jobID, resp.Matches[structs.Jobs][0])
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
||||||
|
|
||||||
assert.Equal(1, len(resp.Matches[structs.Evals]))
|
assert.Equal(1, len(resp.Matches[structs.Evals]))
|
||||||
assert.Equal(eval1.ID, resp.Matches[structs.Evals][0])
|
assert.Equal(eval1.ID, resp.Matches[structs.Evals][0])
|
||||||
|
@ -228,6 +249,10 @@ func TestSearch_PrefixSearch_Evals(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Evals,
|
Context: structs.Evals,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: eval1.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -269,6 +294,10 @@ func TestSearch_PrefixSearch_Allocation(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Allocs,
|
Context: structs.Allocs,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: alloc.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -322,6 +351,10 @@ func TestSearch_PrefixSearch_All_UUID_EvenPrefix(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.All,
|
Context: structs.All,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: eval1.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -359,6 +392,10 @@ func TestSearch_PrefixSearch_Node(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Nodes,
|
Context: structs.Nodes,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -392,6 +429,10 @@ func TestSearch_PrefixSearch_Deployment(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Deployments,
|
Context: structs.Deployments,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: deployment.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -435,6 +476,10 @@ func TestSearch_PrefixSearch_AllContext(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.All,
|
Context: structs.All,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: eval1.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -466,11 +511,15 @@ func TestSearch_PrefixSearch_NoPrefix(t *testing.T) {
|
||||||
codec := rpcClient(t, s)
|
codec := rpcClient(t, s)
|
||||||
testutil.WaitForLeader(t, s.RPC)
|
testutil.WaitForLeader(t, s.RPC)
|
||||||
|
|
||||||
jobID := registerAndVerifyJob(s, t, prefix, 0)
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
||||||
|
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: "",
|
Prefix: "",
|
||||||
Context: structs.Jobs,
|
Context: structs.Jobs,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -479,7 +528,7 @@ func TestSearch_PrefixSearch_NoPrefix(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
||||||
assert.Equal(jobID, resp.Matches[structs.Jobs][0])
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
||||||
assert.Equal(uint64(jobIndex), resp.Index)
|
assert.Equal(uint64(jobIndex), resp.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,6 +551,10 @@ func TestSearch_PrefixSearch_NoMatches(t *testing.T) {
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Jobs,
|
Context: structs.Jobs,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -530,12 +583,16 @@ func TestSearch_PrefixSearch_RoundDownToEven(t *testing.T) {
|
||||||
codec := rpcClient(t, s)
|
codec := rpcClient(t, s)
|
||||||
testutil.WaitForLeader(t, s.RPC)
|
testutil.WaitForLeader(t, s.RPC)
|
||||||
|
|
||||||
jobID1 := registerAndVerifyJob(s, t, id1, 0)
|
job := registerAndVerifyJob(s, t, id1, 0)
|
||||||
registerAndVerifyJob(s, t, id2, 50)
|
registerAndVerifyJob(s, t, id2, 50)
|
||||||
|
|
||||||
req := &structs.SearchRequest{
|
req := &structs.SearchRequest{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
Context: structs.Jobs,
|
Context: structs.Jobs,
|
||||||
|
QueryOptions: structs.QueryOptions{
|
||||||
|
Region: "global",
|
||||||
|
Namespace: job.Namespace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp structs.SearchResponse
|
var resp structs.SearchResponse
|
||||||
|
@ -544,5 +601,5 @@ func TestSearch_PrefixSearch_RoundDownToEven(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
||||||
assert.Equal(jobID1, resp.Matches[structs.Jobs][0])
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,6 +187,7 @@ type endpoints struct {
|
||||||
System *System
|
System *System
|
||||||
Operator *Operator
|
Operator *Operator
|
||||||
ACL *ACL
|
ACL *ACL
|
||||||
|
Enterprise *EnterpriseEndpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer is used to construct a new Nomad server from the
|
// NewServer is used to construct a new Nomad server from the
|
||||||
|
@ -736,6 +737,7 @@ func (s *Server) setupRPC(tlsWrap tlsutil.RegionWrapper) error {
|
||||||
s.endpoints.Status = &Status{s}
|
s.endpoints.Status = &Status{s}
|
||||||
s.endpoints.System = &System{s}
|
s.endpoints.System = &System{s}
|
||||||
s.endpoints.Search = &Search{s}
|
s.endpoints.Search = &Search{s}
|
||||||
|
s.endpoints.Enterprise = NewEnterpriseEndpoints(s)
|
||||||
|
|
||||||
// Register the handlers
|
// Register the handlers
|
||||||
s.rpcServer.Register(s.endpoints.ACL)
|
s.rpcServer.Register(s.endpoints.ACL)
|
||||||
|
@ -751,6 +753,7 @@ func (s *Server) setupRPC(tlsWrap tlsutil.RegionWrapper) error {
|
||||||
s.rpcServer.Register(s.endpoints.Status)
|
s.rpcServer.Register(s.endpoints.Status)
|
||||||
s.rpcServer.Register(s.endpoints.System)
|
s.rpcServer.Register(s.endpoints.System)
|
||||||
s.rpcServer.Register(s.endpoints.Search)
|
s.rpcServer.Register(s.endpoints.Search)
|
||||||
|
s.endpoints.Enterprise.Register(s)
|
||||||
|
|
||||||
list, err := net.ListenTCP("tcp", s.config.RPCAddr)
|
list, err := net.ListenTCP("tcp", s.config.RPCAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -57,7 +57,7 @@ func testACLServer(t *testing.T, cb func(*Config)) (*Server, *structs.ACLToken)
|
||||||
func testServer(t *testing.T, cb func(*Config)) *Server {
|
func testServer(t *testing.T, cb func(*Config)) *Server {
|
||||||
// Setup the default settings
|
// Setup the default settings
|
||||||
config := DefaultConfig()
|
config := DefaultConfig()
|
||||||
config.Build = "unittest"
|
config.Build = "0.7.0+unittest"
|
||||||
config.DevMode = true
|
config.DevMode = true
|
||||||
nodeNum := atomic.AddUint32(&nodeNumber, 1)
|
nodeNum := atomic.AddUint32(&nodeNumber, 1)
|
||||||
config.NodeName = fmt.Sprintf("nomad-%03d", nodeNum)
|
config.NodeName = fmt.Sprintf("nomad-%03d", nodeNum)
|
||||||
|
|
|
@ -2,20 +2,35 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/go-memdb"
|
"github.com/hashicorp/go-memdb"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// stateStoreSchema is used to return the schema for the state store
|
var (
|
||||||
func stateStoreSchema() *memdb.DBSchema {
|
schemaFactories SchemaFactories
|
||||||
// Create the root DB schema
|
factoriesLock sync.Mutex
|
||||||
db := &memdb.DBSchema{
|
)
|
||||||
Tables: make(map[string]*memdb.TableSchema),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect all the schemas that are needed
|
// SchemaFactory is the factory method for returning a TableSchema
|
||||||
schemas := []func() *memdb.TableSchema{
|
type SchemaFactory func() *memdb.TableSchema
|
||||||
|
type SchemaFactories []SchemaFactory
|
||||||
|
|
||||||
|
// RegisterSchemaFactories is used to register a table schema.
|
||||||
|
func RegisterSchemaFactories(factories ...SchemaFactory) {
|
||||||
|
factoriesLock.Lock()
|
||||||
|
defer factoriesLock.Unlock()
|
||||||
|
schemaFactories = append(schemaFactories, factories...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFactories() SchemaFactories {
|
||||||
|
return schemaFactories
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Register all schemas
|
||||||
|
RegisterSchemaFactories([]SchemaFactory{
|
||||||
indexTableSchema,
|
indexTableSchema,
|
||||||
nodeTableSchema,
|
nodeTableSchema,
|
||||||
jobTableSchema,
|
jobTableSchema,
|
||||||
|
@ -28,10 +43,18 @@ func stateStoreSchema() *memdb.DBSchema {
|
||||||
vaultAccessorTableSchema,
|
vaultAccessorTableSchema,
|
||||||
aclPolicyTableSchema,
|
aclPolicyTableSchema,
|
||||||
aclTokenTableSchema,
|
aclTokenTableSchema,
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateStoreSchema is used to return the schema for the state store
|
||||||
|
func stateStoreSchema() *memdb.DBSchema {
|
||||||
|
// Create the root DB schema
|
||||||
|
db := &memdb.DBSchema{
|
||||||
|
Tables: make(map[string]*memdb.TableSchema),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add each of the tables
|
// Add each of the tables
|
||||||
for _, schemaFn := range schemas {
|
for _, schemaFn := range GetFactories() {
|
||||||
schema := schemaFn()
|
schema := schemaFn()
|
||||||
if _, ok := db.Tables[schema.Name]; ok {
|
if _, ok := db.Tables[schema.Name]; ok {
|
||||||
panic(fmt.Sprintf("duplicate table name: %s", schema.Name))
|
panic(fmt.Sprintf("duplicate table name: %s", schema.Name))
|
||||||
|
@ -88,14 +111,24 @@ func jobTableSchema() *memdb.TableSchema {
|
||||||
Indexes: map[string]*memdb.IndexSchema{
|
Indexes: map[string]*memdb.IndexSchema{
|
||||||
// Primary index is used for job management
|
// Primary index is used for job management
|
||||||
// and simple direct lookup. ID is required to be
|
// and simple direct lookup. ID is required to be
|
||||||
// unique.
|
// unique within a namespace.
|
||||||
"id": &memdb.IndexSchema{
|
"id": &memdb.IndexSchema{
|
||||||
Name: "id",
|
Name: "id",
|
||||||
AllowMissing: false,
|
AllowMissing: false,
|
||||||
Unique: true,
|
Unique: true,
|
||||||
Indexer: &memdb.StringFieldIndex{
|
|
||||||
Field: "ID",
|
// Use a compound index so the tuple of (Namespace, ID) is
|
||||||
Lowercase: true,
|
// uniquely identifying
|
||||||
|
Indexer: &memdb.CompoundIndex{
|
||||||
|
Indexes: []memdb.Indexer{
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "ID",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"type": &memdb.IndexSchema{
|
"type": &memdb.IndexSchema{
|
||||||
|
@ -136,9 +169,19 @@ func jobSummarySchema() *memdb.TableSchema {
|
||||||
Name: "id",
|
Name: "id",
|
||||||
AllowMissing: false,
|
AllowMissing: false,
|
||||||
Unique: true,
|
Unique: true,
|
||||||
Indexer: &memdb.StringFieldIndex{
|
|
||||||
Field: "JobID",
|
// Use a compound index so the tuple of (Namespace, JobID) is
|
||||||
Lowercase: true,
|
// uniquely identifying
|
||||||
|
Indexer: &memdb.CompoundIndex{
|
||||||
|
Indexes: []memdb.Indexer{
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "JobID",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -156,16 +199,19 @@ func jobVersionSchema() *memdb.TableSchema {
|
||||||
AllowMissing: false,
|
AllowMissing: false,
|
||||||
Unique: true,
|
Unique: true,
|
||||||
|
|
||||||
// Use a compound index so the tuple of (JobID, Version) is
|
// Use a compound index so the tuple of (Namespace, ID, Version) is
|
||||||
// uniquely identifying
|
// uniquely identifying
|
||||||
Indexer: &memdb.CompoundIndex{
|
Indexer: &memdb.CompoundIndex{
|
||||||
Indexes: []memdb.Indexer{
|
Indexes: []memdb.Indexer{
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
|
||||||
&memdb.StringFieldIndex{
|
&memdb.StringFieldIndex{
|
||||||
Field: "ID",
|
Field: "ID",
|
||||||
Lowercase: true,
|
Lowercase: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Will need to create a new indexer
|
|
||||||
&memdb.UintFieldIndex{
|
&memdb.UintFieldIndex{
|
||||||
Field: "Version",
|
Field: "Version",
|
||||||
},
|
},
|
||||||
|
@ -240,14 +286,33 @@ func deploymentSchema() *memdb.TableSchema {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"namespace": &memdb.IndexSchema{
|
||||||
|
Name: "namespace",
|
||||||
|
AllowMissing: false,
|
||||||
|
Unique: false,
|
||||||
|
Indexer: &memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Job index is used to lookup deployments by job
|
// Job index is used to lookup deployments by job
|
||||||
"job": &memdb.IndexSchema{
|
"job": &memdb.IndexSchema{
|
||||||
Name: "job",
|
Name: "job",
|
||||||
AllowMissing: false,
|
AllowMissing: false,
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Indexer: &memdb.StringFieldIndex{
|
|
||||||
Field: "JobID",
|
// Use a compound index so the tuple of (Namespace, JobID) is
|
||||||
Lowercase: true,
|
// uniquely identifying
|
||||||
|
Indexer: &memdb.CompoundIndex{
|
||||||
|
Indexes: []memdb.Indexer{
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "JobID",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -267,9 +332,19 @@ func periodicLaunchTableSchema() *memdb.TableSchema {
|
||||||
Name: "id",
|
Name: "id",
|
||||||
AllowMissing: false,
|
AllowMissing: false,
|
||||||
Unique: true,
|
Unique: true,
|
||||||
Indexer: &memdb.StringFieldIndex{
|
|
||||||
Field: "ID",
|
// Use a compound index so the tuple of (Namespace, JobID) is
|
||||||
Lowercase: true,
|
// uniquely identifying
|
||||||
|
Indexer: &memdb.CompoundIndex{
|
||||||
|
Indexes: []memdb.Indexer{
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "ID",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -293,6 +368,15 @@ func evalTableSchema() *memdb.TableSchema {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"namespace": &memdb.IndexSchema{
|
||||||
|
Name: "namespace",
|
||||||
|
AllowMissing: false,
|
||||||
|
Unique: false,
|
||||||
|
Indexer: &memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Job index is used to lookup allocations by job
|
// Job index is used to lookup allocations by job
|
||||||
"job": &memdb.IndexSchema{
|
"job": &memdb.IndexSchema{
|
||||||
Name: "job",
|
Name: "job",
|
||||||
|
@ -300,10 +384,15 @@ func evalTableSchema() *memdb.TableSchema {
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Indexer: &memdb.CompoundIndex{
|
Indexer: &memdb.CompoundIndex{
|
||||||
Indexes: []memdb.Indexer{
|
Indexes: []memdb.Indexer{
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
|
||||||
&memdb.StringFieldIndex{
|
&memdb.StringFieldIndex{
|
||||||
Field: "JobID",
|
Field: "JobID",
|
||||||
Lowercase: true,
|
Lowercase: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
&memdb.StringFieldIndex{
|
&memdb.StringFieldIndex{
|
||||||
Field: "Status",
|
Field: "Status",
|
||||||
Lowercase: true,
|
Lowercase: true,
|
||||||
|
@ -332,6 +421,15 @@ func allocTableSchema() *memdb.TableSchema {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"namespace": &memdb.IndexSchema{
|
||||||
|
Name: "namespace",
|
||||||
|
AllowMissing: false,
|
||||||
|
Unique: false,
|
||||||
|
Indexer: &memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Node index is used to lookup allocations by node
|
// Node index is used to lookup allocations by node
|
||||||
"node": &memdb.IndexSchema{
|
"node": &memdb.IndexSchema{
|
||||||
Name: "node",
|
Name: "node",
|
||||||
|
@ -366,9 +464,17 @@ func allocTableSchema() *memdb.TableSchema {
|
||||||
Name: "job",
|
Name: "job",
|
||||||
AllowMissing: false,
|
AllowMissing: false,
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Indexer: &memdb.StringFieldIndex{
|
|
||||||
Field: "JobID",
|
Indexer: &memdb.CompoundIndex{
|
||||||
Lowercase: true,
|
Indexes: []memdb.Indexer{
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "Namespace",
|
||||||
|
},
|
||||||
|
|
||||||
|
&memdb.StringFieldIndex{
|
||||||
|
Field: "JobID",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -197,8 +197,13 @@ func (s *StateStore) UpsertJobSummary(index uint64, jobSummary *structs.JobSumma
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
|
||||||
|
// TODO(alex): Remove before releasing
|
||||||
|
if jobSummary.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the job summary already exists
|
// Check if the job summary already exists
|
||||||
existing, err := txn.First("job_summary", "id", jobSummary.JobID)
|
existing, err := txn.First("job_summary", "id", jobSummary.Namespace, jobSummary.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("job summary lookup failed: %v", err)
|
return fmt.Errorf("job summary lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -228,12 +233,16 @@ func (s *StateStore) UpsertJobSummary(index uint64, jobSummary *structs.JobSumma
|
||||||
|
|
||||||
// DeleteJobSummary deletes the job summary with the given ID. This is for
|
// DeleteJobSummary deletes the job summary with the given ID. This is for
|
||||||
// testing purposes only.
|
// testing purposes only.
|
||||||
func (s *StateStore) DeleteJobSummary(index uint64, id string) error {
|
func (s *StateStore) DeleteJobSummary(index uint64, namespace, id string) error {
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the job summary
|
// Delete the job summary
|
||||||
if _, err := txn.DeleteAll("job_summary", "id", id); err != nil {
|
if _, err := txn.DeleteAll("job_summary", "id", namespace, id); err != nil {
|
||||||
return fmt.Errorf("deleting job summary failed: %v", err)
|
return fmt.Errorf("deleting job summary failed: %v", err)
|
||||||
}
|
}
|
||||||
if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
|
if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
|
||||||
|
@ -283,7 +292,7 @@ func (s *StateStore) upsertDeploymentImpl(index uint64, deployment *structs.Depl
|
||||||
|
|
||||||
// If the deployment is being marked as complete, set the job to stable.
|
// If the deployment is being marked as complete, set the job to stable.
|
||||||
if deployment.Status == structs.DeploymentStatusSuccessful {
|
if deployment.Status == structs.DeploymentStatusSuccessful {
|
||||||
if err := s.updateJobStabilityImpl(index, deployment.JobID, deployment.JobVersion, true, txn); err != nil {
|
if err := s.updateJobStabilityImpl(index, deployment.Namespace, deployment.JobID, deployment.JobVersion, true, txn); err != nil {
|
||||||
return fmt.Errorf("failed to update job stability: %v", err)
|
return fmt.Errorf("failed to update job stability: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,7 +313,20 @@ func (s *StateStore) Deployments(ws memdb.WatchSet) (memdb.ResultIterator, error
|
||||||
return iter, nil
|
return iter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateStore) DeploymentsByIDPrefix(ws memdb.WatchSet, deploymentID string) (memdb.ResultIterator, error) {
|
func (s *StateStore) DeploymentsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) {
|
||||||
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
// Walk the entire deployments table
|
||||||
|
iter, err := txn.Get("deployment", "namespace", namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.Add(iter.WatchCh())
|
||||||
|
return iter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateStore) DeploymentsByIDPrefix(ws memdb.WatchSet, namespace, deploymentID string) (memdb.ResultIterator, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
// Walk the entire deployments table
|
// Walk the entire deployments table
|
||||||
|
@ -314,7 +336,23 @@ func (s *StateStore) DeploymentsByIDPrefix(ws memdb.WatchSet, deploymentID strin
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.Add(iter.WatchCh())
|
ws.Add(iter.WatchCh())
|
||||||
return iter, nil
|
|
||||||
|
// Wrap the iterator in a filter
|
||||||
|
wrap := memdb.NewFilterIterator(iter, deploymentNamespaceFilter(namespace))
|
||||||
|
return wrap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deploymentNamespaceFilter returns a filter function that filters all
|
||||||
|
// deployment not in the given namespace.
|
||||||
|
func deploymentNamespaceFilter(namespace string) func(interface{}) bool {
|
||||||
|
return func(raw interface{}) bool {
|
||||||
|
d, ok := raw.(*structs.Deployment)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.Namespace != namespace
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateStore) DeploymentByID(ws memdb.WatchSet, deploymentID string) (*structs.Deployment, error) {
|
func (s *StateStore) DeploymentByID(ws memdb.WatchSet, deploymentID string) (*structs.Deployment, error) {
|
||||||
|
@ -336,11 +374,15 @@ func (s *StateStore) deploymentByIDImpl(ws memdb.WatchSet, deploymentID string,
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateStore) DeploymentsByJobID(ws memdb.WatchSet, jobID string) ([]*structs.Deployment, error) {
|
func (s *StateStore) DeploymentsByJobID(ws memdb.WatchSet, namespace, jobID string) ([]*structs.Deployment, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// Get an iterator over the deployments
|
// Get an iterator over the deployments
|
||||||
iter, err := txn.Get("deployment", "job", jobID)
|
iter, err := txn.Get("deployment", "job", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -363,11 +405,15 @@ func (s *StateStore) DeploymentsByJobID(ws memdb.WatchSet, jobID string) ([]*str
|
||||||
|
|
||||||
// LatestDeploymentByJobID returns the latest deployment for the given job. The
|
// LatestDeploymentByJobID returns the latest deployment for the given job. The
|
||||||
// latest is determined strictly by CreateIndex.
|
// latest is determined strictly by CreateIndex.
|
||||||
func (s *StateStore) LatestDeploymentByJobID(ws memdb.WatchSet, jobID string) (*structs.Deployment, error) {
|
func (s *StateStore) LatestDeploymentByJobID(ws memdb.WatchSet, namespace, jobID string) (*structs.Deployment, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// Get an iterator over the deployments
|
// Get an iterator over the deployments
|
||||||
iter, err := txn.Get("deployment", "job", jobID)
|
iter, err := txn.Get("deployment", "job", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -610,8 +656,11 @@ func (s *StateStore) UpsertJob(index uint64, job *structs.Job) error {
|
||||||
|
|
||||||
// upsertJobImpl is the implementation for registering a job or updating a job definition
|
// upsertJobImpl is the implementation for registering a job or updating a job definition
|
||||||
func (s *StateStore) upsertJobImpl(index uint64, job *structs.Job, keepVersion bool, txn *memdb.Txn) error {
|
func (s *StateStore) upsertJobImpl(index uint64, job *structs.Job, keepVersion bool, txn *memdb.Txn) error {
|
||||||
|
if job.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Check if the job already exists
|
// Check if the job already exists
|
||||||
existing, err := txn.First("jobs", "id", job.ID)
|
existing, err := txn.First("jobs", "id", job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("job lookup failed: %v", err)
|
return fmt.Errorf("job lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -646,7 +695,7 @@ func (s *StateStore) upsertJobImpl(index uint64, job *structs.Job, keepVersion b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have to get the job again since it could have been updated
|
// Have to get the job again since it could have been updated
|
||||||
updated, err := txn.First("jobs", "id", job.ID)
|
updated, err := txn.First("jobs", "id", job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("job lookup failed: %v", err)
|
return fmt.Errorf("job lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -679,12 +728,15 @@ func (s *StateStore) upsertJobImpl(index uint64, job *structs.Job, keepVersion b
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteJob is used to deregister a job
|
// DeleteJob is used to deregister a job
|
||||||
func (s *StateStore) DeleteJob(index uint64, jobID string) error {
|
func (s *StateStore) DeleteJob(index uint64, namespace, jobID string) error {
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Lookup the node
|
// Lookup the node
|
||||||
existing, err := txn.First("jobs", "id", jobID)
|
existing, err := txn.First("jobs", "id", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("job lookup failed: %v", err)
|
return fmt.Errorf("job lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -695,7 +747,7 @@ func (s *StateStore) DeleteJob(index uint64, jobID string) error {
|
||||||
// Check if we should update a parent job summary
|
// Check if we should update a parent job summary
|
||||||
job := existing.(*structs.Job)
|
job := existing.(*structs.Job)
|
||||||
if job.ParentID != "" {
|
if job.ParentID != "" {
|
||||||
summaryRaw, err := txn.First("job_summary", "id", job.ParentID)
|
summaryRaw, err := txn.First("job_summary", "id", namespace, job.ParentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to retrieve summary for parent job: %v", err)
|
return fmt.Errorf("unable to retrieve summary for parent job: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -752,7 +804,7 @@ func (s *StateStore) DeleteJob(index uint64, jobID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the job summary
|
// Delete the job summary
|
||||||
if _, err = txn.DeleteAll("job_summary", "id", jobID); err != nil {
|
if _, err = txn.DeleteAll("job_summary", "id", namespace, jobID); err != nil {
|
||||||
return fmt.Errorf("deleing job summary failed: %v", err)
|
return fmt.Errorf("deleing job summary failed: %v", err)
|
||||||
}
|
}
|
||||||
if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
|
if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
|
||||||
|
@ -765,7 +817,10 @@ func (s *StateStore) DeleteJob(index uint64, jobID string) error {
|
||||||
|
|
||||||
// deleteJobVersions deletes all versions of the given job.
|
// deleteJobVersions deletes all versions of the given job.
|
||||||
func (s *StateStore) deleteJobVersions(index uint64, job *structs.Job, txn *memdb.Txn) error {
|
func (s *StateStore) deleteJobVersions(index uint64, job *structs.Job, txn *memdb.Txn) error {
|
||||||
iter, err := txn.Get("job_version", "id_prefix", job.ID)
|
if job.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
iter, err := txn.Get("job_version", "id_prefix", job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -782,12 +837,12 @@ func (s *StateStore) deleteJobVersions(index uint64, job *structs.Job, txn *memd
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = txn.DeleteAll("job_version", "id", j.ID, j.Version); err != nil {
|
if _, err = txn.DeleteAll("job_version", "id", j.Namespace, j.ID, j.Version); err != nil {
|
||||||
return fmt.Errorf("deleting job versions failed: %v", err)
|
return fmt.Errorf("deleting job versions failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
|
if err := txn.Insert("index", &IndexEntry{"job_version", index}); err != nil {
|
||||||
return fmt.Errorf("index update failed: %v", err)
|
return fmt.Errorf("index update failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,6 +852,9 @@ func (s *StateStore) deleteJobVersions(index uint64, job *structs.Job, txn *memd
|
||||||
// upsertJobVersion inserts a job into its historic version table and limits the
|
// upsertJobVersion inserts a job into its historic version table and limits the
|
||||||
// number of job versions that are tracked.
|
// number of job versions that are tracked.
|
||||||
func (s *StateStore) upsertJobVersion(index uint64, job *structs.Job, txn *memdb.Txn) error {
|
func (s *StateStore) upsertJobVersion(index uint64, job *structs.Job, txn *memdb.Txn) error {
|
||||||
|
if job.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Insert the job
|
// Insert the job
|
||||||
if err := txn.Insert("job_version", job); err != nil {
|
if err := txn.Insert("job_version", job); err != nil {
|
||||||
return fmt.Errorf("failed to insert job into job_version table: %v", err)
|
return fmt.Errorf("failed to insert job into job_version table: %v", err)
|
||||||
|
@ -807,7 +865,7 @@ func (s *StateStore) upsertJobVersion(index uint64, job *structs.Job, txn *memdb
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all the historic jobs for this ID
|
// Get all the historic jobs for this ID
|
||||||
all, err := s.jobVersionByID(txn, nil, job.ID)
|
all, err := s.jobVersionByID(txn, nil, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to look up job versions for %q: %v", job.ID, err)
|
return fmt.Errorf("failed to look up job versions for %q: %v", job.ID, err)
|
||||||
}
|
}
|
||||||
|
@ -845,10 +903,13 @@ func (s *StateStore) upsertJobVersion(index uint64, job *structs.Job, txn *memdb
|
||||||
|
|
||||||
// JobByID is used to lookup a job by its ID. JobByID returns the current/latest job
|
// JobByID is used to lookup a job by its ID. JobByID returns the current/latest job
|
||||||
// version.
|
// version.
|
||||||
func (s *StateStore) JobByID(ws memdb.WatchSet, id string) (*structs.Job, error) {
|
func (s *StateStore) JobByID(ws memdb.WatchSet, namespace, id string) (*structs.Job, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
watchCh, existing, err := txn.FirstWatch("jobs", "id", id)
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
watchCh, existing, err := txn.FirstWatch("jobs", "id", namespace, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("job lookup failed: %v", err)
|
return nil, fmt.Errorf("job lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -861,10 +922,14 @@ func (s *StateStore) JobByID(ws memdb.WatchSet, id string) (*structs.Job, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobsByIDPrefix is used to lookup a job by prefix
|
// JobsByIDPrefix is used to lookup a job by prefix
|
||||||
func (s *StateStore) JobsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
|
func (s *StateStore) JobsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
iter, err := txn.Get("jobs", "id_prefix", id)
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
iter, err := txn.Get("jobs", "id_prefix", namespace, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("job lookup failed: %v", err)
|
return nil, fmt.Errorf("job lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -875,17 +940,23 @@ func (s *StateStore) JobsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultI
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobVersionsByID returns all the tracked versions of a job.
|
// JobVersionsByID returns all the tracked versions of a job.
|
||||||
func (s *StateStore) JobVersionsByID(ws memdb.WatchSet, id string) ([]*structs.Job, error) {
|
func (s *StateStore) JobVersionsByID(ws memdb.WatchSet, namespace, id string) ([]*structs.Job, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
return s.jobVersionByID(txn, &ws, id)
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
return s.jobVersionByID(txn, &ws, namespace, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// jobVersionByID is the underlying implementation for retrieving all tracked
|
// jobVersionByID is the underlying implementation for retrieving all tracked
|
||||||
// versions of a job and is called under an existing transaction. A watch set
|
// versions of a job and is called under an existing transaction. A watch set
|
||||||
// can optionally be passed in to add the job histories to the watch set.
|
// can optionally be passed in to add the job histories to the watch set.
|
||||||
func (s *StateStore) jobVersionByID(txn *memdb.Txn, ws *memdb.WatchSet, id string) ([]*structs.Job, error) {
|
func (s *StateStore) jobVersionByID(txn *memdb.Txn, ws *memdb.WatchSet, namespace, id string) ([]*structs.Job, error) {
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Get all the historic jobs for this ID
|
// Get all the historic jobs for this ID
|
||||||
iter, err := txn.Get("job_version", "id_prefix", id)
|
iter, err := txn.Get("job_version", "id_prefix", namespace, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -920,15 +991,23 @@ func (s *StateStore) jobVersionByID(txn *memdb.Txn, ws *memdb.WatchSet, id strin
|
||||||
|
|
||||||
// JobByIDAndVersion returns the job identified by its ID and Version. The
|
// JobByIDAndVersion returns the job identified by its ID and Version. The
|
||||||
// passed watchset may be nil.
|
// passed watchset may be nil.
|
||||||
func (s *StateStore) JobByIDAndVersion(ws memdb.WatchSet, id string, version uint64) (*structs.Job, error) {
|
func (s *StateStore) JobByIDAndVersion(ws memdb.WatchSet, namespace, id string, version uint64) (*structs.Job, error) {
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
return s.jobByIDAndVersionImpl(ws, id, version, txn)
|
return s.jobByIDAndVersionImpl(ws, namespace, id, version, txn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// jobByIDAndVersionImpl returns the job identified by its ID and Version. The
|
// jobByIDAndVersionImpl returns the job identified by its ID and Version. The
|
||||||
// passed watchset may be nil.
|
// passed watchset may be nil.
|
||||||
func (s *StateStore) jobByIDAndVersionImpl(ws memdb.WatchSet, id string, version uint64, txn *memdb.Txn) (*structs.Job, error) {
|
func (s *StateStore) jobByIDAndVersionImpl(ws memdb.WatchSet, namespace, id string,
|
||||||
watchCh, existing, err := txn.FirstWatch("job_version", "id", id, version)
|
version uint64, txn *memdb.Txn) (*structs.Job, error) {
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
watchCh, existing, err := txn.FirstWatch("job_version", "id", namespace, id, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -973,6 +1052,25 @@ func (s *StateStore) Jobs(ws memdb.WatchSet) (memdb.ResultIterator, error) {
|
||||||
return iter, nil
|
return iter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JobsByNamespace returns an iterator over all the jobs for the given namespace
|
||||||
|
func (s *StateStore) JobsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) {
|
||||||
|
txn := s.db.Txn(false)
|
||||||
|
return s.jobsByNamespaceImpl(ws, namespace, txn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// jobsByNamespaceImpl returns an iterator over all the jobs for the given namespace
|
||||||
|
func (s *StateStore) jobsByNamespaceImpl(ws memdb.WatchSet, namespace string, txn *memdb.Txn) (memdb.ResultIterator, error) {
|
||||||
|
// Walk the entire jobs table
|
||||||
|
iter, err := txn.Get("jobs", "id_prefix", namespace, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.Add(iter.WatchCh())
|
||||||
|
|
||||||
|
return iter, nil
|
||||||
|
}
|
||||||
|
|
||||||
// JobsByPeriodic returns an iterator over all the periodic or non-periodic jobs.
|
// JobsByPeriodic returns an iterator over all the periodic or non-periodic jobs.
|
||||||
func (s *StateStore) JobsByPeriodic(ws memdb.WatchSet, periodic bool) (memdb.ResultIterator, error) {
|
func (s *StateStore) JobsByPeriodic(ws memdb.WatchSet, periodic bool) (memdb.ResultIterator, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
@ -1019,10 +1117,13 @@ func (s *StateStore) JobsByGC(ws memdb.WatchSet, gc bool) (memdb.ResultIterator,
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobSummary returns a job summary object which matches a specific id.
|
// JobSummary returns a job summary object which matches a specific id.
|
||||||
func (s *StateStore) JobSummaryByID(ws memdb.WatchSet, jobID string) (*structs.JobSummary, error) {
|
func (s *StateStore) JobSummaryByID(ws memdb.WatchSet, namespace, jobID string) (*structs.JobSummary, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
watchCh, existing, err := txn.FirstWatch("job_summary", "id", jobID)
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
watchCh, existing, err := txn.FirstWatch("job_summary", "id", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1053,10 +1154,13 @@ func (s *StateStore) JobSummaries(ws memdb.WatchSet) (memdb.ResultIterator, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobSummaryByPrefix is used to look up Job Summary by id prefix
|
// JobSummaryByPrefix is used to look up Job Summary by id prefix
|
||||||
func (s *StateStore) JobSummaryByPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
|
func (s *StateStore) JobSummaryByPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
iter, err := txn.Get("job_summary", "id_prefix", id)
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
iter, err := txn.Get("job_summary", "id_prefix", namespace, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("eval lookup failed: %v", err)
|
return nil, fmt.Errorf("eval lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1071,8 +1175,11 @@ func (s *StateStore) UpsertPeriodicLaunch(index uint64, launch *structs.Periodic
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
|
||||||
|
if launch.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Check if the job already exists
|
// Check if the job already exists
|
||||||
existing, err := txn.First("periodic_launch", "id", launch.ID)
|
existing, err := txn.First("periodic_launch", "id", launch.Namespace, launch.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("periodic launch lookup failed: %v", err)
|
return fmt.Errorf("periodic launch lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1099,12 +1206,15 @@ func (s *StateStore) UpsertPeriodicLaunch(index uint64, launch *structs.Periodic
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletePeriodicLaunch is used to delete the periodic launch
|
// DeletePeriodicLaunch is used to delete the periodic launch
|
||||||
func (s *StateStore) DeletePeriodicLaunch(index uint64, jobID string) error {
|
func (s *StateStore) DeletePeriodicLaunch(index uint64, namespace, jobID string) error {
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Lookup the launch
|
// Lookup the launch
|
||||||
existing, err := txn.First("periodic_launch", "id", jobID)
|
existing, err := txn.First("periodic_launch", "id", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("launch lookup failed: %v", err)
|
return fmt.Errorf("launch lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1126,10 +1236,13 @@ func (s *StateStore) DeletePeriodicLaunch(index uint64, jobID string) error {
|
||||||
|
|
||||||
// PeriodicLaunchByID is used to lookup a periodic launch by the periodic job
|
// PeriodicLaunchByID is used to lookup a periodic launch by the periodic job
|
||||||
// ID.
|
// ID.
|
||||||
func (s *StateStore) PeriodicLaunchByID(ws memdb.WatchSet, id string) (*structs.PeriodicLaunch, error) {
|
func (s *StateStore) PeriodicLaunchByID(ws memdb.WatchSet, namespace, id string) (*structs.PeriodicLaunch, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
watchCh, existing, err := txn.FirstWatch("periodic_launch", "id", id)
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
watchCh, existing, err := txn.FirstWatch("periodic_launch", "id", namespace, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("periodic launch lookup failed: %v", err)
|
return nil, fmt.Errorf("periodic launch lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1163,13 +1276,17 @@ func (s *StateStore) UpsertEvals(index uint64, evals []*structs.Evaluation) erro
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
|
||||||
// Do a nested upsert
|
// Do a nested upsert
|
||||||
jobs := make(map[string]string, len(evals))
|
jobs := make(map[structs.NamespacedID]string, len(evals))
|
||||||
for _, eval := range evals {
|
for _, eval := range evals {
|
||||||
if err := s.nestedUpsertEval(txn, index, eval); err != nil {
|
if err := s.nestedUpsertEval(txn, index, eval); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs[eval.JobID] = ""
|
tuple := structs.NamespacedID{
|
||||||
|
ID: eval.JobID,
|
||||||
|
Namespace: eval.Namespace,
|
||||||
|
}
|
||||||
|
jobs[tuple] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the job's status
|
// Set the job's status
|
||||||
|
@ -1198,8 +1315,11 @@ func (s *StateStore) nestedUpsertEval(txn *memdb.Txn, index uint64, eval *struct
|
||||||
eval.ModifyIndex = index
|
eval.ModifyIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if eval.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Update the job summary
|
// Update the job summary
|
||||||
summaryRaw, err := txn.First("job_summary", "id", eval.JobID)
|
summaryRaw, err := txn.First("job_summary", "id", eval.Namespace, eval.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("job summary lookup failed: %v", err)
|
return fmt.Errorf("job summary lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1233,9 +1353,9 @@ func (s *StateStore) nestedUpsertEval(txn *memdb.Txn, index uint64, eval *struct
|
||||||
// Check if the job has any blocked evaluations and cancel them
|
// Check if the job has any blocked evaluations and cancel them
|
||||||
if eval.Status == structs.EvalStatusComplete && len(eval.FailedTGAllocs) == 0 {
|
if eval.Status == structs.EvalStatusComplete && len(eval.FailedTGAllocs) == 0 {
|
||||||
// Get the blocked evaluation for a job if it exists
|
// Get the blocked evaluation for a job if it exists
|
||||||
iter, err := txn.Get("evals", "job", eval.JobID, structs.EvalStatusBlocked)
|
iter, err := txn.Get("evals", "job", eval.Namespace, eval.JobID, structs.EvalStatusBlocked)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get blocked evals for job %q: %v", eval.JobID, err)
|
return fmt.Errorf("failed to get blocked evals for job %q in namespace %q: %v", eval.JobID, eval.Namespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var blocked []*structs.Evaluation
|
var blocked []*structs.Evaluation
|
||||||
|
@ -1274,7 +1394,7 @@ func (s *StateStore) DeleteEval(index uint64, evals []string, allocs []string) e
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
|
||||||
jobs := make(map[string]string, len(evals))
|
jobs := make(map[structs.NamespacedID]string, len(evals))
|
||||||
for _, eval := range evals {
|
for _, eval := range evals {
|
||||||
existing, err := txn.First("evals", "id", eval)
|
existing, err := txn.First("evals", "id", eval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1286,8 +1406,13 @@ func (s *StateStore) DeleteEval(index uint64, evals []string, allocs []string) e
|
||||||
if err := txn.Delete("evals", existing); err != nil {
|
if err := txn.Delete("evals", existing); err != nil {
|
||||||
return fmt.Errorf("eval delete failed: %v", err)
|
return fmt.Errorf("eval delete failed: %v", err)
|
||||||
}
|
}
|
||||||
jobID := existing.(*structs.Evaluation).JobID
|
eval := existing.(*structs.Evaluation)
|
||||||
jobs[jobID] = ""
|
|
||||||
|
tuple := structs.NamespacedID{
|
||||||
|
ID: eval.JobID,
|
||||||
|
Namespace: eval.Namespace,
|
||||||
|
}
|
||||||
|
jobs[tuple] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, alloc := range allocs {
|
for _, alloc := range allocs {
|
||||||
|
@ -1337,10 +1462,12 @@ func (s *StateStore) EvalByID(ws memdb.WatchSet, id string) (*structs.Evaluation
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvalsByIDPrefix is used to lookup evaluations by prefix
|
// EvalsByIDPrefix is used to lookup evaluations by prefix in a particular
|
||||||
func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
|
// namespace
|
||||||
|
func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
// Get an iterator over all evals by the id prefix
|
||||||
iter, err := txn.Get("evals", "id_prefix", id)
|
iter, err := txn.Get("evals", "id_prefix", id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("eval lookup failed: %v", err)
|
return nil, fmt.Errorf("eval lookup failed: %v", err)
|
||||||
|
@ -1348,15 +1475,33 @@ func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, id string) (memdb.Result
|
||||||
|
|
||||||
ws.Add(iter.WatchCh())
|
ws.Add(iter.WatchCh())
|
||||||
|
|
||||||
return iter, nil
|
// Wrap the iterator in a filter
|
||||||
|
wrap := memdb.NewFilterIterator(iter, evalNamespaceFilter(namespace))
|
||||||
|
return wrap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNamespaceFilter returns a filter function that filters all evaluations
|
||||||
|
// not in the given namespace.
|
||||||
|
func evalNamespaceFilter(namespace string) func(interface{}) bool {
|
||||||
|
return func(raw interface{}) bool {
|
||||||
|
eval, ok := raw.(*structs.Evaluation)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return eval.Namespace != namespace
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvalsByJob returns all the evaluations by job id
|
// EvalsByJob returns all the evaluations by job id
|
||||||
func (s *StateStore) EvalsByJob(ws memdb.WatchSet, jobID string) ([]*structs.Evaluation, error) {
|
func (s *StateStore) EvalsByJob(ws memdb.WatchSet, namespace, jobID string) ([]*structs.Evaluation, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Get an iterator over the node allocations
|
// Get an iterator over the node allocations
|
||||||
iter, err := txn.Get("evals", "job_prefix", jobID)
|
iter, err := txn.Get("evals", "job_prefix", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1397,6 +1542,22 @@ func (s *StateStore) Evals(ws memdb.WatchSet) (memdb.ResultIterator, error) {
|
||||||
return iter, nil
|
return iter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EvalsByNamespace returns an iterator over all the evaluations in the given
|
||||||
|
// namespace
|
||||||
|
func (s *StateStore) EvalsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) {
|
||||||
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
// Walk the entire table
|
||||||
|
iter, err := txn.Get("evals", "namespace", namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.Add(iter.WatchCh())
|
||||||
|
|
||||||
|
return iter, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateAllocsFromClient is used to update an allocation based on input
|
// UpdateAllocsFromClient is used to update an allocation based on input
|
||||||
// from a client. While the schedulers are the authority on the allocation for
|
// from a client. While the schedulers are the authority on the allocation for
|
||||||
// most things, some updates are authoritative from the client. Specifically,
|
// most things, some updates are authoritative from the client. Specifically,
|
||||||
|
@ -1448,7 +1609,6 @@ func (s *StateStore) nestedUpdateAllocFromClient(txn *memdb.Txn, index uint64, a
|
||||||
// Update the modify index
|
// Update the modify index
|
||||||
copyAlloc.ModifyIndex = index
|
copyAlloc.ModifyIndex = index
|
||||||
|
|
||||||
// TODO TEST
|
|
||||||
if err := s.updateDeploymentWithAlloc(index, copyAlloc, exist, txn); err != nil {
|
if err := s.updateDeploymentWithAlloc(index, copyAlloc, exist, txn); err != nil {
|
||||||
return fmt.Errorf("error updating deployment: %v", err)
|
return fmt.Errorf("error updating deployment: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1467,7 +1627,13 @@ func (s *StateStore) nestedUpdateAllocFromClient(txn *memdb.Txn, index uint64, a
|
||||||
if !copyAlloc.TerminalStatus() {
|
if !copyAlloc.TerminalStatus() {
|
||||||
forceStatus = structs.JobStatusRunning
|
forceStatus = structs.JobStatusRunning
|
||||||
}
|
}
|
||||||
jobs := map[string]string{exist.JobID: forceStatus}
|
|
||||||
|
tuple := structs.NamespacedID{
|
||||||
|
ID: exist.JobID,
|
||||||
|
Namespace: exist.Namespace,
|
||||||
|
}
|
||||||
|
jobs := map[structs.NamespacedID]string{tuple: forceStatus}
|
||||||
|
|
||||||
if err := s.setJobStatuses(index, txn, jobs, false); err != nil {
|
if err := s.setJobStatuses(index, txn, jobs, false); err != nil {
|
||||||
return fmt.Errorf("setting job status failed: %v", err)
|
return fmt.Errorf("setting job status failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1490,7 +1656,7 @@ func (s *StateStore) UpsertAllocs(index uint64, allocs []*structs.Allocation) er
|
||||||
// used with an existing transaction.
|
// used with an existing transaction.
|
||||||
func (s *StateStore) upsertAllocsImpl(index uint64, allocs []*structs.Allocation, txn *memdb.Txn) error {
|
func (s *StateStore) upsertAllocsImpl(index uint64, allocs []*structs.Allocation, txn *memdb.Txn) error {
|
||||||
// Handle the allocations
|
// Handle the allocations
|
||||||
jobs := make(map[string]string, 1)
|
jobs := make(map[structs.NamespacedID]string, 1)
|
||||||
for _, alloc := range allocs {
|
for _, alloc := range allocs {
|
||||||
existing, err := txn.First("allocs", "id", alloc.ID)
|
existing, err := txn.First("allocs", "id", alloc.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1561,7 +1727,12 @@ func (s *StateStore) upsertAllocsImpl(index uint64, allocs []*structs.Allocation
|
||||||
if !alloc.TerminalStatus() {
|
if !alloc.TerminalStatus() {
|
||||||
forceStatus = structs.JobStatusRunning
|
forceStatus = structs.JobStatusRunning
|
||||||
}
|
}
|
||||||
jobs[alloc.JobID] = forceStatus
|
|
||||||
|
tuple := structs.NamespacedID{
|
||||||
|
ID: alloc.JobID,
|
||||||
|
Namespace: alloc.Namespace,
|
||||||
|
}
|
||||||
|
jobs[tuple] = forceStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the indexes
|
// Update the indexes
|
||||||
|
@ -1595,7 +1766,7 @@ func (s *StateStore) AllocByID(ws memdb.WatchSet, id string) (*structs.Allocatio
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllocsByIDPrefix is used to lookup allocs by prefix
|
// AllocsByIDPrefix is used to lookup allocs by prefix
|
||||||
func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
|
func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
iter, err := txn.Get("allocs", "id_prefix", id)
|
iter, err := txn.Get("allocs", "id_prefix", id)
|
||||||
|
@ -1605,7 +1776,22 @@ func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, id string) (memdb.Resul
|
||||||
|
|
||||||
ws.Add(iter.WatchCh())
|
ws.Add(iter.WatchCh())
|
||||||
|
|
||||||
return iter, nil
|
// Wrap the iterator in a filter
|
||||||
|
wrap := memdb.NewFilterIterator(iter, allocNamespaceFilter(namespace))
|
||||||
|
return wrap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocNamespaceFilter returns a filter function that filters all allocations
|
||||||
|
// not in the given namespace.
|
||||||
|
func allocNamespaceFilter(namespace string) func(interface{}) bool {
|
||||||
|
return func(raw interface{}) bool {
|
||||||
|
alloc, ok := raw.(*structs.Allocation)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return alloc.Namespace != namespace
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllocsByNode returns all the allocations by node
|
// AllocsByNode returns all the allocations by node
|
||||||
|
@ -1656,12 +1842,15 @@ func (s *StateStore) AllocsByNodeTerminal(ws memdb.WatchSet, node string, termin
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllocsByJob returns all the allocations by job id
|
// AllocsByJob returns all the allocations by job id
|
||||||
func (s *StateStore) AllocsByJob(ws memdb.WatchSet, jobID string, all bool) ([]*structs.Allocation, error) {
|
func (s *StateStore) AllocsByJob(ws memdb.WatchSet, namespace, jobID string, all bool) ([]*structs.Allocation, error) {
|
||||||
txn := s.db.Txn(false)
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Get the job
|
// Get the job
|
||||||
var job *structs.Job
|
var job *structs.Job
|
||||||
rawJob, err := txn.First("jobs", "id", jobID)
|
rawJob, err := txn.First("jobs", "id", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1670,7 +1859,7 @@ func (s *StateStore) AllocsByJob(ws memdb.WatchSet, jobID string, all bool) ([]*
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an iterator over the node allocations
|
// Get an iterator over the node allocations
|
||||||
iter, err := txn.Get("allocs", "job", jobID)
|
iter, err := txn.Get("allocs", "job", namespace, jobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1757,6 +1946,22 @@ func (s *StateStore) Allocs(ws memdb.WatchSet) (memdb.ResultIterator, error) {
|
||||||
return iter, nil
|
return iter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllocsByNamespace returns an iterator over all the allocations in the
|
||||||
|
// namespace
|
||||||
|
func (s *StateStore) AllocsByNamespace(ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) {
|
||||||
|
txn := s.db.Txn(false)
|
||||||
|
|
||||||
|
// Walk the entire table
|
||||||
|
iter, err := txn.Get("allocs", "namespace", namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.Add(iter.WatchCh())
|
||||||
|
|
||||||
|
return iter, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpsertVaultAccessors is used to register a set of Vault Accessors
|
// UpsertVaultAccessors is used to register a set of Vault Accessors
|
||||||
func (s *StateStore) UpsertVaultAccessor(index uint64, accessors []*structs.VaultAccessor) error {
|
func (s *StateStore) UpsertVaultAccessor(index uint64, accessors []*structs.VaultAccessor) error {
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
|
@ -1935,10 +2140,13 @@ func (s *StateStore) updateDeploymentStatusImpl(index uint64, u *structs.Deploym
|
||||||
if err := txn.Insert("index", &IndexEntry{"deployment", index}); err != nil {
|
if err := txn.Insert("index", &IndexEntry{"deployment", index}); err != nil {
|
||||||
return fmt.Errorf("index update failed: %v", err)
|
return fmt.Errorf("index update failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if copy.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// If the deployment is being marked as complete, set the job to stable.
|
// If the deployment is being marked as complete, set the job to stable.
|
||||||
if copy.Status == structs.DeploymentStatusSuccessful {
|
if copy.Status == structs.DeploymentStatusSuccessful {
|
||||||
if err := s.updateJobStabilityImpl(index, copy.JobID, copy.JobVersion, true, txn); err != nil {
|
if err := s.updateJobStabilityImpl(index, copy.Namespace, copy.JobID, copy.JobVersion, true, txn); err != nil {
|
||||||
return fmt.Errorf("failed to update job stability: %v", err)
|
return fmt.Errorf("failed to update job stability: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1948,11 +2156,14 @@ func (s *StateStore) updateDeploymentStatusImpl(index uint64, u *structs.Deploym
|
||||||
|
|
||||||
// UpdateJobStability updates the stability of the given job and version to the
|
// UpdateJobStability updates the stability of the given job and version to the
|
||||||
// desired status.
|
// desired status.
|
||||||
func (s *StateStore) UpdateJobStability(index uint64, jobID string, jobVersion uint64, stable bool) error {
|
func (s *StateStore) UpdateJobStability(index uint64, namespace, jobID string, jobVersion uint64, stable bool) error {
|
||||||
txn := s.db.Txn(true)
|
txn := s.db.Txn(true)
|
||||||
defer txn.Abort()
|
defer txn.Abort()
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.updateJobStabilityImpl(index, jobID, jobVersion, stable, txn); err != nil {
|
if err := s.updateJobStabilityImpl(index, namespace, jobID, jobVersion, stable, txn); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1961,9 +2172,12 @@ func (s *StateStore) UpdateJobStability(index uint64, jobID string, jobVersion u
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateJobStabilityImpl updates the stability of the given job and version
|
// updateJobStabilityImpl updates the stability of the given job and version
|
||||||
func (s *StateStore) updateJobStabilityImpl(index uint64, jobID string, jobVersion uint64, stable bool, txn *memdb.Txn) error {
|
func (s *StateStore) updateJobStabilityImpl(index uint64, namespace, jobID string, jobVersion uint64, stable bool, txn *memdb.Txn) error {
|
||||||
|
if namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
// Get the job that is referenced
|
// Get the job that is referenced
|
||||||
job, err := s.jobByIDAndVersionImpl(nil, jobID, jobVersion, txn)
|
job, err := s.jobByIDAndVersionImpl(nil, namespace, jobID, jobVersion, txn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2268,15 +2482,19 @@ func (s *StateStore) ReconcileJobSummaries(index uint64) error {
|
||||||
|
|
||||||
// Create a job summary for the job
|
// Create a job summary for the job
|
||||||
summary := &structs.JobSummary{
|
summary := &structs.JobSummary{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Summary: make(map[string]structs.TaskGroupSummary),
|
Namespace: job.Namespace,
|
||||||
|
Summary: make(map[string]structs.TaskGroupSummary),
|
||||||
}
|
}
|
||||||
for _, tg := range job.TaskGroups {
|
for _, tg := range job.TaskGroups {
|
||||||
summary.Summary[tg.Name] = structs.TaskGroupSummary{}
|
summary.Summary[tg.Name] = structs.TaskGroupSummary{}
|
||||||
}
|
}
|
||||||
|
if job.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// Find all the allocations for the jobs
|
// Find all the allocations for the jobs
|
||||||
iterAllocs, err := txn.Get("allocs", "job", job.ID)
|
iterAllocs, err := txn.Get("allocs", "job", job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2336,9 +2554,12 @@ func (s *StateStore) ReconcileJobSummaries(index uint64) error {
|
||||||
// It takes a map of job IDs to an optional forceStatus string. It returns an
|
// It takes a map of job IDs to an optional forceStatus string. It returns an
|
||||||
// error if the job doesn't exist or setJobStatus fails.
|
// error if the job doesn't exist or setJobStatus fails.
|
||||||
func (s *StateStore) setJobStatuses(index uint64, txn *memdb.Txn,
|
func (s *StateStore) setJobStatuses(index uint64, txn *memdb.Txn,
|
||||||
jobs map[string]string, evalDelete bool) error {
|
jobs map[structs.NamespacedID]string, evalDelete bool) error {
|
||||||
for job, forceStatus := range jobs {
|
for tuple, forceStatus := range jobs {
|
||||||
existing, err := txn.First("jobs", "id", job)
|
if tuple.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
existing, err := txn.First("jobs", "id", tuple.Namespace, tuple.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("job lookup failed: %v", err)
|
return fmt.Errorf("job lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2397,10 +2618,14 @@ func (s *StateStore) setJobStatus(index uint64, txn *memdb.Txn,
|
||||||
return fmt.Errorf("index update failed: %v", err)
|
return fmt.Errorf("index update failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if updated.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// Update the children summary
|
// Update the children summary
|
||||||
if updated.ParentID != "" {
|
if updated.ParentID != "" {
|
||||||
// Try to update the summary of the parent job summary
|
// Try to update the summary of the parent job summary
|
||||||
summaryRaw, err := txn.First("job_summary", "id", updated.ParentID)
|
summaryRaw, err := txn.First("job_summary", "id", updated.Namespace, updated.ParentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to retrieve summary for parent job: %v", err)
|
return fmt.Errorf("unable to retrieve summary for parent job: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2460,7 +2685,10 @@ func (s *StateStore) setJobStatus(index uint64, txn *memdb.Txn,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete bool) (string, error) {
|
func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete bool) (string, error) {
|
||||||
allocs, err := txn.Get("allocs", "job", job.ID)
|
if job.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
allocs, err := txn.Get("allocs", "job", job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -2474,7 +2702,7 @@ func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evals, err := txn.Get("evals", "job_prefix", job.ID)
|
evals, err := txn.Get("evals", "job_prefix", job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -2529,8 +2757,12 @@ func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete b
|
||||||
func (s *StateStore) updateSummaryWithJob(index uint64, job *structs.Job,
|
func (s *StateStore) updateSummaryWithJob(index uint64, job *structs.Job,
|
||||||
txn *memdb.Txn) error {
|
txn *memdb.Txn) error {
|
||||||
|
|
||||||
|
if job.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
// Update the job summary
|
// Update the job summary
|
||||||
summaryRaw, err := txn.First("job_summary", "id", job.ID)
|
summaryRaw, err := txn.First("job_summary", "id", job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("job summary lookup failed: %v", err)
|
return fmt.Errorf("job summary lookup failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2543,6 +2775,7 @@ func (s *StateStore) updateSummaryWithJob(index uint64, job *structs.Job,
|
||||||
} else {
|
} else {
|
||||||
summary = &structs.JobSummary{
|
summary = &structs.JobSummary{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
Summary: make(map[string]structs.TaskGroupSummary),
|
Summary: make(map[string]structs.TaskGroupSummary),
|
||||||
Children: new(structs.JobChildrenSummary),
|
Children: new(structs.JobChildrenSummary),
|
||||||
CreateIndex: index,
|
CreateIndex: index,
|
||||||
|
@ -2667,15 +2900,18 @@ func (s *StateStore) updateSummaryWithAlloc(index uint64, alloc *structs.Allocat
|
||||||
if alloc.Job == nil {
|
if alloc.Job == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if alloc.Namespace == "" {
|
||||||
|
panic("empty namespace")
|
||||||
|
}
|
||||||
|
|
||||||
summaryRaw, err := txn.First("job_summary", "id", alloc.JobID)
|
summaryRaw, err := txn.First("job_summary", "id", alloc.Namespace, alloc.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to lookup job summary for job id %q: %v", alloc.JobID, err)
|
return fmt.Errorf("unable to lookup job summary for job id %q in namespace %q: %v", alloc.JobID, alloc.Namespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if summaryRaw == nil {
|
if summaryRaw == nil {
|
||||||
// Check if the job is de-registered
|
// Check if the job is de-registered
|
||||||
rawJob, err := txn.First("jobs", "id", alloc.JobID)
|
rawJob, err := txn.First("jobs", "id", alloc.Namespace, alloc.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to query job: %v", err)
|
return fmt.Errorf("unable to query job: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2685,7 +2921,7 @@ func (s *StateStore) updateSummaryWithAlloc(index uint64, alloc *structs.Allocat
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("job summary for job %q is not present", alloc.JobID)
|
return fmt.Errorf("job summary for job %q in namespace %q is not present", alloc.JobID, alloc.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a copy of the existing summary
|
// Get a copy of the existing summary
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -112,7 +112,8 @@ const (
|
||||||
ACLManagementToken = "management"
|
ACLManagementToken = "management"
|
||||||
|
|
||||||
// DefaultNamespace is the default namespace.
|
// DefaultNamespace is the default namespace.
|
||||||
DefaultNamespace = "default"
|
DefaultNamespace = "default"
|
||||||
|
DefaultNamespaceDescription = "Default shared namespace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context defines the scope in which a search for Nomad object operates, and
|
// Context defines the scope in which a search for Nomad object operates, and
|
||||||
|
@ -125,9 +126,16 @@ const (
|
||||||
Evals Context = "evals"
|
Evals Context = "evals"
|
||||||
Jobs Context = "jobs"
|
Jobs Context = "jobs"
|
||||||
Nodes Context = "nodes"
|
Nodes Context = "nodes"
|
||||||
|
Namespaces Context = "namespaces"
|
||||||
All Context = "all"
|
All Context = "all"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NamespacedID is a tuple of an ID and a namespace
|
||||||
|
type NamespacedID struct {
|
||||||
|
ID string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
// RPCInfo is used to describe common information about query
|
// RPCInfo is used to describe common information about query
|
||||||
type RPCInfo interface {
|
type RPCInfo interface {
|
||||||
RequestRegion() string
|
RequestRegion() string
|
||||||
|
@ -140,6 +148,9 @@ type QueryOptions struct {
|
||||||
// The target region for this query
|
// The target region for this query
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Namespace is the target namespace for the query.
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// If set, wait until query exceeds given index. Must be provided
|
// If set, wait until query exceeds given index. Must be provided
|
||||||
// with MaxQueryTime.
|
// with MaxQueryTime.
|
||||||
MinQueryIndex uint64
|
MinQueryIndex uint64
|
||||||
|
@ -162,6 +173,13 @@ func (q QueryOptions) RequestRegion() string {
|
||||||
return q.Region
|
return q.Region
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q QueryOptions) RequestNamespace() string {
|
||||||
|
if q.Namespace == "" {
|
||||||
|
return DefaultNamespace
|
||||||
|
}
|
||||||
|
return q.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
// QueryOption only applies to reads, so always true
|
// QueryOption only applies to reads, so always true
|
||||||
func (q QueryOptions) IsRead() bool {
|
func (q QueryOptions) IsRead() bool {
|
||||||
return true
|
return true
|
||||||
|
@ -175,6 +193,9 @@ type WriteRequest struct {
|
||||||
// The target region for this write
|
// The target region for this write
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Namespace is the target namespace for the write.
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// SecretID is secret portion of the ACL token used for the request
|
// SecretID is secret portion of the ACL token used for the request
|
||||||
SecretID string
|
SecretID string
|
||||||
}
|
}
|
||||||
|
@ -184,6 +205,13 @@ func (w WriteRequest) RequestRegion() string {
|
||||||
return w.Region
|
return w.Region
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w WriteRequest) RequestNamespace() string {
|
||||||
|
if w.Namespace == "" {
|
||||||
|
return DefaultNamespace
|
||||||
|
}
|
||||||
|
return w.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
// WriteRequest only applies to writes, always false
|
// WriteRequest only applies to writes, always false
|
||||||
func (w WriteRequest) IsRead() bool {
|
func (w WriteRequest) IsRead() bool {
|
||||||
return false
|
return false
|
||||||
|
@ -1434,6 +1462,9 @@ type Job struct {
|
||||||
// Region is the Nomad region that handles scheduling this job
|
// Region is the Nomad region that handles scheduling this job
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
// Namespace is the namespace the job is submitted into.
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// ID is a unique identifier for the job per region. It can be
|
// ID is a unique identifier for the job per region. It can be
|
||||||
// specified hierarchically like LineOfBiz/OrgName/Team/Project
|
// specified hierarchically like LineOfBiz/OrgName/Team/Project
|
||||||
ID string
|
ID string
|
||||||
|
@ -1522,6 +1553,10 @@ type Job struct {
|
||||||
// when registering a Job. A set of warnings are returned if the job was changed
|
// when registering a Job. A set of warnings are returned if the job was changed
|
||||||
// in anyway that the user should be made aware of.
|
// in anyway that the user should be made aware of.
|
||||||
func (j *Job) Canonicalize() (warnings error) {
|
func (j *Job) Canonicalize() (warnings error) {
|
||||||
|
if j == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var mErr multierror.Error
|
var mErr multierror.Error
|
||||||
// Ensure that an empty and nil map are treated the same to avoid scheduling
|
// Ensure that an empty and nil map are treated the same to avoid scheduling
|
||||||
// problems since we use reflect DeepEquals.
|
// problems since we use reflect DeepEquals.
|
||||||
|
@ -1529,6 +1564,11 @@ func (j *Job) Canonicalize() (warnings error) {
|
||||||
j.Meta = nil
|
j.Meta = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the job is in a namespace.
|
||||||
|
if j.Namespace == "" {
|
||||||
|
j.Namespace = DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
for _, tg := range j.TaskGroups {
|
for _, tg := range j.TaskGroups {
|
||||||
tg.Canonicalize(j)
|
tg.Canonicalize(j)
|
||||||
}
|
}
|
||||||
|
@ -1663,6 +1703,9 @@ func (j *Job) Validate() error {
|
||||||
if j.Name == "" {
|
if j.Name == "" {
|
||||||
mErr.Errors = append(mErr.Errors, errors.New("Missing job name"))
|
mErr.Errors = append(mErr.Errors, errors.New("Missing job name"))
|
||||||
}
|
}
|
||||||
|
if j.Namespace == "" {
|
||||||
|
mErr.Errors = append(mErr.Errors, errors.New("Job must be in a namespace"))
|
||||||
|
}
|
||||||
switch j.Type {
|
switch j.Type {
|
||||||
case JobTypeCore, JobTypeService, JobTypeBatch, JobTypeSystem:
|
case JobTypeCore, JobTypeService, JobTypeBatch, JobTypeSystem:
|
||||||
case "":
|
case "":
|
||||||
|
@ -1969,8 +2012,12 @@ type JobListStub struct {
|
||||||
|
|
||||||
// JobSummary summarizes the state of the allocations of a job
|
// JobSummary summarizes the state of the allocations of a job
|
||||||
type JobSummary struct {
|
type JobSummary struct {
|
||||||
|
// JobID is the ID of the job the summary is for
|
||||||
JobID string
|
JobID string
|
||||||
|
|
||||||
|
// Namespace is the namespace of the job and its summary
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// Summmary contains the summary per task group for the Job
|
// Summmary contains the summary per task group for the Job
|
||||||
Summary map[string]TaskGroupSummary
|
Summary map[string]TaskGroupSummary
|
||||||
|
|
||||||
|
@ -2279,8 +2326,9 @@ const (
|
||||||
|
|
||||||
// PeriodicLaunch tracks the last launch time of a periodic job.
|
// PeriodicLaunch tracks the last launch time of a periodic job.
|
||||||
type PeriodicLaunch struct {
|
type PeriodicLaunch struct {
|
||||||
ID string // ID of the periodic job.
|
ID string // ID of the periodic job.
|
||||||
Launch time.Time // The last launch time.
|
Namespace string // Namespace of the periodic job
|
||||||
|
Launch time.Time // The last launch time.
|
||||||
|
|
||||||
// Raft Indexes
|
// Raft Indexes
|
||||||
CreateIndex uint64
|
CreateIndex uint64
|
||||||
|
@ -4199,6 +4247,9 @@ type Deployment struct {
|
||||||
// ID is a generated UUID for the deployment
|
// ID is a generated UUID for the deployment
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
|
// Namespace is the namespace the deployment is created in
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// JobID is the job the deployment is created for
|
// JobID is the job the deployment is created for
|
||||||
JobID string
|
JobID string
|
||||||
|
|
||||||
|
@ -4232,6 +4283,7 @@ type Deployment struct {
|
||||||
func NewDeployment(job *Job) *Deployment {
|
func NewDeployment(job *Job) *Deployment {
|
||||||
return &Deployment{
|
return &Deployment{
|
||||||
ID: GenerateUUID(),
|
ID: GenerateUUID(),
|
||||||
|
Namespace: job.Namespace,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
JobVersion: job.Version,
|
JobVersion: job.Version,
|
||||||
JobModifyIndex: job.ModifyIndex,
|
JobModifyIndex: job.ModifyIndex,
|
||||||
|
@ -4393,6 +4445,9 @@ type Allocation struct {
|
||||||
// ID of the allocation (UUID)
|
// ID of the allocation (UUID)
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
|
// Namespace is the namespace the allocation is created in
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// ID of the evaluation that generated this allocation
|
// ID of the evaluation that generated this allocation
|
||||||
EvalID string
|
EvalID string
|
||||||
|
|
||||||
|
@ -4843,6 +4898,9 @@ type Evaluation struct {
|
||||||
// is assigned upon the creation of the evaluation.
|
// is assigned upon the creation of the evaluation.
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
|
// Namespace is the namespace the evaluation is created in
|
||||||
|
Namespace string
|
||||||
|
|
||||||
// Priority is used to control scheduling importance and if this job
|
// Priority is used to control scheduling importance and if this job
|
||||||
// can preempt other jobs.
|
// can preempt other jobs.
|
||||||
Priority int
|
Priority int
|
||||||
|
@ -4939,7 +4997,7 @@ func (e *Evaluation) TerminalStatus() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Evaluation) GoString() string {
|
func (e *Evaluation) GoString() string {
|
||||||
return fmt.Sprintf("<Eval '%s' JobID: '%s'>", e.ID, e.JobID)
|
return fmt.Sprintf("<Eval %q JobID: %q Namespace: %q>", e.ID, e.JobID, e.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Evaluation) Copy() *Evaluation {
|
func (e *Evaluation) Copy() *Evaluation {
|
||||||
|
@ -5025,6 +5083,7 @@ func (e *Evaluation) MakePlan(j *Job) *Plan {
|
||||||
func (e *Evaluation) NextRollingEval(wait time.Duration) *Evaluation {
|
func (e *Evaluation) NextRollingEval(wait time.Duration) *Evaluation {
|
||||||
return &Evaluation{
|
return &Evaluation{
|
||||||
ID: GenerateUUID(),
|
ID: GenerateUUID(),
|
||||||
|
Namespace: e.Namespace,
|
||||||
Priority: e.Priority,
|
Priority: e.Priority,
|
||||||
Type: e.Type,
|
Type: e.Type,
|
||||||
TriggeredBy: EvalTriggerRollingUpdate,
|
TriggeredBy: EvalTriggerRollingUpdate,
|
||||||
|
@ -5042,6 +5101,7 @@ func (e *Evaluation) NextRollingEval(wait time.Duration) *Evaluation {
|
||||||
func (e *Evaluation) CreateBlockedEval(classEligibility map[string]bool, escaped bool) *Evaluation {
|
func (e *Evaluation) CreateBlockedEval(classEligibility map[string]bool, escaped bool) *Evaluation {
|
||||||
return &Evaluation{
|
return &Evaluation{
|
||||||
ID: GenerateUUID(),
|
ID: GenerateUUID(),
|
||||||
|
Namespace: e.Namespace,
|
||||||
Priority: e.Priority,
|
Priority: e.Priority,
|
||||||
Type: e.Type,
|
Type: e.Type,
|
||||||
TriggeredBy: e.TriggeredBy,
|
TriggeredBy: e.TriggeredBy,
|
||||||
|
@ -5060,6 +5120,7 @@ func (e *Evaluation) CreateBlockedEval(classEligibility map[string]bool, escaped
|
||||||
func (e *Evaluation) CreateFailedFollowUpEval(wait time.Duration) *Evaluation {
|
func (e *Evaluation) CreateFailedFollowUpEval(wait time.Duration) *Evaluation {
|
||||||
return &Evaluation{
|
return &Evaluation{
|
||||||
ID: GenerateUUID(),
|
ID: GenerateUUID(),
|
||||||
|
Namespace: e.Namespace,
|
||||||
Priority: e.Priority,
|
Priority: e.Priority,
|
||||||
Type: e.Type,
|
Type: e.Type,
|
||||||
TriggeredBy: EvalTriggerFailedFollowUp,
|
TriggeredBy: EvalTriggerFailedFollowUp,
|
||||||
|
|
|
@ -26,16 +26,19 @@ func TestJob_Validate(t *testing.T) {
|
||||||
if !strings.Contains(mErr.Errors[2].Error(), "job name") {
|
if !strings.Contains(mErr.Errors[2].Error(), "job name") {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(mErr.Errors[3].Error(), "job type") {
|
if !strings.Contains(mErr.Errors[3].Error(), "namespace") {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(mErr.Errors[4].Error(), "priority") {
|
if !strings.Contains(mErr.Errors[4].Error(), "job type") {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(mErr.Errors[5].Error(), "datacenters") {
|
if !strings.Contains(mErr.Errors[5].Error(), "priority") {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(mErr.Errors[6].Error(), "task groups") {
|
if !strings.Contains(mErr.Errors[6].Error(), "datacenters") {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(mErr.Errors[7].Error(), "task groups") {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +65,7 @@ func TestJob_Validate(t *testing.T) {
|
||||||
j = &Job{
|
j = &Job{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
ID: GenerateUUID(),
|
ID: GenerateUUID(),
|
||||||
|
Namespace: "test",
|
||||||
Name: "my-job",
|
Name: "my-job",
|
||||||
Type: JobTypeService,
|
Type: JobTypeService,
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
|
@ -161,7 +165,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
Name: "One task group",
|
Name: "One task group",
|
||||||
Warnings: []string{"conversion to new update stanza"},
|
Warnings: []string{"conversion to new update stanza"},
|
||||||
Job: &Job{
|
Job: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
Stagger: 10 * time.Second,
|
Stagger: 10 * time.Second,
|
||||||
|
@ -174,7 +179,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Expected: &Job{
|
Expected: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
Stagger: 10 * time.Second,
|
Stagger: 10 * time.Second,
|
||||||
|
@ -202,7 +208,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
Name: "One task group batch",
|
Name: "One task group batch",
|
||||||
Warnings: []string{"Update stanza is disallowed for batch jobs"},
|
Warnings: []string{"Update stanza is disallowed for batch jobs"},
|
||||||
Job: &Job{
|
Job: &Job{
|
||||||
Type: JobTypeBatch,
|
Namespace: "test",
|
||||||
|
Type: JobTypeBatch,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
Stagger: 10 * time.Second,
|
Stagger: 10 * time.Second,
|
||||||
|
@ -215,8 +222,9 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Expected: &Job{
|
Expected: &Job{
|
||||||
Type: JobTypeBatch,
|
Namespace: "test",
|
||||||
Update: UpdateStrategy{},
|
Type: JobTypeBatch,
|
||||||
|
Update: UpdateStrategy{},
|
||||||
TaskGroups: []*TaskGroup{
|
TaskGroups: []*TaskGroup{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -231,7 +239,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
Name: "One task group batch - new spec",
|
Name: "One task group batch - new spec",
|
||||||
Warnings: []string{"Update stanza is disallowed for batch jobs"},
|
Warnings: []string{"Update stanza is disallowed for batch jobs"},
|
||||||
Job: &Job{
|
Job: &Job{
|
||||||
Type: JobTypeBatch,
|
Namespace: "test",
|
||||||
|
Type: JobTypeBatch,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
Stagger: 2 * time.Second,
|
Stagger: 2 * time.Second,
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
|
@ -256,8 +265,9 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Expected: &Job{
|
Expected: &Job{
|
||||||
Type: JobTypeBatch,
|
Namespace: "test",
|
||||||
Update: UpdateStrategy{},
|
Type: JobTypeBatch,
|
||||||
|
Update: UpdateStrategy{},
|
||||||
TaskGroups: []*TaskGroup{
|
TaskGroups: []*TaskGroup{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -271,7 +281,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
{
|
{
|
||||||
Name: "One task group service - new spec",
|
Name: "One task group service - new spec",
|
||||||
Job: &Job{
|
Job: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
Stagger: 2 * time.Second,
|
Stagger: 2 * time.Second,
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
|
@ -296,7 +307,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Expected: &Job{
|
Expected: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
Stagger: 2 * time.Second,
|
Stagger: 2 * time.Second,
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
|
@ -327,7 +339,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
Name: "One task group; too high of parallelism",
|
Name: "One task group; too high of parallelism",
|
||||||
Warnings: []string{"conversion to new update stanza"},
|
Warnings: []string{"conversion to new update stanza"},
|
||||||
Job: &Job{
|
Job: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
MaxParallel: 200,
|
MaxParallel: 200,
|
||||||
Stagger: 10 * time.Second,
|
Stagger: 10 * time.Second,
|
||||||
|
@ -340,7 +353,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Expected: &Job{
|
Expected: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
MaxParallel: 200,
|
MaxParallel: 200,
|
||||||
Stagger: 10 * time.Second,
|
Stagger: 10 * time.Second,
|
||||||
|
@ -368,7 +382,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
Name: "Multiple task group; rounding",
|
Name: "Multiple task group; rounding",
|
||||||
Warnings: []string{"conversion to new update stanza"},
|
Warnings: []string{"conversion to new update stanza"},
|
||||||
Job: &Job{
|
Job: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
Stagger: 10 * time.Second,
|
Stagger: 10 * time.Second,
|
||||||
|
@ -389,7 +404,8 @@ func TestJob_Canonicalize_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Expected: &Job{
|
Expected: &Job{
|
||||||
Type: JobTypeService,
|
Namespace: "test",
|
||||||
|
Type: JobTypeService,
|
||||||
Update: UpdateStrategy{
|
Update: UpdateStrategy{
|
||||||
MaxParallel: 2,
|
MaxParallel: 2,
|
||||||
Stagger: 10 * time.Second,
|
Stagger: 10 * time.Second,
|
||||||
|
@ -515,6 +531,7 @@ func testJob() *Job {
|
||||||
return &Job{
|
return &Job{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
ID: GenerateUUID(),
|
ID: GenerateUUID(),
|
||||||
|
Namespace: "test",
|
||||||
Name: "my-job",
|
Name: "my-job",
|
||||||
Type: JobTypeService,
|
Type: JobTypeService,
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
|
|
|
@ -49,7 +49,7 @@ func TestSystemEndpoint_GarbageCollect(t *testing.T) {
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
// Check if the job has been GC'd
|
// Check if the job has been GC'd
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
exist, err := state.JobByID(ws, job.ID)
|
exist, err := state.JobByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ func TestSystemEndpoint_ReconcileSummaries(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the job summary
|
// Delete the job summary
|
||||||
state.DeleteJobSummary(1001, job.ID)
|
state.DeleteJobSummary(1001, job.Namespace, job.ID)
|
||||||
|
|
||||||
// Make the GC request
|
// Make the GC request
|
||||||
req := &structs.GenericRequest{
|
req := &structs.GenericRequest{
|
||||||
|
@ -94,7 +94,7 @@ func TestSystemEndpoint_ReconcileSummaries(t *testing.T) {
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
testutil.WaitForResult(func() (bool, error) {
|
||||||
// Check if Nomad has reconciled the summary for the job
|
// Check if Nomad has reconciled the summary for the job
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
summary, err := state.JobSummaryByID(ws, job.ID)
|
summary, err := state.JobSummaryByID(ws, job.Namespace, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,8 @@ func TestSystemEndpoint_ReconcileSummaries(t *testing.T) {
|
||||||
// setting the modifyindex and createindex of the expected summary to
|
// setting the modifyindex and createindex of the expected summary to
|
||||||
// the output so that we can do deep equal
|
// the output so that we can do deep equal
|
||||||
expectedSummary := structs.JobSummary{
|
expectedSummary := structs.JobSummary{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
|
Namespace: job.Namespace,
|
||||||
Summary: map[string]structs.TaskGroupSummary{
|
Summary: map[string]structs.TaskGroupSummary{
|
||||||
"web": structs.TaskGroupSummary{
|
"web": structs.TaskGroupSummary{
|
||||||
Queued: 10,
|
Queued: 10,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
version "github.com/hashicorp/go-version"
|
||||||
"github.com/hashicorp/serf/serf"
|
"github.com/hashicorp/serf/serf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,7 +43,9 @@ type serverParts struct {
|
||||||
Expect int
|
Expect int
|
||||||
MajorVersion int
|
MajorVersion int
|
||||||
MinorVersion int
|
MinorVersion int
|
||||||
|
Build version.Version
|
||||||
Addr net.Addr
|
Addr net.Addr
|
||||||
|
Status serf.MemberStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serverParts) String() string {
|
func (s *serverParts) String() string {
|
||||||
|
@ -77,6 +80,11 @@ func isNomadServer(m serf.Member) (bool, *serverParts) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
build_version, err := version.NewVersion(m.Tags["build"])
|
||||||
|
if err != nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// The "vsn" tag was Version, which is now the MajorVersion number.
|
// The "vsn" tag was Version, which is now the MajorVersion number.
|
||||||
majorVersionStr := m.Tags["vsn"]
|
majorVersionStr := m.Tags["vsn"]
|
||||||
majorVersion, err := strconv.Atoi(majorVersionStr)
|
majorVersion, err := strconv.Atoi(majorVersionStr)
|
||||||
|
@ -103,10 +111,26 @@ func isNomadServer(m serf.Member) (bool, *serverParts) {
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
MajorVersion: majorVersion,
|
MajorVersion: majorVersion,
|
||||||
MinorVersion: minorVersion,
|
MinorVersion: minorVersion,
|
||||||
|
Build: *build_version,
|
||||||
|
Status: m.Status,
|
||||||
}
|
}
|
||||||
return true, parts
|
return true, parts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServersMeetMinimumVersion returns whether the given alive servers are at least on the
|
||||||
|
// given Nomad version
|
||||||
|
func ServersMeetMinimumVersion(members []serf.Member, minVersion *version.Version) bool {
|
||||||
|
for _, member := range members {
|
||||||
|
if valid, parts := isNomadServer(member); valid && parts.Status == serf.StatusAlive {
|
||||||
|
if parts.Build.LessThan(minVersion) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// shuffleStrings randomly shuffles the list of strings
|
// shuffleStrings randomly shuffles the list of strings
|
||||||
func shuffleStrings(list []string) {
|
func shuffleStrings(list []string) {
|
||||||
for i := range list {
|
for i := range list {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
version "github.com/hashicorp/go-version"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
"github.com/hashicorp/serf/serf"
|
"github.com/hashicorp/serf/serf"
|
||||||
)
|
)
|
||||||
|
@ -12,14 +13,16 @@ import (
|
||||||
func TestIsNomadServer(t *testing.T) {
|
func TestIsNomadServer(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
m := serf.Member{
|
m := serf.Member{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Addr: net.IP([]byte{127, 0, 0, 1}),
|
Addr: net.IP([]byte{127, 0, 0, 1}),
|
||||||
|
Status: serf.StatusAlive,
|
||||||
Tags: map[string]string{
|
Tags: map[string]string{
|
||||||
"role": "nomad",
|
"role": "nomad",
|
||||||
"region": "aws",
|
"region": "aws",
|
||||||
"dc": "east-aws",
|
"dc": "east-aws",
|
||||||
"port": "10000",
|
"port": "10000",
|
||||||
"vsn": "1",
|
"vsn": "1",
|
||||||
|
"build": "0.7.0+ent",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
valid, parts := isNomadServer(m)
|
valid, parts := isNomadServer(m)
|
||||||
|
@ -36,6 +39,14 @@ func TestIsNomadServer(t *testing.T) {
|
||||||
if parts.Expect != 0 {
|
if parts.Expect != 0 {
|
||||||
t.Fatalf("bad: %v", parts.Expect)
|
t.Fatalf("bad: %v", parts.Expect)
|
||||||
}
|
}
|
||||||
|
if parts.Status != serf.StatusAlive {
|
||||||
|
t.Fatalf("bad: %v", parts.Status)
|
||||||
|
}
|
||||||
|
if seg := parts.Build.Segments(); len(seg) != 3 {
|
||||||
|
t.Fatalf("bad: %v", parts.Build)
|
||||||
|
} else if seg[0] != 0 && seg[1] != 7 && seg[2] != 0 {
|
||||||
|
t.Fatalf("bad: %v", parts.Build)
|
||||||
|
}
|
||||||
|
|
||||||
m.Tags["bootstrap"] = "1"
|
m.Tags["bootstrap"] = "1"
|
||||||
valid, parts = isNomadServer(m)
|
valid, parts = isNomadServer(m)
|
||||||
|
@ -57,6 +68,89 @@ func TestIsNomadServer(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServersMeetMinimumVersion(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
makeMember := func(version string) serf.Member {
|
||||||
|
return serf.Member{
|
||||||
|
Name: "foo",
|
||||||
|
Addr: net.IP([]byte{127, 0, 0, 1}),
|
||||||
|
Tags: map[string]string{
|
||||||
|
"role": "nomad",
|
||||||
|
"region": "aws",
|
||||||
|
"dc": "east-aws",
|
||||||
|
"port": "10000",
|
||||||
|
"build": version,
|
||||||
|
"vsn": "1",
|
||||||
|
},
|
||||||
|
Status: serf.StatusAlive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
members []serf.Member
|
||||||
|
ver *version.Version
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
// One server, meets reqs
|
||||||
|
{
|
||||||
|
members: []serf.Member{
|
||||||
|
makeMember("0.7.5"),
|
||||||
|
},
|
||||||
|
ver: version.Must(version.NewVersion("0.7.5")),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
// One server in dev, meets reqs
|
||||||
|
{
|
||||||
|
members: []serf.Member{
|
||||||
|
makeMember("0.8.5-dev"),
|
||||||
|
},
|
||||||
|
ver: version.Must(version.NewVersion("0.7.5")),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
// One server with meta, meets reqs
|
||||||
|
{
|
||||||
|
members: []serf.Member{
|
||||||
|
makeMember("0.7.5+ent"),
|
||||||
|
},
|
||||||
|
ver: version.Must(version.NewVersion("0.7.5")),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
// One server, doesn't meet reqs
|
||||||
|
{
|
||||||
|
members: []serf.Member{
|
||||||
|
makeMember("0.7.5"),
|
||||||
|
},
|
||||||
|
ver: version.Must(version.NewVersion("0.8.0")),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
// Multiple servers, meets req version
|
||||||
|
{
|
||||||
|
members: []serf.Member{
|
||||||
|
makeMember("0.7.5"),
|
||||||
|
makeMember("0.8.0"),
|
||||||
|
},
|
||||||
|
ver: version.Must(version.NewVersion("0.7.5")),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
// Multiple servers, doesn't meet req version
|
||||||
|
{
|
||||||
|
members: []serf.Member{
|
||||||
|
makeMember("0.7.5"),
|
||||||
|
makeMember("0.8.0"),
|
||||||
|
},
|
||||||
|
ver: version.Must(version.NewVersion("0.8.0")),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
result := ServersMeetMinimumVersion(tc.members, tc.ver)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Fatalf("bad: %v, %v, %v", result, tc.ver.String(), tc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestShuffleStrings(t *testing.T) {
|
func TestShuffleStrings(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Generate input
|
// Generate input
|
||||||
|
|
|
@ -448,7 +448,7 @@ func (w *Worker) ReblockEval(eval *structs.Evaluation) error {
|
||||||
// Update the evaluation if the queued jobs is not same as what is
|
// Update the evaluation if the queued jobs is not same as what is
|
||||||
// recorded in the job summary
|
// recorded in the job summary
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
summary, err := w.srv.fsm.state.JobSummaryByID(ws, eval.JobID)
|
summary, err := w.srv.fsm.state.JobSummaryByID(ws, eval.Namespace, eval.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't retrieve job summary: %v", err)
|
return fmt.Errorf("couldn't retrieve job summary: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,11 +55,12 @@ func TestEvalContext_ProposedAlloc(t *testing.T) {
|
||||||
// Add existing allocations
|
// Add existing allocations
|
||||||
j1, j2 := mock.Job(), mock.Job()
|
j1, j2 := mock.Job(), mock.Job()
|
||||||
alloc1 := &structs.Allocation{
|
alloc1 := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
EvalID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
NodeID: nodes[0].Node.ID,
|
EvalID: structs.GenerateUUID(),
|
||||||
JobID: j1.ID,
|
NodeID: nodes[0].Node.ID,
|
||||||
Job: j1,
|
JobID: j1.ID,
|
||||||
|
Job: j1,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 2048,
|
CPU: 2048,
|
||||||
MemoryMB: 2048,
|
MemoryMB: 2048,
|
||||||
|
@ -69,11 +70,12 @@ func TestEvalContext_ProposedAlloc(t *testing.T) {
|
||||||
TaskGroup: "web",
|
TaskGroup: "web",
|
||||||
}
|
}
|
||||||
alloc2 := &structs.Allocation{
|
alloc2 := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
EvalID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
NodeID: nodes[1].Node.ID,
|
EvalID: structs.GenerateUUID(),
|
||||||
JobID: j2.ID,
|
NodeID: nodes[1].Node.ID,
|
||||||
Job: j2,
|
JobID: j2.ID,
|
||||||
|
Job: j2,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 1024,
|
CPU: 1024,
|
||||||
MemoryMB: 1024,
|
MemoryMB: 1024,
|
||||||
|
|
|
@ -452,6 +452,7 @@ func TestDistinctHostsIterator_JobDistinctHosts(t *testing.T) {
|
||||||
|
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
Constraints: []*structs.Constraint{{Operand: structs.ConstraintDistinctHosts}},
|
Constraints: []*structs.Constraint{{Operand: structs.ConstraintDistinctHosts}},
|
||||||
TaskGroups: []*structs.TaskGroup{tg1, tg2},
|
TaskGroups: []*structs.TaskGroup{tg1, tg2},
|
||||||
}
|
}
|
||||||
|
@ -461,6 +462,7 @@ func TestDistinctHostsIterator_JobDistinctHosts(t *testing.T) {
|
||||||
plan := ctx.Plan()
|
plan := ctx.Plan()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -469,6 +471,7 @@ func TestDistinctHostsIterator_JobDistinctHosts(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -477,6 +480,7 @@ func TestDistinctHostsIterator_JobDistinctHosts(t *testing.T) {
|
||||||
}
|
}
|
||||||
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -485,6 +489,7 @@ func TestDistinctHostsIterator_JobDistinctHosts(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -521,6 +526,7 @@ func TestDistinctHostsIterator_JobDistinctHosts_InfeasibleCount(t *testing.T) {
|
||||||
|
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
Constraints: []*structs.Constraint{{Operand: structs.ConstraintDistinctHosts}},
|
Constraints: []*structs.Constraint{{Operand: structs.ConstraintDistinctHosts}},
|
||||||
TaskGroups: []*structs.TaskGroup{tg1, tg2, tg3},
|
TaskGroups: []*structs.TaskGroup{tg1, tg2, tg3},
|
||||||
}
|
}
|
||||||
|
@ -530,6 +536,7 @@ func TestDistinctHostsIterator_JobDistinctHosts_InfeasibleCount(t *testing.T) {
|
||||||
plan := ctx.Plan()
|
plan := ctx.Plan()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
@ -537,6 +544,7 @@ func TestDistinctHostsIterator_JobDistinctHosts_InfeasibleCount(t *testing.T) {
|
||||||
}
|
}
|
||||||
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
@ -575,6 +583,7 @@ func TestDistinctHostsIterator_TaskGroupDistinctHosts(t *testing.T) {
|
||||||
plan := ctx.Plan()
|
plan := ctx.Plan()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "foo",
|
JobID: "foo",
|
||||||
},
|
},
|
||||||
|
@ -584,6 +593,7 @@ func TestDistinctHostsIterator_TaskGroupDistinctHosts(t *testing.T) {
|
||||||
// different job.
|
// different job.
|
||||||
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "bar",
|
JobID: "bar",
|
||||||
},
|
},
|
||||||
|
@ -591,7 +601,10 @@ func TestDistinctHostsIterator_TaskGroupDistinctHosts(t *testing.T) {
|
||||||
|
|
||||||
proposed := NewDistinctHostsIterator(ctx, static)
|
proposed := NewDistinctHostsIterator(ctx, static)
|
||||||
proposed.SetTaskGroup(tg1)
|
proposed.SetTaskGroup(tg1)
|
||||||
proposed.SetJob(&structs.Job{ID: "foo"})
|
proposed.SetJob(&structs.Job{
|
||||||
|
ID: "foo",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
|
})
|
||||||
|
|
||||||
out := collectFeasible(proposed)
|
out := collectFeasible(proposed)
|
||||||
if len(out) != 1 {
|
if len(out) != 1 {
|
||||||
|
@ -643,7 +656,8 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
tg2 := &structs.TaskGroup{Name: "baz"}
|
tg2 := &structs.TaskGroup{Name: "baz"}
|
||||||
|
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
Constraints: []*structs.Constraint{
|
Constraints: []*structs.Constraint{
|
||||||
{
|
{
|
||||||
Operand: structs.ConstraintDistinctProperty,
|
Operand: structs.ConstraintDistinctProperty,
|
||||||
|
@ -660,6 +674,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
alloc1ID := structs.GenerateUUID()
|
alloc1ID := structs.GenerateUUID()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -669,6 +684,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -678,6 +694,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
}
|
}
|
||||||
plan.NodeAllocation[nodes[2].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[2].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -687,6 +704,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -699,6 +717,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
stoppingAllocID := structs.GenerateUUID()
|
stoppingAllocID := structs.GenerateUUID()
|
||||||
plan.NodeUpdate[nodes[4].ID] = []*structs.Allocation{
|
plan.NodeUpdate[nodes[4].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -711,6 +730,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
// Have one of the allocations exist in both the plan and the state
|
// Have one of the allocations exist in both the plan and the state
|
||||||
// store. This resembles an allocation update
|
// store. This resembles an allocation update
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -720,6 +740,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -730,6 +751,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -738,6 +760,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
NodeID: nodes[1].ID,
|
NodeID: nodes[1].ID,
|
||||||
},
|
},
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -748,6 +771,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -756,6 +780,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty(t *testing.T) {
|
||||||
NodeID: nodes[3].ID,
|
NodeID: nodes[3].ID,
|
||||||
},
|
},
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -809,7 +834,8 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
tg2 := &structs.TaskGroup{Name: "baz"}
|
tg2 := &structs.TaskGroup{Name: "baz"}
|
||||||
|
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
Constraints: []*structs.Constraint{
|
Constraints: []*structs.Constraint{
|
||||||
{
|
{
|
||||||
Operand: structs.ConstraintDistinctProperty,
|
Operand: structs.ConstraintDistinctProperty,
|
||||||
|
@ -827,6 +853,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
alloc1ID := structs.GenerateUUID()
|
alloc1ID := structs.GenerateUUID()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -835,6 +862,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -844,6 +872,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -853,6 +882,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
}
|
}
|
||||||
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[1].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -861,6 +891,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -870,6 +901,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -879,6 +911,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
}
|
}
|
||||||
plan.NodeAllocation[nodes[2].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[2].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -888,6 +921,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -900,6 +934,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
stoppingAllocID := structs.GenerateUUID()
|
stoppingAllocID := structs.GenerateUUID()
|
||||||
plan.NodeUpdate[nodes[2].ID] = []*structs.Allocation{
|
plan.NodeUpdate[nodes[2].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -912,6 +947,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
// Have one of the allocations exist in both the plan and the state
|
// Have one of the allocations exist in both the plan and the state
|
||||||
// store. This resembles an allocation update
|
// store. This resembles an allocation update
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -921,6 +957,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -930,6 +967,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -940,6 +978,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -948,6 +987,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Count(t *testing.T) {
|
||||||
NodeID: nodes[1].ID,
|
NodeID: nodes[1].ID,
|
||||||
},
|
},
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -995,7 +1035,8 @@ func TestDistinctPropertyIterator_JobDistinctProperty_RemoveAndReplace(t *testin
|
||||||
// Create a job with a distinct_property constraint and a task groups.
|
// Create a job with a distinct_property constraint and a task groups.
|
||||||
tg1 := &structs.TaskGroup{Name: "bar"}
|
tg1 := &structs.TaskGroup{Name: "bar"}
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
ID: "foo",
|
Namespace: structs.DefaultNamespace,
|
||||||
|
ID: "foo",
|
||||||
Constraints: []*structs.Constraint{
|
Constraints: []*structs.Constraint{
|
||||||
{
|
{
|
||||||
Operand: structs.ConstraintDistinctProperty,
|
Operand: structs.ConstraintDistinctProperty,
|
||||||
|
@ -1008,6 +1049,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_RemoveAndReplace(t *testin
|
||||||
plan := ctx.Plan()
|
plan := ctx.Plan()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1019,6 +1061,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_RemoveAndReplace(t *testin
|
||||||
stoppingAllocID := structs.GenerateUUID()
|
stoppingAllocID := structs.GenerateUUID()
|
||||||
plan.NodeUpdate[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeUpdate[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1029,6 +1072,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_RemoveAndReplace(t *testin
|
||||||
|
|
||||||
upserting := []*structs.Allocation{
|
upserting := []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1079,7 +1123,8 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible(t *testing.T) {
|
||||||
tg3 := &structs.TaskGroup{Name: "bam"}
|
tg3 := &structs.TaskGroup{Name: "bam"}
|
||||||
|
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
ID: "foo",
|
Namespace: structs.DefaultNamespace,
|
||||||
|
ID: "foo",
|
||||||
Constraints: []*structs.Constraint{
|
Constraints: []*structs.Constraint{
|
||||||
{
|
{
|
||||||
Operand: structs.ConstraintDistinctProperty,
|
Operand: structs.ConstraintDistinctProperty,
|
||||||
|
@ -1094,6 +1139,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible(t *testing.T) {
|
||||||
plan := ctx.Plan()
|
plan := ctx.Plan()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1103,6 +1149,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible(t *testing.T) {
|
||||||
}
|
}
|
||||||
upserting := []*structs.Allocation{
|
upserting := []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1153,7 +1200,8 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible_Count(t *testin
|
||||||
tg3 := &structs.TaskGroup{Name: "bam"}
|
tg3 := &structs.TaskGroup{Name: "bam"}
|
||||||
|
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
ID: "foo",
|
Namespace: structs.DefaultNamespace,
|
||||||
|
ID: "foo",
|
||||||
Constraints: []*structs.Constraint{
|
Constraints: []*structs.Constraint{
|
||||||
{
|
{
|
||||||
Operand: structs.ConstraintDistinctProperty,
|
Operand: structs.ConstraintDistinctProperty,
|
||||||
|
@ -1169,6 +1217,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible_Count(t *testin
|
||||||
plan := ctx.Plan()
|
plan := ctx.Plan()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1176,6 +1225,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible_Count(t *testin
|
||||||
NodeID: nodes[0].ID,
|
NodeID: nodes[0].ID,
|
||||||
},
|
},
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1185,6 +1235,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible_Count(t *testin
|
||||||
}
|
}
|
||||||
upserting := []*structs.Allocation{
|
upserting := []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1193,6 +1244,7 @@ func TestDistinctPropertyIterator_JobDistinctProperty_Infeasible_Count(t *testin
|
||||||
NodeID: nodes[1].ID,
|
NodeID: nodes[1].ID,
|
||||||
},
|
},
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg2.Name,
|
TaskGroup: tg2.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1251,6 +1303,7 @@ func TestDistinctPropertyIterator_TaskGroupDistinctProperty(t *testing.T) {
|
||||||
tg2 := &structs.TaskGroup{Name: "baz"}
|
tg2 := &structs.TaskGroup{Name: "baz"}
|
||||||
|
|
||||||
job := &structs.Job{
|
job := &structs.Job{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
TaskGroups: []*structs.TaskGroup{tg1, tg2},
|
TaskGroups: []*structs.TaskGroup{tg1, tg2},
|
||||||
}
|
}
|
||||||
|
@ -1261,6 +1314,7 @@ func TestDistinctPropertyIterator_TaskGroupDistinctProperty(t *testing.T) {
|
||||||
plan := ctx.Plan()
|
plan := ctx.Plan()
|
||||||
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
plan.NodeAllocation[nodes[0].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1273,6 +1327,7 @@ func TestDistinctPropertyIterator_TaskGroupDistinctProperty(t *testing.T) {
|
||||||
stoppingAllocID := structs.GenerateUUID()
|
stoppingAllocID := structs.GenerateUUID()
|
||||||
plan.NodeUpdate[nodes[2].ID] = []*structs.Allocation{
|
plan.NodeUpdate[nodes[2].ID] = []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1283,6 +1338,7 @@ func TestDistinctPropertyIterator_TaskGroupDistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
upserting := []*structs.Allocation{
|
upserting := []*structs.Allocation{
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1293,6 +1349,7 @@ func TestDistinctPropertyIterator_TaskGroupDistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Should be ignored as it is a different job.
|
// Should be ignored as it is a different job.
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: "ignore 2",
|
JobID: "ignore 2",
|
||||||
Job: job,
|
Job: job,
|
||||||
|
@ -1302,6 +1359,7 @@ func TestDistinctPropertyIterator_TaskGroupDistinctProperty(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
&structs.Allocation{
|
&structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
TaskGroup: tg1.Name,
|
TaskGroup: tg1.Name,
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Job: job,
|
Job: job,
|
||||||
|
|
|
@ -191,7 +191,7 @@ func (s *GenericScheduler) process() (bool, error) {
|
||||||
// Lookup the Job by ID
|
// Lookup the Job by ID
|
||||||
var err error
|
var err error
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
s.job, err = s.state.JobByID(ws, s.eval.JobID)
|
s.job, err = s.state.JobByID(ws, s.eval.Namespace, s.eval.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get job %q: %v", s.eval.JobID, err)
|
return false, fmt.Errorf("failed to get job %q: %v", s.eval.JobID, err)
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ func (s *GenericScheduler) process() (bool, error) {
|
||||||
|
|
||||||
if !s.batch {
|
if !s.batch {
|
||||||
// Get any existing deployment
|
// Get any existing deployment
|
||||||
s.deployment, err = s.state.LatestDeploymentByJobID(ws, s.eval.JobID)
|
s.deployment, err = s.state.LatestDeploymentByJobID(ws, s.eval.Namespace, s.eval.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get job deployment %q: %v", s.eval.JobID, err)
|
return false, fmt.Errorf("failed to get job deployment %q: %v", s.eval.JobID, err)
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ func (s *GenericScheduler) filterCompleteAllocs(allocs []*structs.Allocation) ([
|
||||||
func (s *GenericScheduler) computeJobAllocs() error {
|
func (s *GenericScheduler) computeJobAllocs() error {
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
allocs, err := s.state.AllocsByJob(ws, s.eval.JobID, true)
|
allocs, err := s.state.AllocsByJob(ws, s.eval.Namespace, s.eval.JobID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get allocs for job '%s': %v",
|
return fmt.Errorf("failed to get allocs for job '%s': %v",
|
||||||
s.eval.JobID, err)
|
s.eval.JobID, err)
|
||||||
|
@ -517,6 +517,7 @@ func (s *GenericScheduler) computePlacements(destructive, place []placementResul
|
||||||
// Create an allocation for this
|
// Create an allocation for this
|
||||||
alloc := &structs.Allocation{
|
alloc := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: s.job.Namespace,
|
||||||
EvalID: s.eval.ID,
|
EvalID: s.eval.ID,
|
||||||
Name: missing.Name(),
|
Name: missing.Name(),
|
||||||
JobID: s.job.ID,
|
JobID: s.job.ID,
|
||||||
|
|
|
@ -29,6 +29,7 @@ func TestServiceSched_JobRegister(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -71,7 +72,7 @@ func TestServiceSched_JobRegister(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -111,6 +112,7 @@ func TestServiceSched_JobRegister_StickyAllocs(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -141,6 +143,7 @@ func TestServiceSched_JobRegister_StickyAllocs(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to handle the update
|
// Create a mock evaluation to handle the update
|
||||||
eval = &structs.Evaluation{
|
eval = &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -197,6 +200,7 @@ func TestServiceSched_JobRegister_DiskConstraints(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -236,7 +240,7 @@ func TestServiceSched_JobRegister_DiskConstraints(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure only one allocation was placed
|
// Ensure only one allocation was placed
|
||||||
|
@ -265,6 +269,7 @@ func TestServiceSched_JobRegister_DistinctHosts(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -305,7 +310,7 @@ func TestServiceSched_JobRegister_DistinctHosts(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -353,6 +358,7 @@ func TestServiceSched_JobRegister_DistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -398,7 +404,7 @@ func TestServiceSched_JobRegister_DistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -444,6 +450,7 @@ func TestServiceSched_JobRegister_DistinctProperty_TaskGroup(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -483,7 +490,7 @@ func TestServiceSched_JobRegister_DistinctProperty_TaskGroup(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -535,6 +542,7 @@ func TestServiceSched_JobRegister_DistinctProperty_TaskGroup_Incr(t *testing.T)
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -563,7 +571,7 @@ func TestServiceSched_JobRegister_DistinctProperty_TaskGroup_Incr(t *testing.T)
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
assert.Nil(err, "AllocsByJob")
|
assert.Nil(err, "AllocsByJob")
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -587,6 +595,7 @@ func TestServiceSched_JobRegister_Annotate(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -617,7 +626,7 @@ func TestServiceSched_JobRegister_Annotate(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -664,6 +673,7 @@ func TestServiceSched_JobRegister_CountZero(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -683,7 +693,7 @@ func TestServiceSched_JobRegister_CountZero(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure no allocations placed
|
// Ensure no allocations placed
|
||||||
|
@ -704,6 +714,7 @@ func TestServiceSched_JobRegister_AllocFail(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -785,6 +796,7 @@ func TestServiceSched_JobRegister_CreateBlockedEval(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -881,6 +893,7 @@ func TestServiceSched_JobRegister_FeasibleAndInfeasibleTG(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -910,7 +923,7 @@ func TestServiceSched_JobRegister_FeasibleAndInfeasibleTG(t *testing.T) {
|
||||||
|
|
||||||
// Ensure two allocations placed
|
// Ensure two allocations placed
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
if len(out) != 2 {
|
if len(out) != 2 {
|
||||||
t.Fatalf("bad: %#v", out)
|
t.Fatalf("bad: %#v", out)
|
||||||
|
@ -956,6 +969,7 @@ func TestServiceSched_EvaluateMaxPlanEval(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock blocked evaluation
|
// Create a mock blocked evaluation
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Status: structs.EvalStatusBlocked,
|
Status: structs.EvalStatusBlocked,
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
|
@ -996,6 +1010,7 @@ func TestServiceSched_Plan_Partial_Progress(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1030,7 +1045,7 @@ func TestServiceSched_Plan_Partial_Progress(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure only one allocations placed
|
// Ensure only one allocations placed
|
||||||
|
@ -1055,6 +1070,7 @@ func TestServiceSched_EvaluateBlockedEval(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock blocked evaluation
|
// Create a mock blocked evaluation
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Status: structs.EvalStatusBlocked,
|
Status: structs.EvalStatusBlocked,
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
|
@ -1105,6 +1121,7 @@ func TestServiceSched_EvaluateBlockedEval_Finished(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock blocked evaluation
|
// Create a mock blocked evaluation
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Status: structs.EvalStatusBlocked,
|
Status: structs.EvalStatusBlocked,
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
|
@ -1151,7 +1168,7 @@ func TestServiceSched_EvaluateBlockedEval_Finished(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -1222,6 +1239,7 @@ func TestServiceSched_JobModify(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1260,7 +1278,7 @@ func TestServiceSched_JobModify(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -1305,6 +1323,7 @@ func TestServiceSched_JobModify_IncrCount_NodeLimit(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1352,7 +1371,7 @@ func TestServiceSched_JobModify_IncrCount_NodeLimit(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -1411,6 +1430,7 @@ func TestServiceSched_JobModify_CountZero(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1449,7 +1469,7 @@ func TestServiceSched_JobModify_CountZero(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -1504,6 +1524,7 @@ func TestServiceSched_JobModify_Rolling(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1608,6 +1629,7 @@ func TestServiceSched_JobModify_Rolling_FullNode(t *testing.T) {
|
||||||
noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
|
noErr(t, h.State.UpsertJob(h.NextIndex(), job2))
|
||||||
|
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1708,6 +1730,7 @@ func TestServiceSched_JobModify_Canaries(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1815,6 +1838,7 @@ func TestServiceSched_JobModify_InPlace(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1858,7 +1882,7 @@ func TestServiceSched_JobModify_InPlace(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -1928,6 +1952,7 @@ func TestServiceSched_JobModify_DistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1973,7 +1998,7 @@ func TestServiceSched_JobModify_DistinctProperty(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -2013,6 +2038,7 @@ func TestServiceSched_JobDeregister_Purged(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobDeregister,
|
TriggeredBy: structs.EvalTriggerJobDeregister,
|
||||||
|
@ -2038,7 +2064,7 @@ func TestServiceSched_JobDeregister_Purged(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure that the job field on the allocation is still populated
|
// Ensure that the job field on the allocation is still populated
|
||||||
|
@ -2079,6 +2105,7 @@ func TestServiceSched_JobDeregister_Stopped(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobDeregister,
|
TriggeredBy: structs.EvalTriggerJobDeregister,
|
||||||
|
@ -2104,7 +2131,7 @@ func TestServiceSched_JobDeregister_Stopped(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure that the job field on the allocation is still populated
|
// Ensure that the job field on the allocation is still populated
|
||||||
|
@ -2167,6 +2194,7 @@ func TestServiceSched_NodeDown(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -2232,6 +2260,7 @@ func TestServiceSched_NodeUpdate(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation which won't trigger any new placements
|
// Create a mock evaluation which won't trigger any new placements
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -2282,6 +2311,7 @@ func TestServiceSched_NodeDrain(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -2317,7 +2347,7 @@ func TestServiceSched_NodeDrain(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -2382,6 +2412,7 @@ func TestServiceSched_NodeDrain_Down(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with the node update
|
// Create a mock evaluation to deal with the node update
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -2455,6 +2486,7 @@ func TestServiceSched_NodeDrain_Queued_Allocations(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -2511,6 +2543,7 @@ func TestServiceSched_NodeDrain_UpdateStrategy(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -2569,6 +2602,7 @@ func TestServiceSched_RetryLimit(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -2588,7 +2622,7 @@ func TestServiceSched_RetryLimit(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure no allocations placed
|
// Ensure no allocations placed
|
||||||
|
@ -2623,6 +2657,7 @@ func TestBatchSched_Run_CompleteAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -2642,7 +2677,7 @@ func TestBatchSched_Run_CompleteAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure no allocations placed
|
// Ensure no allocations placed
|
||||||
|
@ -2677,6 +2712,7 @@ func TestBatchSched_Run_DrainedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -2696,7 +2732,7 @@ func TestBatchSched_Run_DrainedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure a replacement alloc was placed.
|
// Ensure a replacement alloc was placed.
|
||||||
|
@ -2730,6 +2766,7 @@ func TestBatchSched_Run_FailedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -2749,7 +2786,7 @@ func TestBatchSched_Run_FailedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure a replacement alloc was placed.
|
// Ensure a replacement alloc was placed.
|
||||||
|
@ -2790,6 +2827,7 @@ func TestBatchSched_Run_FailedAllocQueuedAllocations(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -2849,6 +2887,7 @@ func TestBatchSched_ReRun_SuccessfullyFinishedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to rerun the job
|
// Create a mock evaluation to rerun the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -2868,7 +2907,7 @@ func TestBatchSched_ReRun_SuccessfullyFinishedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure no replacement alloc was placed.
|
// Ensure no replacement alloc was placed.
|
||||||
|
@ -2916,6 +2955,7 @@ func TestBatchSched_JobModify_InPlace_Terminal(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -3046,6 +3086,7 @@ func TestGenericSched_ChainedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -3074,6 +3115,7 @@ func TestGenericSched_ChainedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to update the job
|
// Create a mock evaluation to update the job
|
||||||
eval1 := &structs.Evaluation{
|
eval1 := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job1.Priority,
|
Priority: job1.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -3133,6 +3175,7 @@ func TestServiceSched_NodeDrain_Sticky(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -3190,6 +3233,7 @@ func TestServiceSched_CancelDeployment_Stopped(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobDeregister,
|
TriggeredBy: structs.EvalTriggerJobDeregister,
|
||||||
|
@ -3210,7 +3254,7 @@ func TestServiceSched_CancelDeployment_Stopped(t *testing.T) {
|
||||||
|
|
||||||
// Ensure the plan cancelled the existing deployment
|
// Ensure the plan cancelled the existing deployment
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.LatestDeploymentByJobID(ws, job.ID)
|
out, err := h.State.LatestDeploymentByJobID(ws, job.Namespace, job.ID)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
if out == nil {
|
if out == nil {
|
||||||
|
@ -3258,6 +3302,7 @@ func TestServiceSched_CancelDeployment_NewerJob(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to kick the job
|
// Create a mock evaluation to kick the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -3278,7 +3323,7 @@ func TestServiceSched_CancelDeployment_NewerJob(t *testing.T) {
|
||||||
|
|
||||||
// Ensure the plan cancelled the existing deployment
|
// Ensure the plan cancelled the existing deployment
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.LatestDeploymentByJobID(ws, job.ID)
|
out, err := h.State.LatestDeploymentByJobID(ws, job.Namespace, job.ID)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
if out == nil {
|
if out == nil {
|
||||||
|
|
|
@ -17,6 +17,9 @@ type propertySet struct {
|
||||||
// jobID is the job we are operating on
|
// jobID is the job we are operating on
|
||||||
jobID string
|
jobID string
|
||||||
|
|
||||||
|
// namespace is the namespace of the job we are operating on
|
||||||
|
namespace string
|
||||||
|
|
||||||
// taskGroup is optionally set if the constraint is for a task group
|
// taskGroup is optionally set if the constraint is for a task group
|
||||||
taskGroup string
|
taskGroup string
|
||||||
|
|
||||||
|
@ -50,6 +53,7 @@ func NewPropertySet(ctx Context, job *structs.Job) *propertySet {
|
||||||
p := &propertySet{
|
p := &propertySet{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
jobID: job.ID,
|
jobID: job.ID,
|
||||||
|
namespace: job.Namespace,
|
||||||
existingValues: make(map[string]uint64),
|
existingValues: make(map[string]uint64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +113,7 @@ func (p *propertySet) setConstraint(constraint *structs.Constraint, taskGroup st
|
||||||
func (p *propertySet) populateExisting(constraint *structs.Constraint) {
|
func (p *propertySet) populateExisting(constraint *structs.Constraint) {
|
||||||
// Retrieve all previously placed allocations
|
// Retrieve all previously placed allocations
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
allocs, err := p.ctx.State().AllocsByJob(ws, p.jobID, false)
|
allocs, err := p.ctx.State().AllocsByJob(ws, p.namespace, p.jobID, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.errorBuilding = fmt.Errorf("failed to get job's allocations: %v", err)
|
p.errorBuilding = fmt.Errorf("failed to get job's allocations: %v", err)
|
||||||
p.ctx.Logger().Printf("[ERR] scheduler.dynamic-constraint: %v", p.errorBuilding)
|
p.ctx.Logger().Printf("[ERR] scheduler.dynamic-constraint: %v", p.errorBuilding)
|
||||||
|
|
|
@ -204,11 +204,12 @@ func TestBinPackIterator_ExistingAlloc(t *testing.T) {
|
||||||
// Add existing allocations
|
// Add existing allocations
|
||||||
j1, j2 := mock.Job(), mock.Job()
|
j1, j2 := mock.Job(), mock.Job()
|
||||||
alloc1 := &structs.Allocation{
|
alloc1 := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
EvalID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
NodeID: nodes[0].Node.ID,
|
EvalID: structs.GenerateUUID(),
|
||||||
JobID: j1.ID,
|
NodeID: nodes[0].Node.ID,
|
||||||
Job: j1,
|
JobID: j1.ID,
|
||||||
|
Job: j1,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 2048,
|
CPU: 2048,
|
||||||
MemoryMB: 2048,
|
MemoryMB: 2048,
|
||||||
|
@ -218,11 +219,12 @@ func TestBinPackIterator_ExistingAlloc(t *testing.T) {
|
||||||
TaskGroup: "web",
|
TaskGroup: "web",
|
||||||
}
|
}
|
||||||
alloc2 := &structs.Allocation{
|
alloc2 := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
EvalID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
NodeID: nodes[1].Node.ID,
|
EvalID: structs.GenerateUUID(),
|
||||||
JobID: j2.ID,
|
NodeID: nodes[1].Node.ID,
|
||||||
Job: j2,
|
JobID: j2.ID,
|
||||||
|
Job: j2,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 1024,
|
CPU: 1024,
|
||||||
MemoryMB: 1024,
|
MemoryMB: 1024,
|
||||||
|
@ -291,11 +293,12 @@ func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) {
|
||||||
// Add existing allocations
|
// Add existing allocations
|
||||||
j1, j2 := mock.Job(), mock.Job()
|
j1, j2 := mock.Job(), mock.Job()
|
||||||
alloc1 := &structs.Allocation{
|
alloc1 := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
EvalID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
NodeID: nodes[0].Node.ID,
|
EvalID: structs.GenerateUUID(),
|
||||||
JobID: j1.ID,
|
NodeID: nodes[0].Node.ID,
|
||||||
Job: j1,
|
JobID: j1.ID,
|
||||||
|
Job: j1,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 2048,
|
CPU: 2048,
|
||||||
MemoryMB: 2048,
|
MemoryMB: 2048,
|
||||||
|
@ -305,11 +308,12 @@ func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) {
|
||||||
TaskGroup: "web",
|
TaskGroup: "web",
|
||||||
}
|
}
|
||||||
alloc2 := &structs.Allocation{
|
alloc2 := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
EvalID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
NodeID: nodes[1].Node.ID,
|
EvalID: structs.GenerateUUID(),
|
||||||
JobID: j2.ID,
|
NodeID: nodes[1].Node.ID,
|
||||||
Job: j2,
|
JobID: j2.ID,
|
||||||
|
Job: j2,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 1024,
|
CPU: 1024,
|
||||||
MemoryMB: 1024,
|
MemoryMB: 1024,
|
||||||
|
|
|
@ -66,7 +66,7 @@ type State interface {
|
||||||
Nodes(ws memdb.WatchSet) (memdb.ResultIterator, error)
|
Nodes(ws memdb.WatchSet) (memdb.ResultIterator, error)
|
||||||
|
|
||||||
// AllocsByJob returns the allocations by JobID
|
// AllocsByJob returns the allocations by JobID
|
||||||
AllocsByJob(ws memdb.WatchSet, jobID string, all bool) ([]*structs.Allocation, error)
|
AllocsByJob(ws memdb.WatchSet, namespace, jobID string, all bool) ([]*structs.Allocation, error)
|
||||||
|
|
||||||
// AllocsByNode returns all the allocations by node
|
// AllocsByNode returns all the allocations by node
|
||||||
AllocsByNode(ws memdb.WatchSet, node string) ([]*structs.Allocation, error)
|
AllocsByNode(ws memdb.WatchSet, node string) ([]*structs.Allocation, error)
|
||||||
|
@ -78,11 +78,11 @@ type State interface {
|
||||||
NodeByID(ws memdb.WatchSet, nodeID string) (*structs.Node, error)
|
NodeByID(ws memdb.WatchSet, nodeID string) (*structs.Node, error)
|
||||||
|
|
||||||
// GetJobByID is used to lookup a job by ID
|
// GetJobByID is used to lookup a job by ID
|
||||||
JobByID(ws memdb.WatchSet, id string) (*structs.Job, error)
|
JobByID(ws memdb.WatchSet, namespace, id string) (*structs.Job, error)
|
||||||
|
|
||||||
// LatestDeploymentByJobID returns the latest deployment matching the given
|
// LatestDeploymentByJobID returns the latest deployment matching the given
|
||||||
// job ID
|
// job ID
|
||||||
LatestDeploymentByJobID(ws memdb.WatchSet, jobID string) (*structs.Deployment, error)
|
LatestDeploymentByJobID(ws memdb.WatchSet, namespace, jobID string) (*structs.Deployment, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Planner interface is used to submit a task allocation plan.
|
// Planner interface is used to submit a task allocation plan.
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (s *SystemScheduler) process() (bool, error) {
|
||||||
// Lookup the Job by ID
|
// Lookup the Job by ID
|
||||||
var err error
|
var err error
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
s.job, err = s.state.JobByID(ws, s.eval.JobID)
|
s.job, err = s.state.JobByID(ws, s.eval.Namespace, s.eval.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get job '%s': %v",
|
return false, fmt.Errorf("failed to get job '%s': %v",
|
||||||
s.eval.JobID, err)
|
s.eval.JobID, err)
|
||||||
|
@ -182,7 +182,7 @@ func (s *SystemScheduler) process() (bool, error) {
|
||||||
func (s *SystemScheduler) computeJobAllocs() error {
|
func (s *SystemScheduler) computeJobAllocs() error {
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
allocs, err := s.state.AllocsByJob(ws, s.eval.JobID, true)
|
allocs, err := s.state.AllocsByJob(ws, s.eval.Namespace, s.eval.JobID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get allocs for job '%s': %v",
|
return fmt.Errorf("failed to get allocs for job '%s': %v",
|
||||||
s.eval.JobID, err)
|
s.eval.JobID, err)
|
||||||
|
@ -307,6 +307,7 @@ func (s *SystemScheduler) computePlacements(place []allocTuple) error {
|
||||||
// Create an allocation for this
|
// Create an allocation for this
|
||||||
alloc := &structs.Allocation{
|
alloc := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
|
Namespace: s.job.Namespace,
|
||||||
EvalID: s.eval.ID,
|
EvalID: s.eval.ID,
|
||||||
Name: missing.Name,
|
Name: missing.Name,
|
||||||
JobID: s.job.ID,
|
JobID: s.job.ID,
|
||||||
|
|
|
@ -26,6 +26,7 @@ func TestSystemSched_JobRegister(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -60,7 +61,7 @@ func TestSystemSched_JobRegister(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -98,6 +99,7 @@ func TestSystemeSched_JobRegister_StickyAllocs(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -126,6 +128,7 @@ func TestSystemeSched_JobRegister_StickyAllocs(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to handle the update
|
// Create a mock evaluation to handle the update
|
||||||
eval = &structs.Evaluation{
|
eval = &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -172,6 +175,7 @@ func TestSystemSched_JobRegister_EphemeralDiskConstraint(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -185,7 +189,7 @@ func TestSystemSched_JobRegister_EphemeralDiskConstraint(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -197,6 +201,7 @@ func TestSystemSched_JobRegister_EphemeralDiskConstraint(t *testing.T) {
|
||||||
h1 := NewHarnessWithState(t, h.State)
|
h1 := NewHarnessWithState(t, h.State)
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval1 := &structs.Evaluation{
|
eval1 := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job1.Priority,
|
Priority: job1.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -208,7 +213,7 @@ func TestSystemSched_JobRegister_EphemeralDiskConstraint(t *testing.T) {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err = h1.State.AllocsByJob(ws, job1.ID, false)
|
out, err = h1.State.AllocsByJob(ws, job.Namespace, job1.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
if len(out) != 0 {
|
if len(out) != 0 {
|
||||||
t.Fatalf("bad: %#v", out)
|
t.Fatalf("bad: %#v", out)
|
||||||
|
@ -230,6 +235,7 @@ func TestSystemSched_ExhaustResources(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: svcJob.Priority,
|
Priority: svcJob.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -248,6 +254,7 @@ func TestSystemSched_ExhaustResources(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval1 := &structs.Evaluation{
|
eval1 := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -293,6 +300,7 @@ func TestSystemSched_JobRegister_Annotate(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -323,7 +331,7 @@ func TestSystemSched_JobRegister_Annotate(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -391,6 +399,7 @@ func TestSystemSched_JobRegister_AddNode(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with the node update
|
// Create a mock evaluation to deal with the node update
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -435,7 +444,7 @@ func TestSystemSched_JobRegister_AddNode(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -457,6 +466,7 @@ func TestSystemSched_JobRegister_AllocFail(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -526,6 +536,7 @@ func TestSystemSched_JobModify(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -564,7 +575,7 @@ func TestSystemSched_JobModify(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -616,6 +627,7 @@ func TestSystemSched_JobModify_Rolling(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -710,6 +722,7 @@ func TestSystemSched_JobModify_InPlace(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -753,7 +766,7 @@ func TestSystemSched_JobModify_InPlace(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure all allocations placed
|
// Ensure all allocations placed
|
||||||
|
@ -803,6 +816,7 @@ func TestSystemSched_JobDeregister_Purged(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobDeregister,
|
TriggeredBy: structs.EvalTriggerJobDeregister,
|
||||||
|
@ -830,7 +844,7 @@ func TestSystemSched_JobDeregister_Purged(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure no remaining allocations
|
// Ensure no remaining allocations
|
||||||
|
@ -874,6 +888,7 @@ func TestSystemSched_JobDeregister_Stopped(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerJobDeregister,
|
TriggeredBy: structs.EvalTriggerJobDeregister,
|
||||||
|
@ -901,7 +916,7 @@ func TestSystemSched_JobDeregister_Stopped(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure no remaining allocations
|
// Ensure no remaining allocations
|
||||||
|
@ -934,6 +949,7 @@ func TestSystemSched_NodeDown(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -998,6 +1014,7 @@ func TestSystemSched_NodeDrain_Down(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with the node update
|
// Create a mock evaluation to deal with the node update
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -1056,6 +1073,7 @@ func TestSystemSched_NodeDrain(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -1118,6 +1136,7 @@ func TestSystemSched_NodeUpdate(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal
|
// Create a mock evaluation to deal
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -1155,6 +1174,7 @@ func TestSystemSched_RetryLimit(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deregister the job
|
// Create a mock evaluation to deregister the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1174,7 +1194,7 @@ func TestSystemSched_RetryLimit(t *testing.T) {
|
||||||
|
|
||||||
// Lookup the allocations by JobID
|
// Lookup the allocations by JobID
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
out, err := h.State.AllocsByJob(ws, job.ID, false)
|
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
|
||||||
noErr(t, err)
|
noErr(t, err)
|
||||||
|
|
||||||
// Ensure no allocations placed
|
// Ensure no allocations placed
|
||||||
|
@ -1203,6 +1223,7 @@ func TestSystemSched_Queued_With_Constraints(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal
|
// Create a mock evaluation to deal
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -1237,6 +1258,7 @@ func TestSystemSched_ChainedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to register the job
|
// Create a mock evaluation to register the job
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job.Priority,
|
Priority: job.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1271,6 +1293,7 @@ func TestSystemSched_ChainedAlloc(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to update the job
|
// Create a mock evaluation to update the job
|
||||||
eval1 := &structs.Evaluation{
|
eval1 := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: job1.Priority,
|
Priority: job1.Priority,
|
||||||
TriggeredBy: structs.EvalTriggerJobRegister,
|
TriggeredBy: structs.EvalTriggerJobRegister,
|
||||||
|
@ -1359,6 +1382,7 @@ func TestSystemSched_PlanWithDrainedNode(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
@ -1429,6 +1453,7 @@ func TestSystemSched_QueuedAllocsMultTG(t *testing.T) {
|
||||||
|
|
||||||
// Create a mock evaluation to deal with drain
|
// Create a mock evaluation to deal with drain
|
||||||
eval := &structs.Evaluation{
|
eval := &structs.Evaluation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
Priority: 50,
|
Priority: 50,
|
||||||
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
TriggeredBy: structs.EvalTriggerNodeUpdate,
|
||||||
|
|
|
@ -731,11 +731,12 @@ func TestInplaceUpdate_ChangedTaskGroup(t *testing.T) {
|
||||||
|
|
||||||
// Register an alloc
|
// Register an alloc
|
||||||
alloc := &structs.Allocation{
|
alloc := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
EvalID: eval.ID,
|
ID: structs.GenerateUUID(),
|
||||||
NodeID: node.ID,
|
EvalID: eval.ID,
|
||||||
JobID: job.ID,
|
NodeID: node.ID,
|
||||||
Job: job,
|
JobID: job.ID,
|
||||||
|
Job: job,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 2048,
|
CPU: 2048,
|
||||||
MemoryMB: 2048,
|
MemoryMB: 2048,
|
||||||
|
@ -779,11 +780,12 @@ func TestInplaceUpdate_NoMatch(t *testing.T) {
|
||||||
|
|
||||||
// Register an alloc
|
// Register an alloc
|
||||||
alloc := &structs.Allocation{
|
alloc := &structs.Allocation{
|
||||||
ID: structs.GenerateUUID(),
|
Namespace: structs.DefaultNamespace,
|
||||||
EvalID: eval.ID,
|
ID: structs.GenerateUUID(),
|
||||||
NodeID: node.ID,
|
EvalID: eval.ID,
|
||||||
JobID: job.ID,
|
NodeID: node.ID,
|
||||||
Job: job,
|
JobID: job.ID,
|
||||||
|
Job: job,
|
||||||
Resources: &structs.Resources{
|
Resources: &structs.Resources{
|
||||||
CPU: 2048,
|
CPU: 2048,
|
||||||
MemoryMB: 2048,
|
MemoryMB: 2048,
|
||||||
|
@ -826,6 +828,7 @@ func TestInplaceUpdate_Success(t *testing.T) {
|
||||||
|
|
||||||
// Register an alloc
|
// Register an alloc
|
||||||
alloc := &structs.Allocation{
|
alloc := &structs.Allocation{
|
||||||
|
Namespace: structs.DefaultNamespace,
|
||||||
ID: structs.GenerateUUID(),
|
ID: structs.GenerateUUID(),
|
||||||
EvalID: eval.ID,
|
EvalID: eval.ID,
|
||||||
NodeID: node.ID,
|
NodeID: node.ID,
|
||||||
|
|
Loading…
Reference in New Issue