Merge branch 'master' into f-driver-upgradepath-test

* master: (91 commits)
  increase log level
  CHANGELOG: added 0.9 ui changes
  test: port TestTaskRunner_CheckWatcher_Restart
  test: port RestartTask from 0.8
  test: port SignalFailure test from 0.8
  Rename TaskKillRequest/Response to TaskPreKillRequest/Response
  Fix log comments
  Rename TaskKillHook to TaskPreKillHook to more closely match usage
  Fix comment
  Rename TaskKillHook to TaskPreKillHook to more closely match usage
  Update CHANGELOG.md
  tests: deflake TestClientAllocations_GarbageCollect_Remote
  tests: deflake client TestFS_Logs_TaskPending test
  Update CHANGELOG.md
  test: fix flaky garbage collect test
  drivers: regen proto
  api: avoid codegen for syncing
  code review comments
  revert unintended change
  ar: return error from hooks if occured
  ...
This commit is contained in:
Nick Ethier 2019-01-22 23:28:09 -05:00
commit 753e03f9b8
No known key found for this signature in database
GPG Key ID: 07C1A3ECED90D24A
166 changed files with 6013 additions and 3846 deletions

View File

@ -7,8 +7,8 @@ __BACKWARDS INCOMPATIBILITIES:__
* client: Task config interpolation requires names to be valid identifiers
(`node.region` or `NOMAD_DC`). Interpolating other variables requires a new
indexing syntax: `env[".invalid.identifier."]`. [[GH-4843](https://github.com/hashicorp/nomad/issues/4843)]
* client: Node metadata variables must have valid identifiers, whether
specified in the config file (`client.meta` stanza) or on the command line
* client: Node metadata variables must have valid identifiers, whether
specified in the config file (`client.meta` stanza) or on the command line
(`-meta`). [[GH-5158](https://github.com/hashicorp/nomad/pull/5158)]
IMPROVEMENTS:
@ -18,7 +18,7 @@ IMPROVEMENTS:
* core: Added support for spreading allocations across a specific attribute. Operators can specify spread
target percentages across failure domains such as datacenter or rack [[GH-4512](https://github.com/hashicorp/nomad/issues/4512)]
* agent: Support JSON log output [[GH-5173](https://github.com/hashicorp/nomad/issues/5173)]
* client: Added service metadata tag that enables the Consul UI to show a Nomad
* client: Added service metadata tag that enables the Consul UI to show a Nomad
icon for services registered by Nomad [[GH-4889](https://github.com/hashicorp/nomad/issues/4889)]
* client: Refactor client to support plugins and improve state handling [[GH-4792](https://github.com/hashicorp/nomad/pull/4792)]
* client: Extend timeout to 60 seconds for Windows CPU fingerprinting [[GH-4441](https://github.com/hashicorp/nomad/pull/4441)]
@ -31,16 +31,31 @@ IMPROVEMENTS:
* telemetry: All client metrics include a new `node_class` tag [[GH-3882](https://github.com/hashicorp/nomad/issues/3882)]
* telemetry: Added new tags with value of child job id and parent job id for
parameterized and periodic jobs [[GH-4392](https://github.com/hashicorp/nomad/issues/4392)]
* ui: CPU and Memory metrics are plotted over time during a session in line charts on node detail, allocation detail, and task detail pages [[GH-4661](https://github.com/hashicorp/nomad/issues/4661)], [[GH-4718](https://github.com/hashicorp/nomad/issues/4718)], [[GH-4727](https://github.com/hashicorp/nomad/issues/4727)]
* ui: Switching namespaces in the UI will now always "reset" back to the jobs list page [[GH-4533](https://github.com/hashicorp/nomad/issues/4533)]
* ui: Refactored breadcrumbs and adjusted the breadcrumb paths on each page [[GH-4458](https://github.com/hashicorp/nomad/issues/4458)]
* ui: Jobs can be authored, planned, submitted, and edited from the UI [[GH-4600](https://github.com/hashicorp/nomad/issues/4600)]
* ui: Added links to Jobs and Clients from the error page template [[GH-4850](https://github.com/hashicorp/nomad/issues/4850)]
* ui: Gracefully handle errors from the stats end points [[GH-4833](https://github.com/hashicorp/nomad/issues/4833)]
* ui: Stopped jobs can be restarted from the UI [[GH-4615](https://github.com/hashicorp/nomad/issues/4615)]
* ui: Canaries can now be promoted from the UI [[GH-4616](https://github.com/hashicorp/nomad/issues/4616)]
* ui: Filled out the styleguide [[GH-4468](https://github.com/hashicorp/nomad/issues/4468)]
* vendor: Removed library obsoleted by go 1.8 [[GH-4469](https://github.com/hashicorp/nomad/issues/4469)]
* acls: Allow support for using globs in namespace definitions [[GH-4982](https://github.com/hashicorp/nomad/pull/4982)]
BUG FIXES:
* core: Fix an issue where artifact checksums containing interpolated variables failed validation [[GH-4810](https://github.com/hashicorp/nomad/pull/4819)]
* core: Fix an issue where job summaries for parent dispatch/periodic jobs were not being computed correctly [[GH-5205](https://github.com/hashicorp/nomad/pull/5205)]
* client: Fix an issue reloading the client config [[GH-4730](https://github.com/hashicorp/nomad/issues/4730)]
* client: Fix an issue where driver attributes are not updated in node API responses if they change after after startup [[GH-4984](https://github.com/hashicorp/nomad/pull/4984)]
* driver/docker: Fix a path traversal issue where mounting paths outside alloc dir might be possible despite `docker.volumes.enabled` set to false [[GH-4983](https://github.com/hashicorp/nomad/pull/4983)]
* driver/raw_exec: Fix an issue where tasks that used an interpolated command in driver configuration would not start [[GH-4813](https://github.com/hashicorp/nomad/pull/4813)]
* server/vault: Fixed bug in Vault token renewal that could panic on a malformed Vault response [[GH-4904](https://github.com/hashicorp/nomad/issues/4904)], [[GH-4937](https://github.com/hashicorp/nomad/pull/4937)]
* ui: Fixed an issue where distribution bar corners weren't rounded when there was only one or two slices in the chart [[GH-4507](https://github.com/hashicorp/nomad/issues/4507)]
* ui: Fixed an issue where dispatched jobs would get the wrong template type which could cause runtime errors [[GH-4852](https://github.com/hashicorp/nomad/issues/4852)]
* ui: Added an empty state for the tasks list on the allocation detail page, for when an alloc has no tasks [[GH-4860](https://github.com/hashicorp/nomad/issues/4860)]
* ui: Fixed an issue where the task group breadcrumb didn't always include the namesapce query param [[GH-4801](https://github.com/hashicorp/nomad/issues/4801)]
* ui: Correctly labeled certain classes of unknown errors as 404 errors [[GH-4841](https://github.com/hashicorp/nomad/issues/4841)]
## 0.8.7 (January 14, 2019)

View File

@ -12,7 +12,7 @@ GO_TEST_CMD = $(if $(shell which gotestsum),gotestsum --,go test)
default: help
ifeq (,$(findstring $(THIS_OS),Darwin Linux FreeBSD))
ifeq (,$(findstring $(THIS_OS),Darwin Linux FreeBSD Windows))
$(error Building Nomad is currently only supported on Darwin and Linux.)
endif

View File

@ -12,6 +12,20 @@ var (
NodeDownErr = fmt.Errorf("node down")
)
const (
AllocDesiredStatusRun = "run" // Allocation should run
AllocDesiredStatusStop = "stop" // Allocation should stop
AllocDesiredStatusEvict = "evict" // Allocation should stop, and was evicted
)
const (
AllocClientStatusPending = "pending"
AllocClientStatusRunning = "running"
AllocClientStatusComplete = "complete"
AllocClientStatusFailed = "failed"
AllocClientStatusLost = "lost"
)
// Allocations is used to query the alloc-related endpoints.
type Allocations struct {
client *Client

View File

@ -7,7 +7,6 @@ import (
"time"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/stretchr/testify/require"
)
@ -35,9 +34,9 @@ func TestAllocations_List(t *testing.T) {
return
//job := &Job{
//ID: helper.StringToPtr("job1"),
//Name: helper.StringToPtr("Job #1"),
//Type: helper.StringToPtr(JobTypeService),
//ID: stringToPtr("job1"),
//Name: stringToPtr("Job #1"),
//Type: stringToPtr(JobTypeService),
//}
//eval, _, err := c.Jobs().Register(job, nil)
//if err != nil {
@ -82,9 +81,9 @@ func TestAllocations_PrefixList(t *testing.T) {
return
//job := &Job{
//ID: helper.StringToPtr("job1"),
//Name: helper.StringToPtr("Job #1"),
//Type: helper.StringToPtr(JobTypeService),
//ID: stringToPtr("job1"),
//Name: stringToPtr("Job #1"),
//Type: stringToPtr(JobTypeService),
//}
//eval, _, err := c.Jobs().Register(job, nil)
@ -130,13 +129,13 @@ func TestAllocations_RescheduleInfo(t *testing.T) {
t.Parallel()
// Create a job, task group and alloc
job := &Job{
Name: helper.StringToPtr("foo"),
Namespace: helper.StringToPtr(DefaultNamespace),
ID: helper.StringToPtr("bar"),
ParentID: helper.StringToPtr("lol"),
Name: stringToPtr("foo"),
Namespace: stringToPtr(DefaultNamespace),
ID: stringToPtr("bar"),
ParentID: stringToPtr("lol"),
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("bar"),
Name: stringToPtr("bar"),
Tasks: []*Task{
{
Name: "task1",
@ -176,8 +175,8 @@ func TestAllocations_RescheduleInfo(t *testing.T) {
{
desc: "no reschedule events",
reschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(3),
Interval: helper.TimeToPtr(15 * time.Minute),
Attempts: intToPtr(3),
Interval: timeToPtr(15 * time.Minute),
},
expAttempted: 0,
expTotal: 3,
@ -185,8 +184,8 @@ func TestAllocations_RescheduleInfo(t *testing.T) {
{
desc: "all reschedule events within interval",
reschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(3),
Interval: helper.TimeToPtr(15 * time.Minute),
Attempts: intToPtr(3),
Interval: timeToPtr(15 * time.Minute),
},
time: time.Now(),
rescheduleTracker: &RescheduleTracker{
@ -202,8 +201,8 @@ func TestAllocations_RescheduleInfo(t *testing.T) {
{
desc: "some reschedule events outside interval",
reschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(3),
Interval: helper.TimeToPtr(15 * time.Minute),
Attempts: intToPtr(3),
Interval: timeToPtr(15 * time.Minute),
},
time: time.Now(),
rescheduleTracker: &RescheduleTracker{
@ -242,7 +241,7 @@ func TestAllocations_RescheduleInfo(t *testing.T) {
func TestAllocations_ShouldMigrate(t *testing.T) {
t.Parallel()
require.True(t, DesiredTransition{Migrate: helper.BoolToPtr(true)}.ShouldMigrate())
require.True(t, DesiredTransition{Migrate: boolToPtr(true)}.ShouldMigrate())
require.False(t, DesiredTransition{}.ShouldMigrate())
require.False(t, DesiredTransition{Migrate: helper.BoolToPtr(false)}.ShouldMigrate())
require.False(t, DesiredTransition{Migrate: boolToPtr(false)}.ShouldMigrate())
}

View File

@ -3,8 +3,6 @@ package api
import (
"reflect"
"testing"
"github.com/hashicorp/nomad/helper"
)
func TestCompose(t *testing.T) {
@ -15,13 +13,13 @@ func TestCompose(t *testing.T) {
SetMeta("foo", "bar").
Constrain(NewConstraint("kernel.name", "=", "linux")).
Require(&Resources{
CPU: helper.IntToPtr(1250),
MemoryMB: helper.IntToPtr(1024),
DiskMB: helper.IntToPtr(2048),
CPU: intToPtr(1250),
MemoryMB: intToPtr(1024),
DiskMB: intToPtr(2048),
Networks: []*NetworkResource{
{
CIDR: "0.0.0.0/0",
MBits: helper.IntToPtr(100),
MBits: intToPtr(100),
ReservedPorts: []Port{{"", 80}, {"", 443}},
},
},
@ -47,11 +45,11 @@ func TestCompose(t *testing.T) {
// Check that the composed result looks correct
expect := &Job{
Region: helper.StringToPtr("region1"),
ID: helper.StringToPtr("job1"),
Name: helper.StringToPtr("myjob"),
Type: helper.StringToPtr(JobTypeService),
Priority: helper.IntToPtr(2),
Region: stringToPtr("region1"),
ID: stringToPtr("job1"),
Name: stringToPtr("myjob"),
Type: stringToPtr(JobTypeService),
Priority: intToPtr(2),
Datacenters: []string{
"dc1",
},
@ -67,8 +65,8 @@ func TestCompose(t *testing.T) {
},
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("grp1"),
Count: helper.IntToPtr(2),
Name: stringToPtr("grp1"),
Count: intToPtr(2),
Constraints: []*Constraint{
{
LTarget: "kernel.name",
@ -87,7 +85,7 @@ func TestCompose(t *testing.T) {
Spreads: []*Spread{
{
Attribute: "${node.datacenter}",
Weight: helper.IntToPtr(30),
Weight: intToPtr(30),
SpreadTarget: []*SpreadTarget{
{
Value: "dc1",
@ -105,13 +103,13 @@ func TestCompose(t *testing.T) {
Name: "task1",
Driver: "exec",
Resources: &Resources{
CPU: helper.IntToPtr(1250),
MemoryMB: helper.IntToPtr(1024),
DiskMB: helper.IntToPtr(2048),
CPU: intToPtr(1250),
MemoryMB: intToPtr(1024),
DiskMB: intToPtr(2048),
Networks: []*NetworkResource{
{
CIDR: "0.0.0.0/0",
MBits: helper.IntToPtr(100),
MBits: intToPtr(100),
ReservedPorts: []Port{
{"", 80},
{"", 443},

View File

@ -10,7 +10,6 @@ import (
"time"
units "github.com/docker/go-units"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/testutil"
"github.com/kr/pretty"
"github.com/stretchr/testify/assert"
@ -62,13 +61,13 @@ func TestFS_Logs(t *testing.T) {
}
job := &Job{
ID: helper.StringToPtr("TestFS_Logs"),
Region: helper.StringToPtr("global"),
ID: stringToPtr("TestFS_Logs"),
Region: stringToPtr("global"),
Datacenters: []string{"dc1"},
Type: helper.StringToPtr("batch"),
Type: stringToPtr("batch"),
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("TestFS_LogsGroup"),
Name: stringToPtr("TestFS_LogsGroup"),
Tasks: []*Task{
{
Name: "logger",

View File

@ -8,8 +8,6 @@ import (
"time"
"github.com/gorhill/cronexpr"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/structs"
)
const (
@ -19,6 +17,9 @@ const (
// JobTypeBatch indicates a short-lived process
JobTypeBatch = "batch"
// JobTypeSystem indicates a system process that should run on all clients
JobTypeSystem = "system"
// PeriodicSpecCron is used for a cron spec.
PeriodicSpecCron = "cron"
@ -373,14 +374,14 @@ type UpdateStrategy struct {
// jobs with the old policy or for populating field defaults.
func DefaultUpdateStrategy() *UpdateStrategy {
return &UpdateStrategy{
Stagger: helper.TimeToPtr(30 * time.Second),
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
ProgressDeadline: helper.TimeToPtr(10 * time.Minute),
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(0),
Stagger: timeToPtr(30 * time.Second),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(5 * time.Minute),
ProgressDeadline: timeToPtr(10 * time.Minute),
AutoRevert: boolToPtr(false),
Canary: intToPtr(0),
}
}
@ -392,35 +393,35 @@ func (u *UpdateStrategy) Copy() *UpdateStrategy {
copy := new(UpdateStrategy)
if u.Stagger != nil {
copy.Stagger = helper.TimeToPtr(*u.Stagger)
copy.Stagger = timeToPtr(*u.Stagger)
}
if u.MaxParallel != nil {
copy.MaxParallel = helper.IntToPtr(*u.MaxParallel)
copy.MaxParallel = intToPtr(*u.MaxParallel)
}
if u.HealthCheck != nil {
copy.HealthCheck = helper.StringToPtr(*u.HealthCheck)
copy.HealthCheck = stringToPtr(*u.HealthCheck)
}
if u.MinHealthyTime != nil {
copy.MinHealthyTime = helper.TimeToPtr(*u.MinHealthyTime)
copy.MinHealthyTime = timeToPtr(*u.MinHealthyTime)
}
if u.HealthyDeadline != nil {
copy.HealthyDeadline = helper.TimeToPtr(*u.HealthyDeadline)
copy.HealthyDeadline = timeToPtr(*u.HealthyDeadline)
}
if u.ProgressDeadline != nil {
copy.ProgressDeadline = helper.TimeToPtr(*u.ProgressDeadline)
copy.ProgressDeadline = timeToPtr(*u.ProgressDeadline)
}
if u.AutoRevert != nil {
copy.AutoRevert = helper.BoolToPtr(*u.AutoRevert)
copy.AutoRevert = boolToPtr(*u.AutoRevert)
}
if u.Canary != nil {
copy.Canary = helper.IntToPtr(*u.Canary)
copy.Canary = intToPtr(*u.Canary)
}
return copy
@ -432,35 +433,35 @@ func (u *UpdateStrategy) Merge(o *UpdateStrategy) {
}
if o.Stagger != nil {
u.Stagger = helper.TimeToPtr(*o.Stagger)
u.Stagger = timeToPtr(*o.Stagger)
}
if o.MaxParallel != nil {
u.MaxParallel = helper.IntToPtr(*o.MaxParallel)
u.MaxParallel = intToPtr(*o.MaxParallel)
}
if o.HealthCheck != nil {
u.HealthCheck = helper.StringToPtr(*o.HealthCheck)
u.HealthCheck = stringToPtr(*o.HealthCheck)
}
if o.MinHealthyTime != nil {
u.MinHealthyTime = helper.TimeToPtr(*o.MinHealthyTime)
u.MinHealthyTime = timeToPtr(*o.MinHealthyTime)
}
if o.HealthyDeadline != nil {
u.HealthyDeadline = helper.TimeToPtr(*o.HealthyDeadline)
u.HealthyDeadline = timeToPtr(*o.HealthyDeadline)
}
if o.ProgressDeadline != nil {
u.ProgressDeadline = helper.TimeToPtr(*o.ProgressDeadline)
u.ProgressDeadline = timeToPtr(*o.ProgressDeadline)
}
if o.AutoRevert != nil {
u.AutoRevert = helper.BoolToPtr(*o.AutoRevert)
u.AutoRevert = boolToPtr(*o.AutoRevert)
}
if o.Canary != nil {
u.Canary = helper.IntToPtr(*o.Canary)
u.Canary = intToPtr(*o.Canary)
}
}
@ -552,19 +553,19 @@ type PeriodicConfig struct {
func (p *PeriodicConfig) Canonicalize() {
if p.Enabled == nil {
p.Enabled = helper.BoolToPtr(true)
p.Enabled = boolToPtr(true)
}
if p.Spec == nil {
p.Spec = helper.StringToPtr("")
p.Spec = stringToPtr("")
}
if p.SpecType == nil {
p.SpecType = helper.StringToPtr(PeriodicSpecCron)
p.SpecType = stringToPtr(PeriodicSpecCron)
}
if p.ProhibitOverlap == nil {
p.ProhibitOverlap = helper.BoolToPtr(false)
p.ProhibitOverlap = boolToPtr(false)
}
if p.TimeZone == nil || *p.TimeZone == "" {
p.TimeZone = helper.StringToPtr("UTC")
p.TimeZone = stringToPtr("UTC")
}
}
@ -575,13 +576,27 @@ func (p *PeriodicConfig) Canonicalize() {
func (p *PeriodicConfig) Next(fromTime time.Time) (time.Time, error) {
if *p.SpecType == PeriodicSpecCron {
if e, err := cronexpr.Parse(*p.Spec); err == nil {
return structs.CronParseNext(e, fromTime, *p.Spec)
return cronParseNext(e, fromTime, *p.Spec)
}
}
return time.Time{}, nil
}
// cronParseNext is a helper that parses the next time for the given expression
// but captures any panic that may occur in the underlying library.
// --- THIS FUNCTION IS REPLICATED IN nomad/structs/structs.go
// and should be kept in sync.
func cronParseNext(e *cronexpr.Expression, fromTime time.Time, spec string) (t time.Time, err error) {
defer func() {
if recover() != nil {
t = time.Time{}
err = fmt.Errorf("failed parsing cron expression: %q", spec)
}
}()
return e.Next(fromTime), nil
}
func (p *PeriodicConfig) GetLocation() (*time.Location, error) {
if p.TimeZone == nil || *p.TimeZone == "" {
return time.UTC, nil
@ -644,58 +659,58 @@ func (j *Job) IsParameterized() bool {
func (j *Job) Canonicalize() {
if j.ID == nil {
j.ID = helper.StringToPtr("")
j.ID = stringToPtr("")
}
if j.Name == nil {
j.Name = helper.StringToPtr(*j.ID)
j.Name = stringToPtr(*j.ID)
}
if j.ParentID == nil {
j.ParentID = helper.StringToPtr("")
j.ParentID = stringToPtr("")
}
if j.Namespace == nil {
j.Namespace = helper.StringToPtr(DefaultNamespace)
j.Namespace = stringToPtr(DefaultNamespace)
}
if j.Priority == nil {
j.Priority = helper.IntToPtr(50)
j.Priority = intToPtr(50)
}
if j.Stop == nil {
j.Stop = helper.BoolToPtr(false)
j.Stop = boolToPtr(false)
}
if j.Region == nil {
j.Region = helper.StringToPtr("global")
j.Region = stringToPtr("global")
}
if j.Namespace == nil {
j.Namespace = helper.StringToPtr("default")
j.Namespace = stringToPtr("default")
}
if j.Type == nil {
j.Type = helper.StringToPtr("service")
j.Type = stringToPtr("service")
}
if j.AllAtOnce == nil {
j.AllAtOnce = helper.BoolToPtr(false)
j.AllAtOnce = boolToPtr(false)
}
if j.VaultToken == nil {
j.VaultToken = helper.StringToPtr("")
j.VaultToken = stringToPtr("")
}
if j.Status == nil {
j.Status = helper.StringToPtr("")
j.Status = stringToPtr("")
}
if j.StatusDescription == nil {
j.StatusDescription = helper.StringToPtr("")
j.StatusDescription = stringToPtr("")
}
if j.Stable == nil {
j.Stable = helper.BoolToPtr(false)
j.Stable = boolToPtr(false)
}
if j.Version == nil {
j.Version = helper.Uint64ToPtr(0)
j.Version = uint64ToPtr(0)
}
if j.CreateIndex == nil {
j.CreateIndex = helper.Uint64ToPtr(0)
j.CreateIndex = uint64ToPtr(0)
}
if j.ModifyIndex == nil {
j.ModifyIndex = helper.Uint64ToPtr(0)
j.ModifyIndex = uint64ToPtr(0)
}
if j.JobModifyIndex == nil {
j.JobModifyIndex = helper.Uint64ToPtr(0)
j.JobModifyIndex = uint64ToPtr(0)
}
if j.Periodic != nil {
j.Periodic.Canonicalize()

View File

@ -7,7 +7,6 @@ import (
"testing"
"time"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/testutil"
"github.com/kr/pretty"
@ -131,50 +130,50 @@ func TestJobs_Canonicalize(t *testing.T) {
},
},
expected: &Job{
ID: helper.StringToPtr(""),
Name: helper.StringToPtr(""),
Region: helper.StringToPtr("global"),
Namespace: helper.StringToPtr(DefaultNamespace),
Type: helper.StringToPtr("service"),
ParentID: helper.StringToPtr(""),
Priority: helper.IntToPtr(50),
AllAtOnce: helper.BoolToPtr(false),
VaultToken: helper.StringToPtr(""),
Status: helper.StringToPtr(""),
StatusDescription: helper.StringToPtr(""),
Stop: helper.BoolToPtr(false),
Stable: helper.BoolToPtr(false),
Version: helper.Uint64ToPtr(0),
CreateIndex: helper.Uint64ToPtr(0),
ModifyIndex: helper.Uint64ToPtr(0),
JobModifyIndex: helper.Uint64ToPtr(0),
ID: stringToPtr(""),
Name: stringToPtr(""),
Region: stringToPtr("global"),
Namespace: stringToPtr(DefaultNamespace),
Type: stringToPtr("service"),
ParentID: stringToPtr(""),
Priority: intToPtr(50),
AllAtOnce: boolToPtr(false),
VaultToken: stringToPtr(""),
Status: stringToPtr(""),
StatusDescription: stringToPtr(""),
Stop: boolToPtr(false),
Stable: boolToPtr(false),
Version: uint64ToPtr(0),
CreateIndex: uint64ToPtr(0),
ModifyIndex: uint64ToPtr(0),
JobModifyIndex: uint64ToPtr(0),
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr(""),
Count: helper.IntToPtr(1),
Name: stringToPtr(""),
Count: intToPtr(1),
EphemeralDisk: &EphemeralDisk{
Sticky: helper.BoolToPtr(false),
Migrate: helper.BoolToPtr(false),
SizeMB: helper.IntToPtr(300),
Sticky: boolToPtr(false),
Migrate: boolToPtr(false),
SizeMB: intToPtr(300),
},
RestartPolicy: &RestartPolicy{
Delay: helper.TimeToPtr(15 * time.Second),
Attempts: helper.IntToPtr(2),
Interval: helper.TimeToPtr(30 * time.Minute),
Mode: helper.StringToPtr("fail"),
Delay: timeToPtr(15 * time.Second),
Attempts: intToPtr(2),
Interval: timeToPtr(30 * time.Minute),
Mode: stringToPtr("fail"),
},
ReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr("exponential"),
Delay: helper.TimeToPtr(30 * time.Second),
MaxDelay: helper.TimeToPtr(1 * time.Hour),
Unlimited: helper.BoolToPtr(true),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
DelayFunction: stringToPtr("exponential"),
Delay: timeToPtr(30 * time.Second),
MaxDelay: timeToPtr(1 * time.Hour),
Unlimited: boolToPtr(true),
},
Migrate: DefaultMigrateStrategy(),
Tasks: []*Task{
{
KillTimeout: helper.TimeToPtr(5 * time.Second),
KillTimeout: timeToPtr(5 * time.Second),
LogConfig: DefaultLogConfig(),
Resources: DefaultResources(),
},
@ -186,13 +185,13 @@ func TestJobs_Canonicalize(t *testing.T) {
{
name: "partial",
input: &Job{
Name: helper.StringToPtr("foo"),
Namespace: helper.StringToPtr("bar"),
ID: helper.StringToPtr("bar"),
ParentID: helper.StringToPtr("lol"),
Name: stringToPtr("foo"),
Namespace: stringToPtr("bar"),
ID: stringToPtr("bar"),
ParentID: stringToPtr("lol"),
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("bar"),
Name: stringToPtr("bar"),
Tasks: []*Task{
{
Name: "task1",
@ -202,45 +201,45 @@ func TestJobs_Canonicalize(t *testing.T) {
},
},
expected: &Job{
Namespace: helper.StringToPtr("bar"),
ID: helper.StringToPtr("bar"),
Name: helper.StringToPtr("foo"),
Region: helper.StringToPtr("global"),
Type: helper.StringToPtr("service"),
ParentID: helper.StringToPtr("lol"),
Priority: helper.IntToPtr(50),
AllAtOnce: helper.BoolToPtr(false),
VaultToken: helper.StringToPtr(""),
Stop: helper.BoolToPtr(false),
Stable: helper.BoolToPtr(false),
Version: helper.Uint64ToPtr(0),
Status: helper.StringToPtr(""),
StatusDescription: helper.StringToPtr(""),
CreateIndex: helper.Uint64ToPtr(0),
ModifyIndex: helper.Uint64ToPtr(0),
JobModifyIndex: helper.Uint64ToPtr(0),
Namespace: stringToPtr("bar"),
ID: stringToPtr("bar"),
Name: stringToPtr("foo"),
Region: stringToPtr("global"),
Type: stringToPtr("service"),
ParentID: stringToPtr("lol"),
Priority: intToPtr(50),
AllAtOnce: boolToPtr(false),
VaultToken: stringToPtr(""),
Stop: boolToPtr(false),
Stable: boolToPtr(false),
Version: uint64ToPtr(0),
Status: stringToPtr(""),
StatusDescription: stringToPtr(""),
CreateIndex: uint64ToPtr(0),
ModifyIndex: uint64ToPtr(0),
JobModifyIndex: uint64ToPtr(0),
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("bar"),
Count: helper.IntToPtr(1),
Name: stringToPtr("bar"),
Count: intToPtr(1),
EphemeralDisk: &EphemeralDisk{
Sticky: helper.BoolToPtr(false),
Migrate: helper.BoolToPtr(false),
SizeMB: helper.IntToPtr(300),
Sticky: boolToPtr(false),
Migrate: boolToPtr(false),
SizeMB: intToPtr(300),
},
RestartPolicy: &RestartPolicy{
Delay: helper.TimeToPtr(15 * time.Second),
Attempts: helper.IntToPtr(2),
Interval: helper.TimeToPtr(30 * time.Minute),
Mode: helper.StringToPtr("fail"),
Delay: timeToPtr(15 * time.Second),
Attempts: intToPtr(2),
Interval: timeToPtr(30 * time.Minute),
Mode: stringToPtr("fail"),
},
ReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr("exponential"),
Delay: helper.TimeToPtr(30 * time.Second),
MaxDelay: helper.TimeToPtr(1 * time.Hour),
Unlimited: helper.BoolToPtr(true),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
DelayFunction: stringToPtr("exponential"),
Delay: timeToPtr(30 * time.Second),
MaxDelay: timeToPtr(1 * time.Hour),
Unlimited: boolToPtr(true),
},
Migrate: DefaultMigrateStrategy(),
Tasks: []*Task{
@ -248,7 +247,7 @@ func TestJobs_Canonicalize(t *testing.T) {
Name: "task1",
LogConfig: DefaultLogConfig(),
Resources: DefaultResources(),
KillTimeout: helper.TimeToPtr(5 * time.Second),
KillTimeout: timeToPtr(5 * time.Second),
},
},
},
@ -258,25 +257,25 @@ func TestJobs_Canonicalize(t *testing.T) {
{
name: "example_template",
input: &Job{
ID: helper.StringToPtr("example_template"),
Name: helper.StringToPtr("example_template"),
ID: stringToPtr("example_template"),
Name: stringToPtr("example_template"),
Datacenters: []string{"dc1"},
Type: helper.StringToPtr("service"),
Type: stringToPtr("service"),
Update: &UpdateStrategy{
MaxParallel: helper.IntToPtr(1),
MaxParallel: intToPtr(1),
},
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("cache"),
Count: helper.IntToPtr(1),
Name: stringToPtr("cache"),
Count: intToPtr(1),
RestartPolicy: &RestartPolicy{
Interval: helper.TimeToPtr(5 * time.Minute),
Attempts: helper.IntToPtr(10),
Delay: helper.TimeToPtr(25 * time.Second),
Mode: helper.StringToPtr("delay"),
Interval: timeToPtr(5 * time.Minute),
Attempts: intToPtr(10),
Delay: timeToPtr(25 * time.Second),
Mode: stringToPtr("delay"),
},
EphemeralDisk: &EphemeralDisk{
SizeMB: helper.IntToPtr(300),
SizeMB: intToPtr(300),
},
Tasks: []*Task{
{
@ -289,11 +288,11 @@ func TestJobs_Canonicalize(t *testing.T) {
}},
},
Resources: &Resources{
CPU: helper.IntToPtr(500),
MemoryMB: helper.IntToPtr(256),
CPU: intToPtr(500),
MemoryMB: intToPtr(256),
Networks: []*NetworkResource{
{
MBits: helper.IntToPtr(10),
MBits: intToPtr(10),
DynamicPorts: []Port{
{
Label: "db",
@ -320,14 +319,14 @@ func TestJobs_Canonicalize(t *testing.T) {
},
Templates: []*Template{
{
EmbeddedTmpl: helper.StringToPtr("---"),
DestPath: helper.StringToPtr("local/file.yml"),
EmbeddedTmpl: stringToPtr("---"),
DestPath: stringToPtr("local/file.yml"),
},
{
EmbeddedTmpl: helper.StringToPtr("FOO=bar\n"),
DestPath: helper.StringToPtr("local/file.env"),
Envvars: helper.BoolToPtr(true),
VaultGrace: helper.TimeToPtr(3 * time.Second),
EmbeddedTmpl: stringToPtr("FOO=bar\n"),
DestPath: stringToPtr("local/file.env"),
Envvars: boolToPtr(true),
VaultGrace: timeToPtr(3 * time.Second),
},
},
},
@ -336,67 +335,67 @@ func TestJobs_Canonicalize(t *testing.T) {
},
},
expected: &Job{
Namespace: helper.StringToPtr(DefaultNamespace),
ID: helper.StringToPtr("example_template"),
Name: helper.StringToPtr("example_template"),
ParentID: helper.StringToPtr(""),
Priority: helper.IntToPtr(50),
Region: helper.StringToPtr("global"),
Type: helper.StringToPtr("service"),
AllAtOnce: helper.BoolToPtr(false),
VaultToken: helper.StringToPtr(""),
Stop: helper.BoolToPtr(false),
Stable: helper.BoolToPtr(false),
Version: helper.Uint64ToPtr(0),
Status: helper.StringToPtr(""),
StatusDescription: helper.StringToPtr(""),
CreateIndex: helper.Uint64ToPtr(0),
ModifyIndex: helper.Uint64ToPtr(0),
JobModifyIndex: helper.Uint64ToPtr(0),
Namespace: stringToPtr(DefaultNamespace),
ID: stringToPtr("example_template"),
Name: stringToPtr("example_template"),
ParentID: stringToPtr(""),
Priority: intToPtr(50),
Region: stringToPtr("global"),
Type: stringToPtr("service"),
AllAtOnce: boolToPtr(false),
VaultToken: stringToPtr(""),
Stop: boolToPtr(false),
Stable: boolToPtr(false),
Version: uint64ToPtr(0),
Status: stringToPtr(""),
StatusDescription: stringToPtr(""),
CreateIndex: uint64ToPtr(0),
ModifyIndex: uint64ToPtr(0),
JobModifyIndex: uint64ToPtr(0),
Datacenters: []string{"dc1"},
Update: &UpdateStrategy{
Stagger: helper.TimeToPtr(30 * time.Second),
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
ProgressDeadline: helper.TimeToPtr(10 * time.Minute),
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(0),
Stagger: timeToPtr(30 * time.Second),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(5 * time.Minute),
ProgressDeadline: timeToPtr(10 * time.Minute),
AutoRevert: boolToPtr(false),
Canary: intToPtr(0),
},
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("cache"),
Count: helper.IntToPtr(1),
Name: stringToPtr("cache"),
Count: intToPtr(1),
RestartPolicy: &RestartPolicy{
Interval: helper.TimeToPtr(5 * time.Minute),
Attempts: helper.IntToPtr(10),
Delay: helper.TimeToPtr(25 * time.Second),
Mode: helper.StringToPtr("delay"),
Interval: timeToPtr(5 * time.Minute),
Attempts: intToPtr(10),
Delay: timeToPtr(25 * time.Second),
Mode: stringToPtr("delay"),
},
ReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr("exponential"),
Delay: helper.TimeToPtr(30 * time.Second),
MaxDelay: helper.TimeToPtr(1 * time.Hour),
Unlimited: helper.BoolToPtr(true),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
DelayFunction: stringToPtr("exponential"),
Delay: timeToPtr(30 * time.Second),
MaxDelay: timeToPtr(1 * time.Hour),
Unlimited: boolToPtr(true),
},
EphemeralDisk: &EphemeralDisk{
Sticky: helper.BoolToPtr(false),
Migrate: helper.BoolToPtr(false),
SizeMB: helper.IntToPtr(300),
Sticky: boolToPtr(false),
Migrate: boolToPtr(false),
SizeMB: intToPtr(300),
},
Update: &UpdateStrategy{
Stagger: helper.TimeToPtr(30 * time.Second),
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
ProgressDeadline: helper.TimeToPtr(10 * time.Minute),
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(0),
Stagger: timeToPtr(30 * time.Second),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(5 * time.Minute),
ProgressDeadline: timeToPtr(10 * time.Minute),
AutoRevert: boolToPtr(false),
Canary: intToPtr(0),
},
Migrate: DefaultMigrateStrategy(),
Tasks: []*Task{
@ -410,11 +409,11 @@ func TestJobs_Canonicalize(t *testing.T) {
}},
},
Resources: &Resources{
CPU: helper.IntToPtr(500),
MemoryMB: helper.IntToPtr(256),
CPU: intToPtr(500),
MemoryMB: intToPtr(256),
Networks: []*NetworkResource{
{
MBits: helper.IntToPtr(10),
MBits: intToPtr(10),
DynamicPorts: []Port{
{
Label: "db",
@ -440,34 +439,34 @@ func TestJobs_Canonicalize(t *testing.T) {
},
},
},
KillTimeout: helper.TimeToPtr(5 * time.Second),
KillTimeout: timeToPtr(5 * time.Second),
LogConfig: DefaultLogConfig(),
Templates: []*Template{
{
SourcePath: helper.StringToPtr(""),
DestPath: helper.StringToPtr("local/file.yml"),
EmbeddedTmpl: helper.StringToPtr("---"),
ChangeMode: helper.StringToPtr("restart"),
ChangeSignal: helper.StringToPtr(""),
Splay: helper.TimeToPtr(5 * time.Second),
Perms: helper.StringToPtr("0644"),
LeftDelim: helper.StringToPtr("{{"),
RightDelim: helper.StringToPtr("}}"),
Envvars: helper.BoolToPtr(false),
VaultGrace: helper.TimeToPtr(15 * time.Second),
SourcePath: stringToPtr(""),
DestPath: stringToPtr("local/file.yml"),
EmbeddedTmpl: stringToPtr("---"),
ChangeMode: stringToPtr("restart"),
ChangeSignal: stringToPtr(""),
Splay: timeToPtr(5 * time.Second),
Perms: stringToPtr("0644"),
LeftDelim: stringToPtr("{{"),
RightDelim: stringToPtr("}}"),
Envvars: boolToPtr(false),
VaultGrace: timeToPtr(15 * time.Second),
},
{
SourcePath: helper.StringToPtr(""),
DestPath: helper.StringToPtr("local/file.env"),
EmbeddedTmpl: helper.StringToPtr("FOO=bar\n"),
ChangeMode: helper.StringToPtr("restart"),
ChangeSignal: helper.StringToPtr(""),
Splay: helper.TimeToPtr(5 * time.Second),
Perms: helper.StringToPtr("0644"),
LeftDelim: helper.StringToPtr("{{"),
RightDelim: helper.StringToPtr("}}"),
Envvars: helper.BoolToPtr(true),
VaultGrace: helper.TimeToPtr(3 * time.Second),
SourcePath: stringToPtr(""),
DestPath: stringToPtr("local/file.env"),
EmbeddedTmpl: stringToPtr("FOO=bar\n"),
ChangeMode: stringToPtr("restart"),
ChangeSignal: stringToPtr(""),
Splay: timeToPtr(5 * time.Second),
Perms: stringToPtr("0644"),
LeftDelim: stringToPtr("{{"),
RightDelim: stringToPtr("}}"),
Envvars: boolToPtr(true),
VaultGrace: timeToPtr(3 * time.Second),
},
},
},
@ -480,33 +479,33 @@ func TestJobs_Canonicalize(t *testing.T) {
{
name: "periodic",
input: &Job{
ID: helper.StringToPtr("bar"),
ID: stringToPtr("bar"),
Periodic: &PeriodicConfig{},
},
expected: &Job{
Namespace: helper.StringToPtr(DefaultNamespace),
ID: helper.StringToPtr("bar"),
ParentID: helper.StringToPtr(""),
Name: helper.StringToPtr("bar"),
Region: helper.StringToPtr("global"),
Type: helper.StringToPtr("service"),
Priority: helper.IntToPtr(50),
AllAtOnce: helper.BoolToPtr(false),
VaultToken: helper.StringToPtr(""),
Stop: helper.BoolToPtr(false),
Stable: helper.BoolToPtr(false),
Version: helper.Uint64ToPtr(0),
Status: helper.StringToPtr(""),
StatusDescription: helper.StringToPtr(""),
CreateIndex: helper.Uint64ToPtr(0),
ModifyIndex: helper.Uint64ToPtr(0),
JobModifyIndex: helper.Uint64ToPtr(0),
Namespace: stringToPtr(DefaultNamespace),
ID: stringToPtr("bar"),
ParentID: stringToPtr(""),
Name: stringToPtr("bar"),
Region: stringToPtr("global"),
Type: stringToPtr("service"),
Priority: intToPtr(50),
AllAtOnce: boolToPtr(false),
VaultToken: stringToPtr(""),
Stop: boolToPtr(false),
Stable: boolToPtr(false),
Version: uint64ToPtr(0),
Status: stringToPtr(""),
StatusDescription: stringToPtr(""),
CreateIndex: uint64ToPtr(0),
ModifyIndex: uint64ToPtr(0),
JobModifyIndex: uint64ToPtr(0),
Periodic: &PeriodicConfig{
Enabled: helper.BoolToPtr(true),
Spec: helper.StringToPtr(""),
SpecType: helper.StringToPtr(PeriodicSpecCron),
ProhibitOverlap: helper.BoolToPtr(false),
TimeZone: helper.StringToPtr("UTC"),
Enabled: boolToPtr(true),
Spec: stringToPtr(""),
SpecType: stringToPtr(PeriodicSpecCron),
ProhibitOverlap: boolToPtr(false),
TimeZone: stringToPtr("UTC"),
},
},
},
@ -514,29 +513,29 @@ func TestJobs_Canonicalize(t *testing.T) {
{
name: "update_merge",
input: &Job{
Name: helper.StringToPtr("foo"),
ID: helper.StringToPtr("bar"),
ParentID: helper.StringToPtr("lol"),
Name: stringToPtr("foo"),
ID: stringToPtr("bar"),
ParentID: stringToPtr("lol"),
Update: &UpdateStrategy{
Stagger: helper.TimeToPtr(1 * time.Second),
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(6 * time.Minute),
ProgressDeadline: helper.TimeToPtr(7 * time.Minute),
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(0),
Stagger: timeToPtr(1 * time.Second),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(6 * time.Minute),
ProgressDeadline: timeToPtr(7 * time.Minute),
AutoRevert: boolToPtr(false),
Canary: intToPtr(0),
},
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("bar"),
Name: stringToPtr("bar"),
Update: &UpdateStrategy{
Stagger: helper.TimeToPtr(2 * time.Second),
MaxParallel: helper.IntToPtr(2),
HealthCheck: helper.StringToPtr("manual"),
MinHealthyTime: helper.TimeToPtr(1 * time.Second),
AutoRevert: helper.BoolToPtr(true),
Canary: helper.IntToPtr(1),
Stagger: timeToPtr(2 * time.Second),
MaxParallel: intToPtr(2),
HealthCheck: stringToPtr("manual"),
MinHealthyTime: timeToPtr(1 * time.Second),
AutoRevert: boolToPtr(true),
Canary: intToPtr(1),
},
Tasks: []*Task{
{
@ -545,7 +544,7 @@ func TestJobs_Canonicalize(t *testing.T) {
},
},
{
Name: helper.StringToPtr("baz"),
Name: stringToPtr("baz"),
Tasks: []*Task{
{
Name: "task1",
@ -555,65 +554,65 @@ func TestJobs_Canonicalize(t *testing.T) {
},
},
expected: &Job{
Namespace: helper.StringToPtr(DefaultNamespace),
ID: helper.StringToPtr("bar"),
Name: helper.StringToPtr("foo"),
Region: helper.StringToPtr("global"),
Type: helper.StringToPtr("service"),
ParentID: helper.StringToPtr("lol"),
Priority: helper.IntToPtr(50),
AllAtOnce: helper.BoolToPtr(false),
VaultToken: helper.StringToPtr(""),
Stop: helper.BoolToPtr(false),
Stable: helper.BoolToPtr(false),
Version: helper.Uint64ToPtr(0),
Status: helper.StringToPtr(""),
StatusDescription: helper.StringToPtr(""),
CreateIndex: helper.Uint64ToPtr(0),
ModifyIndex: helper.Uint64ToPtr(0),
JobModifyIndex: helper.Uint64ToPtr(0),
Namespace: stringToPtr(DefaultNamespace),
ID: stringToPtr("bar"),
Name: stringToPtr("foo"),
Region: stringToPtr("global"),
Type: stringToPtr("service"),
ParentID: stringToPtr("lol"),
Priority: intToPtr(50),
AllAtOnce: boolToPtr(false),
VaultToken: stringToPtr(""),
Stop: boolToPtr(false),
Stable: boolToPtr(false),
Version: uint64ToPtr(0),
Status: stringToPtr(""),
StatusDescription: stringToPtr(""),
CreateIndex: uint64ToPtr(0),
ModifyIndex: uint64ToPtr(0),
JobModifyIndex: uint64ToPtr(0),
Update: &UpdateStrategy{
Stagger: helper.TimeToPtr(1 * time.Second),
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(6 * time.Minute),
ProgressDeadline: helper.TimeToPtr(7 * time.Minute),
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(0),
Stagger: timeToPtr(1 * time.Second),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(6 * time.Minute),
ProgressDeadline: timeToPtr(7 * time.Minute),
AutoRevert: boolToPtr(false),
Canary: intToPtr(0),
},
TaskGroups: []*TaskGroup{
{
Name: helper.StringToPtr("bar"),
Count: helper.IntToPtr(1),
Name: stringToPtr("bar"),
Count: intToPtr(1),
EphemeralDisk: &EphemeralDisk{
Sticky: helper.BoolToPtr(false),
Migrate: helper.BoolToPtr(false),
SizeMB: helper.IntToPtr(300),
Sticky: boolToPtr(false),
Migrate: boolToPtr(false),
SizeMB: intToPtr(300),
},
RestartPolicy: &RestartPolicy{
Delay: helper.TimeToPtr(15 * time.Second),
Attempts: helper.IntToPtr(2),
Interval: helper.TimeToPtr(30 * time.Minute),
Mode: helper.StringToPtr("fail"),
Delay: timeToPtr(15 * time.Second),
Attempts: intToPtr(2),
Interval: timeToPtr(30 * time.Minute),
Mode: stringToPtr("fail"),
},
ReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr("exponential"),
Delay: helper.TimeToPtr(30 * time.Second),
MaxDelay: helper.TimeToPtr(1 * time.Hour),
Unlimited: helper.BoolToPtr(true),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
DelayFunction: stringToPtr("exponential"),
Delay: timeToPtr(30 * time.Second),
MaxDelay: timeToPtr(1 * time.Hour),
Unlimited: boolToPtr(true),
},
Update: &UpdateStrategy{
Stagger: helper.TimeToPtr(2 * time.Second),
MaxParallel: helper.IntToPtr(2),
HealthCheck: helper.StringToPtr("manual"),
MinHealthyTime: helper.TimeToPtr(1 * time.Second),
HealthyDeadline: helper.TimeToPtr(6 * time.Minute),
ProgressDeadline: helper.TimeToPtr(7 * time.Minute),
AutoRevert: helper.BoolToPtr(true),
Canary: helper.IntToPtr(1),
Stagger: timeToPtr(2 * time.Second),
MaxParallel: intToPtr(2),
HealthCheck: stringToPtr("manual"),
MinHealthyTime: timeToPtr(1 * time.Second),
HealthyDeadline: timeToPtr(6 * time.Minute),
ProgressDeadline: timeToPtr(7 * time.Minute),
AutoRevert: boolToPtr(true),
Canary: intToPtr(1),
},
Migrate: DefaultMigrateStrategy(),
Tasks: []*Task{
@ -621,41 +620,41 @@ func TestJobs_Canonicalize(t *testing.T) {
Name: "task1",
LogConfig: DefaultLogConfig(),
Resources: DefaultResources(),
KillTimeout: helper.TimeToPtr(5 * time.Second),
KillTimeout: timeToPtr(5 * time.Second),
},
},
},
{
Name: helper.StringToPtr("baz"),
Count: helper.IntToPtr(1),
Name: stringToPtr("baz"),
Count: intToPtr(1),
EphemeralDisk: &EphemeralDisk{
Sticky: helper.BoolToPtr(false),
Migrate: helper.BoolToPtr(false),
SizeMB: helper.IntToPtr(300),
Sticky: boolToPtr(false),
Migrate: boolToPtr(false),
SizeMB: intToPtr(300),
},
RestartPolicy: &RestartPolicy{
Delay: helper.TimeToPtr(15 * time.Second),
Attempts: helper.IntToPtr(2),
Interval: helper.TimeToPtr(30 * time.Minute),
Mode: helper.StringToPtr("fail"),
Delay: timeToPtr(15 * time.Second),
Attempts: intToPtr(2),
Interval: timeToPtr(30 * time.Minute),
Mode: stringToPtr("fail"),
},
ReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr("exponential"),
Delay: helper.TimeToPtr(30 * time.Second),
MaxDelay: helper.TimeToPtr(1 * time.Hour),
Unlimited: helper.BoolToPtr(true),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
DelayFunction: stringToPtr("exponential"),
Delay: timeToPtr(30 * time.Second),
MaxDelay: timeToPtr(1 * time.Hour),
Unlimited: boolToPtr(true),
},
Update: &UpdateStrategy{
Stagger: helper.TimeToPtr(1 * time.Second),
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(6 * time.Minute),
ProgressDeadline: helper.TimeToPtr(7 * time.Minute),
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(0),
Stagger: timeToPtr(1 * time.Second),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(6 * time.Minute),
ProgressDeadline: timeToPtr(7 * time.Minute),
AutoRevert: boolToPtr(false),
Canary: intToPtr(0),
},
Migrate: DefaultMigrateStrategy(),
Tasks: []*Task{
@ -663,7 +662,7 @@ func TestJobs_Canonicalize(t *testing.T) {
Name: "task1",
LogConfig: DefaultLogConfig(),
Resources: DefaultResources(),
KillTimeout: helper.TimeToPtr(5 * time.Second),
KillTimeout: timeToPtr(5 * time.Second),
},
},
},
@ -756,13 +755,13 @@ func TestJobs_Revert(t *testing.T) {
assertWriteMeta(t, wm)
// Fail revert at incorrect enforce
_, _, err = jobs.Revert(*job.ID, 0, helper.Uint64ToPtr(10), nil)
_, _, err = jobs.Revert(*job.ID, 0, uint64ToPtr(10), nil)
if err == nil || !strings.Contains(err.Error(), "enforcing version") {
t.Fatalf("expected enforcement error: %v", err)
}
// Works at correct index
revertResp, wm, err := jobs.Revert(*job.ID, 0, helper.Uint64ToPtr(1), nil)
revertResp, wm, err := jobs.Revert(*job.ID, 0, uint64ToPtr(1), nil)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -1248,11 +1247,11 @@ func TestJobs_NewBatchJob(t *testing.T) {
t.Parallel()
job := NewBatchJob("job1", "myjob", "region1", 5)
expect := &Job{
Region: helper.StringToPtr("region1"),
ID: helper.StringToPtr("job1"),
Name: helper.StringToPtr("myjob"),
Type: helper.StringToPtr(JobTypeBatch),
Priority: helper.IntToPtr(5),
Region: stringToPtr("region1"),
ID: stringToPtr("job1"),
Name: stringToPtr("myjob"),
Type: stringToPtr(JobTypeBatch),
Priority: intToPtr(5),
}
if !reflect.DeepEqual(job, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, job)
@ -1263,11 +1262,11 @@ func TestJobs_NewServiceJob(t *testing.T) {
t.Parallel()
job := NewServiceJob("job1", "myjob", "region1", 5)
expect := &Job{
Region: helper.StringToPtr("region1"),
ID: helper.StringToPtr("job1"),
Name: helper.StringToPtr("myjob"),
Type: helper.StringToPtr(JobTypeService),
Priority: helper.IntToPtr(5),
Region: stringToPtr("region1"),
ID: stringToPtr("job1"),
Name: stringToPtr("myjob"),
Type: stringToPtr(JobTypeService),
Priority: intToPtr(5),
}
if !reflect.DeepEqual(job, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, job)
@ -1413,7 +1412,7 @@ func TestJobs_AddSpread(t *testing.T) {
expect := []*Spread{
{
Attribute: "${meta.rack}",
Weight: helper.IntToPtr(100),
Weight: intToPtr(100),
SpreadTarget: []*SpreadTarget{
{
Value: "r1",
@ -1423,7 +1422,7 @@ func TestJobs_AddSpread(t *testing.T) {
},
{
Attribute: "${node.datacenter}",
Weight: helper.IntToPtr(100),
Weight: intToPtr(100),
SpreadTarget: []*SpreadTarget{
{
Value: "dc1",

View File

@ -6,9 +6,18 @@ import (
"sort"
"strconv"
"time"
)
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/structs"
const (
NodeStatusInit = "initializing"
NodeStatusReady = "ready"
NodeStatusDown = "down"
// NodeSchedulingEligible and Ineligible marks the node as eligible or not,
// respectively, for receiving allocations. This is orthoginal to the node
// status being ready.
NodeSchedulingEligible = "eligible"
NodeSchedulingIneligible = "ineligible"
)
// Nodes is used to query node-related API endpoints
@ -224,7 +233,7 @@ func (n *Nodes) monitorDrainNode(ctx context.Context, nodeID string,
return
}
if node.Status == structs.NodeStatusDown {
if node.Status == NodeStatusDown {
msg := Messagef(MonitorMsgLevelWarn, "Node %q down", nodeID)
select {
case nodeCh <- msg:
@ -295,7 +304,7 @@ func (n *Nodes) monitorDrainAllocs(ctx context.Context, nodeID string, ignoreSys
// Alloc was marked for migration
msg = "marked for migration"
case migrating && (orig.DesiredStatus != a.DesiredStatus) && a.DesiredStatus == structs.AllocDesiredStatusStop:
case migrating && (orig.DesiredStatus != a.DesiredStatus) && a.DesiredStatus == AllocDesiredStatusStop:
// Alloc has already been marked for migration and is now being stopped
msg = "draining"
}
@ -314,12 +323,12 @@ func (n *Nodes) monitorDrainAllocs(ctx context.Context, nodeID string, ignoreSys
}
// Track how many allocs are still running
if ignoreSys && a.Job.Type != nil && *a.Job.Type == structs.JobTypeSystem {
if ignoreSys && a.Job.Type != nil && *a.Job.Type == JobTypeSystem {
continue
}
switch a.ClientStatus {
case structs.AllocClientStatusPending, structs.AllocClientStatusRunning:
case AllocClientStatusPending, AllocClientStatusRunning:
runningAllocs++
}
}
@ -353,9 +362,9 @@ type NodeEligibilityUpdateResponse struct {
// ToggleEligibility is used to update the scheduling eligibility of the node
func (n *Nodes) ToggleEligibility(nodeID string, eligible bool, q *WriteOptions) (*NodeEligibilityUpdateResponse, error) {
e := structs.NodeSchedulingEligible
e := NodeSchedulingEligible
if !eligible {
e = structs.NodeSchedulingIneligible
e = NodeSchedulingIneligible
}
req := &NodeUpdateEligibilityRequest{
@ -662,9 +671,9 @@ func (v *StatValue) String() string {
case v.StringVal != nil:
return *v.StringVal
case v.FloatNumeratorVal != nil:
str := helper.FormatFloat(*v.FloatNumeratorVal, 3)
str := formatFloat(*v.FloatNumeratorVal, 3)
if v.FloatDenominatorVal != nil {
str += " / " + helper.FormatFloat(*v.FloatDenominatorVal, 3)
str += " / " + formatFloat(*v.FloatDenominatorVal, 3)
}
if v.Unit != "" {

View File

@ -9,7 +9,6 @@ import (
"testing"
"time"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
@ -185,8 +184,8 @@ func TestNodes_ToggleDrain(t *testing.T) {
// Check again
out, _, err = nodes.Info(nodeID, nil)
require.Nil(err)
if out.SchedulingEligibility != structs.NodeSchedulingIneligible {
t.Fatalf("bad eligibility: %v vs %v", out.SchedulingEligibility, structs.NodeSchedulingIneligible)
if out.SchedulingEligibility != NodeSchedulingIneligible {
t.Fatalf("bad eligibility: %v vs %v", out.SchedulingEligibility, NodeSchedulingIneligible)
}
// Toggle off again
@ -203,7 +202,7 @@ func TestNodes_ToggleDrain(t *testing.T) {
if out.DrainStrategy != nil {
t.Fatalf("drain strategy should be unset")
}
if out.SchedulingEligibility != structs.NodeSchedulingEligible {
if out.SchedulingEligibility != NodeSchedulingEligible {
t.Fatalf("should be eligible")
}
}
@ -237,7 +236,7 @@ func TestNodes_ToggleEligibility(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
if out.SchedulingEligibility != structs.NodeSchedulingEligible {
if out.SchedulingEligibility != NodeSchedulingEligible {
t.Fatalf("node should be eligible")
}
@ -253,8 +252,8 @@ func TestNodes_ToggleEligibility(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
if out.SchedulingEligibility != structs.NodeSchedulingIneligible {
t.Fatalf("bad eligibility: %v vs %v", out.SchedulingEligibility, structs.NodeSchedulingIneligible)
if out.SchedulingEligibility != NodeSchedulingIneligible {
t.Fatalf("bad eligibility: %v vs %v", out.SchedulingEligibility, NodeSchedulingIneligible)
}
// Toggle on
@ -269,8 +268,8 @@ func TestNodes_ToggleEligibility(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
if out.SchedulingEligibility != structs.NodeSchedulingEligible {
t.Fatalf("bad eligibility: %v vs %v", out.SchedulingEligibility, structs.NodeSchedulingEligible)
if out.SchedulingEligibility != NodeSchedulingEligible {
t.Fatalf("bad eligibility: %v vs %v", out.SchedulingEligibility, NodeSchedulingEligible)
}
if out.DrainStrategy != nil {
t.Fatalf("drain strategy should be unset")
@ -542,69 +541,69 @@ func TestNodeStatValueFormatting(t *testing.T) {
}{
{
"true",
StatValue{BoolVal: helper.BoolToPtr(true)},
StatValue{BoolVal: boolToPtr(true)},
},
{
"false",
StatValue{BoolVal: helper.BoolToPtr(false)},
StatValue{BoolVal: boolToPtr(false)},
},
{
"myvalue",
StatValue{StringVal: helper.StringToPtr("myvalue")},
StatValue{StringVal: stringToPtr("myvalue")},
},
{
"2.718",
StatValue{
FloatNumeratorVal: helper.Float64ToPtr(2.718),
FloatNumeratorVal: float64ToPtr(2.718),
},
},
{
"2.718 / 3.14",
StatValue{
FloatNumeratorVal: helper.Float64ToPtr(2.718),
FloatDenominatorVal: helper.Float64ToPtr(3.14),
FloatNumeratorVal: float64ToPtr(2.718),
FloatDenominatorVal: float64ToPtr(3.14),
},
},
{
"2.718 MHz",
StatValue{
FloatNumeratorVal: helper.Float64ToPtr(2.718),
FloatNumeratorVal: float64ToPtr(2.718),
Unit: "MHz",
},
},
{
"2.718 / 3.14 MHz",
StatValue{
FloatNumeratorVal: helper.Float64ToPtr(2.718),
FloatDenominatorVal: helper.Float64ToPtr(3.14),
FloatNumeratorVal: float64ToPtr(2.718),
FloatDenominatorVal: float64ToPtr(3.14),
Unit: "MHz",
},
},
{
"2",
StatValue{
IntNumeratorVal: helper.Int64ToPtr(2),
IntNumeratorVal: int64ToPtr(2),
},
},
{
"2 / 3",
StatValue{
IntNumeratorVal: helper.Int64ToPtr(2),
IntDenominatorVal: helper.Int64ToPtr(3),
IntNumeratorVal: int64ToPtr(2),
IntDenominatorVal: int64ToPtr(3),
},
},
{
"2 MHz",
StatValue{
IntNumeratorVal: helper.Int64ToPtr(2),
IntNumeratorVal: int64ToPtr(2),
Unit: "MHz",
},
},
{
"2 / 3 MHz",
StatValue{
IntNumeratorVal: helper.Int64ToPtr(2),
IntDenominatorVal: helper.Int64ToPtr(3),
IntNumeratorVal: int64ToPtr(2),
IntDenominatorVal: int64ToPtr(3),
Unit: "MHz",
},
},

View File

@ -2,8 +2,6 @@ package api
import (
"strconv"
"github.com/hashicorp/nomad/helper"
)
// Resources encapsulates the required resources of
@ -46,8 +44,8 @@ func (r *Resources) Canonicalize() {
// and should be kept in sync.
func DefaultResources() *Resources {
return &Resources{
CPU: helper.IntToPtr(100),
MemoryMB: helper.IntToPtr(300),
CPU: intToPtr(100),
MemoryMB: intToPtr(300),
}
}
@ -58,8 +56,8 @@ func DefaultResources() *Resources {
// IN nomad/structs/structs.go and should be kept in sync.
func MinResources() *Resources {
return &Resources{
CPU: helper.IntToPtr(20),
MemoryMB: helper.IntToPtr(10),
CPU: intToPtr(20),
MemoryMB: intToPtr(10),
}
}
@ -103,7 +101,7 @@ type NetworkResource struct {
func (n *NetworkResource) Canonicalize() {
if n.MBits == nil {
n.MBits = helper.IntToPtr(10)
n.MBits = intToPtr(10)
}
}
@ -169,7 +167,7 @@ type Attribute struct {
func (a Attribute) String() string {
switch {
case a.FloatVal != nil:
str := helper.FormatFloat(*a.FloatVal, 3)
str := formatFloat(*a.FloatVal, 3)
if a.Unit != "" {
str += " " + a.Unit
}
@ -223,6 +221,6 @@ type RequestedDevice struct {
func (d *RequestedDevice) Canonicalize() {
if d.Count == nil {
d.Count = helper.Uint64ToPtr(1)
d.Count = uint64ToPtr(1)
}
}

View File

@ -6,9 +6,16 @@ import (
"path/filepath"
"strings"
"time"
)
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/structs"
const (
// RestartPolicyModeDelay causes an artificial delay till the next interval is
// reached when the specified attempts have been reached in the interval.
RestartPolicyModeDelay = "delay"
// RestartPolicyModeFail causes a job to fail if the specified number of
// attempts are reached within an interval.
RestartPolicyModeFail = "fail"
)
// MemoryStats holds memory usage related stats
@ -16,6 +23,7 @@ type MemoryStats struct {
RSS uint64
Cache uint64
Swap uint64
Usage uint64
MaxUsage uint64
KernelUsage uint64
KernelMaxUsage uint64
@ -170,32 +178,38 @@ func NewDefaultReschedulePolicy(jobType string) *ReschedulePolicy {
var dp *ReschedulePolicy
switch jobType {
case "service":
// This needs to be in sync with DefaultServiceJobReschedulePolicy
// in nomad/structs/structs.go
dp = &ReschedulePolicy{
Attempts: helper.IntToPtr(structs.DefaultServiceJobReschedulePolicy.Attempts),
Interval: helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.Interval),
Delay: helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.Delay),
DelayFunction: helper.StringToPtr(structs.DefaultServiceJobReschedulePolicy.DelayFunction),
MaxDelay: helper.TimeToPtr(structs.DefaultServiceJobReschedulePolicy.MaxDelay),
Unlimited: helper.BoolToPtr(structs.DefaultServiceJobReschedulePolicy.Unlimited),
Delay: timeToPtr(30 * time.Second),
DelayFunction: stringToPtr("exponential"),
MaxDelay: timeToPtr(1 * time.Hour),
Unlimited: boolToPtr(true),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
}
case "batch":
// This needs to be in sync with DefaultBatchJobReschedulePolicy
// in nomad/structs/structs.go
dp = &ReschedulePolicy{
Attempts: helper.IntToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts),
Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
Delay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
DelayFunction: helper.StringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
MaxDelay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
Unlimited: helper.BoolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
Attempts: intToPtr(1),
Interval: timeToPtr(24 * time.Hour),
Delay: timeToPtr(5 * time.Second),
DelayFunction: stringToPtr("constant"),
MaxDelay: timeToPtr(0),
Unlimited: boolToPtr(false),
}
case "system":
dp = &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
Delay: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr(""),
MaxDelay: helper.TimeToPtr(0),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
Delay: timeToPtr(0),
DelayFunction: stringToPtr(""),
MaxDelay: timeToPtr(0),
Unlimited: boolToPtr(false),
}
}
return dp
@ -243,14 +257,14 @@ func NewSpreadTarget(value string, percent uint32) *SpreadTarget {
func NewSpread(attribute string, weight int, spreadTargets []*SpreadTarget) *Spread {
return &Spread{
Attribute: attribute,
Weight: helper.IntToPtr(weight),
Weight: intToPtr(weight),
SpreadTarget: spreadTargets,
}
}
func (s *Spread) Canonicalize() {
if s.Weight == nil {
s.Weight = helper.IntToPtr(50)
s.Weight = intToPtr(50)
}
}
@ -269,7 +283,7 @@ func (c *CheckRestart) Canonicalize() {
}
if c.Grace == nil {
c.Grace = helper.TimeToPtr(1 * time.Second)
c.Grace = timeToPtr(1 * time.Second)
}
}
@ -381,21 +395,21 @@ type EphemeralDisk struct {
func DefaultEphemeralDisk() *EphemeralDisk {
return &EphemeralDisk{
Sticky: helper.BoolToPtr(false),
Migrate: helper.BoolToPtr(false),
SizeMB: helper.IntToPtr(300),
Sticky: boolToPtr(false),
Migrate: boolToPtr(false),
SizeMB: intToPtr(300),
}
}
func (e *EphemeralDisk) Canonicalize() {
if e.Sticky == nil {
e.Sticky = helper.BoolToPtr(false)
e.Sticky = boolToPtr(false)
}
if e.Migrate == nil {
e.Migrate = helper.BoolToPtr(false)
e.Migrate = boolToPtr(false)
}
if e.SizeMB == nil {
e.SizeMB = helper.IntToPtr(300)
e.SizeMB = intToPtr(300)
}
}
@ -410,10 +424,10 @@ type MigrateStrategy struct {
func DefaultMigrateStrategy() *MigrateStrategy {
return &MigrateStrategy{
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(5 * time.Minute),
}
}
@ -479,17 +493,17 @@ type TaskGroup struct {
// NewTaskGroup creates a new TaskGroup.
func NewTaskGroup(name string, count int) *TaskGroup {
return &TaskGroup{
Name: helper.StringToPtr(name),
Count: helper.IntToPtr(count),
Name: stringToPtr(name),
Count: intToPtr(count),
}
}
func (g *TaskGroup) Canonicalize(job *Job) {
if g.Name == nil {
g.Name = helper.StringToPtr("")
g.Name = stringToPtr("")
}
if g.Count == nil {
g.Count = helper.IntToPtr(1)
g.Count = intToPtr(1)
}
for _, t := range g.Tasks {
t.Canonicalize(g, job)
@ -555,18 +569,22 @@ func (g *TaskGroup) Canonicalize(job *Job) {
var defaultRestartPolicy *RestartPolicy
switch *job.Type {
case "service", "system":
// These needs to be in sync with DefaultServiceJobRestartPolicy in
// in nomad/structs/structs.go
defaultRestartPolicy = &RestartPolicy{
Delay: helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Delay),
Attempts: helper.IntToPtr(structs.DefaultServiceJobRestartPolicy.Attempts),
Interval: helper.TimeToPtr(structs.DefaultServiceJobRestartPolicy.Interval),
Mode: helper.StringToPtr(structs.DefaultServiceJobRestartPolicy.Mode),
Delay: timeToPtr(15 * time.Second),
Attempts: intToPtr(2),
Interval: timeToPtr(30 * time.Minute),
Mode: stringToPtr(RestartPolicyModeFail),
}
default:
// These needs to be in sync with DefaultBatchJobRestartPolicy in
// in nomad/structs/structs.go
defaultRestartPolicy = &RestartPolicy{
Delay: helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Delay),
Attempts: helper.IntToPtr(structs.DefaultBatchJobRestartPolicy.Attempts),
Interval: helper.TimeToPtr(structs.DefaultBatchJobRestartPolicy.Interval),
Mode: helper.StringToPtr(structs.DefaultBatchJobRestartPolicy.Mode),
Delay: timeToPtr(15 * time.Second),
Attempts: intToPtr(3),
Interval: timeToPtr(24 * time.Hour),
Mode: stringToPtr(RestartPolicyModeFail),
}
}
@ -628,17 +646,17 @@ type LogConfig struct {
func DefaultLogConfig() *LogConfig {
return &LogConfig{
MaxFiles: helper.IntToPtr(10),
MaxFileSizeMB: helper.IntToPtr(10),
MaxFiles: intToPtr(10),
MaxFileSizeMB: intToPtr(10),
}
}
func (l *LogConfig) Canonicalize() {
if l.MaxFiles == nil {
l.MaxFiles = helper.IntToPtr(10)
l.MaxFiles = intToPtr(10)
}
if l.MaxFileSizeMB == nil {
l.MaxFileSizeMB = helper.IntToPtr(10)
l.MaxFileSizeMB = intToPtr(10)
}
}
@ -676,7 +694,7 @@ func (t *Task) Canonicalize(tg *TaskGroup, job *Job) {
}
t.Resources.Canonicalize()
if t.KillTimeout == nil {
t.KillTimeout = helper.TimeToPtr(5 * time.Second)
t.KillTimeout = timeToPtr(5 * time.Second)
}
if t.LogConfig == nil {
t.LogConfig = DefaultLogConfig()
@ -707,11 +725,11 @@ type TaskArtifact struct {
func (a *TaskArtifact) Canonicalize() {
if a.GetterMode == nil {
a.GetterMode = helper.StringToPtr("any")
a.GetterMode = stringToPtr("any")
}
if a.GetterSource == nil {
// Shouldn't be possible, but we don't want to panic
a.GetterSource = helper.StringToPtr("")
a.GetterSource = stringToPtr("")
}
if a.RelativeDest == nil {
switch *a.GetterMode {
@ -723,7 +741,7 @@ func (a *TaskArtifact) Canonicalize() {
a.RelativeDest = &dest
default:
// Default to a directory
a.RelativeDest = helper.StringToPtr("local/")
a.RelativeDest = stringToPtr("local/")
}
}
}
@ -744,44 +762,44 @@ type Template struct {
func (tmpl *Template) Canonicalize() {
if tmpl.SourcePath == nil {
tmpl.SourcePath = helper.StringToPtr("")
tmpl.SourcePath = stringToPtr("")
}
if tmpl.DestPath == nil {
tmpl.DestPath = helper.StringToPtr("")
tmpl.DestPath = stringToPtr("")
}
if tmpl.EmbeddedTmpl == nil {
tmpl.EmbeddedTmpl = helper.StringToPtr("")
tmpl.EmbeddedTmpl = stringToPtr("")
}
if tmpl.ChangeMode == nil {
tmpl.ChangeMode = helper.StringToPtr("restart")
tmpl.ChangeMode = stringToPtr("restart")
}
if tmpl.ChangeSignal == nil {
if *tmpl.ChangeMode == "signal" {
tmpl.ChangeSignal = helper.StringToPtr("SIGHUP")
tmpl.ChangeSignal = stringToPtr("SIGHUP")
} else {
tmpl.ChangeSignal = helper.StringToPtr("")
tmpl.ChangeSignal = stringToPtr("")
}
} else {
sig := *tmpl.ChangeSignal
tmpl.ChangeSignal = helper.StringToPtr(strings.ToUpper(sig))
tmpl.ChangeSignal = stringToPtr(strings.ToUpper(sig))
}
if tmpl.Splay == nil {
tmpl.Splay = helper.TimeToPtr(5 * time.Second)
tmpl.Splay = timeToPtr(5 * time.Second)
}
if tmpl.Perms == nil {
tmpl.Perms = helper.StringToPtr("0644")
tmpl.Perms = stringToPtr("0644")
}
if tmpl.LeftDelim == nil {
tmpl.LeftDelim = helper.StringToPtr("{{")
tmpl.LeftDelim = stringToPtr("{{")
}
if tmpl.RightDelim == nil {
tmpl.RightDelim = helper.StringToPtr("}}")
tmpl.RightDelim = stringToPtr("}}")
}
if tmpl.Envvars == nil {
tmpl.Envvars = helper.BoolToPtr(false)
tmpl.Envvars = boolToPtr(false)
}
if tmpl.VaultGrace == nil {
tmpl.VaultGrace = helper.TimeToPtr(15 * time.Second)
tmpl.VaultGrace = timeToPtr(15 * time.Second)
}
}
@ -794,13 +812,13 @@ type Vault struct {
func (v *Vault) Canonicalize() {
if v.Env == nil {
v.Env = helper.BoolToPtr(true)
v.Env = boolToPtr(true)
}
if v.ChangeMode == nil {
v.ChangeMode = helper.StringToPtr("restart")
v.ChangeMode = stringToPtr("restart")
}
if v.ChangeSignal == nil {
v.ChangeSignal = helper.StringToPtr("SIGHUP")
v.ChangeSignal = stringToPtr("SIGHUP")
}
}

View File

@ -1,11 +1,11 @@
package api
import (
"path/filepath"
"reflect"
"testing"
"time"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -15,8 +15,8 @@ func TestTaskGroup_NewTaskGroup(t *testing.T) {
t.Parallel()
grp := NewTaskGroup("grp1", 2)
expect := &TaskGroup{
Name: helper.StringToPtr("grp1"),
Count: helper.IntToPtr(2),
Name: stringToPtr("grp1"),
Count: intToPtr(2),
}
if !reflect.DeepEqual(grp, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, grp)
@ -143,7 +143,7 @@ func TestTaskGroup_AddSpread(t *testing.T) {
expect := []*Spread{
{
Attribute: "${meta.rack}",
Weight: helper.IntToPtr(100),
Weight: intToPtr(100),
SpreadTarget: []*SpreadTarget{
{
Value: "r1",
@ -153,7 +153,7 @@ func TestTaskGroup_AddSpread(t *testing.T) {
},
{
Attribute: "${node.datacenter}",
Weight: helper.IntToPtr(100),
Weight: intToPtr(100),
SpreadTarget: []*SpreadTarget{
{
Value: "dc1",
@ -263,13 +263,13 @@ func TestTask_Require(t *testing.T) {
// Create some require resources
resources := &Resources{
CPU: helper.IntToPtr(1250),
MemoryMB: helper.IntToPtr(128),
DiskMB: helper.IntToPtr(2048),
CPU: intToPtr(1250),
MemoryMB: intToPtr(128),
DiskMB: intToPtr(2048),
Networks: []*NetworkResource{
{
CIDR: "0.0.0.0/0",
MBits: helper.IntToPtr(100),
MBits: intToPtr(100),
ReservedPorts: []Port{{"", 80}, {"", 443}},
},
},
@ -357,14 +357,14 @@ func TestTask_AddAffinity(t *testing.T) {
func TestTask_Artifact(t *testing.T) {
t.Parallel()
a := TaskArtifact{
GetterSource: helper.StringToPtr("http://localhost/foo.txt"),
GetterMode: helper.StringToPtr("file"),
GetterSource: stringToPtr("http://localhost/foo.txt"),
GetterMode: stringToPtr("file"),
}
a.Canonicalize()
if *a.GetterMode != "file" {
t.Errorf("expected file but found %q", *a.GetterMode)
}
if *a.RelativeDest != "local/foo.txt" {
if filepath.ToSlash(*a.RelativeDest) != "local/foo.txt" {
t.Errorf("expected local/foo.txt but found %q", *a.RelativeDest)
}
}
@ -372,21 +372,21 @@ func TestTask_Artifact(t *testing.T) {
// Ensures no regression on https://github.com/hashicorp/nomad/issues/3132
func TestTaskGroup_Canonicalize_Update(t *testing.T) {
job := &Job{
ID: helper.StringToPtr("test"),
ID: stringToPtr("test"),
Update: &UpdateStrategy{
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(0),
HealthCheck: helper.StringToPtr(""),
HealthyDeadline: helper.TimeToPtr(0),
ProgressDeadline: helper.TimeToPtr(0),
MaxParallel: helper.IntToPtr(0),
MinHealthyTime: helper.TimeToPtr(0),
Stagger: helper.TimeToPtr(0),
AutoRevert: boolToPtr(false),
Canary: intToPtr(0),
HealthCheck: stringToPtr(""),
HealthyDeadline: timeToPtr(0),
ProgressDeadline: timeToPtr(0),
MaxParallel: intToPtr(0),
MinHealthyTime: timeToPtr(0),
Stagger: timeToPtr(0),
},
}
job.Canonicalize()
tg := &TaskGroup{
Name: helper.StringToPtr("foo"),
Name: stringToPtr("foo"),
}
tg.Canonicalize(job)
assert.Nil(t, tg.Update)
@ -407,130 +407,130 @@ func TestTaskGroup_Canonicalize_ReschedulePolicy(t *testing.T) {
jobReschedulePolicy: nil,
taskReschedulePolicy: nil,
expected: &ReschedulePolicy{
Attempts: helper.IntToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts),
Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
Delay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
DelayFunction: helper.StringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
MaxDelay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
Unlimited: helper.BoolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
Attempts: intToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts),
Interval: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
Delay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
DelayFunction: stringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
MaxDelay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
Unlimited: boolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
},
},
{
desc: "Empty job reschedule policy",
jobReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
Delay: helper.TimeToPtr(0),
MaxDelay: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr(""),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
Delay: timeToPtr(0),
MaxDelay: timeToPtr(0),
DelayFunction: stringToPtr(""),
Unlimited: boolToPtr(false),
},
taskReschedulePolicy: nil,
expected: &ReschedulePolicy{
Attempts: helper.IntToPtr(0),
Interval: helper.TimeToPtr(0),
Delay: helper.TimeToPtr(0),
MaxDelay: helper.TimeToPtr(0),
DelayFunction: helper.StringToPtr(""),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(0),
Interval: timeToPtr(0),
Delay: timeToPtr(0),
MaxDelay: timeToPtr(0),
DelayFunction: stringToPtr(""),
Unlimited: boolToPtr(false),
},
},
{
desc: "Inherit from job",
jobReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(1),
Interval: helper.TimeToPtr(20 * time.Second),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(10 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(1),
Interval: timeToPtr(20 * time.Second),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(10 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
taskReschedulePolicy: nil,
expected: &ReschedulePolicy{
Attempts: helper.IntToPtr(1),
Interval: helper.TimeToPtr(20 * time.Second),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(10 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(1),
Interval: timeToPtr(20 * time.Second),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(10 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
},
{
desc: "Set in task",
jobReschedulePolicy: nil,
taskReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(5),
Interval: helper.TimeToPtr(2 * time.Minute),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(10 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(5),
Interval: timeToPtr(2 * time.Minute),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(10 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
expected: &ReschedulePolicy{
Attempts: helper.IntToPtr(5),
Interval: helper.TimeToPtr(2 * time.Minute),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(10 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(5),
Interval: timeToPtr(2 * time.Minute),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(10 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
},
{
desc: "Merge from job",
jobReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(1),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(10 * time.Minute),
Attempts: intToPtr(1),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(10 * time.Minute),
},
taskReschedulePolicy: &ReschedulePolicy{
Interval: helper.TimeToPtr(5 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Interval: timeToPtr(5 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
expected: &ReschedulePolicy{
Attempts: helper.IntToPtr(1),
Interval: helper.TimeToPtr(5 * time.Minute),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(10 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(1),
Interval: timeToPtr(5 * time.Minute),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(10 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
},
{
desc: "Override from group",
jobReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(1),
MaxDelay: helper.TimeToPtr(10 * time.Second),
Attempts: intToPtr(1),
MaxDelay: timeToPtr(10 * time.Second),
},
taskReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(5),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(20 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(5),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(20 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
expected: &ReschedulePolicy{
Attempts: helper.IntToPtr(5),
Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
Delay: helper.TimeToPtr(20 * time.Second),
MaxDelay: helper.TimeToPtr(20 * time.Minute),
DelayFunction: helper.StringToPtr("constant"),
Unlimited: helper.BoolToPtr(false),
Attempts: intToPtr(5),
Interval: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
Delay: timeToPtr(20 * time.Second),
MaxDelay: timeToPtr(20 * time.Minute),
DelayFunction: stringToPtr("constant"),
Unlimited: boolToPtr(false),
},
},
{
desc: "Attempts from job, default interval",
jobReschedulePolicy: &ReschedulePolicy{
Attempts: helper.IntToPtr(1),
Attempts: intToPtr(1),
},
taskReschedulePolicy: nil,
expected: &ReschedulePolicy{
Attempts: helper.IntToPtr(1),
Interval: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
Delay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
DelayFunction: helper.StringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
MaxDelay: helper.TimeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
Unlimited: helper.BoolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
Attempts: intToPtr(1),
Interval: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
Delay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
DelayFunction: stringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
MaxDelay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
Unlimited: boolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
},
},
}
@ -538,13 +538,13 @@ func TestTaskGroup_Canonicalize_ReschedulePolicy(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
job := &Job{
ID: helper.StringToPtr("test"),
ID: stringToPtr("test"),
Reschedule: tc.jobReschedulePolicy,
Type: helper.StringToPtr(JobTypeBatch),
Type: stringToPtr(JobTypeBatch),
}
job.Canonicalize()
tg := &TaskGroup{
Name: helper.StringToPtr("foo"),
Name: stringToPtr("foo"),
ReschedulePolicy: tc.taskReschedulePolicy,
}
tg.Canonicalize(job)
@ -577,44 +577,44 @@ func TestTaskGroup_Canonicalize_MigrateStrategy(t *testing.T) {
jobMigrate: nil,
taskMigrate: nil,
expected: &MigrateStrategy{
MaxParallel: helper.IntToPtr(1),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
MaxParallel: intToPtr(1),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(5 * time.Minute),
},
},
{
desc: "Empty job migrate strategy",
jobType: "service",
jobMigrate: &MigrateStrategy{
MaxParallel: helper.IntToPtr(0),
HealthCheck: helper.StringToPtr(""),
MinHealthyTime: helper.TimeToPtr(0),
HealthyDeadline: helper.TimeToPtr(0),
MaxParallel: intToPtr(0),
HealthCheck: stringToPtr(""),
MinHealthyTime: timeToPtr(0),
HealthyDeadline: timeToPtr(0),
},
taskMigrate: nil,
expected: &MigrateStrategy{
MaxParallel: helper.IntToPtr(0),
HealthCheck: helper.StringToPtr(""),
MinHealthyTime: helper.TimeToPtr(0),
HealthyDeadline: helper.TimeToPtr(0),
MaxParallel: intToPtr(0),
HealthCheck: stringToPtr(""),
MinHealthyTime: timeToPtr(0),
HealthyDeadline: timeToPtr(0),
},
},
{
desc: "Inherit from job",
jobType: "service",
jobMigrate: &MigrateStrategy{
MaxParallel: helper.IntToPtr(3),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
MaxParallel: intToPtr(3),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
taskMigrate: nil,
expected: &MigrateStrategy{
MaxParallel: helper.IntToPtr(3),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
MaxParallel: intToPtr(3),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
},
{
@ -622,67 +622,67 @@ func TestTaskGroup_Canonicalize_MigrateStrategy(t *testing.T) {
jobType: "service",
jobMigrate: nil,
taskMigrate: &MigrateStrategy{
MaxParallel: helper.IntToPtr(3),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
MaxParallel: intToPtr(3),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
expected: &MigrateStrategy{
MaxParallel: helper.IntToPtr(3),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
MaxParallel: intToPtr(3),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
},
{
desc: "Merge from job",
jobType: "service",
jobMigrate: &MigrateStrategy{
MaxParallel: helper.IntToPtr(11),
MaxParallel: intToPtr(11),
},
taskMigrate: &MigrateStrategy{
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
expected: &MigrateStrategy{
MaxParallel: helper.IntToPtr(11),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
MaxParallel: intToPtr(11),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
},
{
desc: "Override from group",
jobType: "service",
jobMigrate: &MigrateStrategy{
MaxParallel: helper.IntToPtr(11),
MaxParallel: intToPtr(11),
},
taskMigrate: &MigrateStrategy{
MaxParallel: helper.IntToPtr(5),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
MaxParallel: intToPtr(5),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
expected: &MigrateStrategy{
MaxParallel: helper.IntToPtr(5),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(2),
HealthyDeadline: helper.TimeToPtr(2),
MaxParallel: intToPtr(5),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(2),
HealthyDeadline: timeToPtr(2),
},
},
{
desc: "Parallel from job, defaulting",
jobType: "service",
jobMigrate: &MigrateStrategy{
MaxParallel: helper.IntToPtr(5),
MaxParallel: intToPtr(5),
},
taskMigrate: nil,
expected: &MigrateStrategy{
MaxParallel: helper.IntToPtr(5),
HealthCheck: helper.StringToPtr("checks"),
MinHealthyTime: helper.TimeToPtr(10 * time.Second),
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
MaxParallel: intToPtr(5),
HealthCheck: stringToPtr("checks"),
MinHealthyTime: timeToPtr(10 * time.Second),
HealthyDeadline: timeToPtr(5 * time.Minute),
},
},
}
@ -690,13 +690,13 @@ func TestTaskGroup_Canonicalize_MigrateStrategy(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
job := &Job{
ID: helper.StringToPtr("test"),
ID: stringToPtr("test"),
Migrate: tc.jobMigrate,
Type: helper.StringToPtr(tc.jobType),
Type: stringToPtr(tc.jobType),
}
job.Canonicalize()
tg := &TaskGroup{
Name: helper.StringToPtr("foo"),
Name: stringToPtr("foo"),
Migrate: tc.taskMigrate,
}
tg.Canonicalize(job)
@ -708,13 +708,13 @@ func TestTaskGroup_Canonicalize_MigrateStrategy(t *testing.T) {
// TestService_CheckRestart asserts Service.CheckRestart settings are properly
// inherited by Checks.
func TestService_CheckRestart(t *testing.T) {
job := &Job{Name: helper.StringToPtr("job")}
tg := &TaskGroup{Name: helper.StringToPtr("group")}
job := &Job{Name: stringToPtr("job")}
tg := &TaskGroup{Name: stringToPtr("group")}
task := &Task{Name: "task"}
service := &Service{
CheckRestart: &CheckRestart{
Limit: 11,
Grace: helper.TimeToPtr(11 * time.Second),
Grace: timeToPtr(11 * time.Second),
IgnoreWarnings: true,
},
Checks: []ServiceCheck{
@ -722,7 +722,7 @@ func TestService_CheckRestart(t *testing.T) {
Name: "all-set",
CheckRestart: &CheckRestart{
Limit: 22,
Grace: helper.TimeToPtr(22 * time.Second),
Grace: timeToPtr(22 * time.Second),
IgnoreWarnings: true,
},
},
@ -730,7 +730,7 @@ func TestService_CheckRestart(t *testing.T) {
Name: "some-set",
CheckRestart: &CheckRestart{
Limit: 33,
Grace: helper.TimeToPtr(33 * time.Second),
Grace: timeToPtr(33 * time.Second),
},
},
{
@ -756,12 +756,12 @@ func TestService_CheckRestart(t *testing.T) {
// TestSpread_Canonicalize asserts that the spread stanza is canonicalized correctly
func TestSpread_Canonicalize(t *testing.T) {
job := &Job{
ID: helper.StringToPtr("test"),
Type: helper.StringToPtr("batch"),
ID: stringToPtr("test"),
Type: stringToPtr("batch"),
}
job.Canonicalize()
tg := &TaskGroup{
Name: helper.StringToPtr("foo"),
Name: stringToPtr("foo"),
}
type testCase struct {
desc string
@ -781,7 +781,7 @@ func TestSpread_Canonicalize(t *testing.T) {
"Zero spread",
&Spread{
Attribute: "test",
Weight: helper.IntToPtr(0),
Weight: intToPtr(0),
},
0,
},
@ -789,7 +789,7 @@ func TestSpread_Canonicalize(t *testing.T) {
"Non Zero spread",
&Spread{
Attribute: "test",
Weight: helper.IntToPtr(100),
Weight: intToPtr(100),
},
100,
},

View File

@ -2,8 +2,6 @@ package api
import (
"testing"
"github.com/hashicorp/nomad/helper"
)
func assertQueryMeta(t *testing.T, qm *QueryMeta) {
@ -27,18 +25,18 @@ func testJob() *Job {
task := NewTask("task1", "exec").
SetConfig("command", "/bin/sleep").
Require(&Resources{
CPU: helper.IntToPtr(100),
MemoryMB: helper.IntToPtr(256),
CPU: intToPtr(100),
MemoryMB: intToPtr(256),
}).
SetLogConfig(&LogConfig{
MaxFiles: helper.IntToPtr(1),
MaxFileSizeMB: helper.IntToPtr(2),
MaxFiles: intToPtr(1),
MaxFileSizeMB: intToPtr(2),
})
group := NewTaskGroup("group1", 1).
AddTask(task).
RequireDisk(&EphemeralDisk{
SizeMB: helper.IntToPtr(25),
SizeMB: intToPtr(25),
})
job := NewBatchJob("job1", "redis", "region1", 1).
@ -50,9 +48,9 @@ func testJob() *Job {
func testPeriodicJob() *Job {
job := testJob().AddPeriodicConfig(&PeriodicConfig{
Enabled: helper.BoolToPtr(true),
Spec: helper.StringToPtr("*/30 * * * *"),
SpecType: helper.StringToPtr("cron"),
Enabled: boolToPtr(true),
Spec: stringToPtr("*/30 * * * *"),
SpecType: stringToPtr("cron"),
})
return job
}
@ -72,10 +70,23 @@ func testQuotaSpec() *QuotaSpec {
{
Region: "global",
RegionLimit: &Resources{
CPU: helper.IntToPtr(2000),
MemoryMB: helper.IntToPtr(2000),
CPU: intToPtr(2000),
MemoryMB: intToPtr(2000),
},
},
},
}
}
// conversions utils only used for testing
// added here to avoid linter warning
// int64ToPtr returns the pointer to an int
func int64ToPtr(i int64) *int64 {
return &i
}
// float64ToPtr returns the pointer to an float64
func float64ToPtr(f float64) *float64 {
return &f
}

53
api/utils.go Normal file
View File

@ -0,0 +1,53 @@
package api
import (
"strconv"
"strings"
"time"
)
// boolToPtr returns the pointer to a boolean
func boolToPtr(b bool) *bool {
return &b
}
// intToPtr returns the pointer to an int
func intToPtr(i int) *int {
return &i
}
// uint64ToPtr returns the pointer to an uint64
func uint64ToPtr(u uint64) *uint64 {
return &u
}
// stringToPtr returns the pointer to a string
func stringToPtr(str string) *string {
return &str
}
// timeToPtr returns the pointer to a time stamp
func timeToPtr(t time.Duration) *time.Duration {
return &t
}
// formatFloat converts the floating-point number f to a string,
// after rounding it to the passed unit.
//
// Uses 'f' format (-ddd.dddddd, no exponent), and uses at most
// maxPrec digits after the decimal point.
func formatFloat(f float64, maxPrec int) string {
v := strconv.FormatFloat(f, 'f', -1, 64)
idx := strings.LastIndex(v, ".")
if idx == -1 {
return v
}
sublen := idx + maxPrec + 1
if sublen > len(v) {
sublen = len(v)
}
return v[:sublen]
}

39
api/utils_test.go Normal file
View File

@ -0,0 +1,39 @@
package api
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFormatRoundedFloat(t *testing.T) {
cases := []struct {
input float64
expected string
}{
{
1323,
"1323",
},
{
10.321,
"10.321",
},
{
100000.31324324,
"100000.313",
},
{
100000.3,
"100000.3",
},
{
0.7654321,
"0.765",
},
}
for _, c := range cases {
require.Equal(t, c.expected, formatFloat(c.input, 3))
}
}

View File

@ -1,15 +1,12 @@
version: "build-{branch}-{build}"
image: Visual Studio 2017
clone_folder: c:\gopath\src\github.com\hashicorp\nomad
environment:
GOPATH: c:\gopath
GOBIN: c:\gopath\bin
matrix:
- RUN_UI_TESTS: 1
SKIP_NOMAD_TESTS: 1
- {}
GOMAXPROCS: 1
install:
- cmd: set PATH=%GOBIN%;c:\go\bin;%PATH%
@ -17,11 +14,29 @@ install:
- cmd: go version
- cmd: go env
- ps: mkdir C:\gopath\bin
- ps: appveyor DownloadFile "https://releases.hashicorp.com/vault/0.7.0/vault_0.7.0_windows_amd64.zip" -FileName "C:\\gopath\\bin\\vault.zip"
- ps: appveyor DownloadFile "https://releases.hashicorp.com/vault/0.10.2/vault_0.10.2_windows_amd64.zip" -FileName "C:\\gopath\\bin\\vault.zip"
- ps: Expand-Archive C:\gopath\bin\vault.zip -DestinationPath C:\gopath\bin
- ps: appveyor DownloadFile "https://releases.hashicorp.com/consul/0.7.0/consul_0.7.0_windows_amd64.zip" -FileName "C:\\gopath\\bin\\consul.zip"
- ps: Expand-Archive C:\gopath\bin\consul.zip -DestinationPath C:\gopath\bin
#- cmd: go install
# - ps: appveyor DownloadFile "https://releases.hashicorp.com/consul/1.0.0/consul_1.0.0_windows_amd64.zip" -FileName "C:\\gopath\\bin\\consul.zip"
# - ps: Expand-Archive C:\gopath\bin\consul.zip -DestinationPath C:\gopath\bin
- ps: choco install make
- ps: |
go get -u github.com/kardianos/govendor
go get -u github.com/ugorji/go/codec/codecgen
go get -u github.com/hashicorp/go-bindata/go-bindata
go get -u github.com/elazarl/go-bindata-assetfs/go-bindata-assetfs
go get -u github.com/a8m/tree/cmd/tree
go get -u github.com/magiconair/vendorfmt/cmd/vendorfmt
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u gotest.tools/gotestsum
build_script:
#- cmd: go test ./...
- cmd: go install
- cmd: |
set PATH=%GOPATH%/bin;%PATH%
mkdir -p $GOPATH\bin
go build -o $GOPATH\bin\nomad
# test_script:
# - cmd: gotestsum -f short-verbose --junitfile results.xml
# on_finish:
# - ps: |
# Push-AppveyorArtifact (Resolve-Path .\results.xml)
# $wc = New-Object 'System.Net.WebClient'
# $wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\results.xml))

View File

@ -249,7 +249,7 @@ func (ar *allocRunner) destroy() error {
}
}
return nil
return merr.ErrorOrNil()
}
// shutdownHooks calls graceful shutdown hooks for when the agent is exiting.

View File

@ -114,14 +114,14 @@ type TaskPoststartHook interface {
Poststart(context.Context, *TaskPoststartRequest, *TaskPoststartResponse) error
}
type TaskKillRequest struct{}
type TaskKillResponse struct{}
type TaskPreKillRequest struct{}
type TaskPreKillResponse struct{}
type TaskKillHook interface {
type TaskPreKillHook interface {
TaskHook
// Killing is called when a task is going to be Killed or Restarted.
Killing(context.Context, *TaskKillRequest, *TaskKillResponse) error
// PreKilling is called right before a task is going to be killed or restarted.
PreKilling(context.Context, *TaskPreKillRequest, *TaskPreKillResponse) error
}
type TaskExitedRequest struct{}

View File

@ -22,8 +22,8 @@ func (tr *TaskRunner) Restart(ctx context.Context, event *structs.TaskEvent, fai
// Emit the event since it may take a long time to kill
tr.EmitEvent(event)
// Run the hooks prior to restarting the task
tr.killing()
// Run the pre-kill hooks prior to restarting the task
tr.preKill()
// Tell the restart tracker that a restart triggered the exit
tr.restartTracker.SetRestartTriggered(failure)

View File

@ -2,6 +2,7 @@ package taskrunner
import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"runtime"
@ -11,6 +12,7 @@ import (
"github.com/hashicorp/nomad/client/allocrunner/interfaces"
"github.com/hashicorp/nomad/client/logmon"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/plugins/shared"
)
// logmonHook launches logmon and manages task logging
@ -58,8 +60,8 @@ func (*logmonHook) Name() string {
return "logmon"
}
func (h *logmonHook) launchLogMon() error {
l, c, err := logmon.LaunchLogMon(h.logger)
func (h *logmonHook) launchLogMon(reattachConfig *plugin.ReattachConfig) error {
l, c, err := logmon.LaunchLogMon(h.logger, reattachConfig)
if err != nil {
return err
}
@ -69,28 +71,58 @@ func (h *logmonHook) launchLogMon() error {
return nil
}
func reattachConfigFromHookData(data map[string]string) (*plugin.ReattachConfig, error) {
if data == nil || data["reattach_config"] == "" {
return nil, nil
}
var cfg *shared.ReattachConfig
err := json.Unmarshal([]byte(data["reattach_config"]), cfg)
if err != nil {
return nil, err
}
return shared.ReattachConfigToGoPlugin(cfg)
}
func (h *logmonHook) Prestart(ctx context.Context,
req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error {
// Launch logmon instance for the task.
if err := h.launchLogMon(); err != nil {
reattachConfig, err := reattachConfigFromHookData(req.HookData)
if err != nil {
h.logger.Error("failed to load reattach config", "error", err)
return err
}
// Launch or reattach logmon instance for the task.
if err := h.launchLogMon(reattachConfig); err != nil {
h.logger.Error("failed to launch logmon process", "error", err)
return err
}
err := h.logmon.Start(&logmon.LogConfig{
LogDir: h.config.logDir,
StdoutLogFile: fmt.Sprintf("%s.stdout", req.Task.Name),
StderrLogFile: fmt.Sprintf("%s.stderr", req.Task.Name),
StdoutFifo: h.config.stdoutFifo,
StderrFifo: h.config.stderrFifo,
MaxFiles: req.Task.LogConfig.MaxFiles,
MaxFileSizeMB: req.Task.LogConfig.MaxFileSizeMB,
})
// Only tell logmon to start when we are not reattaching to a running instance
if reattachConfig == nil {
err := h.logmon.Start(&logmon.LogConfig{
LogDir: h.config.logDir,
StdoutLogFile: fmt.Sprintf("%s.stdout", req.Task.Name),
StderrLogFile: fmt.Sprintf("%s.stderr", req.Task.Name),
StdoutFifo: h.config.stdoutFifo,
StderrFifo: h.config.stderrFifo,
MaxFiles: req.Task.LogConfig.MaxFiles,
MaxFileSizeMB: req.Task.LogConfig.MaxFileSizeMB,
})
if err != nil {
h.logger.Error("failed to start logmon", "error", err)
return err
}
}
rCfg := shared.ReattachConfigFromGoPlugin(h.logmonPluginClient.ReattachConfig())
jsonCfg, err := json.Marshal(rCfg)
if err != nil {
h.logger.Error("failed to start logmon", "error", err)
return err
}
req.HookData = map[string]string{"reattach_config": string(jsonCfg)}
resp.Done = true
return nil

View File

@ -140,7 +140,7 @@ func (h *serviceHook) Update(ctx context.Context, req *interfaces.TaskUpdateRequ
return h.consul.UpdateTask(oldTaskServices, newTaskServices)
}
func (h *serviceHook) Killing(ctx context.Context, req *interfaces.TaskKillRequest, resp *interfaces.TaskKillResponse) error {
func (h *serviceHook) Killing(ctx context.Context, req *interfaces.TaskPreKillRequest, resp *interfaces.TaskPreKillResponse) error {
h.mu.Lock()
defer h.mu.Unlock()
@ -200,7 +200,7 @@ func (h *serviceHook) getTaskServices() *agentconsul.TaskServices {
// values from the task's environment.
func interpolateServices(taskEnv *taskenv.TaskEnv, services []*structs.Service) []*structs.Service {
// Guard against not having a valid taskEnv. This can be the case if the
// Killing or Exited hook is run before post-run.
// PreKilling or Exited hook is run before Poststart.
if taskEnv == nil || len(services) == 0 {
return nil
}

View File

@ -35,6 +35,10 @@ import (
)
const (
// defaultMaxEvents is the default max capacity for task events on the
// task state. Overrideable for testing.
defaultMaxEvents = 10
// killBackoffBaseline is the baseline time for exponential backoff while
// killing a task.
killBackoffBaseline = 5 * time.Second
@ -191,6 +195,10 @@ type TaskRunner struct {
// be accessed via helpers
runLaunched bool
runLaunchedLock sync.Mutex
// maxEvents is the capacity of the TaskEvents on the TaskState.
// Defaults to defaultMaxEvents but overrideable for testing.
maxEvents int
}
type Config struct {
@ -267,6 +275,7 @@ func NewTaskRunner(config *Config) (*TaskRunner, error) {
waitCh: make(chan struct{}),
devicemanager: config.DeviceManager,
driverManager: config.DriverManager,
maxEvents: defaultMaxEvents,
}
// Create the logger based on the allocation ID
@ -440,7 +449,7 @@ MAIN:
case <-tr.killCtx.Done():
// We can go through the normal should restart check since
// the restart tracker knowns it is killed
tr.handleKill()
result = tr.handleKill()
case <-tr.shutdownCtx.Done():
// TaskRunner was told to exit immediately
return
@ -703,11 +712,12 @@ func (tr *TaskRunner) initDriver() error {
return nil
}
// handleKill is used to handle the a request to kill a task. It will store any
// error in the task runner killErr value.
func (tr *TaskRunner) handleKill() {
// Run the hooks prior to killing the task
tr.killing()
// handleKill is used to handle the a request to kill a task. It will return
//// the handle exit result if one is available and store any error in the task
//// runner killErr value.
func (tr *TaskRunner) handleKill() *drivers.ExitResult {
// Run the pre killing hooks
tr.preKill()
// Tell the restart tracker that the task has been killed so it doesn't
// attempt to restart it.
@ -716,7 +726,7 @@ func (tr *TaskRunner) handleKill() {
// Check it is running
handle := tr.getDriverHandle()
if handle == nil {
return
return nil
}
// Kill the task using an exponential backoff in-case of failures.
@ -734,16 +744,18 @@ func (tr *TaskRunner) handleKill() {
// failure in the driver or transport layer occurred
if err != nil {
if err == drivers.ErrTaskNotFound {
return
return nil
}
tr.logger.Error("failed to wait on task. Resources may have been leaked", "error", err)
tr.setKillErr(killErr)
return
return nil
}
select {
case <-waitCh:
case result := <-waitCh:
return result
case <-tr.shutdownCtx.Done():
return nil
}
}
@ -794,9 +806,10 @@ func (tr *TaskRunner) buildTaskConfig() *drivers.TaskConfig {
env := tr.envBuilder.Build()
return &drivers.TaskConfig{
ID: fmt.Sprintf("%s/%s/%s", alloc.ID, task.Name, invocationid),
Name: task.Name,
JobName: alloc.Job.Name,
ID: fmt.Sprintf("%s/%s/%s", alloc.ID, task.Name, invocationid),
Name: task.Name,
JobName: alloc.Job.Name,
TaskGroupName: alloc.TaskGroup,
Resources: &drivers.Resources{
NomadResources: taskResources,
LinuxResources: &drivers.LinuxResources{
@ -1019,7 +1032,7 @@ func (tr *TaskRunner) appendEvent(event *structs.TaskEvent) error {
}
// Append event to slice
appendTaskEvent(tr.state, event)
appendTaskEvent(tr.state, event, tr.maxEvents)
return nil
}
@ -1114,14 +1127,14 @@ func (tr *TaskRunner) UpdateStats(ru *cstructs.TaskResourceUsage) {
//TODO Remove Backwardscompat or use tr.Alloc()?
func (tr *TaskRunner) setGaugeForMemory(ru *cstructs.TaskResourceUsage) {
if !tr.clientConfig.DisableTaggedMetrics {
metrics.SetGaugeWithLabels([]string{"client", "allocs", "memory", "rss"},
float32(ru.ResourceUsage.MemoryStats.RSS), tr.baseLabels)
metrics.SetGaugeWithLabels([]string{"client", "allocs", "memory", "rss"},
float32(ru.ResourceUsage.MemoryStats.RSS), tr.baseLabels)
metrics.SetGaugeWithLabels([]string{"client", "allocs", "memory", "cache"},
float32(ru.ResourceUsage.MemoryStats.Cache), tr.baseLabels)
metrics.SetGaugeWithLabels([]string{"client", "allocs", "memory", "swap"},
float32(ru.ResourceUsage.MemoryStats.Swap), tr.baseLabels)
metrics.SetGaugeWithLabels([]string{"client", "allocs", "memory", "usage"},
float32(ru.ResourceUsage.MemoryStats.Usage), tr.baseLabels)
metrics.SetGaugeWithLabels([]string{"client", "allocs", "memory", "max_usage"},
float32(ru.ResourceUsage.MemoryStats.MaxUsage), tr.baseLabels)
metrics.SetGaugeWithLabels([]string{"client", "allocs", "memory", "kernel_usage"},
@ -1134,6 +1147,7 @@ func (tr *TaskRunner) setGaugeForMemory(ru *cstructs.TaskResourceUsage) {
metrics.SetGauge([]string{"client", "allocs", tr.alloc.Job.Name, tr.alloc.TaskGroup, tr.allocID, tr.taskName, "memory", "rss"}, float32(ru.ResourceUsage.MemoryStats.RSS))
metrics.SetGauge([]string{"client", "allocs", tr.alloc.Job.Name, tr.alloc.TaskGroup, tr.allocID, tr.taskName, "memory", "cache"}, float32(ru.ResourceUsage.MemoryStats.Cache))
metrics.SetGauge([]string{"client", "allocs", tr.alloc.Job.Name, tr.alloc.TaskGroup, tr.allocID, tr.taskName, "memory", "swap"}, float32(ru.ResourceUsage.MemoryStats.Swap))
metrics.SetGauge([]string{"client", "allocs", tr.alloc.Job.Name, tr.alloc.TaskGroup, tr.allocID, tr.taskName, "memory", "usage"}, float32(ru.ResourceUsage.MemoryStats.Usage))
metrics.SetGauge([]string{"client", "allocs", tr.alloc.Job.Name, tr.alloc.TaskGroup, tr.allocID, tr.taskName, "memory", "max_usage"}, float32(ru.ResourceUsage.MemoryStats.MaxUsage))
metrics.SetGauge([]string{"client", "allocs", tr.alloc.Job.Name, tr.alloc.TaskGroup, tr.allocID, tr.taskName, "memory", "kernel_usage"}, float32(ru.ResourceUsage.MemoryStats.KernelUsage))
metrics.SetGauge([]string{"client", "allocs", tr.alloc.Job.Name, tr.alloc.TaskGroup, tr.allocID, tr.taskName, "memory", "kernel_max_usage"}, float32(ru.ResourceUsage.MemoryStats.KernelMaxUsage))
@ -1184,8 +1198,7 @@ func (tr *TaskRunner) emitStats(ru *cstructs.TaskResourceUsage) {
}
// appendTaskEvent updates the task status by appending the new event.
func appendTaskEvent(state *structs.TaskState, event *structs.TaskEvent) {
const capacity = 10
func appendTaskEvent(state *structs.TaskState, event *structs.TaskEvent, capacity int) {
if state.Events == nil {
state.Events = make([]*structs.TaskEvent, 1, capacity)
state.Events[0] = event

View File

@ -437,36 +437,38 @@ func (tr *TaskRunner) updateHooks() {
}
}
// killing is used to run the runners kill hooks.
func (tr *TaskRunner) killing() {
// preKill is used to run the runners preKill hooks
// preKill hooks contain logic that must be executed before
// a task is killed or restarted
func (tr *TaskRunner) preKill() {
if tr.logger.IsTrace() {
start := time.Now()
tr.logger.Trace("running kill hooks", "start", start)
tr.logger.Trace("running pre kill hooks", "start", start)
defer func() {
end := time.Now()
tr.logger.Trace("finished kill hooks", "end", end, "duration", end.Sub(start))
tr.logger.Trace("finished pre kill hooks", "end", end, "duration", end.Sub(start))
}()
}
for _, hook := range tr.runnerHooks {
killHook, ok := hook.(interfaces.TaskKillHook)
killHook, ok := hook.(interfaces.TaskPreKillHook)
if !ok {
continue
}
name := killHook.Name()
// Time the update hook
// Time the pre kill hook
var start time.Time
if tr.logger.IsTrace() {
start = time.Now()
tr.logger.Trace("running kill hook", "name", name, "start", start)
}
// Run the kill hook
req := interfaces.TaskKillRequest{}
var resp interfaces.TaskKillResponse
if err := killHook.Killing(context.Background(), &req, &resp); err != nil {
// Run the pre kill hook
req := interfaces.TaskPreKillRequest{}
var resp interfaces.TaskPreKillResponse
if err := killHook.PreKilling(context.Background(), &req, &resp); err != nil {
tr.emitHookError(err, name)
tr.logger.Error("kill hook failed", "name", name, "error", err)
}

View File

@ -3,10 +3,12 @@ package taskrunner
import (
"context"
"fmt"
"io/ioutil"
"path/filepath"
"testing"
"time"
"github.com/golang/snappy"
"github.com/hashicorp/nomad/client/allocdir"
"github.com/hashicorp/nomad/client/allocrunner/interfaces"
"github.com/hashicorp/nomad/client/config"
@ -16,6 +18,7 @@ import (
"github.com/hashicorp/nomad/client/pluginmanager/drivermanager"
cstate "github.com/hashicorp/nomad/client/state"
"github.com/hashicorp/nomad/client/vaultclient"
agentconsul "github.com/hashicorp/nomad/command/agent/consul"
mockdriver "github.com/hashicorp/nomad/drivers/mock"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/nomad/mock"
@ -124,12 +127,7 @@ func TestTaskRunner_Restore_Running(t *testing.T) {
defer origTR.Kill(context.Background(), structs.NewTaskEvent("cleanup"))
// Wait for it to be running
testutil.WaitForResult(func() (bool, error) {
ts := origTR.TaskState()
return ts.State == structs.TaskStateRunning, fmt.Errorf("%v", ts.State)
}, func(err error) {
t.Fatalf("expected running; got: %v", err)
})
testWaitForTaskToStart(t, origTR)
// Cause TR to exit without shutting down task
origTR.Shutdown()
@ -207,6 +205,48 @@ func TestTaskRunner_TaskEnv(t *testing.T) {
assert.Equal(t, "global bar somebody", mockCfg.StdoutString)
}
func TestTaskRunner_TaskConfig(t *testing.T) {
t.Parallel()
require := require.New(t)
alloc := mock.BatchAlloc()
task := alloc.Job.TaskGroups[0].Tasks[0]
task.Driver = "mock_driver"
//// Use interpolation from both node attributes and meta vars
//task.Config = map[string]interface{}{
// "run_for": "1ms",
//}
conf, cleanup := testTaskRunnerConfig(t, alloc, task.Name)
defer cleanup()
// Run the first TaskRunner
tr, err := NewTaskRunner(conf)
require.NoError(err)
go tr.Run()
defer tr.Kill(context.Background(), structs.NewTaskEvent("cleanup"))
// Wait for task to complete
select {
case <-tr.WaitCh():
case <-time.After(3 * time.Second):
}
// Get the mock driver plugin
driverPlugin, err := conf.DriverManager.Dispense(mockdriver.PluginID.Name)
require.NoError(err)
mockDriver := driverPlugin.(*mockdriver.Driver)
// Assert its config has been properly interpolated
driverCfg, mockCfg := mockDriver.GetTaskConfig()
require.NotNil(driverCfg)
require.NotNil(mockCfg)
assert.Equal(t, alloc.Job.Name, driverCfg.JobName)
assert.Equal(t, alloc.TaskGroup, driverCfg.TaskGroupName)
assert.Equal(t, alloc.Job.TaskGroups[0].Tasks[0].Name, driverCfg.Name)
}
// Test that devices get sent to the driver
func TestTaskRunner_DevicePropogation(t *testing.T) {
t.Parallel()
@ -502,16 +542,237 @@ WAIT:
}
}
// testWaitForTaskToStart waits for the task to or fails the test
func testWaitForTaskToStart(t *testing.T, tr *TaskRunner) {
// Wait for the task to start
testutil.WaitForResult(func() (bool, error) {
tr.stateLock.RLock()
started := !tr.state.StartedAt.IsZero()
tr.stateLock.RUnlock()
// TestTaskRunner_Dispatch_Payload asserts that a dispatch job runs and the
// payload was written to disk.
func TestTaskRunner_Dispatch_Payload(t *testing.T) {
t.Parallel()
return started, nil
alloc := mock.BatchAlloc()
task := alloc.Job.TaskGroups[0].Tasks[0]
task.Driver = "mock_driver"
task.Config = map[string]interface{}{
"run_for": "1s",
}
fileName := "test"
task.DispatchPayload = &structs.DispatchPayloadConfig{
File: fileName,
}
alloc.Job.ParameterizedJob = &structs.ParameterizedJobConfig{}
// Add a payload (they're snappy encoded bytes)
expected := []byte("hello world")
compressed := snappy.Encode(nil, expected)
alloc.Job.Payload = compressed
conf, cleanup := testTaskRunnerConfig(t, alloc, task.Name)
defer cleanup()
tr, err := NewTaskRunner(conf)
require.NoError(t, err)
go tr.Run()
defer tr.Kill(context.Background(), structs.NewTaskEvent("cleanup"))
// Wait for it to finish
testutil.WaitForResult(func() (bool, error) {
ts := tr.TaskState()
return ts.State == structs.TaskStateDead, fmt.Errorf("%v", ts.State)
}, func(err error) {
t.Fatalf("not started")
require.NoError(t, err)
})
// Should have exited successfully
ts := tr.TaskState()
require.False(t, ts.Failed)
require.Zero(t, ts.Restarts)
// Check that the file was written to disk properly
payloadPath := filepath.Join(tr.taskDir.LocalDir, fileName)
data, err := ioutil.ReadFile(payloadPath)
require.NoError(t, err)
require.Equal(t, expected, data)
}
// TestTaskRunner_SignalFailure asserts that signal errors are properly
// propagated from the driver to TaskRunner.
func TestTaskRunner_SignalFailure(t *testing.T) {
t.Parallel()
alloc := mock.Alloc()
task := alloc.Job.TaskGroups[0].Tasks[0]
task.Driver = "mock_driver"
errMsg := "test forcing failure"
task.Config = map[string]interface{}{
"run_for": "10m",
"signal_error": errMsg,
}
conf, cleanup := testTaskRunnerConfig(t, alloc, task.Name)
defer cleanup()
tr, err := NewTaskRunner(conf)
require.NoError(t, err)
go tr.Run()
defer tr.Kill(context.Background(), structs.NewTaskEvent("cleanup"))
testWaitForTaskToStart(t, tr)
require.EqualError(t, tr.Signal(&structs.TaskEvent{}, "SIGINT"), errMsg)
}
// TestTaskRunner_RestartTask asserts that restarting a task works and emits a
// Restarting event.
func TestTaskRunner_RestartTask(t *testing.T) {
t.Parallel()
alloc := mock.Alloc()
task := alloc.Job.TaskGroups[0].Tasks[0]
task.Driver = "mock_driver"
task.Config = map[string]interface{}{
"run_for": "10m",
}
conf, cleanup := testTaskRunnerConfig(t, alloc, task.Name)
defer cleanup()
tr, err := NewTaskRunner(conf)
require.NoError(t, err)
go tr.Run()
defer tr.Kill(context.Background(), structs.NewTaskEvent("cleanup"))
testWaitForTaskToStart(t, tr)
// Restart task. Send a RestartSignal event like check watcher. Restart
// handler emits the Restarting event.
event := structs.NewTaskEvent(structs.TaskRestartSignal).SetRestartReason("test")
const fail = false
tr.Restart(context.Background(), event.Copy(), fail)
// Wait for it to restart and be running again
testutil.WaitForResult(func() (bool, error) {
ts := tr.TaskState()
if ts.Restarts != 1 {
return false, fmt.Errorf("expected 1 restart but found %d\nevents: %s",
ts.Restarts, pretty.Sprint(ts.Events))
}
if ts.State != structs.TaskStateRunning {
return false, fmt.Errorf("expected running but received %s", ts.State)
}
return true, nil
}, func(err error) {
require.NoError(t, err)
})
// Assert the expected Restarting event was emitted
found := false
events := tr.TaskState().Events
for _, e := range events {
if e.Type == structs.TaskRestartSignal {
found = true
require.Equal(t, event.Time, e.Time)
require.Equal(t, event.RestartReason, e.RestartReason)
require.Contains(t, e.DisplayMessage, event.RestartReason)
}
}
require.True(t, found, "restarting task event not found", pretty.Sprint(events))
}
// TestTaskRunner_CheckWatcher_Restart asserts that when enabled an unhealthy
// Consul check will cause a task to restart following restart policy rules.
func TestTaskRunner_CheckWatcher_Restart(t *testing.T) {
t.Parallel()
alloc := mock.Alloc()
// Make the restart policy fail within this test
tg := alloc.Job.TaskGroups[0]
tg.RestartPolicy.Attempts = 2
tg.RestartPolicy.Interval = 1 * time.Minute
tg.RestartPolicy.Delay = 10 * time.Millisecond
tg.RestartPolicy.Mode = structs.RestartPolicyModeFail
task := tg.Tasks[0]
task.Driver = "mock_driver"
task.Config = map[string]interface{}{
"run_for": "10m",
}
// Make the task register a check that fails
task.Services[0].Checks[0] = &structs.ServiceCheck{
Name: "test-restarts",
Type: structs.ServiceCheckTCP,
Interval: 50 * time.Millisecond,
CheckRestart: &structs.CheckRestart{
Limit: 2,
Grace: 100 * time.Millisecond,
},
}
conf, cleanup := testTaskRunnerConfig(t, alloc, task.Name)
defer cleanup()
// Replace mock Consul ServiceClient, with the real ServiceClient
// backed by a mock consul whose checks are always unhealthy.
consulAgent := agentconsul.NewMockAgent()
consulAgent.SetStatus("critical")
consulClient := agentconsul.NewServiceClient(consulAgent, conf.Logger, true)
go consulClient.Run()
defer consulClient.Shutdown()
conf.Consul = consulClient
tr, err := NewTaskRunner(conf)
require.NoError(t, err)
expectedEvents := []string{
"Received",
"Task Setup",
"Started",
"Restart Signaled",
"Terminated",
"Restarting",
"Started",
"Restart Signaled",
"Terminated",
"Restarting",
"Started",
"Restart Signaled",
"Terminated",
"Not Restarting",
}
// Bump maxEvents so task events aren't dropped
tr.maxEvents = 100
go tr.Run()
defer tr.Kill(context.Background(), structs.NewTaskEvent("cleanup"))
// Wait until the task exits. Don't simply wait for it to run as it may
// get restarted and terminated before the test is able to observe it
// running.
select {
case <-tr.WaitCh():
case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
require.Fail(t, "timeout")
}
state := tr.TaskState()
actualEvents := make([]string, len(state.Events))
for i, e := range state.Events {
actualEvents[i] = string(e.Type)
}
require.Equal(t, actualEvents, expectedEvents)
require.Equal(t, structs.TaskStateDead, state.State)
require.True(t, state.Failed, pretty.Sprint(state))
}
// testWaitForTaskToStart waits for the task to be running or fails the test
func testWaitForTaskToStart(t *testing.T, tr *TaskRunner) {
testutil.WaitForResult(func() (bool, error) {
ts := tr.TaskState()
return ts.State == structs.TaskStateRunning, fmt.Errorf("%v", ts.State)
}, func(err error) {
require.NoError(t, err)
})
}

View File

@ -5,19 +5,16 @@ import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/client/allocdir"
cstructs "github.com/hashicorp/nomad/client/structs"
ctestutil "github.com/hashicorp/nomad/client/testutil"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
@ -201,148 +198,6 @@ func TestPrevAlloc_LocalPrevAlloc_Terminated(t *testing.T) {
require.NoError(t, waiter.Wait(ctx))
}
// TestPrevAlloc_StreamAllocDir_Ok asserts that streaming a tar to an alloc dir
// works.
func TestPrevAlloc_StreamAllocDir_Ok(t *testing.T) {
ctestutil.RequireRoot(t)
t.Parallel()
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("err: %v", err)
}
defer os.RemoveAll(dir)
// Create foo/
fooDir := filepath.Join(dir, "foo")
if err := os.Mkdir(fooDir, 0777); err != nil {
t.Fatalf("err: %v", err)
}
// Change ownership of foo/ to test #3702 (any non-root user is fine)
const uid, gid = 1, 1
if err := os.Chown(fooDir, uid, gid); err != nil {
t.Fatalf("err : %v", err)
}
dirInfo, err := os.Stat(fooDir)
if err != nil {
t.Fatalf("err: %v", err)
}
// Create foo/bar
f, err := os.Create(filepath.Join(fooDir, "bar"))
if err != nil {
t.Fatalf("err: %v", err)
}
if _, err := f.WriteString("123"); err != nil {
t.Fatalf("err: %v", err)
}
if err := f.Chmod(0644); err != nil {
t.Fatalf("err: %v", err)
}
fInfo, err := f.Stat()
if err != nil {
t.Fatalf("err: %v", err)
}
f.Close()
// Create foo/baz -> bar symlink
if err := os.Symlink("bar", filepath.Join(dir, "foo", "baz")); err != nil {
t.Fatalf("err: %v", err)
}
linkInfo, err := os.Lstat(filepath.Join(dir, "foo", "baz"))
if err != nil {
t.Fatalf("err: %v", err)
}
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
walkFn := func(path string, fileInfo os.FileInfo, err error) error {
// Include the path of the file name relative to the alloc dir
// so that we can put the files in the right directories
link := ""
if fileInfo.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(path)
if err != nil {
return fmt.Errorf("error reading symlink: %v", err)
}
link = target
}
hdr, err := tar.FileInfoHeader(fileInfo, link)
if err != nil {
return fmt.Errorf("error creating file header: %v", err)
}
hdr.Name = fileInfo.Name()
tw.WriteHeader(hdr)
// If it's a directory or symlink we just write the header into the tar
if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0) {
return nil
}
// Write the file into the archive
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(tw, file); err != nil {
return err
}
return nil
}
if err := filepath.Walk(dir, walkFn); err != nil {
t.Fatalf("err: %v", err)
}
tw.Close()
dir1, err := ioutil.TempDir("", "nomadtest-")
if err != nil {
t.Fatalf("err: %v", err)
}
defer os.RemoveAll(dir1)
rc := ioutil.NopCloser(buf)
prevAlloc := &remotePrevAlloc{logger: testlog.HCLogger(t)}
if err := prevAlloc.streamAllocDir(context.Background(), rc, dir1); err != nil {
t.Fatalf("err: %v", err)
}
// Ensure foo is present
fi, err := os.Stat(filepath.Join(dir1, "foo"))
if err != nil {
t.Fatalf("err: %v", err)
}
if fi.Mode() != dirInfo.Mode() {
t.Fatalf("mode: %v", fi.Mode())
}
stat := fi.Sys().(*syscall.Stat_t)
if stat.Uid != uid || stat.Gid != gid {
t.Fatalf("foo/ has incorrect ownership: expected %d:%d found %d:%d",
uid, gid, stat.Uid, stat.Gid)
}
fi1, err := os.Stat(filepath.Join(dir1, "bar"))
if err != nil {
t.Fatalf("err: %v", err)
}
if fi1.Mode() != fInfo.Mode() {
t.Fatalf("mode: %v", fi1.Mode())
}
fi2, err := os.Lstat(filepath.Join(dir1, "baz"))
if err != nil {
t.Fatalf("err: %v", err)
}
if fi2.Mode() != linkInfo.Mode() {
t.Fatalf("mode: %v", fi2.Mode())
}
}
// TestPrevAlloc_StreamAllocDir_Error asserts that errors encountered while
// streaming a tar cause the migration to be cancelled and no files are written
// (migrations are atomic).

View File

@ -0,0 +1,161 @@
// +build !windows
package allocwatcher
import (
"archive/tar"
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
ctestutil "github.com/hashicorp/nomad/client/testutil"
"github.com/hashicorp/nomad/helper/testlog"
)
// TestPrevAlloc_StreamAllocDir_Ok asserts that streaming a tar to an alloc dir
// works.
func TestPrevAlloc_StreamAllocDir_Ok(t *testing.T) {
ctestutil.RequireRoot(t)
t.Parallel()
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("err: %v", err)
}
defer os.RemoveAll(dir)
// Create foo/
fooDir := filepath.Join(dir, "foo")
if err := os.Mkdir(fooDir, 0777); err != nil {
t.Fatalf("err: %v", err)
}
// Change ownership of foo/ to test #3702 (any non-root user is fine)
const uid, gid = 1, 1
if err := os.Chown(fooDir, uid, gid); err != nil {
t.Fatalf("err : %v", err)
}
dirInfo, err := os.Stat(fooDir)
if err != nil {
t.Fatalf("err: %v", err)
}
// Create foo/bar
f, err := os.Create(filepath.Join(fooDir, "bar"))
if err != nil {
t.Fatalf("err: %v", err)
}
if _, err := f.WriteString("123"); err != nil {
t.Fatalf("err: %v", err)
}
if err := f.Chmod(0644); err != nil {
t.Fatalf("err: %v", err)
}
fInfo, err := f.Stat()
if err != nil {
t.Fatalf("err: %v", err)
}
f.Close()
// Create foo/baz -> bar symlink
if err := os.Symlink("bar", filepath.Join(dir, "foo", "baz")); err != nil {
t.Fatalf("err: %v", err)
}
linkInfo, err := os.Lstat(filepath.Join(dir, "foo", "baz"))
if err != nil {
t.Fatalf("err: %v", err)
}
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
walkFn := func(path string, fileInfo os.FileInfo, err error) error {
// Include the path of the file name relative to the alloc dir
// so that we can put the files in the right directories
link := ""
if fileInfo.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(path)
if err != nil {
return fmt.Errorf("error reading symlink: %v", err)
}
link = target
}
hdr, err := tar.FileInfoHeader(fileInfo, link)
if err != nil {
return fmt.Errorf("error creating file header: %v", err)
}
hdr.Name = fileInfo.Name()
tw.WriteHeader(hdr)
// If it's a directory or symlink we just write the header into the tar
if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0) {
return nil
}
// Write the file into the archive
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(tw, file); err != nil {
return err
}
return nil
}
if err := filepath.Walk(dir, walkFn); err != nil {
t.Fatalf("err: %v", err)
}
tw.Close()
dir1, err := ioutil.TempDir("", "nomadtest-")
if err != nil {
t.Fatalf("err: %v", err)
}
defer os.RemoveAll(dir1)
rc := ioutil.NopCloser(buf)
prevAlloc := &remotePrevAlloc{logger: testlog.HCLogger(t)}
if err := prevAlloc.streamAllocDir(context.Background(), rc, dir1); err != nil {
t.Fatalf("err: %v", err)
}
// Ensure foo is present
fi, err := os.Stat(filepath.Join(dir1, "foo"))
if err != nil {
t.Fatalf("err: %v", err)
}
if fi.Mode() != dirInfo.Mode() {
t.Fatalf("mode: %v", fi.Mode())
}
stat := fi.Sys().(*syscall.Stat_t)
if stat.Uid != uid || stat.Gid != gid {
t.Fatalf("foo/ has incorrect ownership: expected %d:%d found %d:%d",
uid, gid, stat.Uid, stat.Gid)
}
fi1, err := os.Stat(filepath.Join(dir1, "bar"))
if err != nil {
t.Fatalf("err: %v", err)
}
if fi1.Mode() != fInfo.Mode() {
t.Fatalf("mode: %v", fi1.Mode())
}
fi2, err := os.Lstat(filepath.Join(dir1, "baz"))
if err != nil {
t.Fatalf("err: %v", err)
}
if fi2.Mode() != linkInfo.Mode() {
t.Fatalf("mode: %v", fi2.Mode())
}
}

View File

@ -7,7 +7,7 @@ import (
)
func TestNetworkFingerPrint_linkspeed_parse(t *testing.T) {
f := &NetworkFingerprint{logger: testlog.Logger(t), interfaceDetector: &DefaultNetworkInterfaceDetector{}}
f := &NetworkFingerprint{logger: testlog.HCLogger(t), interfaceDetector: &DefaultNetworkInterfaceDetector{}}
var outputTests = []struct {
in string

View File

@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"testing"
"time"
@ -890,7 +891,7 @@ func TestFS_Logs_TaskPending(t *testing.T) {
job := mock.BatchJob()
job.TaskGroups[0].Count = 1
job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{
"start_block_for": "4s",
"start_block_for": "10s",
}
// Register job
@ -915,6 +916,12 @@ func TestFS_Logs_TaskPending(t *testing.T) {
}
allocID = resp.Allocations[0].ID
// wait for alloc runner to be created; otherwise, we get no alloc found error
if _, err := c.getAllocRunner(allocID); err != nil {
return false, fmt.Errorf("alloc runner was not created yet for %v", allocID)
}
return true, nil
}, func(err error) {
t.Fatalf("error getting alloc id: %v", err)
@ -1523,7 +1530,11 @@ func TestFS_streamFile_NoFile(t *testing.T) {
err := c.endpoints.FileSystem.streamFile(
context.Background(), 0, "foo", 0, ad, framer, nil)
require.NotNil(err)
require.Contains(err.Error(), "no such file")
if runtime.GOOS == "windows" {
require.Contains(err.Error(), "cannot find the file")
} else {
require.Contains(err.Error(), "no such file")
}
}
func TestFS_streamFile_Modify(t *testing.T) {
@ -1701,6 +1712,9 @@ func TestFS_streamFile_Truncate(t *testing.T) {
}
func TestFS_streamImpl_Delete(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Windows does not allow us to delete a file while it is open")
}
t.Parallel()
c, cleanup := TestClient(t, nil)
@ -1725,7 +1739,11 @@ func TestFS_streamImpl_Delete(t *testing.T) {
frames := make(chan *sframer.StreamFrame, 4)
go func() {
for {
frame := <-frames
frame, ok := <-frames
if !ok {
return
}
if frame.IsHeartbeat() {
continue
}

View File

@ -14,7 +14,7 @@ import (
// LaunchLogMon an instance of logmon
// TODO: Integrate with base plugin loader
func LaunchLogMon(logger hclog.Logger) (LogMon, *plugin.Client, error) {
func LaunchLogMon(logger hclog.Logger, reattachConfig *plugin.ReattachConfig) (LogMon, *plugin.Client, error) {
logger = logger.Named("logmon")
bin, err := discover.NomadExecutable()
if err != nil {
@ -23,6 +23,7 @@ func LaunchLogMon(logger hclog.Logger) (LogMon, *plugin.Client, error) {
client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: base.Handshake,
Reattach: reattachConfig,
Plugins: map[string]plugin.Plugin{
"logmon": &Plugin{},
},
@ -45,7 +46,6 @@ func LaunchLogMon(logger hclog.Logger) (LogMon, *plugin.Client, error) {
l := raw.(LogMon)
return l, client, nil
}
type Plugin struct {

View File

@ -40,7 +40,7 @@ func (m *StartRequest) Reset() { *m = StartRequest{} }
func (m *StartRequest) String() string { return proto.CompactTextString(m) }
func (*StartRequest) ProtoMessage() {}
func (*StartRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_logmon_6dbff459851a9ae9, []int{0}
return fileDescriptor_logmon_c8f5fe5f286cd193, []int{0}
}
func (m *StartRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StartRequest.Unmarshal(m, b)
@ -119,7 +119,7 @@ func (m *StartResponse) Reset() { *m = StartResponse{} }
func (m *StartResponse) String() string { return proto.CompactTextString(m) }
func (*StartResponse) ProtoMessage() {}
func (*StartResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_logmon_6dbff459851a9ae9, []int{1}
return fileDescriptor_logmon_c8f5fe5f286cd193, []int{1}
}
func (m *StartResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StartResponse.Unmarshal(m, b)
@ -149,7 +149,7 @@ func (m *StopRequest) Reset() { *m = StopRequest{} }
func (m *StopRequest) String() string { return proto.CompactTextString(m) }
func (*StopRequest) ProtoMessage() {}
func (*StopRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_logmon_6dbff459851a9ae9, []int{2}
return fileDescriptor_logmon_c8f5fe5f286cd193, []int{2}
}
func (m *StopRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopRequest.Unmarshal(m, b)
@ -179,7 +179,7 @@ func (m *StopResponse) Reset() { *m = StopResponse{} }
func (m *StopResponse) String() string { return proto.CompactTextString(m) }
func (*StopResponse) ProtoMessage() {}
func (*StopResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_logmon_6dbff459851a9ae9, []int{3}
return fileDescriptor_logmon_c8f5fe5f286cd193, []int{3}
}
func (m *StopResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopResponse.Unmarshal(m, b)
@ -312,10 +312,10 @@ var _LogMon_serviceDesc = grpc.ServiceDesc{
}
func init() {
proto.RegisterFile("client/logmon/proto/logmon.proto", fileDescriptor_logmon_6dbff459851a9ae9)
proto.RegisterFile("client/logmon/proto/logmon.proto", fileDescriptor_logmon_c8f5fe5f286cd193)
}
var fileDescriptor_logmon_6dbff459851a9ae9 = []byte{
var fileDescriptor_logmon_c8f5fe5f286cd193 = []byte{
// 320 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x90, 0x31, 0x6f, 0xc2, 0x30,
0x10, 0x85, 0x1b, 0x0a, 0xa1, 0x1c, 0x0d, 0x45, 0x5e, 0x1a, 0xd1, 0xa1, 0x28, 0x1d, 0xca, 0x14,

View File

@ -32,7 +32,7 @@ type allocRunnerMutableState08 struct {
// 0.8.
//
// https://github.com/hashicorp/nomad/blob/v0.8.6/client/task_runner.go#L188-L197
//
// COMPAT(0.10): Allows upgrading from 0.8.X to 0.9.0.
type taskRunnerState08 struct {
Version string
HandleID string

View File

@ -168,6 +168,7 @@ type MemoryStats struct {
RSS uint64
Cache uint64
Swap uint64
Usage uint64
MaxUsage uint64
KernelUsage uint64
KernelMaxUsage uint64
@ -184,6 +185,7 @@ func (ms *MemoryStats) Add(other *MemoryStats) {
ms.RSS += other.RSS
ms.Cache += other.Cache
ms.Swap += other.Swap
ms.Usage += other.Usage
ms.MaxUsage += other.MaxUsage
ms.KernelUsage += other.KernelUsage
ms.KernelMaxUsage += other.KernelMaxUsage

View File

@ -9,10 +9,12 @@ import (
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/testutil"
vaultapi "github.com/hashicorp/vault/api"
"github.com/stretchr/testify/require"
)
func TestVaultClient_TokenRenewals(t *testing.T) {
t.Parallel()
require := require.New(t)
v := testutil.NewTestVault(t)
defer v.Stop()
@ -67,9 +69,7 @@ func TestVaultClient_TokenRenewals(t *testing.T) {
for {
select {
case err := <-errCh:
if err != nil {
t.Fatalf("error while renewing the token: %v", err)
}
require.NoError(err, "unexpected error while renewing vault token")
}
}
}(errCh)
@ -83,7 +83,7 @@ func TestVaultClient_TokenRenewals(t *testing.T) {
for i := 0; i < num; i++ {
if err := c.StopRenewToken(tokens[i]); err != nil {
t.Fatal(err)
require.NoError(err)
}
}
@ -275,7 +275,8 @@ func TestVaultClient_RenewNonexistentLease(t *testing.T) {
_, err = c.RenewToken(c.client.Token(), 10)
if err == nil {
t.Fatalf("expected error, got nil")
} else if !strings.Contains(err.Error(), "lease not found") {
t.Fatalf("expected \"%s\" in error message, got \"%v\"", "lease not found", err)
// The Vault error message changed between 0.10.2 and 1.0.1
} else if !strings.Contains(err.Error(), "lease not found") && !strings.Contains(err.Error(), "lease is not renewable") {
t.Fatalf("expected \"%s\" or \"%s\" in error message, got \"%v\"", "lease not found", "lease is not renewable", err.Error())
}
}

View File

@ -130,7 +130,7 @@ func TestHTTP_JobsRegister(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
// Create the job
job := api.MockJob()
job := MockJob()
args := api.JobRegisterRequest{
Job: job,
WriteRequest: api.WriteRequest{Region: "global"},
@ -185,7 +185,7 @@ func TestHTTP_JobsRegister_ACL(t *testing.T) {
t.Parallel()
httpACLTest(t, nil, func(s *TestAgent) {
// Create the job
job := api.MockJob()
job := MockJob()
args := api.JobRegisterRequest{
Job: job,
WriteRequest: api.WriteRequest{
@ -215,7 +215,7 @@ func TestHTTP_JobsRegister_Defaulting(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
// Create the job
job := api.MockJob()
job := MockJob()
// Do not set its priority
job.Priority = nil
@ -411,7 +411,7 @@ func TestHTTP_JobUpdate(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
// Create the job
job := api.MockJob()
job := MockJob()
args := api.JobRegisterRequest{
Job: job,
WriteRequest: api.WriteRequest{
@ -985,7 +985,7 @@ func TestHTTP_JobPlan(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
// Create the job
job := api.MockJob()
job := MockJob()
args := api.JobPlanRequest{
Job: job,
Diff: true,

View File

@ -1,14 +1,15 @@
package api
package agent
import (
"time"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/uuid"
)
func MockJob() *Job {
job := &Job{
func MockJob() *api.Job {
job := &api.Job{
Region: helper.StringToPtr("global"),
ID: helper.StringToPtr(uuid.Generate()),
Name: helper.StringToPtr("my-job"),
@ -16,27 +17,27 @@ func MockJob() *Job {
Priority: helper.IntToPtr(50),
AllAtOnce: helper.BoolToPtr(false),
Datacenters: []string{"dc1"},
Constraints: []*Constraint{
Constraints: []*api.Constraint{
{
LTarget: "${attr.kernel.name}",
RTarget: "linux",
Operand: "=",
},
},
TaskGroups: []*TaskGroup{
TaskGroups: []*api.TaskGroup{
{
Name: helper.StringToPtr("web"),
Count: helper.IntToPtr(10),
EphemeralDisk: &EphemeralDisk{
EphemeralDisk: &api.EphemeralDisk{
SizeMB: helper.IntToPtr(150),
},
RestartPolicy: &RestartPolicy{
RestartPolicy: &api.RestartPolicy{
Attempts: helper.IntToPtr(3),
Interval: helper.TimeToPtr(10 * time.Minute),
Delay: helper.TimeToPtr(1 * time.Minute),
Mode: helper.StringToPtr("delay"),
},
Tasks: []*Task{
Tasks: []*api.Task{
{
Name: "web",
Driver: "exec",
@ -46,12 +47,12 @@ func MockJob() *Job {
Env: map[string]string{
"FOO": "bar",
},
Services: []*Service{
Services: []*api.Service{
{
Name: "${TASK}-frontend",
PortLabel: "http",
Tags: []string{"pci:${meta.pci-dss}", "datacenter:${node.datacenter}"},
Checks: []ServiceCheck{
Checks: []api.ServiceCheck{
{
Name: "check-table",
Type: "script",
@ -67,14 +68,14 @@ func MockJob() *Job {
PortLabel: "admin",
},
},
LogConfig: DefaultLogConfig(),
Resources: &Resources{
LogConfig: api.DefaultLogConfig(),
Resources: &api.Resources{
CPU: helper.IntToPtr(500),
MemoryMB: helper.IntToPtr(256),
Networks: []*NetworkResource{
Networks: []*api.NetworkResource{
{
MBits: helper.IntToPtr(50),
DynamicPorts: []Port{{Label: "http"}, {Label: "admin"}},
DynamicPorts: []api.Port{{Label: "http"}, {Label: "admin"}},
},
},
},
@ -98,10 +99,10 @@ func MockJob() *Job {
return job
}
func MockPeriodicJob() *Job {
func MockPeriodicJob() *api.Job {
j := MockJob()
j.Type = helper.StringToPtr("batch")
j.Periodic = &PeriodicConfig{
j.Periodic = &api.PeriodicConfig{
Enabled: helper.BoolToPtr(true),
SpecType: helper.StringToPtr("cron"),
Spec: helper.StringToPtr("*/30 * * * *"),

View File

@ -557,6 +557,8 @@ func (c *AllocStatusCommand) outputVerboseResourceUsage(task string, resourceUsa
measuredStats = append(measuredStats, humanize.IBytes(memoryStats.Cache))
case "Swap":
measuredStats = append(measuredStats, humanize.IBytes(memoryStats.Swap))
case "Usage":
measuredStats = append(measuredStats, humanize.IBytes(memoryStats.Usage))
case "Max Usage":
measuredStats = append(measuredStats, humanize.IBytes(memoryStats.MaxUsage))
case "Kernel Usage":

View File

@ -10,7 +10,6 @@ name = "client1"
# Enable the client
client {
enabled = true
server_join {
retry_join = ["127.0.0.1:4647", "127.0.0.1:5647", "127.0.0.1:6647"]
}

View File

@ -31,7 +31,7 @@ const (
const (
// Nvidia-container-runtime environment variable names
nvidiaVisibleDevices = "NVIDIA_VISIBLE_DEVICES"
NvidiaVisibleDevices = "NVIDIA_VISIBLE_DEVICES"
)
var (
@ -181,7 +181,7 @@ func (d *NvidiaDevice) Reserve(deviceIDs []string) (*device.ContainerReservation
return &device.ContainerReservation{
Envs: map[string]string{
nvidiaVisibleDevices: strings.Join(deviceIDs, ","),
NvidiaVisibleDevices: strings.Join(deviceIDs, ","),
},
}, nil
}

View File

@ -73,7 +73,7 @@ func TestReserve(t *testing.T) {
Name: "All RequestedIDs are managed by Device",
ExpectedReservation: &device.ContainerReservation{
Envs: map[string]string{
nvidiaVisibleDevices: "UUID1,UUID2,UUID3",
NvidiaVisibleDevices: "UUID1,UUID2,UUID3",
},
},
ExpectedError: nil,

View File

@ -101,6 +101,12 @@ func PluginLoader(opts map[string]string) (map[string]interface{}, error) {
if v, err := strconv.ParseBool(opts["docker.privileged.enabled"]); err == nil {
conf["allow_privileged"] = v
}
// nvidia_runtime
if v, ok := opts["docker.nvidia_runtime"]; ok {
conf["nvidia_runtime"] = v
}
return conf, nil
}
@ -153,6 +159,7 @@ var (
// }
// allow_privileged = false
// allow_caps = ["CHOWN", "NET_RAW" ... ]
// nvidia_runtime = "nvidia"
// }
// }
configSpec = hclspec.NewObject(map[string]*hclspec.Spec{
@ -204,6 +211,10 @@ var (
hclspec.NewAttr("allow_caps", "list(string)", false),
hclspec.NewLiteral(`["CHOWN","DAC_OVERRIDE","FSETID","FOWNER","MKNOD","NET_RAW","SETGID","SETUID","SETFCAP","SETPCAP","NET_BIND_SERVICE","SYS_CHROOT","KILL","AUDIT_WRITE"]`),
),
"nvidia_runtime": hclspec.NewDefault(
hclspec.NewAttr("nvidia_runtime", "string", false),
hclspec.NewLiteral(`"nvidia"`),
),
})
// taskConfigSpec is the hcl specification for the driver config section of
@ -470,6 +481,7 @@ type DriverConfig struct {
Volumes VolumeConfig `codec:"volumes"`
AllowPrivileged bool `codec:"allow_privileged"`
AllowCaps []string `codec:"allow_caps"`
GPURuntimeName string `codec:"nvidia_runtime"`
}
type AuthConfig struct {

View File

@ -3,6 +3,7 @@ package docklog
import (
"bytes"
"fmt"
"runtime"
"testing"
docker "github.com/fsouza/go-dockerclient"
@ -13,22 +14,34 @@ import (
"golang.org/x/net/context"
)
func testContainerDetails() (image string, imageName string, imageTag string) {
if runtime.GOOS == "windows" {
return "dantoml/busybox-windows:08012019",
"dantoml/busybox-windows",
"08012019"
}
return "busybox:1", "busybox", "1"
}
func TestDockerLogger(t *testing.T) {
ctu.DockerCompatible(t)
t.Parallel()
require := require.New(t)
containerImage, containerImageName, containerImageTag := testContainerDetails()
client, err := docker.NewClientFromEnv()
if err != nil {
t.Skip("docker unavailable:", err)
}
if img, err := client.InspectImage("busybox:1"); err != nil || img == nil {
if img, err := client.InspectImage(containerImage); err != nil || img == nil {
t.Log("image not found locally, downloading...")
err = client.PullImage(docker.PullImageOptions{
Repository: "busybox",
Tag: "1",
Repository: containerImageName,
Tag: containerImageTag,
}, docker.AuthConfiguration{})
if err != nil {
t.Fatalf("failed to pull image: %v", err)
@ -38,9 +51,9 @@ func TestDockerLogger(t *testing.T) {
containerConf := docker.CreateContainerOptions{
Config: &docker.Config{
Cmd: []string{
"/bin/sh", "-c", "touch /tmp/docklog; tail -f /tmp/docklog",
"sh", "-c", "touch ~/docklog; tail -f ~/docklog",
},
Image: "busybox:1",
Image: containerImage,
},
Context: context.Background(),
}
@ -98,8 +111,8 @@ func echoToContainer(t *testing.T, client *docker.Client, id string, line string
op := docker.CreateExecOptions{
Container: id,
Cmd: []string{
"/bin/ash", "-c",
fmt.Sprintf("echo %s >>/tmp/docklog", line),
"ash", "-c",
fmt.Sprintf("echo %s >>~/docklog", line),
},
}

View File

@ -40,7 +40,7 @@ func (m *StartRequest) Reset() { *m = StartRequest{} }
func (m *StartRequest) String() string { return proto.CompactTextString(m) }
func (*StartRequest) ProtoMessage() {}
func (*StartRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_docker_logger_0aa5a411831bd10e, []int{0}
return fileDescriptor_docker_logger_550e35425edc00c0, []int{0}
}
func (m *StartRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StartRequest.Unmarshal(m, b)
@ -119,7 +119,7 @@ func (m *StartResponse) Reset() { *m = StartResponse{} }
func (m *StartResponse) String() string { return proto.CompactTextString(m) }
func (*StartResponse) ProtoMessage() {}
func (*StartResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_docker_logger_0aa5a411831bd10e, []int{1}
return fileDescriptor_docker_logger_550e35425edc00c0, []int{1}
}
func (m *StartResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StartResponse.Unmarshal(m, b)
@ -149,7 +149,7 @@ func (m *StopRequest) Reset() { *m = StopRequest{} }
func (m *StopRequest) String() string { return proto.CompactTextString(m) }
func (*StopRequest) ProtoMessage() {}
func (*StopRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_docker_logger_0aa5a411831bd10e, []int{2}
return fileDescriptor_docker_logger_550e35425edc00c0, []int{2}
}
func (m *StopRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopRequest.Unmarshal(m, b)
@ -179,7 +179,7 @@ func (m *StopResponse) Reset() { *m = StopResponse{} }
func (m *StopResponse) String() string { return proto.CompactTextString(m) }
func (*StopResponse) ProtoMessage() {}
func (*StopResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_docker_logger_0aa5a411831bd10e, []int{3}
return fileDescriptor_docker_logger_550e35425edc00c0, []int{3}
}
func (m *StopResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopResponse.Unmarshal(m, b)
@ -312,10 +312,10 @@ var _DockerLogger_serviceDesc = grpc.ServiceDesc{
}
func init() {
proto.RegisterFile("drivers/docker/docklog/proto/docker_logger.proto", fileDescriptor_docker_logger_0aa5a411831bd10e)
proto.RegisterFile("drivers/docker/docklog/proto/docker_logger.proto", fileDescriptor_docker_logger_550e35425edc00c0)
}
var fileDescriptor_docker_logger_0aa5a411831bd10e = []byte{
var fileDescriptor_docker_logger_550e35425edc00c0 = []byte{
// 328 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x50, 0xb1, 0x4e, 0xeb, 0x40,
0x10, 0x7c, 0xce, 0x8b, 0x9d, 0x70, 0x49, 0x40, 0x3a, 0x09, 0x61, 0xd2, 0x00, 0xae, 0x28, 0x90,

View File

@ -17,6 +17,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/nomad/client/taskenv"
"github.com/hashicorp/nomad/devices/gpu/nvidia"
"github.com/hashicorp/nomad/drivers/docker/docklog"
"github.com/hashicorp/nomad/drivers/shared/eventer"
nstructs "github.com/hashicorp/nomad/nomad/structs"
@ -40,7 +41,7 @@ var (
waitClient *docker.Client
// The statistics the Docker driver exposes
DockerMeasuredMemStats = []string{"RSS", "Cache", "Swap", "Max Usage"}
DockerMeasuredMemStats = []string{"RSS", "Cache", "Swap", "Usage", "Max Usage"}
DockerMeasuredCpuStats = []string{"Throttled Periods", "Throttled Time", "Percent"}
// recoverableErrTimeouts returns a recoverable error if the error was due
@ -88,6 +89,14 @@ type Driver struct {
// logger will log to the Nomad agent
logger hclog.Logger
// gpuRuntime indicates nvidia-docker runtime availability
gpuRuntime bool
// A tri-state boolean to know if the fingerprinting has happened and
// whether it has been successful
fingerprintSuccess *bool
fingerprintLock sync.RWMutex
}
// NewDockerDriver returns a docker implementation of a driver plugin
@ -629,6 +638,13 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T
PidsLimit: driverConfig.PidsLimit,
}
if _, ok := task.DeviceEnv[nvidia.NvidiaVisibleDevices]; ok {
if !d.gpuRuntime {
return c, fmt.Errorf("requested docker-runtime %q was not found", d.config.GPURuntimeName)
}
hostConfig.Runtime = d.config.GPURuntimeName
}
// Calculate CPU Quota
// cfs_quota_us is the time per core, so we must
// multiply the time by the number of cores available

File diff suppressed because it is too large Load Diff

View File

@ -1,98 +1,735 @@
// +build !windows
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package docker
import (
"context"
"fmt"
"io/ioutil"
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"testing"
"time"
docker "github.com/fsouza/go-dockerclient"
"github.com/hashicorp/nomad/client/allocdir"
"github.com/hashicorp/nomad/client/testutil"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/plugins/drivers"
tu "github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDockerDriver_Signal(t *testing.T) {
func TestDockerDriver_User(t *testing.T) {
if !tu.IsTravis() {
t.Parallel()
}
if !testutil.DockerIsConnected(t) {
t.Skip("Docker not connected")
}
testutil.DockerCompatible(t)
task, cfg, _ := dockerTask(t)
cfg.Command = "/bin/sh"
cfg.Args = []string{"local/test.sh"}
task.User = "alice"
cfg.Command = "/bin/sleep"
cfg.Args = []string{"10000"}
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
driver := dockerDriverHarness(t, nil)
cleanup := driver.MkAllocDir(task, true)
d := dockerDriverHarness(t, nil)
cleanup := d.MkAllocDir(task, true)
defer cleanup()
// Copy the image into the task's directory
copyImage(t, task.TaskDir(), "busybox.tar")
testFile := filepath.Join(task.TaskDir().LocalDir, "test.sh")
testData := []byte(`
at_term() {
echo 'Terminated.' > $NOMAD_TASK_DIR/output
exit 3
_, _, err := d.StartTask(task)
if err == nil {
d.DestroyTask(task.ID, true)
t.Fatalf("Should've failed")
}
if !strings.Contains(err.Error(), "alice") {
t.Fatalf("Expected failure string not found, found %q instead", err.Error())
}
}
trap at_term INT
while true; do
echo 'sleeping'
sleep 0.2
done
`)
require.NoError(t, ioutil.WriteFile(testFile, testData, 0777))
_, _, err := driver.StartTask(task)
func TestDockerDriver_NetworkAliases_Bridge(t *testing.T) {
if !tu.IsTravis() {
t.Parallel()
}
testutil.DockerCompatible(t)
require := require.New(t)
// Because go-dockerclient doesn't provide api for query network aliases, just check that
// a container can be created with a 'network_aliases' property
// Create network, network-scoped alias is supported only for containers in user defined networks
client := newTestDockerClient(t)
networkOpts := docker.CreateNetworkOptions{Name: "foobar", Driver: "bridge"}
network, err := client.CreateNetwork(networkOpts)
require.NoError(err)
defer client.RemoveNetwork(network.ID)
expected := []string{"foobar"}
taskCfg := newTaskConfig("", busyboxLongRunningCmd)
taskCfg.NetworkMode = network.Name
taskCfg.NetworkAliases = expected
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "busybox",
Resources: basicResources,
}
require.NoError(task.EncodeConcreteDriverConfig(&taskCfg))
d := dockerDriverHarness(t, nil)
cleanup := d.MkAllocDir(task, true)
defer cleanup()
copyImage(t, task.TaskDir(), "busybox.tar")
_, _, err = d.StartTask(task)
require.NoError(err)
require.NoError(d.WaitUntilStarted(task.ID, 5*time.Second))
defer d.DestroyTask(task.ID, true)
dockerDriver, ok := d.Impl().(*Driver)
require.True(ok)
handle, ok := dockerDriver.tasks.Get(task.ID)
require.True(ok)
_, err = client.InspectContainer(handle.containerID)
require.NoError(err)
}
func TestDockerDriver_NetworkMode_Host(t *testing.T) {
if !tu.IsTravis() {
t.Parallel()
}
testutil.DockerCompatible(t)
expected := "host"
taskCfg := newTaskConfig("", busyboxLongRunningCmd)
taskCfg.NetworkMode = expected
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "busybox-demo",
Resources: basicResources,
}
require.NoError(t, task.EncodeConcreteDriverConfig(&taskCfg))
d := dockerDriverHarness(t, nil)
cleanup := d.MkAllocDir(task, true)
defer cleanup()
copyImage(t, task.TaskDir(), "busybox.tar")
_, _, err := d.StartTask(task)
require.NoError(t, err)
defer driver.DestroyTask(task.ID, true)
require.NoError(t, driver.WaitUntilStarted(task.ID, time.Duration(tu.TestMultiplier()*5)*time.Second))
handle, ok := driver.Impl().(*Driver).tasks.Get(task.ID)
require.NoError(t, d.WaitUntilStarted(task.ID, 5*time.Second))
defer d.DestroyTask(task.ID, true)
dockerDriver, ok := d.Impl().(*Driver)
require.True(t, ok)
waitForExist(t, newTestDockerClient(t), handle.containerID)
require.NoError(t, handle.Kill(time.Duration(tu.TestMultiplier()*5)*time.Second, os.Interrupt))
handle, ok := dockerDriver.tasks.Get(task.ID)
require.True(t, ok)
waitCh, err := driver.WaitTask(context.Background(), task.ID)
require.NoError(t, err)
select {
case res := <-waitCh:
if res.Successful() {
require.Fail(t, "should err: %v", res)
}
case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
require.Fail(t, "timeout")
}
// Check the log file to see it exited because of the signal
outputFile := filepath.Join(task.TaskDir().LocalDir, "output")
act, err := ioutil.ReadFile(outputFile)
container, err := client.InspectContainer(handle.containerID)
if err != nil {
t.Fatalf("Couldn't read expected output: %v", err)
t.Fatalf("err: %v", err)
}
exp := "Terminated."
if strings.TrimSpace(string(act)) != exp {
t.Fatalf("Command outputted %v; want %v", act, exp)
}
actual := container.HostConfig.NetworkMode
require.Equal(t, expected, actual)
}
func TestDockerDriver_containerBinds(t *testing.T) {
func TestDockerDriver_CPUCFSPeriod(t *testing.T) {
if !tu.IsTravis() {
t.Parallel()
}
testutil.DockerCompatible(t)
task, cfg, _ := dockerTask(t)
driver := dockerDriverHarness(t, nil)
cleanup := driver.MkAllocDir(task, false)
cfg.CPUHardLimit = true
cfg.CPUCFSPeriod = 1000000
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
client, _, handle, cleanup := dockerSetup(t, task)
defer cleanup()
binds, err := driver.Impl().(*Driver).containerBinds(task, cfg)
waitForExist(t, client, handle.containerID)
container, err := client.InspectContainer(handle.containerID)
require.NoError(t, err)
require.Contains(t, binds, fmt.Sprintf("%s:/alloc", task.TaskDir().SharedAllocDir))
require.Contains(t, binds, fmt.Sprintf("%s:/local", task.TaskDir().LocalDir))
require.Contains(t, binds, fmt.Sprintf("%s:/secrets", task.TaskDir().SecretsDir))
require.Equal(t, cfg.CPUCFSPeriod, container.HostConfig.CPUPeriod)
}
func TestDockerDriver_Sysctl_Ulimit(t *testing.T) {
testutil.DockerCompatible(t)
task, cfg, _ := dockerTask(t)
expectedUlimits := map[string]string{
"nproc": "4242",
"nofile": "2048:4096",
}
cfg.Sysctl = map[string]string{
"net.core.somaxconn": "16384",
}
cfg.Ulimit = expectedUlimits
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
client, d, handle, cleanup := dockerSetup(t, task)
defer cleanup()
require.NoError(t, d.WaitUntilStarted(task.ID, 5*time.Second))
container, err := client.InspectContainer(handle.containerID)
assert.Nil(t, err, "unexpected error: %v", err)
want := "16384"
got := container.HostConfig.Sysctls["net.core.somaxconn"]
assert.Equal(t, want, got, "Wrong net.core.somaxconn config for docker job. Expect: %s, got: %s", want, got)
expectedUlimitLen := 2
actualUlimitLen := len(container.HostConfig.Ulimits)
assert.Equal(t, want, got, "Wrong number of ulimit configs for docker job. Expect: %d, got: %d", expectedUlimitLen, actualUlimitLen)
for _, got := range container.HostConfig.Ulimits {
if expectedStr, ok := expectedUlimits[got.Name]; !ok {
t.Errorf("%s config unexpected for docker job.", got.Name)
} else {
if !strings.Contains(expectedStr, ":") {
expectedStr = expectedStr + ":" + expectedStr
}
splitted := strings.SplitN(expectedStr, ":", 2)
soft, _ := strconv.Atoi(splitted[0])
hard, _ := strconv.Atoi(splitted[1])
assert.Equal(t, int64(soft), got.Soft, "Wrong soft %s ulimit for docker job. Expect: %d, got: %d", got.Name, soft, got.Soft)
assert.Equal(t, int64(hard), got.Hard, "Wrong hard %s ulimit for docker job. Expect: %d, got: %d", got.Name, hard, got.Hard)
}
}
}
func TestDockerDriver_Sysctl_Ulimit_Errors(t *testing.T) {
testutil.DockerCompatible(t)
brokenConfigs := []map[string]string{
{
"nofile": "",
},
{
"nofile": "abc:1234",
},
{
"nofile": "1234:abc",
},
}
testCases := []struct {
ulimitConfig map[string]string
err error
}{
{brokenConfigs[0], fmt.Errorf("Malformed ulimit specification nofile: \"\", cannot be empty")},
{brokenConfigs[1], fmt.Errorf("Malformed soft ulimit nofile: abc:1234")},
{brokenConfigs[2], fmt.Errorf("Malformed hard ulimit nofile: 1234:abc")},
}
for _, tc := range testCases {
task, cfg, _ := dockerTask(t)
cfg.Ulimit = tc.ulimitConfig
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
d := dockerDriverHarness(t, nil)
cleanup := d.MkAllocDir(task, true)
defer cleanup()
copyImage(t, task.TaskDir(), "busybox.tar")
_, _, err := d.StartTask(task)
require.NotNil(t, err, "Expected non nil error")
require.Contains(t, err.Error(), tc.err.Error())
}
}
// This test does not run on Windows due to stricter path validation in the
// negative case for non existent mount paths. We should write a similar test
// for windows.
func TestDockerDriver_BindMountsHonorVolumesEnabledFlag(t *testing.T) {
t.Parallel()
testutil.DockerCompatible(t)
allocDir := "/tmp/nomad/alloc-dir"
cases := []struct {
name string
requiresVolumes bool
volumeDriver string
volumes []string
expectedVolumes []string
}{
{
name: "basic plugin",
requiresVolumes: true,
volumeDriver: "nfs",
volumes: []string{"test-path:/tmp/taskpath"},
expectedVolumes: []string{"test-path:/tmp/taskpath"},
},
{
name: "absolute default driver",
requiresVolumes: true,
volumeDriver: "",
volumes: []string{"/abs/test-path:/tmp/taskpath"},
expectedVolumes: []string{"/abs/test-path:/tmp/taskpath"},
},
{
name: "absolute local driver",
requiresVolumes: true,
volumeDriver: "local",
volumes: []string{"/abs/test-path:/tmp/taskpath"},
expectedVolumes: []string{"/abs/test-path:/tmp/taskpath"},
},
{
name: "relative default driver",
requiresVolumes: false,
volumeDriver: "",
volumes: []string{"test-path:/tmp/taskpath"},
expectedVolumes: []string{"/tmp/nomad/alloc-dir/demo/test-path:/tmp/taskpath"},
},
{
name: "relative local driver",
requiresVolumes: false,
volumeDriver: "local",
volumes: []string{"test-path:/tmp/taskpath"},
expectedVolumes: []string{"/tmp/nomad/alloc-dir/demo/test-path:/tmp/taskpath"},
},
{
name: "relative outside task-dir default driver",
requiresVolumes: false,
volumeDriver: "",
volumes: []string{"../test-path:/tmp/taskpath"},
expectedVolumes: []string{"/tmp/nomad/alloc-dir/test-path:/tmp/taskpath"},
},
{
name: "relative outside task-dir local driver",
requiresVolumes: false,
volumeDriver: "local",
volumes: []string{"../test-path:/tmp/taskpath"},
expectedVolumes: []string{"/tmp/nomad/alloc-dir/test-path:/tmp/taskpath"},
},
{
name: "relative outside alloc-dir default driver",
requiresVolumes: true,
volumeDriver: "",
volumes: []string{"../../test-path:/tmp/taskpath"},
expectedVolumes: []string{"/tmp/nomad/test-path:/tmp/taskpath"},
},
{
name: "relative outside task-dir local driver",
requiresVolumes: true,
volumeDriver: "local",
volumes: []string{"../../test-path:/tmp/taskpath"},
expectedVolumes: []string{"/tmp/nomad/test-path:/tmp/taskpath"},
},
}
t.Run("with volumes enabled", func(t *testing.T) {
dh := dockerDriverHarness(t, nil)
driver := dh.Impl().(*Driver)
driver.config.Volumes.Enabled = true
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
task, cfg, _ := dockerTask(t)
cfg.VolumeDriver = c.volumeDriver
cfg.Volumes = c.volumes
task.AllocDir = allocDir
task.Name = "demo"
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
require.NoError(t, err)
for _, v := range c.expectedVolumes {
require.Contains(t, cc.HostConfig.Binds, v)
}
})
}
})
t.Run("with volumes disabled", func(t *testing.T) {
dh := dockerDriverHarness(t, nil)
driver := dh.Impl().(*Driver)
driver.config.Volumes.Enabled = false
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
task, cfg, _ := dockerTask(t)
cfg.VolumeDriver = c.volumeDriver
cfg.Volumes = c.volumes
task.AllocDir = allocDir
task.Name = "demo"
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
if c.requiresVolumes {
require.Error(t, err, "volumes are not enabled")
} else {
require.NoError(t, err)
for _, v := range c.expectedVolumes {
require.Contains(t, cc.HostConfig.Binds, v)
}
}
})
}
})
}
// This test does not run on windows due to differences in the definition of
// an absolute path, changing path expansion behaviour. A similar test should
// be written for windows.
func TestDockerDriver_MountsSerialization(t *testing.T) {
t.Parallel()
testutil.DockerCompatible(t)
allocDir := "/tmp/nomad/alloc-dir"
cases := []struct {
name string
requiresVolumes bool
passedMounts []DockerMount
expectedMounts []docker.HostMount
}{
{
name: "basic volume",
passedMounts: []DockerMount{
{
Target: "/nomad",
ReadOnly: true,
Source: "test",
},
},
expectedMounts: []docker.HostMount{
{
Type: "volume",
Target: "/nomad",
Source: "test",
ReadOnly: true,
VolumeOptions: &docker.VolumeOptions{},
},
},
},
{
name: "basic bind",
passedMounts: []DockerMount{
{
Type: "bind",
Target: "/nomad",
Source: "test",
},
},
expectedMounts: []docker.HostMount{
{
Type: "bind",
Target: "/nomad",
Source: "/tmp/nomad/alloc-dir/demo/test",
BindOptions: &docker.BindOptions{},
},
},
},
{
name: "basic absolute bind",
requiresVolumes: true,
passedMounts: []DockerMount{
{
Type: "bind",
Target: "/nomad",
Source: "/tmp/test",
},
},
expectedMounts: []docker.HostMount{
{
Type: "bind",
Target: "/nomad",
Source: "/tmp/test",
BindOptions: &docker.BindOptions{},
},
},
},
{
name: "bind relative outside",
requiresVolumes: true,
passedMounts: []DockerMount{
{
Type: "bind",
Target: "/nomad",
Source: "../../test",
},
},
expectedMounts: []docker.HostMount{
{
Type: "bind",
Target: "/nomad",
Source: "/tmp/nomad/test",
BindOptions: &docker.BindOptions{},
},
},
},
{
name: "basic tmpfs",
requiresVolumes: false,
passedMounts: []DockerMount{
{
Type: "tmpfs",
Target: "/nomad",
TmpfsOptions: DockerTmpfsOptions{
SizeBytes: 321,
Mode: 0666,
},
},
},
expectedMounts: []docker.HostMount{
{
Type: "tmpfs",
Target: "/nomad",
TempfsOptions: &docker.TempfsOptions{
SizeBytes: 321,
Mode: 0666,
},
},
},
},
}
t.Run("with volumes enabled", func(t *testing.T) {
dh := dockerDriverHarness(t, nil)
driver := dh.Impl().(*Driver)
driver.config.Volumes.Enabled = true
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
task, cfg, _ := dockerTask(t)
cfg.Mounts = c.passedMounts
task.AllocDir = allocDir
task.Name = "demo"
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
require.NoError(t, err)
require.EqualValues(t, c.expectedMounts, cc.HostConfig.Mounts)
})
}
})
t.Run("with volumes disabled", func(t *testing.T) {
dh := dockerDriverHarness(t, nil)
driver := dh.Impl().(*Driver)
driver.config.Volumes.Enabled = false
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
task, cfg, _ := dockerTask(t)
cfg.Mounts = c.passedMounts
task.AllocDir = allocDir
task.Name = "demo"
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
cc, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
if c.requiresVolumes {
require.Error(t, err, "volumes are not enabled")
} else {
require.NoError(t, err)
require.EqualValues(t, c.expectedMounts, cc.HostConfig.Mounts)
}
})
}
})
}
// TestDockerDriver_CreateContainerConfig_MountsCombined asserts that
// devices and mounts set by device managers/plugins are honored
// and present in docker.CreateContainerOptions, and that it is appended
// to any devices/mounts a user sets in the task config.
func TestDockerDriver_CreateContainerConfig_MountsCombined(t *testing.T) {
t.Parallel()
testutil.DockerCompatible(t)
task, cfg, _ := dockerTask(t)
task.Devices = []*drivers.DeviceConfig{
{
HostPath: "/dev/fuse",
TaskPath: "/container/dev/task-fuse",
Permissions: "rw",
},
}
task.Mounts = []*drivers.MountConfig{
{
HostPath: "/tmp/task-mount",
TaskPath: "/container/tmp/task-mount",
Readonly: true,
},
}
cfg.Devices = []DockerDevice{
{
HostPath: "/dev/stdout",
ContainerPath: "/container/dev/cfg-stdout",
CgroupPermissions: "rwm",
},
}
cfg.Mounts = []DockerMount{
{
Type: "bind",
Source: "/tmp/cfg-mount",
Target: "/container/tmp/cfg-mount",
ReadOnly: false,
},
}
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
dh := dockerDriverHarness(t, nil)
driver := dh.Impl().(*Driver)
c, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
require.NoError(t, err)
expectedMounts := []docker.HostMount{
{
Type: "bind",
Source: "/tmp/cfg-mount",
Target: "/container/tmp/cfg-mount",
ReadOnly: false,
BindOptions: &docker.BindOptions{},
},
{
Type: "bind",
Source: "/tmp/task-mount",
Target: "/container/tmp/task-mount",
ReadOnly: true,
},
}
foundMounts := c.HostConfig.Mounts
sort.Slice(foundMounts, func(i, j int) bool {
return foundMounts[i].Target < foundMounts[j].Target
})
require.EqualValues(t, expectedMounts, foundMounts)
expectedDevices := []docker.Device{
{
PathOnHost: "/dev/stdout",
PathInContainer: "/container/dev/cfg-stdout",
CgroupPermissions: "rwm",
},
{
PathOnHost: "/dev/fuse",
PathInContainer: "/container/dev/task-fuse",
CgroupPermissions: "rw",
},
}
foundDevices := c.HostConfig.Devices
sort.Slice(foundDevices, func(i, j int) bool {
return foundDevices[i].PathInContainer < foundDevices[j].PathInContainer
})
require.EqualValues(t, expectedDevices, foundDevices)
}
// TestDockerDriver_Cleanup ensures Cleanup removes only downloaded images.
// Doesn't run on windows because it requires an image variant
func TestDockerDriver_Cleanup(t *testing.T) {
testutil.DockerCompatible(t)
// using a small image and an specific point release to avoid accidental conflicts with other tasks
cfg := newTaskConfig("", []string{"sleep", "100"})
cfg.Image = "busybox:1.29.2"
cfg.LoadImage = ""
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "cleanup_test",
Resources: basicResources,
}
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
client, driver, handle, cleanup := dockerSetup(t, task)
defer cleanup()
require.NoError(t, driver.WaitUntilStarted(task.ID, 5*time.Second))
// Cleanup
require.NoError(t, driver.DestroyTask(task.ID, true))
// Ensure image was removed
tu.WaitForResult(func() (bool, error) {
if _, err := client.InspectImage(cfg.Image); err == nil {
return false, fmt.Errorf("image exists but should have been removed. Does another %v container exist?", cfg.Image)
}
return true, nil
}, func(err error) {
require.NoError(t, err)
})
// The image doesn't exist which shouldn't be an error when calling
// Cleanup, so call it again to make sure.
require.NoError(t, driver.Impl().(*Driver).cleanupImage(handle))
}
func newTaskConfig(variant string, command []string) TaskConfig {
// busyboxImageID is the ID stored in busybox.tar
busyboxImageID := "busybox:1.29.3"
image := busyboxImageID
loadImage := "busybox.tar"
if variant != "" {
image = fmt.Sprintf("%s-%s", busyboxImageID, variant)
loadImage = fmt.Sprintf("busybox_%s.tar", variant)
}
return TaskConfig{
Image: image,
LoadImage: loadImage,
Command: command[0],
Args: command[1:],
}
}
func copyImage(t *testing.T, taskDir *allocdir.TaskDir, image string) {
dst := filepath.Join(taskDir.LocalDir, image)
copyFile(filepath.Join("./test-resources/docker", image), dst, t)
}
// copyFile moves an existing file to the destination
func copyFile(src, dst string, t *testing.T) {
in, err := os.Open(src)
if err != nil {
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
}
defer func() {
if err := out.Close(); err != nil {
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
}
}()
if _, err = io.Copy(out, in); err != nil {
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
}
if err := out.Sync(); err != nil {
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
}
}

View File

@ -0,0 +1,26 @@
// +build windows
package docker
import (
"testing"
"github.com/hashicorp/nomad/client/allocdir"
)
func newTaskConfig(variant string, command []string) TaskConfig {
// busyboxImageID is an id of an image containing nanoserver windows and
// a busybox exe.
// See https://github.com/dantoml/windows/blob/81cff1ed77729d1fa36721abd6cb6efebff2f8ef/docker/busybox/Dockerfile
busyboxImageID := "dantoml/busybox-windows:08012019"
return TaskConfig{
Image: busyboxImageID,
Command: command[0],
Args: command[1:],
}
}
// No-op on windows because we don't load images.
func copyImage(t *testing.T, taskDir *allocdir.TaskDir, image string) {
}

View File

@ -2,8 +2,11 @@ package docker
import (
"context"
"sort"
"strings"
"time"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/plugins/drivers"
pstructs "github.com/hashicorp/nomad/plugins/shared/structs"
)
@ -14,6 +17,28 @@ func (d *Driver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint,
return ch, nil
}
// setFingerprintSuccess marks the driver as having fingerprinted successfully
func (d *Driver) setFingerprintSuccess() {
d.fingerprintLock.Lock()
d.fingerprintSuccess = helper.BoolToPtr(true)
d.fingerprintLock.Unlock()
}
// setFingerprintFailure marks the driver as having failed fingerprinting
func (d *Driver) setFingerprintFailure() {
d.fingerprintLock.Lock()
d.fingerprintSuccess = helper.BoolToPtr(false)
d.fingerprintLock.Unlock()
}
// fingerprintSuccessful returns true if the driver has
// never fingerprinted or has successfully fingerprinted
func (d *Driver) fingerprintSuccessful() bool {
d.fingerprintLock.Lock()
defer d.fingerprintLock.Unlock()
return d.fingerprintSuccess == nil || *d.fingerprintSuccess
}
func (d *Driver) handleFingerprint(ctx context.Context, ch chan *drivers.Fingerprint) {
defer close(ch)
ticker := time.NewTimer(0)
@ -38,7 +63,10 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
}
client, _, err := d.dockerClients()
if err != nil {
d.logger.Info("failed to initialize client", "error", err)
if d.fingerprintSuccessful() {
d.logger.Info("failed to initialize client", "error", err)
}
d.setFingerprintFailure()
return &drivers.Fingerprint{
Health: drivers.HealthStateUndetected,
HealthDescription: "Failed to initialize docker client",
@ -47,7 +75,10 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
env, err := client.Version()
if err != nil {
d.logger.Debug("could not connect to docker daemon", "endpoint", client.Endpoint(), "error", err)
if d.fingerprintSuccessful() {
d.logger.Debug("could not connect to docker daemon", "endpoint", client.Endpoint(), "error", err)
}
d.setFingerprintFailure()
return &drivers.Fingerprint{
Health: drivers.HealthStateUnhealthy,
HealthDescription: "Failed to connect to docker daemon",
@ -82,11 +113,33 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
} else {
// Docker 17.09.0-ce dropped the Gateway IP from the bridge network
// See https://github.com/moby/moby/issues/32648
d.logger.Debug("bridge_ip could not be discovered")
if d.fingerprintSuccessful() {
d.logger.Debug("bridge_ip could not be discovered")
}
}
break
}
}
if dockerInfo, err := client.Info(); err != nil {
d.logger.Warn("failed to get Docker system info", "error", err)
} else {
runtimeNames := make([]string, 0, len(dockerInfo.Runtimes))
for name := range dockerInfo.Runtimes {
if d.config.GPURuntimeName == name {
// Nvidia runtime is detected by Docker.
// It makes possible to run GPU workloads using Docker driver on this host.
d.gpuRuntime = true
}
runtimeNames = append(runtimeNames, name)
}
sort.Strings(runtimeNames)
fp.Attributes["runtimes"] = pstructs.NewStringAttribute(
strings.Join(runtimeNames, ","))
}
d.setFingerprintSuccess()
return fp
}

View File

@ -134,6 +134,7 @@ func dockerStatsToTaskResourceUsage(s *docker.Stats) *cstructs.TaskResourceUsage
RSS: s.MemoryStats.Stats.Rss,
Cache: s.MemoryStats.Stats.Cache,
Swap: s.MemoryStats.Stats.Swap,
Usage: s.MemoryStats.Usage,
MaxUsage: s.MemoryStats.MaxUsage,
Measured: DockerMeasuredMemStats,
}

View File

@ -23,6 +23,7 @@ func TestDriver_DockerStatsCollector(t *testing.T) {
stats.MemoryStats.Stats.Rss = 6537216
stats.MemoryStats.Stats.Cache = 1234
stats.MemoryStats.Stats.Swap = 0
stats.MemoryStats.Usage = 5651904
stats.MemoryStats.MaxUsage = 6651904
go dockerStatsCollector(dst, src, time.Second)
@ -38,6 +39,7 @@ func TestDriver_DockerStatsCollector(t *testing.T) {
require.Equal(stats.MemoryStats.Stats.Rss, ru.ResourceUsage.MemoryStats.RSS)
require.Equal(stats.MemoryStats.Stats.Cache, ru.ResourceUsage.MemoryStats.Cache)
require.Equal(stats.MemoryStats.Stats.Swap, ru.ResourceUsage.MemoryStats.Swap)
require.Equal(stats.MemoryStats.Usage, ru.ResourceUsage.MemoryStats.Usage)
require.Equal(stats.MemoryStats.MaxUsage, ru.ResourceUsage.MemoryStats.MaxUsage)
require.Equal(stats.CPUStats.ThrottlingData.ThrottledPeriods, ru.ResourceUsage.CpuStats.ThrottledPeriods)
require.Equal(stats.CPUStats.ThrottlingData.ThrottledTime, ru.ResourceUsage.CpuStats.ThrottledTime)

View File

@ -6,60 +6,6 @@ import (
"github.com/stretchr/testify/require"
)
func TestValidateCgroupPermission(t *testing.T) {
positiveCases := []string{
"r",
"rw",
"rwm",
"mr",
"mrw",
"",
}
for _, c := range positiveCases {
t.Run("positive case: "+c, func(t *testing.T) {
require.True(t, validateCgroupPermission(c))
})
}
negativeCases := []string{
"q",
"asdf",
"rq",
}
for _, c := range negativeCases {
t.Run("negative case: "+c, func(t *testing.T) {
require.False(t, validateCgroupPermission(c))
})
}
}
func TestExpandPath(t *testing.T) {
cases := []struct {
base string
target string
expected string
}{
{"/tmp/alloc/task", "/home/user", "/home/user"},
{"/tmp/alloc/task", "/home/user/..", "/home"},
{"/tmp/alloc/task", ".", "/tmp/alloc/task"},
{"/tmp/alloc/task", "..", "/tmp/alloc"},
{"/tmp/alloc/task", "d1/d2", "/tmp/alloc/task/d1/d2"},
{"/tmp/alloc/task", "../d1/d2", "/tmp/alloc/d1/d2"},
{"/tmp/alloc/task", "../../d1/d2", "/tmp/d1/d2"},
}
for _, c := range cases {
t.Run(c.expected, func(t *testing.T) {
require.Equal(t, c.expected, expandPath(c.base, c.target))
})
}
}
func TestIsParentPath(t *testing.T) {
require.True(t, isParentPath("/a/b/c", "/a/b/c"))
require.True(t, isParentPath("/a/b/c", "/a/b/c/d"))

View File

@ -0,0 +1,63 @@
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package docker
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestValidateCgroupPermission(t *testing.T) {
positiveCases := []string{
"r",
"rw",
"rwm",
"mr",
"mrw",
"",
}
for _, c := range positiveCases {
t.Run("positive case: "+c, func(t *testing.T) {
require.True(t, validateCgroupPermission(c))
})
}
negativeCases := []string{
"q",
"asdf",
"rq",
}
for _, c := range negativeCases {
t.Run("negative case: "+c, func(t *testing.T) {
require.False(t, validateCgroupPermission(c))
})
}
}
func TestExpandPath(t *testing.T) {
cases := []struct {
base string
target string
expected string
}{
{"/tmp/alloc/task", ".", "/tmp/alloc/task"},
{"/tmp/alloc/task", "..", "/tmp/alloc"},
{"/tmp/alloc/task", "d1/d2", "/tmp/alloc/task/d1/d2"},
{"/tmp/alloc/task", "../d1/d2", "/tmp/alloc/d1/d2"},
{"/tmp/alloc/task", "../../d1/d2", "/tmp/d1/d2"},
{"/tmp/alloc/task", "/home/user", "/home/user"},
{"/tmp/alloc/task", "/home/user/..", "/home"},
}
for _, c := range cases {
t.Run(c.expected, func(t *testing.T) {
require.Equal(t, c.expected, filepath.ToSlash(expandPath(c.base, c.target)))
})
}
}

View File

@ -0,0 +1,34 @@
// +build windows
package docker
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestExpandPath(t *testing.T) {
cases := []struct {
base string
target string
expected string
}{
{"/tmp/alloc/task", ".", "/tmp/alloc/task"},
{"/tmp/alloc/task", "..", "/tmp/alloc"},
{"/tmp/alloc/task", "d1/d2", "/tmp/alloc/task/d1/d2"},
{"/tmp/alloc/task", "../d1/d2", "/tmp/alloc/d1/d2"},
{"/tmp/alloc/task", "../../d1/d2", "/tmp/d1/d2"},
{"/tmp/alloc/task", "c:/home/user", "c:/home/user"},
{"/tmp/alloc/task", "c:/home/user/..", "c:/home"},
}
for _, c := range cases {
t.Run(c.expected, func(t *testing.T) {
require.Equal(t, c.expected, filepath.ToSlash(expandPath(c.base, c.target)))
})
}
}

View File

@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"runtime"
"sync"
"time"
"github.com/hashicorp/consul-template/signals"
@ -14,6 +15,7 @@ import (
"github.com/hashicorp/nomad/client/fingerprint"
"github.com/hashicorp/nomad/drivers/shared/eventer"
"github.com/hashicorp/nomad/drivers/shared/executor"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/plugins/drivers"
"github.com/hashicorp/nomad/plugins/drivers/utils"
@ -100,6 +102,11 @@ type Driver struct {
// logger will log to the Nomad agent
logger hclog.Logger
// A tri-state boolean to know if the fingerprinting has happened and
// whether it has been successful
fingerprintSuccess *bool
fingerprintLock sync.Mutex
}
// TaskConfig is the driver configuration of a task within a job
@ -131,6 +138,28 @@ func NewExecDriver(logger hclog.Logger) drivers.DriverPlugin {
}
}
// setFingerprintSuccess marks the driver as having fingerprinted successfully
func (d *Driver) setFingerprintSuccess() {
d.fingerprintLock.Lock()
d.fingerprintSuccess = helper.BoolToPtr(true)
d.fingerprintLock.Unlock()
}
// setFingerprintFailure marks the driver as having failed fingerprinting
func (d *Driver) setFingerprintFailure() {
d.fingerprintLock.Lock()
d.fingerprintSuccess = helper.BoolToPtr(false)
d.fingerprintLock.Unlock()
}
// fingerprintSuccessful returns true if the driver has
// never fingerprinted or has successfully fingerprinted
func (d *Driver) fingerprintSuccessful() bool {
d.fingerprintLock.Lock()
defer d.fingerprintLock.Unlock()
return d.fingerprintSuccess == nil || *d.fingerprintSuccess
}
func (d *Driver) PluginInfo() (*base.PluginInfoResponse, error) {
return pluginInfo, nil
}
@ -182,6 +211,7 @@ func (d *Driver) handleFingerprint(ctx context.Context, ch chan<- *drivers.Finge
func (d *Driver) buildFingerprint() *drivers.Fingerprint {
if runtime.GOOS != "linux" {
d.setFingerprintFailure()
return &drivers.Fingerprint{
Health: drivers.HealthStateUndetected,
HealthDescription: "exec driver unsupported on client OS",
@ -197,6 +227,7 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
if !utils.IsUnixRoot() {
fp.Health = drivers.HealthStateUndetected
fp.HealthDescription = drivers.DriverRequiresRootMessage
d.setFingerprintFailure()
return fp
}
@ -204,17 +235,22 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
if err != nil {
fp.Health = drivers.HealthStateUnhealthy
fp.HealthDescription = drivers.NoCgroupMountMessage
d.logger.Warn(fp.HealthDescription, "error", err)
if d.fingerprintSuccessful() {
d.logger.Warn(fp.HealthDescription, "error", err)
}
d.setFingerprintFailure()
return fp
}
if mount == "" {
fp.Health = drivers.HealthStateUnhealthy
fp.HealthDescription = drivers.CgroupMountEmpty
d.setFingerprintFailure()
return fp
}
fp.Attributes["driver.exec"] = pstructs.NewBoolAttribute(true)
d.setFingerprintSuccess()
return fp
}

View File

@ -25,7 +25,6 @@ import (
"github.com/hashicorp/nomad/plugins/shared/hclutils"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func TestMain(m *testing.M) {
@ -123,66 +122,6 @@ func TestExecDriver_StartWait(t *testing.T) {
require.NoError(harness.DestroyTask(task.ID, true))
}
func TestExecDriver_StartWaitStop(t *testing.T) {
t.Parallel()
require := require.New(t)
ctestutils.ExecCompatible(t)
d := NewExecDriver(testlog.HCLogger(t))
harness := dtestutil.NewDriverHarness(t, d)
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "test",
Resources: testResources,
}
taskConfig := map[string]interface{}{
"command": "/bin/sleep",
"args": []string{"600"},
}
encodeDriverHelper(require, task, taskConfig)
cleanup := harness.MkAllocDir(task, false)
defer cleanup()
handle, _, err := harness.StartTask(task)
require.NoError(err)
ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
require.NoError(err)
require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second))
go func() {
harness.StopTask(task.ID, 2*time.Second, "SIGINT")
}()
select {
case result := <-ch:
require.Equal(int(unix.SIGINT), result.Signal)
case <-time.After(10 * time.Second):
require.Fail("timeout waiting for task to shutdown")
}
// Ensure that the task is marked as dead, but account
// for WaitTask() closing channel before internal state is updated
testutil.WaitForResult(func() (bool, error) {
status, err := harness.InspectTask(task.ID)
if err != nil {
return false, fmt.Errorf("inspecting task failed: %v", err)
}
if status.State != drivers.TaskStateExited {
return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State)
}
return true, nil
}, func(err error) {
require.NoError(err)
})
require.NoError(harness.DestroyTask(task.ID, true))
}
func TestExecDriver_StartWaitStopKill(t *testing.T) {
t.Parallel()
require := require.New(t)

View File

@ -0,0 +1,79 @@
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package exec
import (
"context"
"fmt"
"testing"
"time"
ctestutils "github.com/hashicorp/nomad/client/testutil"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/plugins/drivers"
dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func TestExecDriver_StartWaitStop(t *testing.T) {
t.Parallel()
require := require.New(t)
ctestutils.ExecCompatible(t)
d := NewExecDriver(testlog.HCLogger(t))
harness := dtestutil.NewDriverHarness(t, d)
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "test",
Resources: testResources,
}
taskConfig := map[string]interface{}{
"command": "/bin/sleep",
"args": []string{"600"},
}
encodeDriverHelper(require, task, taskConfig)
cleanup := harness.MkAllocDir(task, false)
defer cleanup()
handle, _, err := harness.StartTask(task)
require.NoError(err)
ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
require.NoError(err)
require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second))
go func() {
harness.StopTask(task.ID, 2*time.Second, "SIGINT")
}()
select {
case result := <-ch:
require.Equal(int(unix.SIGINT), result.Signal)
case <-time.After(10 * time.Second):
require.Fail("timeout waiting for task to shutdown")
}
// Ensure that the task is marked as dead, but account
// for WaitTask() closing channel before internal state is updated
testutil.WaitForResult(func() (bool, error) {
status, err := harness.InspectTask(task.ID)
if err != nil {
return false, fmt.Errorf("inspecting task failed: %v", err)
}
if status.State != drivers.TaskStateExited {
return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State)
}
return true, nil
}, func(err error) {
require.NoError(err)
})
require.NoError(harness.DestroyTask(task.ID, true))
}

View File

@ -76,7 +76,7 @@ var (
"exit_code": hclspec.NewAttr("exit_code", "number", false),
"exit_signal": hclspec.NewAttr("exit_signal", "number", false),
"exit_err_msg": hclspec.NewAttr("exit_err_msg", "string", false),
"signal_err": hclspec.NewAttr("signal_err", "string", false),
"signal_error": hclspec.NewAttr("signal_error", "string", false),
"driver_ip": hclspec.NewAttr("driver_ip", "string", false),
"driver_advertise": hclspec.NewAttr("driver_advertise", "bool", false),
"driver_port_map": hclspec.NewAttr("driver_port_map", "string", false),
@ -88,7 +88,7 @@ var (
// capabilities is returned by the Capabilities RPC and indicates what
// optional features this driver supports
capabilities = &drivers.Capabilities{
SendSignals: false,
SendSignals: true,
Exec: true,
FSIsolation: drivers.FSIsolationNone,
}

View File

@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strconv"
"sync"
"syscall"
@ -25,7 +26,6 @@ import (
pstructs "github.com/hashicorp/nomad/plugins/shared/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func TestMain(m *testing.M) {
@ -164,73 +164,6 @@ func TestRawExecDriver_StartWait(t *testing.T) {
require.NoError(harness.DestroyTask(task.ID, true))
}
func TestRawExecDriver_StartWaitStop(t *testing.T) {
t.Parallel()
require := require.New(t)
d := NewRawExecDriver(testlog.HCLogger(t))
harness := dtestutil.NewDriverHarness(t, d)
defer harness.Kill()
// Disable cgroups so test works without root
config := &Config{NoCgroups: true}
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
bconfig := &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "test",
}
taskConfig := map[string]interface{}{}
taskConfig["command"] = testtask.Path()
taskConfig["args"] = []string{"sleep", "100s"}
encodeDriverHelper(require, task, taskConfig)
cleanup := harness.MkAllocDir(task, false)
defer cleanup()
handle, _, err := harness.StartTask(task)
require.NoError(err)
ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
require.NoError(err)
require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second))
go func() {
harness.StopTask(task.ID, 2*time.Second, "SIGINT")
}()
select {
case result := <-ch:
require.Equal(int(unix.SIGINT), result.Signal)
case <-time.After(10 * time.Second):
require.Fail("timeout waiting for task to shutdown")
}
// Ensure that the task is marked as dead, but account
// for WaitTask() closing channel before internal state is updated
testutil.WaitForResult(func() (bool, error) {
status, err := harness.InspectTask(task.ID)
if err != nil {
return false, fmt.Errorf("inspecting task failed: %v", err)
}
if status.State != drivers.TaskStateExited {
return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State)
}
return true, nil
}, func(err error) {
require.NoError(err)
})
require.NoError(harness.DestroyTask(task.ID, true))
}
func TestRawExecDriver_StartWaitRecoverWaitStop(t *testing.T) {
t.Parallel()
require := require.New(t)
@ -312,7 +245,6 @@ func TestRawExecDriver_StartWaitRecoverWaitStop(t *testing.T) {
wg.Wait()
require.NoError(d.DestroyTask(task.ID, false))
require.True(waitDone)
}
func TestRawExecDriver_Start_Wait_AllocDir(t *testing.T) {
@ -483,17 +415,31 @@ func TestRawExecDriver_Exec(t *testing.T) {
_, _, err := harness.StartTask(task)
require.NoError(err)
// Exec a command that should work
res, err := harness.ExecTask(task.ID, []string{"/usr/bin/stat", "/tmp"}, 1*time.Second)
require.NoError(err)
require.True(res.ExitResult.Successful())
require.True(len(res.Stdout) > 100)
if runtime.GOOS == "windows" {
// Exec a command that should work
res, err := harness.ExecTask(task.ID, []string{"cmd.exe", "/c", "echo", "hello"}, 1*time.Second)
require.NoError(err)
require.True(res.ExitResult.Successful())
require.Equal(string(res.Stdout), "hello\r\n")
// Exec a command that should fail
res, err = harness.ExecTask(task.ID, []string{"/usr/bin/stat", "notarealfile123abc"}, 1*time.Second)
require.NoError(err)
require.False(res.ExitResult.Successful())
require.Contains(string(res.Stdout), "No such file or directory")
// Exec a command that should fail
res, err = harness.ExecTask(task.ID, []string{"cmd.exe", "/c", "stat", "notarealfile123abc"}, 1*time.Second)
require.NoError(err)
require.False(res.ExitResult.Successful())
require.Contains(string(res.Stdout), "not recognized")
} else {
// Exec a command that should work
res, err := harness.ExecTask(task.ID, []string{"/usr/bin/stat", "/tmp"}, 1*time.Second)
require.NoError(err)
require.True(res.ExitResult.Successful())
require.True(len(res.Stdout) > 100)
// Exec a command that should fail
res, err = harness.ExecTask(task.ID, []string{"/usr/bin/stat", "notarealfile123abc"}, 1*time.Second)
require.NoError(err)
require.False(res.ExitResult.Successful())
require.Contains(string(res.Stdout), "No such file or directory")
}
require.NoError(harness.DestroyTask(task.ID, true))
}

View File

@ -16,10 +16,12 @@ import (
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/helper/testtask"
"github.com/hashicorp/nomad/helper/uuid"
basePlug "github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/plugins/drivers"
dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func TestRawExecDriver_User(t *testing.T) {
@ -128,3 +130,70 @@ done
return true, nil
}, func(err error) { require.NoError(err) })
}
func TestRawExecDriver_StartWaitStop(t *testing.T) {
t.Parallel()
require := require.New(t)
d := NewRawExecDriver(testlog.HCLogger(t))
harness := dtestutil.NewDriverHarness(t, d)
defer harness.Kill()
// Disable cgroups so test works without root
config := &Config{NoCgroups: true}
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
bconfig := &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "test",
}
taskConfig := map[string]interface{}{}
taskConfig["command"] = testtask.Path()
taskConfig["args"] = []string{"sleep", "100s"}
encodeDriverHelper(require, task, taskConfig)
cleanup := harness.MkAllocDir(task, false)
defer cleanup()
handle, _, err := harness.StartTask(task)
require.NoError(err)
ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
require.NoError(err)
require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second))
go func() {
harness.StopTask(task.ID, 2*time.Second, "SIGINT")
}()
select {
case result := <-ch:
require.Equal(int(unix.SIGINT), result.Signal)
case <-time.After(10 * time.Second):
require.Fail("timeout waiting for task to shutdown")
}
// Ensure that the task is marked as dead, but account
// for WaitTask() closing channel before internal state is updated
testutil.WaitForResult(func() (bool, error) {
status, err := harness.InspectTask(task.ID)
if err != nil {
return false, fmt.Errorf("inspecting task failed: %v", err)
}
if status.State != drivers.TaskStateExited {
return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State)
}
return true, nil
}, func(err error) {
require.NoError(err)
})
require.NoError(harness.DestroyTask(task.ID, true))
}

View File

@ -28,6 +28,7 @@ import (
"github.com/hashicorp/nomad/client/taskenv"
"github.com/hashicorp/nomad/drivers/shared/eventer"
"github.com/hashicorp/nomad/drivers/shared/executor"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/plugins/drivers"
"github.com/hashicorp/nomad/plugins/shared"
@ -197,9 +198,10 @@ type Driver struct {
// logger will log to the Nomad agent
logger hclog.Logger
// hasFingerprinted is used to store whether we have fingerprinted before
hasFingerprinted bool
fingerprintLock sync.Mutex
// A tri-state boolean to know if the fingerprinting has happened and
// whether it has been successful
fingerprintSuccess *bool
fingerprintLock sync.Mutex
}
func NewRktDriver(logger hclog.Logger) drivers.DriverPlugin {
@ -268,25 +270,29 @@ func (d *Driver) handleFingerprint(ctx context.Context, ch chan *drivers.Fingerp
}
}
// setFingerprinted marks the driver as having fingerprinted once before
func (d *Driver) setFingerprinted() {
// setFingerprintSuccess marks the driver as having fingerprinted successfully
func (d *Driver) setFingerprintSuccess() {
d.fingerprintLock.Lock()
d.hasFingerprinted = true
d.fingerprintSuccess = helper.BoolToPtr(true)
d.fingerprintLock.Unlock()
}
// fingerprinted returns whether the driver has fingerprinted before
func (d *Driver) fingerprinted() bool {
// setFingerprintFailure marks the driver as having failed fingerprinting
func (d *Driver) setFingerprintFailure() {
d.fingerprintLock.Lock()
d.fingerprintSuccess = helper.BoolToPtr(false)
d.fingerprintLock.Unlock()
}
// fingerprintSuccessful returns true if the driver has
// never fingerprinted or has successfully fingerprinted
func (d *Driver) fingerprintSuccessful() bool {
d.fingerprintLock.Lock()
defer d.fingerprintLock.Unlock()
return d.hasFingerprinted
return d.fingerprintSuccess == nil || *d.fingerprintSuccess
}
func (d *Driver) buildFingerprint() *drivers.Fingerprint {
defer func() {
d.setFingerprinted()
}()
fingerprint := &drivers.Fingerprint{
Attributes: map[string]*pstructs.Attribute{},
Health: drivers.HealthStateHealthy,
@ -295,9 +301,10 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
// Only enable if we are root
if syscall.Geteuid() != 0 {
if !d.fingerprinted() {
if d.fingerprintSuccessful() {
d.logger.Debug("must run as root user, disabling")
}
d.setFingerprintFailure()
fingerprint.Health = drivers.HealthStateUndetected
fingerprint.HealthDescription = drivers.DriverRequiresRootMessage
return fingerprint
@ -307,6 +314,7 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
if err != nil {
fingerprint.Health = drivers.HealthStateUndetected
fingerprint.HealthDescription = fmt.Sprintf("Failed to execute %s version: %v", rktCmd, err)
d.setFingerprintFailure()
return fingerprint
}
out := strings.TrimSpace(string(outBytes))
@ -316,6 +324,7 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
if len(rktMatches) != 2 || len(appcMatches) != 2 {
fingerprint.Health = drivers.HealthStateUndetected
fingerprint.HealthDescription = "Unable to parse rkt version string"
d.setFingerprintFailure()
return fingerprint
}
@ -325,10 +334,11 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
// Do not allow ancient rkt versions
fingerprint.Health = drivers.HealthStateUndetected
fingerprint.HealthDescription = fmt.Sprintf("Unsuported rkt version %s", currentVersion)
if !d.fingerprinted() {
if d.fingerprintSuccessful() {
d.logger.Warn("unsupported rkt version please upgrade to >= "+minVersion.String(),
"rkt_version", currentVersion)
}
d.setFingerprintFailure()
return fingerprint
}
@ -338,7 +348,7 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint {
if d.config.VolumesEnabled {
fingerprint.Attributes["driver.rkt.volumes.enabled"] = pstructs.NewBoolAttribute(true)
}
d.setFingerprintSuccess()
return fingerprint
}
@ -561,7 +571,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
prepareArgs = append(prepareArgs, fmt.Sprintf("--memory=%v", cfg.Resources.LinuxResources.MemoryLimitBytes))
// Add CPU isolator
prepareArgs = append(prepareArgs, fmt.Sprintf("--cpu-shares=%v", cfg.Resources.LinuxResources.CPUShares))
prepareArgs = append(prepareArgs, fmt.Sprintf("--cpu=%v", cfg.Resources.LinuxResources.CPUShares))
// Add DNS servers
if len(driverConfig.DNSServers) == 1 && (driverConfig.DNSServers[0] == "host" || driverConfig.DNSServers[0] == "none") {

View File

@ -39,7 +39,7 @@ const (
var (
// ExecutorCgroupMeasuredMemStats is the list of memory stats captured by the executor
ExecutorCgroupMeasuredMemStats = []string{"RSS", "Cache", "Swap", "Max Usage", "Kernel Usage", "Kernel Max Usage"}
ExecutorCgroupMeasuredMemStats = []string{"RSS", "Cache", "Swap", "Usage", "Max Usage", "Kernel Usage", "Kernel Max Usage"}
// ExecutorCgroupMeasuredCpuStats is the list of CPU stats captures by the executor
ExecutorCgroupMeasuredCpuStats = []string{"System Mode", "User Mode", "Throttled Periods", "Throttled Time", "Percent"}
@ -400,6 +400,7 @@ func (l *LibcontainerExecutor) handleStats(ch chan *cstructs.TaskResourceUsage,
RSS: rss,
Cache: cache,
Swap: swap.Usage,
Usage: stats.MemoryStats.Usage.Usage,
MaxUsage: maxUsage,
KernelUsage: stats.MemoryStats.KernelUsage.Usage,
KernelMaxUsage: stats.MemoryStats.KernelUsage.MaxUsage,

View File

@ -4,8 +4,6 @@ import (
"crypto/sha512"
"fmt"
"regexp"
"strconv"
"strings"
"time"
multierror "github.com/hashicorp/go-multierror"
@ -359,24 +357,3 @@ func CheckHCLKeys(node ast.Node, valid []string) error {
return result
}
// FormatFloat converts the floating-point number f to a string,
// after rounding it to the passed unit.
//
// Uses 'f' format (-ddd.dddddd, no exponent), and uses at most
// maxPrec digits after the decimal point.
func FormatFloat(f float64, maxPrec int) string {
v := strconv.FormatFloat(f, 'f', -1, 64)
idx := strings.LastIndex(v, ".")
if idx == -1 {
return v
}
sublen := idx + maxPrec + 1
if sublen > len(v) {
sublen = len(v)
}
return v[:sublen]
}

View File

@ -4,8 +4,6 @@ import (
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/require"
)
func TestSliceStringIsSubset(t *testing.T) {
@ -89,35 +87,3 @@ func BenchmarkCleanEnvVar(b *testing.B) {
CleanEnvVar(in, replacement)
}
}
func TestFormatRoundedFloat(t *testing.T) {
cases := []struct {
input float64
expected string
}{
{
1323,
"1323",
},
{
10.321,
"10.321",
},
{
100000.31324324,
"100000.313",
},
{
100000.3,
"100000.3",
},
{
0.7654321,
"0.765",
},
}
for _, c := range cases {
require.Equal(t, c.expected, FormatFloat(c.input, 3))
}
}

View File

@ -7,8 +7,6 @@ import (
"io/ioutil"
"os"
"os/exec"
"strconv"
"syscall"
"time"
"github.com/hashicorp/nomad/nomad/structs"
@ -115,21 +113,11 @@ func execute() {
ioutil.WriteFile(file, []byte(msg), 0666)
case "pgrp":
// pgrp <group_int> puts the pid in a new process group
if len(args) < 1 {
fmt.Fprintln(os.Stderr, "expected process group number for pgrp")
os.Exit(1)
}
num := popArg()
grp, err := strconv.Atoi(num)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to convert process group number %q: %v\n", num, err)
os.Exit(1)
}
if err := syscall.Setpgid(0, grp); err != nil {
fmt.Fprintf(os.Stderr, "failed to set process group: %v\n", err)
os.Exit(1)
}
executeProcessGroup(popArg())
case "fork/exec":
// fork/exec <pid_file> <args> forks execs the helper process

View File

@ -0,0 +1,23 @@
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package testtask
import (
"fmt"
"os"
"strconv"
"syscall"
)
func executeProcessGroup(gid string) {
// pgrp <group_int> puts the pid in a new process group
grp, err := strconv.Atoi(gid)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to convert process group number %q: %v\n", gid, err)
os.Exit(1)
}
if err := syscall.Setpgid(0, grp); err != nil {
fmt.Fprintf(os.Stderr, "failed to set process group: %v\n", err)
os.Exit(1)
}
}

View File

@ -0,0 +1,14 @@
// +build windows
package testtask
import (
"fmt"
"os"
)
func executeProcessGroup(gid string) {
fmt.Fprintf(os.Stderr, "TODO: implement process groups are on windows\n")
fmt.Fprintf(os.Stderr, "TODO: see https://github.com/hashicorp/nomad/blob/109c5ef650206fc62334d202002cda92ceb67399/drivers/shared/executor/executor_windows.go#L9-L17\n")
os.Exit(1)
}

View File

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/kr/pretty"
"github.com/stretchr/testify/require"
)
@ -196,7 +197,19 @@ func TestClientAllocations_GarbageCollectAll_Remote(t *testing.T) {
testutil.WaitForResult(func() (bool, error) {
nodes := s2.connectedNodes()
return len(nodes) == 1, nil
if len(nodes) != 1 {
return false, fmt.Errorf("should have 1 client. found %d", len(nodes))
}
req := &structs.NodeSpecificRequest{
NodeID: c.NodeID(),
QueryOptions: structs.QueryOptions{Region: "global"},
}
resp := structs.SingleNodeResponse{}
if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp); err != nil {
return false, err
}
return resp.Node != nil && resp.Node.Status == structs.NodeStatusReady, fmt.Errorf(
"expected ready but found %s", pretty.Sprint(resp.Node))
}, func(err error) {
t.Fatalf("should have a clients")
})
@ -442,7 +455,19 @@ func TestClientAllocations_GarbageCollect_Remote(t *testing.T) {
}
testutil.WaitForResult(func() (bool, error) {
nodes := s2.connectedNodes()
return len(nodes) == 1, nil
if len(nodes) != 1 {
return false, fmt.Errorf("should have 1 client. found %d", len(nodes))
}
req := &structs.NodeSpecificRequest{
NodeID: c.NodeID(),
QueryOptions: structs.QueryOptions{Region: "global"},
}
resp := structs.SingleNodeResponse{}
if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp); err != nil {
return false, err
}
return resp.Node != nil && resp.Node.Status == structs.NodeStatusReady, fmt.Errorf(
"expected ready but found %s", pretty.Sprint(resp.Node))
}, func(err error) {
t.Fatalf("should have a clients")
})

View File

@ -1401,6 +1401,11 @@ func (n *nomadFSM) reconcileQueuedAllocations(index uint64) error {
break
}
job := rawJob.(*structs.Job)
// Nothing to do for queued allocations if the job is a parent periodic/parameterized job
if job.IsParameterized() || job.IsPeriodic() {
continue
}
planner := &scheduler.Harness{
State: &snap.StateStore,
}

View File

@ -2816,6 +2816,78 @@ func TestFSM_ReconcileSummaries(t *testing.T) {
}
}
// COMPAT: Remove in 0.11
func TestFSM_ReconcileParentJobSummary(t *testing.T) {
// This test exercises code to handle https://github.com/hashicorp/nomad/issues/3886
t.Parallel()
require := require.New(t)
// Add some state
fsm := testFSM(t)
state := fsm.State()
// Add a node
node := mock.Node()
state.UpsertNode(800, node)
// Make a parameterized job
job1 := mock.BatchJob()
job1.ID = "test"
job1.ParameterizedJob = &structs.ParameterizedJobConfig{
Payload: "random",
}
job1.TaskGroups[0].Count = 1
state.UpsertJob(1000, job1)
// Make a child job
childJob := job1.Copy()
childJob.ID = job1.ID + "dispatch-23423423"
childJob.ParentID = job1.ID
childJob.Dispatched = true
childJob.Status = structs.JobStatusRunning
// Create an alloc for child job
alloc := mock.Alloc()
alloc.NodeID = node.ID
alloc.Job = childJob
alloc.JobID = childJob.ID
alloc.ClientStatus = structs.AllocClientStatusRunning
state.UpsertJob(1010, childJob)
state.UpsertAllocs(1011, []*structs.Allocation{alloc})
// Make the summary incorrect in the state store
summary, err := state.JobSummaryByID(nil, job1.Namespace, job1.ID)
require.Nil(err)
summary.Children = nil
summary.Summary = make(map[string]structs.TaskGroupSummary)
summary.Summary["web"] = structs.TaskGroupSummary{
Queued: 1,
}
req := structs.GenericRequest{}
buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req)
require.Nil(err)
resp := fsm.Apply(makeLog(buf))
require.Nil(resp)
ws := memdb.NewWatchSet()
out1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID)
expected := structs.JobSummary{
JobID: job1.ID,
Namespace: job1.Namespace,
Summary: make(map[string]structs.TaskGroupSummary),
CreateIndex: 1000,
ModifyIndex: out1.ModifyIndex,
Children: &structs.JobChildrenSummary{
Running: 1,
},
}
require.Equal(&expected, out1)
}
func TestFSM_LeakedDeployments(t *testing.T) {
t.Parallel()
require := require.New(t)

View File

@ -6,6 +6,8 @@ import (
"sort"
"time"
"reflect"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-memdb"
multierror "github.com/hashicorp/go-multierror"
@ -3049,12 +3051,86 @@ func (s *StateStore) ReconcileJobSummaries(index uint64) error {
if err != nil {
return err
}
// COMPAT: Remove after 0.11
// Iterate over jobs to build a list of parent jobs and their children
parentMap := make(map[string][]*structs.Job)
for {
rawJob := iter.Next()
if rawJob == nil {
break
}
job := rawJob.(*structs.Job)
if job.ParentID != "" {
children := parentMap[job.ParentID]
children = append(children, job)
parentMap[job.ParentID] = children
}
}
// Get all the jobs again
iter, err = txn.Get("jobs", "id")
if err != nil {
return err
}
for {
rawJob := iter.Next()
if rawJob == nil {
break
}
job := rawJob.(*structs.Job)
if job.IsParameterized() || job.IsPeriodic() {
// COMPAT: Remove after 0.11
// The following block of code fixes incorrect child summaries due to a bug
// See https://github.com/hashicorp/nomad/issues/3886 for details
rawSummary, err := txn.First("job_summary", "id", job.Namespace, job.ID)
if err != nil {
return err
}
if rawSummary == nil {
continue
}
oldSummary := rawSummary.(*structs.JobSummary)
// Create an empty summary
summary := &structs.JobSummary{
JobID: job.ID,
Namespace: job.Namespace,
Summary: make(map[string]structs.TaskGroupSummary),
Children: &structs.JobChildrenSummary{},
}
// Iterate over children of this job if any to fix summary counts
children := parentMap[job.ID]
for _, childJob := range children {
switch childJob.Status {
case structs.JobStatusPending:
summary.Children.Pending++
case structs.JobStatusDead:
summary.Children.Dead++
case structs.JobStatusRunning:
summary.Children.Running++
}
}
// Insert the job summary if its different
if !reflect.DeepEqual(summary, oldSummary) {
// Set the create index of the summary same as the job's create index
// and the modify index to the current index
summary.CreateIndex = job.CreateIndex
summary.ModifyIndex = index
if err := txn.Insert("job_summary", summary); err != nil {
return fmt.Errorf("error inserting job summary: %v", err)
}
}
// Done with handling a parent job, continue to next
continue
}
// Create a job summary for the job
summary := &structs.JobSummary{

View File

@ -4354,6 +4354,95 @@ func TestStateStore_ReconcileJobSummary(t *testing.T) {
}
}
func TestStateStore_ReconcileParentJobSummary(t *testing.T) {
t.Parallel()
require := require.New(t)
state := testStateStore(t)
// Add a node
node := mock.Node()
state.UpsertNode(80, node)
// Make a parameterized job
job1 := mock.BatchJob()
job1.ID = "test"
job1.ParameterizedJob = &structs.ParameterizedJobConfig{
Payload: "random",
}
job1.TaskGroups[0].Count = 1
state.UpsertJob(100, job1)
// Make a child job
childJob := job1.Copy()
childJob.ID = job1.ID + "dispatch-23423423"
childJob.ParentID = job1.ID
childJob.Dispatched = true
childJob.Status = structs.JobStatusRunning
// Make some allocs for child job
alloc := mock.Alloc()
alloc.NodeID = node.ID
alloc.Job = childJob
alloc.JobID = childJob.ID
alloc.ClientStatus = structs.AllocClientStatusRunning
alloc2 := mock.Alloc()
alloc2.NodeID = node.ID
alloc2.Job = childJob
alloc2.JobID = childJob.ID
alloc2.ClientStatus = structs.AllocClientStatusFailed
require.Nil(state.UpsertJob(110, childJob))
require.Nil(state.UpsertAllocs(111, []*structs.Allocation{alloc, alloc2}))
// Make the summary incorrect in the state store
summary, err := state.JobSummaryByID(nil, job1.Namespace, job1.ID)
require.Nil(err)
summary.Children = nil
summary.Summary = make(map[string]structs.TaskGroupSummary)
summary.Summary["web"] = structs.TaskGroupSummary{
Queued: 1,
}
// Delete the child job summary
state.DeleteJobSummary(125, childJob.Namespace, childJob.ID)
state.ReconcileJobSummaries(120)
ws := memdb.NewWatchSet()
// Verify parent summary is corrected
summary, _ = state.JobSummaryByID(ws, alloc.Namespace, job1.ID)
expectedSummary := structs.JobSummary{
JobID: job1.ID,
Namespace: job1.Namespace,
Summary: make(map[string]structs.TaskGroupSummary),
Children: &structs.JobChildrenSummary{
Running: 1,
},
CreateIndex: 100,
ModifyIndex: 120,
}
require.Equal(&expectedSummary, summary)
// Verify child job summary is also correct
childSummary, _ := state.JobSummaryByID(ws, childJob.Namespace, childJob.ID)
expectedChildSummary := structs.JobSummary{
JobID: childJob.ID,
Namespace: childJob.Namespace,
Summary: map[string]structs.TaskGroupSummary{
"web": {
Running: 1,
Failed: 1,
},
},
CreateIndex: 110,
ModifyIndex: 120,
}
require.Equal(&expectedChildSummary, childSummary)
}
func TestStateStore_UpdateAlloc_JobNotPresent(t *testing.T) {
state := testStateStore(t)

View File

@ -4017,6 +4017,9 @@ func (d *DispatchPayloadConfig) Validate() error {
}
var (
// These default restart policies needs to be in sync with
// Canonicalize in api/tasks.go
DefaultServiceJobRestartPolicy = RestartPolicy{
Delay: 15 * time.Second,
Attempts: 2,
@ -4032,6 +4035,9 @@ var (
)
var (
// These default reschedule policies needs to be in sync with
// NewDefaultReschedulePolicy in api/tasks.go
DefaultServiceJobReschedulePolicy = ReschedulePolicy{
Delay: 30 * time.Second,
DelayFunction: "exponential",

View File

@ -48,7 +48,7 @@ func (x PluginType) String() string {
return proto.EnumName(PluginType_name, int32(x))
}
func (PluginType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{0}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{0}
}
// PluginInfoRequest is used to request the plugins basic information.
@ -62,7 +62,7 @@ func (m *PluginInfoRequest) Reset() { *m = PluginInfoRequest{} }
func (m *PluginInfoRequest) String() string { return proto.CompactTextString(m) }
func (*PluginInfoRequest) ProtoMessage() {}
func (*PluginInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{0}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{0}
}
func (m *PluginInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PluginInfoRequest.Unmarshal(m, b)
@ -104,7 +104,7 @@ func (m *PluginInfoResponse) Reset() { *m = PluginInfoResponse{} }
func (m *PluginInfoResponse) String() string { return proto.CompactTextString(m) }
func (*PluginInfoResponse) ProtoMessage() {}
func (*PluginInfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{1}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{1}
}
func (m *PluginInfoResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PluginInfoResponse.Unmarshal(m, b)
@ -163,7 +163,7 @@ func (m *ConfigSchemaRequest) Reset() { *m = ConfigSchemaRequest{} }
func (m *ConfigSchemaRequest) String() string { return proto.CompactTextString(m) }
func (*ConfigSchemaRequest) ProtoMessage() {}
func (*ConfigSchemaRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{2}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{2}
}
func (m *ConfigSchemaRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConfigSchemaRequest.Unmarshal(m, b)
@ -196,7 +196,7 @@ func (m *ConfigSchemaResponse) Reset() { *m = ConfigSchemaResponse{} }
func (m *ConfigSchemaResponse) String() string { return proto.CompactTextString(m) }
func (*ConfigSchemaResponse) ProtoMessage() {}
func (*ConfigSchemaResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{3}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{3}
}
func (m *ConfigSchemaResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConfigSchemaResponse.Unmarshal(m, b)
@ -240,7 +240,7 @@ func (m *SetConfigRequest) Reset() { *m = SetConfigRequest{} }
func (m *SetConfigRequest) String() string { return proto.CompactTextString(m) }
func (*SetConfigRequest) ProtoMessage() {}
func (*SetConfigRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{4}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{4}
}
func (m *SetConfigRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetConfigRequest.Unmarshal(m, b)
@ -294,7 +294,7 @@ func (m *NomadConfig) Reset() { *m = NomadConfig{} }
func (m *NomadConfig) String() string { return proto.CompactTextString(m) }
func (*NomadConfig) ProtoMessage() {}
func (*NomadConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{5}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{5}
}
func (m *NomadConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NomadConfig.Unmarshal(m, b)
@ -339,7 +339,7 @@ func (m *NomadDriverConfig) Reset() { *m = NomadDriverConfig{} }
func (m *NomadDriverConfig) String() string { return proto.CompactTextString(m) }
func (*NomadDriverConfig) ProtoMessage() {}
func (*NomadDriverConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{6}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{6}
}
func (m *NomadDriverConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NomadDriverConfig.Unmarshal(m, b)
@ -384,7 +384,7 @@ func (m *SetConfigResponse) Reset() { *m = SetConfigResponse{} }
func (m *SetConfigResponse) String() string { return proto.CompactTextString(m) }
func (*SetConfigResponse) ProtoMessage() {}
func (*SetConfigResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_base_f2480776612a8fbd, []int{7}
return fileDescriptor_base_6a1a5ff99a0b9e5d, []int{7}
}
func (m *SetConfigResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetConfigResponse.Unmarshal(m, b)
@ -560,9 +560,9 @@ var _BasePlugin_serviceDesc = grpc.ServiceDesc{
Metadata: "plugins/base/proto/base.proto",
}
func init() { proto.RegisterFile("plugins/base/proto/base.proto", fileDescriptor_base_f2480776612a8fbd) }
func init() { proto.RegisterFile("plugins/base/proto/base.proto", fileDescriptor_base_6a1a5ff99a0b9e5d) }
var fileDescriptor_base_f2480776612a8fbd = []byte{
var fileDescriptor_base_6a1a5ff99a0b9e5d = []byte{
// 535 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x41, 0x8f, 0x12, 0x4d,
0x10, 0xdd, 0x01, 0x3e, 0x36, 0x14, 0xb0, 0x81, 0xe6, 0x33, 0x21, 0x24, 0x26, 0x64, 0xa2, 0x09,

View File

@ -37,7 +37,7 @@ func (m *FingerprintRequest) Reset() { *m = FingerprintRequest{} }
func (m *FingerprintRequest) String() string { return proto.CompactTextString(m) }
func (*FingerprintRequest) ProtoMessage() {}
func (*FingerprintRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{0}
return fileDescriptor_device_a4d1cccedbd8401c, []int{0}
}
func (m *FingerprintRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FingerprintRequest.Unmarshal(m, b)
@ -72,7 +72,7 @@ func (m *FingerprintResponse) Reset() { *m = FingerprintResponse{} }
func (m *FingerprintResponse) String() string { return proto.CompactTextString(m) }
func (*FingerprintResponse) ProtoMessage() {}
func (*FingerprintResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{1}
return fileDescriptor_device_a4d1cccedbd8401c, []int{1}
}
func (m *FingerprintResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FingerprintResponse.Unmarshal(m, b)
@ -121,7 +121,7 @@ func (m *DeviceGroup) Reset() { *m = DeviceGroup{} }
func (m *DeviceGroup) String() string { return proto.CompactTextString(m) }
func (*DeviceGroup) ProtoMessage() {}
func (*DeviceGroup) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{2}
return fileDescriptor_device_a4d1cccedbd8401c, []int{2}
}
func (m *DeviceGroup) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeviceGroup.Unmarshal(m, b)
@ -198,7 +198,7 @@ func (m *DetectedDevice) Reset() { *m = DetectedDevice{} }
func (m *DetectedDevice) String() string { return proto.CompactTextString(m) }
func (*DetectedDevice) ProtoMessage() {}
func (*DetectedDevice) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{3}
return fileDescriptor_device_a4d1cccedbd8401c, []int{3}
}
func (m *DetectedDevice) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DetectedDevice.Unmarshal(m, b)
@ -260,7 +260,7 @@ func (m *DeviceLocality) Reset() { *m = DeviceLocality{} }
func (m *DeviceLocality) String() string { return proto.CompactTextString(m) }
func (*DeviceLocality) ProtoMessage() {}
func (*DeviceLocality) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{4}
return fileDescriptor_device_a4d1cccedbd8401c, []int{4}
}
func (m *DeviceLocality) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeviceLocality.Unmarshal(m, b)
@ -301,7 +301,7 @@ func (m *ReserveRequest) Reset() { *m = ReserveRequest{} }
func (m *ReserveRequest) String() string { return proto.CompactTextString(m) }
func (*ReserveRequest) ProtoMessage() {}
func (*ReserveRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{5}
return fileDescriptor_device_a4d1cccedbd8401c, []int{5}
}
func (m *ReserveRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReserveRequest.Unmarshal(m, b)
@ -344,7 +344,7 @@ func (m *ReserveResponse) Reset() { *m = ReserveResponse{} }
func (m *ReserveResponse) String() string { return proto.CompactTextString(m) }
func (*ReserveResponse) ProtoMessage() {}
func (*ReserveResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{6}
return fileDescriptor_device_a4d1cccedbd8401c, []int{6}
}
func (m *ReserveResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReserveResponse.Unmarshal(m, b)
@ -389,7 +389,7 @@ func (m *ContainerReservation) Reset() { *m = ContainerReservation{} }
func (m *ContainerReservation) String() string { return proto.CompactTextString(m) }
func (*ContainerReservation) ProtoMessage() {}
func (*ContainerReservation) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{7}
return fileDescriptor_device_a4d1cccedbd8401c, []int{7}
}
func (m *ContainerReservation) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContainerReservation.Unmarshal(m, b)
@ -448,7 +448,7 @@ func (m *Mount) Reset() { *m = Mount{} }
func (m *Mount) String() string { return proto.CompactTextString(m) }
func (*Mount) ProtoMessage() {}
func (*Mount) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{8}
return fileDescriptor_device_a4d1cccedbd8401c, []int{8}
}
func (m *Mount) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Mount.Unmarshal(m, b)
@ -509,7 +509,7 @@ func (m *DeviceSpec) Reset() { *m = DeviceSpec{} }
func (m *DeviceSpec) String() string { return proto.CompactTextString(m) }
func (*DeviceSpec) ProtoMessage() {}
func (*DeviceSpec) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{9}
return fileDescriptor_device_a4d1cccedbd8401c, []int{9}
}
func (m *DeviceSpec) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeviceSpec.Unmarshal(m, b)
@ -563,7 +563,7 @@ func (m *StatsRequest) Reset() { *m = StatsRequest{} }
func (m *StatsRequest) String() string { return proto.CompactTextString(m) }
func (*StatsRequest) ProtoMessage() {}
func (*StatsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{10}
return fileDescriptor_device_a4d1cccedbd8401c, []int{10}
}
func (m *StatsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatsRequest.Unmarshal(m, b)
@ -603,7 +603,7 @@ func (m *StatsResponse) Reset() { *m = StatsResponse{} }
func (m *StatsResponse) String() string { return proto.CompactTextString(m) }
func (*StatsResponse) ProtoMessage() {}
func (*StatsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{11}
return fileDescriptor_device_a4d1cccedbd8401c, []int{11}
}
func (m *StatsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatsResponse.Unmarshal(m, b)
@ -647,7 +647,7 @@ func (m *DeviceGroupStats) Reset() { *m = DeviceGroupStats{} }
func (m *DeviceGroupStats) String() string { return proto.CompactTextString(m) }
func (*DeviceGroupStats) ProtoMessage() {}
func (*DeviceGroupStats) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{12}
return fileDescriptor_device_a4d1cccedbd8401c, []int{12}
}
func (m *DeviceGroupStats) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeviceGroupStats.Unmarshal(m, b)
@ -713,7 +713,7 @@ func (m *DeviceStats) Reset() { *m = DeviceStats{} }
func (m *DeviceStats) String() string { return proto.CompactTextString(m) }
func (*DeviceStats) ProtoMessage() {}
func (*DeviceStats) Descriptor() ([]byte, []int) {
return fileDescriptor_device_c21dc006d6a19ae5, []int{13}
return fileDescriptor_device_a4d1cccedbd8401c, []int{13}
}
func (m *DeviceStats) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeviceStats.Unmarshal(m, b)
@ -984,10 +984,10 @@ var _DevicePlugin_serviceDesc = grpc.ServiceDesc{
}
func init() {
proto.RegisterFile("plugins/device/proto/device.proto", fileDescriptor_device_c21dc006d6a19ae5)
proto.RegisterFile("plugins/device/proto/device.proto", fileDescriptor_device_a4d1cccedbd8401c)
}
var fileDescriptor_device_c21dc006d6a19ae5 = []byte{
var fileDescriptor_device_a4d1cccedbd8401c = []byte{
// 979 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xef, 0x8e, 0x1b, 0x35,
0x10, 0x27, 0xb9, 0xcb, 0x25, 0x99, 0xdc, 0x5d, 0x8b, 0x7b, 0x42, 0x61, 0x81, 0xf6, 0x58, 0x09,

View File

@ -50,7 +50,7 @@ func (x TaskState) String() string {
return proto.EnumName(TaskState_name, int32(x))
}
func (TaskState) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{0}
return fileDescriptor_driver_d79b0e12b1c93702, []int{0}
}
type FingerprintResponse_HealthState int32
@ -76,7 +76,7 @@ func (x FingerprintResponse_HealthState) String() string {
return proto.EnumName(FingerprintResponse_HealthState_name, int32(x))
}
func (FingerprintResponse_HealthState) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{5, 0}
return fileDescriptor_driver_d79b0e12b1c93702, []int{5, 0}
}
type StartTaskResponse_Result int32
@ -102,7 +102,7 @@ func (x StartTaskResponse_Result) String() string {
return proto.EnumName(StartTaskResponse_Result_name, int32(x))
}
func (StartTaskResponse_Result) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{9, 0}
return fileDescriptor_driver_d79b0e12b1c93702, []int{9, 0}
}
type DriverCapabilities_FSIsolation int32
@ -128,7 +128,7 @@ func (x DriverCapabilities_FSIsolation) String() string {
return proto.EnumName(DriverCapabilities_FSIsolation_name, int32(x))
}
func (DriverCapabilities_FSIsolation) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{25, 0}
return fileDescriptor_driver_d79b0e12b1c93702, []int{25, 0}
}
type CPUUsage_Fields int32
@ -163,7 +163,7 @@ func (x CPUUsage_Fields) String() string {
return proto.EnumName(CPUUsage_Fields_name, int32(x))
}
func (CPUUsage_Fields) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{43, 0}
return fileDescriptor_driver_d79b0e12b1c93702, []int{43, 0}
}
type MemoryUsage_Fields int32
@ -174,6 +174,7 @@ const (
MemoryUsage_MAX_USAGE MemoryUsage_Fields = 2
MemoryUsage_KERNEL_USAGE MemoryUsage_Fields = 3
MemoryUsage_KERNEL_MAX_USAGE MemoryUsage_Fields = 4
MemoryUsage_USAGE MemoryUsage_Fields = 5
)
var MemoryUsage_Fields_name = map[int32]string{
@ -182,6 +183,7 @@ var MemoryUsage_Fields_name = map[int32]string{
2: "MAX_USAGE",
3: "KERNEL_USAGE",
4: "KERNEL_MAX_USAGE",
5: "USAGE",
}
var MemoryUsage_Fields_value = map[string]int32{
"RSS": 0,
@ -189,13 +191,14 @@ var MemoryUsage_Fields_value = map[string]int32{
"MAX_USAGE": 2,
"KERNEL_USAGE": 3,
"KERNEL_MAX_USAGE": 4,
"USAGE": 5,
}
func (x MemoryUsage_Fields) String() string {
return proto.EnumName(MemoryUsage_Fields_name, int32(x))
}
func (MemoryUsage_Fields) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{44, 0}
return fileDescriptor_driver_d79b0e12b1c93702, []int{44, 0}
}
type TaskConfigSchemaRequest struct {
@ -208,7 +211,7 @@ func (m *TaskConfigSchemaRequest) Reset() { *m = TaskConfigSchemaRequest
func (m *TaskConfigSchemaRequest) String() string { return proto.CompactTextString(m) }
func (*TaskConfigSchemaRequest) ProtoMessage() {}
func (*TaskConfigSchemaRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{0}
return fileDescriptor_driver_d79b0e12b1c93702, []int{0}
}
func (m *TaskConfigSchemaRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskConfigSchemaRequest.Unmarshal(m, b)
@ -240,7 +243,7 @@ func (m *TaskConfigSchemaResponse) Reset() { *m = TaskConfigSchemaRespon
func (m *TaskConfigSchemaResponse) String() string { return proto.CompactTextString(m) }
func (*TaskConfigSchemaResponse) ProtoMessage() {}
func (*TaskConfigSchemaResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{1}
return fileDescriptor_driver_d79b0e12b1c93702, []int{1}
}
func (m *TaskConfigSchemaResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskConfigSchemaResponse.Unmarshal(m, b)
@ -277,7 +280,7 @@ func (m *CapabilitiesRequest) Reset() { *m = CapabilitiesRequest{} }
func (m *CapabilitiesRequest) String() string { return proto.CompactTextString(m) }
func (*CapabilitiesRequest) ProtoMessage() {}
func (*CapabilitiesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{2}
return fileDescriptor_driver_d79b0e12b1c93702, []int{2}
}
func (m *CapabilitiesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CapabilitiesRequest.Unmarshal(m, b)
@ -312,7 +315,7 @@ func (m *CapabilitiesResponse) Reset() { *m = CapabilitiesResponse{} }
func (m *CapabilitiesResponse) String() string { return proto.CompactTextString(m) }
func (*CapabilitiesResponse) ProtoMessage() {}
func (*CapabilitiesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{3}
return fileDescriptor_driver_d79b0e12b1c93702, []int{3}
}
func (m *CapabilitiesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CapabilitiesResponse.Unmarshal(m, b)
@ -349,7 +352,7 @@ func (m *FingerprintRequest) Reset() { *m = FingerprintRequest{} }
func (m *FingerprintRequest) String() string { return proto.CompactTextString(m) }
func (*FingerprintRequest) ProtoMessage() {}
func (*FingerprintRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{4}
return fileDescriptor_driver_d79b0e12b1c93702, []int{4}
}
func (m *FingerprintRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FingerprintRequest.Unmarshal(m, b)
@ -392,7 +395,7 @@ func (m *FingerprintResponse) Reset() { *m = FingerprintResponse{} }
func (m *FingerprintResponse) String() string { return proto.CompactTextString(m) }
func (*FingerprintResponse) ProtoMessage() {}
func (*FingerprintResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{5}
return fileDescriptor_driver_d79b0e12b1c93702, []int{5}
}
func (m *FingerprintResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FingerprintResponse.Unmarshal(m, b)
@ -447,7 +450,7 @@ func (m *RecoverTaskRequest) Reset() { *m = RecoverTaskRequest{} }
func (m *RecoverTaskRequest) String() string { return proto.CompactTextString(m) }
func (*RecoverTaskRequest) ProtoMessage() {}
func (*RecoverTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{6}
return fileDescriptor_driver_d79b0e12b1c93702, []int{6}
}
func (m *RecoverTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RecoverTaskRequest.Unmarshal(m, b)
@ -491,7 +494,7 @@ func (m *RecoverTaskResponse) Reset() { *m = RecoverTaskResponse{} }
func (m *RecoverTaskResponse) String() string { return proto.CompactTextString(m) }
func (*RecoverTaskResponse) ProtoMessage() {}
func (*RecoverTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{7}
return fileDescriptor_driver_d79b0e12b1c93702, []int{7}
}
func (m *RecoverTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RecoverTaskResponse.Unmarshal(m, b)
@ -523,7 +526,7 @@ func (m *StartTaskRequest) Reset() { *m = StartTaskRequest{} }
func (m *StartTaskRequest) String() string { return proto.CompactTextString(m) }
func (*StartTaskRequest) ProtoMessage() {}
func (*StartTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{8}
return fileDescriptor_driver_d79b0e12b1c93702, []int{8}
}
func (m *StartTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StartTaskRequest.Unmarshal(m, b)
@ -577,7 +580,7 @@ func (m *StartTaskResponse) Reset() { *m = StartTaskResponse{} }
func (m *StartTaskResponse) String() string { return proto.CompactTextString(m) }
func (*StartTaskResponse) ProtoMessage() {}
func (*StartTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{9}
return fileDescriptor_driver_d79b0e12b1c93702, []int{9}
}
func (m *StartTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StartTaskResponse.Unmarshal(m, b)
@ -637,7 +640,7 @@ func (m *WaitTaskRequest) Reset() { *m = WaitTaskRequest{} }
func (m *WaitTaskRequest) String() string { return proto.CompactTextString(m) }
func (*WaitTaskRequest) ProtoMessage() {}
func (*WaitTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{10}
return fileDescriptor_driver_d79b0e12b1c93702, []int{10}
}
func (m *WaitTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WaitTaskRequest.Unmarshal(m, b)
@ -678,7 +681,7 @@ func (m *WaitTaskResponse) Reset() { *m = WaitTaskResponse{} }
func (m *WaitTaskResponse) String() string { return proto.CompactTextString(m) }
func (*WaitTaskResponse) ProtoMessage() {}
func (*WaitTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{11}
return fileDescriptor_driver_d79b0e12b1c93702, []int{11}
}
func (m *WaitTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WaitTaskResponse.Unmarshal(m, b)
@ -730,7 +733,7 @@ func (m *StopTaskRequest) Reset() { *m = StopTaskRequest{} }
func (m *StopTaskRequest) String() string { return proto.CompactTextString(m) }
func (*StopTaskRequest) ProtoMessage() {}
func (*StopTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{12}
return fileDescriptor_driver_d79b0e12b1c93702, []int{12}
}
func (m *StopTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopTaskRequest.Unmarshal(m, b)
@ -781,7 +784,7 @@ func (m *StopTaskResponse) Reset() { *m = StopTaskResponse{} }
func (m *StopTaskResponse) String() string { return proto.CompactTextString(m) }
func (*StopTaskResponse) ProtoMessage() {}
func (*StopTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{13}
return fileDescriptor_driver_d79b0e12b1c93702, []int{13}
}
func (m *StopTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopTaskResponse.Unmarshal(m, b)
@ -815,7 +818,7 @@ func (m *DestroyTaskRequest) Reset() { *m = DestroyTaskRequest{} }
func (m *DestroyTaskRequest) String() string { return proto.CompactTextString(m) }
func (*DestroyTaskRequest) ProtoMessage() {}
func (*DestroyTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{14}
return fileDescriptor_driver_d79b0e12b1c93702, []int{14}
}
func (m *DestroyTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DestroyTaskRequest.Unmarshal(m, b)
@ -859,7 +862,7 @@ func (m *DestroyTaskResponse) Reset() { *m = DestroyTaskResponse{} }
func (m *DestroyTaskResponse) String() string { return proto.CompactTextString(m) }
func (*DestroyTaskResponse) ProtoMessage() {}
func (*DestroyTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{15}
return fileDescriptor_driver_d79b0e12b1c93702, []int{15}
}
func (m *DestroyTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DestroyTaskResponse.Unmarshal(m, b)
@ -891,7 +894,7 @@ func (m *InspectTaskRequest) Reset() { *m = InspectTaskRequest{} }
func (m *InspectTaskRequest) String() string { return proto.CompactTextString(m) }
func (*InspectTaskRequest) ProtoMessage() {}
func (*InspectTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{16}
return fileDescriptor_driver_d79b0e12b1c93702, []int{16}
}
func (m *InspectTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InspectTaskRequest.Unmarshal(m, b)
@ -934,7 +937,7 @@ func (m *InspectTaskResponse) Reset() { *m = InspectTaskResponse{} }
func (m *InspectTaskResponse) String() string { return proto.CompactTextString(m) }
func (*InspectTaskResponse) ProtoMessage() {}
func (*InspectTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{17}
return fileDescriptor_driver_d79b0e12b1c93702, []int{17}
}
func (m *InspectTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InspectTaskResponse.Unmarshal(m, b)
@ -989,7 +992,7 @@ func (m *TaskStatsRequest) Reset() { *m = TaskStatsRequest{} }
func (m *TaskStatsRequest) String() string { return proto.CompactTextString(m) }
func (*TaskStatsRequest) ProtoMessage() {}
func (*TaskStatsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{18}
return fileDescriptor_driver_d79b0e12b1c93702, []int{18}
}
func (m *TaskStatsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskStatsRequest.Unmarshal(m, b)
@ -1035,7 +1038,7 @@ func (m *TaskStatsResponse) Reset() { *m = TaskStatsResponse{} }
func (m *TaskStatsResponse) String() string { return proto.CompactTextString(m) }
func (*TaskStatsResponse) ProtoMessage() {}
func (*TaskStatsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{19}
return fileDescriptor_driver_d79b0e12b1c93702, []int{19}
}
func (m *TaskStatsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskStatsResponse.Unmarshal(m, b)
@ -1072,7 +1075,7 @@ func (m *TaskEventsRequest) Reset() { *m = TaskEventsRequest{} }
func (m *TaskEventsRequest) String() string { return proto.CompactTextString(m) }
func (*TaskEventsRequest) ProtoMessage() {}
func (*TaskEventsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{20}
return fileDescriptor_driver_d79b0e12b1c93702, []int{20}
}
func (m *TaskEventsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskEventsRequest.Unmarshal(m, b)
@ -1106,7 +1109,7 @@ func (m *SignalTaskRequest) Reset() { *m = SignalTaskRequest{} }
func (m *SignalTaskRequest) String() string { return proto.CompactTextString(m) }
func (*SignalTaskRequest) ProtoMessage() {}
func (*SignalTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{21}
return fileDescriptor_driver_d79b0e12b1c93702, []int{21}
}
func (m *SignalTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignalTaskRequest.Unmarshal(m, b)
@ -1150,7 +1153,7 @@ func (m *SignalTaskResponse) Reset() { *m = SignalTaskResponse{} }
func (m *SignalTaskResponse) String() string { return proto.CompactTextString(m) }
func (*SignalTaskResponse) ProtoMessage() {}
func (*SignalTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{22}
return fileDescriptor_driver_d79b0e12b1c93702, []int{22}
}
func (m *SignalTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignalTaskResponse.Unmarshal(m, b)
@ -1187,7 +1190,7 @@ func (m *ExecTaskRequest) Reset() { *m = ExecTaskRequest{} }
func (m *ExecTaskRequest) String() string { return proto.CompactTextString(m) }
func (*ExecTaskRequest) ProtoMessage() {}
func (*ExecTaskRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{23}
return fileDescriptor_driver_d79b0e12b1c93702, []int{23}
}
func (m *ExecTaskRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExecTaskRequest.Unmarshal(m, b)
@ -1244,7 +1247,7 @@ func (m *ExecTaskResponse) Reset() { *m = ExecTaskResponse{} }
func (m *ExecTaskResponse) String() string { return proto.CompactTextString(m) }
func (*ExecTaskResponse) ProtoMessage() {}
func (*ExecTaskResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{24}
return fileDescriptor_driver_d79b0e12b1c93702, []int{24}
}
func (m *ExecTaskResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExecTaskResponse.Unmarshal(m, b)
@ -1303,7 +1306,7 @@ func (m *DriverCapabilities) Reset() { *m = DriverCapabilities{} }
func (m *DriverCapabilities) String() string { return proto.CompactTextString(m) }
func (*DriverCapabilities) ProtoMessage() {}
func (*DriverCapabilities) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{25}
return fileDescriptor_driver_d79b0e12b1c93702, []int{25}
}
func (m *DriverCapabilities) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DriverCapabilities.Unmarshal(m, b)
@ -1389,7 +1392,7 @@ func (m *TaskConfig) Reset() { *m = TaskConfig{} }
func (m *TaskConfig) String() string { return proto.CompactTextString(m) }
func (*TaskConfig) ProtoMessage() {}
func (*TaskConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{26}
return fileDescriptor_driver_d79b0e12b1c93702, []int{26}
}
func (m *TaskConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskConfig.Unmarshal(m, b)
@ -1528,7 +1531,7 @@ func (m *Resources) Reset() { *m = Resources{} }
func (m *Resources) String() string { return proto.CompactTextString(m) }
func (*Resources) ProtoMessage() {}
func (*Resources) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{27}
return fileDescriptor_driver_d79b0e12b1c93702, []int{27}
}
func (m *Resources) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Resources.Unmarshal(m, b)
@ -1575,7 +1578,7 @@ func (m *AllocatedTaskResources) Reset() { *m = AllocatedTaskResources{}
func (m *AllocatedTaskResources) String() string { return proto.CompactTextString(m) }
func (*AllocatedTaskResources) ProtoMessage() {}
func (*AllocatedTaskResources) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{28}
return fileDescriptor_driver_d79b0e12b1c93702, []int{28}
}
func (m *AllocatedTaskResources) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AllocatedTaskResources.Unmarshal(m, b)
@ -1627,7 +1630,7 @@ func (m *AllocatedCpuResources) Reset() { *m = AllocatedCpuResources{} }
func (m *AllocatedCpuResources) String() string { return proto.CompactTextString(m) }
func (*AllocatedCpuResources) ProtoMessage() {}
func (*AllocatedCpuResources) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{29}
return fileDescriptor_driver_d79b0e12b1c93702, []int{29}
}
func (m *AllocatedCpuResources) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AllocatedCpuResources.Unmarshal(m, b)
@ -1665,7 +1668,7 @@ func (m *AllocatedMemoryResources) Reset() { *m = AllocatedMemoryResourc
func (m *AllocatedMemoryResources) String() string { return proto.CompactTextString(m) }
func (*AllocatedMemoryResources) ProtoMessage() {}
func (*AllocatedMemoryResources) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{30}
return fileDescriptor_driver_d79b0e12b1c93702, []int{30}
}
func (m *AllocatedMemoryResources) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AllocatedMemoryResources.Unmarshal(m, b)
@ -1708,7 +1711,7 @@ func (m *NetworkResource) Reset() { *m = NetworkResource{} }
func (m *NetworkResource) String() string { return proto.CompactTextString(m) }
func (*NetworkResource) ProtoMessage() {}
func (*NetworkResource) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{31}
return fileDescriptor_driver_d79b0e12b1c93702, []int{31}
}
func (m *NetworkResource) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkResource.Unmarshal(m, b)
@ -1782,7 +1785,7 @@ func (m *NetworkPort) Reset() { *m = NetworkPort{} }
func (m *NetworkPort) String() string { return proto.CompactTextString(m) }
func (*NetworkPort) ProtoMessage() {}
func (*NetworkPort) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{32}
return fileDescriptor_driver_d79b0e12b1c93702, []int{32}
}
func (m *NetworkPort) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkPort.Unmarshal(m, b)
@ -1842,7 +1845,7 @@ func (m *LinuxResources) Reset() { *m = LinuxResources{} }
func (m *LinuxResources) String() string { return proto.CompactTextString(m) }
func (*LinuxResources) ProtoMessage() {}
func (*LinuxResources) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{33}
return fileDescriptor_driver_d79b0e12b1c93702, []int{33}
}
func (m *LinuxResources) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LinuxResources.Unmarshal(m, b)
@ -1934,7 +1937,7 @@ func (m *Mount) Reset() { *m = Mount{} }
func (m *Mount) String() string { return proto.CompactTextString(m) }
func (*Mount) ProtoMessage() {}
func (*Mount) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{34}
return fileDescriptor_driver_d79b0e12b1c93702, []int{34}
}
func (m *Mount) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Mount.Unmarshal(m, b)
@ -1997,7 +2000,7 @@ func (m *Device) Reset() { *m = Device{} }
func (m *Device) String() string { return proto.CompactTextString(m) }
func (*Device) ProtoMessage() {}
func (*Device) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{35}
return fileDescriptor_driver_d79b0e12b1c93702, []int{35}
}
func (m *Device) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Device.Unmarshal(m, b)
@ -2040,7 +2043,8 @@ func (m *Device) GetCgroupPermissions() string {
// TaskHandle is created when starting a task and is used to recover task
type TaskHandle struct {
// Version is used by the driver to version the DriverState schema
// Version is used by the driver to version the DriverState schema.
// Version 0 is reserved by Nomad and should not be used.
Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
// Config is the TaskConfig for the task
Config *TaskConfig `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
@ -2057,7 +2061,7 @@ func (m *TaskHandle) Reset() { *m = TaskHandle{} }
func (m *TaskHandle) String() string { return proto.CompactTextString(m) }
func (*TaskHandle) ProtoMessage() {}
func (*TaskHandle) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{36}
return fileDescriptor_driver_d79b0e12b1c93702, []int{36}
}
func (m *TaskHandle) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskHandle.Unmarshal(m, b)
@ -2124,7 +2128,7 @@ func (m *NetworkOverride) Reset() { *m = NetworkOverride{} }
func (m *NetworkOverride) String() string { return proto.CompactTextString(m) }
func (*NetworkOverride) ProtoMessage() {}
func (*NetworkOverride) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{37}
return fileDescriptor_driver_d79b0e12b1c93702, []int{37}
}
func (m *NetworkOverride) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkOverride.Unmarshal(m, b)
@ -2182,7 +2186,7 @@ func (m *ExitResult) Reset() { *m = ExitResult{} }
func (m *ExitResult) String() string { return proto.CompactTextString(m) }
func (*ExitResult) ProtoMessage() {}
func (*ExitResult) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{38}
return fileDescriptor_driver_d79b0e12b1c93702, []int{38}
}
func (m *ExitResult) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExitResult.Unmarshal(m, b)
@ -2245,7 +2249,7 @@ func (m *TaskStatus) Reset() { *m = TaskStatus{} }
func (m *TaskStatus) String() string { return proto.CompactTextString(m) }
func (*TaskStatus) ProtoMessage() {}
func (*TaskStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{39}
return fileDescriptor_driver_d79b0e12b1c93702, []int{39}
}
func (m *TaskStatus) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskStatus.Unmarshal(m, b)
@ -2320,7 +2324,7 @@ func (m *TaskDriverStatus) Reset() { *m = TaskDriverStatus{} }
func (m *TaskDriverStatus) String() string { return proto.CompactTextString(m) }
func (*TaskDriverStatus) ProtoMessage() {}
func (*TaskDriverStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{40}
return fileDescriptor_driver_d79b0e12b1c93702, []int{40}
}
func (m *TaskDriverStatus) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskDriverStatus.Unmarshal(m, b)
@ -2365,7 +2369,7 @@ func (m *TaskStats) Reset() { *m = TaskStats{} }
func (m *TaskStats) String() string { return proto.CompactTextString(m) }
func (*TaskStats) ProtoMessage() {}
func (*TaskStats) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{41}
return fileDescriptor_driver_d79b0e12b1c93702, []int{41}
}
func (m *TaskStats) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskStats.Unmarshal(m, b)
@ -2427,7 +2431,7 @@ func (m *TaskResourceUsage) Reset() { *m = TaskResourceUsage{} }
func (m *TaskResourceUsage) String() string { return proto.CompactTextString(m) }
func (*TaskResourceUsage) ProtoMessage() {}
func (*TaskResourceUsage) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{42}
return fileDescriptor_driver_d79b0e12b1c93702, []int{42}
}
func (m *TaskResourceUsage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TaskResourceUsage.Unmarshal(m, b)
@ -2479,7 +2483,7 @@ func (m *CPUUsage) Reset() { *m = CPUUsage{} }
func (m *CPUUsage) String() string { return proto.CompactTextString(m) }
func (*CPUUsage) ProtoMessage() {}
func (*CPUUsage) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{43}
return fileDescriptor_driver_d79b0e12b1c93702, []int{43}
}
func (m *CPUUsage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CPUUsage.Unmarshal(m, b)
@ -2554,6 +2558,7 @@ type MemoryUsage struct {
MaxUsage uint64 `protobuf:"varint,3,opt,name=max_usage,json=maxUsage,proto3" json:"max_usage,omitempty"`
KernelUsage uint64 `protobuf:"varint,4,opt,name=kernel_usage,json=kernelUsage,proto3" json:"kernel_usage,omitempty"`
KernelMaxUsage uint64 `protobuf:"varint,5,opt,name=kernel_max_usage,json=kernelMaxUsage,proto3" json:"kernel_max_usage,omitempty"`
Usage uint64 `protobuf:"varint,7,opt,name=usage,proto3" json:"usage,omitempty"`
// MeasuredFields indicates which fields were actually sampled
MeasuredFields []MemoryUsage_Fields `protobuf:"varint,6,rep,packed,name=measured_fields,json=measuredFields,proto3,enum=hashicorp.nomad.plugins.drivers.proto.MemoryUsage_Fields" json:"measured_fields,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -2565,7 +2570,7 @@ func (m *MemoryUsage) Reset() { *m = MemoryUsage{} }
func (m *MemoryUsage) String() string { return proto.CompactTextString(m) }
func (*MemoryUsage) ProtoMessage() {}
func (*MemoryUsage) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{44}
return fileDescriptor_driver_d79b0e12b1c93702, []int{44}
}
func (m *MemoryUsage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MemoryUsage.Unmarshal(m, b)
@ -2620,6 +2625,13 @@ func (m *MemoryUsage) GetKernelMaxUsage() uint64 {
return 0
}
func (m *MemoryUsage) GetUsage() uint64 {
if m != nil {
return m.Usage
}
return 0
}
func (m *MemoryUsage) GetMeasuredFields() []MemoryUsage_Fields {
if m != nil {
return m.MeasuredFields
@ -2649,7 +2661,7 @@ func (m *DriverTaskEvent) Reset() { *m = DriverTaskEvent{} }
func (m *DriverTaskEvent) String() string { return proto.CompactTextString(m) }
func (*DriverTaskEvent) ProtoMessage() {}
func (*DriverTaskEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_driver_c5667ca6005812fb, []int{45}
return fileDescriptor_driver_d79b0e12b1c93702, []int{45}
}
func (m *DriverTaskEvent) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DriverTaskEvent.Unmarshal(m, b)
@ -3384,195 +3396,196 @@ var _Driver_serviceDesc = grpc.ServiceDesc{
}
func init() {
proto.RegisterFile("plugins/drivers/proto/driver.proto", fileDescriptor_driver_c5667ca6005812fb)
proto.RegisterFile("plugins/drivers/proto/driver.proto", fileDescriptor_driver_d79b0e12b1c93702)
}
var fileDescriptor_driver_c5667ca6005812fb = []byte{
// 2962 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xdd, 0x6f, 0xe3, 0xc6,
0x11, 0x37, 0xf5, 0x65, 0x69, 0x64, 0xcb, 0xbc, 0xbd, 0xbb, 0x44, 0x51, 0xd0, 0xe6, 0x42, 0x20,
0x85, 0x91, 0xe4, 0xe4, 0xc4, 0x41, 0xef, 0xab, 0xf9, 0x52, 0x24, 0x9e, 0xed, 0x9c, 0x2d, 0xbb,
0x2b, 0x19, 0x97, 0x6b, 0x9b, 0xb0, 0x34, 0xb9, 0x27, 0xf1, 0x2c, 0x7e, 0x84, 0x5c, 0x3a, 0x36,
0x8a, 0xa2, 0x45, 0x0a, 0x14, 0xed, 0x43, 0x81, 0xbe, 0x04, 0x7d, 0x6f, 0x1f, 0xfb, 0x17, 0xb4,
0x45, 0xfe, 0x8f, 0x02, 0xed, 0x4b, 0x0b, 0x14, 0xe8, 0x6b, 0xff, 0x83, 0x62, 0x3f, 0x48, 0x51,
0xb6, 0x2f, 0x47, 0xe9, 0xf2, 0x24, 0xed, 0xec, 0xce, 0x6f, 0x87, 0x33, 0xb3, 0x33, 0xb3, 0x3b,
0xa0, 0x05, 0x93, 0x78, 0xe4, 0x78, 0xd1, 0x86, 0x1d, 0x3a, 0x27, 0x24, 0x8c, 0x36, 0x82, 0xd0,
0xa7, 0xbe, 0x1c, 0xb5, 0xf9, 0x00, 0xbd, 0x36, 0x36, 0xa3, 0xb1, 0x63, 0xf9, 0x61, 0xd0, 0xf6,
0x7c, 0xd7, 0xb4, 0xdb, 0x92, 0xa7, 0x2d, 0x79, 0xc4, 0xb2, 0xd6, 0x77, 0x47, 0xbe, 0x3f, 0x9a,
0x10, 0x81, 0x70, 0x14, 0x3f, 0xde, 0xb0, 0xe3, 0xd0, 0xa4, 0x8e, 0xef, 0xc9, 0xf9, 0x57, 0xce,
0xcf, 0x53, 0xc7, 0x25, 0x11, 0x35, 0xdd, 0x40, 0x2e, 0xf8, 0x70, 0xe4, 0xd0, 0x71, 0x7c, 0xd4,
0xb6, 0x7c, 0x77, 0x23, 0xdd, 0x72, 0x83, 0x6f, 0xb9, 0x91, 0x88, 0x19, 0x8d, 0xcd, 0x90, 0xd8,
0x1b, 0x63, 0x6b, 0x12, 0x05, 0xc4, 0x62, 0xbf, 0x06, 0xfb, 0x23, 0x11, 0xb6, 0xf2, 0x23, 0x44,
0x34, 0x8c, 0x2d, 0x9a, 0x7c, 0xaf, 0x49, 0x69, 0xe8, 0x1c, 0xc5, 0x94, 0x08, 0x20, 0xed, 0x25,
0x78, 0x71, 0x68, 0x46, 0xc7, 0x5d, 0xdf, 0x7b, 0xec, 0x8c, 0x06, 0xd6, 0x98, 0xb8, 0x26, 0x26,
0x9f, 0xc7, 0x24, 0xa2, 0xda, 0x4f, 0xa0, 0x79, 0x71, 0x2a, 0x0a, 0x7c, 0x2f, 0x22, 0xe8, 0x43,
0x28, 0x31, 0x69, 0x9a, 0xca, 0x0d, 0x65, 0xbd, 0xbe, 0xf9, 0x66, 0xfb, 0x69, 0x8a, 0x13, 0x32,
0xb4, 0xe5, 0x57, 0xb4, 0x07, 0x01, 0xb1, 0x30, 0xe7, 0xd4, 0xae, 0xc3, 0xd5, 0xae, 0x19, 0x98,
0x47, 0xce, 0xc4, 0xa1, 0x0e, 0x89, 0x92, 0x4d, 0x63, 0xb8, 0x36, 0x4b, 0x96, 0x1b, 0x7e, 0x0a,
0x2b, 0x56, 0x86, 0x2e, 0x37, 0xbe, 0xdb, 0xce, 0x65, 0xb1, 0x76, 0x8f, 0x8f, 0x66, 0x80, 0x67,
0xe0, 0xb4, 0x6b, 0x80, 0xee, 0x3b, 0xde, 0x88, 0x84, 0x41, 0xe8, 0x78, 0x34, 0x11, 0xe6, 0xeb,
0x22, 0x5c, 0x9d, 0x21, 0x4b, 0x61, 0x9e, 0x00, 0xa4, 0x7a, 0x64, 0xa2, 0x14, 0xd7, 0xeb, 0x9b,
0x1f, 0xe7, 0x14, 0xe5, 0x12, 0xbc, 0x76, 0x27, 0x05, 0xd3, 0x3d, 0x1a, 0x9e, 0xe1, 0x0c, 0x3a,
0xfa, 0x0c, 0x2a, 0x63, 0x62, 0x4e, 0xe8, 0xb8, 0x59, 0xb8, 0xa1, 0xac, 0x37, 0x36, 0xef, 0x3f,
0xc7, 0x3e, 0xdb, 0x1c, 0x68, 0x40, 0x4d, 0x4a, 0xb0, 0x44, 0x45, 0x37, 0x01, 0x89, 0x7f, 0x86,
0x4d, 0x22, 0x2b, 0x74, 0x02, 0xe6, 0xc8, 0xcd, 0xe2, 0x0d, 0x65, 0xbd, 0x86, 0xaf, 0x88, 0x99,
0xde, 0x74, 0xa2, 0x15, 0xc0, 0xda, 0x39, 0x69, 0x91, 0x0a, 0xc5, 0x63, 0x72, 0xc6, 0x2d, 0x52,
0xc3, 0xec, 0x2f, 0xda, 0x82, 0xf2, 0x89, 0x39, 0x89, 0x09, 0x17, 0xb9, 0xbe, 0xf9, 0xf6, 0xb3,
0xdc, 0x43, 0xba, 0xe8, 0x54, 0x0f, 0x58, 0xf0, 0xdf, 0x2b, 0xdc, 0x51, 0xb4, 0xbb, 0x50, 0xcf,
0xc8, 0x8d, 0x1a, 0x00, 0x87, 0xfd, 0x9e, 0x3e, 0xd4, 0xbb, 0x43, 0xbd, 0xa7, 0x2e, 0xa1, 0x55,
0xa8, 0x1d, 0xf6, 0xb7, 0xf5, 0xce, 0xee, 0x70, 0xfb, 0x91, 0xaa, 0xa0, 0x3a, 0x2c, 0x27, 0x83,
0x82, 0x76, 0x0a, 0x08, 0x13, 0xcb, 0x3f, 0x21, 0x21, 0x73, 0x64, 0x69, 0x55, 0xf4, 0x22, 0x2c,
0x53, 0x33, 0x3a, 0x36, 0x1c, 0x5b, 0xca, 0x5c, 0x61, 0xc3, 0x1d, 0x1b, 0xed, 0x40, 0x65, 0x6c,
0x7a, 0xf6, 0xe4, 0xd9, 0x72, 0xcf, 0xaa, 0x9a, 0x81, 0x6f, 0x73, 0x46, 0x2c, 0x01, 0x98, 0x77,
0xcf, 0xec, 0x2c, 0x0c, 0xa0, 0x3d, 0x02, 0x75, 0x40, 0xcd, 0x90, 0x66, 0xc5, 0xd1, 0xa1, 0xc4,
0xf6, 0x97, 0x1e, 0x3d, 0xcf, 0x9e, 0xe2, 0x64, 0x62, 0xce, 0xae, 0xfd, 0xaf, 0x00, 0x57, 0x32,
0xd8, 0xd2, 0x53, 0x1f, 0x42, 0x25, 0x24, 0x51, 0x3c, 0xa1, 0x1c, 0xbe, 0xb1, 0xf9, 0x41, 0x4e,
0xf8, 0x0b, 0x48, 0x6d, 0xcc, 0x61, 0xb0, 0x84, 0x43, 0xeb, 0xa0, 0x0a, 0x0e, 0x83, 0x84, 0xa1,
0x1f, 0x1a, 0x6e, 0x34, 0xe2, 0x5a, 0xab, 0xe1, 0x86, 0xa0, 0xeb, 0x8c, 0xbc, 0x17, 0x8d, 0x32,
0x5a, 0x2d, 0x3e, 0xa7, 0x56, 0x91, 0x09, 0xaa, 0x47, 0xe8, 0x17, 0x7e, 0x78, 0x6c, 0x30, 0xd5,
0x86, 0x8e, 0x4d, 0x9a, 0x25, 0x0e, 0x7a, 0x2b, 0x27, 0x68, 0x5f, 0xb0, 0xef, 0x4b, 0x6e, 0xbc,
0xe6, 0xcd, 0x12, 0xb4, 0x37, 0xa0, 0x22, 0xbe, 0x94, 0x79, 0xd2, 0xe0, 0xb0, 0xdb, 0xd5, 0x07,
0x03, 0x75, 0x09, 0xd5, 0xa0, 0x8c, 0xf5, 0x21, 0x66, 0x1e, 0x56, 0x83, 0xf2, 0xfd, 0xce, 0xb0,
0xb3, 0xab, 0x16, 0xb4, 0xd7, 0x61, 0xed, 0xa1, 0xe9, 0xd0, 0x3c, 0xce, 0xa5, 0xf9, 0xa0, 0x4e,
0xd7, 0x4a, 0xeb, 0xec, 0xcc, 0x58, 0x27, 0xbf, 0x6a, 0xf4, 0x53, 0x87, 0x9e, 0xb3, 0x87, 0x0a,
0x45, 0x12, 0x86, 0xd2, 0x04, 0xec, 0xaf, 0xf6, 0x05, 0xac, 0x0d, 0xa8, 0x1f, 0xe4, 0xf2, 0xfc,
0x77, 0x60, 0x99, 0xe5, 0x28, 0x3f, 0xa6, 0xd2, 0xf5, 0x5f, 0x6a, 0x8b, 0x1c, 0xd6, 0x4e, 0x72,
0x58, 0xbb, 0x27, 0x73, 0x1c, 0x4e, 0x56, 0xa2, 0x17, 0xa0, 0x12, 0x39, 0x23, 0xcf, 0x9c, 0xc8,
0x68, 0x21, 0x47, 0x1a, 0x62, 0x4e, 0x9e, 0x6c, 0x2c, 0x1d, 0xbf, 0x0b, 0xa8, 0x47, 0x22, 0x1a,
0xfa, 0x67, 0xb9, 0xe4, 0xb9, 0x06, 0xe5, 0xc7, 0x7e, 0x68, 0x89, 0x83, 0x58, 0xc5, 0x62, 0xc0,
0x0e, 0xd5, 0x0c, 0x88, 0xc4, 0xbe, 0x09, 0x68, 0xc7, 0x63, 0x39, 0x25, 0x9f, 0x21, 0x7e, 0x5f,
0x80, 0xab, 0x33, 0xeb, 0xa5, 0x31, 0x16, 0x3f, 0x87, 0x2c, 0x30, 0xc5, 0x91, 0x38, 0x87, 0x68,
0x1f, 0x2a, 0x62, 0x85, 0xd4, 0xe4, 0xed, 0x39, 0x80, 0x44, 0x9a, 0x92, 0x70, 0x12, 0xe6, 0x52,
0xa7, 0x2f, 0x7e, 0xbb, 0x4e, 0xbf, 0x05, 0x6a, 0xf2, 0x1d, 0xd1, 0x33, 0x6d, 0xd3, 0x82, 0xaa,
0xe3, 0x51, 0x12, 0x9e, 0x98, 0x13, 0xfe, 0x89, 0x45, 0x9c, 0x8e, 0xb5, 0x1f, 0xc3, 0x95, 0x0c,
0x90, 0x54, 0xec, 0x7d, 0x28, 0x47, 0x8c, 0x20, 0x35, 0xfb, 0xd6, 0x9c, 0x9a, 0x8d, 0xb0, 0x60,
0xd7, 0xae, 0x0a, 0x70, 0xfd, 0x84, 0x78, 0xa9, 0x98, 0x5a, 0x0f, 0xae, 0x0c, 0xb8, 0xdb, 0xe5,
0xf2, 0xab, 0xa9, 0xcb, 0x16, 0x66, 0x5c, 0xf6, 0x1a, 0xa0, 0x2c, 0x8a, 0x74, 0xac, 0x33, 0x58,
0xd3, 0x4f, 0x89, 0x95, 0x0b, 0xb9, 0x09, 0xcb, 0x96, 0xef, 0xba, 0xa6, 0x67, 0x37, 0x0b, 0x37,
0x8a, 0xeb, 0x35, 0x9c, 0x0c, 0xb3, 0x67, 0xab, 0x98, 0xf7, 0x6c, 0x69, 0xbf, 0x53, 0x40, 0x9d,
0xee, 0x2d, 0x15, 0xc9, 0xa4, 0xa7, 0x36, 0x03, 0x62, 0x7b, 0xaf, 0x60, 0x39, 0x92, 0xf4, 0xe4,
0xf8, 0x0b, 0x3a, 0x09, 0xc3, 0x4c, 0x78, 0x29, 0x3e, 0x67, 0x78, 0xd1, 0xfe, 0xa3, 0x00, 0xba,
0x58, 0x44, 0xa1, 0x57, 0x61, 0x25, 0x22, 0x9e, 0x6d, 0x08, 0x35, 0x0a, 0x0b, 0x57, 0x71, 0x9d,
0xd1, 0x84, 0x3e, 0x23, 0x84, 0xa0, 0x44, 0x4e, 0x89, 0x25, 0x4f, 0x32, 0xff, 0x8f, 0xc6, 0xb0,
0xf2, 0x38, 0x32, 0x9c, 0xc8, 0x9f, 0x98, 0x69, 0xb5, 0xd1, 0xd8, 0xd4, 0x17, 0x2e, 0xe6, 0xda,
0xf7, 0x07, 0x3b, 0x09, 0x18, 0xae, 0x3f, 0x8e, 0xd2, 0x81, 0xd6, 0x86, 0x7a, 0x66, 0x0e, 0x55,
0xa1, 0xd4, 0xdf, 0xef, 0xeb, 0xea, 0x12, 0x02, 0xa8, 0x74, 0xb7, 0xf1, 0xfe, 0xfe, 0x50, 0x44,
0xf4, 0x9d, 0xbd, 0xce, 0x96, 0xae, 0x16, 0xb4, 0xbf, 0x54, 0x00, 0xa6, 0xa9, 0x15, 0x35, 0xa0,
0x90, 0x5a, 0xba, 0xe0, 0xd8, 0xec, 0x63, 0x3c, 0xd3, 0x25, 0xd2, 0x7b, 0xf8, 0x7f, 0xb4, 0x09,
0xd7, 0xdd, 0x68, 0x14, 0x98, 0xd6, 0xb1, 0x21, 0x33, 0xa2, 0xc5, 0x99, 0xf9, 0x57, 0xad, 0xe0,
0xab, 0x72, 0x52, 0x4a, 0x2d, 0x70, 0x77, 0xa1, 0x48, 0xbc, 0x93, 0x66, 0x89, 0x57, 0x8e, 0xf7,
0xe6, 0x4e, 0xf9, 0x6d, 0xdd, 0x3b, 0x11, 0x95, 0x22, 0x83, 0x41, 0x06, 0x80, 0x4d, 0x4e, 0x1c,
0x8b, 0x18, 0x0c, 0xb4, 0xcc, 0x41, 0x3f, 0x9c, 0x1f, 0xb4, 0xc7, 0x31, 0x52, 0xe8, 0x9a, 0x9d,
0x8c, 0x51, 0x1f, 0x6a, 0x21, 0x89, 0xfc, 0x38, 0xb4, 0x48, 0xd4, 0xac, 0xcc, 0x75, 0x8a, 0x71,
0xc2, 0x87, 0xa7, 0x10, 0xa8, 0x07, 0x15, 0xd7, 0x8f, 0x3d, 0x1a, 0x35, 0x97, 0xb9, 0xb0, 0x6f,
0xe6, 0x04, 0xdb, 0x63, 0x4c, 0x58, 0xf2, 0xa2, 0x2d, 0x58, 0x16, 0x22, 0x46, 0xcd, 0x2a, 0x87,
0xb9, 0x99, 0xd7, 0x81, 0x38, 0x17, 0x4e, 0xb8, 0x99, 0x55, 0xe3, 0x88, 0x84, 0xcd, 0x9a, 0xb0,
0x2a, 0xfb, 0x8f, 0x5e, 0x86, 0x9a, 0x39, 0x99, 0xf8, 0x96, 0x61, 0x3b, 0x61, 0x13, 0xf8, 0x44,
0x95, 0x13, 0x7a, 0x4e, 0x88, 0x5e, 0x81, 0xba, 0x38, 0x7a, 0x46, 0x60, 0xd2, 0x71, 0xb3, 0xce,
0xa7, 0x41, 0x90, 0x0e, 0x4c, 0x3a, 0x96, 0x0b, 0x48, 0x18, 0x8a, 0x05, 0x2b, 0xe9, 0x02, 0x12,
0x86, 0x7c, 0xc1, 0xf7, 0x60, 0x8d, 0xc7, 0x91, 0x51, 0xe8, 0xc7, 0x81, 0xc1, 0x7d, 0x6a, 0x95,
0x2f, 0x5a, 0x65, 0xe4, 0x2d, 0x46, 0xed, 0x33, 0xe7, 0x7a, 0x09, 0xaa, 0x4f, 0xfc, 0x23, 0xb1,
0xa0, 0xc1, 0x17, 0x2c, 0x3f, 0xf1, 0x8f, 0x92, 0x29, 0x21, 0xa1, 0x63, 0x37, 0xd7, 0xc4, 0x14,
0x1f, 0xef, 0xd8, 0xad, 0x5b, 0x50, 0x4d, 0xcc, 0x78, 0x49, 0x75, 0x7e, 0x2d, 0x5b, 0x9d, 0xd7,
0x32, 0xa5, 0x76, 0xeb, 0x5d, 0x68, 0xcc, 0x3a, 0xc1, 0x3c, 0xdc, 0xda, 0x3f, 0x14, 0xa8, 0xa5,
0xe6, 0x46, 0x1e, 0x5c, 0xe5, 0xe2, 0x98, 0x94, 0xd8, 0xc6, 0xd4, 0x7b, 0x44, 0x0e, 0x78, 0x2f,
0xa7, 0xa5, 0x3a, 0x09, 0x82, 0x8c, 0x83, 0xd2, 0x95, 0x50, 0x8a, 0x3c, 0xdd, 0xef, 0x33, 0x58,
0x9b, 0x38, 0x5e, 0x7c, 0x9a, 0xd9, 0x4b, 0x24, 0xe0, 0xef, 0xe7, 0xdc, 0x6b, 0x97, 0x71, 0x4f,
0xf7, 0x68, 0x4c, 0x66, 0xc6, 0xda, 0x57, 0x05, 0x78, 0xe1, 0x72, 0x71, 0x50, 0x1f, 0x8a, 0x56,
0x10, 0xcb, 0x4f, 0x7b, 0x77, 0xde, 0x4f, 0xeb, 0x06, 0xf1, 0x74, 0x57, 0x06, 0xc4, 0x8a, 0x76,
0x97, 0xb8, 0x7e, 0x78, 0x26, 0xbf, 0xe0, 0x83, 0x79, 0x21, 0xf7, 0x38, 0xf7, 0x14, 0x55, 0xc2,
0x21, 0x0c, 0x55, 0x99, 0xfa, 0x23, 0x19, 0x26, 0xe6, 0x2c, 0x21, 0x12, 0x48, 0x9c, 0xe2, 0x68,
0xb7, 0xe0, 0xfa, 0xa5, 0x9f, 0x82, 0xbe, 0x03, 0x60, 0x05, 0xb1, 0xc1, 0xaf, 0x78, 0xc2, 0xee,
0x45, 0x5c, 0xb3, 0x82, 0x78, 0xc0, 0x09, 0xda, 0x6d, 0x68, 0x3e, 0x4d, 0x5e, 0x76, 0xf8, 0x84,
0xc4, 0x86, 0x7b, 0x94, 0xd4, 0x18, 0x82, 0xb0, 0x77, 0xa4, 0xfd, 0xa1, 0x00, 0x6b, 0xe7, 0xc4,
0x61, 0x19, 0x50, 0x1c, 0xe6, 0x24, 0x2b, 0x8b, 0x11, 0x3b, 0xd9, 0x96, 0x63, 0x27, 0x65, 0x31,
0xff, 0xcf, 0x63, 0x7a, 0x20, 0x4b, 0xd6, 0x82, 0x13, 0x30, 0x87, 0x76, 0x8f, 0x1c, 0x1a, 0xf1,
0x9b, 0x44, 0x19, 0x8b, 0x01, 0x7a, 0x04, 0x8d, 0x90, 0x44, 0x24, 0x3c, 0x21, 0xb6, 0x11, 0xf8,
0x21, 0x4d, 0x14, 0xb6, 0x39, 0x9f, 0xc2, 0x0e, 0xfc, 0x90, 0xe2, 0xd5, 0x04, 0x89, 0x8d, 0x22,
0xf4, 0x10, 0x56, 0xed, 0x33, 0xcf, 0x74, 0x1d, 0x4b, 0x22, 0x57, 0x16, 0x46, 0x5e, 0x91, 0x40,
0x1c, 0x98, 0xdd, 0x94, 0x33, 0x93, 0xec, 0xc3, 0x26, 0xe6, 0x11, 0x99, 0x48, 0x9d, 0x88, 0xc1,
0xec, 0xf9, 0x2d, 0xcb, 0xf3, 0xab, 0xfd, 0xa9, 0x00, 0x8d, 0xd9, 0x03, 0x90, 0xd8, 0x2f, 0x20,
0xa1, 0xe3, 0xdb, 0x19, 0xfb, 0x1d, 0x70, 0x02, 0xb3, 0x11, 0x9b, 0xfe, 0x3c, 0xf6, 0xa9, 0x99,
0xd8, 0xc8, 0x0a, 0xe2, 0x1f, 0xb2, 0xf1, 0x39, 0xdb, 0x17, 0xcf, 0xd9, 0x1e, 0xbd, 0x09, 0x48,
0xda, 0x77, 0xe2, 0xb8, 0x0e, 0x35, 0x8e, 0xce, 0x28, 0x11, 0xfa, 0x2f, 0x62, 0x55, 0xcc, 0xec,
0xb2, 0x89, 0x8f, 0x18, 0x1d, 0x69, 0xb0, 0xea, 0xfb, 0xae, 0x11, 0x59, 0x7e, 0x48, 0x0c, 0xd3,
0x7e, 0xd2, 0x2c, 0xf3, 0x85, 0x75, 0xdf, 0x77, 0x07, 0x8c, 0xd6, 0xb1, 0x9f, 0xb0, 0x80, 0x6b,
0x05, 0x71, 0x44, 0xa8, 0xc1, 0x7e, 0x78, 0x8e, 0xaa, 0x61, 0x10, 0xa4, 0x6e, 0x10, 0x47, 0x99,
0x05, 0x2e, 0x71, 0x59, 0xde, 0xc9, 0x2c, 0xd8, 0x23, 0x2e, 0xdb, 0x65, 0xe5, 0x80, 0x84, 0x16,
0xf1, 0xe8, 0xd0, 0xb1, 0x8e, 0x59, 0x4a, 0x51, 0xd6, 0x15, 0x3c, 0x43, 0xd3, 0x3e, 0x85, 0x32,
0x4f, 0x41, 0xec, 0xe3, 0x79, 0xf8, 0xe6, 0xd1, 0x5d, 0xa8, 0xb7, 0xca, 0x08, 0x3c, 0xb6, 0xbf,
0x0c, 0xb5, 0xb1, 0x1f, 0xc9, 0xdc, 0x20, 0x3c, 0xaf, 0xca, 0x08, 0x7c, 0xb2, 0x05, 0xd5, 0x90,
0x98, 0xb6, 0xef, 0x4d, 0xce, 0xb8, 0x5e, 0xaa, 0x38, 0x1d, 0x6b, 0x9f, 0x43, 0x45, 0x84, 0xdf,
0xe7, 0xc0, 0xbf, 0x09, 0xc8, 0x12, 0x49, 0x25, 0x20, 0xa1, 0xeb, 0x44, 0x91, 0xe3, 0x7b, 0x51,
0xf2, 0x9c, 0x23, 0x66, 0x0e, 0xa6, 0x13, 0xda, 0x3f, 0x15, 0x51, 0xef, 0x88, 0x8b, 0x36, 0xab,
0x62, 0x99, 0xa7, 0xb1, 0x9a, 0x4c, 0xe1, 0xee, 0x91, 0x0c, 0x59, 0x2d, 0x29, 0xcb, 0x9a, 0xc2,
0xa2, 0xef, 0x14, 0x12, 0x20, 0xb9, 0x0f, 0x10, 0x59, 0xf6, 0xcd, 0x7b, 0x1f, 0x20, 0xe2, 0x3e,
0x40, 0x58, 0xf1, 0x29, 0x0b, 0x2e, 0x01, 0x57, 0xe2, 0xf5, 0x56, 0xdd, 0x4e, 0x2f, 0x51, 0x44,
0xfb, 0xaf, 0x92, 0xc6, 0x8a, 0xe4, 0xb2, 0x83, 0x3e, 0x83, 0x2a, 0x3b, 0x76, 0x86, 0x6b, 0x06,
0xf2, 0xe9, 0xae, 0xbb, 0xd8, 0x3d, 0xaa, 0xcd, 0x4e, 0xd9, 0x9e, 0x19, 0x88, 0x72, 0x69, 0x39,
0x10, 0x23, 0x16, 0x73, 0x4c, 0x7b, 0x1a, 0x73, 0xd8, 0x7f, 0xf4, 0x1a, 0x34, 0xcc, 0x98, 0xfa,
0x86, 0x69, 0x9f, 0x90, 0x90, 0x3a, 0x11, 0x91, 0xb6, 0x5f, 0x65, 0xd4, 0x4e, 0x42, 0x6c, 0xdd,
0x83, 0x95, 0x2c, 0xe6, 0xb3, 0xb2, 0x6f, 0x39, 0x9b, 0x7d, 0x7f, 0x0a, 0x30, 0xad, 0xdb, 0x99,
0x8f, 0x90, 0x53, 0x87, 0x1a, 0x96, 0x6f, 0x13, 0x69, 0xca, 0x2a, 0x23, 0x74, 0x7d, 0x9b, 0x9c,
0xbb, 0x05, 0x95, 0x93, 0x5b, 0x10, 0x3b, 0xb5, 0xec, 0xa0, 0x1d, 0x3b, 0x93, 0x09, 0xb1, 0xa5,
0x84, 0x35, 0xdf, 0x77, 0x1f, 0x70, 0x82, 0xf6, 0x75, 0x41, 0xf8, 0x8a, 0xb8, 0x9f, 0xe6, 0xaa,
0x8d, 0xbf, 0x2d, 0x53, 0xdf, 0x05, 0x88, 0xa8, 0x19, 0xb2, 0x52, 0xc2, 0xa4, 0xf2, 0xc9, 0xa7,
0x75, 0xe1, 0x1a, 0x35, 0x4c, 0x9e, 0xd9, 0x71, 0x4d, 0xae, 0xee, 0x50, 0xf4, 0x1e, 0xac, 0x58,
0xbe, 0x1b, 0x4c, 0x88, 0x64, 0x2e, 0x3f, 0x93, 0xb9, 0x9e, 0xae, 0xef, 0xd0, 0xcc, 0x1d, 0xaa,
0xf2, 0xbc, 0x77, 0xa8, 0xbf, 0x2a, 0xe2, 0x9a, 0x9d, 0xbd, 0xe5, 0xa3, 0xd1, 0x25, 0x4f, 0xc9,
0x5b, 0x0b, 0x3e, 0x19, 0x7c, 0xd3, 0x3b, 0x72, 0xeb, 0xbd, 0x3c, 0x0f, 0xb7, 0x4f, 0x2f, 0xee,
0xfe, 0x56, 0x84, 0x5a, 0x7a, 0x23, 0xbf, 0x60, 0xfb, 0x3b, 0x50, 0x4b, 0x7b, 0x1c, 0x32, 0x40,
0x7c, 0xa3, 0x79, 0xd2, 0xc5, 0xe8, 0x31, 0x20, 0x73, 0x34, 0x4a, 0x8b, 0x36, 0x23, 0x8e, 0xcc,
0x51, 0xf2, 0xbe, 0x71, 0x67, 0x0e, 0x3d, 0x24, 0x79, 0xeb, 0x90, 0xf1, 0x63, 0xd5, 0x1c, 0x8d,
0x66, 0x28, 0xe8, 0x67, 0x70, 0x7d, 0x76, 0x0f, 0xe3, 0xe8, 0xcc, 0x08, 0x1c, 0x5b, 0xde, 0xc1,
0xb6, 0xe7, 0x7d, 0x94, 0x68, 0xcf, 0xc0, 0x7f, 0x74, 0x76, 0xe0, 0xd8, 0x42, 0xe7, 0x28, 0xbc,
0x30, 0xd1, 0xfa, 0x05, 0xbc, 0xf8, 0x94, 0xe5, 0x97, 0xd8, 0xa0, 0x3f, 0xfb, 0x78, 0xbe, 0xb8,
0x12, 0x32, 0xd6, 0xfb, 0xa3, 0x22, 0xde, 0x4e, 0x66, 0x75, 0xd2, 0xc9, 0xd6, 0xad, 0x1b, 0x39,
0xf7, 0xe9, 0x1e, 0x1c, 0x0a, 0x78, 0x5e, 0xaa, 0x7e, 0x7c, 0xae, 0x54, 0xcd, 0x5b, 0xc4, 0x88,
0x8a, 0x4f, 0x00, 0x49, 0x04, 0xed, 0xcf, 0x45, 0xa8, 0x26, 0xe8, 0xfc, 0x06, 0x75, 0x16, 0x51,
0xe2, 0x1a, 0x6e, 0x12, 0xc2, 0x14, 0x0c, 0x82, 0xb4, 0xc7, 0x82, 0xd8, 0xcb, 0x50, 0x63, 0x17,
0x35, 0x31, 0x5d, 0xe0, 0xd3, 0x55, 0x46, 0xe0, 0x93, 0xaf, 0x40, 0x9d, 0xfa, 0xd4, 0x9c, 0x18,
0x94, 0xe7, 0xf2, 0xa2, 0xe0, 0xe6, 0x24, 0x9e, 0xc9, 0xd1, 0x1b, 0x70, 0x85, 0x8e, 0x43, 0x9f,
0xd2, 0x09, 0xab, 0xef, 0x78, 0x45, 0x23, 0x0a, 0x90, 0x12, 0x56, 0xd3, 0x09, 0x51, 0xe9, 0x44,
0x2c, 0x7a, 0x4f, 0x17, 0x33, 0xd7, 0xe5, 0x41, 0xa4, 0x84, 0x57, 0x53, 0x2a, 0x73, 0x6d, 0x96,
0x3c, 0x03, 0x51, 0x2d, 0xf0, 0x58, 0xa1, 0xe0, 0x64, 0x88, 0x0c, 0x58, 0x73, 0x89, 0x19, 0xc5,
0x21, 0xb1, 0x8d, 0xc7, 0x0e, 0x99, 0xd8, 0xe2, 0xe2, 0xdb, 0xc8, 0x5d, 0x7e, 0x27, 0x6a, 0x69,
0xdf, 0xe7, 0xdc, 0xb8, 0x91, 0xc0, 0x89, 0x31, 0xab, 0x1c, 0xc4, 0x3f, 0xb4, 0x06, 0xf5, 0xc1,
0xa3, 0xc1, 0x50, 0xdf, 0x33, 0xf6, 0xf6, 0x7b, 0xba, 0xec, 0x8f, 0x0c, 0x74, 0x2c, 0x86, 0x0a,
0x9b, 0x1f, 0xee, 0x0f, 0x3b, 0xbb, 0xc6, 0x70, 0xa7, 0xfb, 0x60, 0xa0, 0x16, 0xd0, 0x75, 0xb8,
0x32, 0xdc, 0xc6, 0xfb, 0xc3, 0xe1, 0xae, 0xde, 0x33, 0x0e, 0x74, 0xbc, 0xb3, 0xdf, 0x1b, 0xa8,
0x45, 0x84, 0xa0, 0x31, 0x25, 0x0f, 0x77, 0xf6, 0x74, 0xb5, 0x84, 0xea, 0xb0, 0x7c, 0xa0, 0xe3,
0xae, 0xde, 0x1f, 0xaa, 0x65, 0xed, 0xef, 0x05, 0xa8, 0x67, 0xac, 0xc8, 0x1c, 0x39, 0x8c, 0x44,
0x9d, 0x5f, 0xc2, 0xec, 0x2f, 0x0b, 0x26, 0x96, 0x69, 0x8d, 0x85, 0x75, 0x4a, 0x58, 0x0c, 0x78,
0x6d, 0x6f, 0x9e, 0x66, 0xce, 0x79, 0x09, 0x57, 0x5d, 0xf3, 0x54, 0x80, 0xbc, 0x0a, 0x2b, 0xc7,
0x24, 0xf4, 0xc8, 0x44, 0xce, 0x0b, 0x8b, 0xd4, 0x05, 0x4d, 0x2c, 0x59, 0x07, 0x55, 0x2e, 0x99,
0xc2, 0x08, 0x73, 0x34, 0x04, 0x7d, 0x2f, 0x01, 0x3b, 0xba, 0xa8, 0xf5, 0x0a, 0xd7, 0xfa, 0xdd,
0xf9, 0x9d, 0xf4, 0x69, 0x8a, 0x1f, 0xa4, 0x8a, 0x5f, 0x86, 0x22, 0x4e, 0x5a, 0x05, 0xdd, 0x4e,
0x77, 0x9b, 0x29, 0x7b, 0x15, 0x6a, 0x7b, 0x9d, 0x4f, 0x8c, 0xc3, 0x01, 0x7f, 0x5c, 0x42, 0x2a,
0xac, 0x3c, 0xd0, 0x71, 0x5f, 0xdf, 0x95, 0x94, 0x22, 0xba, 0x06, 0xaa, 0xa4, 0x4c, 0xd7, 0x95,
0xb4, 0x7f, 0x15, 0x60, 0x4d, 0xc4, 0xf5, 0xf4, 0xbd, 0xf3, 0xe9, 0x0f, 0x8f, 0xd9, 0x67, 0x80,
0xc2, 0xcc, 0x33, 0x40, 0x5a, 0x45, 0xf2, 0xb4, 0x5c, 0x9c, 0x56, 0x91, 0xfc, 0xf9, 0x60, 0x26,
0x64, 0x97, 0xe6, 0x09, 0xd9, 0x4d, 0x58, 0x76, 0x49, 0x94, 0x2a, 0xbe, 0x86, 0x93, 0x21, 0x72,
0xa0, 0x6e, 0x7a, 0x9e, 0x4f, 0xf9, 0x63, 0x5b, 0x72, 0xaf, 0xd9, 0x9a, 0xeb, 0x59, 0x2f, 0xfd,
0xe2, 0x76, 0x67, 0x8a, 0x24, 0x22, 0x6b, 0x16, 0xbb, 0xf5, 0x3e, 0xa8, 0xe7, 0x17, 0xcc, 0x93,
0xcf, 0x5e, 0x7f, 0x7b, 0x9a, 0xce, 0x08, 0x73, 0xec, 0xc3, 0xfe, 0x83, 0xfe, 0xfe, 0xc3, 0xbe,
0xba, 0xc4, 0x06, 0xf8, 0xb0, 0xdf, 0xdf, 0xe9, 0x6f, 0xa9, 0x0a, 0x02, 0xa8, 0xe8, 0x9f, 0xec,
0x0c, 0xf5, 0x9e, 0x5a, 0xd8, 0xfc, 0xf7, 0x2a, 0x54, 0x84, 0x90, 0xe8, 0x2b, 0x99, 0xca, 0xb3,
0xbd, 0x71, 0xf4, 0xfe, 0xdc, 0x25, 0xf1, 0x4c, 0xbf, 0xbd, 0xf5, 0xc1, 0xc2, 0xfc, 0xf2, 0xbd,
0x7a, 0x09, 0xfd, 0x56, 0x81, 0x95, 0x99, 0x07, 0xda, 0xbc, 0x6f, 0x8b, 0x97, 0xb4, 0xe2, 0x5b,
0x3f, 0x58, 0x88, 0x37, 0x95, 0xe5, 0x37, 0x0a, 0xd4, 0x33, 0x4d, 0x68, 0x74, 0x77, 0x91, 0xc6,
0xb5, 0x90, 0xe4, 0xde, 0xe2, 0x3d, 0x6f, 0x6d, 0xe9, 0x2d, 0x05, 0xfd, 0x5a, 0x81, 0x7a, 0xa6,
0x1d, 0x9b, 0x5b, 0x94, 0x8b, 0xcd, 0xe3, 0xdc, 0xa2, 0x5c, 0xd6, 0xfd, 0x5d, 0x42, 0xbf, 0x54,
0xa0, 0x96, 0xb6, 0x56, 0xd1, 0xed, 0xf9, 0x9b, 0xb1, 0x42, 0x88, 0x3b, 0x8b, 0x76, 0x71, 0xb5,
0x25, 0xf4, 0x73, 0xa8, 0x26, 0x7d, 0x48, 0x94, 0x37, 0xfd, 0x9c, 0x6b, 0x72, 0xb6, 0x6e, 0xcf,
0xcd, 0x97, 0xdd, 0x3e, 0x69, 0x0e, 0xe6, 0xde, 0xfe, 0x5c, 0x1b, 0xb3, 0x75, 0x7b, 0x6e, 0xbe,
0x74, 0x7b, 0xe6, 0x09, 0x99, 0x1e, 0x62, 0x6e, 0x4f, 0xb8, 0xd8, 0xbc, 0xcc, 0xed, 0x09, 0x97,
0xb5, 0x2c, 0x85, 0x20, 0x99, 0x2e, 0x64, 0x6e, 0x41, 0x2e, 0x76, 0x3a, 0x73, 0x0b, 0x72, 0x49,
0xd3, 0x53, 0x5b, 0x42, 0x5f, 0x2a, 0xd9, 0xc2, 0xfe, 0xf6, 0xdc, 0xcd, 0xb9, 0x39, 0x5d, 0xf2,
0x42, 0x7b, 0x90, 0x1f, 0xd0, 0x2f, 0xe5, 0x33, 0x84, 0xe8, 0xed, 0xa1, 0x79, 0xc0, 0x66, 0xda,
0x81, 0xad, 0x5b, 0x8b, 0x25, 0x1b, 0x2e, 0xc4, 0xaf, 0x14, 0x80, 0x69, 0x17, 0x30, 0xb7, 0x10,
0x17, 0xda, 0x8f, 0xad, 0xbb, 0x0b, 0x70, 0x66, 0x0f, 0x48, 0xd2, 0xf8, 0xcb, 0x7d, 0x40, 0xce,
0x75, 0x29, 0x73, 0x1f, 0x90, 0xf3, 0x1d, 0x46, 0x6d, 0xe9, 0xa3, 0xe5, 0x1f, 0x95, 0x45, 0xf6,
0xaf, 0xf0, 0x9f, 0x77, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xa6, 0xcf, 0xe1, 0x38, 0x27,
var fileDescriptor_driver_d79b0e12b1c93702 = []byte{
// 2978 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xdb, 0x6f, 0xe3, 0xc6,
0xd5, 0x37, 0x75, 0xb3, 0x74, 0x64, 0xcb, 0xdc, 0xd9, 0xdd, 0x44, 0x51, 0xf0, 0x7d, 0xd9, 0x10,
0xc8, 0x07, 0x23, 0xc9, 0xca, 0x89, 0x83, 0x6f, 0x6f, 0xcd, 0x4d, 0x91, 0xb8, 0xb6, 0xb3, 0xb6,
0xec, 0x8e, 0x64, 0x6c, 0xb6, 0x6d, 0x96, 0xa5, 0xc9, 0x59, 0x89, 0x6b, 0xf1, 0x12, 0x72, 0xe8,
0xd8, 0x28, 0x8a, 0x16, 0x29, 0x50, 0xb4, 0x0f, 0x05, 0xfa, 0x12, 0xf4, 0xbd, 0x7d, 0xec, 0x5f,
0xd0, 0x16, 0xf9, 0x4b, 0xda, 0x97, 0x16, 0x28, 0xd0, 0xd7, 0x3e, 0xf6, 0xad, 0x98, 0x0b, 0x29,
0xca, 0xf6, 0x66, 0x29, 0x6d, 0x9e, 0xc8, 0x73, 0x66, 0xce, 0x6f, 0xce, 0xcc, 0x39, 0x33, 0xe7,
0xcc, 0x1c, 0xd0, 0x82, 0x49, 0x3c, 0x72, 0xbc, 0x68, 0xc3, 0x0e, 0x9d, 0x13, 0x12, 0x46, 0x1b,
0x41, 0xe8, 0x53, 0x5f, 0x52, 0x6d, 0x4e, 0xa0, 0x37, 0xc6, 0x66, 0x34, 0x76, 0x2c, 0x3f, 0x0c,
0xda, 0x9e, 0xef, 0x9a, 0x76, 0x5b, 0xca, 0xb4, 0xa5, 0x8c, 0xe8, 0xd6, 0xfa, 0xdf, 0x91, 0xef,
0x8f, 0x26, 0x44, 0x20, 0x1c, 0xc5, 0x4f, 0x36, 0xec, 0x38, 0x34, 0xa9, 0xe3, 0x7b, 0xb2, 0xfd,
0xb5, 0xf3, 0xed, 0xd4, 0x71, 0x49, 0x44, 0x4d, 0x37, 0x90, 0x1d, 0x3e, 0x1e, 0x39, 0x74, 0x1c,
0x1f, 0xb5, 0x2d, 0xdf, 0xdd, 0x48, 0x87, 0xdc, 0xe0, 0x43, 0x6e, 0x24, 0x6a, 0x46, 0x63, 0x33,
0x24, 0xf6, 0xc6, 0xd8, 0x9a, 0x44, 0x01, 0xb1, 0xd8, 0xd7, 0x60, 0x3f, 0x12, 0x61, 0x2b, 0x3f,
0x42, 0x44, 0xc3, 0xd8, 0xa2, 0xc9, 0x7c, 0x4d, 0x4a, 0x43, 0xe7, 0x28, 0xa6, 0x44, 0x00, 0x69,
0xaf, 0xc0, 0xcb, 0x43, 0x33, 0x3a, 0xee, 0xfa, 0xde, 0x13, 0x67, 0x34, 0xb0, 0xc6, 0xc4, 0x35,
0x31, 0xf9, 0x22, 0x26, 0x11, 0xd5, 0x7e, 0x04, 0xcd, 0x8b, 0x4d, 0x51, 0xe0, 0x7b, 0x11, 0x41,
0x1f, 0x43, 0x89, 0x69, 0xd3, 0x54, 0x6e, 0x28, 0xeb, 0xf5, 0xcd, 0xb7, 0xdb, 0xcf, 0x5a, 0x38,
0xa1, 0x43, 0x5b, 0xce, 0xa2, 0x3d, 0x08, 0x88, 0x85, 0xb9, 0xa4, 0x76, 0x1d, 0xae, 0x76, 0xcd,
0xc0, 0x3c, 0x72, 0x26, 0x0e, 0x75, 0x48, 0x94, 0x0c, 0x1a, 0xc3, 0xb5, 0x59, 0xb6, 0x1c, 0xf0,
0x73, 0x58, 0xb1, 0x32, 0x7c, 0x39, 0xf0, 0xdd, 0x76, 0x2e, 0x8b, 0xb5, 0x7b, 0x9c, 0x9a, 0x01,
0x9e, 0x81, 0xd3, 0xae, 0x01, 0xba, 0xef, 0x78, 0x23, 0x12, 0x06, 0xa1, 0xe3, 0xd1, 0x44, 0x99,
0x6f, 0x8a, 0x70, 0x75, 0x86, 0x2d, 0x95, 0x79, 0x0a, 0x90, 0xae, 0x23, 0x53, 0xa5, 0xb8, 0x5e,
0xdf, 0xfc, 0x34, 0xa7, 0x2a, 0x97, 0xe0, 0xb5, 0x3b, 0x29, 0x98, 0xee, 0xd1, 0xf0, 0x0c, 0x67,
0xd0, 0xd1, 0x63, 0xa8, 0x8c, 0x89, 0x39, 0xa1, 0xe3, 0x66, 0xe1, 0x86, 0xb2, 0xde, 0xd8, 0xbc,
0xff, 0x02, 0xe3, 0x6c, 0x73, 0xa0, 0x01, 0x35, 0x29, 0xc1, 0x12, 0x15, 0xdd, 0x04, 0x24, 0xfe,
0x0c, 0x9b, 0x44, 0x56, 0xe8, 0x04, 0xcc, 0x91, 0x9b, 0xc5, 0x1b, 0xca, 0x7a, 0x0d, 0x5f, 0x11,
0x2d, 0xbd, 0x69, 0x43, 0x2b, 0x80, 0xb5, 0x73, 0xda, 0x22, 0x15, 0x8a, 0xc7, 0xe4, 0x8c, 0x5b,
0xa4, 0x86, 0xd9, 0x2f, 0xda, 0x82, 0xf2, 0x89, 0x39, 0x89, 0x09, 0x57, 0xb9, 0xbe, 0xf9, 0xee,
0xf3, 0xdc, 0x43, 0xba, 0xe8, 0x74, 0x1d, 0xb0, 0x90, 0xbf, 0x57, 0xb8, 0xa3, 0x68, 0x77, 0xa1,
0x9e, 0xd1, 0x1b, 0x35, 0x00, 0x0e, 0xfb, 0x3d, 0x7d, 0xa8, 0x77, 0x87, 0x7a, 0x4f, 0x5d, 0x42,
0xab, 0x50, 0x3b, 0xec, 0x6f, 0xeb, 0x9d, 0xdd, 0xe1, 0xf6, 0x23, 0x55, 0x41, 0x75, 0x58, 0x4e,
0x88, 0x82, 0x76, 0x0a, 0x08, 0x13, 0xcb, 0x3f, 0x21, 0x21, 0x73, 0x64, 0x69, 0x55, 0xf4, 0x32,
0x2c, 0x53, 0x33, 0x3a, 0x36, 0x1c, 0x5b, 0xea, 0x5c, 0x61, 0xe4, 0x8e, 0x8d, 0x76, 0xa0, 0x32,
0x36, 0x3d, 0x7b, 0xf2, 0x7c, 0xbd, 0x67, 0x97, 0x9a, 0x81, 0x6f, 0x73, 0x41, 0x2c, 0x01, 0x98,
0x77, 0xcf, 0x8c, 0x2c, 0x0c, 0xa0, 0x3d, 0x02, 0x75, 0x40, 0xcd, 0x90, 0x66, 0xd5, 0xd1, 0xa1,
0xc4, 0xc6, 0x97, 0x1e, 0x3d, 0xcf, 0x98, 0x62, 0x67, 0x62, 0x2e, 0xae, 0xfd, 0xbb, 0x00, 0x57,
0x32, 0xd8, 0xd2, 0x53, 0x1f, 0x42, 0x25, 0x24, 0x51, 0x3c, 0xa1, 0x1c, 0xbe, 0xb1, 0xf9, 0x51,
0x4e, 0xf8, 0x0b, 0x48, 0x6d, 0xcc, 0x61, 0xb0, 0x84, 0x43, 0xeb, 0xa0, 0x0a, 0x09, 0x83, 0x84,
0xa1, 0x1f, 0x1a, 0x6e, 0x34, 0xe2, 0xab, 0x56, 0xc3, 0x0d, 0xc1, 0xd7, 0x19, 0x7b, 0x2f, 0x1a,
0x65, 0x56, 0xb5, 0xf8, 0x82, 0xab, 0x8a, 0x4c, 0x50, 0x3d, 0x42, 0xbf, 0xf4, 0xc3, 0x63, 0x83,
0x2d, 0x6d, 0xe8, 0xd8, 0xa4, 0x59, 0xe2, 0xa0, 0xb7, 0x72, 0x82, 0xf6, 0x85, 0xf8, 0xbe, 0x94,
0xc6, 0x6b, 0xde, 0x2c, 0x43, 0x7b, 0x0b, 0x2a, 0x62, 0xa6, 0xcc, 0x93, 0x06, 0x87, 0xdd, 0xae,
0x3e, 0x18, 0xa8, 0x4b, 0xa8, 0x06, 0x65, 0xac, 0x0f, 0x31, 0xf3, 0xb0, 0x1a, 0x94, 0xef, 0x77,
0x86, 0x9d, 0x5d, 0xb5, 0xa0, 0xbd, 0x09, 0x6b, 0x0f, 0x4d, 0x87, 0xe6, 0x71, 0x2e, 0xcd, 0x07,
0x75, 0xda, 0x57, 0x5a, 0x67, 0x67, 0xc6, 0x3a, 0xf9, 0x97, 0x46, 0x3f, 0x75, 0xe8, 0x39, 0x7b,
0xa8, 0x50, 0x24, 0x61, 0x28, 0x4d, 0xc0, 0x7e, 0xb5, 0x2f, 0x61, 0x6d, 0x40, 0xfd, 0x20, 0x97,
0xe7, 0xbf, 0x07, 0xcb, 0x2c, 0x46, 0xf9, 0x31, 0x95, 0xae, 0xff, 0x4a, 0x5b, 0xc4, 0xb0, 0x76,
0x12, 0xc3, 0xda, 0x3d, 0x19, 0xe3, 0x70, 0xd2, 0x13, 0xbd, 0x04, 0x95, 0xc8, 0x19, 0x79, 0xe6,
0x44, 0x9e, 0x16, 0x92, 0xd2, 0x10, 0x73, 0xf2, 0x64, 0x60, 0xe9, 0xf8, 0x5d, 0x40, 0x3d, 0x12,
0xd1, 0xd0, 0x3f, 0xcb, 0xa5, 0xcf, 0x35, 0x28, 0x3f, 0xf1, 0x43, 0x4b, 0x6c, 0xc4, 0x2a, 0x16,
0x04, 0xdb, 0x54, 0x33, 0x20, 0x12, 0xfb, 0x26, 0xa0, 0x1d, 0x8f, 0xc5, 0x94, 0x7c, 0x86, 0xf8,
0x6d, 0x01, 0xae, 0xce, 0xf4, 0x97, 0xc6, 0x58, 0x7c, 0x1f, 0xb2, 0x83, 0x29, 0x8e, 0xc4, 0x3e,
0x44, 0xfb, 0x50, 0x11, 0x3d, 0xe4, 0x4a, 0xde, 0x9e, 0x03, 0x48, 0x84, 0x29, 0x09, 0x27, 0x61,
0x2e, 0x75, 0xfa, 0xe2, 0x77, 0xeb, 0xf4, 0x5b, 0xa0, 0x26, 0xf3, 0x88, 0x9e, 0x6b, 0x9b, 0x16,
0x54, 0x1d, 0x8f, 0x92, 0xf0, 0xc4, 0x9c, 0xf0, 0x29, 0x16, 0x71, 0x4a, 0x6b, 0x3f, 0x84, 0x2b,
0x19, 0x20, 0xb9, 0xb0, 0xf7, 0xa1, 0x1c, 0x31, 0x86, 0x5c, 0xd9, 0x77, 0xe6, 0x5c, 0xd9, 0x08,
0x0b, 0x71, 0xed, 0xaa, 0x00, 0xd7, 0x4f, 0x88, 0x97, 0xaa, 0xa9, 0xf5, 0xe0, 0xca, 0x80, 0xbb,
0x5d, 0x2e, 0xbf, 0x9a, 0xba, 0x6c, 0x61, 0xc6, 0x65, 0xaf, 0x01, 0xca, 0xa2, 0x48, 0xc7, 0x3a,
0x83, 0x35, 0xfd, 0x94, 0x58, 0xb9, 0x90, 0x9b, 0xb0, 0x6c, 0xf9, 0xae, 0x6b, 0x7a, 0x76, 0xb3,
0x70, 0xa3, 0xb8, 0x5e, 0xc3, 0x09, 0x99, 0xdd, 0x5b, 0xc5, 0xbc, 0x7b, 0x4b, 0xfb, 0x8d, 0x02,
0xea, 0x74, 0x6c, 0xb9, 0x90, 0x4c, 0x7b, 0x6a, 0x33, 0x20, 0x36, 0xf6, 0x0a, 0x96, 0x94, 0xe4,
0x27, 0xdb, 0x5f, 0xf0, 0x49, 0x18, 0x66, 0x8e, 0x97, 0xe2, 0x0b, 0x1e, 0x2f, 0xda, 0x3f, 0x15,
0x40, 0x17, 0x93, 0x28, 0xf4, 0x3a, 0xac, 0x44, 0xc4, 0xb3, 0x0d, 0xb1, 0x8c, 0xc2, 0xc2, 0x55,
0x5c, 0x67, 0x3c, 0xb1, 0x9e, 0x11, 0x42, 0x50, 0x22, 0xa7, 0xc4, 0x92, 0x3b, 0x99, 0xff, 0xa3,
0x31, 0xac, 0x3c, 0x89, 0x0c, 0x27, 0xf2, 0x27, 0x66, 0x9a, 0x6d, 0x34, 0x36, 0xf5, 0x85, 0x93,
0xb9, 0xf6, 0xfd, 0xc1, 0x4e, 0x02, 0x86, 0xeb, 0x4f, 0xa2, 0x94, 0xd0, 0xda, 0x50, 0xcf, 0xb4,
0xa1, 0x2a, 0x94, 0xfa, 0xfb, 0x7d, 0x5d, 0x5d, 0x42, 0x00, 0x95, 0xee, 0x36, 0xde, 0xdf, 0x1f,
0x8a, 0x13, 0x7d, 0x67, 0xaf, 0xb3, 0xa5, 0xab, 0x05, 0xed, 0x4f, 0x15, 0x80, 0x69, 0x68, 0x45,
0x0d, 0x28, 0xa4, 0x96, 0x2e, 0x38, 0x36, 0x9b, 0x8c, 0x67, 0xba, 0x44, 0x7a, 0x0f, 0xff, 0x47,
0x9b, 0x70, 0xdd, 0x8d, 0x46, 0x81, 0x69, 0x1d, 0x1b, 0x32, 0x22, 0x5a, 0x5c, 0x98, 0xcf, 0x6a,
0x05, 0x5f, 0x95, 0x8d, 0x52, 0x6b, 0x81, 0xbb, 0x0b, 0x45, 0xe2, 0x9d, 0x34, 0x4b, 0x3c, 0x73,
0xbc, 0x37, 0x77, 0xc8, 0x6f, 0xeb, 0xde, 0x89, 0xc8, 0x14, 0x19, 0x0c, 0x32, 0x00, 0x6c, 0x72,
0xe2, 0x58, 0xc4, 0x60, 0xa0, 0x65, 0x0e, 0xfa, 0xf1, 0xfc, 0xa0, 0x3d, 0x8e, 0x91, 0x42, 0xd7,
0xec, 0x84, 0x46, 0x7d, 0xa8, 0x85, 0x24, 0xf2, 0xe3, 0xd0, 0x22, 0x51, 0xb3, 0x32, 0xd7, 0x2e,
0xc6, 0x89, 0x1c, 0x9e, 0x42, 0xa0, 0x1e, 0x54, 0x5c, 0x3f, 0xf6, 0x68, 0xd4, 0x5c, 0xe6, 0xca,
0xbe, 0x9d, 0x13, 0x6c, 0x8f, 0x09, 0x61, 0x29, 0x8b, 0xb6, 0x60, 0x59, 0xa8, 0x18, 0x35, 0xab,
0x1c, 0xe6, 0x66, 0x5e, 0x07, 0xe2, 0x52, 0x38, 0x91, 0x66, 0x56, 0x8d, 0x23, 0x12, 0x36, 0x6b,
0xc2, 0xaa, 0xec, 0x1f, 0xbd, 0x0a, 0x35, 0x73, 0x32, 0xf1, 0x2d, 0xc3, 0x76, 0xc2, 0x26, 0xf0,
0x86, 0x2a, 0x67, 0xf4, 0x9c, 0x10, 0xbd, 0x06, 0x75, 0xb1, 0xf5, 0x8c, 0xc0, 0xa4, 0xe3, 0x66,
0x9d, 0x37, 0x83, 0x60, 0x1d, 0x98, 0x74, 0x2c, 0x3b, 0x90, 0x30, 0x14, 0x1d, 0x56, 0xd2, 0x0e,
0x24, 0x0c, 0x79, 0x87, 0xff, 0x83, 0x35, 0x7e, 0x8e, 0x8c, 0x42, 0x3f, 0x0e, 0x0c, 0xee, 0x53,
0xab, 0xbc, 0xd3, 0x2a, 0x63, 0x6f, 0x31, 0x6e, 0x9f, 0x39, 0xd7, 0x2b, 0x50, 0x7d, 0xea, 0x1f,
0x89, 0x0e, 0x0d, 0xde, 0x61, 0xf9, 0xa9, 0x7f, 0x94, 0x34, 0x09, 0x0d, 0x1d, 0xbb, 0xb9, 0x26,
0x9a, 0x38, 0xbd, 0x63, 0xb7, 0x6e, 0x41, 0x35, 0x31, 0xe3, 0x25, 0xd9, 0xf9, 0xb5, 0x6c, 0x76,
0x5e, 0xcb, 0xa4, 0xda, 0xad, 0xf7, 0xa1, 0x31, 0xeb, 0x04, 0xf3, 0x48, 0x6b, 0x7f, 0x55, 0xa0,
0x96, 0x9a, 0x1b, 0x79, 0x70, 0x95, 0xab, 0x63, 0x52, 0x62, 0x1b, 0x53, 0xef, 0x11, 0x31, 0xe0,
0x83, 0x9c, 0x96, 0xea, 0x24, 0x08, 0xf2, 0x1c, 0x94, 0xae, 0x84, 0x52, 0xe4, 0xe9, 0x78, 0x8f,
0x61, 0x6d, 0xe2, 0x78, 0xf1, 0x69, 0x66, 0x2c, 0x11, 0x80, 0xff, 0x3f, 0xe7, 0x58, 0xbb, 0x4c,
0x7a, 0x3a, 0x46, 0x63, 0x32, 0x43, 0x6b, 0x5f, 0x17, 0xe0, 0xa5, 0xcb, 0xd5, 0x41, 0x7d, 0x28,
0x5a, 0x41, 0x2c, 0xa7, 0xf6, 0xfe, 0xbc, 0x53, 0xeb, 0x06, 0xf1, 0x74, 0x54, 0x06, 0xc4, 0x92,
0x76, 0x97, 0xb8, 0x7e, 0x78, 0x26, 0x67, 0xf0, 0xd1, 0xbc, 0x90, 0x7b, 0x5c, 0x7a, 0x8a, 0x2a,
0xe1, 0x10, 0x86, 0xaa, 0x0c, 0xfd, 0x91, 0x3c, 0x26, 0xe6, 0x4c, 0x21, 0x12, 0x48, 0x9c, 0xe2,
0x68, 0xb7, 0xe0, 0xfa, 0xa5, 0x53, 0x41, 0xff, 0x03, 0x60, 0x05, 0xb1, 0xc1, 0xaf, 0x78, 0xc2,
0xee, 0x45, 0x5c, 0xb3, 0x82, 0x78, 0xc0, 0x19, 0xda, 0x6d, 0x68, 0x3e, 0x4b, 0x5f, 0xb6, 0xf9,
0x84, 0xc6, 0x86, 0x7b, 0x94, 0xe4, 0x18, 0x82, 0xb1, 0x77, 0xa4, 0xfd, 0xae, 0x00, 0x6b, 0xe7,
0xd4, 0x61, 0x11, 0x50, 0x6c, 0xe6, 0x24, 0x2a, 0x0b, 0x8a, 0xed, 0x6c, 0xcb, 0xb1, 0x93, 0xb4,
0x98, 0xff, 0xf3, 0x33, 0x3d, 0x90, 0x29, 0x6b, 0xc1, 0x09, 0x98, 0x43, 0xbb, 0x47, 0x0e, 0x8d,
0xf8, 0x4d, 0xa2, 0x8c, 0x05, 0x81, 0x1e, 0x41, 0x23, 0x24, 0x11, 0x09, 0x4f, 0x88, 0x6d, 0x04,
0x7e, 0x48, 0x93, 0x05, 0xdb, 0x9c, 0x6f, 0xc1, 0x0e, 0xfc, 0x90, 0xe2, 0xd5, 0x04, 0x89, 0x51,
0x11, 0x7a, 0x08, 0xab, 0xf6, 0x99, 0x67, 0xba, 0x8e, 0x25, 0x91, 0x2b, 0x0b, 0x23, 0xaf, 0x48,
0x20, 0x0e, 0xcc, 0x6e, 0xca, 0x99, 0x46, 0x36, 0xb1, 0x89, 0x79, 0x44, 0x26, 0x72, 0x4d, 0x04,
0x31, 0xbb, 0x7f, 0xcb, 0x72, 0xff, 0x6a, 0x7f, 0x28, 0x40, 0x63, 0x76, 0x03, 0x24, 0xf6, 0x0b,
0x48, 0xe8, 0xf8, 0x76, 0xc6, 0x7e, 0x07, 0x9c, 0xc1, 0x6c, 0xc4, 0x9a, 0xbf, 0x88, 0x7d, 0x6a,
0x26, 0x36, 0xb2, 0x82, 0xf8, 0xfb, 0x8c, 0x3e, 0x67, 0xfb, 0xe2, 0x39, 0xdb, 0xa3, 0xb7, 0x01,
0x49, 0xfb, 0x4e, 0x1c, 0xd7, 0xa1, 0xc6, 0xd1, 0x19, 0x25, 0x62, 0xfd, 0x8b, 0x58, 0x15, 0x2d,
0xbb, 0xac, 0xe1, 0x13, 0xc6, 0x47, 0x1a, 0xac, 0xfa, 0xbe, 0x6b, 0x44, 0x96, 0x1f, 0x12, 0xc3,
0xb4, 0x9f, 0x36, 0xcb, 0xbc, 0x63, 0xdd, 0xf7, 0xdd, 0x01, 0xe3, 0x75, 0xec, 0xa7, 0xec, 0xc0,
0xb5, 0x82, 0x38, 0x22, 0xd4, 0x60, 0x1f, 0x1e, 0xa3, 0x6a, 0x18, 0x04, 0xab, 0x1b, 0xc4, 0x51,
0xa6, 0x83, 0x4b, 0x5c, 0x16, 0x77, 0x32, 0x1d, 0xf6, 0x88, 0xcb, 0x46, 0x59, 0x39, 0x20, 0xa1,
0x45, 0x3c, 0x3a, 0x74, 0xac, 0x63, 0x16, 0x52, 0x94, 0x75, 0x05, 0xcf, 0xf0, 0xb4, 0xcf, 0xa1,
0xcc, 0x43, 0x10, 0x9b, 0x3c, 0x3f, 0xbe, 0xf9, 0xe9, 0x2e, 0x96, 0xb7, 0xca, 0x18, 0xfc, 0x6c,
0x7f, 0x15, 0x6a, 0x63, 0x3f, 0x92, 0xb1, 0x41, 0x78, 0x5e, 0x95, 0x31, 0x78, 0x63, 0x0b, 0xaa,
0x21, 0x31, 0x6d, 0xdf, 0x9b, 0x9c, 0xf1, 0x75, 0xa9, 0xe2, 0x94, 0xd6, 0xbe, 0x80, 0x8a, 0x38,
0x7e, 0x5f, 0x00, 0xff, 0x26, 0x20, 0x4b, 0x04, 0x95, 0x80, 0x84, 0xae, 0x13, 0x45, 0x8e, 0xef,
0x45, 0xc9, 0x73, 0x8e, 0x68, 0x39, 0x98, 0x36, 0x68, 0x7f, 0x53, 0x44, 0xbe, 0x23, 0x2e, 0xda,
0x2c, 0x8b, 0x65, 0x9e, 0xc6, 0x72, 0x32, 0x85, 0xbb, 0x47, 0x42, 0xb2, 0x5c, 0x52, 0xa6, 0x35,
0x85, 0x45, 0xdf, 0x29, 0x24, 0x40, 0x72, 0x1f, 0x20, 0x32, 0xed, 0x9b, 0xf7, 0x3e, 0x40, 0xc4,
0x7d, 0x80, 0xb0, 0xe4, 0x53, 0x26, 0x5c, 0x02, 0xae, 0xc4, 0xf3, 0xad, 0xba, 0x9d, 0x5e, 0xa2,
0x88, 0xf6, 0x2f, 0x25, 0x3d, 0x2b, 0x92, 0xcb, 0x0e, 0x7a, 0x0c, 0x55, 0xb6, 0xed, 0x0c, 0xd7,
0x0c, 0xe4, 0xd3, 0x5d, 0x77, 0xb1, 0x7b, 0x54, 0x9b, 0xed, 0xb2, 0x3d, 0x33, 0x10, 0xe9, 0xd2,
0x72, 0x20, 0x28, 0x76, 0xe6, 0x98, 0xf6, 0xf4, 0xcc, 0x61, 0xff, 0xe8, 0x0d, 0x68, 0x98, 0x31,
0xf5, 0x0d, 0xd3, 0x3e, 0x21, 0x21, 0x75, 0x22, 0x22, 0x6d, 0xbf, 0xca, 0xb8, 0x9d, 0x84, 0xd9,
0xba, 0x07, 0x2b, 0x59, 0xcc, 0xe7, 0x45, 0xdf, 0x72, 0x36, 0xfa, 0xfe, 0x18, 0x60, 0x9a, 0xb7,
0x33, 0x1f, 0x21, 0xa7, 0x0e, 0x35, 0x2c, 0xdf, 0x26, 0xd2, 0x94, 0x55, 0xc6, 0xe8, 0xfa, 0x36,
0x39, 0x77, 0x0b, 0x2a, 0x27, 0xb7, 0x20, 0xb6, 0x6b, 0xd9, 0x46, 0x3b, 0x76, 0x26, 0x13, 0x62,
0x4b, 0x0d, 0x6b, 0xbe, 0xef, 0x3e, 0xe0, 0x0c, 0xed, 0x9b, 0x82, 0xf0, 0x15, 0x71, 0x3f, 0xcd,
0x95, 0x1b, 0x7f, 0x57, 0xa6, 0xbe, 0x0b, 0x10, 0x51, 0x33, 0x64, 0xa9, 0x84, 0x49, 0xe5, 0x93,
0x4f, 0xeb, 0xc2, 0x35, 0x6a, 0x98, 0x3c, 0xb3, 0xe3, 0x9a, 0xec, 0xdd, 0xa1, 0xe8, 0x03, 0x58,
0xb1, 0x7c, 0x37, 0x98, 0x10, 0x29, 0x5c, 0x7e, 0xae, 0x70, 0x3d, 0xed, 0xdf, 0xa1, 0x99, 0x3b,
0x54, 0xe5, 0x45, 0xef, 0x50, 0x7f, 0x56, 0xc4, 0x35, 0x3b, 0x7b, 0xcb, 0x47, 0xa3, 0x4b, 0x9e,
0x92, 0xb7, 0x16, 0x7c, 0x32, 0xf8, 0xb6, 0x77, 0xe4, 0xd6, 0x07, 0x79, 0x1e, 0x6e, 0x9f, 0x9d,
0xdc, 0xfd, 0xa5, 0x08, 0xb5, 0xf4, 0x46, 0x7e, 0xc1, 0xf6, 0x77, 0xa0, 0x96, 0xd6, 0x38, 0xe4,
0x01, 0xf1, 0xad, 0xe6, 0x49, 0x3b, 0xa3, 0x27, 0x80, 0xcc, 0xd1, 0x28, 0x4d, 0xda, 0x8c, 0x38,
0x32, 0x47, 0xc9, 0xfb, 0xc6, 0x9d, 0x39, 0xd6, 0x21, 0x89, 0x5b, 0x87, 0x4c, 0x1e, 0xab, 0xe6,
0x68, 0x34, 0xc3, 0x41, 0x3f, 0x81, 0xeb, 0xb3, 0x63, 0x18, 0x47, 0x67, 0x46, 0xe0, 0xd8, 0xf2,
0x0e, 0xb6, 0x3d, 0xef, 0xa3, 0x44, 0x7b, 0x06, 0xfe, 0x93, 0xb3, 0x03, 0xc7, 0x16, 0x6b, 0x8e,
0xc2, 0x0b, 0x0d, 0xad, 0x9f, 0xc1, 0xcb, 0xcf, 0xe8, 0x7e, 0x89, 0x0d, 0xfa, 0xb3, 0x8f, 0xe7,
0x8b, 0x2f, 0x42, 0xc6, 0x7a, 0xbf, 0x57, 0xc4, 0xdb, 0xc9, 0xec, 0x9a, 0x74, 0xb2, 0x79, 0xeb,
0x46, 0xce, 0x71, 0xba, 0x07, 0x87, 0x02, 0x9e, 0xa7, 0xaa, 0x9f, 0x9e, 0x4b, 0x55, 0xf3, 0x26,
0x31, 0x22, 0xe3, 0x13, 0x40, 0x12, 0x41, 0xfb, 0x63, 0x11, 0xaa, 0x09, 0x3a, 0xbf, 0x41, 0x9d,
0x45, 0x94, 0xb8, 0x86, 0x9b, 0x1c, 0x61, 0x0a, 0x06, 0xc1, 0xda, 0x63, 0x87, 0xd8, 0xab, 0x50,
0x63, 0x17, 0x35, 0xd1, 0x5c, 0xe0, 0xcd, 0x55, 0xc6, 0xe0, 0x8d, 0xaf, 0x41, 0x9d, 0xfa, 0xd4,
0x9c, 0x18, 0x94, 0xc7, 0xf2, 0xa2, 0x90, 0xe6, 0x2c, 0x1e, 0xc9, 0xd1, 0x5b, 0x70, 0x85, 0x8e,
0x43, 0x9f, 0xd2, 0x09, 0xcb, 0xef, 0x78, 0x46, 0x23, 0x12, 0x90, 0x12, 0x56, 0xd3, 0x06, 0x91,
0xe9, 0x44, 0xec, 0xf4, 0x9e, 0x76, 0x66, 0xae, 0xcb, 0x0f, 0x91, 0x12, 0x5e, 0x4d, 0xb9, 0xcc,
0xb5, 0x59, 0xf0, 0x0c, 0x44, 0xb6, 0xc0, 0xcf, 0x0a, 0x05, 0x27, 0x24, 0x32, 0x60, 0xcd, 0x25,
0x66, 0x14, 0x87, 0xc4, 0x36, 0x9e, 0x38, 0x64, 0x62, 0x8b, 0x8b, 0x6f, 0x23, 0x77, 0xfa, 0x9d,
0x2c, 0x4b, 0xfb, 0x3e, 0x97, 0xc6, 0x8d, 0x04, 0x4e, 0xd0, 0x2c, 0x73, 0x10, 0x7f, 0x68, 0x0d,
0xea, 0x83, 0x47, 0x83, 0xa1, 0xbe, 0x67, 0xec, 0xed, 0xf7, 0x74, 0x59, 0x1f, 0x19, 0xe8, 0x58,
0x90, 0x0a, 0x6b, 0x1f, 0xee, 0x0f, 0x3b, 0xbb, 0xc6, 0x70, 0xa7, 0xfb, 0x60, 0xa0, 0x16, 0xd0,
0x75, 0xb8, 0x32, 0xdc, 0xc6, 0xfb, 0xc3, 0xe1, 0xae, 0xde, 0x33, 0x0e, 0x74, 0xbc, 0xb3, 0xdf,
0x1b, 0xa8, 0x45, 0x84, 0xa0, 0x31, 0x65, 0x0f, 0x77, 0xf6, 0x74, 0xb5, 0x84, 0xea, 0xb0, 0x7c,
0xa0, 0xe3, 0xae, 0xde, 0x1f, 0xaa, 0x65, 0xed, 0x3f, 0x05, 0xa8, 0x67, 0xac, 0xc8, 0x1c, 0x39,
0x8c, 0x44, 0x9e, 0x5f, 0xc2, 0xec, 0x97, 0x1d, 0x26, 0x96, 0x69, 0x8d, 0x85, 0x75, 0x4a, 0x58,
0x10, 0x3c, 0xb7, 0x37, 0x4f, 0x33, 0xfb, 0xbc, 0x84, 0xab, 0xae, 0x79, 0x2a, 0x40, 0x5e, 0x87,
0x95, 0x63, 0x12, 0x7a, 0x64, 0x22, 0xdb, 0x85, 0x45, 0xea, 0x82, 0x27, 0xba, 0xac, 0x83, 0x2a,
0xbb, 0x4c, 0x61, 0x84, 0x39, 0x1a, 0x82, 0xbf, 0x97, 0x80, 0x5d, 0x83, 0xb2, 0x68, 0x5e, 0x16,
0xe3, 0x73, 0x02, 0x1d, 0x5d, 0xb4, 0x45, 0x85, 0xdb, 0xe2, 0xee, 0xfc, 0xae, 0xfb, 0x2c, 0x73,
0x3c, 0x4e, 0xcd, 0xb1, 0x0c, 0x45, 0x9c, 0x14, 0x10, 0xba, 0x9d, 0xee, 0x36, 0x33, 0xc1, 0x2a,
0xd4, 0xf6, 0x3a, 0x9f, 0x19, 0x87, 0x03, 0xfe, 0xe4, 0x84, 0x54, 0x58, 0x79, 0xa0, 0xe3, 0xbe,
0xbe, 0x2b, 0x39, 0x45, 0x74, 0x0d, 0x54, 0xc9, 0x99, 0xf6, 0x2b, 0x31, 0x04, 0xf1, 0x5b, 0xd6,
0xfe, 0x5e, 0x80, 0x35, 0x71, 0xf0, 0xa7, 0x0f, 0xa2, 0xcf, 0x7e, 0x99, 0xcc, 0xbe, 0x13, 0x14,
0x66, 0xde, 0x09, 0xd2, 0x34, 0x93, 0xc7, 0xed, 0xe2, 0x34, 0xcd, 0xe4, 0xef, 0x0b, 0x33, 0x67,
0x7a, 0x69, 0x9e, 0x33, 0xbd, 0x09, 0xcb, 0x2e, 0x89, 0x52, 0xcb, 0xd4, 0x70, 0x42, 0x22, 0x07,
0xea, 0xa6, 0xe7, 0xf9, 0x94, 0xbf, 0xc6, 0x25, 0x17, 0x9f, 0xad, 0xb9, 0xde, 0xfd, 0xd2, 0x19,
0xb7, 0x3b, 0x53, 0x24, 0x71, 0xf4, 0x66, 0xb1, 0x5b, 0x1f, 0x82, 0x7a, 0xbe, 0xc3, 0x3c, 0x01,
0xef, 0xcd, 0x77, 0xa7, 0xf1, 0x8e, 0x30, 0xcf, 0x3f, 0xec, 0x3f, 0xe8, 0xef, 0x3f, 0xec, 0xab,
0x4b, 0x8c, 0xc0, 0x87, 0xfd, 0xfe, 0x4e, 0x7f, 0x4b, 0x55, 0x10, 0x40, 0x45, 0xff, 0x6c, 0x67,
0xa8, 0xf7, 0xd4, 0xc2, 0xe6, 0x3f, 0x56, 0xa1, 0x22, 0x94, 0x44, 0x5f, 0xcb, 0x58, 0x9f, 0x2d,
0x9e, 0xa3, 0x0f, 0xe7, 0xce, 0x99, 0x67, 0x0a, 0xf2, 0xad, 0x8f, 0x16, 0x96, 0x97, 0x0f, 0xda,
0x4b, 0xe8, 0xd7, 0x0a, 0xac, 0xcc, 0xbc, 0xe0, 0xe6, 0x7d, 0x7c, 0xbc, 0xa4, 0x56, 0xdf, 0xfa,
0xde, 0x42, 0xb2, 0xa9, 0x2e, 0xbf, 0x52, 0xa0, 0x9e, 0xa9, 0x52, 0xa3, 0xbb, 0x8b, 0x54, 0xb6,
0x85, 0x26, 0xf7, 0x16, 0x2f, 0x8a, 0x6b, 0x4b, 0xef, 0x28, 0xe8, 0x97, 0x0a, 0xd4, 0x33, 0xf5,
0xda, 0xdc, 0xaa, 0x5c, 0xac, 0x2e, 0xe7, 0x56, 0xe5, 0xb2, 0xf2, 0xf0, 0x12, 0xfa, 0xb9, 0x02,
0xb5, 0xb4, 0xf6, 0x8a, 0x6e, 0xcf, 0x5f, 0xad, 0x15, 0x4a, 0xdc, 0x59, 0xb4, 0xcc, 0xab, 0x2d,
0xa1, 0x9f, 0x42, 0x35, 0x29, 0x54, 0xa2, 0xbc, 0xf1, 0xe9, 0x5c, 0x15, 0xb4, 0x75, 0x7b, 0x6e,
0xb9, 0xec, 0xf0, 0x49, 0xf5, 0x30, 0xf7, 0xf0, 0xe7, 0xea, 0x9c, 0xad, 0xdb, 0x73, 0xcb, 0xa5,
0xc3, 0x33, 0x4f, 0xc8, 0x14, 0x19, 0x73, 0x7b, 0xc2, 0xc5, 0xea, 0x66, 0x6e, 0x4f, 0xb8, 0xac,
0xa6, 0x29, 0x14, 0xc9, 0x94, 0x29, 0x73, 0x2b, 0x72, 0xb1, 0x14, 0x9a, 0x5b, 0x91, 0x4b, 0xaa,
0xa2, 0xda, 0x12, 0xfa, 0x4a, 0xc9, 0x66, 0xfe, 0xb7, 0xe7, 0xae, 0xde, 0xcd, 0xe9, 0x92, 0x17,
0xea, 0x87, 0x7c, 0x83, 0x7e, 0x25, 0xdf, 0x29, 0x44, 0xf1, 0x0f, 0xcd, 0x03, 0x36, 0x53, 0x2f,
0x6c, 0xdd, 0x5a, 0x2c, 0xd8, 0x70, 0x25, 0x7e, 0xa1, 0x00, 0x4c, 0xcb, 0x84, 0xb9, 0x95, 0xb8,
0x50, 0x9f, 0x6c, 0xdd, 0x5d, 0x40, 0x32, 0xbb, 0x41, 0x92, 0xca, 0x60, 0xee, 0x0d, 0x72, 0xae,
0x8c, 0x99, 0x7b, 0x83, 0x9c, 0x2f, 0x41, 0x6a, 0x4b, 0x9f, 0x2c, 0xff, 0xa0, 0x2c, 0xa2, 0x7f,
0x85, 0x7f, 0xde, 0xfb, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe4, 0xbf, 0x79, 0x07, 0x59, 0x27,
0x00, 0x00,
}

View File

@ -568,6 +568,7 @@ message MemoryUsage {
uint64 max_usage = 3;
uint64 kernel_usage = 4;
uint64 kernel_max_usage = 5;
uint64 usage = 7;
enum Fields {
RSS = 0;
@ -575,6 +576,7 @@ message MemoryUsage {
MAX_USAGE = 2;
KERNEL_USAGE = 3;
KERNEL_MAX_USAGE = 4;
USAGE = 5;
}
// MeasuredFields indicates which fields were actually sampled
repeated Fields measured_fields = 6;

View File

@ -454,6 +454,9 @@ func resourceUsageToProto(ru *ResourceUsage) *proto.TaskResourceUsage {
case "Cache":
memory.Cache = ru.MemoryStats.Cache
memory.MeasuredFields = append(memory.MeasuredFields, proto.MemoryUsage_CACHE)
case "Usage":
memory.Usage = ru.MemoryStats.Usage
memory.MeasuredFields = append(memory.MeasuredFields, proto.MemoryUsage_USAGE)
case "Max Usage":
memory.MaxUsage = ru.MemoryStats.MaxUsage
memory.MeasuredFields = append(memory.MeasuredFields, proto.MemoryUsage_MAX_USAGE)
@ -509,6 +512,9 @@ func resourceUsageFromProto(pb *proto.TaskResourceUsage) *ResourceUsage {
case proto.MemoryUsage_CACHE:
memory.Cache = pb.Memory.Cache
memory.Measured = append(memory.Measured, "Cache")
case proto.MemoryUsage_USAGE:
memory.Usage = pb.Memory.Usage
memory.Measured = append(memory.Measured, "Usage")
case proto.MemoryUsage_MAX_USAGE:
memory.MaxUsage = pb.Memory.MaxUsage
memory.Measured = append(memory.Measured, "Max Usage")

View File

@ -94,7 +94,7 @@ func (m *Spec) Reset() { *m = Spec{} }
func (m *Spec) String() string { return proto.CompactTextString(m) }
func (*Spec) ProtoMessage() {}
func (*Spec) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{0}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{0}
}
func (m *Spec) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Spec.Unmarshal(m, b)
@ -522,7 +522,7 @@ func (m *Attr) Reset() { *m = Attr{} }
func (m *Attr) String() string { return proto.CompactTextString(m) }
func (*Attr) ProtoMessage() {}
func (*Attr) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{1}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{1}
}
func (m *Attr) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Attr.Unmarshal(m, b)
@ -611,7 +611,7 @@ func (m *Block) Reset() { *m = Block{} }
func (m *Block) String() string { return proto.CompactTextString(m) }
func (*Block) ProtoMessage() {}
func (*Block) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{2}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{2}
}
func (m *Block) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Block.Unmarshal(m, b)
@ -697,7 +697,7 @@ func (m *BlockAttrs) Reset() { *m = BlockAttrs{} }
func (m *BlockAttrs) String() string { return proto.CompactTextString(m) }
func (*BlockAttrs) ProtoMessage() {}
func (*BlockAttrs) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{3}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{3}
}
func (m *BlockAttrs) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockAttrs.Unmarshal(m, b)
@ -792,7 +792,7 @@ func (m *BlockList) Reset() { *m = BlockList{} }
func (m *BlockList) String() string { return proto.CompactTextString(m) }
func (*BlockList) ProtoMessage() {}
func (*BlockList) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{4}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{4}
}
func (m *BlockList) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockList.Unmarshal(m, b)
@ -875,7 +875,7 @@ func (m *BlockSet) Reset() { *m = BlockSet{} }
func (m *BlockSet) String() string { return proto.CompactTextString(m) }
func (*BlockSet) ProtoMessage() {}
func (*BlockSet) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{5}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{5}
}
func (m *BlockSet) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockSet.Unmarshal(m, b)
@ -974,7 +974,7 @@ func (m *BlockMap) Reset() { *m = BlockMap{} }
func (m *BlockMap) String() string { return proto.CompactTextString(m) }
func (*BlockMap) ProtoMessage() {}
func (*BlockMap) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{6}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{6}
}
func (m *BlockMap) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockMap.Unmarshal(m, b)
@ -1044,7 +1044,7 @@ func (m *Literal) Reset() { *m = Literal{} }
func (m *Literal) String() string { return proto.CompactTextString(m) }
func (*Literal) ProtoMessage() {}
func (*Literal) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{7}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{7}
}
func (m *Literal) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Literal.Unmarshal(m, b)
@ -1108,7 +1108,7 @@ func (m *Default) Reset() { *m = Default{} }
func (m *Default) String() string { return proto.CompactTextString(m) }
func (*Default) ProtoMessage() {}
func (*Default) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{8}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{8}
}
func (m *Default) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Default.Unmarshal(m, b)
@ -1182,7 +1182,7 @@ func (m *Object) Reset() { *m = Object{} }
func (m *Object) String() string { return proto.CompactTextString(m) }
func (*Object) ProtoMessage() {}
func (*Object) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{9}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{9}
}
func (m *Object) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Object.Unmarshal(m, b)
@ -1238,7 +1238,7 @@ func (m *Array) Reset() { *m = Array{} }
func (m *Array) String() string { return proto.CompactTextString(m) }
func (*Array) ProtoMessage() {}
func (*Array) Descriptor() ([]byte, []int) {
return fileDescriptor_hcl_spec_45ead239ae3df7c4, []int{10}
return fileDescriptor_hcl_spec_8d078e4df12ae415, []int{10}
}
func (m *Array) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Array.Unmarshal(m, b)
@ -1281,10 +1281,10 @@ func init() {
}
func init() {
proto.RegisterFile("plugins/shared/hclspec/hcl_spec.proto", fileDescriptor_hcl_spec_45ead239ae3df7c4)
proto.RegisterFile("plugins/shared/hclspec/hcl_spec.proto", fileDescriptor_hcl_spec_8d078e4df12ae415)
}
var fileDescriptor_hcl_spec_45ead239ae3df7c4 = []byte{
var fileDescriptor_hcl_spec_8d078e4df12ae415 = []byte{
// 624 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0x4d, 0x6f, 0xd3, 0x4c,
0x10, 0xc7, 0xe3, 0xc4, 0xaf, 0xd3, 0xc3, 0xf3, 0x68, 0x85, 0x90, 0x55, 0x0e, 0x54, 0x96, 0x40,

View File

@ -0,0 +1,10 @@
// +build !windows
package loader
import "os"
// executable Checks to see if the file is executable by anyone.
func executable(path string, f os.FileInfo) bool {
return f.Mode().Perm()&0111 != 0
}

View File

@ -0,0 +1,15 @@
// +build windows
package loader
import (
"os"
"path/filepath"
)
// On windows, an executable can be any file with any extension. To avoid
// introspecting the file, here we skip executability checks on windows systems
// and simply check for the convention of an `exe` extension.
func executable(path string, s os.FileInfo) bool {
return filepath.Ext(path) == "exe"
}

View File

@ -226,7 +226,7 @@ func (l *PluginLoader) scan() ([]os.FileInfo, error) {
if err != nil {
// There are no plugins to scan
if os.IsNotExist(err) {
l.logger.Debug("skipping external plugins since plugin_dir doesn't exist")
l.logger.Warn("skipping external plugins since plugin_dir doesn't exist")
return nil, nil
}
@ -246,13 +246,12 @@ func (l *PluginLoader) scan() ([]os.FileInfo, error) {
return nil, fmt.Errorf("failed to stat file %q: %v", f, err)
}
if s.IsDir() {
l.logger.Debug("skipping subdir in plugin folder", "subdir", f)
l.logger.Warn("skipping subdir in plugin folder", "subdir", f)
continue
}
// Check if it is executable by anyone
if s.Mode().Perm()&0111 == 0 {
l.logger.Debug("skipping un-executable file in plugin folder", "file", f)
if !executable(f, s) {
l.logger.Warn("skipping un-executable file in plugin folder", "file", f)
continue
}
plugins = append(plugins, s)
@ -428,7 +427,7 @@ func (l *PluginLoader) mergePlugins(internal, external map[PluginID]*pluginInfo)
func (l *PluginLoader) validatePluginConfigs() error {
var mErr multierror.Error
for id, info := range l.plugins {
if err := l.validePluginConfig(id, info); err != nil {
if err := l.validatePluginConfig(id, info); err != nil {
wrapped := multierror.Prefix(err, fmt.Sprintf("plugin %s:", id))
multierror.Append(&mErr, wrapped)
}
@ -440,7 +439,7 @@ func (l *PluginLoader) validatePluginConfigs() error {
// validatePluginConfig is used to validate the plugin's configuration. If the
// plugin has a config, it is parsed with the plugins config schema and
// SetConfig is called to ensure the config is valid.
func (l *PluginLoader) validePluginConfig(id PluginID, info *pluginInfo) error {
func (l *PluginLoader) validatePluginConfig(id PluginID, info *pluginInfo) error {
var mErr multierror.Error
// Check if a config is allowed

View File

@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"testing"
@ -55,8 +56,12 @@ func newHarness(t *testing.T, plugins []string) *harness {
t.Fatalf("failed to get self executable path: %v", err)
}
exeSuffix := ""
if runtime.GOOS == "windows" {
exeSuffix = ".exe"
}
for _, p := range plugins {
dest := filepath.Join(h.tmpDir, p)
dest := filepath.Join(h.tmpDir, p) + exeSuffix
if err := copyFile(selfExe, dest); err != nil {
t.Fatalf("failed to copy file: %v", err)
}
@ -366,7 +371,7 @@ func TestPluginLoader_External_Config_Bad(t *testing.T) {
t.Parallel()
require := require.New(t)
// Create two plugins
// Create a plugin
plugins := []string{"mock-device"}
pluginVersions := []string{"v0.0.1"}
h := newHarness(t, plugins)
@ -1217,6 +1222,9 @@ func TestPluginLoader_Bad_Executable(t *testing.T) {
// Test that we skip directories, non-executables and follow symlinks
func TestPluginLoader_External_SkipBadFiles(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Windows currently does not skip non exe files")
}
t.Parallel()
require := require.New(t)

View File

@ -38,7 +38,7 @@ func (m *Attribute) Reset() { *m = Attribute{} }
func (m *Attribute) String() string { return proto.CompactTextString(m) }
func (*Attribute) ProtoMessage() {}
func (*Attribute) Descriptor() ([]byte, []int) {
return fileDescriptor_attribute_47573829d12e3945, []int{0}
return fileDescriptor_attribute_aa187fb710a98f5a, []int{0}
}
func (m *Attribute) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Attribute.Unmarshal(m, b)
@ -230,10 +230,10 @@ func init() {
}
func init() {
proto.RegisterFile("plugins/shared/structs/proto/attribute.proto", fileDescriptor_attribute_47573829d12e3945)
proto.RegisterFile("plugins/shared/structs/proto/attribute.proto", fileDescriptor_attribute_aa187fb710a98f5a)
}
var fileDescriptor_attribute_47573829d12e3945 = []byte{
var fileDescriptor_attribute_aa187fb710a98f5a = []byte{
// 218 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x34, 0x8f, 0xb1, 0x4e, 0xc3, 0x30,
0x10, 0x40, 0x63, 0xda, 0x34, 0xc9, 0x8d, 0x99, 0x8a, 0x10, 0x22, 0x62, 0x40, 0x19, 0x90, 0x33,

View File

@ -31,7 +31,7 @@ func (m *RecoverableError) Reset() { *m = RecoverableError{} }
func (m *RecoverableError) String() string { return proto.CompactTextString(m) }
func (*RecoverableError) ProtoMessage() {}
func (*RecoverableError) Descriptor() ([]byte, []int) {
return fileDescriptor_recoverable_error_8c5d7f86073ca00c, []int{0}
return fileDescriptor_recoverable_error_f746254fd69675b0, []int{0}
}
func (m *RecoverableError) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RecoverableError.Unmarshal(m, b)
@ -63,10 +63,10 @@ func init() {
}
func init() {
proto.RegisterFile("plugins/shared/structs/proto/recoverable_error.proto", fileDescriptor_recoverable_error_8c5d7f86073ca00c)
proto.RegisterFile("plugins/shared/structs/proto/recoverable_error.proto", fileDescriptor_recoverable_error_f746254fd69675b0)
}
var fileDescriptor_recoverable_error_8c5d7f86073ca00c = []byte{
var fileDescriptor_recoverable_error_f746254fd69675b0 = []byte{
// 138 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x29, 0xc8, 0x29, 0x4d,
0xcf, 0xcc, 0x2b, 0xd6, 0x2f, 0xce, 0x48, 0x2c, 0x4a, 0x4d, 0xd1, 0x2f, 0x2e, 0x29, 0x2a, 0x4d,

View File

@ -35,7 +35,7 @@ func (m *StatObject) Reset() { *m = StatObject{} }
func (m *StatObject) String() string { return proto.CompactTextString(m) }
func (*StatObject) ProtoMessage() {}
func (*StatObject) Descriptor() ([]byte, []int) {
return fileDescriptor_stats_9457a07d62f0d5fa, []int{0}
return fileDescriptor_stats_73a5e405c9cf442c, []int{0}
}
func (m *StatObject) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatObject.Unmarshal(m, b)
@ -100,7 +100,7 @@ func (m *StatValue) Reset() { *m = StatValue{} }
func (m *StatValue) String() string { return proto.CompactTextString(m) }
func (*StatValue) ProtoMessage() {}
func (*StatValue) Descriptor() ([]byte, []int) {
return fileDescriptor_stats_9457a07d62f0d5fa, []int{1}
return fileDescriptor_stats_73a5e405c9cf442c, []int{1}
}
func (m *StatValue) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatValue.Unmarshal(m, b)
@ -184,10 +184,10 @@ func init() {
}
func init() {
proto.RegisterFile("plugins/shared/structs/proto/stats.proto", fileDescriptor_stats_9457a07d62f0d5fa)
proto.RegisterFile("plugins/shared/structs/proto/stats.proto", fileDescriptor_stats_73a5e405c9cf442c)
}
var fileDescriptor_stats_9457a07d62f0d5fa = []byte{
var fileDescriptor_stats_73a5e405c9cf442c = []byte{
// 444 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0xd2, 0xdf, 0x6a, 0x13, 0x41,
0x14, 0x06, 0x70, 0x36, 0xdb, 0x24, 0xcd, 0xc9, 0x45, 0xed, 0x14, 0x61, 0x89, 0x22, 0xa1, 0x17,

View File

@ -12,7 +12,8 @@ import (
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/structs/config"
vapi "github.com/hashicorp/vault/api"
"github.com/mitchellh/go-testing-interface"
testing "github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/require"
)
// TestVault is a test helper. It uses a fork/exec model to create a test Vault
@ -167,13 +168,16 @@ func NewTestVaultDelayed(t testing.T) *TestVault {
// Start starts the test Vault server and waits for it to respond to its HTTP
// API
func (tv *TestVault) Start() error {
if err := tv.cmd.Start(); err != nil {
tv.t.Fatalf("failed to start vault: %v", err)
}
// Start the waiter
tv.waitCh = make(chan error, 1)
go func() {
// Must call Start and Wait in the same goroutine on Windows #5174
if err := tv.cmd.Start(); err != nil {
tv.waitCh <- err
return
}
err := tv.cmd.Wait()
tv.waitCh <- err
}()
@ -198,7 +202,12 @@ func (tv *TestVault) Stop() {
tv.t.Errorf("err: %s", err)
}
if tv.waitCh != nil {
<-tv.waitCh
select {
case <-tv.waitCh:
return
case <-time.After(1 * time.Second):
require.Fail(tv.t, "Timed out waiting for vault to terminate")
}
}
}

View File

@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
Copyright 2013-2017 Docker, Inc.
Copyright 2013-2018 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -55,3 +55,10 @@ type PluginEnableConfig struct {
type PluginDisableConfig struct {
ForceDisable bool
}
// NetworkListConfig stores the options available for listing networks
type NetworkListConfig struct {
// TODO(@cpuguy83): naming is hard, this is pulled from what was being used in the router before moving here
Detailed bool
Verbose bool
}

View File

@ -329,6 +329,7 @@ type Resources struct {
DeviceCgroupRules []string // List of rule to be added to the device cgroup
DiskQuota int64 // Disk limit (in bytes)
KernelMemory int64 // Kernel memory limit (in bytes)
KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes)
MemoryReservation int64 // Memory soft limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
MemorySwappiness *int64 // Tuning container memory swappiness behaviour

View File

@ -323,6 +323,22 @@ func (args Args) WalkValues(field string, op func(value string) error) error {
return nil
}
// Clone returns a copy of args.
func (args Args) Clone() (newArgs Args) {
newArgs.fields = make(map[string]map[string]bool, len(args.fields))
for k, m := range args.fields {
var mm map[string]bool
if m != nil {
mm = make(map[string]bool, len(m))
for kk, v := range m {
mm[kk] = v
}
}
newArgs.fields[k] = mm
}
return newArgs
}
func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
m := map[string]map[string]bool{}
for k, v := range d {

View File

@ -79,7 +79,8 @@ const (
// BindOptions defines options specific to mounts of type "bind".
type BindOptions struct {
Propagation Propagation `json:",omitempty"`
Propagation Propagation `json:",omitempty"`
NonRecursive bool `json:",omitempty"`
}
// VolumeOptions represents the options for a mount of type volume.

View File

@ -1,4 +1,8 @@
package network // import "github.com/docker/docker/api/types/network"
import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/errdefs"
)
// Address represents an IP address
type Address struct {
@ -106,3 +110,17 @@ type NetworkingConfig struct {
type ConfigReference struct {
Network string
}
var acceptedFilters = map[string]bool{
"driver": true,
"type": true,
"name": true,
"id": true,
"label": true,
"scope": true,
}
// ValidateFilters validates the list of filter args with the available filters.
func ValidateFilters(filter filters.Args) error {
return errdefs.InvalidParameter(filter.Validate(acceptedFilters))
}

View File

@ -120,7 +120,7 @@ type NetworkStats struct {
RxBytes uint64 `json:"rx_bytes"`
// Packets received. Windows and Linux.
RxPackets uint64 `json:"rx_packets"`
// Received errors. Not used on Windows. Note that we dont `omitempty` this
// Received errors. Not used on Windows. Note that we don't `omitempty` this
// field as it is expected in the >=v1.21 API stats structure.
RxErrors uint64 `json:"rx_errors"`
// Incoming packets dropped. Windows and Linux.
@ -129,7 +129,7 @@ type NetworkStats struct {
TxBytes uint64 `json:"tx_bytes"`
// Packets sent. Windows and Linux.
TxPackets uint64 `json:"tx_packets"`
// Sent errors. Not used on Windows. Note that we dont `omitempty` this
// Sent errors. Not used on Windows. Note that we don't `omitempty` this
// field as it is expected in the >=v1.21 API stats structure.
TxErrors uint64 `json:"tx_errors"`
// Outgoing packets dropped. Windows and Linux.

View File

@ -71,4 +71,5 @@ type ContainerSpec struct {
Secrets []*SecretReference `json:",omitempty"`
Configs []*ConfigReference `json:",omitempty"`
Isolation container.Isolation `json:",omitempty"`
Sysctls map[string]string `json:",omitempty"`
}

View File

@ -1,6 +1,8 @@
package swarm // import "github.com/docker/docker/api/types/swarm"
import "time"
import (
"time"
)
// ClusterInfo represents info about the cluster for outputting in "info"
// it contains the same information as "Swarm", but without the JoinTokens
@ -10,6 +12,9 @@ type ClusterInfo struct {
Spec Spec
TLSInfo TLSInfo
RootRotationInProgress bool
DefaultAddrPool []string
SubnetSize uint32
DataPathPort uint32
}
// Swarm represents a swarm.
@ -149,10 +154,13 @@ type InitRequest struct {
ListenAddr string
AdvertiseAddr string
DataPathAddr string
DataPathPort uint32
ForceNewCluster bool
Spec Spec
AutoLockManagers bool
Availability NodeAvailability
DefaultAddrPool []string
SubnetSize uint32
}
// JoinRequest is the request used to join a swarm.

View File

@ -102,9 +102,10 @@ type ContainerStats struct {
// Ping contains response of Engine API:
// GET "/_ping"
type Ping struct {
APIVersion string
OSType string
Experimental bool
APIVersion string
OSType string
Experimental bool
BuilderVersion BuilderVersion
}
// ComponentVersion describes the version information for a specific component.
@ -157,6 +158,7 @@ type Info struct {
MemoryLimit bool
SwapLimit bool
KernelMemory bool
KernelMemoryTCP bool
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool
@ -204,6 +206,8 @@ type Info struct {
RuncCommit Commit
InitCommit Commit
SecurityOptions []string
ProductLicense string `json:",omitempty"`
Warnings []string
}
// KeyValue holds a key/value pair
@ -540,6 +544,7 @@ type ImagesPruneReport struct {
// BuildCachePruneReport contains the response for Engine API:
// POST "/build/prune"
type BuildCachePruneReport struct {
CachesDeleted []string
SpaceReclaimed uint64
}
@ -589,14 +594,21 @@ type BuildResult struct {
// BuildCache contains information about a build cache record
type BuildCache struct {
ID string
Mutable bool
InUse bool
Size int64
ID string
Parent string
Type string
Description string
InUse bool
Shared bool
Size int64
CreatedAt time.Time
LastUsedAt *time.Time
UsageCount int
Parent string
Description string
}
// BuildCachePruneOptions hold parameters to prune the build cache
type BuildCachePruneOptions struct {
All bool
KeepStorage int64
Filters filters.Args
}

View File

@ -52,7 +52,7 @@ type (
NoLchown bool
UIDMaps []idtools.IDMap
GIDMaps []idtools.IDMap
ChownOpts *idtools.IDPair
ChownOpts *idtools.Identity
IncludeSourceDir bool
// WhiteoutFormat is the expected on disk format for whiteout files.
// This format will be converted to the standard format on pack
@ -72,13 +72,13 @@ type (
// this package with a pluggable Untar function. Also, to facilitate the passing of specific id
// mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations.
type Archiver struct {
Untar func(io.Reader, string, *TarOptions) error
IDMappingsVar *idtools.IDMappings
Untar func(io.Reader, string, *TarOptions) error
IDMapping *idtools.IdentityMapping
}
// NewDefaultArchiver returns a new Archiver without any IDMappings
// NewDefaultArchiver returns a new Archiver without any IdentityMapping
func NewDefaultArchiver() *Archiver {
return &Archiver{Untar: Untar, IDMappingsVar: &idtools.IDMappings{}}
return &Archiver{Untar: Untar, IDMapping: &idtools.IdentityMapping{}}
}
// breakoutError is used to differentiate errors related to breaking out
@ -367,11 +367,7 @@ func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, erro
hdr.AccessTime = time.Time{}
hdr.ChangeTime = time.Time{}
hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi)
name, err = canonicalTarName(name, fi.IsDir())
if err != nil {
return nil, fmt.Errorf("tar: cannot canonicalize path: %v", err)
}
hdr.Name = name
hdr.Name = canonicalTarName(name, fi.IsDir())
if err := setHeaderForSpecialDevice(hdr, name, fi.Sys()); err != nil {
return nil, err
}
@ -424,9 +420,9 @@ type tarAppender struct {
Buffer *bufio.Writer
// for hardlink mapping
SeenFiles map[uint64]string
IDMappings *idtools.IDMappings
ChownOpts *idtools.IDPair
SeenFiles map[uint64]string
IdentityMapping *idtools.IdentityMapping
ChownOpts *idtools.Identity
// For packing and unpacking whiteout files in the
// non standard format. The whiteout files defined
@ -435,29 +431,26 @@ type tarAppender struct {
WhiteoutConverter tarWhiteoutConverter
}
func newTarAppender(idMapping *idtools.IDMappings, writer io.Writer, chownOpts *idtools.IDPair) *tarAppender {
func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender {
return &tarAppender{
SeenFiles: make(map[uint64]string),
TarWriter: tar.NewWriter(writer),
Buffer: pools.BufioWriter32KPool.Get(nil),
IDMappings: idMapping,
ChownOpts: chownOpts,
SeenFiles: make(map[uint64]string),
TarWriter: tar.NewWriter(writer),
Buffer: pools.BufioWriter32KPool.Get(nil),
IdentityMapping: idMapping,
ChownOpts: chownOpts,
}
}
// canonicalTarName provides a platform-independent and consistent posix-style
//path for files and directories to be archived regardless of the platform.
func canonicalTarName(name string, isDir bool) (string, error) {
name, err := CanonicalTarNameForPath(name)
if err != nil {
return "", err
}
func canonicalTarName(name string, isDir bool) string {
name = CanonicalTarNameForPath(name)
// suffix with '/' for directories
if isDir && !strings.HasSuffix(name, "/") {
name += "/"
}
return name, nil
return name
}
// addTarFile adds to the tar archive a file from `path` as `name`
@ -509,14 +502,12 @@ func (ta *tarAppender) addTarFile(path, name string) error {
//handle re-mapping container ID mappings back to host ID mappings before
//writing tar headers/files. We skip whiteout files because they were written
//by the kernel and already have proper ownership relative to the host
if !isOverlayWhiteout &&
!strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) &&
!ta.IDMappings.Empty() {
if !isOverlayWhiteout && !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() {
fileIDPair, err := getFileUIDGID(fi.Sys())
if err != nil {
return err
}
hdr.Uid, hdr.Gid, err = ta.IDMappings.ToContainer(fileIDPair)
hdr.Uid, hdr.Gid, err = ta.IdentityMapping.ToContainer(fileIDPair)
if err != nil {
return err
}
@ -579,7 +570,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
return nil
}
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.IDPair, inUserns bool) error {
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.Identity, inUserns bool) error {
// hdr.Mode is in linux format, which we can use for sycalls,
// but for os.Foo() calls we need the mode converted to os.FileMode,
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
@ -659,7 +650,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
// Lchown is not supported on Windows.
if Lchown && runtime.GOOS != "windows" {
if chownOpts == nil {
chownOpts = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
chownOpts = &idtools.Identity{UID: hdr.Uid, GID: hdr.Gid}
}
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
return err
@ -669,11 +660,13 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
var errors []string
for key, value := range hdr.Xattrs {
if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil {
if err == syscall.ENOTSUP {
if err == syscall.ENOTSUP || err == syscall.EPERM {
// We ignore errors here because not all graphdrivers support
// xattrs *cough* old versions of AUFS *cough*. However only
// ENOTSUP should be emitted in that case, otherwise we still
// bail.
// EPERM occurs if modifying xattrs is not allowed. This can
// happen when running in userns with restrictions (ChromeOS).
errors = append(errors, err.Error())
continue
}
@ -908,8 +901,8 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
defer pools.BufioReader32KPool.Put(trBuf)
var dirs []*tar.Header
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
rootIDs := idMappings.RootPair()
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
rootIDs := idMapping.RootPair()
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
// Iterate through the files in the archive.
@ -988,7 +981,7 @@ loop:
}
trBuf.Reset(tr)
if err := remapIDs(idMappings, hdr); err != nil {
if err := remapIDs(idMapping, hdr); err != nil {
return err
}
@ -1075,8 +1068,8 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
}
defer archive.Close()
options := &TarOptions{
UIDMaps: archiver.IDMappingsVar.UIDs(),
GIDMaps: archiver.IDMappingsVar.GIDs(),
UIDMaps: archiver.IDMapping.UIDs(),
GIDMaps: archiver.IDMapping.GIDs(),
}
return archiver.Untar(archive, dst, options)
}
@ -1089,8 +1082,8 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
}
defer archive.Close()
options := &TarOptions{
UIDMaps: archiver.IDMappingsVar.UIDs(),
GIDMaps: archiver.IDMappingsVar.GIDs(),
UIDMaps: archiver.IDMapping.UIDs(),
GIDMaps: archiver.IDMapping.GIDs(),
}
return archiver.Untar(archive, dst, options)
}
@ -1111,7 +1104,7 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error {
// if this Archiver is set up with ID mapping we need to create
// the new destination directory with the remapped root UID/GID pair
// as owner
rootIDs := archiver.IDMappingsVar.RootPair()
rootIDs := archiver.IDMapping.RootPair()
// Create dst, copy src's content into it
logrus.Debugf("Creating dest directory: %s", dst)
if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
@ -1171,7 +1164,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
hdr.Name = filepath.Base(dst)
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
if err := remapIDs(archiver.IDMappingsVar, hdr); err != nil {
if err := remapIDs(archiver.IDMapping, hdr); err != nil {
return err
}
@ -1199,13 +1192,13 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
return err
}
// IDMappings returns the IDMappings of the archiver.
func (archiver *Archiver) IDMappings() *idtools.IDMappings {
return archiver.IDMappingsVar
// IdentityMapping returns the IdentityMapping of the archiver.
func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
return archiver.IDMapping
}
func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error {
ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error {
ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
hdr.Uid, hdr.Gid = ids.UID, ids.GID
return err
}

View File

@ -32,8 +32,8 @@ func getWalkRoot(srcPath string, include string) string {
// CanonicalTarNameForPath returns platform-specific filepath
// to canonical posix-style path for tar archival. p is relative
// path.
func CanonicalTarNameForPath(p string) (string, error) {
return p, nil // already unix-style
func CanonicalTarNameForPath(p string) string {
return p // already unix-style
}
// chmodTarEntry is used to adjust the file permissions used in tar header based
@ -68,13 +68,13 @@ func getInodeFromStat(stat interface{}) (inode uint64, err error) {
return
}
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
s, ok := stat.(*syscall.Stat_t)
if !ok {
return idtools.IDPair{}, errors.New("cannot convert stat value to syscall.Stat_t")
return idtools.Identity{}, errors.New("cannot convert stat value to syscall.Stat_t")
}
return idtools.IDPair{UID: int(s.Uid), GID: int(s.Gid)}, nil
return idtools.Identity{UID: int(s.Uid), GID: int(s.Gid)}, nil
}
// handleTarTypeBlockCharFifo is an OS-specific helper function used by

View File

@ -2,10 +2,8 @@ package archive // import "github.com/docker/docker/pkg/archive"
import (
"archive/tar"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/longpath"
@ -26,16 +24,8 @@ func getWalkRoot(srcPath string, include string) string {
// CanonicalTarNameForPath returns platform-specific filepath
// to canonical posix-style path for tar archival. p is relative
// path.
func CanonicalTarNameForPath(p string) (string, error) {
// windows: convert windows style relative path with backslashes
// into forward slashes. Since windows does not allow '/' or '\'
// in file names, it is mostly safe to replace however we must
// check just in case
if strings.Contains(p, "/") {
return "", fmt.Errorf("Windows path contains forward slash: %s", p)
}
return strings.Replace(p, string(os.PathSeparator), "/", -1), nil
func CanonicalTarNameForPath(p string) string {
return filepath.ToSlash(p)
}
// chmodTarEntry is used to adjust the file permissions used in tar header based
@ -71,7 +61,7 @@ func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
return nil
}
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
// no notion of file ownership mapping yet on Windows
return idtools.IDPair{UID: 0, GID: 0}, nil
return idtools.Identity{UID: 0, GID: 0}, nil
}

View File

@ -63,12 +63,16 @@ func (c changesByPath) Less(i, j int) bool { return c[i].Path < c[j].Path }
func (c changesByPath) Len() int { return len(c) }
func (c changesByPath) Swap(i, j int) { c[j], c[i] = c[i], c[j] }
// Gnu tar and the go tar writer don't have sub-second mtime
// precision, which is problematic when we apply changes via tar
// files, we handle this by comparing for exact times, *or* same
// Gnu tar doesn't have sub-second mtime precision. The go tar
// writer (1.10+) does when using PAX format, but we round times to seconds
// to ensure archives have the same hashes for backwards compatibility.
// See https://github.com/moby/moby/pull/35739/commits/fb170206ba12752214630b269a40ac7be6115ed4.
//
// Non-sub-second is problematic when we apply changes via tar
// files. We handle this by comparing for exact times, *or* same
// second count and either a or b having exactly 0 nanoseconds
func sameFsTime(a, b time.Time) bool {
return a == b ||
return a.Equal(b) ||
(a.Unix() == b.Unix() &&
(a.Nanosecond() == 0 || b.Nanosecond() == 0))
}

Some files were not shown because too many files have changed in this diff Show More