mirror of
https://code.forgejo.org/forgejo/runner.git
synced 2025-08-06 17:40:58 +00:00
This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [github.com/golangci/golangci-lint/cmd/golangci-lint](https://github.com/golangci/golangci-lint) | `v1.62.2` -> `v2.2.2` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>golangci/golangci-lint (github.com/golangci/golangci-lint/cmd/golangci-lint)</summary> ### [`v2.2.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v222) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.2.1...v2.2.2) 1. Linters bug fixes - `noinlineerr`: from 1.0.3 to 1.0.4 2. Documentation - Improve debug keys documentation 3. Misc. - fix: panic close of closed channel - godot: add noinline value into the JSONSchema ### [`v2.2.1`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v221) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.2.0...v2.2.1) 1. Linters bug fixes - `varnamelen`: fix configuration ### [`v2.2.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v220) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.1.6...v2.2.0) 1. New linters - Add `arangolint` linter https://github.com/Crocmagnon/arangolint - Add `embeddedstructfieldcheck` linter https://github.com/manuelarte/embeddedstructfieldcheck - Add `noinlineerr` linter https://github.com/AlwxSin/noinlineerr - Add `swaggo` formatter https://github.com/golangci/swaggoswag 2. Linters new features or changes - `errcheck`: add `verbose` option - `funcorder`: from 0.2.1 to 0.5.0 (new option `alphabetical`) - `gomoddirectives`: from 0.6.1 to 0.7.0 (new option `ignore-forbidden`) - `iface`: from 1.3.1 to 1.4.0 (new option `unexported`) - `noctx`: from 0.1.0 to 0.3.3 (new report messages, and new rules related to `database/sql`) - `noctx`: from 0.3.3 to 0.3.4 (new SQL functions detection) - `revive`: from 1.9.0 to 1.10.0 (new rules: `time-date`, `unnecessary-format`, `use-fmt-print`) - `usestdlibvars`: from 1.28.0 to 1.29.0 (new option `time-date-month`) - `wsl`: deprecation - `wsl_v5`: from 4.7.0 to 5.0.0 (major version with new configuration) 3. Linters bug fixes - `dupword`: from 0.1.3 to 0.1.6 - `exptostd`: from 0.4.3 to 0.4.4 - `forbidigo`: from 1.6.0 to 2.1.0 - `gci`: consistently format the code - `go-spancheck`: from 0.6.4 to 0.6.5 - `goconst`: from 1.8.1 to 1.8.2 - `gosec`: from 2.22.3 to 2.22.4 - `gosec`: from 2.22.4 to 2.22.5 - `makezero`: from 1.2.0 to 2.0.1 - `misspell`: from 0.6.0 to 0.7.0 - `usetesting`: from 0.4.3 to 0.5.0 4. Misc. - exclusions: fix `path-expect` - formatters: write the input to `stdout` when using `stdin` and there are no changes - migration: improve the error message when trying to migrate a migrated config - `typecheck`: deduplicate errors - `typecheck`: stops the analysis after the first error - Deprecate `print-resources-usage` flag - Unique version per custom build 5. Documentation - Improves typecheck FAQ - Adds plugin systems recommendations - Add description for `linters.default` sets ### [`v2.1.6`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v216) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.1.5...v2.1.6) 1. Linters bug fixes - `godot`: from 1.5.0 to 1.5.1 - `musttag`: from 0.13.0 to 0.13.1 2. Documentation - Add note about golangci-lint v2 integration in VS Code ### [`v2.1.5`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v215) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.1.4...v2.1.5) Due to an error related to Snapcraft, some artifacts of the v2.1.4 release have not been published. This release contains the same things as v2.1.3. ### [`v2.1.4`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v214) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.1.3...v2.1.4) Due to an error related to Snapcraft, some artifacts of the v2.1.3 release have not been published. This release contains the same things as v2.1.3. ### [`v2.1.3`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v213) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.1.2...v2.1.3) 1. Linters bug fixes - `fatcontext`: from 0.7.2 to 0.8.0 2. Misc. - migration: fix `nakedret.max-func-lines: 0` - migration: fix order of `staticcheck` settings - fix: add `go.mod` hash to the cache salt - fix: use diagnostic position for related information position ### [`v2.1.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v212) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.1.1...v2.1.2) 1. Linters bug fixes - `exptostd`: from 0.4.2 to 0.4.3 - `gofumpt`: from 0.7.0 to 0.8.0 - `protogetter`: from 0.3.13 to 0.3.15 - `usetesting`: from 0.4.2 to 0.4.3 ### [`v2.1.1`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v211) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.1.0...v2.1.1) The release process of v2.1.0 failed due to a regression inside goreleaser. The binaries of v2.1.0 have been published, but not the other artifacts (AUR, Docker, etc.). ### [`v2.1.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v210) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.0.2...v2.1.0) 1. Enhancements - Add an option to display absolute paths (`--path-mode=abs`) - Add configuration path placeholder (`${config-path}`) - Add `warn-unused` option for `fmt` command - Colored diff for `fmt` command (`golangci-lint fmt --diff-colored`) 2. New linters - Add `funcorder` linter https://github.com/manuelarte/funcorder 3. Linters new features or changes - `go-errorlint`: from 1.7.1 to 1.8.0 (automatic error comparison and type assertion fixes) - ⚠️ `goconst`: `ignore-strings` is deprecated and replaced by `ignore-string-values` - `goconst`: from 1.7.1 to 1.8.1 (new options: `find-duplicates`, `eval-const-expressions`) - `govet`: add `httpmux` analyzer - `nilnesserr`: from 0.1.2 to 0.2.0 (detect more cases) - `paralleltest`: from 1.0.10 to 1.0.14 (checks only `_test.go` files) - `revive`: from 1.7.0 to 1.9.0 (support kebab case for setting names) - `sloglint`: from 0.9.0 to 0.11.0 (autofix, new option `msg-style`, suggest `slog.DiscardHandler`) - `wrapcheck`: from 2.10.0 to 2.11.0 (new option `report-internal-errors`) - `wsl`: from 4.6.0 to 4.7.0 (cgo files are always excluded) 4. Linters bug fixes - `fatcontext`: from 0.7.1 to 0.7.2 - `gocritic`: fix `importshadow` checker - `gosec`: from 2.22.2 to 2.22.3 - `ireturn`: from 0.3.1 to 0.4.0 - `loggercheck`: from 0.10.1 to 0.11.0 - `nakedret`: from 2.0.5 to 2.0.6 - `nonamedreturns`: from 1.0.5 to 1.0.6 - `protogetter`: from 0.3.12 to 0.3.13 - `testifylint`: from 1.6.0 to 1.6.1 - `unconvert`: update to HEAD 5. Misc. - Fixes memory leaks when using go1.(N) with golangci-lint built with go1.(N-X) - Adds `golangci-lint-fmt` pre-commit hook 6. Documentation - Improvements - Updates section about vscode integration ### [`v2.0.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v202) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.0.1...v2.0.2) 1. Misc. - Fixes flags parsing for formatters - Fixes the filepath used by the exclusion `source` option 2. Documentation - Adds a section about flags migration - Cleaning pages with v1 options ### [`v2.0.1`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v201) [Compare Source](https://github.com/golangci/golangci-lint/compare/v2.0.0...v2.0.1) 1. Linters/formatters bug fixes - `golines`: fix settings during linter load 2. Misc. - Validates the `version` field before the configuration - `forbidigo`: fix migration ### [`v2.0.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v200) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.8...v2.0.0) 1. Enhancements - 🌟 New `golangci-lint fmt` command with dedicated [formatter configuration](https://golangci-lint.run/welcome/quick-start/#formatting) - ♻️ New `golangci-lint migrate` command to help migration from v1 to v2 (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#command-migrate)) - ⚠️ New default values (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - ⚠️ No exclusions by default (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#issuesexclude-use-default)) - ⚠️ New default sort order (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#outputsort-order)) - 🌟 New option `run.relative-path-mode` (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#runrelative-path-mode)) - 🌟 New linters configuration (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#linters)) - 🌟 New output format configuration (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#output)) - 🌟 New `--fast-only` flag (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#lintersfast)) - 🌟 New option `linters.exclusions.warn-unused` to log a warning if an exclusion rule is unused. 2. New linters/formatters - Add `golines` formatter https://github.com/segmentio/golines 3. Linters new features - ⚠️ Merge `staticcheck`, `stylecheck`, `gosimple` into one linter (`staticcheck`) (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#lintersenablestylecheckgosimplestaticcheck)) - `go-critic`: from 0.12.0 to 0.13.0 - `gomodguard`: from 1.3.5 to 1.4.1 (block explicit indirect dependencies) - `nilnil`: from 1.0.1 to 1.1.0 (new option: `only-two`) - `perfsprint`: from 0.8.2 to 0.9.1 (checker name in the diagnostic message) - `staticcheck`: new `quickfix` set of rules - `testifylint`: from 1.5.2 to 1.6.0 (new options: `equal-values`, `suite-method-signature`, `require-string-msg`) - `wsl`: from 4.5.0 to 4.6.0 (new option: `allow-cuddle-used-in-block`) 4. Linters bug fixes - `bidichk`: from 0.3.2 to 0.3.3 - `errchkjson`: from 0.4.0 to 0.4.1 - `errname`: from 1.0.0 to 1.1.0 - `funlen`: fix `ignore-comments` option - `gci`: from 0.13.5 to 0.13.6 - `gosmopolitan`: from 1.2.2 to 1.3.0 - `inamedparam`: from 0.1.3 to 0.2.0 - `intrange`: from 0.3.0 to 0.3.1 - `protogetter`: from 0.3.9 to 0.3.12 - `unparam`: from [`8a5130c`](8a5130ca72
) to [`0df0534`](0df0534333
) 5. Misc. - 🧹 Configuration options renaming (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - 🧹 Remove options (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - 🧹 Remove flags (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - 🧹 Remove alternative names (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#alternative-linter-names)) - 🧹 Remove or replace deprecated elements (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - Adds an option to display some commands as JSON: - `golangci-lint config path --json` - `golangci-lint help linters --json` - `golangci-lint help formatters --json` - `golangci-lint linters --json` - `golangci-lint formatters --json` - `golangci-lint version --json` 6. Documentation - [Migration guide](https://golangci-lint.run/product/migration-guide/) ### [`v1.64.8`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1648) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.7...v1.64.8) - Detects use of configuration files from golangci-lint v2 ### [`v1.64.7`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1647) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.6...v1.64.7) 1. Linters bug fixes - `depguard`: from 2.2.0 to 2.2.1 - `dupl`: from [`3e9179a`](3e9179ac44
) to [`f665c8d`](f665c8d69b
) - `gosec`: from 2.22.1 to 2.22.2 - `staticcheck`: from 0.6.0 to 0.6.1 2. Documentation - Add GitLab documentation ### [`v1.64.6`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1646) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.5...v1.64.6) 1. Linters bug fixes - `asciicheck`: from 0.4.0 to 0.4.1 - `contextcheck`: from 1.1.5 to 1.1.6 - `errcheck`: from 1.8.0 to 1.9.0 - `exptostd`: from 0.4.1 to 0.4.2 - `ginkgolinter`: from 0.19.0 to 0.19.1 - `go-exhaustruct`: from 3.3.0 to 3.3.1 - `gocheckcompilerdirectives`: from 1.2.1 to 1.3.0 - `godot`: from 1.4.20 to 1.5.0 - `perfsprint`: from 0.8.1 to 0.8.2 - `revive`: from 1.6.1 to 1.7.0 - `tagalign`: from 1.4.1 to 1.4.2 ### [`v1.64.5`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1645) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.4...v1.64.5) 1. Bug fixes - Add missing flag `new-from-merge-base-flag` 2. Linters bug fixes - `asciicheck`: from 0.3.0 to 0.4.0 - `forcetypeassert`: from 0.1.0 to 0.2.0 - `gosec`: from 2.22.0 to 2.22.1 ### [`v1.64.4`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1644) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.3...v1.64.4) 1. Linters bug fixes - `gci`: fix standard packages list for go1.24 ### [`v1.64.3`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1643) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.2...v1.64.3) 1. Linters bug fixes - `ginkgolinter`: from 0.18.4 to 0.19.0 - `go-critic`: from 0.11.5 to 0.12.0 - `revive`: from 1.6.0 to 1.6.1 - `gci`: fix standard packages list for go1.24 2. Misc. - Build Docker images with go1.24 ### [`v1.64.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1642) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.1...v1.64.2) This is the last minor release of golangci-lint v1. The next release will be golangci-lint [v2](https://github.com/golangci/golangci-lint/issues/5300). 1. Enhancements - 🎉 go1.24 support - New `issues.new-from-merge-base` option - New `run.relative-path-mode` option 2. Linters new features - `copyloopvar`: from 1.1.0 to 1.2.1 (support suggested fixes) - `exptostd`: from 0.3.1 to 0.4.1 (handles `golang.org/x/exp/constraints.Ordered`) - `fatcontext`: from 0.5.3 to 0.7.1 (new option: `check-struct-pointers`) - `perfsprint`: from 0.7.1 to 0.8.1 (new options: `integer-format`, `error-format`, `string-format`, `bool-format`, and `hex-format`) - `revive`: from 1.5.1 to 1.6.0 (new rules: `redundant-build-tag`, `use-errors-new`. New option `early-return.early-return`) 3. Linters bug fixes - `go-errorlint`: from 1.7.0 to 1.7.1 - `gochecknoglobals`: from 0.2.1 to 0.2.2 - `godox`: from [`006bad1`](006bad1f9d
) to 1.1.0 - `gosec`: from 2.21.4 to 2.22.0 - `iface`: from 1.3.0 to 1.3.1 - `nilnesserr`: from 0.1.1 to 0.1.2 - `protogetter`: from 0.3.8 to 0.3.9 - `sloglint`: from 0.7.2 to 0.9.0 - `spancheck`: fix default `StartSpanMatchersSlice` values - `staticcheck`: from 0.5.1 to 0.6.0 4. Deprecations - ⚠️ `tenv` is deprecated and replaced by `usetesting.os-setenv: true`. - ⚠️ `exportloopref` deprecation step 2 5. Misc. - Sanitize severities by output format - Avoid panic with plugin without description 6. Documentation - Clarify `depguard` configuration ### [`v1.64.1`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1641) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.0...v1.64.1) Cancelled due to CI failure. ### [`v1.64.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1640) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.4...v1.64.0) Cancelled due to CI failure. ### [`v1.63.4`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1634) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.3...v1.63.4) 1. Linters bug fixes - `dupl`, `gomodguard`, `revive`: keep only Go-files. ### [`v1.63.3`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1633) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.2...v1.63.3) 1. Linters bug fixes - `gofmt`, `gofumpt`, `goimports`, `gci`: panic with several trailing EOL - `goheader`: skip issues with invalid positions ### [`v1.63.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1632) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.1...v1.63.2) 1. Linters bug fixes - `gofmt`, `gofumpt`, `goimports`, `gci`: panic with missing trailing EOL ### [`v1.63.1`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1631) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.0...v1.63.1) 1. Linters bug fixes - `cgi`: invalid reports with cgo - `gofumpt`: panic with autofix and cgo ### [`v1.63.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1630) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.62.2...v1.63.0) 1. Enhancements - Add support for SuggestedFixes 🎉 (35 linters can "autofix" reports). - Formatters (`gofmt`, `goimports`, `gofumpt`, `gci`) are applied after the suggested fixes. 2. New linters - Add `exptostd` linter https://github.com/ldez/exptostd - Add `nilnesserr` linter https://github.com/alingse/nilnesserr - Add `usetesting` linter https://github.com/ldez/usetesting 3. Linters new features - `gci`: new options: `no-inline-comments`, `no-prefix-comments` - `gomoddirectives`: from 0.2.4 to 0.6.0 (new options: `go-version-pattern`, `toolchain-pattern`,`toolchain-forbidden`, `tool-forbidden`, `go-debug-forbidden`) - `govet`: new `stdversion`, `waitgroup` analyzers - `importas`: allow multiple empty aliases - `loggercheck`: new `slog` option - `recvcheck`: from 0.1.2 to 0.2.0 (new options: `disable-builtin`, `exclusions`) - `tagliatelle`: from 0.5.0 to 0.7.1 (new options: `ignored-fields`, `extended-rules`,`overrides`, `pkg`, `ignore`) - `usestdlibvars`: from 1.27.0 to 1.28.0 (autofix) - `wrapcheck`: from 2.9.0 to 2.10.0 (new option: `extra-ignore-sigs`) 4. Linters bug fixes - `asciicheck`: from 0.2.0 to 0.3.0 - `bodyclose`: from [`5742072`](5742072509
) to [`ed6a65f`](ed6a65f985
) - `funlen`: from 0.1.0 to 0.2.0 - `ginkgolinter`: from 0.18.3 to 0.18.4 - `gochecksumtype`: from 0.2.0 to 0.3.1 - `gocognit`: from 1.1.3 to 1.2.0 - `godot`: from 1.4.18 to 1.4.20 - `goheader`: report position improvement - `gosec`: handling of global nosec option when it is false - `iface`: from 1.2.1 to 1.3.0 - `importas`: from 0.1.0 to 0.2.0 - `intrange`: from 0.2.1 to 0.3.0 - `makezero`: from 1.1.1 to 1.2.0 - `mirror`: from 1.2.0 to 1.3.0 - `nilnil`: from 1.0.0 to 1.0.1 - `nosprintfhostport`: from 0.1.1 to 0.2.0 - `reassign`: from 0.2.0 to 0.3.0 - `spancheck`: from 0.6.2 to 0.6.4 - `tagalign`: from 1.3.4 to 1.4.1 - `wastedassign`: from 2.0.7 to 2.1.0 - `whitespace`: from 0.1.1 to 0.2.0 - `wsl`: from 4.4.1 to 4.5.0 5. Deprecations - ⚠️ `output.uniq-by-line` is deprecated and replaced by `issues.uniq-by-line`. 6. Misc. - Improvements of the help command (color and JSON support). - Removes `decoder`, `sloglint`, `tagalign` from `format` preset. - Enables paths with junction inside Windows. - The timeout is disabled if `run.timeout` <= 0. </details> --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4zMi4xIiwidXBkYXRlZEluVmVyIjoiNDEuMzIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: Earl Warren <contact@earl-warren.org> Reviewed-on: https://code.forgejo.org/forgejo/act/pulls/185 Reviewed-by: earl-warren <earl-warren@noreply.code.forgejo.org> Co-authored-by: Renovate Bot <bot@kriese.eu> Co-committed-by: Renovate Bot <bot@kriese.eu>
1083 lines
40 KiB
Go
1083 lines
40 KiB
Go
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
|
|
|
|
// This file is exact copy of https://github.com/docker/cli/blob/9ac8584acfd501c3f4da0e845e3a40ed15c85041/cli/command/container/opts.go
|
|
// appended with license information.
|
|
//
|
|
// docker/cli is licensed under the Apache License, Version 2.0.
|
|
// See DOCKER_LICENSE for the full license text.
|
|
//
|
|
|
|
//nolint:unparam,errcheck,depguard,unused
|
|
package container
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/docker/cli/cli/compose/loader"
|
|
"github.com/docker/cli/opts"
|
|
"github.com/docker/docker/api/types/container"
|
|
mounttypes "github.com/docker/docker/api/types/mount"
|
|
networktypes "github.com/docker/docker/api/types/network"
|
|
"github.com/docker/docker/api/types/strslice"
|
|
"github.com/docker/docker/api/types/versions"
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
var deviceCgroupRuleRegexp = regexp.MustCompile(`^[acb] ([0-9]+|\*):([0-9]+|\*) [rwm]{1,3}$`)
|
|
|
|
// containerOptions is a data object with all the options for creating a container
|
|
type containerOptions struct {
|
|
attach opts.ListOpts
|
|
volumes opts.ListOpts
|
|
tmpfs opts.ListOpts
|
|
mounts opts.MountOpt
|
|
blkioWeightDevice opts.WeightdeviceOpt
|
|
deviceReadBps opts.ThrottledeviceOpt
|
|
deviceWriteBps opts.ThrottledeviceOpt
|
|
links opts.ListOpts
|
|
aliases opts.ListOpts
|
|
linkLocalIPs opts.ListOpts
|
|
deviceReadIOps opts.ThrottledeviceOpt
|
|
deviceWriteIOps opts.ThrottledeviceOpt
|
|
env opts.ListOpts
|
|
labels opts.ListOpts
|
|
deviceCgroupRules opts.ListOpts
|
|
devices opts.ListOpts
|
|
gpus opts.GpuOpts
|
|
ulimits *opts.UlimitOpt
|
|
sysctls *opts.MapOpts
|
|
publish opts.ListOpts
|
|
expose opts.ListOpts
|
|
dns opts.ListOpts
|
|
dnsSearch opts.ListOpts
|
|
dnsOptions opts.ListOpts
|
|
extraHosts opts.ListOpts
|
|
volumesFrom opts.ListOpts
|
|
envFile opts.ListOpts
|
|
capAdd opts.ListOpts
|
|
capDrop opts.ListOpts
|
|
groupAdd opts.ListOpts
|
|
securityOpt opts.ListOpts
|
|
storageOpt opts.ListOpts
|
|
labelsFile opts.ListOpts
|
|
loggingOpts opts.ListOpts
|
|
privileged bool
|
|
pidMode string
|
|
utsMode string
|
|
usernsMode string
|
|
cgroupnsMode string
|
|
publishAll bool
|
|
stdin bool
|
|
tty bool
|
|
oomKillDisable bool
|
|
oomScoreAdj int
|
|
containerIDFile string
|
|
entrypoint string
|
|
hostname string
|
|
domainname string
|
|
memory opts.MemBytes
|
|
memoryReservation opts.MemBytes
|
|
memorySwap opts.MemSwapBytes
|
|
kernelMemory opts.MemBytes
|
|
user string
|
|
workingDir string
|
|
cpuCount int64
|
|
cpuShares int64
|
|
cpuPercent int64
|
|
cpuPeriod int64
|
|
cpuRealtimePeriod int64
|
|
cpuRealtimeRuntime int64
|
|
cpuQuota int64
|
|
cpus opts.NanoCPUs
|
|
cpusetCpus string
|
|
cpusetMems string
|
|
blkioWeight uint16
|
|
ioMaxBandwidth opts.MemBytes
|
|
ioMaxIOps uint64
|
|
swappiness int64
|
|
netMode opts.NetworkOpt
|
|
macAddress string
|
|
ipv4Address string
|
|
ipv6Address string
|
|
ipcMode string
|
|
pidsLimit int64
|
|
restartPolicy string
|
|
readonlyRootfs bool
|
|
loggingDriver string
|
|
cgroupParent string
|
|
volumeDriver string
|
|
stopSignal string
|
|
stopTimeout int
|
|
isolation string
|
|
shmSize opts.MemBytes
|
|
noHealthcheck bool
|
|
healthCmd string
|
|
healthInterval time.Duration
|
|
healthTimeout time.Duration
|
|
healthStartPeriod time.Duration
|
|
healthRetries int
|
|
runtime string
|
|
autoRemove bool
|
|
init bool
|
|
|
|
Image string
|
|
Args []string
|
|
}
|
|
|
|
// addFlags adds all command line flags that will be used by parse to the FlagSet
|
|
func addFlags(flags *pflag.FlagSet) *containerOptions {
|
|
copts := &containerOptions{
|
|
aliases: opts.NewListOpts(nil),
|
|
attach: opts.NewListOpts(validateAttach),
|
|
blkioWeightDevice: opts.NewWeightdeviceOpt(opts.ValidateWeightDevice),
|
|
capAdd: opts.NewListOpts(nil),
|
|
capDrop: opts.NewListOpts(nil),
|
|
dns: opts.NewListOpts(opts.ValidateIPAddress),
|
|
dnsOptions: opts.NewListOpts(nil),
|
|
dnsSearch: opts.NewListOpts(opts.ValidateDNSSearch),
|
|
deviceCgroupRules: opts.NewListOpts(validateDeviceCgroupRule),
|
|
deviceReadBps: opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice),
|
|
deviceReadIOps: opts.NewThrottledeviceOpt(opts.ValidateThrottleIOpsDevice),
|
|
deviceWriteBps: opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice),
|
|
deviceWriteIOps: opts.NewThrottledeviceOpt(opts.ValidateThrottleIOpsDevice),
|
|
devices: opts.NewListOpts(nil), // devices can only be validated after we know the server OS
|
|
env: opts.NewListOpts(opts.ValidateEnv),
|
|
envFile: opts.NewListOpts(nil),
|
|
expose: opts.NewListOpts(nil),
|
|
extraHosts: opts.NewListOpts(opts.ValidateExtraHost),
|
|
groupAdd: opts.NewListOpts(nil),
|
|
labels: opts.NewListOpts(opts.ValidateLabel),
|
|
labelsFile: opts.NewListOpts(nil),
|
|
linkLocalIPs: opts.NewListOpts(nil),
|
|
links: opts.NewListOpts(opts.ValidateLink),
|
|
loggingOpts: opts.NewListOpts(nil),
|
|
publish: opts.NewListOpts(nil),
|
|
securityOpt: opts.NewListOpts(nil),
|
|
storageOpt: opts.NewListOpts(nil),
|
|
sysctls: opts.NewMapOpts(nil, opts.ValidateSysctl),
|
|
tmpfs: opts.NewListOpts(nil),
|
|
ulimits: opts.NewUlimitOpt(nil),
|
|
volumes: opts.NewListOpts(nil),
|
|
volumesFrom: opts.NewListOpts(nil),
|
|
}
|
|
|
|
// General purpose flags
|
|
flags.VarP(&copts.attach, "attach", "a", "Attach to STDIN, STDOUT or STDERR")
|
|
flags.Var(&copts.deviceCgroupRules, "device-cgroup-rule", "Add a rule to the cgroup allowed devices list")
|
|
flags.Var(&copts.devices, "device", "Add a host device to the container")
|
|
flags.Var(&copts.gpus, "gpus", "GPU devices to add to the container ('all' to pass all GPUs)")
|
|
flags.SetAnnotation("gpus", "version", []string{"1.40"})
|
|
flags.VarP(&copts.env, "env", "e", "Set environment variables")
|
|
flags.Var(&copts.envFile, "env-file", "Read in a file of environment variables")
|
|
flags.StringVar(&copts.entrypoint, "entrypoint", "", "Overwrite the default ENTRYPOINT of the image")
|
|
flags.Var(&copts.groupAdd, "group-add", "Add additional groups to join")
|
|
flags.StringVarP(&copts.hostname, "hostname", "h", "", "Container host name")
|
|
flags.StringVar(&copts.domainname, "domainname", "", "Container NIS domain name")
|
|
flags.BoolVarP(&copts.stdin, "interactive", "i", false, "Keep STDIN open even if not attached")
|
|
flags.VarP(&copts.labels, "label", "l", "Set meta data on a container")
|
|
flags.Var(&copts.labelsFile, "label-file", "Read in a line delimited file of labels")
|
|
flags.BoolVar(&copts.readonlyRootfs, "read-only", false, "Mount the container's root filesystem as read only")
|
|
flags.StringVar(&copts.restartPolicy, "restart", "no", "Restart policy to apply when a container exits")
|
|
flags.StringVar(&copts.stopSignal, "stop-signal", "", "Signal to stop the container")
|
|
flags.IntVar(&copts.stopTimeout, "stop-timeout", 0, "Timeout (in seconds) to stop a container")
|
|
flags.SetAnnotation("stop-timeout", "version", []string{"1.25"})
|
|
flags.Var(copts.sysctls, "sysctl", "Sysctl options")
|
|
flags.BoolVarP(&copts.tty, "tty", "t", false, "Allocate a pseudo-TTY")
|
|
flags.Var(copts.ulimits, "ulimit", "Ulimit options")
|
|
flags.StringVarP(&copts.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
|
flags.StringVarP(&copts.workingDir, "workdir", "w", "", "Working directory inside the container")
|
|
flags.BoolVar(&copts.autoRemove, "rm", false, "Automatically remove the container when it exits")
|
|
|
|
// Security
|
|
flags.Var(&copts.capAdd, "cap-add", "Add Linux capabilities")
|
|
flags.Var(&copts.capDrop, "cap-drop", "Drop Linux capabilities")
|
|
flags.BoolVar(&copts.privileged, "privileged", false, "Give extended privileges to this container")
|
|
flags.Var(&copts.securityOpt, "security-opt", "Security Options")
|
|
flags.StringVar(&copts.usernsMode, "userns", "", "User namespace to use")
|
|
flags.StringVar(&copts.cgroupnsMode, "cgroupns", "", `Cgroup namespace to use (host|private)
|
|
'host': Run the container in the Docker host's cgroup namespace
|
|
'private': Run the container in its own private cgroup namespace
|
|
'': Use the cgroup namespace as configured by the
|
|
default-cgroupns-mode option on the daemon (default)`)
|
|
flags.SetAnnotation("cgroupns", "version", []string{"1.41"})
|
|
|
|
// Network and port publishing flag
|
|
flags.Var(&copts.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
|
|
flags.Var(&copts.dns, "dns", "Set custom DNS servers")
|
|
// We allow for both "--dns-opt" and "--dns-option", although the latter is the recommended way.
|
|
// This is to be consistent with service create/update
|
|
flags.Var(&copts.dnsOptions, "dns-opt", "Set DNS options")
|
|
flags.Var(&copts.dnsOptions, "dns-option", "Set DNS options")
|
|
flags.MarkHidden("dns-opt")
|
|
flags.Var(&copts.dnsSearch, "dns-search", "Set custom DNS search domains")
|
|
flags.Var(&copts.expose, "expose", "Expose a port or a range of ports")
|
|
flags.StringVar(&copts.ipv4Address, "ip", "", "IPv4 address (e.g., 172.30.100.104)")
|
|
flags.StringVar(&copts.ipv6Address, "ip6", "", "IPv6 address (e.g., 2001:db8::33)")
|
|
flags.Var(&copts.links, "link", "Add link to another container")
|
|
flags.Var(&copts.linkLocalIPs, "link-local-ip", "Container IPv4/IPv6 link-local addresses")
|
|
flags.StringVar(&copts.macAddress, "mac-address", "", "Container MAC address (e.g., 92:d0:c6:0a:29:33)")
|
|
flags.VarP(&copts.publish, "publish", "p", "Publish a container's port(s) to the host")
|
|
flags.BoolVarP(&copts.publishAll, "publish-all", "P", false, "Publish all exposed ports to random ports")
|
|
// We allow for both "--net" and "--network", although the latter is the recommended way.
|
|
flags.Var(&copts.netMode, "net", "Connect a container to a network")
|
|
flags.Var(&copts.netMode, "network", "Connect a container to a network")
|
|
flags.MarkHidden("net")
|
|
// We allow for both "--net-alias" and "--network-alias", although the latter is the recommended way.
|
|
flags.Var(&copts.aliases, "net-alias", "Add network-scoped alias for the container")
|
|
flags.Var(&copts.aliases, "network-alias", "Add network-scoped alias for the container")
|
|
flags.MarkHidden("net-alias")
|
|
|
|
// Logging and storage
|
|
flags.StringVar(&copts.loggingDriver, "log-driver", "", "Logging driver for the container")
|
|
flags.StringVar(&copts.volumeDriver, "volume-driver", "", "Optional volume driver for the container")
|
|
flags.Var(&copts.loggingOpts, "log-opt", "Log driver options")
|
|
flags.Var(&copts.storageOpt, "storage-opt", "Storage driver options for the container")
|
|
flags.Var(&copts.tmpfs, "tmpfs", "Mount a tmpfs directory")
|
|
flags.Var(&copts.volumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
|
|
flags.VarP(&copts.volumes, "volume", "v", "Bind mount a volume")
|
|
flags.Var(&copts.mounts, "mount", "Attach a filesystem mount to the container")
|
|
|
|
// Health-checking
|
|
flags.StringVar(&copts.healthCmd, "health-cmd", "", "Command to run to check health")
|
|
flags.DurationVar(&copts.healthInterval, "health-interval", 0, "Time between running the check (ms|s|m|h) (default 0s)")
|
|
flags.IntVar(&copts.healthRetries, "health-retries", 0, "Consecutive failures needed to report unhealthy")
|
|
flags.DurationVar(&copts.healthTimeout, "health-timeout", 0, "Maximum time to allow one check to run (ms|s|m|h) (default 0s)")
|
|
flags.DurationVar(&copts.healthStartPeriod, "health-start-period", 0, "Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 0s)")
|
|
flags.SetAnnotation("health-start-period", "version", []string{"1.29"})
|
|
flags.BoolVar(&copts.noHealthcheck, "no-healthcheck", false, "Disable any container-specified HEALTHCHECK")
|
|
|
|
// Resource management
|
|
flags.Uint16Var(&copts.blkioWeight, "blkio-weight", 0, "Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)")
|
|
flags.Var(&copts.blkioWeightDevice, "blkio-weight-device", "Block IO weight (relative device weight)")
|
|
flags.StringVar(&copts.containerIDFile, "cidfile", "", "Write the container ID to the file")
|
|
flags.StringVar(&copts.cpusetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
|
|
flags.StringVar(&copts.cpusetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
|
|
flags.Int64Var(&copts.cpuCount, "cpu-count", 0, "CPU count (Windows only)")
|
|
flags.SetAnnotation("cpu-count", "ostype", []string{"windows"})
|
|
flags.Int64Var(&copts.cpuPercent, "cpu-percent", 0, "CPU percent (Windows only)")
|
|
flags.SetAnnotation("cpu-percent", "ostype", []string{"windows"})
|
|
flags.Int64Var(&copts.cpuPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period")
|
|
flags.Int64Var(&copts.cpuQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
|
|
flags.Int64Var(&copts.cpuRealtimePeriod, "cpu-rt-period", 0, "Limit CPU real-time period in microseconds")
|
|
flags.SetAnnotation("cpu-rt-period", "version", []string{"1.25"})
|
|
flags.Int64Var(&copts.cpuRealtimeRuntime, "cpu-rt-runtime", 0, "Limit CPU real-time runtime in microseconds")
|
|
flags.SetAnnotation("cpu-rt-runtime", "version", []string{"1.25"})
|
|
flags.Int64VarP(&copts.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
|
flags.Var(&copts.cpus, "cpus", "Number of CPUs")
|
|
flags.SetAnnotation("cpus", "version", []string{"1.25"})
|
|
flags.Var(&copts.deviceReadBps, "device-read-bps", "Limit read rate (bytes per second) from a device")
|
|
flags.Var(&copts.deviceReadIOps, "device-read-iops", "Limit read rate (IO per second) from a device")
|
|
flags.Var(&copts.deviceWriteBps, "device-write-bps", "Limit write rate (bytes per second) to a device")
|
|
flags.Var(&copts.deviceWriteIOps, "device-write-iops", "Limit write rate (IO per second) to a device")
|
|
flags.Var(&copts.ioMaxBandwidth, "io-maxbandwidth", "Maximum IO bandwidth limit for the system drive (Windows only)")
|
|
flags.SetAnnotation("io-maxbandwidth", "ostype", []string{"windows"})
|
|
flags.Uint64Var(&copts.ioMaxIOps, "io-maxiops", 0, "Maximum IOps limit for the system drive (Windows only)")
|
|
flags.SetAnnotation("io-maxiops", "ostype", []string{"windows"})
|
|
flags.Var(&copts.kernelMemory, "kernel-memory", "Kernel memory limit")
|
|
flags.VarP(&copts.memory, "memory", "m", "Memory limit")
|
|
flags.Var(&copts.memoryReservation, "memory-reservation", "Memory soft limit")
|
|
flags.Var(&copts.memorySwap, "memory-swap", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
|
flags.Int64Var(&copts.swappiness, "memory-swappiness", -1, "Tune container memory swappiness (0 to 100)")
|
|
flags.BoolVar(&copts.oomKillDisable, "oom-kill-disable", false, "Disable OOM Killer")
|
|
flags.IntVar(&copts.oomScoreAdj, "oom-score-adj", 0, "Tune host's OOM preferences (-1000 to 1000)")
|
|
flags.Int64Var(&copts.pidsLimit, "pids-limit", 0, "Tune container pids limit (set -1 for unlimited)")
|
|
|
|
// Low-level execution (cgroups, namespaces, ...)
|
|
flags.StringVar(&copts.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
|
|
flags.StringVar(&copts.ipcMode, "ipc", "", "IPC mode to use")
|
|
flags.StringVar(&copts.isolation, "isolation", "", "Container isolation technology")
|
|
flags.StringVar(&copts.pidMode, "pid", "", "PID namespace to use")
|
|
flags.Var(&copts.shmSize, "shm-size", "Size of /dev/shm")
|
|
flags.StringVar(&copts.utsMode, "uts", "", "UTS namespace to use")
|
|
flags.StringVar(&copts.runtime, "runtime", "", "Runtime to use for this container")
|
|
|
|
flags.BoolVar(&copts.init, "init", false, "Run an init inside the container that forwards signals and reaps processes")
|
|
flags.SetAnnotation("init", "version", []string{"1.25"})
|
|
return copts
|
|
}
|
|
|
|
type containerConfig struct {
|
|
Config *container.Config
|
|
HostConfig *container.HostConfig
|
|
NetworkingConfig *networktypes.NetworkingConfig
|
|
}
|
|
|
|
// parse parses the args for the specified command and generates a Config,
|
|
// a HostConfig and returns them with the specified command.
|
|
// If the specified args are not valid, it will return an error.
|
|
//
|
|
//nolint:gocyclo
|
|
func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*containerConfig, error) {
|
|
var (
|
|
attachStdin = copts.attach.Get("stdin")
|
|
attachStdout = copts.attach.Get("stdout")
|
|
attachStderr = copts.attach.Get("stderr")
|
|
)
|
|
|
|
// Validate the input mac address
|
|
if copts.macAddress != "" {
|
|
if _, err := opts.ValidateMACAddress(copts.macAddress); err != nil {
|
|
return nil, errors.Errorf("%s is not a valid mac address", copts.macAddress)
|
|
}
|
|
}
|
|
if copts.stdin {
|
|
attachStdin = true
|
|
}
|
|
// If -a is not set, attach to stdout and stderr
|
|
if copts.attach.Len() == 0 {
|
|
attachStdout = true
|
|
attachStderr = true
|
|
}
|
|
|
|
var err error
|
|
|
|
swappiness := copts.swappiness
|
|
if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
|
|
return nil, errors.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
|
}
|
|
|
|
mounts := copts.mounts.Value()
|
|
if len(mounts) > 0 && copts.volumeDriver != "" {
|
|
logrus.Warn("`--volume-driver` is ignored for volumes specified via `--mount`. Use `--mount type=volume,volume-driver=...` instead.")
|
|
}
|
|
var binds []string
|
|
volumes := copts.volumes.GetMap()
|
|
// add any bind targets to the list of container volumes
|
|
for bind := range copts.volumes.GetMap() {
|
|
parsed, _ := loader.ParseVolume(bind)
|
|
|
|
if parsed.Source != "" {
|
|
toBind := bind
|
|
|
|
if parsed.Type == string(mounttypes.TypeBind) {
|
|
if arr := strings.SplitN(bind, ":", 2); len(arr) == 2 {
|
|
hostPart := arr[0]
|
|
if strings.HasPrefix(hostPart, "."+string(filepath.Separator)) || hostPart == "." {
|
|
if absHostPart, err := filepath.Abs(hostPart); err == nil {
|
|
hostPart = absHostPart
|
|
}
|
|
}
|
|
toBind = hostPart + ":" + arr[1]
|
|
}
|
|
}
|
|
|
|
// after creating the bind mount we want to delete it from the copts.volumes values because
|
|
// we do not want bind mounts being committed to image configs
|
|
binds = append(binds, toBind)
|
|
// We should delete from the map (`volumes`) here, as deleting from copts.volumes will not work if
|
|
// there are duplicates entries.
|
|
delete(volumes, bind)
|
|
}
|
|
}
|
|
|
|
// Can't evaluate options passed into --tmpfs until we actually mount
|
|
tmpfs := make(map[string]string)
|
|
for _, t := range copts.tmpfs.GetAll() {
|
|
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
|
tmpfs[arr[0]] = arr[1]
|
|
} else {
|
|
tmpfs[arr[0]] = ""
|
|
}
|
|
}
|
|
|
|
var (
|
|
runCmd strslice.StrSlice
|
|
entrypoint strslice.StrSlice
|
|
)
|
|
|
|
if len(copts.Args) > 0 {
|
|
runCmd = strslice.StrSlice(copts.Args)
|
|
}
|
|
|
|
if copts.entrypoint != "" {
|
|
entrypoint = strslice.StrSlice{copts.entrypoint}
|
|
} else if flags.Changed("entrypoint") {
|
|
// if `--entrypoint=` is parsed then Entrypoint is reset
|
|
entrypoint = []string{""}
|
|
}
|
|
|
|
publishOpts := copts.publish.GetAll()
|
|
var (
|
|
ports map[nat.Port]struct{}
|
|
portBindings map[nat.Port][]nat.PortBinding
|
|
convertedOpts []string
|
|
)
|
|
|
|
convertedOpts, err = convertToStandardNotation(publishOpts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ports, portBindings, err = nat.ParsePortSpecs(convertedOpts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Merge in exposed ports to the map of published ports
|
|
for _, e := range copts.expose.GetAll() {
|
|
if strings.Contains(e, ":") {
|
|
return nil, errors.Errorf("invalid port format for --expose: %s", e)
|
|
}
|
|
// support two formats for expose, original format <portnum>/[<proto>]
|
|
// or <startport-endport>/[<proto>]
|
|
proto, port := nat.SplitProtoPort(e)
|
|
// parse the start and end port and create a sequence of ports to expose
|
|
// if expose a port, the start and end port are the same
|
|
start, end, err := nat.ParsePortRange(port)
|
|
if err != nil {
|
|
return nil, errors.Errorf("invalid range format for --expose: %s, error: %s", e, err)
|
|
}
|
|
for i := start; i <= end; i++ {
|
|
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, exists := ports[p]; !exists {
|
|
ports[p] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
// validate and parse device mappings. Note we do late validation of the
|
|
// device path (as opposed to during flag parsing), as at the time we are
|
|
// parsing flags, we haven't yet sent a _ping to the daemon to determine
|
|
// what operating system it is.
|
|
deviceMappings := []container.DeviceMapping{}
|
|
for _, device := range copts.devices.GetAll() {
|
|
var (
|
|
validated string
|
|
deviceMapping container.DeviceMapping
|
|
err error
|
|
)
|
|
validated, err = validateDevice(device, serverOS)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deviceMapping, err = parseDevice(validated, serverOS)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deviceMappings = append(deviceMappings, deviceMapping)
|
|
}
|
|
|
|
// collect all the environment variables for the container
|
|
envVariables, err := opts.ReadKVEnvStrings(copts.envFile.GetAll(), copts.env.GetAll())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// collect all the labels for the container
|
|
labels, err := opts.ReadKVStrings(copts.labelsFile.GetAll(), copts.labels.GetAll())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pidMode := container.PidMode(copts.pidMode)
|
|
if !pidMode.Valid() {
|
|
return nil, errors.Errorf("--pid: invalid PID mode")
|
|
}
|
|
|
|
utsMode := container.UTSMode(copts.utsMode)
|
|
if !utsMode.Valid() {
|
|
return nil, errors.Errorf("--uts: invalid UTS mode")
|
|
}
|
|
|
|
usernsMode := container.UsernsMode(copts.usernsMode)
|
|
if !usernsMode.Valid() {
|
|
return nil, errors.Errorf("--userns: invalid USER mode")
|
|
}
|
|
|
|
cgroupnsMode := container.CgroupnsMode(copts.cgroupnsMode)
|
|
if !cgroupnsMode.Valid() {
|
|
return nil, errors.Errorf("--cgroupns: invalid CGROUP mode")
|
|
}
|
|
|
|
restartPolicy, err := opts.ParseRestartPolicy(copts.restartPolicy)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loggingOpts, err := parseLoggingOpts(copts.loggingDriver, copts.loggingOpts.GetAll())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
securityOpts, err := parseSecurityOpts(copts.securityOpt.GetAll())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
securityOpts, maskedPaths, readonlyPaths := parseSystemPaths(securityOpts)
|
|
|
|
storageOpts, err := parseStorageOpts(copts.storageOpt.GetAll())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Healthcheck
|
|
var healthConfig *container.HealthConfig
|
|
haveHealthSettings := copts.healthCmd != "" ||
|
|
copts.healthInterval != 0 ||
|
|
copts.healthTimeout != 0 ||
|
|
copts.healthStartPeriod != 0 ||
|
|
copts.healthRetries != 0
|
|
if copts.noHealthcheck {
|
|
if haveHealthSettings {
|
|
return nil, errors.Errorf("--no-healthcheck conflicts with --health-* options")
|
|
}
|
|
test := strslice.StrSlice{"NONE"}
|
|
healthConfig = &container.HealthConfig{Test: test}
|
|
} else if haveHealthSettings {
|
|
var probe strslice.StrSlice
|
|
if copts.healthCmd != "" {
|
|
args := []string{"CMD-SHELL", copts.healthCmd}
|
|
probe = strslice.StrSlice(args)
|
|
}
|
|
if copts.healthInterval < 0 {
|
|
return nil, errors.Errorf("--health-interval cannot be negative")
|
|
}
|
|
if copts.healthTimeout < 0 {
|
|
return nil, errors.Errorf("--health-timeout cannot be negative")
|
|
}
|
|
if copts.healthRetries < 0 {
|
|
return nil, errors.Errorf("--health-retries cannot be negative")
|
|
}
|
|
if copts.healthStartPeriod < 0 {
|
|
return nil, fmt.Errorf("--health-start-period cannot be negative")
|
|
}
|
|
|
|
healthConfig = &container.HealthConfig{
|
|
Test: probe,
|
|
Interval: copts.healthInterval,
|
|
Timeout: copts.healthTimeout,
|
|
StartPeriod: copts.healthStartPeriod,
|
|
Retries: copts.healthRetries,
|
|
}
|
|
}
|
|
|
|
resources := container.Resources{
|
|
CgroupParent: copts.cgroupParent,
|
|
Memory: copts.memory.Value(),
|
|
MemoryReservation: copts.memoryReservation.Value(),
|
|
MemorySwap: copts.memorySwap.Value(),
|
|
MemorySwappiness: &copts.swappiness,
|
|
KernelMemory: copts.kernelMemory.Value(),
|
|
OomKillDisable: &copts.oomKillDisable,
|
|
NanoCPUs: copts.cpus.Value(),
|
|
CPUCount: copts.cpuCount,
|
|
CPUPercent: copts.cpuPercent,
|
|
CPUShares: copts.cpuShares,
|
|
CPUPeriod: copts.cpuPeriod,
|
|
CpusetCpus: copts.cpusetCpus,
|
|
CpusetMems: copts.cpusetMems,
|
|
CPUQuota: copts.cpuQuota,
|
|
CPURealtimePeriod: copts.cpuRealtimePeriod,
|
|
CPURealtimeRuntime: copts.cpuRealtimeRuntime,
|
|
PidsLimit: &copts.pidsLimit,
|
|
BlkioWeight: copts.blkioWeight,
|
|
BlkioWeightDevice: copts.blkioWeightDevice.GetList(),
|
|
BlkioDeviceReadBps: copts.deviceReadBps.GetList(),
|
|
BlkioDeviceWriteBps: copts.deviceWriteBps.GetList(),
|
|
BlkioDeviceReadIOps: copts.deviceReadIOps.GetList(),
|
|
BlkioDeviceWriteIOps: copts.deviceWriteIOps.GetList(),
|
|
IOMaximumIOps: copts.ioMaxIOps,
|
|
IOMaximumBandwidth: uint64(copts.ioMaxBandwidth), //nolint:gosec
|
|
Ulimits: copts.ulimits.GetList(),
|
|
DeviceCgroupRules: copts.deviceCgroupRules.GetAll(),
|
|
Devices: deviceMappings,
|
|
DeviceRequests: copts.gpus.Value(),
|
|
}
|
|
|
|
config := &container.Config{
|
|
Hostname: copts.hostname,
|
|
Domainname: copts.domainname,
|
|
ExposedPorts: ports,
|
|
User: copts.user,
|
|
Tty: copts.tty,
|
|
// TODO: deprecated, it comes from -n, --networking
|
|
// it's still needed internally to set the network to disabled
|
|
// if e.g. bridge is none in daemon opts, and in inspect
|
|
NetworkDisabled: false,
|
|
OpenStdin: copts.stdin,
|
|
AttachStdin: attachStdin,
|
|
AttachStdout: attachStdout,
|
|
AttachStderr: attachStderr,
|
|
Env: envVariables,
|
|
Cmd: runCmd,
|
|
Image: copts.Image,
|
|
Volumes: volumes,
|
|
MacAddress: copts.macAddress,
|
|
Entrypoint: entrypoint,
|
|
WorkingDir: copts.workingDir,
|
|
Labels: opts.ConvertKVStringsToMap(labels),
|
|
StopSignal: copts.stopSignal,
|
|
Healthcheck: healthConfig,
|
|
}
|
|
if flags.Changed("stop-timeout") {
|
|
config.StopTimeout = &copts.stopTimeout
|
|
}
|
|
|
|
hostConfig := &container.HostConfig{
|
|
Binds: binds,
|
|
ContainerIDFile: copts.containerIDFile,
|
|
OomScoreAdj: copts.oomScoreAdj,
|
|
AutoRemove: copts.autoRemove,
|
|
Privileged: copts.privileged,
|
|
PortBindings: portBindings,
|
|
Links: copts.links.GetAll(),
|
|
PublishAllPorts: copts.publishAll,
|
|
// Make sure the dns fields are never nil.
|
|
// New containers don't ever have those fields nil,
|
|
// but pre created containers can still have those nil values.
|
|
// See https://github.com/docker/docker/pull/17779
|
|
// for a more detailed explanation on why we don't want that.
|
|
DNS: copts.dns.GetAllOrEmpty(),
|
|
DNSSearch: copts.dnsSearch.GetAllOrEmpty(),
|
|
DNSOptions: copts.dnsOptions.GetAllOrEmpty(),
|
|
ExtraHosts: copts.extraHosts.GetAll(),
|
|
VolumesFrom: copts.volumesFrom.GetAll(),
|
|
IpcMode: container.IpcMode(copts.ipcMode),
|
|
NetworkMode: container.NetworkMode(copts.netMode.NetworkMode()),
|
|
PidMode: pidMode,
|
|
UTSMode: utsMode,
|
|
UsernsMode: usernsMode,
|
|
CgroupnsMode: cgroupnsMode,
|
|
CapAdd: strslice.StrSlice(copts.capAdd.GetAll()),
|
|
CapDrop: strslice.StrSlice(copts.capDrop.GetAll()),
|
|
GroupAdd: copts.groupAdd.GetAll(),
|
|
RestartPolicy: restartPolicy,
|
|
SecurityOpt: securityOpts,
|
|
StorageOpt: storageOpts,
|
|
ReadonlyRootfs: copts.readonlyRootfs,
|
|
LogConfig: container.LogConfig{Type: copts.loggingDriver, Config: loggingOpts},
|
|
VolumeDriver: copts.volumeDriver,
|
|
Isolation: container.Isolation(copts.isolation),
|
|
ShmSize: copts.shmSize.Value(),
|
|
Resources: resources,
|
|
Tmpfs: tmpfs,
|
|
Sysctls: copts.sysctls.GetAll(),
|
|
Runtime: copts.runtime,
|
|
Mounts: mounts,
|
|
MaskedPaths: maskedPaths,
|
|
ReadonlyPaths: readonlyPaths,
|
|
}
|
|
|
|
if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {
|
|
return nil, errors.Errorf("Conflicting options: --restart and --rm")
|
|
}
|
|
|
|
// only set this value if the user provided the flag, else it should default to nil
|
|
if flags.Changed("init") {
|
|
hostConfig.Init = &copts.init
|
|
}
|
|
|
|
// When allocating stdin in attached mode, close stdin at client disconnect
|
|
if config.OpenStdin && config.AttachStdin {
|
|
config.StdinOnce = true
|
|
}
|
|
|
|
networkingConfig := &networktypes.NetworkingConfig{
|
|
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
|
|
}
|
|
|
|
networkingConfig.EndpointsConfig, err = parseNetworkOpts(copts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &containerConfig{
|
|
Config: config,
|
|
HostConfig: hostConfig,
|
|
NetworkingConfig: networkingConfig,
|
|
}, nil
|
|
}
|
|
|
|
// parseNetworkOpts converts --network advanced options to endpoint-specs, and combines
|
|
// them with the old --network-alias and --links. If returns an error if conflicting options
|
|
// are found.
|
|
//
|
|
// this function may return _multiple_ endpoints, which is not currently supported
|
|
// by the daemon, but may be in future; it's up to the daemon to produce an error
|
|
// in case that is not supported.
|
|
func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.EndpointSettings, error) {
|
|
var (
|
|
endpoints = make(map[string]*networktypes.EndpointSettings, len(copts.netMode.Value()))
|
|
hasUserDefined, hasNonUserDefined bool
|
|
)
|
|
|
|
for i, n := range copts.netMode.Value() {
|
|
n := n
|
|
if container.NetworkMode(n.Target).IsUserDefined() {
|
|
hasUserDefined = true
|
|
} else {
|
|
hasNonUserDefined = true
|
|
}
|
|
if i == 0 {
|
|
// The first network corresponds with what was previously the "only"
|
|
// network, and what would be used when using the non-advanced syntax
|
|
// `--network-alias`, `--link`, `--ip`, `--ip6`, and `--link-local-ip`
|
|
// are set on this network, to preserve backward compatibility with
|
|
// the non-advanced notation
|
|
if err := applyContainerOptions(&n, copts); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
ep, err := parseNetworkAttachmentOpt(n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, ok := endpoints[n.Target]; ok {
|
|
return nil, errdefs.InvalidParameter(errors.Errorf("network %q is specified multiple times", n.Target))
|
|
}
|
|
|
|
// For backward compatibility: if no custom options are provided for the network,
|
|
// and only a single network is specified, omit the endpoint-configuration
|
|
// on the client (the daemon will still create it when creating the container)
|
|
if i == 0 && len(copts.netMode.Value()) == 1 {
|
|
if ep == nil || reflect.DeepEqual(*ep, networktypes.EndpointSettings{}) {
|
|
continue
|
|
}
|
|
}
|
|
endpoints[n.Target] = ep
|
|
}
|
|
if hasUserDefined && hasNonUserDefined {
|
|
return nil, errdefs.InvalidParameter(errors.New("conflicting options: cannot attach both user-defined and non-user-defined network-modes"))
|
|
}
|
|
return endpoints, nil
|
|
}
|
|
|
|
func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOptions) error {
|
|
// TODO should copts.MacAddress actually be set on the first network? (currently it's not)
|
|
// TODO should we error if _any_ advanced option is used? (i.e. forbid to combine advanced notation with the "old" flags (`--network-alias`, `--link`, `--ip`, `--ip6`)?
|
|
if len(n.Aliases) > 0 && copts.aliases.Len() > 0 {
|
|
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --network-alias and per-network alias"))
|
|
}
|
|
if len(n.Links) > 0 && copts.links.Len() > 0 {
|
|
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --link and per-network links"))
|
|
}
|
|
if n.IPv4Address != "" && copts.ipv4Address != "" {
|
|
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --ip and per-network IPv4 address"))
|
|
}
|
|
if n.IPv6Address != "" && copts.ipv6Address != "" {
|
|
return errdefs.InvalidParameter(errors.New("conflicting options: cannot specify both --ip6 and per-network IPv6 address"))
|
|
}
|
|
if copts.aliases.Len() > 0 {
|
|
n.Aliases = make([]string, copts.aliases.Len())
|
|
copy(n.Aliases, copts.aliases.GetAll())
|
|
}
|
|
if copts.links.Len() > 0 {
|
|
n.Links = make([]string, copts.links.Len())
|
|
copy(n.Links, copts.links.GetAll())
|
|
}
|
|
if copts.ipv4Address != "" {
|
|
n.IPv4Address = copts.ipv4Address
|
|
}
|
|
if copts.ipv6Address != "" {
|
|
n.IPv6Address = copts.ipv6Address
|
|
}
|
|
|
|
// TODO should linkLocalIPs be added to the _first_ network only, or to _all_ networks? (should this be a per-network option as well?)
|
|
if copts.linkLocalIPs.Len() > 0 {
|
|
n.LinkLocalIPs = make([]string, copts.linkLocalIPs.Len())
|
|
copy(n.LinkLocalIPs, copts.linkLocalIPs.GetAll())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*networktypes.EndpointSettings, error) {
|
|
if strings.TrimSpace(ep.Target) == "" {
|
|
return nil, errors.New("no name set for network")
|
|
}
|
|
if !container.NetworkMode(ep.Target).IsUserDefined() {
|
|
if len(ep.Aliases) > 0 {
|
|
return nil, errors.New("network-scoped aliases are only supported for user-defined networks")
|
|
}
|
|
if len(ep.Links) > 0 {
|
|
return nil, errors.New("links are only supported for user-defined networks")
|
|
}
|
|
}
|
|
|
|
epConfig := &networktypes.EndpointSettings{}
|
|
epConfig.Aliases = append(epConfig.Aliases, ep.Aliases...)
|
|
if len(ep.DriverOpts) > 0 {
|
|
epConfig.DriverOpts = make(map[string]string)
|
|
epConfig.DriverOpts = ep.DriverOpts
|
|
}
|
|
if len(ep.Links) > 0 {
|
|
epConfig.Links = ep.Links
|
|
}
|
|
if ep.IPv4Address != "" || ep.IPv6Address != "" || len(ep.LinkLocalIPs) > 0 {
|
|
epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
|
|
IPv4Address: ep.IPv4Address,
|
|
IPv6Address: ep.IPv6Address,
|
|
LinkLocalIPs: ep.LinkLocalIPs,
|
|
}
|
|
}
|
|
return epConfig, nil
|
|
}
|
|
|
|
func convertToStandardNotation(ports []string) ([]string, error) {
|
|
optsList := []string{}
|
|
for _, publish := range ports {
|
|
if strings.Contains(publish, "=") {
|
|
params := map[string]string{"protocol": "tcp"}
|
|
for _, param := range strings.Split(publish, ",") {
|
|
opt := strings.Split(param, "=")
|
|
if len(opt) < 2 {
|
|
return optsList, errors.Errorf("invalid publish opts format (should be name=value but got '%s')", param)
|
|
}
|
|
|
|
params[opt[0]] = opt[1]
|
|
}
|
|
optsList = append(optsList, fmt.Sprintf("%s:%s/%s", params["published"], params["target"], params["protocol"]))
|
|
} else {
|
|
optsList = append(optsList, publish)
|
|
}
|
|
}
|
|
return optsList, nil
|
|
}
|
|
|
|
func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
|
|
loggingOptsMap := opts.ConvertKVStringsToMap(loggingOpts)
|
|
if loggingDriver == "none" && len(loggingOpts) > 0 {
|
|
return map[string]string{}, errors.Errorf("invalid logging opts for driver %s", loggingDriver)
|
|
}
|
|
return loggingOptsMap, nil
|
|
}
|
|
|
|
// takes a local seccomp daemon, reads the file contents for sending to the daemon
|
|
func parseSecurityOpts(securityOpts []string) ([]string, error) {
|
|
for key, opt := range securityOpts {
|
|
con := strings.SplitN(opt, "=", 2)
|
|
if len(con) == 1 && con[0] != "no-new-privileges" {
|
|
if strings.Contains(opt, ":") {
|
|
con = strings.SplitN(opt, ":", 2)
|
|
} else {
|
|
return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt)
|
|
}
|
|
}
|
|
if con[0] == "seccomp" && con[1] != "unconfined" {
|
|
f, err := os.ReadFile(con[1])
|
|
if err != nil {
|
|
return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
|
|
}
|
|
b := bytes.NewBuffer(nil)
|
|
if err := json.Compact(b, f); err != nil {
|
|
return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
|
|
}
|
|
securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
|
|
}
|
|
}
|
|
|
|
return securityOpts, nil
|
|
}
|
|
|
|
// parseSystemPaths checks if `systempaths=unconfined` security option is set,
|
|
// and returns the `MaskedPaths` and `ReadonlyPaths` accordingly. An updated
|
|
// list of security options is returned with this option removed, because the
|
|
// `unconfined` option is handled client-side, and should not be sent to the
|
|
// daemon.
|
|
func parseSystemPaths(securityOpts []string) (filtered, maskedPaths, readonlyPaths []string) {
|
|
filtered = securityOpts[:0]
|
|
for _, opt := range securityOpts {
|
|
if opt == "systempaths=unconfined" {
|
|
maskedPaths = []string{}
|
|
readonlyPaths = []string{}
|
|
} else {
|
|
filtered = append(filtered, opt)
|
|
}
|
|
}
|
|
|
|
return filtered, maskedPaths, readonlyPaths
|
|
}
|
|
|
|
// parses storage options per container into a map
|
|
func parseStorageOpts(storageOpts []string) (map[string]string, error) {
|
|
m := make(map[string]string)
|
|
for _, option := range storageOpts {
|
|
if strings.Contains(option, "=") {
|
|
opt := strings.SplitN(option, "=", 2)
|
|
m[opt[0]] = opt[1]
|
|
} else {
|
|
return nil, errors.Errorf("invalid storage option")
|
|
}
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// parseDevice parses a device mapping string to a container.DeviceMapping struct
|
|
func parseDevice(device, serverOS string) (container.DeviceMapping, error) {
|
|
switch serverOS {
|
|
case "linux":
|
|
return parseLinuxDevice(device)
|
|
case "windows":
|
|
return parseWindowsDevice(device)
|
|
}
|
|
return container.DeviceMapping{}, errors.Errorf("unknown server OS: %s", serverOS)
|
|
}
|
|
|
|
// parseLinuxDevice parses a device mapping string to a container.DeviceMapping struct
|
|
// knowing that the target is a Linux daemon
|
|
func parseLinuxDevice(device string) (container.DeviceMapping, error) {
|
|
var src, dst string
|
|
permissions := "rwm"
|
|
arr := strings.Split(device, ":")
|
|
switch len(arr) {
|
|
case 3:
|
|
permissions = arr[2]
|
|
fallthrough
|
|
case 2:
|
|
if validDeviceMode(arr[1]) {
|
|
permissions = arr[1]
|
|
} else {
|
|
dst = arr[1]
|
|
}
|
|
fallthrough
|
|
case 1:
|
|
src = arr[0]
|
|
default:
|
|
return container.DeviceMapping{}, errors.Errorf("invalid device specification: %s", device)
|
|
}
|
|
|
|
if dst == "" {
|
|
dst = src
|
|
}
|
|
|
|
deviceMapping := container.DeviceMapping{
|
|
PathOnHost: src,
|
|
PathInContainer: dst,
|
|
CgroupPermissions: permissions,
|
|
}
|
|
return deviceMapping, nil
|
|
}
|
|
|
|
// parseWindowsDevice parses a device mapping string to a container.DeviceMapping struct
|
|
// knowing that the target is a Windows daemon
|
|
func parseWindowsDevice(device string) (container.DeviceMapping, error) {
|
|
return container.DeviceMapping{PathOnHost: device}, nil
|
|
}
|
|
|
|
// validateDeviceCgroupRule validates a device cgroup rule string format
|
|
// It will make sure 'val' is in the form:
|
|
//
|
|
// 'type major:minor mode'
|
|
func validateDeviceCgroupRule(val string) (string, error) {
|
|
if deviceCgroupRuleRegexp.MatchString(val) {
|
|
return val, nil
|
|
}
|
|
|
|
return val, errors.Errorf("invalid device cgroup format '%s'", val)
|
|
}
|
|
|
|
// validDeviceMode checks if the mode for device is valid or not.
|
|
// Valid mode is a composition of r (read), w (write), and m (mknod).
|
|
func validDeviceMode(mode string) bool {
|
|
legalDeviceMode := map[rune]bool{
|
|
'r': true,
|
|
'w': true,
|
|
'm': true,
|
|
}
|
|
if mode == "" {
|
|
return false
|
|
}
|
|
for _, c := range mode {
|
|
if !legalDeviceMode[c] {
|
|
return false
|
|
}
|
|
legalDeviceMode[c] = false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// validateDevice validates a path for devices
|
|
func validateDevice(val, serverOS string) (string, error) {
|
|
switch serverOS {
|
|
case "linux":
|
|
return validateLinuxPath(val, validDeviceMode)
|
|
case "windows":
|
|
// Windows does validation entirely server-side
|
|
return val, nil
|
|
}
|
|
return "", errors.Errorf("unknown server OS: %s", serverOS)
|
|
}
|
|
|
|
// validateLinuxPath is the implementation of validateDevice knowing that the
|
|
// target server operating system is a Linux daemon.
|
|
// It will make sure 'val' is in the form:
|
|
//
|
|
// [host-dir:]container-path[:mode]
|
|
//
|
|
// It also validates the device mode.
|
|
func validateLinuxPath(val string, validator func(string) bool) (string, error) {
|
|
var containerPath string
|
|
var mode string
|
|
|
|
if strings.Count(val, ":") > 2 {
|
|
return val, errors.Errorf("bad format for path: %s", val)
|
|
}
|
|
|
|
split := strings.SplitN(val, ":", 3)
|
|
if split[0] == "" {
|
|
return val, errors.Errorf("bad format for path: %s", val)
|
|
}
|
|
switch len(split) {
|
|
case 1:
|
|
containerPath = split[0]
|
|
val = path.Clean(containerPath)
|
|
case 2:
|
|
if isValid := validator(split[1]); isValid {
|
|
containerPath = split[0]
|
|
mode = split[1]
|
|
val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
|
|
} else {
|
|
containerPath = split[1]
|
|
val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
|
|
}
|
|
case 3:
|
|
containerPath = split[1]
|
|
mode = split[2]
|
|
if isValid := validator(split[2]); !isValid {
|
|
return val, errors.Errorf("bad mode specified: %s", mode)
|
|
}
|
|
val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
|
|
}
|
|
|
|
if !path.IsAbs(containerPath) {
|
|
return val, errors.Errorf("%s is not an absolute path", containerPath)
|
|
}
|
|
return val, nil
|
|
}
|
|
|
|
// validateAttach validates that the specified string is a valid attach option.
|
|
func validateAttach(val string) (string, error) {
|
|
s := strings.ToLower(val)
|
|
for _, str := range []string{"stdin", "stdout", "stderr"} {
|
|
if s == str {
|
|
return s, nil
|
|
}
|
|
}
|
|
return val, errors.Errorf("valid streams are STDIN, STDOUT and STDERR")
|
|
}
|
|
|
|
func validateAPIVersion(c *containerConfig, serverAPIVersion string) error {
|
|
for _, m := range c.HostConfig.Mounts {
|
|
if m.BindOptions != nil && m.BindOptions.NonRecursive && versions.LessThan(serverAPIVersion, "1.40") {
|
|
return errors.Errorf("bind-nonrecursive requires API v1.40 or later")
|
|
}
|
|
}
|
|
return nil
|
|
}
|