From 396781e036e8691448eee707bfb5ac9d8862a1aa Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sun, 21 Feb 2016 18:51:34 -0800 Subject: [PATCH 1/6] nomad: batch client updates for 50msec --- nomad/node_endpoint.go | 104 ++++++++++++++++++++++++++++++++++-- nomad/node_endpoint_test.go | 86 +++++++++++++++++++++++++++++ nomad/server.go | 2 +- 3 files changed, 186 insertions(+), 6 deletions(-) diff --git a/nomad/node_endpoint.go b/nomad/node_endpoint.go index a56d842f3..0f428da96 100644 --- a/nomad/node_endpoint.go +++ b/nomad/node_endpoint.go @@ -2,6 +2,7 @@ package nomad import ( "fmt" + "sync" "time" "github.com/armon/go-metrics" @@ -10,9 +11,29 @@ import ( "github.com/hashicorp/nomad/nomad/watch" ) +const ( + // batchUpdateInterval is how long we wait to batch updates + batchUpdateInterval = 50 * time.Millisecond +) + // Node endpoint is used for client interactions type Node struct { srv *Server + + // updates holds pending client status updates for allocations + updates []*structs.Allocation + + // updateFuture is used to wait for the pending batch update + // to complete. This may be nil if no batch is pending. + updateFuture *batchFuture + + // updateTimer is the timer that will trigger the next batch + // update, and may be nil if there is no batch pending. + updateTimer *time.Timer + + // updatesLock synchronizes access to the updates list, + // the future and the timer. + updatesLock sync.Mutex } // Register is used to upsert a client that is available for scheduling @@ -456,18 +477,59 @@ func (n *Node) UpdateAlloc(args *structs.AllocUpdateRequest, reply *structs.Gene return fmt.Errorf("must update a single allocation") } - // Commit this update via Raft - _, index, err := n.srv.raftApply(structs.AllocClientUpdateRequestType, args) - if err != nil { - n.srv.logger.Printf("[ERR] nomad.client: alloc update failed: %v", err) + // Add this to the batch + n.updatesLock.Lock() + n.updates = append(n.updates, args.Alloc...) + + // Start a new batch if none + future := n.updateFuture + if future == nil { + future = NewBatchFuture() + n.updateFuture = future + n.updateTimer = time.AfterFunc(batchUpdateInterval, func() { + // Get the pending updates + n.updatesLock.Lock() + updates := n.updates + future := n.updateFuture + n.updates = nil + n.updateFuture = nil + n.updateTimer = nil + n.updatesLock.Unlock() + + // Perform the batch update + n.batchUpdate(future, updates) + }) + } + n.updatesLock.Unlock() + + // Wait for the future + if err := future.Wait(); err != nil { return err } // Setup the response - reply.Index = index + reply.Index = future.Index() return nil } +// batchUpdate is used to update all the allocations +func (n *Node) batchUpdate(future *batchFuture, updates []*structs.Allocation) { + // Prepare the batch update + batch := &structs.AllocUpdateRequest{ + Alloc: updates, + WriteRequest: structs.WriteRequest{Region: n.srv.config.Region}, + } + + // Commit this update via Raft + _, index, err := n.srv.raftApply(structs.AllocClientUpdateRequestType, batch) + if err != nil { + n.srv.logger.Printf("[ERR] nomad.client: alloc update failed: %v", err) + } + + // Respond to the future + future.Respond(index, err) +} + // List is used to list the available nodes func (n *Node) List(args *structs.NodeListRequest, reply *structs.NodeListResponse) error { @@ -617,3 +679,35 @@ func (n *Node) createNodeEvals(nodeID string, nodeIndex uint64) ([]string, uint6 } return evalIDs, evalIndex, nil } + +// batchFuture is used to wait on a batch update to complete +type batchFuture struct { + doneCh chan struct{} + err error + index uint64 +} + +// NewBatchFuture creates a new batch future +func NewBatchFuture() *batchFuture { + return &batchFuture{ + doneCh: make(chan struct{}), + } +} + +// Wait is used to block for the future to complete and returns the error +func (b *batchFuture) Wait() error { + <-b.doneCh + return b.err +} + +// Index is used to return the index of the batch, only after Wait() +func (b *batchFuture) Index() uint64 { + return b.index +} + +// Respond is used to unblock the future +func (b *batchFuture) Respond(index uint64, err error) { + b.index = index + b.err = err + close(b.doneCh) +} diff --git a/nomad/node_endpoint_test.go b/nomad/node_endpoint_test.go index c670e93dd..f7886b737 100644 --- a/nomad/node_endpoint_test.go +++ b/nomad/node_endpoint_test.go @@ -1,6 +1,7 @@ package nomad import ( + "fmt" "reflect" "testing" "time" @@ -817,12 +818,70 @@ func TestClientEndpoint_UpdateAlloc(t *testing.T) { WriteRequest: structs.WriteRequest{Region: "global"}, } var resp2 structs.NodeAllocsResponse + start := time.Now() if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateAlloc", update, &resp2); err != nil { t.Fatalf("err: %v", err) } if resp2.Index == 0 { t.Fatalf("Bad index: %d", resp2.Index) } + if diff := time.Since(start); diff < batchUpdateInterval { + t.Fatalf("too fast: %v", diff) + } + + // Lookup the alloc + out, err := state.AllocByID(alloc.ID) + if err != nil { + t.Fatalf("err: %v", err) + } + if out.ClientStatus != structs.AllocClientStatusFailed { + t.Fatalf("Bad: %#v", out) + } +} + +func TestClientEndpoint_BatchUpdate(t *testing.T) { + s1 := testServer(t, nil) + defer s1.Shutdown() + codec := rpcClient(t, s1) + testutil.WaitForLeader(t, s1.RPC) + + // Create the register request + node := mock.Node() + reg := &structs.NodeRegisterRequest{ + Node: node, + WriteRequest: structs.WriteRequest{Region: "global"}, + } + + // Fetch the response + var resp structs.GenericResponse + if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { + t.Fatalf("err: %v", err) + } + + // Inject fake evaluations + alloc := mock.Alloc() + alloc.NodeID = node.ID + state := s1.fsm.State() + err := state.UpsertAllocs(100, []*structs.Allocation{alloc}) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Attempt update + clientAlloc := new(structs.Allocation) + *clientAlloc = *alloc + clientAlloc.ClientStatus = structs.AllocClientStatusFailed + + // Call to do the batch update + bf := NewBatchFuture() + endpoint := s1.endpoints.Node + endpoint.batchUpdate(bf, []*structs.Allocation{clientAlloc}) + if err := bf.Wait(); err != nil { + t.Fatalf("err: %v", err) + } + if bf.Index() == 0 { + t.Fatalf("Bad index: %d", bf.Index()) + } // Lookup the alloc out, err := state.AllocByID(alloc.ID) @@ -1168,3 +1227,30 @@ func TestClientEndpoint_ListNodes_Blocking(t *testing.T) { t.Fatalf("bad: %#v", resp4.Nodes) } } + +func TestBatchFuture(t *testing.T) { + bf := NewBatchFuture() + + // Async respond to the future + expect := fmt.Errorf("testing") + go func() { + time.Sleep(10 * time.Millisecond) + bf.Respond(1000, expect) + }() + + // Block for the result + start := time.Now() + err := bf.Wait() + diff := time.Since(start) + if diff < 5*time.Millisecond { + t.Fatalf("too fast") + } + + // Check the results + if err != expect { + t.Fatalf("bad: %s", err) + } + if bf.Index() != 1000 { + t.Fatalf("bad: %d", bf.Index()) + } +} diff --git a/nomad/server.go b/nomad/server.go index e377ef304..f0a66e688 100644 --- a/nomad/server.go +++ b/nomad/server.go @@ -374,7 +374,7 @@ func (s *Server) Leave() error { func (s *Server) setupRPC(tlsWrap tlsutil.DCWrapper) error { // Create endpoints s.endpoints.Status = &Status{s} - s.endpoints.Node = &Node{s} + s.endpoints.Node = &Node{srv: s} s.endpoints.Job = &Job{s} s.endpoints.Eval = &Eval{s} s.endpoints.Plan = &Plan{s} From 6b3c22e91a6fd09c6d0cdb60909b436a2033faba Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Sun, 21 Feb 2016 19:20:50 -0800 Subject: [PATCH 2/6] Batch client allocation updates to the server --- client/alloc_runner.go | 40 +++++------------ client/alloc_runner_test.go | 4 +- client/client.go | 85 +++++++++++++++++++++++++++++-------- client/client_test.go | 32 +++++++------- 4 files changed, 94 insertions(+), 67 deletions(-) diff --git a/client/alloc_runner.go b/client/alloc_runner.go index a8bb0186d..5ec5873df 100644 --- a/client/alloc_runner.go +++ b/client/alloc_runner.go @@ -30,7 +30,7 @@ const ( ) // AllocStateUpdater is used to update the status of an allocation -type AllocStateUpdater func(alloc *structs.Allocation) error +type AllocStateUpdater func(alloc *structs.Allocation) // AllocRunner is used to wrap an allocation and provide the execution context. type AllocRunner struct { @@ -262,9 +262,12 @@ func (r *AllocRunner) Alloc() *structs.Allocation { alloc.ClientStatus = structs.AllocClientStatusFailed } else if running { alloc.ClientStatus = structs.AllocClientStatusRunning - } else if dead && !pending { + } else if pending { + alloc.ClientStatus = structs.AllocClientStatusPending + } else if dead { alloc.ClientStatus = structs.AllocClientStatusDead } + return alloc } @@ -273,42 +276,19 @@ func (r *AllocRunner) dirtySyncState() { for { select { case <-r.dirtyCh: - r.retrySyncState(r.destroyCh) + r.syncStatus() case <-r.destroyCh: return } } } -// retrySyncState is used to retry the state sync until success -func (r *AllocRunner) retrySyncState(stopCh chan struct{}) { - for { - if err := r.syncStatus(); err == nil { - // The Alloc State might have been re-computed so we are - // snapshoting only the alloc runner - r.saveAllocRunnerState() - return - } - select { - case <-time.After(allocSyncRetryIntv + randomStagger(allocSyncRetryIntv)): - case <-stopCh: - return - } - } -} - // syncStatus is used to run and sync the status when it changes func (r *AllocRunner) syncStatus() error { - // Get a copy of our alloc. + // Get a copy of our alloc, update status server side and sync to disk alloc := r.Alloc() - - // Attempt to update the status - if err := r.updater(alloc); err != nil { - r.logger.Printf("[ERR] client: failed to update alloc '%s' status to %s: %s", - alloc.ID, alloc.ClientStatus, err) - return err - } - return nil + r.updater(alloc) + return r.saveAllocRunnerState() } // setStatus is used to update the allocation status @@ -475,7 +455,7 @@ OUTER: r.taskLock.Unlock() // Final state sync - r.retrySyncState(nil) + r.syncStatus() // Block until we should destroy the state of the alloc r.handleDestroy() diff --git a/client/alloc_runner_test.go b/client/alloc_runner_test.go index 6b526b846..5d31e97e9 100644 --- a/client/alloc_runner_test.go +++ b/client/alloc_runner_test.go @@ -16,13 +16,11 @@ import ( type MockAllocStateUpdater struct { Count int Allocs []*structs.Allocation - Err error } -func (m *MockAllocStateUpdater) Update(alloc *structs.Allocation) error { +func (m *MockAllocStateUpdater) Update(alloc *structs.Allocation) { m.Count += 1 m.Allocs = append(m.Allocs, alloc) - return m.Err } func testAllocRunner(restarts bool) (*MockAllocStateUpdater, *AllocRunner) { diff --git a/client/client.go b/client/client.go index 8fb1cf0d8..c58797963 100644 --- a/client/client.go +++ b/client/client.go @@ -58,6 +58,10 @@ const ( // nodeUpdateRetryIntv is how often the client checks for updates to the // node attributes or meta map. nodeUpdateRetryIntv = 5 * time.Second + + // allocSyncIntv is the batching period of allocation updates before they + // are synced with the server. + allocSyncIntv = 200 * time.Millisecond ) // DefaultConfig returns the default configuration @@ -100,6 +104,9 @@ type Client struct { allocs map[string]*AllocRunner allocLock sync.RWMutex + // allocUpdates stores allocations that need to be synced to the server. + allocUpdates chan *structs.Allocation + shutdown bool shutdownCh chan struct{} shutdownLock sync.Mutex @@ -112,12 +119,13 @@ func NewClient(cfg *config.Config) (*Client, error) { // Create the client c := &Client{ - config: cfg, - start: time.Now(), - connPool: nomad.NewPool(cfg.LogOutput, clientRPCCache, clientMaxStreams, nil), - logger: logger, - allocs: make(map[string]*AllocRunner), - shutdownCh: make(chan struct{}), + config: cfg, + start: time.Now(), + connPool: nomad.NewPool(cfg.LogOutput, clientRPCCache, clientMaxStreams, nil), + logger: logger, + allocs: make(map[string]*AllocRunner), + allocUpdates: make(chan *structs.Allocation, 64), + shutdownCh: make(chan struct{}), } // Setup the Consul Service @@ -166,6 +174,9 @@ func NewClient(cfg *config.Config) (*Client, error) { // Begin periodic snapshotting of state. go c.periodicSnapshot() + // Begin syncing allocations to the server + go c.allocSync() + // Start the client! go c.run() @@ -816,19 +827,57 @@ func (c *Client) updateNodeStatus() error { } // updateAllocStatus is used to update the status of an allocation -func (c *Client) updateAllocStatus(alloc *structs.Allocation) error { - args := structs.AllocUpdateRequest{ - Alloc: []*structs.Allocation{alloc}, - WriteRequest: structs.WriteRequest{Region: c.config.Region}, - } - var resp structs.GenericResponse - err := c.RPC("Node.UpdateAlloc", &args, &resp) - if err != nil { - c.logger.Printf("[ERR] client: failed to update allocation: %v", err) - return err - } +func (c *Client) updateAllocStatus(alloc *structs.Allocation) { + // Only send the fields that are updatable by the client. + stripped := new(structs.Allocation) + stripped.ID = alloc.ID + stripped.TaskStates = alloc.TaskStates + stripped.ClientStatus = alloc.ClientStatus + stripped.ClientDescription = alloc.ClientDescription + c.allocUpdates <- stripped +} - return nil +// allocSync is a long lived function that batches allocation updates to the +// server. +func (c *Client) allocSync() { + timeoutTimer := time.NewTimer(allocSyncIntv) + timeoutCh := timeoutTimer.C + updates := make(map[string]*structs.Allocation) + for { + select { + case <-c.shutdownCh: + return + case alloc := <-c.allocUpdates: + // Batch the allocation updates until the timer triggers. + updates[alloc.ID] = alloc + case <-timeoutCh: + // Reset the timer + timeoutTimer.Reset(allocSyncIntv) + + // Fast path if there are no updates + if len(updates) == 0 { + continue + } + + sync := make([]*structs.Allocation, 0, len(updates)) + for _, alloc := range updates { + sync = append(sync, alloc) + } + + // Send to server. + args := structs.AllocUpdateRequest{ + Alloc: sync, + WriteRequest: structs.WriteRequest{Region: c.config.Region}, + } + + var resp structs.GenericResponse + if err := c.RPC("Node.UpdateAlloc", &args, &resp); err != nil { + c.logger.Printf("[ERR] client: failed to update allocations: %v", err) + } else { + updates = make(map[string]*structs.Allocation) + } + } + } } // allocUpdates holds the results of receiving updated allocations from the diff --git a/client/client_test.go b/client/client_test.go index 6b6c2d277..66c59be87 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -324,27 +324,27 @@ func TestClient_UpdateAllocStatus(t *testing.T) { alloc := mock.Alloc() alloc.NodeID = c1.Node().ID + originalStatus := "foo" + alloc.ClientStatus = originalStatus state := s1.State() state.UpsertAllocs(100, []*structs.Allocation{alloc}) - newAlloc := new(structs.Allocation) - *newAlloc = *alloc - newAlloc.ClientStatus = structs.AllocClientStatusRunning - - err := c1.updateAllocStatus(newAlloc) - if err != nil { + testutil.WaitForResult(func() (bool, error) { + out, err := state.AllocByID(alloc.ID) + if err != nil { + return false, err + } + if out == nil { + return false, fmt.Errorf("no such alloc") + } + if out.ClientStatus == originalStatus { + return false, fmt.Errorf("Alloc client status not updated; got %v", out.ClientStatus) + } + return true, nil + }, func(err error) { t.Fatalf("err: %v", err) - } - - out, err := state.AllocByID(alloc.ID) - if err != nil { - t.Fatalf("err: %v", err) - } - - if out == nil || out.ClientStatus != structs.AllocClientStatusRunning { - t.Fatalf("bad: %#v", out) - } + }) } func TestClient_WatchAllocs(t *testing.T) { From a6eb1ec98a648f023edf8edcf440650e2c2ff6cb Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Sun, 21 Feb 2016 21:12:58 -0800 Subject: [PATCH 3/6] Fix test --- client/client_test.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 66c59be87..462a600fb 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -440,8 +440,7 @@ func TestClient_SaveRestoreState(t *testing.T) { task.Config["args"] = []string{"10"} state := s1.State() - err := state.UpsertAllocs(100, - []*structs.Allocation{alloc1}) + err := state.UpsertAllocs(100, []*structs.Allocation{alloc1}) if err != nil { t.Fatalf("err: %v", err) } @@ -470,12 +469,20 @@ func TestClient_SaveRestoreState(t *testing.T) { defer c2.Shutdown() // Ensure the allocation is running - c2.allocLock.RLock() - ar := c2.allocs[alloc1.ID] - c2.allocLock.RUnlock() - if ar.Alloc().ClientStatus != structs.AllocClientStatusRunning { - t.Fatalf("bad: %#v", ar.Alloc()) - } + testutil.WaitForResult(func() (bool, error) { + c2.allocLock.RLock() + ar := c2.allocs[alloc1.ID] + c2.allocLock.RUnlock() + status := ar.Alloc().ClientStatus + alive := status != structs.AllocClientStatusRunning || + status != structs.AllocClientStatusPending + if !alive { + return false, fmt.Errorf("incorrect client status: %#v", ar.Alloc()) + } + return true, nil + }, func(err error) { + t.Fatalf("err: %v", err) + }) } func TestClient_Init(t *testing.T) { From 48eff00427c438329810c41e3f300f01317be9d8 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Sun, 21 Feb 2016 21:32:32 -0800 Subject: [PATCH 4/6] address feedback --- client/alloc_runner.go | 4 ---- client/client.go | 27 ++++++++++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/client/alloc_runner.go b/client/alloc_runner.go index 5ec5873df..dd0f1f8db 100644 --- a/client/alloc_runner.go +++ b/client/alloc_runner.go @@ -16,10 +16,6 @@ import ( ) const ( - // allocSyncRetryIntv is the interval on which we retry updating - // the status of the allocation - allocSyncRetryIntv = 15 * time.Second - // taskReceivedSyncLimit is how long the client will wait before sending // that a task was received to the server. The client does not immediately // send that the task was received to the server because another transistion diff --git a/client/client.go b/client/client.go index c58797963..1295ed062 100644 --- a/client/client.go +++ b/client/client.go @@ -62,6 +62,10 @@ const ( // allocSyncIntv is the batching period of allocation updates before they // are synced with the server. allocSyncIntv = 200 * time.Millisecond + + // allocSyncRetryIntv is the interval on which we retry updating + // the status of the allocation + allocSyncRetryIntv = 5 * time.Second ) // DefaultConfig returns the default configuration @@ -834,26 +838,27 @@ func (c *Client) updateAllocStatus(alloc *structs.Allocation) { stripped.TaskStates = alloc.TaskStates stripped.ClientStatus = alloc.ClientStatus stripped.ClientDescription = alloc.ClientDescription - c.allocUpdates <- stripped + select { + case c.allocUpdates <- stripped: + case <-c.shutdownCh: + } } // allocSync is a long lived function that batches allocation updates to the // server. func (c *Client) allocSync() { - timeoutTimer := time.NewTimer(allocSyncIntv) - timeoutCh := timeoutTimer.C + staggered := false + syncTicker := time.NewTicker(allocSyncIntv) updates := make(map[string]*structs.Allocation) for { select { case <-c.shutdownCh: + syncTicker.Stop() return case alloc := <-c.allocUpdates: // Batch the allocation updates until the timer triggers. updates[alloc.ID] = alloc - case <-timeoutCh: - // Reset the timer - timeoutTimer.Reset(allocSyncIntv) - + case <-syncTicker.C: // Fast path if there are no updates if len(updates) == 0 { continue @@ -873,8 +878,16 @@ func (c *Client) allocSync() { var resp structs.GenericResponse if err := c.RPC("Node.UpdateAlloc", &args, &resp); err != nil { c.logger.Printf("[ERR] client: failed to update allocations: %v", err) + syncTicker.Stop() + syncTicker = time.NewTicker(c.retryIntv(allocSyncRetryIntv)) + staggered = true } else { updates = make(map[string]*structs.Allocation) + if staggered { + syncTicker.Stop() + syncTicker = time.NewTicker(allocSyncIntv) + staggered = false + } } } } From 525af88682c92f74d62bf9ccf4d332771c76034e Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sun, 21 Feb 2016 23:31:49 -0800 Subject: [PATCH 5/6] Updating the vendored iradix library --- Godeps/Godeps.json | 2 +- vendor/github.com/hashicorp/go-immutable-radix/node.go | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index b24e9cac5..a7c3590c4 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -164,7 +164,7 @@ }, { "ImportPath": "github.com/hashicorp/go-immutable-radix", - "Rev": "12e90058b2897552deea141eff51bb7a07a09e63" + "Rev": "8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990" }, { "ImportPath": "github.com/hashicorp/go-memdb", diff --git a/vendor/github.com/hashicorp/go-immutable-radix/node.go b/vendor/github.com/hashicorp/go-immutable-radix/node.go index 245ecedf1..fea6f6343 100644 --- a/vendor/github.com/hashicorp/go-immutable-radix/node.go +++ b/vendor/github.com/hashicorp/go-immutable-radix/node.go @@ -41,8 +41,15 @@ func (n *Node) isLeaf() bool { } func (n *Node) addEdge(e edge) { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= e.label + }) n.edges = append(n.edges, e) - n.edges.Sort() + if idx != num { + copy(n.edges[idx+1:], n.edges[idx:num]) + n.edges[idx] = e + } } func (n *Node) replaceEdge(e edge) { From 0682581ded5a143da607a9341283f2df0d5c080b Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 22 Feb 2016 00:21:04 -0800 Subject: [PATCH 6/6] Removing support for Go 1.5.3 --- .travis.yml | 4 ++-- scripts/build.sh | 2 -- scripts/test.sh | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 41dca2daf..ed018a6db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,11 +7,11 @@ services: language: go go: - - 1.5.3 - 1.6 + - tip env: - - DOCKER_VERSION=1.9.1 GO15VENDOREXPERIMENT=1 + - DOCKER_VERSION=1.9.1 matrix: allow_failures: diff --git a/scripts/build.sh b/scripts/build.sh index c1faef5b6..90e6b0853 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -3,8 +3,6 @@ # This script builds the application from source for multiple platforms. set -e -export GO15VENDOREXPERIMENT=1 - # Get the parent directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done diff --git a/scripts/test.sh b/scripts/test.sh index d1828e3b1..ea23141d5 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -export GO15VENDOREXPERIMENT=1 - # Create a temp dir and clean it up on exit TEMPDIR=`mktemp -d -t nomad-test.XXX` trap "rm -rf $TEMPDIR" EXIT HUP INT QUIT TERM