From eaf81b65d9d95d57ee5b64ed6f0846ab6df11384 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 27 Jan 2026 09:34:04 +0100 Subject: [PATCH 1/5] vendor: github.com/klauspost/compress v1.18.3 no changes in vendored code - fixes / downstream CVE-2025-61728 full diff: https://github.com/klauspost/compress/compare/v1.18.2...v1.18.3 Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- go.sum | 4 ++-- vendor/modules.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index decf2d5d13b9..f324cd387e5a 100644 --- a/go.mod +++ b/go.mod @@ -154,7 +154,7 @@ require ( github.com/in-toto/attestation v1.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.2 // indirect + github.com/klauspost/compress v1.18.3 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect github.com/lestrrat-go/dsig v1.0.0 // indirect github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect diff --git a/go.sum b/go.sum index d821ba76d28f..e05b3b894dbc 100644 --- a/go.sum +++ b/go.sum @@ -378,8 +378,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= +github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= diff --git a/vendor/modules.txt b/vendor/modules.txt index 696ac92ac605..587adbfdeccc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -565,7 +565,7 @@ github.com/inconshreveable/mousetrap # github.com/json-iterator/go v1.1.12 ## explicit; go 1.12 github.com/json-iterator/go -# github.com/klauspost/compress v1.18.2 +# github.com/klauspost/compress v1.18.3 ## explicit; go 1.23 github.com/klauspost/compress github.com/klauspost/compress/fse From 6b71de00a053bc7cf1a347b19e982bb2369e3527 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 27 Jan 2026 09:30:19 +0100 Subject: [PATCH 2/5] vendor: github.com/go-viper/mapstructure/v2 v2.5.0 full diff: https://github.com/go-viper/mapstructure/compare/v2.4.0...v2.5.0 Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- go.sum | 4 +- .../go-viper/mapstructure/v2/.editorconfig | 3 + .../go-viper/mapstructure/v2/.envrc | 11 +- .../go-viper/mapstructure/v2/.gitignore | 10 +- .../go-viper/mapstructure/v2/devenv.lock | 103 ++++++ .../go-viper/mapstructure/v2/devenv.nix | 14 + .../go-viper/mapstructure/v2/devenv.yaml | 4 + .../go-viper/mapstructure/v2/flake.lock | 294 ---------------- .../go-viper/mapstructure/v2/flake.nix | 46 --- .../go-viper/mapstructure/v2/mapstructure.go | 332 +++++++++++++++--- vendor/modules.txt | 2 +- 12 files changed, 426 insertions(+), 399 deletions(-) create mode 100644 vendor/github.com/go-viper/mapstructure/v2/devenv.lock create mode 100644 vendor/github.com/go-viper/mapstructure/v2/devenv.nix create mode 100644 vendor/github.com/go-viper/mapstructure/v2/devenv.yaml delete mode 100644 vendor/github.com/go-viper/mapstructure/v2/flake.lock delete mode 100644 vendor/github.com/go-viper/mapstructure/v2/flake.nix diff --git a/go.mod b/go.mod index f324cd387e5a..74b767cded9f 100644 --- a/go.mod +++ b/go.mod @@ -138,7 +138,7 @@ require ( github.com/go-openapi/swag/typeutils v0.25.3 // indirect github.com/go-openapi/swag/yamlutils v0.25.3 // indirect github.com/go-openapi/validate v0.25.1 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect diff --git a/go.sum b/go.sum index e05b3b894dbc..7aa705879a33 100644 --- a/go.sum +++ b/go.sum @@ -271,8 +271,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= diff --git a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig index faef0c91e7e6..c37602a02da8 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig +++ b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig @@ -19,3 +19,6 @@ indent_size = 2 [.golangci.yaml] indent_size = 2 + +[devenv.yaml] +indent_size = 2 diff --git a/vendor/github.com/go-viper/mapstructure/v2/.envrc b/vendor/github.com/go-viper/mapstructure/v2/.envrc index 2e0f9f5f7119..e2be8891cb6e 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/.envrc +++ b/vendor/github.com/go-viper/mapstructure/v2/.envrc @@ -1,4 +1,7 @@ -if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then - source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=" -fi -use flake . --impure +#!/usr/bin/env bash + +export DIRENV_WARN_TIMEOUT=20s + +eval "$(devenv direnvrc)" + +use devenv diff --git a/vendor/github.com/go-viper/mapstructure/v2/.gitignore b/vendor/github.com/go-viper/mapstructure/v2/.gitignore index 470e7ca2bd2b..71caea19ff01 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/.gitignore +++ b/vendor/github.com/go-viper/mapstructure/v2/.gitignore @@ -1,6 +1,10 @@ -/.devenv/ -/.direnv/ -/.pre-commit-config.yaml /bin/ /build/ /var/ + +# Devenv +.devenv* +devenv.local.nix +devenv.local.yaml +.direnv +.pre-commit-config.yaml diff --git a/vendor/github.com/go-viper/mapstructure/v2/devenv.lock b/vendor/github.com/go-viper/mapstructure/v2/devenv.lock new file mode 100644 index 000000000000..72c2c9b4db87 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/devenv.lock @@ -0,0 +1,103 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1765288076, + "owner": "cachix", + "repo": "devenv", + "rev": "93c055af1e8fcac49251f1b2e1c57f78620ad351", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1765121682, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1765016596, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762808025, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1764580874, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "dcf61356c3ab25f1362b4a4428a6d871e84f1d1d", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": [ + "git-hooks" + ] + } + } + }, + "root": "root", + "version": 7 +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/devenv.nix b/vendor/github.com/go-viper/mapstructure/v2/devenv.nix new file mode 100644 index 000000000000..b31ab7a1ffbe --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/devenv.nix @@ -0,0 +1,14 @@ +{ + pkgs, + ... +}: + +{ + languages = { + go.enable = true; + }; + + packages = with pkgs; [ + golangci-lint + ]; +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/devenv.yaml b/vendor/github.com/go-viper/mapstructure/v2/devenv.yaml new file mode 100644 index 000000000000..68616a49cddb --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/devenv.yaml @@ -0,0 +1,4 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.lock b/vendor/github.com/go-viper/mapstructure/v2/flake.lock deleted file mode 100644 index 5e67bdd6b42e..000000000000 --- a/vendor/github.com/go-viper/mapstructure/v2/flake.lock +++ /dev/null @@ -1,294 +0,0 @@ -{ - "nodes": { - "cachix": { - "inputs": { - "devenv": [ - "devenv" - ], - "flake-compat": [ - "devenv" - ], - "git-hooks": [ - "devenv" - ], - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1742042642, - "narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=", - "owner": "cachix", - "repo": "cachix", - "rev": "a624d3eaf4b1d225f918de8543ed739f2f574203", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "latest", - "repo": "cachix", - "type": "github" - } - }, - "devenv": { - "inputs": { - "cachix": "cachix", - "flake-compat": "flake-compat", - "git-hooks": "git-hooks", - "nix": "nix", - "nixpkgs": "nixpkgs_3" - }, - "locked": { - "lastModified": 1744876578, - "narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=", - "owner": "cachix", - "repo": "devenv", - "rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": [ - "devenv", - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": [ - "devenv" - ], - "gitignore": "gitignore", - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1742649964, - "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "devenv", - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "libgit2": { - "flake": false, - "locked": { - "lastModified": 1697646580, - "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", - "owner": "libgit2", - "repo": "libgit2", - "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", - "type": "github" - }, - "original": { - "owner": "libgit2", - "repo": "libgit2", - "type": "github" - } - }, - "nix": { - "inputs": { - "flake-compat": [ - "devenv" - ], - "flake-parts": "flake-parts", - "libgit2": "libgit2", - "nixpkgs": "nixpkgs_2", - "nixpkgs-23-11": [ - "devenv" - ], - "nixpkgs-regression": [ - "devenv" - ], - "pre-commit-hooks": [ - "devenv" - ] - }, - "locked": { - "lastModified": 1741798497, - "narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=", - "owner": "domenkozar", - "repo": "nix", - "rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd", - "type": "github" - }, - "original": { - "owner": "domenkozar", - "ref": "devenv-2.24", - "repo": "nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1733212471, - "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1717432640, - "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "release-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1733477122, - "narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=", - "owner": "cachix", - "repo": "devenv-nixpkgs", - "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "rolling", - "repo": "devenv-nixpkgs", - "type": "github" - } - }, - "nixpkgs_4": { - "locked": { - "lastModified": 1744536153, - "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "devenv": "devenv", - "flake-parts": "flake-parts_2", - "nixpkgs": "nixpkgs_4" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.nix b/vendor/github.com/go-viper/mapstructure/v2/flake.nix deleted file mode 100644 index 3b116f426d46..000000000000 --- a/vendor/github.com/go-viper/mapstructure/v2/flake.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - devenv.url = "github:cachix/devenv"; - }; - - outputs = - inputs@{ flake-parts, ... }: - flake-parts.lib.mkFlake { inherit inputs; } { - imports = [ - inputs.devenv.flakeModule - ]; - - systems = [ - "x86_64-linux" - "x86_64-darwin" - "aarch64-darwin" - ]; - - perSystem = - { pkgs, ... }: - rec { - devenv.shells = { - default = { - languages = { - go.enable = true; - }; - - pre-commit.hooks = { - nixpkgs-fmt.enable = true; - }; - - packages = with pkgs; [ - golangci-lint - ]; - - # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 - containers = pkgs.lib.mkForce { }; - }; - - ci = devenv.shells.default; - }; - }; - }; -} diff --git a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go index 7c35bce02026..9087fd96c79e 100644 --- a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go +++ b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go @@ -173,6 +173,25 @@ // Public: "I made it through!" // } // +// # Custom Decoding with Unmarshaler +// +// Types can implement the Unmarshaler interface to control their own decoding. The interface +// behaves similarly to how UnmarshalJSON does in the standard library. It can be used as an +// alternative or companion to a DecodeHook. +// +// type TrimmedString string +// +// func (t *TrimmedString) UnmarshalMapstructure(input any) error { +// str, ok := input.(string) +// if !ok { +// return fmt.Errorf("expected string, got %T", input) +// } +// *t = TrimmedString(strings.TrimSpace(str)) +// return nil +// } +// +// See the Unmarshaler interface documentation for more details. +// // # Other Configuration // // mapstructure is highly configurable. See the DecoderConfig struct @@ -218,6 +237,17 @@ type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error) // values. type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error) +// Unmarshaler is the interface implemented by types that can unmarshal +// themselves. UnmarshalMapstructure receives the input data (potentially +// transformed by DecodeHook) and should populate the receiver with the +// decoded values. +// +// The Unmarshaler interface takes precedence over the default decoding +// logic for any type (structs, slices, maps, primitives, etc.). +type Unmarshaler interface { + UnmarshalMapstructure(any) error +} + // DecoderConfig is the configuration that is used to create a new decoder // and allows customization of various aspects of decoding. type DecoderConfig struct { @@ -281,6 +311,13 @@ type DecoderConfig struct { // } Squash bool + // Deep will map structures in slices instead of copying them + // + // type Parent struct { + // Children []Child `mapstructure:",deep"` + // } + Deep bool + // Metadata is the struct that will contain extra metadata about // the decoding. If this is nil, then no metadata will be tracked. Metadata *Metadata @@ -290,9 +327,15 @@ type DecoderConfig struct { Result any // The tag name that mapstructure reads for field names. This - // defaults to "mapstructure" + // defaults to "mapstructure". Multiple tag names can be specified + // as a comma-separated list (e.g., "yaml,json"), and the first + // matching non-empty tag will be used. TagName string + // RootName specifies the name to use for the root element in error messages. For example: + // '' has unset fields: + RootName string + // The option of the value in the tag that indicates a field should // be squashed. This defaults to "squash". SquashTagOption string @@ -304,11 +347,34 @@ type DecoderConfig struct { // MatchName is the function used to match the map key to the struct // field name or tag. Defaults to `strings.EqualFold`. This can be used // to implement case-sensitive tag values, support snake casing, etc. + // + // MatchName is used as a fallback comparison when the direct key lookup fails. + // See also MapFieldName for transforming field names before lookup. MatchName func(mapKey, fieldName string) bool // DecodeNil, if set to true, will cause the DecodeHook (if present) to run // even if the input is nil. This can be used to provide default values. DecodeNil bool + + // MapFieldName is the function used to convert the struct field name to the map's key name. + // + // This is useful for automatically converting between naming conventions without + // explicitly tagging each field. For example, to convert Go's PascalCase field names + // to snake_case map keys: + // + // MapFieldName: func(s string) string { + // return strcase.ToSnake(s) + // } + // + // When decoding from a map to a struct, the transformed field name is used for + // the initial lookup. If not found, MatchName is used as a fallback comparison. + // Explicit struct tags always take precedence over MapFieldName. + MapFieldName func(string) string + + // DisableUnmarshaler, if set to true, disables the use of the Unmarshaler + // interface. Types implementing Unmarshaler will be decoded using the + // standard struct decoding logic instead. + DisableUnmarshaler bool } // A Decoder takes a raw interface value and turns it into structured @@ -445,6 +511,12 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { config.MatchName = strings.EqualFold } + if config.MapFieldName == nil { + config.MapFieldName = func(s string) string { + return s + } + } + result := &Decoder{ config: config, } @@ -458,7 +530,7 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { // Decode decodes the given raw interface to the target pointer specified // by the configuration. func (d *Decoder) Decode(input any) error { - err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) + err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem()) // Retain some of the original behavior when multiple errors ocurr var joinedErr interface{ Unwrap() []error } @@ -540,36 +612,50 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { var err error addMetaKey := true - switch outputKind { - case reflect.Bool: - err = d.decodeBool(name, input, outVal) - case reflect.Interface: - err = d.decodeBasic(name, input, outVal) - case reflect.String: - err = d.decodeString(name, input, outVal) - case reflect.Int: - err = d.decodeInt(name, input, outVal) - case reflect.Uint: - err = d.decodeUint(name, input, outVal) - case reflect.Float32: - err = d.decodeFloat(name, input, outVal) - case reflect.Complex64: - err = d.decodeComplex(name, input, outVal) - case reflect.Struct: - err = d.decodeStruct(name, input, outVal) - case reflect.Map: - err = d.decodeMap(name, input, outVal) - case reflect.Ptr: - addMetaKey, err = d.decodePtr(name, input, outVal) - case reflect.Slice: - err = d.decodeSlice(name, input, outVal) - case reflect.Array: - err = d.decodeArray(name, input, outVal) - case reflect.Func: - err = d.decodeFunc(name, input, outVal) - default: - // If we reached this point then we weren't able to decode it - return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind)) + + // Check if the target implements Unmarshaler and use it if not disabled + unmarshaled := false + if !d.config.DisableUnmarshaler { + if unmarshaler, ok := getUnmarshaler(outVal); ok { + if err = unmarshaler.UnmarshalMapstructure(input); err != nil { + err = newDecodeError(name, err) + } + unmarshaled = true + } + } + + if !unmarshaled { + switch outputKind { + case reflect.Bool: + err = d.decodeBool(name, input, outVal) + case reflect.Interface: + err = d.decodeBasic(name, input, outVal) + case reflect.String: + err = d.decodeString(name, input, outVal) + case reflect.Int: + err = d.decodeInt(name, input, outVal) + case reflect.Uint: + err = d.decodeUint(name, input, outVal) + case reflect.Float32: + err = d.decodeFloat(name, input, outVal) + case reflect.Complex64: + err = d.decodeComplex(name, input, outVal) + case reflect.Struct: + err = d.decodeStruct(name, input, outVal) + case reflect.Map: + err = d.decodeMap(name, input, outVal) + case reflect.Ptr: + addMetaKey, err = d.decodePtr(name, input, outVal) + case reflect.Slice: + err = d.decodeSlice(name, input, outVal) + case reflect.Array: + err = d.decodeArray(name, input, outVal) + case reflect.Func: + err = d.decodeFunc(name, input, outVal) + default: + // If we reached this point then we weren't able to decode it + return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind)) + } } // If we reached here, then we successfully decoded SOMETHING, so @@ -668,7 +754,7 @@ func (d *Decoder) decodeString(name string, data any, val reflect.Value) error { case reflect.Uint8: var uints []uint8 if dataKind == reflect.Array { - uints = make([]uint8, dataVal.Len(), dataVal.Len()) + uints = make([]uint8, dataVal.Len()) for i := range uints { uints[i] = dataVal.Index(i).Interface().(uint8) } @@ -1060,8 +1146,8 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re ) } - tagValue := f.Tag.Get(d.config.TagName) - keyName := f.Name + tagValue, _ := getTagValue(f, d.config.TagName) + keyName := d.config.MapFieldName(f.Name) if tagValue == "" && d.config.IgnoreUntaggedFields { continue @@ -1070,6 +1156,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re // If Squash is set in the config, we squash the field down. squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous + // If Deep is set in the config, set as default value. + deep := d.config.Deep + v = dereferencePtrToStructIfNeeded(v, d.config.TagName) // Determine the name of the key in the map @@ -1078,12 +1167,12 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re continue } // If "omitempty" is specified in the tag, it ignores empty values. - if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) { + if strings.Contains(tagValue[index+1:], "omitempty") && isEmptyValue(v) { continue } // If "omitzero" is specified in the tag, it ignores zero values. - if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() { + if strings.Contains(tagValue[index+1:], "omitzero") && v.IsZero() { continue } @@ -1103,7 +1192,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re ) } } else { - if strings.Index(tagValue[index+1:], "remain") != -1 { + if strings.Contains(tagValue[index+1:], "remain") { if v.Kind() != reflect.Map { return newDecodeError( name+"."+f.Name, @@ -1118,6 +1207,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re continue } } + + deep = deep || strings.Contains(tagValue[index+1:], "deep") + if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { keyName = keyNameTagValue } @@ -1164,6 +1256,41 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) } + case reflect.Slice: + if deep { + var childType reflect.Type + switch v.Type().Elem().Kind() { + case reflect.Struct: + childType = reflect.TypeOf(map[string]any{}) + default: + childType = v.Type().Elem() + } + + sType := reflect.SliceOf(childType) + + addrVal := reflect.New(sType) + + vSlice := reflect.MakeSlice(sType, v.Len(), v.Cap()) + + if v.Len() > 0 { + reflect.Indirect(addrVal).Set(vSlice) + + err := d.decode(keyName, v.Interface(), reflect.Indirect(addrVal)) + if err != nil { + return err + } + } + + vSlice = reflect.Indirect(addrVal) + + valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice) + + break + } + + // When deep mapping is not needed, fallthrough to normal copy + fallthrough + default: valMap.SetMapIndex(reflect.ValueOf(keyName), v) } @@ -1471,7 +1598,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e remain := false // We always parse the tags cause we're looking for other tags too - tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") + tagParts := getTagParts(fieldType, d.config.TagName) + if len(tagParts) == 0 { + tagParts = []string{""} + } for _, tag := range tagParts[1:] { if tag == d.config.SquashTagOption { squash = true @@ -1492,6 +1622,18 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e if !fieldVal.IsNil() { structs = append(structs, fieldVal.Elem().Elem()) } + case reflect.Ptr: + if fieldVal.Type().Elem().Kind() == reflect.Struct { + if fieldVal.IsNil() { + fieldVal.Set(reflect.New(fieldVal.Type().Elem())) + } + structs = append(structs, fieldVal.Elem()) + } else { + errs = append(errs, newDecodeError( + name+"."+fieldType.Name, + fmt.Errorf("unsupported type for squashed pointer: %s", fieldVal.Type().Elem().Kind()), + )) + } default: errs = append(errs, newDecodeError( name+"."+fieldType.Name, @@ -1516,13 +1658,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e field, fieldValue := f.field, f.val fieldName := field.Name - tagValue := field.Tag.Get(d.config.TagName) + tagValue, _ := getTagValue(field, d.config.TagName) if tagValue == "" && d.config.IgnoreUntaggedFields { continue } tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue + } else { + fieldName = d.config.MapFieldName(fieldName) } rawMapKey := reflect.ValueOf(fieldName) @@ -1605,8 +1749,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } sort.Strings(keys) + // Improve error message when name is empty by showing the target struct type + // in the case where it is empty for embedded structs. + errorName := name + if errorName == "" { + errorName = val.Type().String() + } errs = append(errs, newDecodeError( - name, + errorName, fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")), )) } @@ -1692,7 +1842,7 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields return true } - if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside + if checkMapstructureTags && hasAnyTag(f, tagName) { // check for mapstructure tags inside return true } } @@ -1700,13 +1850,99 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, } func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value { - if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + if v.Kind() != reflect.Ptr { + return v + } + + switch v.Elem().Kind() { + case reflect.Slice: + return v.Elem() + + case reflect.Struct: + deref := v.Elem() + derefT := deref.Type() + if isStructTypeConvertibleToMap(derefT, true, tagName) { + return deref + } + return v + + default: return v } - deref := v.Elem() - derefT := deref.Type() - if isStructTypeConvertibleToMap(derefT, true, tagName) { - return deref +} + +func hasAnyTag(field reflect.StructField, tagName string) bool { + _, ok := getTagValue(field, tagName) + return ok +} + +func getTagParts(field reflect.StructField, tagName string) []string { + tagValue, ok := getTagValue(field, tagName) + if !ok { + return nil } - return v + return strings.Split(tagValue, ",") +} + +func getTagValue(field reflect.StructField, tagName string) (string, bool) { + for _, name := range splitTagNames(tagName) { + if tag := field.Tag.Get(name); tag != "" { + return tag, true + } + } + return "", false +} + +func splitTagNames(tagName string) []string { + if tagName == "" { + return []string{"mapstructure"} + } + parts := strings.Split(tagName, ",") + result := make([]string, 0, len(parts)) + + for _, name := range parts { + name = strings.TrimSpace(name) + if name != "" { + result = append(result, name) + } + } + + return result +} + +// unmarshalerType is cached for performance +var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + +// getUnmarshaler checks if the value implements Unmarshaler and returns +// the Unmarshaler and a boolean indicating if it was found. It handles both +// pointer and value receivers. +func getUnmarshaler(val reflect.Value) (Unmarshaler, bool) { + // Skip invalid or nil values + if !val.IsValid() { + return nil, false + } + + switch val.Kind() { + case reflect.Pointer, reflect.Interface: + if val.IsNil() { + return nil, false + } + } + + // Check pointer receiver first (most common case) + if val.CanAddr() { + ptrVal := val.Addr() + // Quick check: if no methods, can't implement any interface + if ptrVal.Type().NumMethod() > 0 && ptrVal.Type().Implements(unmarshalerType) { + return ptrVal.Interface().(Unmarshaler), true + } + } + + // Check value receiver + // Quick check: if no methods, can't implement any interface + if val.Type().NumMethod() > 0 && val.CanInterface() && val.Type().Implements(unmarshalerType) { + return val.Interface().(Unmarshaler), true + } + + return nil, false } diff --git a/vendor/modules.txt b/vendor/modules.txt index 587adbfdeccc..d1819622ea63 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -439,7 +439,7 @@ github.com/go-openapi/swag/yamlutils # github.com/go-openapi/validate v0.25.1 ## explicit; go 1.24.0 github.com/go-openapi/validate -# github.com/go-viper/mapstructure/v2 v2.4.0 +# github.com/go-viper/mapstructure/v2 v2.5.0 ## explicit; go 1.18 github.com/go-viper/mapstructure/v2 github.com/go-viper/mapstructure/v2/internal/errors From 9aa7e1578a81c8b70dfa35bc8e31c63d39e896fc Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 19 Jan 2026 13:06:15 +0100 Subject: [PATCH 3/5] vendor: moby/api v1.53.0, moby/client v0.2.2 full diff: - api: https://github.com/moby/moby/compare/api/v1.52.0...api/v1.53.0 - client: https://github.com/moby/moby/compare/client/v0.2.1...client/v0.2.2 Signed-off-by: Sebastiaan van Stijn --- go.mod | 4 +- go.sum | 8 +-- vendor/github.com/moby/moby/api/LICENSE | 17 +++++- .../moby/api/pkg/authconfig/authconfig.go | 6 +- .../moby/api/types/image/build_identity.go | 15 +++++ ...ge_history.go => history_response_item.go} | 11 ++-- .../moby/moby/api/types/image/identity.go | 15 +++++ .../moby/api/types/image/image_inspect.go | 29 ++++++++++ .../moby/api/types/image/pull_identity.go | 8 +++ .../api/types/image/signature_identity.go | 26 +++++++++ .../api/types/image/signature_timestamp.go | 12 ++++ .../moby/api/types/image/signer_identity.go | 57 +++++++++++++++++++ .../moby/moby/api/types/jsonstream/message.go | 4 +- .../moby/moby/api/types/network/endpoint.go | 6 +- .../moby/moby/api/types/network/hwaddr.go | 8 ++- .../moby/moby/api/types/network/ipam.go | 6 +- .../moby/moby/api/types/swarm/network.go | 8 +-- .../moby/moby/api/types/swarm/task.go | 8 +-- .../moby/moby/api/types/system/info.go | 6 ++ .../github.com/moby/moby/api/types/types.go | 5 +- vendor/github.com/moby/moby/client/LICENSE | 17 +++++- vendor/github.com/moby/moby/client/client.go | 10 +++- .../moby/moby/client/client_options.go | 38 ++++++++++++- .../moby/moby/client/client_responsehook.go | 23 ++++++++ .../moby/moby/client/container_attach.go | 2 +- .../moby/moby/client/container_exec.go | 12 ++-- .../moby/moby/client/container_list.go | 26 +++++---- .../moby/moby/client/container_rename.go | 4 +- .../moby/moby/client/container_resize.go | 4 +- .../moby/moby/client/internal/json-stream.go | 2 +- .../moby/moby/client/internal/jsonmessages.go | 7 ++- .../moby/moby/client/system_disk_usage.go | 2 + .../moby/moby/client/system_events.go | 1 + vendor/github.com/moby/moby/client/utils.go | 9 ++- .../moby/moby/client/volume_prune.go | 4 +- vendor/modules.txt | 4 +- 36 files changed, 348 insertions(+), 76 deletions(-) create mode 100644 vendor/github.com/moby/moby/api/types/image/build_identity.go rename vendor/github.com/moby/moby/api/types/image/{image_history.go => history_response_item.go} (59%) create mode 100644 vendor/github.com/moby/moby/api/types/image/identity.go create mode 100644 vendor/github.com/moby/moby/api/types/image/pull_identity.go create mode 100644 vendor/github.com/moby/moby/api/types/image/signature_identity.go create mode 100644 vendor/github.com/moby/moby/api/types/image/signature_timestamp.go create mode 100644 vendor/github.com/moby/moby/api/types/image/signer_identity.go create mode 100644 vendor/github.com/moby/moby/client/client_responsehook.go diff --git a/go.mod b/go.mod index 74b767cded9f..dd1b196bc942 100644 --- a/go.mod +++ b/go.mod @@ -30,8 +30,8 @@ require ( github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/moby/buildkit v0.27.0 github.com/moby/go-archive v0.2.0 - github.com/moby/moby/api v1.52.0 - github.com/moby/moby/client v0.2.1 + github.com/moby/moby/api v1.53.0 + github.com/moby/moby/client v0.2.2 github.com/moby/policy-helpers v0.0.0-20251206004813-9fcc1a9ec5c9 github.com/moby/sys/atomicwriter v0.1.0 github.com/moby/sys/mountinfo v0.7.2 diff --git a/go.sum b/go.sum index 7aa705879a33..77cfb002f0ec 100644 --- a/go.sum +++ b/go.sum @@ -426,10 +426,10 @@ github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8 github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= -github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= -github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k= -github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE= +github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w= +github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= +github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM= +github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/policy-helpers v0.0.0-20251206004813-9fcc1a9ec5c9 h1:SISQT6l9QO+JGSTJ8QN0yWCueiSgfTStSU9KT+p537M= diff --git a/vendor/github.com/moby/moby/api/LICENSE b/vendor/github.com/moby/moby/api/LICENSE index 6d8d58fb676b..d64569567334 100644 --- a/vendor/github.com/moby/moby/api/LICENSE +++ b/vendor/github.com/moby/moby/api/LICENSE @@ -1,7 +1,7 @@ Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -176,13 +176,24 @@ END OF TERMS AND CONDITIONS - Copyright 2013-2018 Docker, Inc. + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/vendor/github.com/moby/moby/api/pkg/authconfig/authconfig.go b/vendor/github.com/moby/moby/api/pkg/authconfig/authconfig.go index 51f883e1c236..d1b0105a6894 100644 --- a/vendor/github.com/moby/moby/api/pkg/authconfig/authconfig.go +++ b/vendor/github.com/moby/moby/api/pkg/authconfig/authconfig.go @@ -71,11 +71,15 @@ func DecodeRequestBody(r io.ReadCloser) (*registry.AuthConfig, error) { func decode(r io.Reader) (*registry.AuthConfig, error) { authConfig := ®istry.AuthConfig{} - if err := json.NewDecoder(r).Decode(authConfig); err != nil { + dec := json.NewDecoder(r) + if err := dec.Decode(authConfig); err != nil { // always return an (empty) AuthConfig to increase compatibility with // the existing API. return ®istry.AuthConfig{}, invalid(fmt.Errorf("invalid JSON: %w", err)) } + if dec.More() { + return ®istry.AuthConfig{}, invalid(errors.New("multiple JSON documents not allowed")) + } return authConfig, nil } diff --git a/vendor/github.com/moby/moby/api/types/image/build_identity.go b/vendor/github.com/moby/moby/api/types/image/build_identity.go new file mode 100644 index 000000000000..1e827dc430d9 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/image/build_identity.go @@ -0,0 +1,15 @@ +package image + +import ( + "time" +) + +// BuildIdentity contains build reference information if image was created via build. +type BuildIdentity struct { + // Ref is the identifier for the build request. This reference can be used to + // look up the build details in BuildKit history API. + Ref string `json:"Ref,omitempty"` + + // CreatedAt is the time when the build ran. + CreatedAt time.Time `json:"CreatedAt,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/image/image_history.go b/vendor/github.com/moby/moby/api/types/image/history_response_item.go similarity index 59% rename from vendor/github.com/moby/moby/api/types/image/image_history.go rename to vendor/github.com/moby/moby/api/types/image/history_response_item.go index 648ba779e547..3de3181ab95a 100644 --- a/vendor/github.com/moby/moby/api/types/image/image_history.go +++ b/vendor/github.com/moby/moby/api/types/image/history_response_item.go @@ -2,13 +2,12 @@ package image -// ---------------------------------------------------------------------------- -// Code generated by `swagger generate operation`. -// -// See hack/generate-swagger-api.sh -// ---------------------------------------------------------------------------- +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command -// HistoryResponseItem individual image layer information in response to ImageHistory operation +// HistoryResponseItem HistoryResponseItem +// +// individual image layer information in response to ImageHistory operation // // swagger:model HistoryResponseItem type HistoryResponseItem struct { diff --git a/vendor/github.com/moby/moby/api/types/image/identity.go b/vendor/github.com/moby/moby/api/types/image/identity.go new file mode 100644 index 000000000000..3e030456348f --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/image/identity.go @@ -0,0 +1,15 @@ +package image + +// Identity holds information about the identity and origin of the image. +// This is trusted information verified by the daemon and cannot be modified +// by tagging an image to a different name. +type Identity struct { + // Signature contains the properties of verified signatures for the image. + Signature []SignatureIdentity `json:"Signature,omitzero"` + // Pull contains remote location information if image was created via pull. + // If image was pulled via mirror, this contains the original repository location. + // After successful push this images also contains the pushed repository location. + Pull []PullIdentity `json:"Pull,omitzero"` + // Build contains build reference information if image was created via build. + Build []BuildIdentity `json:"Build,omitzero"` +} diff --git a/vendor/github.com/moby/moby/api/types/image/image_inspect.go b/vendor/github.com/moby/moby/api/types/image/image_inspect.go index 66a277e55747..df09c9511bd4 100644 --- a/vendor/github.com/moby/moby/api/types/image/image_inspect.go +++ b/vendor/github.com/moby/moby/api/types/image/image_inspect.go @@ -105,4 +105,33 @@ type InspectResponse struct { // WARNING: This is experimental and may change at any time without any backward // compatibility. Manifests []ManifestSummary `json:"Manifests,omitempty"` + + // Identity holds information about the identity and origin of the image. + // This is trusted information verified by the daemon and cannot be modified + // by tagging an image to a different name. + Identity *Identity `json:"Identity,omitempty"` } + +// SignatureTimestampType is the type of timestamp used in the signature. +type SignatureTimestampType string + +const ( + SignatureTimestampTlog SignatureTimestampType = "Tlog" + SignatureTimestampAuthority SignatureTimestampType = "TimestampAuthority" +) + +// SignatureType is the type of signature format. +type SignatureType string + +const ( + SignatureTypeBundleV03 SignatureType = "bundle-v0.3" + SignatureTypeSimpleSigningV1 SignatureType = "simplesigning-v1" +) + +// KnownSignerIdentity is an identifier for a special signer identity that is known to the implementation. +type KnownSignerIdentity string + +const ( + // KnownSignerDHI is the known identity for Docker Hardened Images. + KnownSignerDHI KnownSignerIdentity = "DHI" +) diff --git a/vendor/github.com/moby/moby/api/types/image/pull_identity.go b/vendor/github.com/moby/moby/api/types/image/pull_identity.go new file mode 100644 index 000000000000..711492b5c564 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/image/pull_identity.go @@ -0,0 +1,8 @@ +package image + +// PullIdentity contains remote location information if image was created via pull. +// If image was pulled via mirror, this contains the original repository location. +type PullIdentity struct { + // Repository is the remote repository location the image was pulled from. + Repository string `json:"Repository,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/image/signature_identity.go b/vendor/github.com/moby/moby/api/types/image/signature_identity.go new file mode 100644 index 000000000000..243c2997c40f --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/image/signature_identity.go @@ -0,0 +1,26 @@ +package image + +// SignatureIdentity contains the properties of verified signatures for the image. +type SignatureIdentity struct { + // Name is a textual description summarizing the type of signature. + Name string `json:"Name,omitempty"` + // Timestamps contains a list of verified signed timestamps for the signature. + Timestamps []SignatureTimestamp `json:"Timestamps,omitzero"` + // KnownSigner is an identifier for a special signer identity that is known to the implementation. + KnownSigner KnownSignerIdentity `json:"KnownSigner,omitempty"` + // DockerReference is the Docker image reference associated with the signature. + // This is an optional field only present in older hashedrecord signatures. + DockerReference string `json:"DockerReference,omitempty"` + // Signer contains information about the signer certificate used to sign the image. + Signer *SignerIdentity `json:"Signer,omitempty"` + // SignatureType is the type of signature format. E.g. "bundle-v0.3" or "hashedrecord". + SignatureType SignatureType `json:"SignatureType,omitempty"` + + // Error contains error information if signature verification failed. + // Other fields will be empty in this case. + Error string `json:"Error,omitempty"` + // Warnings contains any warnings that occurred during signature verification. + // For example, if there was no internet connectivity and cached trust roots were used. + // Warning does not indicate a failed verification but may point to configuration issues. + Warnings []string `json:"Warnings,omitzero"` +} diff --git a/vendor/github.com/moby/moby/api/types/image/signature_timestamp.go b/vendor/github.com/moby/moby/api/types/image/signature_timestamp.go new file mode 100644 index 000000000000..a975ef0eeaa5 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/image/signature_timestamp.go @@ -0,0 +1,12 @@ +package image + +import ( + "time" +) + +// SignatureTimestamp contains information about a verified signed timestamp for an image signature. +type SignatureTimestamp struct { + Type SignatureTimestampType `json:"Type"` + URI string `json:"URI"` + Timestamp time.Time `json:"Timestamp"` +} diff --git a/vendor/github.com/moby/moby/api/types/image/signer_identity.go b/vendor/github.com/moby/moby/api/types/image/signer_identity.go new file mode 100644 index 000000000000..87419e1484d3 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/image/signer_identity.go @@ -0,0 +1,57 @@ +package image + +// SignerIdentity contains information about the signer certificate used to sign the image. +// This is [certificate.Summary] with deprecated fields removed and keys in Moby uppercase style. +// +// [certificate.Summary]: https://pkg.go.dev/github.com/sigstore/sigstore-go/pkg/fulcio/certificate#Summary +type SignerIdentity struct { + CertificateIssuer string `json:"CertificateIssuer"` + SubjectAlternativeName string `json:"SubjectAlternativeName"` + // The OIDC issuer. Should match `iss` claim of ID token or, in the case of + // a federated login like Dex it should match the issuer URL of the + // upstream issuer. The issuer is not set the extensions are invalid and + // will fail to render. + Issuer string `json:"Issuer,omitempty"` // OID 1.3.6.1.4.1.57264.1.8 and 1.3.6.1.4.1.57264.1.1 (Deprecated) + + // Reference to specific build instructions that are responsible for signing. + BuildSignerURI string `json:"BuildSignerURI,omitempty"` // 1.3.6.1.4.1.57264.1.9 + + // Immutable reference to the specific version of the build instructions that is responsible for signing. + BuildSignerDigest string `json:"BuildSignerDigest,omitempty"` // 1.3.6.1.4.1.57264.1.10 + + // Specifies whether the build took place in platform-hosted cloud infrastructure or customer/self-hosted infrastructure. + RunnerEnvironment string `json:"RunnerEnvironment,omitempty"` // 1.3.6.1.4.1.57264.1.11 + + // Source repository URL that the build was based on. + SourceRepositoryURI string `json:"SourceRepositoryURI,omitempty"` // 1.3.6.1.4.1.57264.1.12 + + // Immutable reference to a specific version of the source code that the build was based upon. + SourceRepositoryDigest string `json:"SourceRepositoryDigest,omitempty"` // 1.3.6.1.4.1.57264.1.13 + + // Source Repository Ref that the build run was based upon. + SourceRepositoryRef string `json:"SourceRepositoryRef,omitempty"` // 1.3.6.1.4.1.57264.1.14 + + // Immutable identifier for the source repository the workflow was based upon. + SourceRepositoryIdentifier string `json:"SourceRepositoryIdentifier,omitempty"` // 1.3.6.1.4.1.57264.1.15 + + // Source repository owner URL of the owner of the source repository that the build was based on. + SourceRepositoryOwnerURI string `json:"SourceRepositoryOwnerURI,omitempty"` // 1.3.6.1.4.1.57264.1.16 + + // Immutable identifier for the owner of the source repository that the workflow was based upon. + SourceRepositoryOwnerIdentifier string `json:"SourceRepositoryOwnerIdentifier,omitempty"` // 1.3.6.1.4.1.57264.1.17 + + // Build Config URL to the top-level/initiating build instructions. + BuildConfigURI string `json:"BuildConfigURI,omitempty"` // 1.3.6.1.4.1.57264.1.18 + + // Immutable reference to the specific version of the top-level/initiating build instructions. + BuildConfigDigest string `json:"BuildConfigDigest,omitempty"` // 1.3.6.1.4.1.57264.1.19 + + // Event or action that initiated the build. + BuildTrigger string `json:"BuildTrigger,omitempty"` // 1.3.6.1.4.1.57264.1.20 + + // Run Invocation URL to uniquely identify the build execution. + RunInvocationURI string `json:"RunInvocationURI,omitempty"` // 1.3.6.1.4.1.57264.1.21 + + // Source repository visibility at the time of signing the certificate. + SourceRepositoryVisibilityAtSigning string `json:"SourceRepositoryVisibilityAtSigning,omitempty"` // 1.3.6.1.4.1.57264.1.22 +} diff --git a/vendor/github.com/moby/moby/api/types/jsonstream/message.go b/vendor/github.com/moby/moby/api/types/jsonstream/message.go index 2e1346d41903..6b74bd932706 100644 --- a/vendor/github.com/moby/moby/api/types/jsonstream/message.go +++ b/vendor/github.com/moby/moby/api/types/jsonstream/message.go @@ -2,9 +2,9 @@ package jsonstream import "encoding/json" -// JSONMessage defines a message struct. It describes +// Message defines a message struct. It describes // the created time, where it from, status, ID of the -// message. It's used for docker events. +// message. type Message struct { Stream string `json:"stream,omitempty"` Status string `json:"status,omitempty"` diff --git a/vendor/github.com/moby/moby/api/types/network/endpoint.go b/vendor/github.com/moby/moby/api/types/network/endpoint.go index cd39c579f081..c4c1766cf297 100644 --- a/vendor/github.com/moby/moby/api/types/network/endpoint.go +++ b/vendor/github.com/moby/moby/api/types/network/endpoint.go @@ -58,9 +58,9 @@ func (es *EndpointSettings) Copy() *EndpointSettings { // EndpointIPAMConfig represents IPAM configurations for the endpoint type EndpointIPAMConfig struct { - IPv4Address netip.Addr `json:",omitempty"` - IPv6Address netip.Addr `json:",omitempty"` - LinkLocalIPs []netip.Addr `json:",omitempty"` + IPv4Address netip.Addr `json:"IPv4Address,omitzero"` + IPv6Address netip.Addr `json:"IPv6Address,omitzero"` + LinkLocalIPs []netip.Addr `json:"LinkLocalIPs,omitempty"` } // Copy makes a copy of the endpoint ipam config diff --git a/vendor/github.com/moby/moby/api/types/network/hwaddr.go b/vendor/github.com/moby/moby/api/types/network/hwaddr.go index 9d0c2b4b6221..b2a4dfb1a129 100644 --- a/vendor/github.com/moby/moby/api/types/network/hwaddr.go +++ b/vendor/github.com/moby/moby/api/types/network/hwaddr.go @@ -11,9 +11,11 @@ import ( // in the absence of go.dev/issue/29678. type HardwareAddr net.HardwareAddr -var _ encoding.TextMarshaler = (HardwareAddr)(nil) -var _ encoding.TextUnmarshaler = (*HardwareAddr)(nil) -var _ fmt.Stringer = (HardwareAddr)(nil) +var ( + _ encoding.TextMarshaler = (HardwareAddr)(nil) + _ encoding.TextUnmarshaler = (*HardwareAddr)(nil) + _ fmt.Stringer = (HardwareAddr)(nil) +) func (m *HardwareAddr) UnmarshalText(text []byte) error { if len(text) == 0 { diff --git a/vendor/github.com/moby/moby/api/types/network/ipam.go b/vendor/github.com/moby/moby/api/types/network/ipam.go index e57be481b72c..3fb357fc6c4f 100644 --- a/vendor/github.com/moby/moby/api/types/network/ipam.go +++ b/vendor/github.com/moby/moby/api/types/network/ipam.go @@ -13,9 +13,9 @@ type IPAM struct { // IPAMConfig represents IPAM configurations type IPAMConfig struct { - Subnet netip.Prefix `json:",omitempty"` - IPRange netip.Prefix `json:",omitempty"` - Gateway netip.Addr `json:",omitempty"` + Subnet netip.Prefix `json:"Subnet,omitzero"` + IPRange netip.Prefix `json:"IPRange,omitzero"` + Gateway netip.Addr `json:"Gateway,omitzero"` AuxAddress map[string]netip.Addr `json:"AuxiliaryAddresses,omitempty"` } diff --git a/vendor/github.com/moby/moby/api/types/swarm/network.go b/vendor/github.com/moby/moby/api/types/swarm/network.go index 65aabc9d3689..a70ac0690e2d 100644 --- a/vendor/github.com/moby/moby/api/types/swarm/network.go +++ b/vendor/github.com/moby/moby/api/types/swarm/network.go @@ -61,7 +61,7 @@ type EndpointVirtualIP struct { // Addr is the virtual ip address. // This field accepts CIDR notation, for example `10.0.0.1/24`, to maintain backwards // compatibility, but only the IP address is used. - Addr netip.Prefix `json:",omitempty"` + Addr netip.Prefix `json:"Addr,omitzero"` } // Network represents a network. @@ -111,7 +111,7 @@ type IPAMOptions struct { // IPAMConfig represents ipam configuration. type IPAMConfig struct { - Subnet netip.Prefix `json:",omitempty"` - Range netip.Prefix `json:",omitempty"` - Gateway netip.Addr `json:",omitempty"` + Subnet netip.Prefix `json:"Subnet,omitzero"` + Range netip.Prefix `json:"Range,omitzero"` + Gateway netip.Addr `json:"Gateway,omitzero"` } diff --git a/vendor/github.com/moby/moby/api/types/swarm/task.go b/vendor/github.com/moby/moby/api/types/swarm/task.go index f61190683c45..e2633037df96 100644 --- a/vendor/github.com/moby/moby/api/types/swarm/task.go +++ b/vendor/github.com/moby/moby/api/types/swarm/task.go @@ -109,14 +109,14 @@ type Limit struct { Pids int64 `json:",omitempty"` } -// GenericResource represents a "user defined" resource which can +// GenericResource represents a "user-defined" resource which can // be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1) type GenericResource struct { NamedResourceSpec *NamedGenericResource `json:",omitempty"` DiscreteResourceSpec *DiscreteGenericResource `json:",omitempty"` } -// NamedGenericResource represents a "user defined" resource which is defined +// NamedGenericResource represents a "user-defined" resource which is defined // as a string. // "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) // Value is used to identify the resource (GPU="UUID-1", FPGA="/dev/sdb5", ...) @@ -125,7 +125,7 @@ type NamedGenericResource struct { Value string `json:",omitempty"` } -// DiscreteGenericResource represents a "user defined" resource which is defined +// DiscreteGenericResource represents a "user-defined" resource which is defined // as an integer // "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) // Value is used to count the resource (SSD=5, HDD=3, ...) @@ -148,7 +148,7 @@ type ResourceRequirements struct { // Tune container memory swappiness (0 to 100) - if not specified, defaults // to the container OS's default - generally 60, or the value predefined in // the image; set to -1 to unset a previously set value - MemorySwappiness *int64 `json:MemorySwappiness,omitzero"` + MemorySwappiness *int64 `json:"MemorySwappiness,omitzero"` } // Placement represents orchestration parameters. diff --git a/vendor/github.com/moby/moby/api/types/system/info.go b/vendor/github.com/moby/moby/api/types/system/info.go index bca0459df24f..20df949e422f 100644 --- a/vendor/github.com/moby/moby/api/types/system/info.go +++ b/vendor/github.com/moby/moby/api/types/system/info.go @@ -74,6 +74,7 @@ type Info struct { FirewallBackend *FirewallInfo `json:"FirewallBackend,omitempty"` CDISpecDirs []string DiscoveredDevices []DeviceInfo `json:",omitempty"` + NRI *NRIInfo `json:",omitempty"` Containerd *ContainerdInfo `json:",omitempty"` @@ -163,3 +164,8 @@ type DeviceInfo struct { // Example: CDI FQDN like "vendor.com/gpu=0", or other driver-specific device ID ID string `json:"ID"` } + +// NRIInfo describes the NRI configuration. +type NRIInfo struct { + Info [][2]string `json:"Info,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/types.go b/vendor/github.com/moby/moby/api/types/types.go index a89befdda43f..bb5aa41a83aa 100644 --- a/vendor/github.com/moby/moby/api/types/types.go +++ b/vendor/github.com/moby/moby/api/types/types.go @@ -10,9 +10,12 @@ const ( // MediaTypeJSON is the MIME-Type for JSON objects. MediaTypeJSON = "application/json" - // MediaTypeNDJSON is the MIME-Type for Newline Delimited JSON objects streams. + // MediaTypeNDJSON is the MIME-Type for Newline Delimited JSON objects streams (https://github.com/ndjson/ndjson-spec). MediaTypeNDJSON = "application/x-ndjson" + // MediaTypeJSONLines is the MIME-Type for JSONLines objects streams (https://jsonlines.org/). + MediaTypeJSONLines = "application/jsonl" + // MediaTypeJSONSequence is the MIME-Type for JSON Text Sequences (RFC7464). MediaTypeJSONSequence = "application/json-seq" ) diff --git a/vendor/github.com/moby/moby/client/LICENSE b/vendor/github.com/moby/moby/client/LICENSE index 6d8d58fb676b..d64569567334 100644 --- a/vendor/github.com/moby/moby/client/LICENSE +++ b/vendor/github.com/moby/moby/client/LICENSE @@ -1,7 +1,7 @@ Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -176,13 +176,24 @@ END OF TERMS AND CONDITIONS - Copyright 2013-2018 Docker, Inc. + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/vendor/github.com/moby/moby/client/client.go b/vendor/github.com/moby/moby/client/client.go index 0567ba11d46d..d8e69953d525 100644 --- a/vendor/github.com/moby/moby/client/client.go +++ b/vendor/github.com/moby/moby/client/client.go @@ -59,6 +59,7 @@ import ( "net/http" "net/url" "path" + "slices" "strings" "sync" "sync/atomic" @@ -106,7 +107,7 @@ const DummyHost = "api.moby.localhost" // overriding the version and disable API-version negotiation. // // This version may be lower than the version of the api library module used. -const MaxAPIVersion = "1.52" +const MaxAPIVersion = "1.53" // MinAPIVersion is the minimum API version supported by the client. API versions // below this version are not considered when performing API-version negotiation. @@ -241,6 +242,13 @@ func New(ops ...Opt) (*Client, error) { c.client.Transport = otelhttp.NewTransport(c.client.Transport, c.traceOpts...) + if len(cfg.responseHooks) > 0 { + c.client.Transport = &responseHookTransport{ + base: c.client.Transport, + hooks: slices.Clone(cfg.responseHooks), + } + } + return c, nil } diff --git a/vendor/github.com/moby/moby/client/client_options.go b/vendor/github.com/moby/moby/client/client_options.go index 295d2991807b..d92a16a455f7 100644 --- a/vendor/github.com/moby/moby/client/client_options.go +++ b/vendor/github.com/moby/moby/client/client_options.go @@ -2,6 +2,7 @@ package client import ( "context" + "errors" "fmt" "net" "net/http" @@ -55,10 +56,19 @@ type clientConfig struct { // takes precedence. Either field disables API-version negotiation. envAPIVersion string + // responseHooks is a list of custom response hooks to call on responses. + responseHooks []ResponseHook + // traceOpts is a list of options to configure the tracing span. traceOpts []otelhttp.Option } +// ResponseHook is called for each HTTP response returned by the daemon. +// Hooks are invoked in the order they were added. +// +// Hooks must not read or close resp.Body. +type ResponseHook func(*http.Response) + // Opt is a configuration option to initialize a [Client]. type Opt func(*clientConfig) error @@ -133,8 +143,6 @@ func (tf testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) return tf(req) } -func (testRoundTripper) skipConfigureTransport() bool { return true } - // WithHostFromEnv overrides the client host with the host specified in the // DOCKER_HOST ([EnvOverrideHost]) environment variable. If DOCKER_HOST is not set, // or set to an empty value, the host is not modified. @@ -151,7 +159,16 @@ func WithHostFromEnv() Opt { func WithHTTPClient(client *http.Client) Opt { return func(c *clientConfig) error { if client != nil { - c.client = client + // Make a clone of client so modifications do not affect + // the caller's client. Clone here instead of in New() + // as other options (WithHost) also mutate c.client. + // Cloned clients share the same CookieJar as the + // original. + hc := *client + if ht, ok := hc.Transport.(*http.Transport); ok { + hc.Transport = ht.Clone() + } + c.client = &hc } return nil } @@ -341,3 +358,18 @@ func WithTraceOptions(opts ...otelhttp.Option) Opt { return nil } } + +// WithResponseHook adds a ResponseHook to the client. ResponseHooks are called +// for each HTTP response returned by the daemon. Hooks are invoked in the order +// they were added. +// +// Hooks must not read or close resp.Body. +func WithResponseHook(h ResponseHook) Opt { + return func(c *clientConfig) error { + if h == nil { + return errors.New("invalid response hook: hook is nil") + } + c.responseHooks = append(c.responseHooks, h) + return nil + } +} diff --git a/vendor/github.com/moby/moby/client/client_responsehook.go b/vendor/github.com/moby/moby/client/client_responsehook.go new file mode 100644 index 000000000000..7c93f111c783 --- /dev/null +++ b/vendor/github.com/moby/moby/client/client_responsehook.go @@ -0,0 +1,23 @@ +package client + +import ( + "net/http" +) + +type responseHookTransport struct { + base http.RoundTripper + hooks []ResponseHook +} + +func (t *responseHookTransport) RoundTrip(req *http.Request) (*http.Response, error) { + resp, err := t.base.RoundTrip(req) + if err != nil { + return resp, err + } + + for _, h := range t.hooks { + h(resp) + } + + return resp, nil +} diff --git a/vendor/github.com/moby/moby/client/container_attach.go b/vendor/github.com/moby/moby/client/container_attach.go index fa3a2efcc998..ce84122d32fc 100644 --- a/vendor/github.com/moby/moby/client/container_attach.go +++ b/vendor/github.com/moby/moby/client/container_attach.go @@ -23,7 +23,7 @@ type ContainerAttachResult struct { // ContainerAttach attaches a connection to a container in the server. // It returns a [HijackedResponse] with the hijacked connection -// and a reader to get output. It's up to the called to close +// and a reader to get output. It's up to the caller to close // the hijacked connection by calling [HijackedResponse.Close]. // // The stream format on the response uses one of two formats: diff --git a/vendor/github.com/moby/moby/client/container_exec.go b/vendor/github.com/moby/moby/client/container_exec.go index 1af6a4e20f98..30ed00ea52d7 100644 --- a/vendor/github.com/moby/moby/client/container_exec.go +++ b/vendor/github.com/moby/moby/client/container_exec.go @@ -5,7 +5,7 @@ import ( "encoding/json" "net/http" - "github.com/containerd/errdefs" + cerrdefs "github.com/containerd/errdefs" "github.com/moby/moby/api/types/container" ) @@ -82,8 +82,7 @@ type ExecStartOptions struct { } // ExecStartResult holds the result of starting a container exec. -type ExecStartResult struct { -} +type ExecStartResult struct{} // ExecStart starts an exec process already created in the docker host. func (cli *Client) ExecStart(ctx context.Context, execID string, options ExecStartOptions) (ExecStartResult, error) { @@ -118,7 +117,7 @@ type ExecAttachResult struct { // ExecAttach attaches a connection to an exec process in the server. // // It returns a [HijackedResponse] with the hijacked connection -// and a reader to get output. It's up to the called to close +// and a reader to get output. It's up to the caller to close // the hijacked connection by calling [HijackedResponse.Close]. // // The stream format on the response uses one of two formats: @@ -152,7 +151,7 @@ func (cli *Client) ExecAttach(ctx context.Context, execID string, options ExecAt func getConsoleSize(hasTTY bool, consoleSize ConsoleSize) (*[2]uint, error) { if consoleSize.Height != 0 || consoleSize.Width != 0 { if !hasTTY { - return nil, errdefs.ErrInvalidArgument.WithMessage("console size is only supported when TTY is enabled") + return nil, cerrdefs.ErrInvalidArgument.WithMessage("console size is only supported when TTY is enabled") } return &[2]uint{consoleSize.Height, consoleSize.Width}, nil } @@ -160,8 +159,7 @@ func getConsoleSize(hasTTY bool, consoleSize ConsoleSize) (*[2]uint, error) { } // ExecInspectOptions holds options for inspecting a container exec. -type ExecInspectOptions struct { -} +type ExecInspectOptions struct{} // ExecInspectResult holds the result of inspecting a container exec. // diff --git a/vendor/github.com/moby/moby/client/container_list.go b/vendor/github.com/moby/moby/client/container_list.go index fcd39c4da592..d9334c544e67 100644 --- a/vendor/github.com/moby/moby/client/container_list.go +++ b/vendor/github.com/moby/moby/client/container_list.go @@ -13,11 +13,23 @@ import ( type ContainerListOptions struct { Size bool All bool - Latest bool - Since string - Before string Limit int Filters Filters + + // Latest is non-functional and should not be used. Use Limit: 1 instead. + // + // Deprecated: the Latest option is non-functional and should not be used. Use Limit: 1 instead. + Latest bool + + // Since is no longer supported. Use the "since" filter instead. + // + // Deprecated: the Since option is no longer supported since docker 1.12 (API 1.24). Use the "since" filter instead. + Since string + + // Before is no longer supported. Use the "since" filter instead. + // + // Deprecated: the Before option is no longer supported since docker 1.12 (API 1.24). Use the "before" filter instead. + Before string } type ContainerListResult struct { @@ -36,14 +48,6 @@ func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptio query.Set("limit", strconv.Itoa(options.Limit)) } - if options.Since != "" { - query.Set("since", options.Since) - } - - if options.Before != "" { - query.Set("before", options.Before) - } - if options.Size { query.Set("size", "1") } diff --git a/vendor/github.com/moby/moby/client/container_rename.go b/vendor/github.com/moby/moby/client/container_rename.go index 7c6d515b393d..4fd28a498647 100644 --- a/vendor/github.com/moby/moby/client/container_rename.go +++ b/vendor/github.com/moby/moby/client/container_rename.go @@ -5,7 +5,7 @@ import ( "net/url" "strings" - "github.com/containerd/errdefs" + cerrdefs "github.com/containerd/errdefs" ) // ContainerRenameOptions represents the options for renaming a container. @@ -28,7 +28,7 @@ func (cli *Client) ContainerRename(ctx context.Context, containerID string, opti if options.NewName == "" || strings.TrimPrefix(options.NewName, "/") == "" { // daemons before v29.0 did not handle the canonical name ("/") well // let's be nice and validate it here before sending - return ContainerRenameResult{}, errdefs.ErrInvalidArgument.WithMessage("new name cannot be blank") + return ContainerRenameResult{}, cerrdefs.ErrInvalidArgument.WithMessage("new name cannot be blank") } query := url.Values{} diff --git a/vendor/github.com/moby/moby/client/container_resize.go b/vendor/github.com/moby/moby/client/container_resize.go index 311a9dcf5a9f..8ce26fb5855e 100644 --- a/vendor/github.com/moby/moby/client/container_resize.go +++ b/vendor/github.com/moby/moby/client/container_resize.go @@ -42,8 +42,7 @@ func (cli *Client) ContainerResize(ctx context.Context, containerID string, opti type ExecResizeOptions ContainerResizeOptions // ExecResizeResult holds the result of resizing a container exec TTY. -type ExecResizeResult struct { -} +type ExecResizeResult struct{} // ExecResize changes the size of the tty for an exec process running inside a container. func (cli *Client) ExecResize(ctx context.Context, execID string, options ExecResizeOptions) (ExecResizeResult, error) { @@ -62,5 +61,4 @@ func (cli *Client) ExecResize(ctx context.Context, execID string, options ExecRe return ExecResizeResult{}, err } return ExecResizeResult{}, nil - } diff --git a/vendor/github.com/moby/moby/client/internal/json-stream.go b/vendor/github.com/moby/moby/client/internal/json-stream.go index 552978f9a183..07d07bd7e355 100644 --- a/vendor/github.com/moby/moby/client/internal/json-stream.go +++ b/vendor/github.com/moby/moby/client/internal/json-stream.go @@ -17,7 +17,7 @@ func NewJSONStreamDecoder(r io.Reader, contentType string) DecoderFn { switch contentType { case types.MediaTypeJSONSequence: return json.NewDecoder(NewRSFilterReader(r)).Decode - case types.MediaTypeJSON, types.MediaTypeNDJSON: + case types.MediaTypeJSON, types.MediaTypeNDJSON, types.MediaTypeJSONLines: fallthrough default: return json.NewDecoder(r).Decode diff --git a/vendor/github.com/moby/moby/client/internal/jsonmessages.go b/vendor/github.com/moby/moby/client/internal/jsonmessages.go index ebbb5faa39c1..03afc4e80a63 100644 --- a/vendor/github.com/moby/moby/client/internal/jsonmessages.go +++ b/vendor/github.com/moby/moby/client/internal/jsonmessages.go @@ -45,12 +45,15 @@ func (r stream) Close() error { // JSONMessages decodes the response stream as a sequence of JSONMessages. // if stream ends or context is cancelled, the underlying [io.Reader] is closed. func (r stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error] { - context.AfterFunc(ctx, func() { + stop := context.AfterFunc(ctx, func() { _ = r.Close() }) dec := json.NewDecoder(r) return func(yield func(jsonstream.Message, error) bool) { - defer r.Close() + defer func() { + stop() // unregister AfterFunc + r.Close() + }() for { var jm jsonstream.Message err := dec.Decode(&jm) diff --git a/vendor/github.com/moby/moby/client/system_disk_usage.go b/vendor/github.com/moby/moby/client/system_disk_usage.go index 64a369df8f92..1bb2d0d7efa2 100644 --- a/vendor/github.com/moby/moby/client/system_disk_usage.go +++ b/vendor/github.com/moby/moby/client/system_disk_usage.go @@ -276,6 +276,8 @@ func containerDiskUsageFromLegacyAPI(du *legacyDiskUsage) ContainersDiskUsage { case container.StateRunning, container.StatePaused, container.StateRestarting: cdu.ActiveCount++ used += c.SizeRw + case container.StateCreated, container.StateRemoving, container.StateExited, container.StateDead: + // not active } } diff --git a/vendor/github.com/moby/moby/client/system_events.go b/vendor/github.com/moby/moby/client/system_events.go index 748e01208626..b33e02aa3d7a 100644 --- a/vendor/github.com/moby/moby/client/system_events.go +++ b/vendor/github.com/moby/moby/client/system_events.go @@ -46,6 +46,7 @@ func (cli *Client) Events(ctx context.Context, options EventsListOptions) Events headers := http.Header{} headers.Add("Accept", types.MediaTypeJSONSequence) + headers.Add("Accept", types.MediaTypeJSONLines) headers.Add("Accept", types.MediaTypeNDJSON) resp, err := cli.get(ctx, "/events", query, headers) if err != nil { diff --git a/vendor/github.com/moby/moby/client/utils.go b/vendor/github.com/moby/moby/client/utils.go index f2ba4744c4f7..4415e0dc5a62 100644 --- a/vendor/github.com/moby/moby/client/utils.go +++ b/vendor/github.com/moby/moby/client/utils.go @@ -136,14 +136,19 @@ func newCancelReadCloser(ctx context.Context, rc io.ReadCloser) io.ReadCloser { rc: rc, close: sync.OnceValue(rc.Close), } - context.AfterFunc(ctx, func() { _ = crc.Close() }) + crc.stop = context.AfterFunc(ctx, func() { _ = crc.Close() }) return crc } type cancelReadCloser struct { rc io.ReadCloser close func() error + stop func() bool } func (c *cancelReadCloser) Read(p []byte) (int, error) { return c.rc.Read(p) } -func (c *cancelReadCloser) Close() error { return c.close() } + +func (c *cancelReadCloser) Close() error { + c.stop() // unregister AfterFunc + return c.close() +} diff --git a/vendor/github.com/moby/moby/client/volume_prune.go b/vendor/github.com/moby/moby/client/volume_prune.go index 561e328d7367..eec0f482baf1 100644 --- a/vendor/github.com/moby/moby/client/volume_prune.go +++ b/vendor/github.com/moby/moby/client/volume_prune.go @@ -6,7 +6,7 @@ import ( "fmt" "net/url" - "github.com/containerd/errdefs" + cerrdefs "github.com/containerd/errdefs" "github.com/moby/moby/api/types/volume" ) @@ -29,7 +29,7 @@ type VolumePruneResult struct { func (cli *Client) VolumePrune(ctx context.Context, options VolumePruneOptions) (VolumePruneResult, error) { if options.All { if _, ok := options.Filters["all"]; ok { - return VolumePruneResult{}, errdefs.ErrInvalidArgument.WithMessage(`conflicting options: cannot specify both "all" and "all" filter`) + return VolumePruneResult{}, cerrdefs.ErrInvalidArgument.WithMessage(`conflicting options: cannot specify both "all" and "all" filter`) } if options.Filters == nil { options.Filters = Filters{} diff --git a/vendor/modules.txt b/vendor/modules.txt index d1819622ea63..600c4861e76b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -748,7 +748,7 @@ github.com/moby/go-archive/tarheader # github.com/moby/locker v1.0.1 ## explicit; go 1.13 github.com/moby/locker -# github.com/moby/moby/api v1.52.0 +# github.com/moby/moby/api v1.53.0 ## explicit; go 1.24.0 github.com/moby/moby/api/pkg/authconfig github.com/moby/moby/api/pkg/stdcopy @@ -769,7 +769,7 @@ github.com/moby/moby/api/types/storage github.com/moby/moby/api/types/swarm github.com/moby/moby/api/types/system github.com/moby/moby/api/types/volume -# github.com/moby/moby/client v0.2.1 +# github.com/moby/moby/client v0.2.2 ## explicit; go 1.24.0 github.com/moby/moby/client github.com/moby/moby/client/internal From 8bd5bd49835aa8ec42296e242f665981452412f8 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 19 Jan 2026 13:27:48 +0100 Subject: [PATCH 4/5] vendor: github.com/docker/cli v29.2.0 full diff: https://github.com/docker/cli/compare/v29.1.5...v29.2.0 Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- go.sum | 4 +- .../github.com/docker/cli/cli/command/cli.go | 5 +-- .../docker/cli/cli/command/cli_options.go | 12 +++++- .../docker/cli/cli/command/registry.go | 6 +-- .../docker/cli/templates/templates.go | 42 ++++++++++++++++++- vendor/modules.txt | 2 +- 7 files changed, 59 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index dd1b196bc942..74bebcd0e414 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/creack/pty v1.1.24 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/distribution/reference v0.6.0 - github.com/docker/cli v29.1.5+incompatible + github.com/docker/cli v29.2.0+incompatible github.com/docker/cli-docs-tool v0.11.0 github.com/docker/docker v28.5.2+incompatible github.com/docker/go-units v0.5.0 diff --git a/go.sum b/go.sum index 77cfb002f0ec..96dcb5d903a1 100644 --- a/go.sum +++ b/go.sum @@ -175,8 +175,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v29.1.5+incompatible h1:GckbANUt3j+lsnQ6eCcQd70mNSOismSHWt8vk2AX8ao= -github.com/docker/cli v29.1.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM= +github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE= github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= diff --git a/vendor/github.com/docker/cli/cli/command/cli.go b/vendor/github.com/docker/cli/cli/command/cli.go index b1e9453343a1..42d01b95644e 100644 --- a/vendor/github.com/docker/cli/cli/command/cli.go +++ b/vendor/github.com/docker/cli/cli/command/cli.go @@ -60,6 +60,7 @@ type Cli interface { type DockerCli struct { configFile *configfile.ConfigFile options *cliflags.ClientOptions + clientOpts []client.Opt in *streams.In out *streams.Out err *streams.Out @@ -72,7 +73,6 @@ type DockerCli struct { dockerEndpoint docker.Endpoint contextStoreConfig *store.Config initTimeout time.Duration - userAgent string res telemetryResource // baseCtx is the base context used for internal operations. In the future @@ -533,8 +533,7 @@ func (cli *DockerCli) initialize() error { return } if cli.client == nil { - ops := []client.Opt{client.WithUserAgent(cli.userAgent)} - if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile, ops...); cli.initErr != nil { + if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile, cli.clientOpts...); cli.initErr != nil { return } } diff --git a/vendor/github.com/docker/cli/cli/command/cli_options.go b/vendor/github.com/docker/cli/cli/command/cli_options.go index f787956c5b7e..469932f9c399 100644 --- a/vendor/github.com/docker/cli/cli/command/cli_options.go +++ b/vendor/github.com/docker/cli/cli/command/cli_options.go @@ -104,6 +104,16 @@ func WithInitializeClient(makeClient func(*DockerCli) (client.APIClient, error)) } } +// WithAPIClientOptions configures additional [client.Opt] to use when +// initializing the API client. These options have no effect if a custom +// client is set (through [WithAPIClient] or [WithInitializeClient]). +func WithAPIClientOptions(c ...client.Opt) CLIOption { + return func(cli *DockerCli) error { + cli.clientOpts = append(cli.clientOpts, c...) + return nil + } +} + // envOverrideHTTPHeaders is the name of the environment-variable that can be // used to set custom HTTP headers to be sent by the client. This environment // variable is the equivalent to the HttpHeaders field in the configuration @@ -221,7 +231,7 @@ func WithUserAgent(userAgent string) CLIOption { if userAgent == "" { return errors.New("user agent cannot be blank") } - cli.userAgent = userAgent + cli.clientOpts = append(cli.clientOpts, client.WithUserAgent(userAgent)) return nil } } diff --git a/vendor/github.com/docker/cli/cli/command/registry.go b/vendor/github.com/docker/cli/cli/command/registry.go index 87c81c6da5da..452e2d7354cc 100644 --- a/vendor/github.com/docker/cli/cli/command/registry.go +++ b/vendor/github.com/docker/cli/cli/command/registry.go @@ -200,7 +200,7 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin return "", err } - encodedAuth, err := authconfig.Encode(registrytypes.AuthConfig{ + return authconfig.Encode(registrytypes.AuthConfig{ Username: authConfig.Username, Password: authConfig.Password, ServerAddress: authConfig.ServerAddress, @@ -210,10 +210,6 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin IdentityToken: authConfig.IdentityToken, RegistryToken: authConfig.RegistryToken, }) - if err != nil { - return "", err - } - return encodedAuth, nil } // getAuthConfigKey special-cases using the full index address of the official diff --git a/vendor/github.com/docker/cli/templates/templates.go b/vendor/github.com/docker/cli/templates/templates.go index e339050abb3f..1145e82c82ad 100644 --- a/vendor/github.com/docker/cli/templates/templates.go +++ b/vendor/github.com/docker/cli/templates/templates.go @@ -6,6 +6,9 @@ package templates import ( "bytes" "encoding/json" + "fmt" + "reflect" + "sort" "strings" "text/template" ) @@ -15,7 +18,7 @@ import ( var basicFunctions = template.FuncMap{ "json": formatJSON, "split": strings.Split, - "join": strings.Join, + "join": joinElements, "title": strings.Title, //nolint:nolintlint,staticcheck // strings.Title is deprecated, but we only use it for ASCII, so replacing with golang.org/x/text is out of scope "lower": strings.ToLower, "upper": strings.ToUpper, @@ -97,3 +100,40 @@ func formatJSON(v any) string { // Remove the trailing new line added by the encoder return strings.TrimSpace(buf.String()) } + +// joinElements joins a slice of items with the given separator. It uses +// [strings.Join] if it's a slice of strings, otherwise uses [fmt.Sprint] +// to join each item to the output. +func joinElements(elems any, sep string) (string, error) { + if elems == nil { + return "", nil + } + + if ss, ok := elems.([]string); ok { + return strings.Join(ss, sep), nil + } + + switch rv := reflect.ValueOf(elems); rv.Kind() { //nolint:exhaustive // ignore: too many options to make exhaustive + case reflect.Array, reflect.Slice: + var b strings.Builder + for i := range rv.Len() { + if i > 0 { + b.WriteString(sep) + } + _, _ = fmt.Fprint(&b, rv.Index(i).Interface()) + } + return b.String(), nil + + case reflect.Map: + var out []string + for _, k := range rv.MapKeys() { + out = append(out, fmt.Sprint(rv.MapIndex(k).Interface())) + } + // Not ideal, but trying to keep a consistent order + sort.Strings(out) + return strings.Join(out, sep), nil + + default: + return "", fmt.Errorf("expected slice, got %T", elems) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 600c4861e76b..89564a3d2942 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -284,7 +284,7 @@ github.com/digitorus/timestamp # github.com/distribution/reference v0.6.0 ## explicit; go 1.20 github.com/distribution/reference -# github.com/docker/cli v29.1.5+incompatible +# github.com/docker/cli v29.2.0+incompatible ## explicit github.com/docker/cli/cli github.com/docker/cli/cli-plugins/metadata From 79b14eaeab4eea2c3ee463ed0e2c618ba6b08c8c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 2 Feb 2026 19:27:13 +0100 Subject: [PATCH 5/5] vendor: github.com/docker/cli v29.2.1 full diff: https://github.com/docker/cli/compare/v29.2.0...v29.2.1 Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- go.sum | 4 +- vendor/github.com/docker/cli/opts/mount.go | 147 +++++------------- .../github.com/docker/cli/opts/mount_utils.go | 135 ++++++++++++++++ vendor/modules.txt | 2 +- 5 files changed, 181 insertions(+), 109 deletions(-) create mode 100644 vendor/github.com/docker/cli/opts/mount_utils.go diff --git a/go.mod b/go.mod index 74bebcd0e414..10295ca4d6a8 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/creack/pty v1.1.24 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/distribution/reference v0.6.0 - github.com/docker/cli v29.2.0+incompatible + github.com/docker/cli v29.2.1+incompatible github.com/docker/cli-docs-tool v0.11.0 github.com/docker/docker v28.5.2+incompatible github.com/docker/go-units v0.5.0 diff --git a/go.sum b/go.sum index 96dcb5d903a1..83339e4a9b27 100644 --- a/go.sum +++ b/go.sum @@ -175,8 +175,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM= -github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg= +github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE= github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= diff --git a/vendor/github.com/docker/cli/opts/mount.go b/vendor/github.com/docker/cli/opts/mount.go index 0ac252f31187..642f6500ab4c 100644 --- a/vendor/github.com/docker/cli/opts/mount.go +++ b/vendor/github.com/docker/cli/opts/mount.go @@ -22,72 +22,43 @@ type MountOpt struct { // //nolint:gocyclo func (m *MountOpt) Set(value string) error { + value = strings.TrimSpace(value) + if value == "" { + return errors.New("value is empty") + } + csvReader := csv.NewReader(strings.NewReader(value)) fields, err := csvReader.Read() if err != nil { return err } - mount := mounttypes.Mount{} - - volumeOptions := func() *mounttypes.VolumeOptions { - if mount.VolumeOptions == nil { - mount.VolumeOptions = &mounttypes.VolumeOptions{ - Labels: make(map[string]string), - } - } - if mount.VolumeOptions.DriverConfig == nil { - mount.VolumeOptions.DriverConfig = &mounttypes.Driver{} - } - return mount.VolumeOptions - } - - imageOptions := func() *mounttypes.ImageOptions { - if mount.ImageOptions == nil { - mount.ImageOptions = new(mounttypes.ImageOptions) - } - return mount.ImageOptions + mount := mounttypes.Mount{ + Type: mounttypes.TypeVolume, // default to volume mounts } - bindOptions := func() *mounttypes.BindOptions { - if mount.BindOptions == nil { - mount.BindOptions = new(mounttypes.BindOptions) - } - return mount.BindOptions - } - - tmpfsOptions := func() *mounttypes.TmpfsOptions { - if mount.TmpfsOptions == nil { - mount.TmpfsOptions = new(mounttypes.TmpfsOptions) + for _, field := range fields { + key, val, hasValue := strings.Cut(field, "=") + if k := strings.TrimSpace(key); k != key { + return fmt.Errorf("invalid option '%s' in '%s': option should not have whitespace", k, field) } - return mount.TmpfsOptions - } - - setValueOnMap := func(target map[string]string, value string) { - k, v, _ := strings.Cut(value, "=") - if k != "" { - target[k] = v + if hasValue { + v := strings.TrimSpace(val) + if v == "" { + return fmt.Errorf("invalid value for '%s': value is empty", key) + } + if v != val { + return fmt.Errorf("invalid value for '%s' in '%s': value should not have whitespace", key, field) + } } - } - - mount.Type = mounttypes.TypeVolume // default to volume mounts - // Set writable as the default - for _, field := range fields { - key, val, ok := strings.Cut(field, "=") // TODO(thaJeztah): these options should not be case-insensitive. key = strings.ToLower(key) - if !ok { + if !hasValue { switch key { - case "readonly", "ro": - mount.ReadOnly = true - continue - case "volume-nocopy": - volumeOptions().NoCopy = true - continue - case "bind-nonrecursive": - return errors.New("bind-nonrecursive is deprecated, use bind-recursive=disabled instead") + case "readonly", "ro", "volume-nocopy", "bind-nonrecursive": + // boolean values default: return fmt.Errorf("invalid field '%s' must be a key=value pair", field) } @@ -106,14 +77,14 @@ func (m *MountOpt) Set(value string) error { case "target", "dst", "destination": mount.Target = val case "readonly", "ro": - mount.ReadOnly, err = strconv.ParseBool(val) + mount.ReadOnly, err = parseBoolValue(key, val, hasValue) if err != nil { - return fmt.Errorf("invalid value for %s: %s", key, val) + return err } case "consistency": mount.Consistency = mounttypes.Consistency(strings.ToLower(val)) case "bind-propagation": - bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(val)) + ensureBindOptions(&mount).Propagation = mounttypes.Propagation(strings.ToLower(val)) case "bind-nonrecursive": return errors.New("bind-nonrecursive is deprecated, use bind-recursive=disabled instead") case "bind-recursive": @@ -121,86 +92,52 @@ func (m *MountOpt) Set(value string) error { case "enabled": // read-only mounts are recursively read-only if Engine >= v25 && kernel >= v5.12, otherwise writable // NOP case "disabled": // previously "bind-nonrecursive=true" - bindOptions().NonRecursive = true + ensureBindOptions(&mount).NonRecursive = true case "writable": // conforms to the default read-only bind-mount of Docker v24; read-only mounts are recursively mounted but not recursively read-only - bindOptions().ReadOnlyNonRecursive = true + ensureBindOptions(&mount).ReadOnlyNonRecursive = true case "readonly": // force recursively read-only, or raise an error - bindOptions().ReadOnlyForceRecursive = true + ensureBindOptions(&mount).ReadOnlyForceRecursive = true // TODO: implicitly set propagation and error if the user specifies a propagation in a future refactor/UX polish pass // https://github.com/docker/cli/pull/4316#discussion_r1341974730 default: return fmt.Errorf(`invalid value for %s: %s (must be "enabled", "disabled", "writable", or "readonly")`, key, val) } case "volume-subpath": - volumeOptions().Subpath = val + ensureVolumeOptions(&mount).Subpath = val case "volume-nocopy": - volumeOptions().NoCopy, err = strconv.ParseBool(val) + ensureVolumeOptions(&mount).NoCopy, err = parseBoolValue(key, val, hasValue) if err != nil { - return fmt.Errorf("invalid value for volume-nocopy: %s", val) + return err } case "volume-label": - setValueOnMap(volumeOptions().Labels, val) + volumeOpts := ensureVolumeOptions(&mount) + volumeOpts.Labels = setValueOnMap(volumeOpts.Labels, val) case "volume-driver": - volumeOptions().DriverConfig.Name = val + ensureVolumeDriver(&mount).Name = val case "volume-opt": - if volumeOptions().DriverConfig.Options == nil { - volumeOptions().DriverConfig.Options = make(map[string]string) - } - setValueOnMap(volumeOptions().DriverConfig.Options, val) + volumeDriver := ensureVolumeDriver(&mount) + volumeDriver.Options = setValueOnMap(volumeDriver.Options, val) case "image-subpath": - imageOptions().Subpath = val + ensureImageOptions(&mount).Subpath = val case "tmpfs-size": sizeBytes, err := units.RAMInBytes(val) if err != nil { return fmt.Errorf("invalid value for %s: %s", key, val) } - tmpfsOptions().SizeBytes = sizeBytes + ensureTmpfsOptions(&mount).SizeBytes = sizeBytes case "tmpfs-mode": ui64, err := strconv.ParseUint(val, 8, 32) if err != nil { return fmt.Errorf("invalid value for %s: %s", key, val) } - tmpfsOptions().Mode = os.FileMode(ui64) + ensureTmpfsOptions(&mount).Mode = os.FileMode(ui64) default: - return fmt.Errorf("unexpected key '%s' in '%s'", key, field) + return fmt.Errorf("unknown option '%s' in '%s'", key, field) } } - if mount.Type == "" { - return errors.New("type is required") - } - - if mount.Target == "" { - return errors.New("target is required") - } - - if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume { - return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type) - } - if mount.ImageOptions != nil && mount.Type != mounttypes.TypeImage { - return fmt.Errorf("cannot mix 'image-*' options with mount type '%s'", mount.Type) - } - if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind { - return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type) - } - if mount.TmpfsOptions != nil && mount.Type != mounttypes.TypeTmpfs { - return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", mount.Type) - } - - if mount.BindOptions != nil { - if mount.BindOptions.ReadOnlyNonRecursive { - if !mount.ReadOnly { - return errors.New("option 'bind-recursive=writable' requires 'readonly' to be specified in conjunction") - } - } - if mount.BindOptions.ReadOnlyForceRecursive { - if !mount.ReadOnly { - return errors.New("option 'bind-recursive=readonly' requires 'readonly' to be specified in conjunction") - } - if mount.BindOptions.Propagation != mounttypes.PropagationRPrivate { - return errors.New("option 'bind-recursive=readonly' requires 'bind-propagation=rprivate' to be specified in conjunction") - } - } + if err := validateMountOptions(&mount); err != nil { + return err } m.values = append(m.values, mount) diff --git a/vendor/github.com/docker/cli/opts/mount_utils.go b/vendor/github.com/docker/cli/opts/mount_utils.go new file mode 100644 index 000000000000..974f54dc0405 --- /dev/null +++ b/vendor/github.com/docker/cli/opts/mount_utils.go @@ -0,0 +1,135 @@ +package opts + +import ( + "errors" + "fmt" + "strings" + + "github.com/moby/moby/api/types/mount" +) + +// validateMountOptions performs client-side validation of mount options. Similar +// validation happens on the daemon side, but this validation allows us to +// produce user-friendly errors matching command-line options. +func validateMountOptions(m *mount.Mount) error { + if err := validateExclusiveOptions(m); err != nil { + return err + } + + if m.BindOptions != nil { + if m.BindOptions.ReadOnlyNonRecursive && !m.ReadOnly { + return errors.New("option 'bind-recursive=writable' requires 'readonly' to be specified in conjunction") + } + if m.BindOptions.ReadOnlyForceRecursive { + if !m.ReadOnly { + return errors.New("option 'bind-recursive=readonly' requires 'readonly' to be specified in conjunction") + } + if m.BindOptions.Propagation != mount.PropagationRPrivate { + // FIXME(thaJeztah): this is missing daemon-side validation + // + // docker run --rm --mount type=bind,src=/var/run,target=/foo,bind-recursive=readonly,readonly alpine + // # no error + return errors.New("option 'bind-recursive=readonly' requires 'bind-propagation=rprivate' to be specified in conjunction") + } + } + } + + return nil +} + +// validateExclusiveOptions checks if the given mount config only contains +// options for the given mount-type. +// +// This is the client-side equivalent of [mounts.validateExclusiveOptions] in +// the daemon, but with error-messages matching client-side flags / options. +// +// [mounts.validateExclusiveOptions]: https://github.com/moby/moby/blob/v2.0.0-beta.6/daemon/volume/mounts/validate.go#L31-L50 +func validateExclusiveOptions(m *mount.Mount) error { + if m.Type == "" { + return errors.New("type is required") + } + + if m.Type != mount.TypeBind && m.BindOptions != nil { + return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", m.Type) + } + if m.Type != mount.TypeVolume && m.VolumeOptions != nil { + return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", m.Type) + } + if m.Type != mount.TypeImage && m.ImageOptions != nil { + return fmt.Errorf("cannot mix 'image-*' options with mount type '%s'", m.Type) + } + if m.Type != mount.TypeTmpfs && m.TmpfsOptions != nil { + return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", m.Type) + } + if m.Type != mount.TypeCluster && m.ClusterOptions != nil { + return fmt.Errorf("cannot mix 'cluster-*' options with mount type '%s'", m.Type) + } + return nil +} + +// parseBoolValue returns the boolean value represented by the string. It returns +// true if no value is set. +// +// It is similar to [strconv.ParseBool], but only accepts 1, true, 0, false. +// Any other value returns an error. +func parseBoolValue(key string, val string, hasValue bool) (bool, error) { + if !hasValue { + return true, nil + } + switch val { + case "1", "true": + return true, nil + case "0", "false": + return false, nil + default: + return false, fmt.Errorf(`invalid value for '%s': invalid boolean value (%q): must be one of "true", "1", "false", or "0" (default "true")`, key, val) + } +} + +func ensureVolumeOptions(m *mount.Mount) *mount.VolumeOptions { + if m.VolumeOptions == nil { + m.VolumeOptions = &mount.VolumeOptions{} + } + return m.VolumeOptions +} + +func ensureVolumeDriver(m *mount.Mount) *mount.Driver { + ensureVolumeOptions(m) + if m.VolumeOptions.DriverConfig == nil { + m.VolumeOptions.DriverConfig = &mount.Driver{} + } + return m.VolumeOptions.DriverConfig +} + +func ensureImageOptions(m *mount.Mount) *mount.ImageOptions { + if m.ImageOptions == nil { + m.ImageOptions = &mount.ImageOptions{} + } + return m.ImageOptions +} + +func ensureBindOptions(m *mount.Mount) *mount.BindOptions { + if m.BindOptions == nil { + m.BindOptions = &mount.BindOptions{} + } + return m.BindOptions +} + +func ensureTmpfsOptions(m *mount.Mount) *mount.TmpfsOptions { + if m.TmpfsOptions == nil { + m.TmpfsOptions = &mount.TmpfsOptions{} + } + return m.TmpfsOptions +} + +func setValueOnMap(target map[string]string, keyValue string) map[string]string { + k, v, _ := strings.Cut(keyValue, "=") + if k == "" { + return target + } + if target == nil { + target = map[string]string{} + } + target[k] = v + return target +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 89564a3d2942..79b8ec41d1d8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -284,7 +284,7 @@ github.com/digitorus/timestamp # github.com/distribution/reference v0.6.0 ## explicit; go 1.20 github.com/distribution/reference -# github.com/docker/cli v29.2.0+incompatible +# github.com/docker/cli v29.2.1+incompatible ## explicit github.com/docker/cli/cli github.com/docker/cli/cli-plugins/metadata