let lib = import "../../lib.ncl" in # DUMMY let time = { parseDurationSecond = fun arg => 0 | String, } in # DUMMY # let stanza = {} in { json = { Job = { Namespace | String, Id | Name, Name | String, Type | [| 'service, 'system, 'batch |], Priority | std.number.Nat, Datacenters | std.array.NonEmpty, TaskGroups | Array TaskGroup | default = [], Affinities | Array Affinity | default = [], Constraints | Array Constraint | default = [], Spreads | Array Spread | default = [], ConsulToken | lib.contracts.Nullable String, VaultToken | lib.contracts.Nullable String, Vault | lib.contracts.Nullable json.Vault | default = null, Update | lib.contracts.Nullable json.Update | default = null, Migrate | lib.contracts.Nullable json.Migrate | default = null, Periodic | lib.contracts.Nullable json.Periodic | default = null, }, Affinity = { LTarget | String, RTargety | String, Operand | ( lib.contracts.OneOf [ "regexp", "set_contains_all", "set_contains", "set_contains_any", "=", "==", "is", "!=", "not", ">", ">=", "<", "<=", "version" ] ), Weight | std.number.Nat | lib.contracts.NotEq 0 | lib.contracts.GreaterEq - 100 | lib.contracts.SmallerEq 100, }, Constraint = { LTarget | lib.contracts.Nullable String | default = null, RTarget | String, Operand | ( lib.contracts.OneOf [ "regexp", "set_contains", "distinct_hosts", "distinct_property", "=", "==", "is", "!=", "not", ">", ">=", "<", "<=" ] ), }, Spread = { Attribute | String, Weight | lib.contracts.Nullable ( lib.contracts.AllOf [ std.number.Nat, lib.contracts.GreaterEq - 100, lib.contracts.LesserEq 100 ] ) | default = null, SpreadTarget | Array SpreadTargetElem | default = [], }, SpreadTargetElem = { Value | String, Percent | lib.contracts.Nullable ( lib.contracts.AllOf [ std.number.PosNat, lib.contracts.LesserEq 100 ] ) | default = null, }, RestartPolicy = { Attempts | std.number.Nat, Interval | std.number.Nat, Delay | std.number.Nat, Mode | (lib.contracts.OneOf [ "delay", "fail" ]), }, Volume = { Name | String, Type | (lib.contracts.OneOf [ null, "host", "csi" ]), Source | String, ReadOnly | Bool | default = false, MountOptions | lib.contracts.Nullable { FsType | lib.contracts.Nullable String | default = null, mountFlags | lib.contracts.Nullable String | default = null } | default = null, }, ReschedulePolicy = { Attempts | lib.contracts.Nullable std.number.Nat | default = null, DelayFunction | lib.contracts.Nullable ( lib.contracts.OneOf [ "constant", "exponential", "fibonacci" ] ) | default = null, Delay | lib.contracts.Nullable ( lib.contracts.AllOf [ std.number.Nat, lib.contracts.GreaterEq 5 * 1000 ] ) # >=time.ParseDuration("5s") | default = null, Interval | lib.contracts.Nullable std.number.Nat | default = null, MaxDelay | lib.contracts.Nullable std.number.Nat | default = null, Unlimited | lib.contracts.Nullable Bool | default = null, }, Migrate = { HealthCheck | [| 'checks, 'task_states |] | default = 'checks, HealthyDeadline | std.number.Nat | default = 500000000000, MaxParallel | std.number.Nat | default = 1, MinHealthyTime | std.number.Nat | default = 10000000000, }, Periodic = { Enabled | Bool | default = false, TimeZone | String | default = "UTC", SpecType = "cron", Spec | String, ProhibitOverlap | Bool | default = false, }, Update = { AutoPromote | Bool | default = false, AutoRevert | Bool | default = false, Canary | std.number.Nat | default = 0, HealthCheck | [| 'checks, 'task_states, 'manual |] | default = 'checks, HealthyDeadline | lib.contracts.Nullable std.number.Nat | default = null, MaxParallel | std.number.Nat | default = 1, MinHealthyTime | lib.contracts.Nullable std.number.Nat | default = null, ProgressDeadline | lib.contracts.Nullable std.number.Nat | default = null, Stagger | lib.contracts.Nullable std.number.Nat | default = null, }, TaskGroup = { Affinities | Array Affinity | default = [], Constraints | Array Constraint | default = [], Spreads | Array Spread | default = [], Count | std.number.Nat | lib.contracts.Greater 0, # TODO: Meta [string] string Name | String, RestartPolicy | lib.contracts.Nullable json.RestartPolicy | default = null, Services | Array Service | default = [], ShutdownDelay | std.number.Nat | default = 0, Tasks | Array Task | default = [], # TODO Volumes: [string]: #json.Volume ReschedulePolicy | json.ReschedulePolicy, EphemeralDisk | lib.contracts.Nullable { Migrate | Bool, SizeMB | std.number.Nat, Sticky | Bool, } | default = null, Migrate | lib.contracts.Nullable json.Migrate | default = null, Update | lib.contracts.Nullable json.Update | default = null, Networks | Array json.Network | default = [], StopAfterClientDisconnect | lib.contracts.Nullable std.number.Nat | default = null, Scaling = null, Vault | lib.contracts.Nullable json.Vault | default = null, }, Port = { Label | String, Value | lib.contracts.Nullable std.number.Nat | default = null, # used for static ports To | lib.contracts.Nullable std.number.Nat | default = null, HostNetwork | String | default = "", }, Network = { Mode | [| 'host, 'bridge |] | default = 'host, Device | String | default = "", CIDR | String | default = "", IP | String | default = "", DNS = null, ReservedPorts | lib.contracts.Nullable (Array json.Port) | default = null, DynamicPorts | lib.contracts.Nullable (Array json.Port) | default = null, MBits = null, }, ServiceCheck = { AddressMode | [| 'alloc, 'driver, 'host |], Args | lib.contracts.Nullable (Array String) | default = null, CheckRestart | json.CheckRestart, Command | String | default = "", Expose = false, FailuresBeforeCritical | std.number.Nat | default = 0, Id | String | default = "", InitialStatus | String | default = "", Interval | std.number.Nat | default = 10000000000, Method | String | default = "", Name | String | default = "", Path | String | default = "", PortLabel | String, Protocol | String | default = "", SuccessBeforePassing | std.number.Nat | default = 0, TaskName | String | default = "", Timeout | std.number.Nat, TLSSkipVerify | Bool | default = false, Type | [| 'http, 'tcp, 'script, 'grpc |], Body | lib.contracts.Nullable String | default = null, # TODO Header [string] [...string] }, CheckRestart | lib.contracts.Nullable { Limit | std.number.Nat | default = 0, Grace | std.number.Nat | default = 10000000000, IgnoreWarnings | Bool | default = false, }, Lifecycle = { Hook | [| 'prestart, 'poststart, 'poststop |], Sidecar | lib.contracts.Nullable Bool | default = null, }, LogConfig | lib.contracts.Nullable { MaxFiles | std.number.PosNat, MaxFileSizeMB | std.number.PosNat, }, Service = { Id | String | default = "", Name | String, Tags | Array String | default = [], CanaryTags | Array String | default = [], EnableTagOverride | Bool | default = false, PortLabel | String, AddressMode | [| 'alloc, 'auto, 'driver, 'host |], Checks | Array ServiceCheck | default = [], CheckRestart | json.CheckRestart, Connect = null, # TODO Meta: [string]: string TaskName | String | default = "", }, Task = { Name | String, Driver | [| 'exec, 'docker, 'nspawn |], Config | stanza.taskConfig | { driver | Driver }, Constraints | Array Constraint | default = [], Affinities | Array Affinity | default = [], # TODO: Env: [string]: string Services | Array Service | default = [], Resources = { CPU | std.number.Nat | lib.contracts.GreaterEq 100 | default = 100, MemoryMB | std.number.Nat | lib.contracts.GreaterEq 32 | default = 300, DiskMB | lib.contracts.Nullable std.number.Nat | default = null, }, Meta = {}, RestartPolicy | lib.contracts.Nullable json.RestartPolicy | default = null, ShutdownDelay | std.number.Nat | default = 0, User | String | default = "", Lifecycle | lib.contracts.Nullable json.Lifecycle | default = null, KillTimeout | lib.contracts.Nullable std.number.Nat | default = null, LogConfig | json.LogConfig, Artifacts | Array json.Artifact | default = [], Templates | Array json.Template | default = [], DispatchPayload = null, VolumeMounts | Array json.VolumeMount | default = [], Leader | Bool | default = false, KillSignal | String, ScalingPolicies = null, Vault | lib.contracts.Nullable json.Vault | default = null, }, VolumeMount = { Destination | String, PropagationMode | String, ReadOnly | Bool, Volume | String, }, Artifact = { GetterSource | String, # TODO GetterOptions: [string]: string # TODO GetterHeaders: [string]: string GetterMode | [| 'any, 'file, 'dir |] | default = 'any, RelativeDest | String, }, Template = { SourcePath | String | default = "", DestPath | String, EmbeddedTmpl | String, ChangeMode | [| 'restart, 'noop, 'signal |] | default = 'restart, ChangeSignal | String | default = "", Splay | std.number.Nat | default = 5000000000, Perms | lib.contracts.MatchRegexp "^[0-7]{4}$" | default = "0644", LeftDelim | String, RightDelim | String, Envvars | Bool, }, Vault = { ChangeMode | [| 'noop, 'restart, 'signal |] | default = 'restart, ChangeSignal | String | default = "", Env | Bool | default = true, Namespace | String | default = "", Policies | std.array.NonEmpty, } }, Duration = lib.contracts.MatchRegexp "^[1-9]\\d*[hms]$", GitRevision = lib.contracts.MatchRegexp "^[a-f0-9]{40}$", Flake = lib.contracts.MatchRegexp "^(github|git\\+ssh|git):[0-9a-zA-Z_-]+/[0-9a-zA-Z_-]+", toJson = json.Job & { job = stanza.job, jobName | String, Name = jobName, Datacenters = job.datacenters, Namespace = job.namespace, Type = job.type, Priority = job.priority_, # TODO: cannot convert to merge yet because job.update is located in another # piece of the record. # if #job.update != null { # let u = #job.update # Update: { # AutoPromote: u.auto_promote # AutoRevert: u.auto_revert # Canary: u.canary # HealthCheck: u.health_check # HealthyDeadline: time.ParseDuration(u.healthy_deadline) # MaxParallel: u.max_parallel # MinHealthyTime: time.ParseDuration(u.min_healthy_time) # ProgressDeadline: time.ParseDuration(u.progress_deadline) # Stagger: time.ParseDuration(u.stagger) # } # } # TODO: Same. # if #job.migrate != null { # let m = #job.migrate # Migrate: { # HealthCheck: m.health_check # HealthyDeadline: m.healthy_deadline # MaxParallel: m.max_parallel # MinHealthyTime: m.min_healthy_time # } # } # TODO: Same. # if #job.periodic != null { # let p = #job.periodic # Periodic: { # Enabled: true # TimeZone: p.time_zone # Spec: p.cron # ProhibitOverlap: p.prohibit_overlap # } # } # TODO: Same. # if #job.vault != null { # Vault: { # ChangeMode: #job.vault.change_mode # ChangeSignal: #job.vault.change_signal # Env: #job.vault.env # Namespace: #job.vault.namespace # Policies: #job.vault.policies # } # } Affinities = std.array.map ( fun a => { LTarget = a.attribute, RTarget = a.value, Operand = a.operator, Weight = a.weight } ) job.affinities, Constraints = std.array.map ( fun c => { LTarget = c.attribute, RTarget = c.value, Operand = c.operator } ) job.constraints, Spreads = std.array.map ( fun s => { Attribute = s.attribute, Weight = s.weight, SpreadTarget = std.array.map ( fun t => { Value = t.value, Percent = t.percent } ) s.target, } ) job.spreads, TaskGroups = lib.records.mapToArray ( fun tgName tg => { Name = tgName, Count = tg.count, Affinities = std.array.map ( fun a => { LTarget = a.attribute, RTarget = a.value, Operand = a.operator, Weight = a.weight, } ) tg.affinities, Constraints = std.array.map ( fun c => { LTarget = c.attribute, RTarget = c.value, Operand = c.operator, } ) tg.constraints, Spreads = std.array.map ( fun s => { Attribute = s.attribute, Weight = s.weight, SpreadTarget = std.array.map ( fun t => { Value = t.value, Percent = t.percent, } ) s.target, } ) tg.spreads, } & ( if tg.reschedule != null then { ReschedulePolicy = { Attempts = tg.reschedule.attempts, DelayFunction = tg.reschedule.delay_function, Unlimited = tg.reschedule.unlimited, } & ( if tg.reschedule.delay != null then { # test above was: != _|_. Not sure what is the difference? Should # we test for the presence of the field? Delay = time.ParseDuration tg.reschedule.delay } else {} ) & ( if tg.reschedule.interval != null then { # test above was: != _|_. Not sure what is the difference? Should # we test for the presence of the field? Interval = time.ParseDuration tg.reschedule.interval } else {} ) & ( if tg.reschedule.max_delay != null then { # test above was: != _|_. Not sure what is the difference? Should # we test for the presence of the field? MaxDelay = time.ParseDuration tg.reschedule.max_delay } else {} ) } else {} ) & ( if tg.ephemeral_disk != null then { EphemeralDisk = { SizeMB = tg.ephemeral_disk.size, Migrate = tg.ephemeral_disk.migrate, Sticky = tg.ephemeral_disk.sticky, } } else {} ) & ( if tg.restart != null then { RestartPolicy = { Interval = time.ParseDuration tg.restart.interval, Attempts = tg.restart.attempts, Delay = time.ParseDuration tg.restart.delay, Mode = tg.restart.mode, } } else {} ) # only one network can be specified at group level, and we never use # deprecated task level ones. & ( if tg.network != null then let ports = tg.network.port |> lib.records.toArray |> std.array.partition tg.network.port ( fun entry => entry.value.static != null ) in # would be better with destructuring let mkPort = fun { key, value = { static, to, host_network }, .. } => { Label = key, Value = static, To = to, HostNetwork = host_network, } in { Networks = [{ Mode = tg.network.mode, ReservedPorts = std.array.map mkPort ports.right, DynamicPorts = std.array.map mkPort ports.wrong, }] } else {} ) & { Services = std.array.map ( fun sName s => { Name = sName, TaskName = s.task, Tags = s.tags, AddressMode = s.address_mode, } & ( if s.check_restart != null then { CheckRestart = { Limit = s.check_restart.limit, Grace = time.ParseDuration (s.check_restart.grace), IgnoreWarnings = s.check_restart.ignore_warnings, } } else {} ) & { Checks = lib.records.mapToArray ( fun cName c => { AddressMode = c.address_mode, Type = c.type, PortLabel = c.port, Interval = time.ParseDuration (c.interval) } & ( if c.type == "http" then { Path = c.path, Method = c.method, Protocol = c.protocol } else {} ) & { Timeout = time.ParseDuration (c.timeout), SuccessBeforePassing = c.success_before_passing, FailuresBeforeCritical = c.failures_before_critical, TLSSkipVerify = c.tls_skip_verify, InitialStatus = c.initial_status, Header = c.header, Body = c.body, } & ( if c.check_restart != null then { CheckRestart = { Limit = c.check_restart.limit, Grace = time.ParseDuration (c.check_restart.grace), IgnoreWarnings = c.check_restart.ignore_warnings, } } else {} ) ) s.check, PortLabel = s.port, Meta = s.meta, } ) tg.service, Tasks = lib.records.mapToArray ( fun tName t => { Name = tName, Driver = t.driver, Config = t.config, Env = t.env, KillSignal = t.kill_signal, } & ( if t.kill_timeout != null then { KillTimeout = time.ParseDuration (t.kill_timeout) } else {} ) & { Affinities = std.array.map ( fun { attribute, value, operator, weight, .. } => { LTarget = attribute, RTarget = value, Operand = operator, Weight = weight, } ) t.affinities, # END AFFINITIES Constraints = lib.records.mapToArray ( fun { attribute, value, operator } => { LTarget = attribute, RTarget = value, Operand = operator, } ) t.constraints, # END CONSTRAINTS } & ( if t.logs != null then { LogConfig = { MaxFiles = t.logs.max_files, MaxFileSizeMB = t.logs.max_file_size, } } else {} ) & ( if t.restart != null then { RestartPolicy = { Interval = time.ParseDuration (t.restart.interval), Attempts = t.restart.attempts, Delay = time.ParseDuration (t.restart.delay), Mode = t.restart.mode, } } else {} ) & ( if t.lifecycle != null then { Lifecycle = { Hook = t.lifecycle.hook, Sidecar = t.lifecycle.sidecar, } } else {} ) & { Resources = { CPU = t.resources.cpu, MemoryMB = t.resources.memory, }, Leader = t.leader, Templates = lib.records.mapToArray ( fun tplName tpl => { DestPath = tplName, EmbeddedTmpl = tpl.data, SourcePath = tpl.source, Envvars = tpl.env, ChangeMode = tpl.change_mode, ChangeSignal = tpl.change_signal, Perms = tpl.perms, LeftDelim = tpl.left_delimiter, RightDelim = tpl.right_delimiter, } ) t.template, Artifacts = lib.records.mapToArray ( fun artName art => { GetterHeaders = art.headers, GetterMode = art.mode, GetterOptions = art.options, GetterSource = art.source, RelativeDest = artName, } ) t.artifact, } & ( if t.vault != null then { Vault = { ChangeMode = t.vault.change_mode, ChangeSignal = t.vault.change_signal, Env = t.vault.env, Namespace = t.vault.namespace, Policies = t.vault.policies, } } else {} ) & { VolumeMounts = lib.records.mapToArray ( fun volName vol => { Destination = vol.destination, PropagationMode = "private", ReadOnly = vol.read_only, Volume = volName, } ) t.volume_mount, } ) tg.task, # END TASKS } & ( if tg.vault != null then { Vault = { ChangeMode = tg.vault.change_mode, ChangeSignal = tg.vault.change_signal, Env = tg.vault.env, Namespace = tg.vault.namespace, Policies = tg.vault.policies, } } else {} ) & lib.records.mapToArray ( fun volName vol => { Volumes."#{volName}" = { Name = volName, Type = vol.type, Source = vol.source, ReadOnly = vol.read_only, } & ( if vol.type == "csi" then { MountOptions = { FsType = vol.mount_options.fs_type, mountFlags = vol.mount_options.mount_flags, } } else {} ) } ) tg.volume ) job.group, }, stanza = { job = let type_schema = [| 'batch, 'service, 'system |] in { datacenters | std.array.NonEmpty, namespace | String, type | type_schema | default = 'service, affinities | Array stanza.affinity | default = [], constraints | Array stanza.constraint | default = [], spreads | Array stanza.spread | default = [], group | { _ : stanza.group & { type | type_schema | default = 'service } }, update | lib.contracts.Nullable stanza.update | default = null, vaultc | lib.contracts.Nullable stanza.vault | default = null, priority_ | std.number.Nat | default = 50, periodic | lib.contracts.Nullable stanza.periodic | default = null, migrate | lib.contracts.Nullable stanza.migrate | default = null, }, migrate = { health_check | [| 'checks, 'task_states |] | default = 'checks, healthy_deadline | std.number.Nat | default = 500000000000, max_parallel | std.number.Nat | default = 1, min_healthy_time | std.number.Nat | default = 10000000000, }, periodic = { time_zone | String | default = "UTC", prohibit_overlap | Bool | default = false, cron | String, }, affinity = { LTarget | String | default = null, RTarget | String, Operand | lib.contracts.OneOf [ "regexp", "set_contains_all", "set_contains", "set_contains_any", "=", "==", "is", "!=", "not", ">", ">=", "<", "<=", "version" ] | default = "=", Weight | std.number.Nat | lib.contracts.NotEq 0 | lib.contracts.GreaterEq - 100 | lib.contracts.SmallerEq 100, }, constraint = { attribute | String | default = null, value | String, operator | lib.contracts.OneOf [ "=", "!=", ">", ">=", "<", "<=", "distinct_hosts", "distinct_property", "regexp", "set_contains", "version", "semver", "is_set", "is_not_set" ] | default = "=", }, spread = { attribute | lib.contracts.Nullable String | default = null, weight | lib.contracts.Nullable ( lib.contracts.AllOf [ std.number.Nat, lib.contracts.GreaterEq - 100, lib.contracts.SmallerEq 100 ] ) | default = null, target | Array stanza.targetElem | default = [], }, targetElem = { value | lib.contracts.Nullable String | default = null, percent | lib.contracts.Nullable ( lib.contracts.AllOf [ std.number.Nat, lib.contracts.GreaterEq - 100, lib.contracts.SmallerEq 100 ] ) | default = null, }, ephemeral_disk = lib.contracts.Nullable { size | std.number.PosNat, migrate | Bool | default = false, sticky | Bool | default = false, }, group = { type | [| 'service, 'batch, 'system |], affinities | Array stanza.affinity | default = [], constraints | Array stanza.constraint | default = [], spreads | Array stanza.spread | default = [], ephemeral_disk | stanza.ephemeral_disk | default = null, network | lib.contracts.Nullable stanza.network | default = null, service | { _ : stanza.service }, task | { _ : stanza.task }, count | std.number.Nat, volume | { _ : stanza.volume } | default = {}, vault | lib.contracts.Nullable stanza.vault | default = null, restart | lib.contracts.Nullable stanza.restart | default = null, restart_policy | lib.contracts.Nullable stanza.restart | default = null, reschedule | stanza.reschedule & { type | [| 'service, 'batch, 'system |] } | default = {}, }, reschedule = { type | [| 'batch, 'service, 'system |] | default = 'service, # TODO: convert with ifs or sth # if #type == "batch" { # attempts: uint | *1 # delay: #Duration | *"5s" # delay_function: *"constant" | "exponential" | "fibonacci" # interval: #Duration | *"24h" # unlimited: bool | *false # } # if #type == "service" || type == "system" { # interval: #Duration | *"0m" # attempts: uint | *0 # delay: #Duration | *"30s" # delay_function: "constant" | *"exponential" | "fibonacci" # max_delay: #Duration | *"1h" # // if unlimited is true, interval and attempts are ignored # unlimited: bool | *true # } }, network = { mode | [| 'host, 'bridge |], dns | lib.contracts.Nullable { servers | Array String | default = [] } | default = null, port | { _ : { static | lib.contracts.Nullable std.number.Nat | default = null, to | lib.contracts.Nullable std.number.Nat | default = null, host_network | String | default = "", } }, }, check_restart = { limit | std.number.PosNat, grace | Duration, ignore_warnings | Bool | default = false, }, service = { check_restart | lib.contracts.Nullable stanza.check_restart | default = null, port | String, address_mode | [| 'alloc, 'driver, 'auto, 'host |] | default = 'auto, tags | Array String | default = [], task | String | default = "", check | { _ : stanza.check } | default = {}, meta | { _ : String } | default = {}, }, check = { address_mode | [| 'alloc, 'driver, 'host |] | default = 'driver, type | [| 'http, 'tcp, 'script, 'grpc |], port | String, interval | Duration, timeout | Duration, check_restart | lib.contracts.Nullable stanza.check_restart | default = null, header | { _ : Array String } | default = {}, body | lib.contracts.Nullable String | default = null, initial_status | (lib.contracts.OneOf [ "passing", "warning", "critical", "" ]) | default = "", success_before_passing | std.number.Nat | default = 0, failures_before_critical | std.number.Nat | default = 0, tls_skip_verify | Bool | default = false, #TODO: convert once we can do it #if type == "http" { # method: *"GET" | "POST" # path: string # protocol: *"http" | "https" #} #if type != "http" { # method: "" # path: "" # protocol: "" #} path | String | default = "", method | String | default = "", protocol | String | default = "", }, #Hmm... actual union :( hard to do #taskConfig: dockerConfig | execConfig execConfig = { flake | String, command | String, args | Array String | default = [], }, label = { _ : String }, dockerConfig = { image | String, command | lib.contracts.Nullable String | default = null, args | Array String | default = [], ports | Array String | default = [], labels | Array label | default = [], logging | dockerConfigLogging }, dockerConfigLogging = { type = "journald", config | Array dockerConfigLoggingConfig | default = [], }, dockerConfigLoggingConfig = { tag | String, labels | String, }, lifecycle = { hook | [| 'prestart, 'poststart, 'poststop |], sidecar | lib.contracts.Nullable Bool | default = null, }, logs = { max_files | std.number.PosNat, max_file_size | std.number.PosNat, }, task = { affinities | Array stanza.affinity | default = [], constraints | Array stanza.constraint | default = [], #TODO: contracts on field names #artifact: [Destination=_]: { artifact | { _ : { # actually, the key must be the destination # destination | Destination, headers | { _ : String }, mode | [| 'any, 'file, 'dir |] | default = 'any, options | { _ : String }, source | String, } } | default = {}, #TODO: dependent if #if driver == "docker" { # config: #stanza.dockerConfig #} #if driver == "exec" { # config: #stanza.execConfig #} # meantime, allow config field: config | { .. }, driver | [| 'exec, 'docker, 'nspawn |], env | { _ : String } | default = {}, kill_signal | String | default = "SIGINT", #TODO #if driver == "docker" { # kill_signal: "SIGTERM" #} kill_timeout | lib.contracts.Nullable Duration | default = null, lifecycle | lib.contracts.Nullable stanza.lifecycle | default = null, logs | lib.contracts.Nullable stanza.logs | default = null, resources = { cpu | std.number.Nat | lib.contracts.GreaterEq 100, memory | std.number.Nat | lib.contracts.GreaterEq 32, }, #TODO: contracts on fields #template: [Destination=_]: { template | { _ : { # Actually, the key must be a destination, there is no # destination field # destination | Destination, data | String | default = "", source | String | default = "", env | Bool | default = false, change_mode | [| 'restart, 'noop, 'signal |] | default = 'restart, change_signal | String | default = "", perms | (lib.contracts.MatchRegexp "^[0-7]{4}$") | default = "0644", left_delimiter | String | default = "{{", right_delimiter | String | default = "}}", splay | Duration | default = "3s", } }, vault | lib.contracts.Nullable stanza.vault | default = null, volume_mount | { _ : stanza.volume_mount } | default = {}, restart | lib.contracts.Nullable stanza.restart | default = null, restart_policy | lib.contracts.Nullable stanza.restart | default = null, leader | Bool | default = false, .. }, restart = { interval | Duration, attempts | std.number.PosNat, delay | Duration, mode | [| 'delay, 'fail |], }, update = { auto_promote | Bool | default = false, auto_revert | Bool | default = false, canary | std.number.Nat | default = 0, health_check | [| 'checks, 'task_states, 'manual |] | default = 'checks, healthy_deadline | Duration | default = "5m", max_parallel | std.number.Nat | default = 1, min_healthy_time | Duration | default = "10s", progress_deadline | Duration | default = "10m", stagger | Duration | default = "30s", }, vault = { change_mode | [| 'noop, 'restart, 'signal |] | default = 'restart, change_signal | String | default = "", env | Bool | default = true, namespace | String | default = "", policies | Array String | default = [], }, volume = { type | [| 'host, 'csi |], source | String, read_only | Bool | default = false, #TODO: dependent if #if type == "csi" { # mount_options: { # fs_type: *null | string # mount_flags: *null | string # } #} }, volume_mount = { # Specifies the group volume that the mount is going to access. volume | String | default = "", # Specifies where the volume should be mounted inside the task's # allocation. destination | String | default = "", foo : String = destination ++ volume, # When a group volume is writeable, you may specify that it is read_only # on a per mount level using the read_only option here. read_only | Bool | default = false, }, }, }