Merge branch 'main' into dependabot/docker/dot-github/actions/retest-action/alpine-3.19

This commit is contained in:
Tomofumi Hayashi 2024-03-12 02:27:00 +09:00 committed by GitHub
commit c8d165df6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
391 changed files with 13139 additions and 33338 deletions

106
.github/workflows/release.yaml vendored Normal file
View File

@ -0,0 +1,106 @@
---
name: Release binaries
on:
push:
tags:
- 'v*'
jobs:
linux_release:
name: Release linux binaries
runs-on: ubuntu-latest
strategy:
matrix:
goarch: [amd64, arm, arm64, mips64le, ppc64le, riscv64, s390x]
steps:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21
- name: Checkout code
uses: actions/checkout@v4
- name: Build
env:
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: ./build_linux.sh
- name: COPY files
run: cp README.md LICENSE bin/
- name: Create dist directory
run: mkdir dist
- name: Create archive file
working-directory: ./bin
run: tar cfzv ../dist/cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz .
- name: Create sha256 checksum
working-directory: ./dist
run: sha256sum cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha256
- name: Create sha512 checksum
working-directory: ./dist
run: sha512sum cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha512
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./dist/*
tag: ${{ github.ref }}
overwrite: true
file_glob: true
windows_releases:
name: Release windows binaries
runs-on: ubuntu-latest
strategy:
matrix:
goarch: [amd64]
steps:
- name: Install dos2unix
run: sudo apt-get install dos2unix
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21
- name: Checkout code
uses: actions/checkout@v4
- name: Build
env:
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: ./build_windows.sh
- name: COPY files
run: cp README.md LICENSE bin/
- name: Create dist directory
run: mkdir dist
- name: Create archive file
working-directory: ./bin
run: tar cfzv ../dist/cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz .
- name: Create sha256 checksum
working-directory: ./dist
run: sha256sum cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha256
- name: Create sha512 checksum
working-directory: ./dist
run: sha512sum cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha512
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./dist/*
tag: ${{ github.ref }}
overwrite: true
file_glob: true

View File

@ -4,7 +4,7 @@ name: test
on: ["push", "pull_request"] on: ["push", "pull_request"]
env: env:
GO_VERSION: "1.21" GO_VERSION: "1.22"
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le riscv64" LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le riscv64"
jobs: jobs:
@ -14,14 +14,15 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: setup go - name: setup go
uses: actions/setup-go@v4 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
- uses: ibiqlik/action-yamllint@v3 - uses: ibiqlik/action-yamllint@v3
with: with:
format: auto format: auto
- uses: golangci/golangci-lint-action@v3 - uses: golangci/golangci-lint-action@v4
with: with:
version: v1.55.2
args: -v args: -v
skip-cache: true skip-cache: true
build: build:
@ -31,7 +32,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: setup go - name: setup go
uses: actions/setup-go@v4 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
- name: Build on all supported architectures - name: Build on all supported architectures
@ -55,7 +56,7 @@ jobs:
run: sudo apt-get install nftables run: sudo apt-get install nftables
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: setup go - name: setup go
uses: actions/setup-go@v4 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
- name: Set up Go for root - name: Set up Go for root
@ -86,7 +87,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: setup go - name: setup go
uses: actions/setup-go@v4 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
- name: test - name: test

34
go.mod
View File

@ -3,8 +3,8 @@ module github.com/containernetworking/plugins
go 1.20 go 1.20
require ( require (
github.com/Microsoft/hcsshim v0.11.4 github.com/Microsoft/hcsshim v0.12.0
github.com/alexflint/go-filemutex v1.2.0 github.com/alexflint/go-filemutex v1.3.0
github.com/buger/jsonparser v1.1.1 github.com/buger/jsonparser v1.1.1
github.com/containernetworking/cni v1.1.2 github.com/containernetworking/cni v1.1.2
github.com/coreos/go-iptables v0.7.0 github.com/coreos/go-iptables v0.7.0
@ -15,36 +15,36 @@ require (
github.com/godbus/dbus/v5 v5.1.0 github.com/godbus/dbus/v5 v5.1.0
github.com/mattn/go-shellwords v1.0.12 github.com/mattn/go-shellwords v1.0.12
github.com/networkplumbing/go-nft v0.4.0 github.com/networkplumbing/go-nft v0.4.0
github.com/onsi/ginkgo/v2 v2.13.2 github.com/onsi/ginkgo/v2 v2.16.0
github.com/onsi/gomega v1.30.0 github.com/onsi/gomega v1.31.1
github.com/opencontainers/selinux v1.11.0 github.com/opencontainers/selinux v1.11.0
github.com/safchain/ethtool v0.3.0 github.com/safchain/ethtool v0.3.0
github.com/vishvananda/netlink v1.2.1-beta.2 github.com/vishvananda/netlink v1.2.1-beta.2
golang.org/x/sys v0.15.0 golang.org/x/sys v0.17.0
) )
require ( require (
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/cgroups/v3 v3.0.2 // indirect
github.com/containerd/containerd v1.6.23 // indirect github.com/containerd/errdefs v0.1.0 // indirect
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect
github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/logr v1.4.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/vishvananda/netns v0.0.4 // indirect github.com/vishvananda/netns v0.0.4 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
golang.org/x/mod v0.13.0 // indirect golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.20.0 // indirect
golang.org/x/text v0.13.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.14.0 // indirect golang.org/x/tools v0.17.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/grpc v1.56.3 // indirect google.golang.org/grpc v1.62.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

81
go.sum
View File

@ -2,10 +2,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= github.com/Microsoft/hcsshim v0.12.0 h1:rbICA+XZFwrBef2Odk++0LjFvClNCJGRK+fsrP254Ts=
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/Microsoft/hcsshim v0.12.0/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g=
github.com/alexflint/go-filemutex v1.2.0 h1:1v0TJPDtlhgpW4nJ+GvxCLSlUDC3+gW0CQQvlmfDR/s= github.com/alexflint/go-filemutex v1.3.0 h1:LgE+nTUWnQCyRKbpoceKZsPQbs84LivvgwUymZXdOcM=
github.com/alexflint/go-filemutex v1.2.0/go.mod h1:mYyQSWvw9Tx2/H2n9qXPb52tTYfE0pZAWcBq5mK025c= github.com/alexflint/go-filemutex v1.3.0/go.mod h1:U0+VA/i30mGBlLCrFPGtTe9y6wGQfNAWPBTekHQ+c8A=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -14,10 +14,10 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
github.com/containerd/containerd v1.6.23 h1:KYJd6UJhKHzwMhiD70iTtSmU+k4565ac22GOTI3AuTA= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
github.com/containerd/containerd v1.6.23/go.mod h1:UrQOiyzrLi3n4aezYJbQH6Il+YzTvnHFbEuO3yfDrM4= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ= github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ=
github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw=
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
@ -41,16 +41,14 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
@ -85,8 +83,6 @@ github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHa
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
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/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/networkplumbing/go-nft v0.4.0 h1:kExVMwXW48DOAukkBwyI16h4uhE5lN9iMvQd52lpTyU= github.com/networkplumbing/go-nft v0.4.0 h1:kExVMwXW48DOAukkBwyI16h4uhE5lN9iMvQd52lpTyU=
@ -97,13 +93,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -113,8 +109,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -124,14 +120,14 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
@ -143,11 +139,10 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -155,22 +150,20 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -188,29 +181,27 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -220,15 +211,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -240,8 +231,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=

View File

@ -60,6 +60,7 @@ type NetConf struct {
PreserveDefaultVlan bool `json:"preserveDefaultVlan"` PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
MacSpoofChk bool `json:"macspoofchk,omitempty"` MacSpoofChk bool `json:"macspoofchk,omitempty"`
EnableDad bool `json:"enabledad,omitempty"` EnableDad bool `json:"enabledad,omitempty"`
DisableContainerInterface bool `json:"disableContainerInterface,omitempty"`
Args struct { Args struct {
Cni BridgeArgs `json:"cni,omitempty"` Cni BridgeArgs `json:"cni,omitempty"`
@ -530,6 +531,10 @@ func cmdAdd(args *skel.CmdArgs) error {
isLayer3 := n.IPAM.Type != "" isLayer3 := n.IPAM.Type != ""
if isLayer3 && n.DisableContainerInterface {
return fmt.Errorf("cannot use IPAM when DisableContainerInterface flag is set")
}
if n.IsDefaultGW { if n.IsDefaultGW {
n.IsGW = true n.IsGW = true
} }
@ -676,12 +681,13 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
} }
} }
} else { } else if !n.DisableContainerInterface {
if err := netns.Do(func(_ ns.NetNS) error { if err := netns.Do(func(_ ns.NetNS) error {
link, err := netlink.LinkByName(args.IfName) link, err := netlink.LinkByName(args.IfName)
if err != nil { if err != nil {
return fmt.Errorf("failed to retrieve link: %v", err) return fmt.Errorf("failed to retrieve link: %v", err)
} }
// If layer 2 we still need to set the container veth to up // If layer 2 we still need to set the container veth to up
if err = netlink.LinkSetUp(link); err != nil { if err = netlink.LinkSetUp(link); err != nil {
return fmt.Errorf("failed to set %q up: %v", args.IfName, err) return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
@ -692,8 +698,12 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
} }
var hostVeth netlink.Link hostVeth, err := netlink.LinkByName(hostInterface.Name)
if err != nil {
return err
}
if !n.DisableContainerInterface {
// check bridge port state // check bridge port state
retries := []int{0, 50, 500, 1000, 1000} retries := []int{0, 50, 500, 1000, 1000}
for idx, sleep := range retries { for idx, sleep := range retries {
@ -711,6 +721,7 @@ func cmdAdd(args *skel.CmdArgs) error {
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState) return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
} }
} }
}
// In certain circumstances, the host-side of the veth may change addrs // In certain circumstances, the host-side of the veth may change addrs
hostInterface.Mac = hostVeth.Attrs().HardwareAddr.String() hostInterface.Mac = hostVeth.Attrs().HardwareAddr.String()
@ -779,7 +790,6 @@ func cmdDel(args *skel.CmdArgs) error {
} }
return err return err
}) })
if err != nil { if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times // if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed. // so don't return an error if the device is already removed.

View File

@ -78,6 +78,8 @@ type testCase struct {
removeDefaultVlan bool removeDefaultVlan bool
ipMasq bool ipMasq bool
macspoofchk bool macspoofchk bool
disableContIface bool
AddErr020 string AddErr020 string
DelErr020 string DelErr020 string
AddErr010 string AddErr010 string
@ -154,6 +156,9 @@ const (
netDefault = `, netDefault = `,
"isDefaultGateway": true` "isDefaultGateway": true`
disableContainerInterface = `,
"disableContainerInterface": true`
ipamStartStr = `, ipamStartStr = `,
"ipam": { "ipam": {
"type": "host-local"` "type": "host-local"`
@ -248,6 +253,10 @@ func (tc testCase) netConfJSON(dataDir string) string {
conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk) conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk)
} }
if tc.disableContIface {
conf += disableContainerInterface
}
if !tc.isLayer2 { if !tc.isLayer2 {
conf += netDefault conf += netDefault
if tc.subnet != "" || tc.ranges != nil { if tc.subnet != "" || tc.ranges != nil {
@ -677,14 +686,16 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME)) Expect(link.Attrs().Name).To(Equal(IFNAME))
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{})) Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
assertContainerInterfaceLinkState(&tc, link)
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs() expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(addrs).To(HaveLen(len(expCIDRsV4))) Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6) addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
// Ignore link local address which may or may not be // Ignore link local address which may or may not be
// ready when we read addresses. // ready when we read addresses.
var foundAddrs int var foundAddrs int
@ -728,6 +739,15 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
return result, nil return result, nil
} }
func assertContainerInterfaceLinkState(tc *testCase, link netlink.Link) {
linkState := int(link.Attrs().OperState)
if tc.disableContIface {
Expect(linkState).ToNot(Equal(netlink.OperUp))
} else {
Expect(linkState).To(Equal(netlink.OperUp))
}
}
func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, _ string) { func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, _ string) {
// Generate network config and command arguments // Generate network config and command arguments
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf) tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
@ -1008,8 +1028,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(addrs).To(HaveLen(len(expCIDRsV4))) Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6) addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
// Ignore link local address which may or may not be // Ignore link local address which may or may not be
// ready when we read addresses. // ready when we read addresses.
var foundAddrs int var foundAddrs int
@ -1053,6 +1074,14 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
return result, nil return result, nil
} }
func assertIPv6Addresses(tc *testCase, addrs []netlink.Addr, expCIDRsV6 []*net.IPNet) {
if tc.disableContIface {
Expect(addrs).To(BeEmpty())
} else {
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
}
}
func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, _ string) { func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, _ string) {
// Generate network config and command arguments // Generate network config and command arguments
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf) tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
@ -2461,6 +2490,36 @@ var _ = Describe("bridge Operations", func() {
return nil return nil
})).To(Succeed()) })).To(Succeed())
}) })
It(fmt.Sprintf("[%s] should fail when both IPAM and DisableContainerInterface are set", ver), func() {
Expect(originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
tc := testCase{
cniVersion: ver,
subnet: "10.1.2.0/24",
disableContIface: true,
}
args := tc.createCmdArgs(targetNS, dataDir)
Expect(cmdAdd(args)).To(HaveOccurred())
return nil
})).To(Succeed())
})
It(fmt.Sprintf("[%s] should set the container veth peer state down", ver), func() {
Expect(originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
tc := testCase{
cniVersion: ver,
disableContIface: true,
isLayer2: true,
AddErr020: "cannot convert: no valid IP addresses",
AddErr010: "cannot convert: no valid IP addresses",
}
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
return nil
})).To(Succeed())
})
} }
It("check vlan id when loading net conf", func() { It("check vlan id when loading net conf", func() {

View File

@ -136,7 +136,6 @@ func cmdAdd(args *skel.CmdArgs) error {
err = netns.Do(func(_ ns.NetNS) error { err = netns.Do(func(_ ns.NetNS) error {
return ipam.ConfigureIface(args.IfName, result) return ipam.ConfigureIface(args.IfName, result)
}) })
if err != nil { if err != nil {
return err return err
} }
@ -165,7 +164,6 @@ func cmdDel(args *skel.CmdArgs) error {
} }
return err return err
}) })
if err != nil { if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times // if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed. // so don't return an error if the device is already removed.

View File

@ -371,7 +371,7 @@ var _ = Describe("dummy Operations", func() {
StdinData: []byte(fmt.Sprintf(confFmt, ver)), StdinData: []byte(fmt.Sprintf(confFmt, ver)),
} }
_ = originalNS.Do(func(netNS ns.NetNS) error { _ = originalNS.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err = testutils.CmdAddWithArgs(args, func() error { _, _, err = testutils.CmdAddWithArgs(args, func() error {

View File

@ -334,7 +334,6 @@ func cmdDel(args *skel.CmdArgs) error {
} }
return nil return nil
}) })
if err != nil { if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times // if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed. // so don't return an error if the device is already removed.

View File

@ -411,7 +411,6 @@ func cmdDel(args *skel.CmdArgs) error {
} }
return nil return nil
}) })
if err != nil { if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times // if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed. // so don't return an error if the device is already removed.

View File

@ -371,7 +371,6 @@ func cmdDel(args *skel.CmdArgs) error {
} }
return nil return nil
}) })
if err != nil { if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times // if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed. // so don't return an error if the device is already removed.

View File

@ -244,7 +244,6 @@ func cmdDel(args *skel.CmdArgs) error {
} }
return err return err
}) })
if err != nil { if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times // if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed. // so don't return an error if the device is already removed.

View File

@ -499,7 +499,7 @@ var _ = Describe("vlan Operations", func() {
StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, 1600, isInContainer, dataDir)), StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, 1600, isInContainer, dataDir)),
} }
_ = originalNS.Do(func(netNS ns.NetNS) error { _ = originalNS.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err = testutils.CmdAddWithArgs(args, func() error { _, _, err = testutils.CmdAddWithArgs(args, func() error {
@ -520,7 +520,7 @@ var _ = Describe("vlan Operations", func() {
StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, -100, isInContainer, dataDir)), StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, -100, isInContainer, dataDir)),
} }
_ = originalNS.Do(func(netNS ns.NetNS) error { _ = originalNS.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err = testutils.CmdAddWithArgs(args, func() error { _, _, err = testutils.CmdAddWithArgs(args, func() error {

View File

@ -165,7 +165,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred(), string(out)) Expect(err).NotTo(HaveOccurred(), string(out))
@ -202,7 +202,7 @@ var _ = Describe("bandwidth test", func() {
return nil return nil
})).To(Succeed()) })).To(Succeed())
Expect(hostNs.Do(func(n ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
ifbLink, err := netlink.LinkByName(hostIfname) ifbLink, err := netlink.LinkByName(hostIfname)
@ -260,7 +260,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) }) _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
@ -271,7 +271,7 @@ var _ = Describe("bandwidth test", func() {
return nil return nil
})).To(Succeed()) })).To(Succeed())
Expect(hostNs.Do(func(n ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
containerIfLink, err := netlink.LinkByName(hostIfname) containerIfLink, err := netlink.LinkByName(hostIfname)
@ -327,7 +327,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) }) _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
@ -338,7 +338,7 @@ var _ = Describe("bandwidth test", func() {
return nil return nil
})).To(Succeed()) })).To(Succeed())
Expect(hostNs.Do(func(n ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
containerIfLink, err := netlink.LinkByName(hostIfname) containerIfLink, err := netlink.LinkByName(hostIfname)
@ -396,7 +396,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
@ -448,7 +448,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred(), string(out)) Expect(err).NotTo(HaveOccurred(), string(out))
@ -485,7 +485,7 @@ var _ = Describe("bandwidth test", func() {
return nil return nil
})).To(Succeed()) })).To(Succeed())
Expect(hostNs.Do(func(n ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
ifbLink, err := netlink.LinkByName(hostIfname) ifbLink, err := netlink.LinkByName(hostIfname)
@ -551,7 +551,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
@ -601,7 +601,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred(), string(out)) Expect(err).NotTo(HaveOccurred(), string(out))
@ -669,7 +669,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred(), string(out)) Expect(err).NotTo(HaveOccurred(), string(out))
@ -706,7 +706,7 @@ var _ = Describe("bandwidth test", func() {
return nil return nil
})).To(Succeed()) })).To(Succeed())
Expect(hostNs.Do(func(n ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
ifbLink, err := netlink.LinkByName(hostIfname) ifbLink, err := netlink.LinkByName(hostIfname)
@ -768,7 +768,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
@ -801,7 +801,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
@ -853,7 +853,7 @@ var _ = Describe("bandwidth test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
Expect(hostNs.Do(func(netNS ns.NetNS) error { Expect(hostNs.Do(func(_ ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })

View File

@ -66,7 +66,6 @@ func withLockAndNetNS(nspath string, toRun func(_ ns.NetNS) error) error {
} }
err = ns.WithNetNSPath(nspath, toRun) err = ns.WithNetNSPath(nspath, toRun)
if err != nil { if err != nil {
return err return err
} }

View File

@ -75,7 +75,6 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
return nil return nil
}) })
if err != nil { if err != nil {
return fmt.Errorf("cmdAdd failed: %v", err) return fmt.Errorf("cmdAdd failed: %v", err)
} }
@ -121,7 +120,6 @@ func cmdDel(args *skel.CmdArgs) error {
} }
return nil return nil
}) })
if err != nil { if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times // if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed. // so don't return an error if the device is already removed.

View File

@ -3,7 +3,7 @@ set -xe
SRC_DIR="${SRC_DIR:-$PWD}" SRC_DIR="${SRC_DIR:-$PWD}"
DOCKER="${DOCKER:-docker}" DOCKER="${DOCKER:-docker}"
GOLANG="${GOLANG:-golang:1.21-alpine}" GOLANG="${GOLANG:-golang:1.22-alpine}"
TAG=$(git describe --tags --dirty) TAG=$(git describe --tags --dirty)
RELEASE_DIR=release-${TAG} RELEASE_DIR=release-${TAG}

View File

@ -20,18 +20,33 @@ linters:
# - typecheck # - typecheck
# - unused # - unused
- errorlint # error wrapping (eg, not using `errors.Is`, using `%s` instead of `%w` in `fmt.Errorf`)
- gofmt # whether code was gofmt-ed - gofmt # whether code was gofmt-ed
- govet # enabled by default, but just to be sure
- nolintlint # ill-formed or insufficient nolint directives - nolintlint # ill-formed or insufficient nolint directives
- stylecheck # golint replacement - stylecheck # golint replacement
- thelper # test helpers without t.Helper() - thelper # test helpers without t.Helper()
linters-settings: linters-settings:
govet:
enable-all: true
disable:
# struct order is often for Win32 compat
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
- fieldalignment
check-shadowing: true
stylecheck: stylecheck:
# https://staticcheck.io/docs/checks # https://staticcheck.io/docs/checks
checks: ["all"] checks: ["all"]
issues: issues:
exclude-rules: exclude-rules:
# err is very often shadowed in nested scopes
- linters:
- govet
text: '^shadow: declaration of "err" shadows declaration'
# path is relative to module root, which is ./test/ # path is relative to module root, which is ./test/
- path: cri-containerd - path: cri-containerd
linters: linters:
@ -39,6 +54,12 @@ issues:
text: "^ST1003: should not use underscores in package names$" text: "^ST1003: should not use underscores in package names$"
source: "^package cri_containerd$" source: "^package cri_containerd$"
# don't bother with propper error wrapping in test code
- path: cri-containerd
linters:
- errorlint
text: "non-wrapping format verb for fmt.Errorf"
# This repo has a LOT of generated schema files, operating system bindings, and other # This repo has a LOT of generated schema files, operating system bindings, and other
# things that ST1003 from stylecheck won't like (screaming case Windows api constants for example). # things that ST1003 from stylecheck won't like (screaming case Windows api constants for example).
# There's also some structs that we *could* change the initialisms to be Go friendly # There's also some structs that we *could* change the initialisms to be Go friendly
@ -146,3 +167,8 @@ issues:
linters: linters:
- staticcheck - staticcheck
text: "^SA1019: .*nodenetsvc[/]?v0" text: "^SA1019: .*nodenetsvc[/]?v0"
- path: internal\\vhdx\\info
linters:
- stylecheck
Text: "ST1003:"

View File

@ -29,12 +29,23 @@ ifeq "$(DEV_BUILD)" "1"
DELTA_TARGET=out/delta-dev.tar.gz DELTA_TARGET=out/delta-dev.tar.gz
endif endif
ifeq "$(SNP_BUILD)" "1"
DELTA_TARGET=out/delta-snp.tar.gz
endif
# The link aliases for gcstools # The link aliases for gcstools
GCS_TOOLS=\ GCS_TOOLS=\
generichook \ generichook \
install-drivers install-drivers
.PHONY: all always rootfs test # Common path prefix.
PATH_PREFIX:=
# These have PATH_PREFIX prepended to obtain the full path in recipies e.g. $(PATH_PREFIX)/$(VMGS_TOOL)
VMGS_TOOL:=
IGVM_TOOL:=
KERNEL_PATH:=
.PHONY: all always rootfs test snp simple
.DEFAULT_GOAL := all .DEFAULT_GOAL := all
@ -49,9 +60,58 @@ test:
rootfs: out/rootfs.vhd rootfs: out/rootfs.vhd
out/rootfs.vhd: out/rootfs.tar.gz bin/cmd/tar2ext4 snp: out/kernelinitrd.vmgs out/rootfs.hash.vhd out/rootfs.vhd out/v2056.vmgs
simple: out/simple.vmgs snp
%.vmgs: %.bin
rm -f $@
# du -BM returns the size of the bin file in M, eg 7M. The sed command replaces the M with *1024*1024 and then bc does the math to convert to bytes
$(PATH_PREFIX)/$(VMGS_TOOL) create --filepath $@ --filesize `du -BM $< | sed "s/M.*/*1024*1024/" | bc`
$(PATH_PREFIX)/$(VMGS_TOOL) write --filepath $@ --datapath $< -i=8
# Simplest debug UVM used to test changes to the linux kernel. No dmverity protection. Boots an initramdisk rather than directly booting a vhd disk.
out/simple.bin: out/initrd.img $(PATH_PREFIX)/$(KERNEL_PATH) boot/startup_simple.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) -o $@ -kernel $(PATH_PREFIX)/$(KERNEL_PATH) -append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 rdinit=/startup_simple.sh" -rdinit out/initrd.img -vtl 0
ROOTFS_DEVICE:=/dev/sda
VERITY_DEVICE:=/dev/sdb
# Debug build for use with uvmtester. UVM with dm-verity protected vhd disk mounted directly via the kernel command line. Ignores corruption in dm-verity protected disk. (Use dmesg to see if dm-verity is ignoring data corruption.)
out/v2056.bin: out/rootfs.vhd out/rootfs.hash.vhd $(PATH_PREFIX)/$(KERNEL_PATH) out/rootfs.hash.datasectors out/rootfs.hash.datablocksize out/rootfs.hash.hashblocksize out/rootfs.hash.datablocks out/rootfs.hash.rootdigest out/rootfs.hash.salt boot/startup_v2056.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) -o $@ -kernel $(PATH_PREFIX)/$(KERNEL_PATH) -append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(VERITY_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) 0 sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt) 1 ignore_corruption\" init=/startup_v2056.sh" -vtl 0
# Full UVM with dm-verity protected vhd disk mounted directly via the kernel command line.
out/kernelinitrd.bin: out/rootfs.vhd out/rootfs.hash.vhd out/rootfs.hash.datasectors out/rootfs.hash.datablocksize out/rootfs.hash.hashblocksize out/rootfs.hash.datablocks out/rootfs.hash.rootdigest out/rootfs.hash.salt $(PATH_PREFIX)/$(KERNEL_PATH) boot/startup.sh
rm -f $@
python3 $(PATH_PREFIX)/$(IGVM_TOOL) -o $@ -kernel $(PATH_PREFIX)/$(KERNEL_PATH) -append "8250_core.nr_uarts=0 panic=-1 debug loglevel=7 root=/dev/dm-0 dm-mod.create=\"dmverity,,,ro,0 $(shell cat out/rootfs.hash.datasectors) verity 1 $(ROOTFS_DEVICE) $(VERITY_DEVICE) $(shell cat out/rootfs.hash.datablocksize) $(shell cat out/rootfs.hash.hashblocksize) $(shell cat out/rootfs.hash.datablocks) 0 sha256 $(shell cat out/rootfs.hash.rootdigest) $(shell cat out/rootfs.hash.salt)\" init=/startup.sh" -vtl 0
# Rule to make a vhd from a file. This is used to create the rootfs.hash.vhd from rootfs.hash.
%.vhd: % bin/cmd/tar2ext4
./bin/cmd/tar2ext4 -only-vhd -i $< -o $@
# Rule to make a vhd from an ext4 file. This is used to create the rootfs.vhd from rootfs.ext4.
%.vhd: %.ext4 bin/cmd/tar2ext4
./bin/cmd/tar2ext4 -only-vhd -i $< -o $@
%.hash %.hash.info %.hash.datablocks %.hash.rootdigest %hash.datablocksize %.hash.datasectors %.hash.hashblocksize: %.ext4 %.hash.salt
veritysetup format --no-superblock --salt $(shell cat out/rootfs.hash.salt) $< $*.hash > $*.hash.info
# Retrieve info required by dm-verity at boot time
# Get the blocksize of rootfs
cat $*.hash.info | awk '/^Root hash:/{ print $$3 }' > $*.hash.rootdigest
cat $*.hash.info | awk '/^Salt:/{ print $$2 }' > $*.hash.salt
cat $*.hash.info | awk '/^Data block size:/{ print $$4 }' > $*.hash.datablocksize
cat $*.hash.info | awk '/^Hash block size:/{ print $$4 }' > $*.hash.hashblocksize
cat $*.hash.info | awk '/^Data blocks:/{ print $$3 }' > $*.hash.datablocks
echo $$(( $$(cat $*.hash.datablocks) * $$(cat $*.hash.datablocksize) / 512 )) > $*.hash.datasectors
out/rootfs.hash.salt:
hexdump -vn32 -e'8/4 "%08X" 1 "\n"' /dev/random > $@
out/rootfs.ext4: out/rootfs.tar.gz bin/cmd/tar2ext4
gzip -f -d ./out/rootfs.tar.gz gzip -f -d ./out/rootfs.tar.gz
bin/cmd/tar2ext4 -vhd -i ./out/rootfs.tar -o $@ ./bin/cmd/tar2ext4 -i ./out/rootfs.tar -o $@
out/rootfs.tar.gz: out/initrd.img out/rootfs.tar.gz: out/initrd.img
rm -rf rootfs-conv rm -rf rootfs-conv
@ -74,6 +134,20 @@ out/delta-dev.tar.gz: out/delta.tar.gz bin/internal/tools/snp-report
tar -zcf $@ -C rootfs-dev . tar -zcf $@ -C rootfs-dev .
rm -rf rootfs-dev rm -rf rootfs-dev
out/delta-snp.tar.gz: out/delta.tar.gz bin/internal/tools/snp-report boot/startup_v2056.sh boot/startup_simple.sh boot/startup.sh
rm -rf rootfs-snp
mkdir rootfs-snp
tar -xzf out/delta.tar.gz -C rootfs-snp
cp boot/startup_v2056.sh rootfs-snp/startup_v2056.sh
cp boot/startup_simple.sh rootfs-snp/startup_simple.sh
cp boot/startup.sh rootfs-snp/startup.sh
cp bin/internal/tools/snp-report rootfs-snp/bin/
chmod a+x rootfs-snp/startup_v2056.sh
chmod a+x rootfs-snp/startup_simple.sh
chmod a+x rootfs-snp/startup.sh
tar -zcf $@ -C rootfs-snp .
rm -rf rootfs-snp
out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths Makefile out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths Makefile
@mkdir -p out @mkdir -p out
rm -rf rootfs rm -rf rootfs
@ -94,7 +168,10 @@ out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/ho
tar -zcf $@ -C rootfs . tar -zcf $@ -C rootfs .
rm -rf rootfs rm -rf rootfs
bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths bin/cmd/tar2ext4 bin/internal/tools/snp-report: out/containerd-shim-runhcs-v1.exe:
GOOS=windows $(GO_BUILD) -o $@ $(SRCROOT)/cmd/containerd-shim-runhcs-v1
bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths bin/cmd/tar2ext4 bin/internal/tools/snp-report bin/cmd/dmverity-vhd:
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
GOOS=linux $(GO_BUILD) -o $@ $(SRCROOT)/$(@:bin/%=%) GOOS=linux $(GO_BUILD) -o $@ $(SRCROOT)/$(@:bin/%=%)

View File

@ -1,52 +1,25 @@
version = "1" version = "2"
generator = "gogoctrd" generators = ["go", "go-grpc"]
plugins = ["grpc", "fieldpath"]
# Control protoc include paths. Below are usually some good defaults, but feel # Control protoc include paths.
# free to try it without them if it works for your project.
[includes] [includes]
# Include paths that will be added before all others. Typically, you want to
# treat the root of the project as an include, but this may not be necessary.
before = ["./protobuf"] before = ["./protobuf"]
# defaults are "/usr/local/include" and "/usr/include", which don't exist on Windows. # defaults are "/usr/local/include" and "/usr/include", which don't exist on Windows.
# override defaults to supress errors about non-existant directories. # override defaults to supress errors about non-existant directories.
after = [] after = []
# Paths that should be treated as include roots in relation to the vendor # This section maps protobuf imports to Go packages.
# directory. These will be calculated with the vendor directory nearest the
# target package.
packages = ["github.com/gogo/protobuf"]
# This section maps protobuf imports to Go packages. These will become
# `-M` directives in the call to the go protobuf generator.
[packages] [packages]
"gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto" # github.com/containerd/cgroups protofiles still list their go path as "github.com/containerd/cgroups/cgroup1/stats"
"google/protobuf/any.proto" = "github.com/gogo/protobuf/types" "github.com/containerd/cgroups/v3/cgroup1/stats/metrics.proto" = "github.com/containerd/cgroups/v3/cgroup1/stats"
"google/protobuf/empty.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/struct.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
"google/protobuf/field_mask.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types"
"google/protobuf/duration.proto" = "github.com/gogo/protobuf/types"
"github/containerd/cgroups/stats/v1/metrics.proto" = "github.com/containerd/cgroups/stats/v1"
[[overrides]] [[overrides]]
prefixes = ["github.com/Microsoft/hcsshim/internal/shimdiag"] prefixes = [
plugins = ["ttrpc"] "github.com/Microsoft/hcsshim/internal/shimdiag",
"github.com/Microsoft/hcsshim/internal/extendedtask",
[[overrides]] "github.com/Microsoft/hcsshim/internal/computeagent",
prefixes = ["github.com/Microsoft/hcsshim/internal/extendedtask"] "github.com/Microsoft/hcsshim/internal/ncproxyttrpc",
plugins = ["ttrpc"] "github.com/Microsoft/hcsshim/internal/vmservice",
]
[[overrides]] generators = ["go", "go-ttrpc"]
prefixes = ["github.com/Microsoft/hcsshim/internal/computeagent"]
plugins = ["ttrpc"]
[[overrides]]
prefixes = ["github.com/Microsoft/hcsshim/internal/ncproxyttrpc"]
plugins = ["ttrpc"]
[[overrides]]
prefixes = ["github.com/Microsoft/hcsshim/internal/vmservice"]
plugins = ["ttrpc"]

View File

@ -9,15 +9,18 @@ It is primarily used in the [Moby](https://github.com/moby/moby) and [Containerd
## Building ## Building
While this repository can be used as a library of sorts to call the HCS apis, there are a couple binaries built out of the repository as well. The main ones being the Linux guest agent, and an implementation of the [runtime v2 containerd shim api](https://github.com/containerd/containerd/blob/master/runtime/v2/README.md). While this repository can be used as a library of sorts to call the HCS apis, there are a couple binaries built out of the repository as well. The main ones being the Linux guest agent, and an implementation of the [runtime v2 containerd shim api](https://github.com/containerd/containerd/blob/master/runtime/v2/README.md).
### Linux Hyper-V Container Guest Agent ### Linux Hyper-V Container Guest Agent
To build the Linux guest agent itself all that's needed is to set your GOOS to "Linux" and build out of ./cmd/gcs. To build the Linux guest agent itself all that's needed is to set your GOOS to "Linux" and build out of ./cmd/gcs.
```powershell ```powershell
C:\> $env:GOOS="linux" C:\> $env:GOOS="linux"
C:\> go build .\cmd\gcs\ C:\> go build .\cmd\gcs\
``` ```
or on a Linux machine or on a Linux machine
```sh ```sh
> go build ./cmd/gcs > go build ./cmd/gcs
``` ```
@ -33,13 +36,15 @@ make all
``` ```
If the build is successful, in the `./out` folder you should see: If the build is successful, in the `./out` folder you should see:
```sh ```sh
> ls ./out/ > ls ./out/
delta.tar.gz initrd.img rootfs.tar.gz delta.tar.gz initrd.img rootfs.tar.gz
``` ```
### Containerd Shim ### Containerd Shim
For info on the Runtime V2 API: https://github.com/containerd/containerd/blob/master/runtime/v2/README.md.
For info on the [Runtime V2 API](https://github.com/containerd/containerd/blob/master/runtime/v2/README.md).
Contrary to the typical Linux architecture of shim -> runc, the runhcs shim is used both to launch and manage the lifetime of containers. Contrary to the typical Linux architecture of shim -> runc, the runhcs shim is used both to launch and manage the lifetime of containers.
@ -48,7 +53,9 @@ C:\> $env:GOOS="windows"
C:\> go build .\cmd\containerd-shim-runhcs-v1 C:\> go build .\cmd\containerd-shim-runhcs-v1
``` ```
Then place the binary in the same directory that Containerd is located at in your environment. A default Containerd configuration file can be generated by running: Then place the binary in the same directory that Containerd is located at in your environment.
A default Containerd configuration file can be generated by running:
```powershell ```powershell
.\containerd.exe config default | Out-File "C:\Program Files\containerd\config.toml" -Encoding ascii .\containerd.exe config default | Out-File "C:\Program Files\containerd\config.toml" -Encoding ascii
``` ```
@ -56,6 +63,7 @@ Then place the binary in the same directory that Containerd is located at in you
This config file will already have the shim set as the default runtime for cri interactions. This config file will already have the shim set as the default runtime for cri interactions.
To trial using the shim out with ctr.exe: To trial using the shim out with ctr.exe:
```powershell ```powershell
C:\> ctr.exe run --runtime io.containerd.runhcs.v1 --rm mcr.microsoft.com/windows/nanoserver:2004 windows-test cmd /c "echo Hello World!" C:\> ctr.exe run --runtime io.containerd.runhcs.v1 --rm mcr.microsoft.com/windows/nanoserver:2004 windows-test cmd /c "echo Hello World!"
``` ```
@ -64,16 +72,69 @@ C:\> ctr.exe run --runtime io.containerd.runhcs.v1 --rm mcr.microsoft.com/window
This project welcomes contributions and suggestions. Most contributions require you to agree to a This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com. the rights to use your contribution. For details, visit [Microsoft CLA](https://cla.microsoft.com).
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA. provided by the bot. You will only need to do this once across all repos using our CLA.
We also require that contributors [sign their commits](https://git-scm.com/docs/git-commit) using `git commit -s` or `git commit --signoff` to We require that contributors sign their commits
certify they either authored the work themselves or otherwise have permission to use it in this project. Please see https://developercertificate.org/ for to certify they either authored the work themselves or otherwise have permission to use it in this project.
more info, as well as to make sure that you can attest to the rules listed. Our CI uses the [DCO Github app](https://github.com/apps/dco) to ensure
that all commits in a given PR are signed-off. We also require that contributors sign their commits using using [`git commit --signoff`][git-commit-s]
to certify they either authored the work themselves or otherwise have permission to use it in this project.
A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s].
Please see [the developer certificate](https://developercertificate.org) for more info,
as well as to make sure that you can attest to the rules listed.
Our CI uses the [DCO Github app](https://github.com/apps/dco) to ensure that all commits in a given PR are signed-off.
### Linting
Code must pass a linting stage, which uses [`golangci-lint`][lint].
Since `./test` is a separate Go module, the linter is run from both the root and the
`test` directories. Additionally, the linter is run with `GOOS` set to both `windows` and
`linux`.
The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run
automatically with VSCode by adding the following to your workspace or folder settings:
```json
"go.lintTool": "golangci-lint",
"go.lintOnSave": "package",
```
Additional editor [integrations options are also available][lint-ide].
Alternatively, `golangci-lint` can be [installed][lint-install] and run locally:
```shell
# use . or specify a path to only lint a package
# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0"
> golangci-lint run
```
To run across the entire repo for both `GOOS=windows` and `linux`:
```powershell
> foreach ( $goos in ('windows', 'linux') ) {
foreach ( $repo in ('.', 'test') ) {
pwsh -Command "cd $repo && go env -w GOOS=$goos && golangci-lint.exe run --verbose"
}
}
```
### Go Generate
The pipeline checks that auto-generated code, via `go generate`, are up to date.
Similar to the [linting stage](#linting), `go generate` is run in both the root and test Go modules.
This can be done via:
```shell
> go generate ./...
> cd test && go generate ./...
```
## Code of Conduct ## Code of Conduct
@ -83,7 +144,7 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
## Dependencies ## Dependencies
This project requires Golang 1.17 or newer to build. This project requires Golang 1.18 or newer to build.
For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements). For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements).
@ -100,3 +161,10 @@ For additional details, see [Report a Computer Security Vulnerability](https://t
--------------- ---------------
Copyright (c) 2018 Microsoft Corp. All rights reserved. Copyright (c) 2018 Microsoft Corp. All rights reserved.
[lint]: https://golangci-lint.run/
[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration
[lint-install]: https://golangci-lint.run/usage/install/#local-installation
[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s
[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff

View File

@ -38,3 +38,31 @@ func AttachLayerStorageFilter(ctx context.Context, layerPath string, layerData L
} }
return nil return nil
} }
// AttachOverlayFilter sets up a filter of the given type on a writable container layer. Currently the only
// supported filter types are WCIFS & UnionFS (defined in internal/hcs/schema2/layer.go)
//
// `volumePath` is volume path at which writable layer is mounted. If the
// path does not end in a `\` the platform will append it automatically.
//
// `layerData` is the parent read-only layer data.
func AttachOverlayFilter(ctx context.Context, volumePath string, layerData LayerData) (err error) {
title := "hcsshim::AttachOverlayFilter"
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(
trace.StringAttribute("volumePath", volumePath),
)
bytes, err := json.Marshal(layerData)
if err != nil {
return err
}
err = hcsAttachOverlayFilter(volumePath, string(bytes))
if err != nil {
return errors.Wrap(err, "failed to attach overlay filter")
}
return nil
}

View File

@ -4,7 +4,9 @@ package computestorage
import ( import (
"context" "context"
"encoding/json"
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/oc"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.opencensus.io/trace" "go.opencensus.io/trace"
@ -26,3 +28,27 @@ func DetachLayerStorageFilter(ctx context.Context, layerPath string) (err error)
} }
return nil return nil
} }
// DetachOverlayFilter detaches the filter on a writable container layer.
//
// `volumePath` is a path to writable container volume.
func DetachOverlayFilter(ctx context.Context, volumePath string, filterType hcsschema.FileSystemFilterType) (err error) {
title := "hcsshim::DetachOverlayFilter"
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(trace.StringAttribute("volumePath", volumePath))
layerData := LayerData{}
layerData.FilterType = filterType
bytes, err := json.Marshal(layerData)
if err != nil {
return err
}
err = hcsDetachOverlayFilter(volumePath, string(bytes))
if err != nil {
return errors.Wrap(err, "failed to detach overlay filter")
}
return nil
}

View File

@ -16,7 +16,9 @@ import (
"github.com/Microsoft/hcsshim/internal/security" "github.com/Microsoft/hcsshim/internal/security"
) )
const defaultVHDXBlockSizeInMB = 1 const (
defaultVHDXBlockSizeInMB = 1
)
// SetupContainerBaseLayer is a helper to setup a containers scratch. It // SetupContainerBaseLayer is a helper to setup a containers scratch. It
// will create and format the vhdx's inside and the size is configurable with the sizeInGB // will create and format the vhdx's inside and the size is configurable with the sizeInGB

View File

@ -11,7 +11,7 @@ import (
//sys hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) = computestorage.HcsImportLayer? //sys hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) = computestorage.HcsImportLayer?
//sys hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) = computestorage.HcsExportLayer? //sys hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) = computestorage.HcsExportLayer?
//sys hcsDestroyLayer(layerPath string) (hr error) = computestorage.HcsDestoryLayer? //sys hcsDestroyLayer(layerPath string) (hr error) = computestorage.HcsDestroyLayer?
//sys hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) = computestorage.HcsSetupBaseOSLayer? //sys hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) = computestorage.HcsSetupBaseOSLayer?
//sys hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) = computestorage.HcsInitializeWritableLayer? //sys hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) = computestorage.HcsInitializeWritableLayer?
//sys hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) = computestorage.HcsAttachLayerStorageFilter? //sys hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) = computestorage.HcsAttachLayerStorageFilter?
@ -19,6 +19,8 @@ import (
//sys hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) = computestorage.HcsFormatWritableLayerVhd? //sys hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) = computestorage.HcsFormatWritableLayerVhd?
//sys hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) = computestorage.HcsGetLayerVhdMountPath? //sys hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) = computestorage.HcsGetLayerVhdMountPath?
//sys hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) = computestorage.HcsSetupBaseOSVolume? //sys hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) = computestorage.HcsSetupBaseOSVolume?
//sys hcsAttachOverlayFilter(volumePath string, layerData string) (hr error) = computestorage.HcsAttachOverlayFilter?
//sys hcsDetachOverlayFilter(volumePath string, layerData string) (hr error) = computestorage.HcsDetachOverlayFilter?
type Version = hcsschema.Version type Version = hcsschema.Version
type Layer = hcsschema.Layer type Layer = hcsschema.Layer
@ -27,6 +29,7 @@ type Layer = hcsschema.Layer
type LayerData struct { type LayerData struct {
SchemaVersion Version `json:"SchemaVersion,omitempty"` SchemaVersion Version `json:"SchemaVersion,omitempty"`
Layers []Layer `json:"Layers,omitempty"` Layers []Layer `json:"Layers,omitempty"`
FilterType hcsschema.FileSystemFilterType `json:"FilterType,omitempty"`
} }
// ExportLayerOptions are the set of options that are used with the `computestorage.HcsExportLayer` syscall. // ExportLayerOptions are the set of options that are used with the `computestorage.HcsExportLayer` syscall.

View File

@ -43,8 +43,10 @@ var (
modcomputestorage = windows.NewLazySystemDLL("computestorage.dll") modcomputestorage = windows.NewLazySystemDLL("computestorage.dll")
procHcsAttachLayerStorageFilter = modcomputestorage.NewProc("HcsAttachLayerStorageFilter") procHcsAttachLayerStorageFilter = modcomputestorage.NewProc("HcsAttachLayerStorageFilter")
procHcsDestoryLayer = modcomputestorage.NewProc("HcsDestoryLayer") procHcsAttachOverlayFilter = modcomputestorage.NewProc("HcsAttachOverlayFilter")
procHcsDestroyLayer = modcomputestorage.NewProc("HcsDestroyLayer")
procHcsDetachLayerStorageFilter = modcomputestorage.NewProc("HcsDetachLayerStorageFilter") procHcsDetachLayerStorageFilter = modcomputestorage.NewProc("HcsDetachLayerStorageFilter")
procHcsDetachOverlayFilter = modcomputestorage.NewProc("HcsDetachOverlayFilter")
procHcsExportLayer = modcomputestorage.NewProc("HcsExportLayer") procHcsExportLayer = modcomputestorage.NewProc("HcsExportLayer")
procHcsFormatWritableLayerVhd = modcomputestorage.NewProc("HcsFormatWritableLayerVhd") procHcsFormatWritableLayerVhd = modcomputestorage.NewProc("HcsFormatWritableLayerVhd")
procHcsGetLayerVhdMountPath = modcomputestorage.NewProc("HcsGetLayerVhdMountPath") procHcsGetLayerVhdMountPath = modcomputestorage.NewProc("HcsGetLayerVhdMountPath")
@ -83,6 +85,35 @@ func _hcsAttachLayerStorageFilter(layerPath *uint16, layerData *uint16) (hr erro
return return
} }
func hcsAttachOverlayFilter(volumePath string, layerData string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(volumePath)
if hr != nil {
return
}
var _p1 *uint16
_p1, hr = syscall.UTF16PtrFromString(layerData)
if hr != nil {
return
}
return _hcsAttachOverlayFilter(_p0, _p1)
}
func _hcsAttachOverlayFilter(volumePath *uint16, layerData *uint16) (hr error) {
hr = procHcsAttachOverlayFilter.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsAttachOverlayFilter.Addr(), 2, uintptr(unsafe.Pointer(volumePath)), uintptr(unsafe.Pointer(layerData)), 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func hcsDestroyLayer(layerPath string) (hr error) { func hcsDestroyLayer(layerPath string) (hr error) {
var _p0 *uint16 var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(layerPath) _p0, hr = syscall.UTF16PtrFromString(layerPath)
@ -93,11 +124,11 @@ func hcsDestroyLayer(layerPath string) (hr error) {
} }
func _hcsDestroyLayer(layerPath *uint16) (hr error) { func _hcsDestroyLayer(layerPath *uint16) (hr error) {
hr = procHcsDestoryLayer.Find() hr = procHcsDestroyLayer.Find()
if hr != nil { if hr != nil {
return return
} }
r0, _, _ := syscall.Syscall(procHcsDestoryLayer.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0) r0, _, _ := syscall.Syscall(procHcsDestroyLayer.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0)
if int32(r0) < 0 { if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 { if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff r0 &= 0xffff
@ -131,6 +162,35 @@ func _hcsDetachLayerStorageFilter(layerPath *uint16) (hr error) {
return return
} }
func hcsDetachOverlayFilter(volumePath string, layerData string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(volumePath)
if hr != nil {
return
}
var _p1 *uint16
_p1, hr = syscall.UTF16PtrFromString(layerData)
if hr != nil {
return
}
return _hcsDetachOverlayFilter(_p0, _p1)
}
func _hcsDetachOverlayFilter(volumePath *uint16, layerData *uint16) (hr error) {
hr = procHcsDetachOverlayFilter.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsDetachOverlayFilter.Addr(), 2, uintptr(unsafe.Pointer(volumePath)), uintptr(unsafe.Pointer(layerData)), 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) { func hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) {
var _p0 *uint16 var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(layerPath) _p0, hr = syscall.UTF16PtrFromString(layerPath)

View File

@ -75,7 +75,7 @@ func init() {
func CreateContainer(id string, c *ContainerConfig) (Container, error) { func CreateContainer(id string, c *ContainerConfig) (Container, error) {
fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON) fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err) return nil, fmt.Errorf("failed to merge additional JSON '%s': %w", createContainerAdditionalJSON, err)
} }
system, err := hcs.CreateComputeSystem(context.Background(), id, fullConfig) system, err := hcs.CreateComputeSystem(context.Background(), id, fullConfig)

View File

@ -115,6 +115,7 @@ func (e *ContainerError) Error() string {
s += " encountered an error during " + e.Operation s += " encountered an error during " + e.Operation
} }
//nolint:errorlint // legacy code
switch e.Err.(type) { switch e.Err.(type) {
case nil: case nil:
break break
@ -145,6 +146,7 @@ func (e *ProcessError) Error() string {
s += " encountered an error during " + e.Operation s += " encountered an error during " + e.Operation
} }
//nolint:errorlint // legacy code
switch e.Err.(type) { switch e.Err.(type) {
case nil: case nil:
break break
@ -166,10 +168,10 @@ func (e *ProcessError) Error() string {
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
// will currently return true when the error is ErrElementNotFound. // will currently return true when the error is ErrElementNotFound.
func IsNotExist(err error) bool { func IsNotExist(err error) bool {
if _, ok := err.(EndpointNotFoundError); ok { if _, ok := err.(EndpointNotFoundError); ok { //nolint:errorlint // legacy code
return true return true
} }
if _, ok := err.(NetworkNotFoundError); ok { if _, ok := err.(NetworkNotFoundError); ok { //nolint:errorlint // legacy code
return true return true
} }
return hcs.IsNotExist(getInnerError(err)) return hcs.IsNotExist(getInnerError(err))
@ -224,6 +226,7 @@ func IsAccessIsDenied(err error) bool {
} }
func getInnerError(err error) error { func getInnerError(err error) error {
//nolint:errorlint // legacy code
switch pe := err.(type) { switch pe := err.(type) {
case nil: case nil:
return nil return nil
@ -236,14 +239,14 @@ func getInnerError(err error) error {
} }
func convertSystemError(err error, c *container) error { func convertSystemError(err error, c *container) error {
if serr, ok := err.(*hcs.SystemError); ok { if serr, ok := err.(*hcs.SystemError); ok { //nolint:errorlint // legacy code
return &ContainerError{Container: c, Operation: serr.Op, Err: serr.Err, Events: serr.Events} return &ContainerError{Container: c, Operation: serr.Op, Err: serr.Err, Events: serr.Events}
} }
return err return err
} }
func convertProcessError(err error, p *process) error { func convertProcessError(err error, p *process) error {
if perr, ok := err.(*hcs.ProcessError); ok { if perr, ok := err.(*hcs.ProcessError); ok { //nolint:errorlint // legacy code
return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events} return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events}
} }
return err return err

View File

@ -312,6 +312,18 @@ func NestedIpSetSupported() error {
return platformDoesNotSupportError("NestedIpSet") return platformDoesNotSupportError("NestedIpSet")
} }
// DisableHostPortSupported returns an error if the HCN version does not support DisableHostPort flag
func DisableHostPortSupported() error {
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.DisableHostPort {
return nil
}
return platformDoesNotSupportError("DisableHostPort")
}
// RequestType are the different operations performed to settings. // RequestType are the different operations performed to settings.
// Used to update the settings of Endpoint/Namespace objects. // Used to update the settings of Endpoint/Namespace objects.
type RequestType string type RequestType string

View File

@ -6,11 +6,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
"github.com/Microsoft/hcsshim/internal/hcs" "github.com/Microsoft/hcsshim/internal/hcs"
"github.com/Microsoft/hcsshim/internal/hcserror" "github.com/Microsoft/hcsshim/internal/hcserror"
"github.com/Microsoft/hcsshim/internal/interop" "github.com/Microsoft/hcsshim/internal/interop"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
) )
var ( var (
@ -63,8 +64,8 @@ func (e *HcnError) Error() string {
} }
func CheckErrorWithCode(err error, code ErrorCode) bool { func CheckErrorWithCode(err error, code ErrorCode) bool {
hcnError, ok := err.(*HcnError) var hcnError *HcnError
if ok { if errors.As(err, &hcnError) {
return hcnError.code == code return hcnError.code == code
} }
return false return false
@ -81,7 +82,7 @@ func IsPortAlreadyExistsError(err error) bool {
func new(hr error, title string, rest string) error { func new(hr error, title string, rest string) error {
err := &HcnError{} err := &HcnError{}
hcsError := hcserror.New(hr, title, rest) hcsError := hcserror.New(hr, title, rest)
err.HcsError = hcsError.(*hcserror.HcsError) err.HcsError = hcsError.(*hcserror.HcsError) //nolint:errorlint
err.code = ErrorCode(hcserror.Win32FromError(hr)) err.code = ErrorCode(hcserror.Win32FromError(hr))
return err return err
} }
@ -97,6 +98,8 @@ type NetworkNotFoundError struct {
NetworkID string NetworkID string
} }
var _ error = NetworkNotFoundError{}
func (e NetworkNotFoundError) Error() string { func (e NetworkNotFoundError) Error() string {
if e.NetworkName != "" { if e.NetworkName != "" {
return fmt.Sprintf("Network name %q not found", e.NetworkName) return fmt.Sprintf("Network name %q not found", e.NetworkName)
@ -110,6 +113,8 @@ type EndpointNotFoundError struct {
EndpointID string EndpointID string
} }
var _ error = EndpointNotFoundError{}
func (e EndpointNotFoundError) Error() string { func (e EndpointNotFoundError) Error() string {
if e.EndpointName != "" { if e.EndpointName != "" {
return fmt.Sprintf("Endpoint name %q not found", e.EndpointName) return fmt.Sprintf("Endpoint name %q not found", e.EndpointName)
@ -122,6 +127,8 @@ type NamespaceNotFoundError struct {
NamespaceID string NamespaceID string
} }
var _ error = NamespaceNotFoundError{}
func (e NamespaceNotFoundError) Error() string { func (e NamespaceNotFoundError) Error() string {
return fmt.Sprintf("Namespace ID %q not found", e.NamespaceID) return fmt.Sprintf("Namespace ID %q not found", e.NamespaceID)
} }
@ -131,6 +138,8 @@ type LoadBalancerNotFoundError struct {
LoadBalancerId string LoadBalancerId string
} }
var _ error = LoadBalancerNotFoundError{}
func (e LoadBalancerNotFoundError) Error() string { func (e LoadBalancerNotFoundError) Error() string {
return fmt.Sprintf("LoadBalancer %q not found", e.LoadBalancerId) return fmt.Sprintf("LoadBalancer %q not found", e.LoadBalancerId)
} }
@ -140,6 +149,8 @@ type RouteNotFoundError struct {
RouteId string RouteId string
} }
var _ error = RouteNotFoundError{}
func (e RouteNotFoundError) Error() string { func (e RouteNotFoundError) Error() string {
return fmt.Sprintf("SDN Route %q not found", e.RouteId) return fmt.Sprintf("SDN Route %q not found", e.RouteId)
} }
@ -147,19 +158,31 @@ func (e RouteNotFoundError) Error() string {
// IsNotFoundError returns a boolean indicating whether the error was caused by // IsNotFoundError returns a boolean indicating whether the error was caused by
// a resource not being found. // a resource not being found.
func IsNotFoundError(err error) bool { func IsNotFoundError(err error) bool {
switch pe := err.(type) { // Calling [errors.As] in a loop over `[]error{NetworkNotFoundError{}, ...}` will not work,
case NetworkNotFoundError: // since the loop variable will be an interface type (ie, `error`) and `errors.As(error, *error)` will
// always succeed.
// Unless golang adds loops over (or arrays of) types, we need to manually call `errors.As` for
// each potential error type.
//
// Also, for T = NetworkNotFoundError and co, the error implementation is for T, not *T
if e := (NetworkNotFoundError{}); errors.As(err, &e) {
return true return true
case EndpointNotFoundError:
return true
case NamespaceNotFoundError:
return true
case LoadBalancerNotFoundError:
return true
case RouteNotFoundError:
return true
case *hcserror.HcsError:
return pe.Err == hcs.ErrElementNotFound
} }
if e := (EndpointNotFoundError{}); errors.As(err, &e) {
return true
}
if e := (NamespaceNotFoundError{}); errors.As(err, &e) {
return true
}
if e := (LoadBalancerNotFoundError{}); errors.As(err, &e) {
return true
}
if e := (RouteNotFoundError{}); errors.As(err, &e) {
return true
}
if e := (&hcserror.HcsError{}); errors.As(err, &e) {
return errors.Is(e.Err, hcs.ErrElementNotFound)
}
return false return false
} }

View File

@ -84,6 +84,9 @@ var (
//HNS 15.0 allows for NestedIpSet support //HNS 15.0 allows for NestedIpSet support
NestedIpSetVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 15, Minor: 0}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}} NestedIpSetVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 15, Minor: 0}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
//HNS 15.1 allows support for DisableHostPort flag.
DisableHostPortVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 15, Minor: 1}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
) )
// GetGlobals returns the global properties of the HCN Service. // GetGlobals returns the global properties of the HCN Service.

View File

@ -4,6 +4,7 @@ package hcn
import ( import (
"encoding/json" "encoding/json"
"errors"
"os" "os"
"syscall" "syscall"
@ -378,7 +379,8 @@ func (namespace *HostComputeNamespace) Sync() error {
shimPath := runhcs.VMPipePath(cfg.HostUniqueID) shimPath := runhcs.VMPipePath(cfg.HostUniqueID)
if err := runhcs.IssueVMRequest(shimPath, &req); err != nil { if err := runhcs.IssueVMRequest(shimPath, &req); err != nil {
// The shim is likely gone. Simply ignore the sync as if it didn't exist. // The shim is likely gone. Simply ignore the sync as if it didn't exist.
if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND { var perr *os.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, syscall.ERROR_FILE_NOT_FOUND) {
// Remove the reg key there is no point to try again // Remove the reg key there is no point to try again
_ = cfg.Remove() _ = cfg.Remove()
return nil return nil

View File

@ -72,6 +72,7 @@ type NetworkFlags uint32
const ( const (
None NetworkFlags = 0 None NetworkFlags = 0
EnableNonPersistent NetworkFlags = 8 EnableNonPersistent NetworkFlags = 8
DisableHostPort NetworkFlags = 1024
) )
// HostComputeNetwork represents a network // HostComputeNetwork represents a network

View File

@ -37,6 +37,7 @@ type SupportedFeatures struct {
TierAcl bool `json:"TierAcl"` TierAcl bool `json:"TierAcl"`
NetworkACL bool `json:"NetworkACL"` NetworkACL bool `json:"NetworkACL"`
NestedIpSet bool `json:"NestedIpSet"` NestedIpSet bool `json:"NestedIpSet"`
DisableHostPort bool `json:"DisableHostPort"`
} }
// AclFeatures are the supported ACL possibilities. // AclFeatures are the supported ACL possibilities.
@ -114,6 +115,7 @@ func getSupportedFeatures() (SupportedFeatures, error) {
features.TierAcl = isFeatureSupported(globals.Version, TierAclPolicyVersion) features.TierAcl = isFeatureSupported(globals.Version, TierAclPolicyVersion)
features.NetworkACL = isFeatureSupported(globals.Version, NetworkACLPolicyVersion) features.NetworkACL = isFeatureSupported(globals.Version, NetworkACLPolicyVersion)
features.NestedIpSet = isFeatureSupported(globals.Version, NestedIpSetVersion) features.NestedIpSet = isFeatureSupported(globals.Version, NestedIpSetVersion)
features.DisableHostPort = isFeatureSupported(globals.Version, DisableHostPortVersion)
log.L.WithFields(logrus.Fields{ log.L.WithFields(logrus.Fields{
"version": globals.Version, "version": globals.Version,

View File

@ -12,14 +12,16 @@ import (
"syscall" "syscall"
"time" "time"
"go.opencensus.io/trace"
"github.com/Microsoft/hcsshim/internal/cow" "github.com/Microsoft/hcsshim/internal/cow"
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/oc"
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
"github.com/Microsoft/hcsshim/internal/vmcompute" "github.com/Microsoft/hcsshim/internal/vmcompute"
"go.opencensus.io/trace"
) )
// ContainerError is an error encountered in HCS
type Process struct { type Process struct {
handleLock sync.RWMutex handleLock sync.RWMutex
handle vmcompute.HcsProcess handle vmcompute.HcsProcess
@ -50,35 +52,6 @@ func newProcess(process vmcompute.HcsProcess, processID int, computeSystem *Syst
} }
} }
type processModifyRequest struct {
Operation string
ConsoleSize *consoleSize `json:",omitempty"`
CloseHandle *closeHandle `json:",omitempty"`
}
type consoleSize struct {
Height uint16
Width uint16
}
type closeHandle struct {
Handle string
}
type processStatus struct {
ProcessID uint32
Exited bool
ExitCode uint32
LastWaitResult int32
}
const stdIn string = "StdIn"
const (
modifyConsoleSize string = "ConsoleSize"
modifyCloseHandle string = "CloseHandle"
)
// Pid returns the process ID of the process within the container. // Pid returns the process ID of the process within the container.
func (process *Process) Pid() int { func (process *Process) Pid() int {
return process.processID return process.processID
@ -90,7 +63,7 @@ func (process *Process) SystemID() string {
} }
func (process *Process) processSignalResult(ctx context.Context, err error) (bool, error) { func (process *Process) processSignalResult(ctx context.Context, err error) (bool, error) {
switch err { switch err { //nolint:errorlint
case nil: case nil:
return true, nil return true, nil
case ErrVmcomputeOperationInvalidState, ErrComputeSystemDoesNotExist, ErrElementNotFound: case ErrVmcomputeOperationInvalidState, ErrComputeSystemDoesNotExist, ErrElementNotFound:
@ -260,14 +233,14 @@ func (process *Process) waitBackground() {
process.handleLock.RLock() process.handleLock.RLock()
defer process.handleLock.RUnlock() defer process.handleLock.RUnlock()
// Make sure we didnt race with Close() here // Make sure we didn't race with Close() here
if process.handle != 0 { if process.handle != 0 {
propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle) propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle)
events := processHcsResult(ctx, resultJSON) events := processHcsResult(ctx, resultJSON)
if err != nil { if err != nil {
err = makeProcessError(process, operation, err, events) err = makeProcessError(process, operation, err, events)
} else { } else {
properties := &processStatus{} properties := &hcsschema.ProcessStatus{}
err = json.Unmarshal([]byte(propertiesJSON), properties) err = json.Unmarshal([]byte(propertiesJSON), properties)
if err != nil { if err != nil {
err = makeProcessError(process, operation, err, nil) err = makeProcessError(process, operation, err, nil)
@ -318,10 +291,9 @@ func (process *Process) ResizeConsole(ctx context.Context, width, height uint16)
if process.handle == 0 { if process.handle == 0 {
return makeProcessError(process, operation, ErrAlreadyClosed, nil) return makeProcessError(process, operation, ErrAlreadyClosed, nil)
} }
modifyRequest := hcsschema.ProcessModifyRequest{
modifyRequest := processModifyRequest{ Operation: guestrequest.ModifyProcessConsoleSize,
Operation: modifyConsoleSize, ConsoleSize: &hcsschema.ConsoleSize{
ConsoleSize: &consoleSize{
Height: height, Height: height,
Width: width, Width: width,
}, },
@ -423,10 +395,10 @@ func (process *Process) CloseStdin(ctx context.Context) (err error) {
//HcsModifyProcess request to close stdin will fail if the process has already exited //HcsModifyProcess request to close stdin will fail if the process has already exited
if !process.stopped() { if !process.stopped() {
modifyRequest := processModifyRequest{ modifyRequest := hcsschema.ProcessModifyRequest{
Operation: modifyCloseHandle, Operation: guestrequest.CloseProcessHandle,
CloseHandle: &closeHandle{ CloseHandle: &hcsschema.CloseHandle{
Handle: stdIn, Handle: guestrequest.STDInHandle,
}, },
} }

View File

@ -0,0 +1,25 @@
/*
* HCS API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.5
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package hcsschema
const (
CimMountFlagNone uint32 = 0x0
CimMountFlagChildOnly uint32 = 0x1
CimMountFlagEnableDax uint32 = 0x2
CimMountFlagCacheFiles uint32 = 0x4
CimMountFlagCacheRegions uint32 = 0x8
)
type CimMount struct {
ImagePath string `json:"ImagePath,omitempty"`
FileSystemName string `json:"FileSystemName,omitempty"`
VolumeGuid string `json:"VolumeGuid,omitempty"`
MountFlags uint32 `json:"MountFlags,omitempty"`
}

View File

@ -9,6 +9,8 @@
package hcsschema package hcsschema
import "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
type CloseHandle struct { type CloseHandle struct {
Handle string `json:"Handle,omitempty"` Handle guestrequest.STDIOHandle `json:"Handle,omitempty"` // NOTE: Swagger generated as string. Locally updated.
} }

View File

@ -9,8 +9,11 @@
package hcsschema package hcsschema
type ConsoleSize struct { // NOTE: Swagger generated fields as int32. Locally updated to uint16 to match documentation.
Height int32 `json:"Height,omitempty"` // https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#ConsoleSize
Width int32 `json:"Width,omitempty"` type ConsoleSize struct {
Height uint16 `json:"Height,omitempty"`
Width uint16 `json:"Width,omitempty"`
} }

View File

@ -17,5 +17,5 @@ type IsolationSettings struct {
DebugPort int64 `json:"DebugPort,omitempty"` DebugPort int64 `json:"DebugPort,omitempty"`
// Optional data passed by host on isolated virtual machine start // Optional data passed by host on isolated virtual machine start
LaunchData string `json:"LaunchData,omitempty"` LaunchData string `json:"LaunchData,omitempty"`
HclEnabled bool `json:"HclEnabled,omitempty"` HclEnabled *bool `json:"HclEnabled,omitempty"`
} }

View File

@ -9,6 +9,13 @@
package hcsschema package hcsschema
type FileSystemFilterType string
const (
UnionFS FileSystemFilterType = "UnionFS"
WCIFS FileSystemFilterType = "WCIFS"
)
type Layer struct { type Layer struct {
Id string `json:"Id,omitempty"` Id string `json:"Id,omitempty"`

View File

@ -9,9 +9,11 @@
package hcsschema package hcsschema
import "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
// Passed to HcsRpc_ModifyProcess // Passed to HcsRpc_ModifyProcess
type ProcessModifyRequest struct { type ProcessModifyRequest struct {
Operation string `json:"Operation,omitempty"` Operation guestrequest.ProcessModifyOperation `json:"Operation,omitempty"` // NOTE: Swagger generated as string. Locally updated.
ConsoleSize *ConsoleSize `json:"ConsoleSize,omitempty"` ConsoleSize *ConsoleSize `json:"ConsoleSize,omitempty"`

View File

@ -9,13 +9,16 @@
package hcsschema package hcsschema
// NOTE: Swagger generated fields as int32. Locally updated to uint16 to match documentation.
// https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#ConsoleSize
// Status of a process running in a container // Status of a process running in a container
type ProcessStatus struct { type ProcessStatus struct {
ProcessId int32 `json:"ProcessId,omitempty"` ProcessId uint32 `json:"ProcessId,omitempty"` // NOTE: Swagger generated as int32. Locally updated to match documentation.
Exited bool `json:"Exited,omitempty"` Exited bool `json:"Exited,omitempty"`
ExitCode int32 `json:"ExitCode,omitempty"` ExitCode uint32 `json:"ExitCode,omitempty"` // NOTE: Swagger generated as int32. Locally updated to match documentation.
LastWaitResult int32 `json:"LastWaitResult,omitempty"` LastWaitResult int32 `json:"LastWaitResult,omitempty"`
} }

View File

@ -10,7 +10,7 @@
package hcsschema package hcsschema
import ( import (
v1 "github.com/containerd/cgroups/stats/v1" v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
) )
type Properties struct { type Properties struct {

View File

@ -0,0 +1,13 @@
package hcsschema
// NOTE: manually added
type RegistryHive string
// List of RegistryHive
const (
RegistryHive_SYSTEM RegistryHive = "System"
RegistryHive_SOFTWARE RegistryHive = "Software"
RegistryHive_SECURITY RegistryHive = "Security"
RegistryHive_SAM RegistryHive = "Sam"
)

View File

@ -10,7 +10,7 @@
package hcsschema package hcsschema
type RegistryKey struct { type RegistryKey struct {
Hive string `json:"Hive,omitempty"` Hive RegistryHive `json:"Hive,omitempty"`
Name string `json:"Name,omitempty"` Name string `json:"Name,omitempty"`

View File

@ -14,7 +14,7 @@ type RegistryValue struct {
Name string `json:"Name,omitempty"` Name string `json:"Name,omitempty"`
Type_ string `json:"Type,omitempty"` Type_ RegistryValueType `json:"Type,omitempty"`
// One and only one value type must be set. // One and only one value type must be set.
StringValue string `json:"StringValue,omitempty"` StringValue string `json:"StringValue,omitempty"`

View File

@ -0,0 +1,17 @@
package hcsschema
// NOTE: manually added
type RegistryValueType string
// List of RegistryValueType
const (
RegistryValueType_NONE RegistryValueType = "None"
RegistryValueType_STRING RegistryValueType = "String"
RegistryValueType_EXPANDED_STRING RegistryValueType = "ExpandedString"
RegistryValueType_MULTI_STRING RegistryValueType = "MultiString"
RegistryValueType_BINARY RegistryValueType = "Binary"
RegistryValueType_D_WORD RegistryValueType = "DWord"
RegistryValueType_Q_WORD RegistryValueType = "QWord"
RegistryValueType_CUSTOM_TYPE RegistryValueType = "CustomType"
)

View File

@ -97,7 +97,7 @@ func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface in
events, err := processAsyncHcsResult(ctx, createError, resultJSON, computeSystem.callbackNumber, events, err := processAsyncHcsResult(ctx, createError, resultJSON, computeSystem.callbackNumber,
hcsNotificationSystemCreateCompleted, &timeout.SystemCreate) hcsNotificationSystemCreateCompleted, &timeout.SystemCreate)
if err != nil { if err != nil {
if err == ErrTimeout { if errors.Is(err, ErrTimeout) {
// Terminate the compute system if it still exists. We're okay to // Terminate the compute system if it still exists. We're okay to
// ignore a failure here. // ignore a failure here.
_ = computeSystem.Terminate(ctx) _ = computeSystem.Terminate(ctx)
@ -238,7 +238,7 @@ func (computeSystem *System) Shutdown(ctx context.Context) error {
resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "") resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "")
events := processHcsResult(ctx, resultJSON) events := processHcsResult(ctx, resultJSON)
switch err { switch err { //nolint:errorlint
case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending:
default: default:
return makeSystemError(computeSystem, operation, err, events) return makeSystemError(computeSystem, operation, err, events)
@ -259,7 +259,7 @@ func (computeSystem *System) Terminate(ctx context.Context) error {
resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "") resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "")
events := processHcsResult(ctx, resultJSON) events := processHcsResult(ctx, resultJSON)
switch err { switch err { //nolint:errorlint
case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending:
default: default:
return makeSystemError(computeSystem, operation, err, events) return makeSystemError(computeSystem, operation, err, events)
@ -279,7 +279,7 @@ func (computeSystem *System) waitBackground() {
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil) err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil)
switch err { switch err { //nolint:errorlint
case nil: case nil:
log.G(ctx).Debug("system exited") log.G(ctx).Debug("system exited")
case ErrVmcomputeUnexpectedExit: case ErrVmcomputeUnexpectedExit:
@ -304,11 +304,22 @@ func (computeSystem *System) WaitError() error {
return computeSystem.waitError return computeSystem.waitError
} }
// Wait synchronously waits for the compute system to shutdown or terminate. If // Wait synchronously waits for the compute system to shutdown or terminate.
// the compute system has already exited returns the previous error (if any). // If the compute system has already exited returns the previous error (if any).
func (computeSystem *System) Wait() error { func (computeSystem *System) Wait() error {
<-computeSystem.WaitChannel() return computeSystem.WaitCtx(context.Background())
}
// WaitCtx synchronously waits for the compute system to shutdown or terminate, or the context to be cancelled.
//
// See [System.Wait] for more information.
func (computeSystem *System) WaitCtx(ctx context.Context) error {
select {
case <-computeSystem.WaitChannel():
return computeSystem.WaitError() return computeSystem.WaitError()
case <-ctx.Done():
return ctx.Err()
}
} }
// stopped returns true if the compute system stopped. // stopped returns true if the compute system stopped.
@ -735,9 +746,17 @@ func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process
} }
// Close cleans up any state associated with the compute system but does not terminate or wait for it. // Close cleans up any state associated with the compute system but does not terminate or wait for it.
func (computeSystem *System) Close() (err error) { func (computeSystem *System) Close() error {
return computeSystem.CloseCtx(context.Background())
}
// CloseCtx is similar to [System.Close], but accepts a context.
//
// The context is used for all operations, including waits, so timeouts/cancellations may prevent
// proper system cleanup.
func (computeSystem *System) CloseCtx(ctx context.Context) (err error) {
operation := "hcs::System::Close" operation := "hcs::System::Close"
ctx, span := oc.StartSpan(context.Background(), operation) ctx, span := oc.StartSpan(ctx, operation)
defer span.End() defer span.End()
defer func() { oc.SetSpanStatus(span, err) }() defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))

View File

@ -31,7 +31,7 @@ func hnsCallRawResponse(method, path, request string) (*hnsResponse, error) {
func hnsCall(method, path, request string, returnResponse interface{}) error { func hnsCall(method, path, request string, returnResponse interface{}) error {
hnsresponse, err := hnsCallRawResponse(method, path, request) hnsresponse, err := hnsCallRawResponse(method, path, request)
if err != nil { if err != nil {
return fmt.Errorf("failed during hnsCallRawResponse: %v", err) return fmt.Errorf("failed during hnsCallRawResponse: %w", err)
} }
if !hnsresponse.Success { if !hnsresponse.Success {
return fmt.Errorf("hns failed with error : %s", hnsresponse.Error) return fmt.Errorf("hns failed with error : %s", hnsresponse.Error)

View File

@ -56,7 +56,7 @@ func issueNamespaceRequest(id *string, method, subpath string, request interface
if strings.Contains(err.Error(), "Element not found.") { if strings.Contains(err.Error(), "Element not found.") {
return nil, os.ErrNotExist return nil, os.ErrNotExist
} }
return nil, fmt.Errorf("%s %s: %s", method, hnspath, err) return nil, fmt.Errorf("%s %s: %w", method, hnspath, err)
} }
return &ns, err return &ns, err
} }
@ -86,7 +86,7 @@ func GetNamespaceEndpoints(id string) ([]string, error) {
var endpoint namespaceEndpointRequest var endpoint namespaceEndpointRequest
err = json.Unmarshal(rsrc.Data, &endpoint) err = json.Unmarshal(rsrc.Data, &endpoint)
if err != nil { if err != nil {
return nil, fmt.Errorf("unmarshal endpoint: %s", err) return nil, fmt.Errorf("unmarshal endpoint: %w", err)
} }
endpoints = append(endpoints, endpoint.ID) endpoints = append(endpoints, endpoint.ID)
} }

View File

@ -4,6 +4,7 @@ package jobobject
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"sync" "sync"
"unsafe" "unsafe"
@ -59,7 +60,7 @@ func pollIOCP(ctx context.Context, iocpHandle windows.Handle) {
}).Warn("failed to parse job object message") }).Warn("failed to parse job object message")
continue continue
} }
if err := msq.Enqueue(notification); err == queue.ErrQueueClosed { if err := msq.Enqueue(notification); errors.Is(err, queue.ErrQueueClosed) {
// Write will only return an error when the queue is closed. // Write will only return an error when the queue is closed.
// The only time a queue would ever be closed is when we call `Close` on // The only time a queue would ever be closed is when we call `Close` on
// the job it belongs to which also removes it from the jobMap, so something // the job it belongs to which also removes it from the jobMap, so something

View File

@ -167,7 +167,7 @@ func Create(ctx context.Context, options *Options) (_ *JobObject, err error) {
// //
// Returns a JobObject structure and an error if there is one. // Returns a JobObject structure and an error if there is one.
func Open(ctx context.Context, options *Options) (_ *JobObject, err error) { func Open(ctx context.Context, options *Options) (_ *JobObject, err error) {
if options == nil || (options != nil && options.Name == "") { if options == nil || options.Name == "" {
return nil, errors.New("no job object name specified to open") return nil, errors.New("no job object name specified to open")
} }
@ -374,7 +374,7 @@ func (job *JobObject) Pids() ([]uint32, error) {
return []uint32{}, nil return []uint32{}, nil
} }
if err != winapi.ERROR_MORE_DATA { if err != winapi.ERROR_MORE_DATA { //nolint:errorlint
return nil, fmt.Errorf("failed initial query for PIDs in job object: %w", err) return nil, fmt.Errorf("failed initial query for PIDs in job object: %w", err)
} }

View File

@ -143,6 +143,13 @@ func (job *JobObject) SetCPUAffinity(affinityBitMask uint64) error {
return err return err
} }
info.BasicLimitInformation.LimitFlags |= uint32(windows.JOB_OBJECT_LIMIT_AFFINITY) info.BasicLimitInformation.LimitFlags |= uint32(windows.JOB_OBJECT_LIMIT_AFFINITY)
// We really, really shouldn't be running on 32 bit, but just in case (and to satisfy CodeQL) ...
const maxUintptr = ^uintptr(0)
if affinityBitMask > uint64(maxUintptr) {
return fmt.Errorf("affinity bitmask (%d) exceeds max allowable value (%d)", affinityBitMask, maxUintptr)
}
info.BasicLimitInformation.Affinity = uintptr(affinityBitMask) info.BasicLimitInformation.Affinity = uintptr(affinityBitMask)
return job.setExtendedInformation(info) return job.setExtendedInformation(info)
} }

View File

@ -8,6 +8,10 @@ import (
"net" "net"
"reflect" "reflect"
"time" "time"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
) )
// TimeFormat is [time.RFC3339Nano] with nanoseconds padded using // TimeFormat is [time.RFC3339Nano] with nanoseconds padded using
@ -61,25 +65,49 @@ func formatAddr(a net.Addr) string {
func Format(ctx context.Context, v interface{}) string { func Format(ctx context.Context, v interface{}) string {
b, err := encode(v) b, err := encode(v)
if err != nil { if err != nil {
G(ctx).WithError(err).Warning("could not format value") // logging errors aren't really warning worthy, and can potentially spam a lot of logs out
G(ctx).WithFields(logrus.Fields{
logrus.ErrorKey: err,
"type": fmt.Sprintf("%T", v),
}).Debug("could not format value")
return "" return ""
} }
return string(b) return string(b)
} }
func encode(v interface{}) ([]byte, error) { func encode(v interface{}) (_ []byte, err error) {
return encodeBuffer(&bytes.Buffer{}, v) if m, ok := v.(proto.Message); ok {
} // use canonical JSON encoding for protobufs (instead of [encoding/json])
// https://protobuf.dev/programming-guides/proto3/#json
var b []byte
b, err = protojson.MarshalOptions{
AllowPartial: true,
// protobuf defaults to camel case for JSON encoding; use proto field name instead (snake case)
UseProtoNames: true,
}.Marshal(m)
if err == nil {
// the protojson marshaller tries to unmarshal anypb.Any fields, which can
// fail for types encoded with "github.com/containerd/typeurl/v2"
// we can try creating a dedicated protoregistry.MessageTypeResolver that uses typeurl, but, its
// more robust to fall back on json marshalling for errors in general
return b, nil
}
func encodeBuffer(buf *bytes.Buffer, v interface{}) ([]byte, error) { }
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf) enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false) enc.SetEscapeHTML(false)
enc.SetIndent("", "") enc.SetIndent("", "")
if err := enc.Encode(v); err != nil { if jErr := enc.Encode(v); jErr != nil {
err = fmt.Errorf("could not marshall %T to JSON for logging: %w", v, err) if err != nil {
return nil, err // TODO (go1.20): use multierror via fmt.Errorf("...: %w; ...: %w", ...)
//nolint:errorlint // non-wrapping format verb for fmt.Errorf
return nil, fmt.Errorf("protojson encoding: %v; json encoding: %w", err, jErr)
}
return nil, fmt.Errorf("json encoding: %w", jErr)
} }
// encoder.Encode appends a newline to the end // encoder.Encode appends a newline to the end

View File

@ -0,0 +1,12 @@
package log
import (
"github.com/sirupsen/logrus"
)
type NopFormatter struct{}
var _ logrus.Formatter = NopFormatter{}
// Format does nothing and returns a nil slice.
func (NopFormatter) Format(*logrus.Entry) ([]byte, error) { return nil, nil }

View File

@ -55,7 +55,7 @@ func ScrubProcessParameters(s string) (string, error) {
} }
pp.Environment = map[string]string{_scrubbedReplacement: _scrubbedReplacement} pp.Environment = map[string]string{_scrubbedReplacement: _scrubbedReplacement}
b, err := encodeBuffer(bytes.NewBuffer(b[:0]), pp) b, err := encode(pp)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -89,11 +89,11 @@ func scrubBridgeCreate(m genMap) error {
} }
func scrubLinuxHostedSystem(m genMap) error { func scrubLinuxHostedSystem(m genMap) error {
if m, ok := index(m, "OciSpecification"); ok { if m, ok := index(m, "OciSpecification"); ok { //nolint:govet // shadow
if _, ok := m["annotations"]; ok { if _, ok := m["annotations"]; ok {
m["annotations"] = map[string]string{_scrubbedReplacement: _scrubbedReplacement} m["annotations"] = map[string]string{_scrubbedReplacement: _scrubbedReplacement}
} }
if m, ok := index(m, "process"); ok { if m, ok := index(m, "process"); ok { //nolint:govet // shadow
if _, ok := m["env"]; ok { if _, ok := m["env"]; ok {
m["env"] = []string{_scrubbedReplacement} m["env"] = []string{_scrubbedReplacement}
return nil return nil
@ -113,7 +113,7 @@ func scrubExecuteProcess(m genMap) error {
if !isRequestBase(m) { if !isRequestBase(m) {
return ErrUnknownType return ErrUnknownType
} }
if m, ok := index(m, "Settings"); ok { if m, ok := index(m, "Settings"); ok { //nolint:govet // shadow
if ss, ok := m["ProcessParameters"]; ok { if ss, ok := m["ProcessParameters"]; ok {
// ProcessParameters is a json encoded struct passed as a regular sting field // ProcessParameters is a json encoded struct passed as a regular sting field
s, ok := ss.(string) s, ok := ss.(string)

View File

@ -46,6 +46,7 @@ const (
ExpectedType = "expected-type" ExpectedType = "expected-type"
Bool = "bool" Bool = "bool"
Int32 = "int32"
Uint32 = "uint32" Uint32 = "uint32"
Uint64 = "uint64" Uint64 = "uint64"

View File

@ -126,7 +126,7 @@ func (pa *PoolAllocator) Allocate(size uint64) (MappedRegion, error) {
// this means that there are no more regions for the current class, try expanding // this means that there are no more regions for the current class, try expanding
if nextCls != memCls { if nextCls != memCls {
if err := pa.split(memCls); err != nil { if err := pa.split(memCls); err != nil {
if err == ErrInvalidMemoryClass { if errors.Is(err, ErrInvalidMemoryClass) {
return nil, ErrNotEnoughSpace return nil, ErrNotEnoughSpace
} }
return nil, err return nil, err
@ -147,7 +147,7 @@ func (pa *PoolAllocator) Allocate(size uint64) (MappedRegion, error) {
} }
// Release marks a memory region of class `memCls` and offset `offset` as free and tries to merge smaller regions into // Release marks a memory region of class `memCls` and offset `offset` as free and tries to merge smaller regions into
// a bigger one // a bigger one.
func (pa *PoolAllocator) Release(reg MappedRegion) error { func (pa *PoolAllocator) Release(reg MappedRegion) error {
mp := pa.pools[reg.Type()] mp := pa.pools[reg.Type()]
if mp == nil { if mp == nil {
@ -164,7 +164,7 @@ func (pa *PoolAllocator) Release(reg MappedRegion) error {
return ErrNotAllocated return ErrNotAllocated
} }
if err := pa.merge(n.parent); err != nil { if err := pa.merge(n.parent); err != nil {
if err != ErrEarlyMerge { if !errors.Is(err, ErrEarlyMerge) {
return err return err
} }
} }

View File

@ -6,7 +6,7 @@ import (
"net" "net"
"os" "os"
"github.com/containerd/containerd/errdefs" "github.com/containerd/errdefs"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )
@ -16,7 +16,7 @@ import (
func toStatusCode(err error) codes.Code { func toStatusCode(err error) codes.Code {
// checks if err implements GRPCStatus() *"google.golang.org/grpc/status".Status, // checks if err implements GRPCStatus() *"google.golang.org/grpc/status".Status,
// wraps an error defined in "github.com/containerd/containerd/errdefs", or is a // wraps an error defined in "github.com/containerd/errdefs", or is a
// context timeout or cancelled error // context timeout or cancelled error
if s, ok := status.FromError(errdefs.ToGRPC(err)); ok { if s, ok := status.FromError(errdefs.ToGRPC(err)); ok {
return s.Code() return s.Code()

View File

@ -5,7 +5,7 @@ package guestrequest
type RequestType string type RequestType string
type ResourceType string type ResourceType string
// RequestType const // RequestType const.
const ( const (
RequestTypeAdd RequestType = "Add" RequestTypeAdd RequestType = "Add"
RequestTypeRemove RequestType = "Remove" RequestTypeRemove RequestType = "Remove"
@ -54,3 +54,23 @@ var (
"305891a9-b251-5dfe-91a2-c25d9212275b", "305891a9-b251-5dfe-91a2-c25d9212275b",
} }
) )
// constants for v2 schema ProcessModifyRequest
// Operation type for [hcsschema.ProcessModifyRequest].
type ProcessModifyOperation string
const (
ModifyProcessConsoleSize ProcessModifyOperation = "ConsoleSize"
CloseProcessHandle ProcessModifyOperation = "CloseHandle"
)
// Standard IO handle(s) to close for [hcsschema.CloseHandle] in [hcsschema.ProcessModifyRequest].
type STDIOHandle string
const (
STDInHandle STDIOHandle = "StdIn"
STDOutHandle STDIOHandle = "StdOut"
STDErrHandle STDIOHandle = "StdErr"
AllHandles STDIOHandle = "All"
)

View File

@ -4,6 +4,7 @@ package regstate
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
@ -44,8 +45,8 @@ func (err *NotFoundError) Error() string {
} }
func IsNotFoundError(err error) bool { func IsNotFoundError(err error) bool {
_, ok := err.(*NotFoundError) var e *NotFoundError
return ok return errors.As(err, &e)
} }
type NoStateError struct { type NoStateError struct {
@ -152,7 +153,8 @@ func (k *Key) openid(id string) (*Key, error) {
escaped := url.PathEscape(id) escaped := url.PathEscape(id)
fullpath := filepath.Join(k.Name, escaped) fullpath := filepath.Join(k.Name, escaped)
nk, err := k.open(escaped) nk, err := k.open(escaped)
if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND { var perr *os.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, syscall.ERROR_FILE_NOT_FOUND) {
return nil, &NotFoundError{id} return nil, &NotFoundError{id}
} }
if err != nil { if err != nil {
@ -165,7 +167,7 @@ func (k *Key) Remove(id string) error {
escaped := url.PathEscape(id) escaped := url.PathEscape(id)
err := registry.DeleteKey(k.Key, escaped) err := registry.DeleteKey(k.Key, escaped)
if err != nil { if err != nil {
if err == syscall.ERROR_FILE_NOT_FOUND { if err == syscall.ERROR_FILE_NOT_FOUND { //nolint:errorlint
return &NotFoundError{id} return &NotFoundError{id}
} }
return &os.PathError{Op: "RegDeleteKey", Path: filepath.Join(k.Name, escaped), Err: err} return &os.PathError{Op: "RegDeleteKey", Path: filepath.Join(k.Name, escaped), Err: err}
@ -215,7 +217,7 @@ func (k *Key) set(id string, create bool, key string, state interface{}) error {
err = sk.SetBinaryValue(key, js) err = sk.SetBinaryValue(key, js)
} }
if err != nil { if err != nil {
if err == syscall.ERROR_FILE_NOT_FOUND { if err == syscall.ERROR_FILE_NOT_FOUND { //nolint:errorlint
return &NoStateError{id, key} return &NoStateError{id, key}
} }
return &os.PathError{Op: "RegSetValueEx", Path: sk.Name + ":" + key, Err: err} return &os.PathError{Op: "RegSetValueEx", Path: sk.Name + ":" + key, Err: err}
@ -239,7 +241,7 @@ func (k *Key) Clear(id, key string) error {
defer sk.Close() defer sk.Close()
err = sk.DeleteValue(key) err = sk.DeleteValue(key)
if err != nil { if err != nil {
if err == syscall.ERROR_FILE_NOT_FOUND { if err == syscall.ERROR_FILE_NOT_FOUND { //nolint:errorlint
return &NoStateError{id, key} return &NoStateError{id, key}
} }
return &os.PathError{Op: "RegDeleteValue", Path: sk.Name + ":" + key, Err: err} return &os.PathError{Op: "RegDeleteValue", Path: sk.Name + ":" + key, Err: err}
@ -278,7 +280,7 @@ func (k *Key) Get(id, key string, state interface{}) error {
js, _, err = sk.GetBinaryValue(key) js, _, err = sk.GetBinaryValue(key)
} }
if err != nil { if err != nil {
if err == syscall.ERROR_FILE_NOT_FOUND { if err == syscall.ERROR_FILE_NOT_FOUND { //nolint:errorlint
return &NoStateError{id, key} return &NoStateError{id, key}
} }
return &os.PathError{Op: "RegQueryValueEx", Path: sk.Name + ":" + key, Err: err} return &os.PathError{Op: "RegQueryValueEx", Path: sk.Name + ":" + key, Err: err}

View File

@ -243,7 +243,7 @@ func RemoveRelative(path string, root *os.File) error {
if err == nil { if err == nil {
defer f.Close() defer f.Close()
err = deleteOnClose(f) err = deleteOnClose(f)
if err == syscall.ERROR_ACCESS_DENIED { if err == syscall.ERROR_ACCESS_DENIED { //nolint:errorlint
// Maybe the file is marked readonly. Clear the bit and retry. // Maybe the file is marked readonly. Clear the bit and retry.
_ = clearReadOnly(f) _ = clearReadOnly(f)
err = deleteOnClose(f) err = deleteOnClose(f)
@ -276,7 +276,7 @@ func RemoveAllRelative(path string, root *os.File) error {
} }
// It is necessary to use os.Open as Readdirnames does not work with // It is necessary to use os.Open as Readdirnames does not work with
// OpenRelative. This is safe because the above lstatrelative fails // OpenRelative. This is safe because the above LstatRelative fails
// if the target is outside the root, and we know this is not a // if the target is outside the root, and we know this is not a
// symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check. // symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check.
fd, err := os.Open(filepath.Join(root.Name(), path)) fd, err := os.Open(filepath.Join(root.Name(), path))
@ -293,12 +293,12 @@ func RemoveAllRelative(path string, root *os.File) error {
for { for {
names, err1 := fd.Readdirnames(100) names, err1 := fd.Readdirnames(100)
for _, name := range names { for _, name := range names {
err1 := RemoveAllRelative(path+string(os.PathSeparator)+name, root) if err2 := RemoveAllRelative(path+string(os.PathSeparator)+name, root); err == nil {
if err == nil { err = err2
err = err1
} }
} }
if err1 == io.EOF { if err1 == io.EOF {
// Readdirnames has no more files to return
break break
} }
// If Readdirnames returned an error, use it. // If Readdirnames returned an error, use it.

View File

@ -104,7 +104,7 @@ func execute(ctx gcontext.Context, timeout time.Duration, f func() error) error
}() }()
select { select {
case <-ctx.Done(): case <-ctx.Done():
if ctx.Err() == gcontext.DeadlineExceeded { if ctx.Err() == gcontext.DeadlineExceeded { //nolint:errorlint
log.G(ctx).WithField(logfields.Timeout, trueTimeout). log.G(ctx).WithField(logfields.Timeout, trueTimeout).
Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. " + Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. " +
"If it appears to be making no forward progress, obtain the stacks and see if there is a syscall " + "If it appears to be making no forward progress, obtain the stacks and see if there is a syscall " +
@ -150,7 +150,7 @@ func HcsCreateComputeSystem(ctx gcontext.Context, id string, configuration strin
if result != "" { if result != "" {
span.AddAttributes(trace.StringAttribute("result", result)) span.AddAttributes(trace.StringAttribute("result", result))
} }
if hr != errVmcomputeOperationPending { if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned
oc.SetSpanStatus(span, hr) oc.SetSpanStatus(span, hr)
} }
}() }()
@ -205,7 +205,7 @@ func HcsStartComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, option
if result != "" { if result != "" {
span.AddAttributes(trace.StringAttribute("result", result)) span.AddAttributes(trace.StringAttribute("result", result))
} }
if hr != errVmcomputeOperationPending { if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned
oc.SetSpanStatus(span, hr) oc.SetSpanStatus(span, hr)
} }
}() }()
@ -228,7 +228,7 @@ func HcsShutdownComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, opt
if result != "" { if result != "" {
span.AddAttributes(trace.StringAttribute("result", result)) span.AddAttributes(trace.StringAttribute("result", result))
} }
if hr != errVmcomputeOperationPending { if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned
oc.SetSpanStatus(span, hr) oc.SetSpanStatus(span, hr)
} }
}() }()
@ -251,7 +251,7 @@ func HcsTerminateComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, op
if result != "" { if result != "" {
span.AddAttributes(trace.StringAttribute("result", result)) span.AddAttributes(trace.StringAttribute("result", result))
} }
if hr != errVmcomputeOperationPending { if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned
oc.SetSpanStatus(span, hr) oc.SetSpanStatus(span, hr)
} }
}() }()
@ -274,7 +274,7 @@ func HcsPauseComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, option
if result != "" { if result != "" {
span.AddAttributes(trace.StringAttribute("result", result)) span.AddAttributes(trace.StringAttribute("result", result))
} }
if hr != errVmcomputeOperationPending { if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned
oc.SetSpanStatus(span, hr) oc.SetSpanStatus(span, hr)
} }
}() }()
@ -297,7 +297,7 @@ func HcsResumeComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, optio
if result != "" { if result != "" {
span.AddAttributes(trace.StringAttribute("result", result)) span.AddAttributes(trace.StringAttribute("result", result))
} }
if hr != errVmcomputeOperationPending { if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned
oc.SetSpanStatus(span, hr) oc.SetSpanStatus(span, hr)
} }
}() }()
@ -621,7 +621,7 @@ func HcsSaveComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options
if result != "" { if result != "" {
span.AddAttributes(trace.StringAttribute("result", result)) span.AddAttributes(trace.StringAttribute("result", result))
} }
if hr != errVmcomputeOperationPending { if hr != errVmcomputeOperationPending { //nolint:errorlint // explicitly returned
oc.SetSpanStatus(span, hr) oc.SetSpanStatus(span, hr)
} }
}() }()

View File

@ -1,3 +1,5 @@
//go:build windows
package wclayer package wclayer
import ( import (
@ -64,7 +66,7 @@ func (r *baseLayerReader) walkUntilCancelled() error {
return nil return nil
}) })
if err == errorIterationCanceled { if err == errorIterationCanceled { //nolint:errorlint // explicitly returned
return nil return nil
} }
@ -72,8 +74,8 @@ func (r *baseLayerReader) walkUntilCancelled() error {
return err return err
} }
utilityVMAbsPath := filepath.Join(r.root, utilityVMPath) utilityVMAbsPath := filepath.Join(r.root, UtilityVMPath)
utilityVMFilesAbsPath := filepath.Join(r.root, utilityVMFilesPath) utilityVMFilesAbsPath := filepath.Join(r.root, UtilityVMFilesPath)
// Ignore a UtilityVM without Files, that's not _really_ a UtiltyVM // Ignore a UtilityVM without Files, that's not _really_ a UtiltyVM
if _, err = os.Lstat(utilityVMFilesAbsPath); err != nil { if _, err = os.Lstat(utilityVMFilesAbsPath); err != nil {
@ -103,7 +105,7 @@ func (r *baseLayerReader) walkUntilCancelled() error {
return nil return nil
}) })
if err == errorIterationCanceled { if err == errorIterationCanceled { //nolint:errorlint // explicitly returned
return nil return nil
} }

View File

@ -1,3 +1,5 @@
//go:build windows
package wclayer package wclayer
import ( import (
@ -5,7 +7,6 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"syscall"
"github.com/Microsoft/hcsshim/internal/hcserror" "github.com/Microsoft/hcsshim/internal/hcserror"
"github.com/Microsoft/hcsshim/internal/longpath" "github.com/Microsoft/hcsshim/internal/longpath"
@ -37,7 +38,7 @@ func ensureHive(path string, root *os.File) (err error) {
return fmt.Errorf("getting path: %w", err) return fmt.Errorf("getting path: %w", err)
} }
var key syscall.Handle var key winapi.ORHKey
err = winapi.ORCreateHive(&key) err = winapi.ORCreateHive(&key)
if err != nil { if err != nil {
return fmt.Errorf("creating hive: %w", err) return fmt.Errorf("creating hive: %w", err)
@ -72,7 +73,7 @@ func ensureBaseLayer(root *os.File) (hasUtilityVM bool, err error) {
} }
} }
stat, err := safefile.LstatRelative(utilityVMFilesPath, root) stat, err := safefile.LstatRelative(UtilityVMFilesPath, root)
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false, nil return false, nil
@ -83,7 +84,7 @@ func ensureBaseLayer(root *os.File) (hasUtilityVM bool, err error) {
} }
if !stat.Mode().IsDir() { if !stat.Mode().IsDir() {
fullPath := filepath.Join(root.Name(), utilityVMFilesPath) fullPath := filepath.Join(root.Name(), UtilityVMFilesPath)
return false, errors.Errorf("%s has unexpected file mode %s", fullPath, stat.Mode().String()) return false, errors.Errorf("%s has unexpected file mode %s", fullPath, stat.Mode().String())
} }
@ -92,7 +93,7 @@ func ensureBaseLayer(root *os.File) (hasUtilityVM bool, err error) {
// Just check that this exists as a regular file. If it exists but is not a valid registry hive, // Just check that this exists as a regular file. If it exists but is not a valid registry hive,
// ProcessUtilityVMImage will complain: // ProcessUtilityVMImage will complain:
// "The registry could not read in, or write out, or flush, one of the files that contain the system's image of the registry." // "The registry could not read in, or write out, or flush, one of the files that contain the system's image of the registry."
bcdPath := filepath.Join(utilityVMFilesPath, bcdRelativePath) bcdPath := filepath.Join(UtilityVMFilesPath, bcdRelativePath)
stat, err = safefile.LstatRelative(bcdPath, root) stat, err = safefile.LstatRelative(bcdPath, root)
if err != nil { if err != nil {
@ -122,12 +123,12 @@ func convertToBaseLayer(ctx context.Context, root *os.File) error {
return nil return nil
} }
err = safefile.EnsureNotReparsePointRelative(utilityVMPath, root) err = safefile.EnsureNotReparsePointRelative(UtilityVMPath, root)
if err != nil { if err != nil {
return err return err
} }
utilityVMPath := filepath.Join(root.Name(), utilityVMPath) utilityVMPath := filepath.Join(root.Name(), UtilityVMPath)
return ProcessUtilityVMImage(ctx, utilityVMPath) return ProcessUtilityVMImage(ctx, utilityVMPath)
} }

View File

@ -11,7 +11,6 @@ import (
"github.com/Microsoft/hcsshim/internal/hcserror" "github.com/Microsoft/hcsshim/internal/hcserror"
"github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/oc"
"github.com/Microsoft/hcsshim/osversion"
"go.opencensus.io/trace" "go.opencensus.io/trace"
) )
@ -30,14 +29,17 @@ func ExpandScratchSize(ctx context.Context, path string, size uint64) (err error
return hcserror.New(err, title, "") return hcserror.New(err, title, "")
} }
// Manually expand the volume now in order to work around bugs in 19H1 and // Always expand the volume too. In case of legacy layers not expanding the volume here works because
// prerelease versions of Vb. Remove once this is fixed in Windows. // the PrepareLayer call internally handles the expansion. However, in other cases (like CimFS) we
if build := osversion.Build(); build >= osversion.V19H1 && build < 19020 { // don't call PrepareLayer and so the volume will never be expanded. This also means in case of
// legacy layers, we might have a small perf hit because the VHD is mounted twice for expansion (once
// here and once during the PrepareLayer call). But as long as the perf hit is minimal, we should be
// okay.
err = expandSandboxVolume(ctx, path) err = expandSandboxVolume(ctx, path)
if err != nil { if err != nil {
return err return err
} }
}
return nil return nil
} }

View File

@ -7,6 +7,10 @@ package wclayer
import ( import (
"context" "context"
"fmt"
"os"
"path/filepath"
"strconv"
"syscall" "syscall"
"github.com/Microsoft/go-winio/pkg/guid" "github.com/Microsoft/go-winio/pkg/guid"
@ -101,3 +105,23 @@ func layerPathsToDescriptors(ctx context.Context, parentLayerPaths []string) ([]
return layers, nil return layers, nil
} }
// GetLayerUvmBuild looks for a file named `uvmbuildversion` at `layerPath\uvmbuildversion` and returns the
// build number of the UVM from that file.
func GetLayerUvmBuild(layerPath string) (uint16, error) {
data, err := os.ReadFile(filepath.Join(layerPath, UvmBuildFileName))
if err != nil {
return 0, err
}
ver, err := strconv.ParseUint(string(data), 10, 16)
if err != nil {
return 0, err
}
return uint16(ver), nil
}
// WriteLayerUvmBuildFile writes a file at path `layerPath\uvmbuildversion` that contains the given `build`
// version for future reference.
func WriteLayerUvmBuildFile(layerPath string, build uint16) error {
return os.WriteFile(filepath.Join(layerPath, UvmBuildFileName), []byte(fmt.Sprintf("%d", build)), 0777)
}

View File

@ -30,9 +30,18 @@ var mutatedUtilityVMFiles = map[string]bool{
const ( const (
filesPath = `Files` filesPath = `Files`
hivesPath = `Hives` HivesPath = `Hives`
utilityVMPath = `UtilityVM` UtilityVMPath = `UtilityVM`
utilityVMFilesPath = `UtilityVM\Files` UtilityVMFilesPath = `UtilityVM\Files`
RegFilesPath = `Files\Windows\System32\config`
BcdFilePath = `UtilityVM\Files\EFI\Microsoft\Boot\BCD`
BootMgrFilePath = `UtilityVM\Files\EFI\Microsoft\Boot\bootmgfw.efi`
ContainerBaseVhd = `blank-base.vhdx`
ContainerScratchVhd = `blank.vhdx`
UtilityVMBaseVhd = `SystemTemplateBase.vhdx`
UtilityVMScratchVhd = `SystemTemplate.vhdx`
LayoutFileName = `layout`
UvmBuildFileName = `uvmbuildversion`
) )
func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) { func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) {
@ -145,7 +154,7 @@ func (r *legacyLayerReader) walkUntilCancelled() error {
} }
return nil return nil
}) })
if err == errorIterationCanceled { if err == errorIterationCanceled { //nolint:errorlint // explicitly returned
return nil return nil
} }
if err == nil { if err == nil {
@ -187,7 +196,7 @@ func findBackupStreamSize(r io.Reader) (int64, error) {
for { for {
hdr, err := br.Next() hdr, err := br.Next()
if err != nil { if err != nil {
if err == io.EOF { if errors.Is(err, io.EOF) {
err = nil err = nil
} }
return 0, err return 0, err
@ -243,11 +252,11 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil
if !hasPathPrefix(path, filesPath) { if !hasPathPrefix(path, filesPath) {
size = fe.fi.Size() size = fe.fi.Size()
r.backupReader = winio.NewBackupFileReader(f, false) r.backupReader = winio.NewBackupFileReader(f, false)
if path == hivesPath || path == filesPath { if path == HivesPath || path == filesPath {
// The Hives directory has a non-deterministic file time because of the // The Hives directory has a non-deterministic file time because of the
// nature of the import process. Use the times from System_Delta. // nature of the import process. Use the times from System_Delta.
var g *os.File var g *os.File
g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`)) g, err = os.Open(filepath.Join(r.root, HivesPath, `System_Delta`))
if err != nil { if err != nil {
return return
} }
@ -409,7 +418,7 @@ func (w *legacyLayerWriter) CloseRoots() {
func (w *legacyLayerWriter) initUtilityVM() error { func (w *legacyLayerWriter) initUtilityVM() error {
if !w.HasUtilityVM { if !w.HasUtilityVM {
err := safefile.MkdirRelative(utilityVMPath, w.destRoot) err := safefile.MkdirRelative(UtilityVMPath, w.destRoot)
if err != nil { if err != nil {
return err return err
} }
@ -417,9 +426,9 @@ func (w *legacyLayerWriter) initUtilityVM() error {
// clone the utility VM from the parent layer into this layer. Use hard // clone the utility VM from the parent layer into this layer. Use hard
// links to avoid unnecessary copying, since most of the files are // links to avoid unnecessary copying, since most of the files are
// immutable. // immutable.
err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles) err = cloneTree(w.parentRoots[0], w.destRoot, UtilityVMFilesPath, mutatedUtilityVMFiles)
if err != nil { if err != nil {
return fmt.Errorf("cloning the parent utility VM image failed: %s", err) return fmt.Errorf("cloning the parent utility VM image failed: %w", err)
} }
w.HasUtilityVM = true w.HasUtilityVM = true
} }
@ -442,7 +451,7 @@ func (w *legacyLayerWriter) reset() error {
for { for {
bhdr, err := br.Next() bhdr, err := br.Next()
if err == io.EOF { if errors.Is(err, io.EOF) {
// end of backupstream data // end of backupstream data
break break
} }
@ -592,7 +601,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
return err return err
} }
if name == utilityVMPath { if name == UtilityVMPath {
return w.initUtilityVM() return w.initUtilityVM()
} }
@ -601,11 +610,11 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
} }
name = filepath.Clean(name) name = filepath.Clean(name)
if hasPathPrefix(name, utilityVMPath) { if hasPathPrefix(name, UtilityVMPath) {
if !w.HasUtilityVM { if !w.HasUtilityVM {
return errors.New("missing UtilityVM directory") return errors.New("missing UtilityVM directory")
} }
if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath { if !hasPathPrefix(name, UtilityVMFilesPath) && name != UtilityVMFilesPath {
return errors.New("invalid UtilityVM layer") return errors.New("invalid UtilityVM layer")
} }
createDisposition := uint32(winapi.FILE_OPEN) createDisposition := uint32(winapi.FILE_OPEN)
@ -699,7 +708,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
return err return err
} }
if hasPathPrefix(name, hivesPath) { if hasPathPrefix(name, HivesPath) {
w.backupWriter = winio.NewBackupFileWriter(f, false) w.backupWriter = winio.NewBackupFileWriter(f, false)
w.bufWriter.Reset(w.backupWriter) w.bufWriter.Reset(w.backupWriter)
} else { } else {
@ -731,14 +740,14 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
// Look for cross-layer hard link targets in the parent layers, since // Look for cross-layer hard link targets in the parent layers, since
// nothing is in the destination path yet. // nothing is in the destination path yet.
roots = w.parentRoots roots = w.parentRoots
} else if hasPathPrefix(target, utilityVMFilesPath) { } else if hasPathPrefix(target, UtilityVMFilesPath) {
// Since the utility VM is fully cloned into the destination path // Since the utility VM is fully cloned into the destination path
// already, look for cross-layer hard link targets directly in the // already, look for cross-layer hard link targets directly in the
// destination path. // destination path.
roots = []*os.File{w.destRoot} roots = []*os.File{w.destRoot}
} }
if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) { if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, UtilityVMFilesPath)) {
return errors.New("invalid hard link in layer") return errors.New("invalid hard link in layer")
} }
@ -777,7 +786,7 @@ func (w *legacyLayerWriter) Remove(name string) error {
name = filepath.Clean(name) name = filepath.Clean(name)
if hasPathPrefix(name, filesPath) { if hasPathPrefix(name, filesPath) {
w.Tombstones = append(w.Tombstones, name) w.Tombstones = append(w.Tombstones, name)
} else if hasPathPrefix(name, utilityVMFilesPath) { } else if hasPathPrefix(name, UtilityVMFilesPath) {
err := w.initUtilityVM() err := w.initUtilityVM()
if err != nil { if err != nil {
return err return err

View File

@ -0,0 +1,47 @@
//go:build windows
package winapi
import (
"unsafe"
"github.com/Microsoft/go-winio/pkg/guid"
"golang.org/x/sys/windows"
)
type g = guid.GUID
type FsHandle uintptr
type StreamHandle uintptr
type CimFsFileMetadata struct {
Attributes uint32
FileSize int64
CreationTime windows.Filetime
LastWriteTime windows.Filetime
ChangeTime windows.Filetime
LastAccessTime windows.Filetime
SecurityDescriptorBuffer unsafe.Pointer
SecurityDescriptorSize uint32
ReparseDataBuffer unsafe.Pointer
ReparseDataSize uint32
ExtendedAttributes unsafe.Pointer
EACount uint32
}
//sys CimMountImage(imagePath string, fsName string, flags uint32, volumeID *g) (hr error) = cimfs.CimMountImage?
//sys CimDismountImage(volumeID *g) (hr error) = cimfs.CimDismountImage?
//sys CimCreateImage(imagePath string, oldFSName *uint16, newFSName *uint16, cimFSHandle *FsHandle) (hr error) = cimfs.CimCreateImage?
//sys CimCloseImage(cimFSHandle FsHandle) = cimfs.CimCloseImage?
//sys CimCommitImage(cimFSHandle FsHandle) (hr error) = cimfs.CimCommitImage?
//sys CimCreateFile(cimFSHandle FsHandle, path string, file *CimFsFileMetadata, cimStreamHandle *StreamHandle) (hr error) = cimfs.CimCreateFile?
//sys CimCloseStream(cimStreamHandle StreamHandle) (hr error) = cimfs.CimCloseStream?
//sys CimWriteStream(cimStreamHandle StreamHandle, buffer uintptr, bufferSize uint32) (hr error) = cimfs.CimWriteStream?
//sys CimDeletePath(cimFSHandle FsHandle, path string) (hr error) = cimfs.CimDeletePath?
//sys CimCreateHardLink(cimFSHandle FsHandle, newPath string, oldPath string) (hr error) = cimfs.CimCreateHardLink?
//sys CimCreateAlternateStream(cimFSHandle FsHandle, path string, size uint64, cimStreamHandle *StreamHandle) (hr error) = cimfs.CimCreateAlternateStream?

View File

@ -0,0 +1,37 @@
package winapi
// Offline registry management API
type ORHKey uintptr
type RegType uint32
const (
// Registry value types: https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types
REG_TYPE_NONE RegType = 0
REG_TYPE_SZ RegType = 1
REG_TYPE_EXPAND_SZ RegType = 2
REG_TYPE_BINARY RegType = 3
REG_TYPE_DWORD RegType = 4
REG_TYPE_DWORD_LITTLE_ENDIAN RegType = 4
REG_TYPE_DWORD_BIG_ENDIAN RegType = 5
REG_TYPE_LINK RegType = 6
REG_TYPE_MULTI_SZ RegType = 7
REG_TYPE_RESOURCE_LIST RegType = 8
REG_TYPE_FULL_RESOURCE_DESCRIPTOR RegType = 9
REG_TYPE_RESOURCE_REQUIREMENTS_LIST RegType = 10
REG_TYPE_QWORD RegType = 11
REG_TYPE_QWORD_LITTLE_ENDIAN RegType = 11
)
//sys ORCreateHive(key *ORHKey) (win32err error) = offreg.ORCreateHive
//sys ORMergeHives(hiveHandles []ORHKey, result *ORHKey) (win32err error) = offreg.ORMergeHives
//sys OROpenHive(hivePath string, result *ORHKey) (win32err error) = offreg.OROpenHive
//sys ORCloseHive(handle ORHKey) (win32err error) = offreg.ORCloseHive
//sys ORSaveHive(handle ORHKey, hivePath string, osMajorVersion uint32, osMinorVersion uint32) (win32err error) = offreg.ORSaveHive
//sys OROpenKey(handle ORHKey, subKey string, result *ORHKey) (win32err error) = offreg.OROpenKey
//sys ORCloseKey(handle ORHKey) (win32err error) = offreg.ORCloseKey
//sys ORCreateKey(handle ORHKey, subKey string, class uintptr, options uint32, securityDescriptor uintptr, result *ORHKey, disposition *uint32) (win32err error) = offreg.ORCreateKey
//sys ORDeleteKey(handle ORHKey, subKey string) (win32err error) = offreg.ORDeleteKey
//sys ORGetValue(handle ORHKey, subKey string, value string, valueType *uint32, data *byte, dataLen *uint32) (win32err error) = offreg.ORGetValue
//sys ORSetValue(handle ORHKey, valueName string, valueType uint32, data *byte, dataLen uint32) (win32err error) = offreg.ORSetValue

View File

@ -1,5 +0,0 @@
package winapi
//sys ORCreateHive(key *syscall.Handle) (regerrno error) = offreg.ORCreateHive
//sys ORSaveHive(key syscall.Handle, file string, OsMajorVersion uint32, OsMinorVersion uint32) (regerrno error) = offreg.ORSaveHive
//sys ORCloseHive(key syscall.Handle) (regerrno error) = offreg.ORCloseHive

View File

@ -4,7 +4,6 @@ package winapi
import ( import (
"errors" "errors"
"reflect"
"syscall" "syscall"
"unsafe" "unsafe"
@ -14,11 +13,7 @@ import (
// Uint16BufferToSlice wraps a uint16 pointer-and-length into a slice // Uint16BufferToSlice wraps a uint16 pointer-and-length into a slice
// for easier interop with Go APIs // for easier interop with Go APIs
func Uint16BufferToSlice(buffer *uint16, bufferLength int) (result []uint16) { func Uint16BufferToSlice(buffer *uint16, bufferLength int) (result []uint16) {
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&result)) result = unsafe.Slice(buffer, bufferLength)
hdr.Data = uintptr(unsafe.Pointer(buffer))
hdr.Cap = bufferLength
hdr.Len = bufferLength
return return
} }
@ -80,3 +75,9 @@ func ConvertStringSetToSlice(buf []byte) ([]string, error) {
} }
return nil, errors.New("string set malformed: missing null terminator at end of buffer") return nil, errors.New("string set malformed: missing null terminator at end of buffer")
} }
// ParseUtf16LE parses a UTF-16LE byte array into a string (without passing
// through a uint16 or rune array).
func ParseUtf16LE(b []byte) string {
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(&b[0])))
}

View File

@ -43,6 +43,7 @@ var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
modbindfltapi = windows.NewLazySystemDLL("bindfltapi.dll") modbindfltapi = windows.NewLazySystemDLL("bindfltapi.dll")
modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll") modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll")
modcimfs = windows.NewLazySystemDLL("cimfs.dll")
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modnetapi32 = windows.NewLazySystemDLL("netapi32.dll") modnetapi32 = windows.NewLazySystemDLL("netapi32.dll")
@ -55,6 +56,17 @@ var (
procCM_Get_Device_ID_ListA = modcfgmgr32.NewProc("CM_Get_Device_ID_ListA") procCM_Get_Device_ID_ListA = modcfgmgr32.NewProc("CM_Get_Device_ID_ListA")
procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA") procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA")
procCM_Locate_DevNodeW = modcfgmgr32.NewProc("CM_Locate_DevNodeW") procCM_Locate_DevNodeW = modcfgmgr32.NewProc("CM_Locate_DevNodeW")
procCimCloseImage = modcimfs.NewProc("CimCloseImage")
procCimCloseStream = modcimfs.NewProc("CimCloseStream")
procCimCommitImage = modcimfs.NewProc("CimCommitImage")
procCimCreateAlternateStream = modcimfs.NewProc("CimCreateAlternateStream")
procCimCreateFile = modcimfs.NewProc("CimCreateFile")
procCimCreateHardLink = modcimfs.NewProc("CimCreateHardLink")
procCimCreateImage = modcimfs.NewProc("CimCreateImage")
procCimDeletePath = modcimfs.NewProc("CimDeletePath")
procCimDismountImage = modcimfs.NewProc("CimDismountImage")
procCimMountImage = modcimfs.NewProc("CimMountImage")
procCimWriteStream = modcimfs.NewProc("CimWriteStream")
procSetJobCompartmentId = modiphlpapi.NewProc("SetJobCompartmentId") procSetJobCompartmentId = modiphlpapi.NewProc("SetJobCompartmentId")
procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole") procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole")
procCopyFileW = modkernel32.NewProc("CopyFileW") procCopyFileW = modkernel32.NewProc("CopyFileW")
@ -84,8 +96,16 @@ var (
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile") procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
procRtlNtStatusToDosError = modntdll.NewProc("RtlNtStatusToDosError") procRtlNtStatusToDosError = modntdll.NewProc("RtlNtStatusToDosError")
procORCloseHive = modoffreg.NewProc("ORCloseHive") procORCloseHive = modoffreg.NewProc("ORCloseHive")
procORCloseKey = modoffreg.NewProc("ORCloseKey")
procORCreateHive = modoffreg.NewProc("ORCreateHive") procORCreateHive = modoffreg.NewProc("ORCreateHive")
procORCreateKey = modoffreg.NewProc("ORCreateKey")
procORDeleteKey = modoffreg.NewProc("ORDeleteKey")
procORGetValue = modoffreg.NewProc("ORGetValue")
procORMergeHives = modoffreg.NewProc("ORMergeHives")
procOROpenHive = modoffreg.NewProc("OROpenHive")
procOROpenKey = modoffreg.NewProc("OROpenKey")
procORSaveHive = modoffreg.NewProc("ORSaveHive") procORSaveHive = modoffreg.NewProc("ORSaveHive")
procORSetValue = modoffreg.NewProc("ORSetValue")
) )
func LogonUser(username *uint16, domain *uint16, password *uint16, logonType uint32, logonProvider uint32, token *windows.Token) (err error) { func LogonUser(username *uint16, domain *uint16, password *uint16, logonType uint32, logonProvider uint32, token *windows.Token) (err error) {
@ -164,6 +184,229 @@ func _CMLocateDevNode(pdnDevInst *uint32, pDeviceID *uint16, uFlags uint32) (hr
return return
} }
func CimCloseImage(cimFSHandle FsHandle) (err error) {
err = procCimCloseImage.Find()
if err != nil {
return
}
syscall.Syscall(procCimCloseImage.Addr(), 1, uintptr(cimFSHandle), 0, 0)
return
}
func CimCloseStream(cimStreamHandle StreamHandle) (hr error) {
hr = procCimCloseStream.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procCimCloseStream.Addr(), 1, uintptr(cimStreamHandle), 0, 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimCommitImage(cimFSHandle FsHandle) (hr error) {
hr = procCimCommitImage.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procCimCommitImage.Addr(), 1, uintptr(cimFSHandle), 0, 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimCreateAlternateStream(cimFSHandle FsHandle, path string, size uint64, cimStreamHandle *StreamHandle) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(path)
if hr != nil {
return
}
return _CimCreateAlternateStream(cimFSHandle, _p0, size, cimStreamHandle)
}
func _CimCreateAlternateStream(cimFSHandle FsHandle, path *uint16, size uint64, cimStreamHandle *StreamHandle) (hr error) {
hr = procCimCreateAlternateStream.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procCimCreateAlternateStream.Addr(), 4, uintptr(cimFSHandle), uintptr(unsafe.Pointer(path)), uintptr(size), uintptr(unsafe.Pointer(cimStreamHandle)), 0, 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimCreateFile(cimFSHandle FsHandle, path string, file *CimFsFileMetadata, cimStreamHandle *StreamHandle) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(path)
if hr != nil {
return
}
return _CimCreateFile(cimFSHandle, _p0, file, cimStreamHandle)
}
func _CimCreateFile(cimFSHandle FsHandle, path *uint16, file *CimFsFileMetadata, cimStreamHandle *StreamHandle) (hr error) {
hr = procCimCreateFile.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procCimCreateFile.Addr(), 4, uintptr(cimFSHandle), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(file)), uintptr(unsafe.Pointer(cimStreamHandle)), 0, 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimCreateHardLink(cimFSHandle FsHandle, newPath string, oldPath string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(newPath)
if hr != nil {
return
}
var _p1 *uint16
_p1, hr = syscall.UTF16PtrFromString(oldPath)
if hr != nil {
return
}
return _CimCreateHardLink(cimFSHandle, _p0, _p1)
}
func _CimCreateHardLink(cimFSHandle FsHandle, newPath *uint16, oldPath *uint16) (hr error) {
hr = procCimCreateHardLink.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procCimCreateHardLink.Addr(), 3, uintptr(cimFSHandle), uintptr(unsafe.Pointer(newPath)), uintptr(unsafe.Pointer(oldPath)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimCreateImage(imagePath string, oldFSName *uint16, newFSName *uint16, cimFSHandle *FsHandle) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(imagePath)
if hr != nil {
return
}
return _CimCreateImage(_p0, oldFSName, newFSName, cimFSHandle)
}
func _CimCreateImage(imagePath *uint16, oldFSName *uint16, newFSName *uint16, cimFSHandle *FsHandle) (hr error) {
hr = procCimCreateImage.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procCimCreateImage.Addr(), 4, uintptr(unsafe.Pointer(imagePath)), uintptr(unsafe.Pointer(oldFSName)), uintptr(unsafe.Pointer(newFSName)), uintptr(unsafe.Pointer(cimFSHandle)), 0, 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimDeletePath(cimFSHandle FsHandle, path string) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(path)
if hr != nil {
return
}
return _CimDeletePath(cimFSHandle, _p0)
}
func _CimDeletePath(cimFSHandle FsHandle, path *uint16) (hr error) {
hr = procCimDeletePath.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procCimDeletePath.Addr(), 2, uintptr(cimFSHandle), uintptr(unsafe.Pointer(path)), 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimDismountImage(volumeID *g) (hr error) {
hr = procCimDismountImage.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procCimDismountImage.Addr(), 1, uintptr(unsafe.Pointer(volumeID)), 0, 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimMountImage(imagePath string, fsName string, flags uint32, volumeID *g) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(imagePath)
if hr != nil {
return
}
var _p1 *uint16
_p1, hr = syscall.UTF16PtrFromString(fsName)
if hr != nil {
return
}
return _CimMountImage(_p0, _p1, flags, volumeID)
}
func _CimMountImage(imagePath *uint16, fsName *uint16, flags uint32, volumeID *g) (hr error) {
hr = procCimMountImage.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procCimMountImage.Addr(), 4, uintptr(unsafe.Pointer(imagePath)), uintptr(unsafe.Pointer(fsName)), uintptr(flags), uintptr(unsafe.Pointer(volumeID)), 0, 0)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func CimWriteStream(cimStreamHandle StreamHandle, buffer uintptr, bufferSize uint32) (hr error) {
hr = procCimWriteStream.Find()
if hr != nil {
return
}
r0, _, _ := syscall.Syscall(procCimWriteStream.Addr(), 3, uintptr(cimStreamHandle), uintptr(buffer), uintptr(bufferSize))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
hr = syscall.Errno(r0)
}
return
}
func SetJobCompartmentId(handle windows.Handle, compartmentId uint32) (win32Err error) { func SetJobCompartmentId(handle windows.Handle, compartmentId uint32) (win32Err error) {
r0, _, _ := syscall.Syscall(procSetJobCompartmentId.Addr(), 2, uintptr(handle), uintptr(compartmentId), 0) r0, _, _ := syscall.Syscall(procSetJobCompartmentId.Addr(), 2, uintptr(handle), uintptr(compartmentId), 0)
if r0 != 0 { if r0 != 0 {
@ -381,35 +624,162 @@ func RtlNtStatusToDosError(status uint32) (winerr error) {
return return
} }
func ORCloseHive(key syscall.Handle) (regerrno error) { func ORCloseHive(handle ORHKey) (win32err error) {
r0, _, _ := syscall.Syscall(procORCloseHive.Addr(), 1, uintptr(key), 0, 0) r0, _, _ := syscall.Syscall(procORCloseHive.Addr(), 1, uintptr(handle), 0, 0)
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) win32err = syscall.Errno(r0)
} }
return return
} }
func ORCreateHive(key *syscall.Handle) (regerrno error) { func ORCloseKey(handle ORHKey) (win32err error) {
r0, _, _ := syscall.Syscall(procORCloseKey.Addr(), 1, uintptr(handle), 0, 0)
if r0 != 0 {
win32err = syscall.Errno(r0)
}
return
}
func ORCreateHive(key *ORHKey) (win32err error) {
r0, _, _ := syscall.Syscall(procORCreateHive.Addr(), 1, uintptr(unsafe.Pointer(key)), 0, 0) r0, _, _ := syscall.Syscall(procORCreateHive.Addr(), 1, uintptr(unsafe.Pointer(key)), 0, 0)
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) win32err = syscall.Errno(r0)
} }
return return
} }
func ORSaveHive(key syscall.Handle, file string, OsMajorVersion uint32, OsMinorVersion uint32) (regerrno error) { func ORCreateKey(handle ORHKey, subKey string, class uintptr, options uint32, securityDescriptor uintptr, result *ORHKey, disposition *uint32) (win32err error) {
var _p0 *uint16 var _p0 *uint16
_p0, regerrno = syscall.UTF16PtrFromString(file) _p0, win32err = syscall.UTF16PtrFromString(subKey)
if regerrno != nil { if win32err != nil {
return return
} }
return _ORSaveHive(key, _p0, OsMajorVersion, OsMinorVersion) return _ORCreateKey(handle, _p0, class, options, securityDescriptor, result, disposition)
} }
func _ORSaveHive(key syscall.Handle, file *uint16, OsMajorVersion uint32, OsMinorVersion uint32) (regerrno error) { func _ORCreateKey(handle ORHKey, subKey *uint16, class uintptr, options uint32, securityDescriptor uintptr, result *ORHKey, disposition *uint32) (win32err error) {
r0, _, _ := syscall.Syscall6(procORSaveHive.Addr(), 4, uintptr(key), uintptr(unsafe.Pointer(file)), uintptr(OsMajorVersion), uintptr(OsMinorVersion), 0, 0) r0, _, _ := syscall.Syscall9(procORCreateKey.Addr(), 7, uintptr(handle), uintptr(unsafe.Pointer(subKey)), uintptr(class), uintptr(options), uintptr(securityDescriptor), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition)), 0, 0)
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) win32err = syscall.Errno(r0)
}
return
}
func ORDeleteKey(handle ORHKey, subKey string) (win32err error) {
var _p0 *uint16
_p0, win32err = syscall.UTF16PtrFromString(subKey)
if win32err != nil {
return
}
return _ORDeleteKey(handle, _p0)
}
func _ORDeleteKey(handle ORHKey, subKey *uint16) (win32err error) {
r0, _, _ := syscall.Syscall(procORDeleteKey.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(subKey)), 0)
if r0 != 0 {
win32err = syscall.Errno(r0)
}
return
}
func ORGetValue(handle ORHKey, subKey string, value string, valueType *uint32, data *byte, dataLen *uint32) (win32err error) {
var _p0 *uint16
_p0, win32err = syscall.UTF16PtrFromString(subKey)
if win32err != nil {
return
}
var _p1 *uint16
_p1, win32err = syscall.UTF16PtrFromString(value)
if win32err != nil {
return
}
return _ORGetValue(handle, _p0, _p1, valueType, data, dataLen)
}
func _ORGetValue(handle ORHKey, subKey *uint16, value *uint16, valueType *uint32, data *byte, dataLen *uint32) (win32err error) {
r0, _, _ := syscall.Syscall6(procORGetValue.Addr(), 6, uintptr(handle), uintptr(unsafe.Pointer(subKey)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(valueType)), uintptr(unsafe.Pointer(data)), uintptr(unsafe.Pointer(dataLen)))
if r0 != 0 {
win32err = syscall.Errno(r0)
}
return
}
func ORMergeHives(hiveHandles []ORHKey, result *ORHKey) (win32err error) {
var _p0 *ORHKey
if len(hiveHandles) > 0 {
_p0 = &hiveHandles[0]
}
r0, _, _ := syscall.Syscall(procORMergeHives.Addr(), 3, uintptr(unsafe.Pointer(_p0)), uintptr(len(hiveHandles)), uintptr(unsafe.Pointer(result)))
if r0 != 0 {
win32err = syscall.Errno(r0)
}
return
}
func OROpenHive(hivePath string, result *ORHKey) (win32err error) {
var _p0 *uint16
_p0, win32err = syscall.UTF16PtrFromString(hivePath)
if win32err != nil {
return
}
return _OROpenHive(_p0, result)
}
func _OROpenHive(hivePath *uint16, result *ORHKey) (win32err error) {
r0, _, _ := syscall.Syscall(procOROpenHive.Addr(), 2, uintptr(unsafe.Pointer(hivePath)), uintptr(unsafe.Pointer(result)), 0)
if r0 != 0 {
win32err = syscall.Errno(r0)
}
return
}
func OROpenKey(handle ORHKey, subKey string, result *ORHKey) (win32err error) {
var _p0 *uint16
_p0, win32err = syscall.UTF16PtrFromString(subKey)
if win32err != nil {
return
}
return _OROpenKey(handle, _p0, result)
}
func _OROpenKey(handle ORHKey, subKey *uint16, result *ORHKey) (win32err error) {
r0, _, _ := syscall.Syscall(procOROpenKey.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(subKey)), uintptr(unsafe.Pointer(result)))
if r0 != 0 {
win32err = syscall.Errno(r0)
}
return
}
func ORSaveHive(handle ORHKey, hivePath string, osMajorVersion uint32, osMinorVersion uint32) (win32err error) {
var _p0 *uint16
_p0, win32err = syscall.UTF16PtrFromString(hivePath)
if win32err != nil {
return
}
return _ORSaveHive(handle, _p0, osMajorVersion, osMinorVersion)
}
func _ORSaveHive(handle ORHKey, hivePath *uint16, osMajorVersion uint32, osMinorVersion uint32) (win32err error) {
r0, _, _ := syscall.Syscall6(procORSaveHive.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(hivePath)), uintptr(osMajorVersion), uintptr(osMinorVersion), 0, 0)
if r0 != 0 {
win32err = syscall.Errno(r0)
}
return
}
func ORSetValue(handle ORHKey, valueName string, valueType uint32, data *byte, dataLen uint32) (win32err error) {
var _p0 *uint16
_p0, win32err = syscall.UTF16PtrFromString(valueName)
if win32err != nil {
return
}
return _ORSetValue(handle, _p0, valueType, data, dataLen)
}
func _ORSetValue(handle ORHKey, valueName *uint16, valueType uint32, data *byte, dataLen uint32) (win32err error) {
r0, _, _ := syscall.Syscall6(procORSetValue.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(valueName)), uintptr(valueType), uintptr(unsafe.Pointer(data)), uintptr(dataLen), 0)
if r0 != 0 {
win32err = syscall.Errno(r0)
} }
return return
} }

View File

@ -32,6 +32,7 @@ func CreateScratchLayer(info DriverInfo, layerId, parentId string, parentLayerPa
func DeactivateLayer(info DriverInfo, id string) error { func DeactivateLayer(info DriverInfo, id string) error {
return wclayer.DeactivateLayer(context.Background(), layerPath(&info, id)) return wclayer.DeactivateLayer(context.Background(), layerPath(&info, id))
} }
func DestroyLayer(info DriverInfo, id string) error { func DestroyLayer(info DriverInfo, id string) error {
return wclayer.DestroyLayer(context.Background(), layerPath(&info, id)) return wclayer.DestroyLayer(context.Background(), layerPath(&info, id))
} }

View File

@ -5,6 +5,7 @@ import (
"sync" "sync"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
) )
// OSVersion is a wrapper for Windows version information // OSVersion is a wrapper for Windows version information
@ -25,16 +26,15 @@ var (
// The calling application must be manifested to get the correct version information. // The calling application must be manifested to get the correct version information.
func Get() OSVersion { func Get() OSVersion {
once.Do(func() { once.Do(func() {
var err error v := *windows.RtlGetVersion()
osv = OSVersion{} osv = OSVersion{}
osv.Version, err = windows.GetVersion() osv.MajorVersion = uint8(v.MajorVersion)
if err != nil { osv.MinorVersion = uint8(v.MinorVersion)
// GetVersion never fails. osv.Build = uint16(v.BuildNumber)
panic(err) // Fill version value so that existing clients don't break
} osv.Version = v.BuildNumber << 16
osv.MajorVersion = uint8(osv.Version & 0xFF) osv.Version = osv.Version | (uint32(v.MinorVersion) << 8)
osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) osv.Version = osv.Version | v.MajorVersion
osv.Build = uint16(osv.Version >> 16)
}) })
return osv return osv
} }
@ -57,3 +57,18 @@ func (osv OSVersion) String() string {
func (osv OSVersion) ToString() string { func (osv OSVersion) ToString() string {
return osv.String() return osv.String()
} }
// Running `cmd /c ver` shows something like "10.0.20348.1000". The last component ("1000") is the revision
// number
func BuildRevision() (uint32, error) {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err != nil {
return 0, fmt.Errorf("open `CurrentVersion` registry key: %w", err)
}
defer k.Close()
s, _, err := k.GetIntegerValue("UBR")
if err != nil {
return 0, fmt.Errorf("read `UBR` from registry: %w", err)
}
return uint32(s), nil
}

View File

@ -5,14 +5,9 @@
package filemutex package filemutex
import ( import (
"syscall"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
var errLockUnlocked syscall.Errno = 0x9E
// FileMutex is similar to sync.RWMutex, but also synchronizes across processes. // FileMutex is similar to sync.RWMutex, but also synchronizes across processes.
// This implementation is based on flock syscall. // This implementation is based on flock syscall.
type FileMutex struct { type FileMutex struct {
@ -58,8 +53,11 @@ func (m *FileMutex) RUnlock() error {
// Close unlocks the lock and closes the underlying file descriptor. // Close unlocks the lock and closes the underlying file descriptor.
func (m *FileMutex) Close() error { func (m *FileMutex) Close() error {
if err := windows.UnlockFileEx(m.fd, 0, 1, 0, &windows.Overlapped{}); err != nil && err != errLockUnlocked { // See comment section of https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex
return err // It's recommended to unlock a file explicitly before closing in order to
} // avoid delays, but all locks are definitly unlocked when closing a file.
// So any unlocking error can be ignored.
_ = windows.UnlockFileEx(m.fd, 0, 1, 0, &windows.Overlapped{})
return windows.Close(m.fd) return windows.Close(m.fd)
} }

File diff suppressed because it is too large Load Diff

View File

@ -14,4 +14,4 @@
limitations under the License. limitations under the License.
*/ */
package v1 package stats

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
file { file {
name: "github.com/containerd/cgroups/stats/v1/metrics.proto" name: "github.com/containerd/cgroups/cgroup1/stats/metrics.proto"
package: "io.containerd.cgroups.v1" package: "io.containerd.cgroups.v1"
dependency: "gogoproto/gogo.proto"
message_type { message_type {
name: "Metrics" name: "Metrics"
field { field {
@ -26,9 +25,6 @@ file {
label: LABEL_OPTIONAL label: LABEL_OPTIONAL
type: TYPE_MESSAGE type: TYPE_MESSAGE
type_name: ".io.containerd.cgroups.v1.CPUStat" type_name: ".io.containerd.cgroups.v1.CPUStat"
options {
65004: "CPU"
}
json_name: "cpu" json_name: "cpu"
} }
field { field {
@ -175,9 +171,6 @@ file {
number: 4 number: 4
label: LABEL_REPEATED label: LABEL_REPEATED
type: TYPE_UINT64 type: TYPE_UINT64
options {
65004: "PerCPU"
}
json_name: "perCpu" json_name: "perCpu"
} }
} }
@ -219,9 +212,6 @@ file {
number: 2 number: 2
label: LABEL_OPTIONAL label: LABEL_OPTIONAL
type: TYPE_UINT64 type: TYPE_UINT64
options {
65004: "RSS"
}
json_name: "rss" json_name: "rss"
} }
field { field {
@ -229,9 +219,6 @@ file {
number: 3 number: 3
label: LABEL_OPTIONAL label: LABEL_OPTIONAL
type: TYPE_UINT64 type: TYPE_UINT64
options {
65004: "RSSHuge"
}
json_name: "rssHuge" json_name: "rssHuge"
} }
field { field {
@ -344,9 +331,6 @@ file {
number: 19 number: 19
label: LABEL_OPTIONAL label: LABEL_OPTIONAL
type: TYPE_UINT64 type: TYPE_UINT64
options {
65004: "TotalRSS"
}
json_name: "totalRss" json_name: "totalRss"
} }
field { field {
@ -354,9 +338,6 @@ file {
number: 20 number: 20
label: LABEL_OPTIONAL label: LABEL_OPTIONAL
type: TYPE_UINT64 type: TYPE_UINT64
options {
65004: "TotalRSSHuge"
}
json_name: "totalRssHuge" json_name: "totalRssHuge"
} }
field { field {
@ -473,9 +454,6 @@ file {
label: LABEL_OPTIONAL label: LABEL_OPTIONAL
type: TYPE_MESSAGE type: TYPE_MESSAGE
type_name: ".io.containerd.cgroups.v1.MemoryEntry" type_name: ".io.containerd.cgroups.v1.MemoryEntry"
options {
65004: "KernelTCP"
}
json_name: "kernelTcp" json_name: "kernelTcp"
} }
} }
@ -786,5 +764,8 @@ file {
json_name: "nrIoWait" json_name: "nrIoWait"
} }
} }
options {
go_package: "github.com/containerd/cgroups/cgroup1/stats"
}
syntax: "proto3" syntax: "proto3"
} }

View File

@ -2,12 +2,12 @@ syntax = "proto3";
package io.containerd.cgroups.v1; package io.containerd.cgroups.v1;
import "gogoproto/gogo.proto"; option go_package = "github.com/containerd/cgroups/cgroup1/stats";
message Metrics { message Metrics {
repeated HugetlbStat hugetlb = 1; repeated HugetlbStat hugetlb = 1;
PidsStat pids = 2; PidsStat pids = 2;
CPUStat cpu = 3 [(gogoproto.customname) = "CPU"]; CPUStat cpu = 3;
MemoryStat memory = 4; MemoryStat memory = 4;
BlkIOStat blkio = 5; BlkIOStat blkio = 5;
RdmaStat rdma = 6; RdmaStat rdma = 6;
@ -38,7 +38,7 @@ message CPUUsage {
uint64 total = 1; uint64 total = 1;
uint64 kernel = 2; uint64 kernel = 2;
uint64 user = 3; uint64 user = 3;
repeated uint64 per_cpu = 4 [(gogoproto.customname) = "PerCPU"]; repeated uint64 per_cpu = 4;
} }
@ -50,8 +50,8 @@ message Throttle {
message MemoryStat { message MemoryStat {
uint64 cache = 1; uint64 cache = 1;
uint64 rss = 2 [(gogoproto.customname) = "RSS"]; uint64 rss = 2;
uint64 rss_huge = 3 [(gogoproto.customname) = "RSSHuge"]; uint64 rss_huge = 3;
uint64 mapped_file = 4; uint64 mapped_file = 4;
uint64 dirty = 5; uint64 dirty = 5;
uint64 writeback = 6; uint64 writeback = 6;
@ -67,8 +67,8 @@ message MemoryStat {
uint64 hierarchical_memory_limit = 16; uint64 hierarchical_memory_limit = 16;
uint64 hierarchical_swap_limit = 17; uint64 hierarchical_swap_limit = 17;
uint64 total_cache = 18; uint64 total_cache = 18;
uint64 total_rss = 19 [(gogoproto.customname) = "TotalRSS"]; uint64 total_rss = 19;
uint64 total_rss_huge = 20 [(gogoproto.customname) = "TotalRSSHuge"]; uint64 total_rss_huge = 20;
uint64 total_mapped_file = 21; uint64 total_mapped_file = 21;
uint64 total_dirty = 22; uint64 total_dirty = 22;
uint64 total_writeback = 23; uint64 total_writeback = 23;
@ -84,7 +84,7 @@ message MemoryStat {
MemoryEntry usage = 33; MemoryEntry usage = 33;
MemoryEntry swap = 34; MemoryEntry swap = 34;
MemoryEntry kernel = 35; MemoryEntry kernel = 35;
MemoryEntry kernel_tcp = 36 [(gogoproto.customname) = "KernelTCP"]; MemoryEntry kernel_tcp = 36;
} }

View File

@ -1,16 +0,0 @@
Docker
Copyright 2012-2015 Docker, Inc.
This product includes software developed at Docker, Inc. (https://www.docker.com).
The following is courtesy of our legal counsel:
Use and transfer of Docker may be subject to certain restrictions by the
United States and other governments.
It is your responsibility to ensure that your use and/or transfer does not
violate applicable laws.
For more information, please see https://www.bis.doc.gov
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.

13
vendor/github.com/containerd/errdefs/README.md generated vendored Normal file
View File

@ -0,0 +1,13 @@
# errdefs
A Go package for defining and checking common containerd errors.
## Project details
**errdefs** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View File

@ -91,11 +91,12 @@ logr design but also left out some parts and changed others:
| Adding a name to a logger | `WithName` | no API | | Adding a name to a logger | `WithName` | no API |
| Modify verbosity of log entries in a call chain | `V` | no API | | Modify verbosity of log entries in a call chain | `V` | no API |
| Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` | | Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` |
| Pass context for extracting additional values | no API | API variants like `InfoCtx` |
The high-level slog API is explicitly meant to be one of many different APIs The high-level slog API is explicitly meant to be one of many different APIs
that can be layered on top of a shared `slog.Handler`. logr is one such that can be layered on top of a shared `slog.Handler`. logr is one such
alternative API, with [interoperability](#slog-interoperability) provided by the [`slogr`](slogr) alternative API, with [interoperability](#slog-interoperability) provided by
package. some conversion functions.
### Inspiration ### Inspiration
@ -145,24 +146,24 @@ There are implementations for the following logging libraries:
## slog interoperability ## slog interoperability
Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler` Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler`
and using the `slog.Logger` API with a `logr.LogSink`. [slogr](./slogr) provides `NewLogr` and and using the `slog.Logger` API with a `logr.LogSink`. `FromSlogHandler` and
`NewSlogHandler` API calls to convert between a `logr.Logger` and a `slog.Handler`. `ToSlogHandler` convert between a `logr.Logger` and a `slog.Handler`.
As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level
slog API. `slogr` itself leaves that to the caller. slog API.
## Using a `logr.Sink` as backend for slog ### Using a `logr.LogSink` as backend for slog
Ideally, a logr sink implementation should support both logr and slog by Ideally, a logr sink implementation should support both logr and slog by
implementing both the normal logr interface(s) and `slogr.SlogSink`. Because implementing both the normal logr interface(s) and `SlogSink`. Because
of a conflict in the parameters of the common `Enabled` method, it is [not of a conflict in the parameters of the common `Enabled` method, it is [not
possible to implement both slog.Handler and logr.Sink in the same possible to implement both slog.Handler and logr.Sink in the same
type](https://github.com/golang/go/issues/59110). type](https://github.com/golang/go/issues/59110).
If both are supported, log calls can go from the high-level APIs to the backend If both are supported, log calls can go from the high-level APIs to the backend
without the need to convert parameters. `NewLogr` and `NewSlogHandler` can without the need to convert parameters. `FromSlogHandler` and `ToSlogHandler` can
convert back and forth without adding additional wrappers, with one exception: convert back and forth without adding additional wrappers, with one exception:
when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then
`NewSlogHandler` has to use a wrapper which adjusts the verbosity for future `ToSlogHandler` has to use a wrapper which adjusts the verbosity for future
log calls. log calls.
Such an implementation should also support values that implement specific Such an implementation should also support values that implement specific
@ -187,13 +188,13 @@ Not supporting slog has several drawbacks:
These drawbacks are severe enough that applications using a mixture of slog and These drawbacks are severe enough that applications using a mixture of slog and
logr should switch to a different backend. logr should switch to a different backend.
## Using a `slog.Handler` as backend for logr ### Using a `slog.Handler` as backend for logr
Using a plain `slog.Handler` without support for logr works better than the Using a plain `slog.Handler` without support for logr works better than the
other direction: other direction:
- All logr verbosity levels can be mapped 1:1 to their corresponding slog level - All logr verbosity levels can be mapped 1:1 to their corresponding slog level
by negating them. by negating them.
- Stack unwinding is done by the `slogr.SlogSink` and the resulting program - Stack unwinding is done by the `SlogSink` and the resulting program
counter is passed to the `slog.Handler`. counter is passed to the `slog.Handler`.
- Names added via `Logger.WithName` are gathered and recorded in an additional - Names added via `Logger.WithName` are gathered and recorded in an additional
attribute with `logger` as key and the names separated by slash as value. attribute with `logger` as key and the names separated by slash as value.
@ -205,27 +206,39 @@ ideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility
with logr implementations without slog support is not important, then with logr implementations without slog support is not important, then
`slog.Valuer` is sufficient. `slog.Valuer` is sufficient.
## Context support for slog ### Context support for slog
Storing a logger in a `context.Context` is not supported by Storing a logger in a `context.Context` is not supported by
slog. `logr.NewContext` and `logr.FromContext` can be used with slog like this slog. `NewContextWithSlogLogger` and `FromContextAsSlogLogger` can be
to fill this gap: used to fill this gap. They store and retrieve a `slog.Logger` pointer
under the same context key that is also used by `NewContext` and
`FromContext` for `logr.Logger` value.
func HandlerFromContext(ctx context.Context) slog.Handler { When `NewContextWithSlogLogger` is followed by `FromContext`, the latter will
logger, err := logr.FromContext(ctx) automatically convert the `slog.Logger` to a
if err == nil { `logr.Logger`. `FromContextAsSlogLogger` does the same for the other direction.
return slogr.NewSlogHandler(logger)
}
return slog.Default().Handler()
}
func ContextWithHandler(ctx context.Context, handler slog.Handler) context.Context { With this approach, binaries which use either slog or logr are as efficient as
return logr.NewContext(ctx, slogr.NewLogr(handler)) possible with no unnecessary allocations. This is also why the API stores a
} `slog.Logger` pointer: when storing a `slog.Handler`, creating a `slog.Logger`
on retrieval would need to allocate one.
The downside is that storing and retrieving a `slog.Handler` needs more The downside is that switching back and forth needs more allocations. Because
allocations compared to using a `logr.Logger`. Therefore the recommendation is logr is the API that is already in use by different packages, in particular
to use the `logr.Logger` API in code which uses contextual logging. Kubernetes, the recommendation is to use the `logr.Logger` API in code which
uses contextual logging.
An alternative to adding values to a logger and storing that logger in the
context is to store the values in the context and to configure a logging
backend to extract those values when emitting log entries. This only works when
log calls are passed the context, which is not supported by the logr API.
With the slog API, it is possible, but not
required. https://github.com/veqryn/slog-context is a package for slog which
provides additional support code for this approach. It also contains wrappers
for the context functions in logr, so developers who prefer to not use the logr
APIs directly can use those instead and the resulting code will still be
interoperable with logr.
## FAQ ## FAQ

33
vendor/github.com/go-logr/logr/context.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright 2023 The logr Authors.
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
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logr
// contextKey is how we find Loggers in a context.Context. With Go < 1.21,
// the value is always a Logger value. With Go >= 1.21, the value can be a
// Logger value or a slog.Logger pointer.
type contextKey struct{}
// notFoundError exists to carry an IsNotFound method.
type notFoundError struct{}
func (notFoundError) Error() string {
return "no logr.Logger was present"
}
func (notFoundError) IsNotFound() bool {
return true
}

49
vendor/github.com/go-logr/logr/context_noslog.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
//go:build !go1.21
// +build !go1.21
/*
Copyright 2019 The logr Authors.
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
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logr
import (
"context"
)
// FromContext returns a Logger from ctx or an error if no Logger is found.
func FromContext(ctx context.Context) (Logger, error) {
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
return v, nil
}
return Logger{}, notFoundError{}
}
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
// returns a Logger that discards all log messages.
func FromContextOrDiscard(ctx context.Context) Logger {
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
return v
}
return Discard()
}
// NewContext returns a new Context, derived from ctx, which carries the
// provided Logger.
func NewContext(ctx context.Context, logger Logger) context.Context {
return context.WithValue(ctx, contextKey{}, logger)
}

83
vendor/github.com/go-logr/logr/context_slog.go generated vendored Normal file
View File

@ -0,0 +1,83 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2019 The logr Authors.
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
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logr
import (
"context"
"fmt"
"log/slog"
)
// FromContext returns a Logger from ctx or an error if no Logger is found.
func FromContext(ctx context.Context) (Logger, error) {
v := ctx.Value(contextKey{})
if v == nil {
return Logger{}, notFoundError{}
}
switch v := v.(type) {
case Logger:
return v, nil
case *slog.Logger:
return FromSlogHandler(v.Handler()), nil
default:
// Not reached.
panic(fmt.Sprintf("unexpected value type for logr context key: %T", v))
}
}
// FromContextAsSlogLogger returns a slog.Logger from ctx or nil if no such Logger is found.
func FromContextAsSlogLogger(ctx context.Context) *slog.Logger {
v := ctx.Value(contextKey{})
if v == nil {
return nil
}
switch v := v.(type) {
case Logger:
return slog.New(ToSlogHandler(v))
case *slog.Logger:
return v
default:
// Not reached.
panic(fmt.Sprintf("unexpected value type for logr context key: %T", v))
}
}
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
// returns a Logger that discards all log messages.
func FromContextOrDiscard(ctx context.Context) Logger {
if logger, err := FromContext(ctx); err == nil {
return logger
}
return Discard()
}
// NewContext returns a new Context, derived from ctx, which carries the
// provided Logger.
func NewContext(ctx context.Context, logger Logger) context.Context {
return context.WithValue(ctx, contextKey{}, logger)
}
// NewContextWithSlogLogger returns a new Context, derived from ctx, which carries the
// provided slog.Logger.
func NewContextWithSlogLogger(ctx context.Context, logger *slog.Logger) context.Context {
return context.WithValue(ctx, contextKey{}, logger)
}

View File

@ -100,6 +100,11 @@ type Options struct {
// details, see docs for Go's time.Layout. // details, see docs for Go's time.Layout.
TimestampFormat string TimestampFormat string
// LogInfoLevel tells funcr what key to use to log the info level.
// If not specified, the info level will be logged as "level".
// If this is set to "", the info level will not be logged at all.
LogInfoLevel *string
// Verbosity tells funcr which V logs to produce. Higher values enable // Verbosity tells funcr which V logs to produce. Higher values enable
// more logs. Info logs at or below this level will be written, while logs // more logs. Info logs at or below this level will be written, while logs
// above this level will be discarded. // above this level will be discarded.
@ -213,6 +218,10 @@ func newFormatter(opts Options, outfmt outputFormat) Formatter {
if opts.MaxLogDepth == 0 { if opts.MaxLogDepth == 0 {
opts.MaxLogDepth = defaultMaxLogDepth opts.MaxLogDepth = defaultMaxLogDepth
} }
if opts.LogInfoLevel == nil {
opts.LogInfoLevel = new(string)
*opts.LogInfoLevel = "level"
}
f := Formatter{ f := Formatter{
outputFormat: outfmt, outputFormat: outfmt,
prefix: "", prefix: "",
@ -231,8 +240,11 @@ type Formatter struct {
prefix string prefix string
values []any values []any
valuesStr string valuesStr string
parentValuesStr string
depth int depth int
opts *Options opts *Options
group string // for slog groups
groupDepth int
} }
// outputFormat indicates which outputFormat to use. // outputFormat indicates which outputFormat to use.
@ -253,33 +265,62 @@ func (f Formatter) render(builtins, args []any) string {
// Empirically bytes.Buffer is faster than strings.Builder for this. // Empirically bytes.Buffer is faster than strings.Builder for this.
buf := bytes.NewBuffer(make([]byte, 0, 1024)) buf := bytes.NewBuffer(make([]byte, 0, 1024))
if f.outputFormat == outputJSON { if f.outputFormat == outputJSON {
buf.WriteByte('{') buf.WriteByte('{') // for the whole line
} }
vals := builtins vals := builtins
if hook := f.opts.RenderBuiltinsHook; hook != nil { if hook := f.opts.RenderBuiltinsHook; hook != nil {
vals = hook(f.sanitize(vals)) vals = hook(f.sanitize(vals))
} }
f.flatten(buf, vals, false, false) // keys are ours, no need to escape f.flatten(buf, vals, false, false) // keys are ours, no need to escape
continuing := len(builtins) > 0 continuing := len(builtins) > 0
if len(f.valuesStr) > 0 {
if f.parentValuesStr != "" {
if continuing { if continuing {
if f.outputFormat == outputJSON { buf.WriteByte(f.comma())
buf.WriteByte(',')
} else {
buf.WriteByte(' ')
}
} }
buf.WriteString(f.parentValuesStr)
continuing = true continuing = true
buf.WriteString(f.valuesStr)
} }
groupDepth := f.groupDepth
if f.group != "" {
if f.valuesStr != "" || len(args) != 0 {
if continuing {
buf.WriteByte(f.comma())
}
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
buf.WriteByte(f.colon())
buf.WriteByte('{') // for the group
continuing = false
} else {
// The group was empty
groupDepth--
}
}
if f.valuesStr != "" {
if continuing {
buf.WriteByte(f.comma())
}
buf.WriteString(f.valuesStr)
continuing = true
}
vals = args vals = args
if hook := f.opts.RenderArgsHook; hook != nil { if hook := f.opts.RenderArgsHook; hook != nil {
vals = hook(f.sanitize(vals)) vals = hook(f.sanitize(vals))
} }
f.flatten(buf, vals, continuing, true) // escape user-provided keys f.flatten(buf, vals, continuing, true) // escape user-provided keys
if f.outputFormat == outputJSON {
buf.WriteByte('}') for i := 0; i < groupDepth; i++ {
buf.WriteByte('}') // for the groups
} }
if f.outputFormat == outputJSON {
buf.WriteByte('}') // for the whole line
}
return buf.String() return buf.String()
} }
@ -298,9 +339,16 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
if len(kvList)%2 != 0 { if len(kvList)%2 != 0 {
kvList = append(kvList, noValue) kvList = append(kvList, noValue)
} }
copied := false
for i := 0; i < len(kvList); i += 2 { for i := 0; i < len(kvList); i += 2 {
k, ok := kvList[i].(string) k, ok := kvList[i].(string)
if !ok { if !ok {
if !copied {
newList := make([]any, len(kvList))
copy(newList, kvList)
kvList = newList
copied = true
}
k = f.nonStringKey(kvList[i]) k = f.nonStringKey(kvList[i])
kvList[i] = k kvList[i] = k
} }
@ -308,7 +356,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
if i > 0 || continuing { if i > 0 || continuing {
if f.outputFormat == outputJSON { if f.outputFormat == outputJSON {
buf.WriteByte(',') buf.WriteByte(f.comma())
} else { } else {
// In theory the format could be something we don't understand. In // In theory the format could be something we don't understand. In
// practice, we control it, so it won't be. // practice, we control it, so it won't be.
@ -316,24 +364,35 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
} }
} }
if escapeKeys { buf.WriteString(f.quoted(k, escapeKeys))
buf.WriteString(prettyString(k)) buf.WriteByte(f.colon())
} else {
// this is faster
buf.WriteByte('"')
buf.WriteString(k)
buf.WriteByte('"')
}
if f.outputFormat == outputJSON {
buf.WriteByte(':')
} else {
buf.WriteByte('=')
}
buf.WriteString(f.pretty(v)) buf.WriteString(f.pretty(v))
} }
return kvList return kvList
} }
func (f Formatter) quoted(str string, escape bool) string {
if escape {
return prettyString(str)
}
// this is faster
return `"` + str + `"`
}
func (f Formatter) comma() byte {
if f.outputFormat == outputJSON {
return ','
}
return ' '
}
func (f Formatter) colon() byte {
if f.outputFormat == outputJSON {
return ':'
}
return '='
}
func (f Formatter) pretty(value any) string { func (f Formatter) pretty(value any) string {
return f.prettyWithFlags(value, 0, 0) return f.prettyWithFlags(value, 0, 0)
} }
@ -407,12 +466,12 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
} }
for i := 0; i < len(v); i += 2 { for i := 0; i < len(v); i += 2 {
if i > 0 { if i > 0 {
buf.WriteByte(',') buf.WriteByte(f.comma())
} }
k, _ := v[i].(string) // sanitize() above means no need to check success k, _ := v[i].(string) // sanitize() above means no need to check success
// arbitrary keys might need escaping // arbitrary keys might need escaping
buf.WriteString(prettyString(k)) buf.WriteString(prettyString(k))
buf.WriteByte(':') buf.WriteByte(f.colon())
buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1)) buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1))
} }
if flags&flagRawStruct == 0 { if flags&flagRawStruct == 0 {
@ -481,7 +540,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
continue continue
} }
if printComma { if printComma {
buf.WriteByte(',') buf.WriteByte(f.comma())
} }
printComma = true // if we got here, we are rendering a field printComma = true // if we got here, we are rendering a field
if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" { if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" {
@ -492,10 +551,8 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
name = fld.Name name = fld.Name
} }
// field names can't contain characters which need escaping // field names can't contain characters which need escaping
buf.WriteByte('"') buf.WriteString(f.quoted(name, false))
buf.WriteString(name) buf.WriteByte(f.colon())
buf.WriteByte('"')
buf.WriteByte(':')
buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1)) buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1))
} }
if flags&flagRawStruct == 0 { if flags&flagRawStruct == 0 {
@ -520,7 +577,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
buf.WriteByte('[') buf.WriteByte('[')
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
if i > 0 { if i > 0 {
buf.WriteByte(',') buf.WriteByte(f.comma())
} }
e := v.Index(i) e := v.Index(i)
buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1)) buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1))
@ -534,7 +591,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
i := 0 i := 0
for it.Next() { for it.Next() {
if i > 0 { if i > 0 {
buf.WriteByte(',') buf.WriteByte(f.comma())
} }
// If a map key supports TextMarshaler, use it. // If a map key supports TextMarshaler, use it.
keystr := "" keystr := ""
@ -556,7 +613,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
} }
} }
buf.WriteString(keystr) buf.WriteString(keystr)
buf.WriteByte(':') buf.WriteByte(f.colon())
buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1)) buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1))
i++ i++
} }
@ -706,6 +763,53 @@ func (f Formatter) sanitize(kvList []any) []any {
return kvList return kvList
} }
// startGroup opens a new group scope (basically a sub-struct), which locks all
// the current saved values and starts them anew. This is needed to satisfy
// slog.
func (f *Formatter) startGroup(group string) {
// Unnamed groups are just inlined.
if group == "" {
return
}
// Any saved values can no longer be changed.
buf := bytes.NewBuffer(make([]byte, 0, 1024))
continuing := false
if f.parentValuesStr != "" {
buf.WriteString(f.parentValuesStr)
continuing = true
}
if f.group != "" && f.valuesStr != "" {
if continuing {
buf.WriteByte(f.comma())
}
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
buf.WriteByte(f.colon())
buf.WriteByte('{') // for the group
continuing = false
}
if f.valuesStr != "" {
if continuing {
buf.WriteByte(f.comma())
}
buf.WriteString(f.valuesStr)
}
// NOTE: We don't close the scope here - that's done later, when a log line
// is actually rendered (because we have N scopes to close).
f.parentValuesStr = buf.String()
// Start collecting new values.
f.group = group
f.groupDepth++
f.valuesStr = ""
f.values = nil
}
// Init configures this Formatter from runtime info, such as the call depth // Init configures this Formatter from runtime info, such as the call depth
// imposed by logr itself. // imposed by logr itself.
// Note that this receiver is a pointer, so depth can be saved. // Note that this receiver is a pointer, so depth can be saved.
@ -740,7 +844,10 @@ func (f Formatter) FormatInfo(level int, msg string, kvList []any) (prefix, args
if policy := f.opts.LogCaller; policy == All || policy == Info { if policy := f.opts.LogCaller; policy == All || policy == Info {
args = append(args, "caller", f.caller()) args = append(args, "caller", f.caller())
} }
args = append(args, "level", level, "msg", msg) if key := *f.opts.LogInfoLevel; key != "" {
args = append(args, key, level)
}
args = append(args, "msg", msg)
return prefix, f.render(args, kvList) return prefix, f.render(args, kvList)
} }

105
vendor/github.com/go-logr/logr/funcr/slogsink.go generated vendored Normal file
View File

@ -0,0 +1,105 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 The logr Authors.
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
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package funcr
import (
"context"
"log/slog"
"github.com/go-logr/logr"
)
var _ logr.SlogSink = &fnlogger{}
const extraSlogSinkDepth = 3 // 2 for slog, 1 for SlogSink
func (l fnlogger) Handle(_ context.Context, record slog.Record) error {
kvList := make([]any, 0, 2*record.NumAttrs())
record.Attrs(func(attr slog.Attr) bool {
kvList = attrToKVs(attr, kvList)
return true
})
if record.Level >= slog.LevelError {
l.WithCallDepth(extraSlogSinkDepth).Error(nil, record.Message, kvList...)
} else {
level := l.levelFromSlog(record.Level)
l.WithCallDepth(extraSlogSinkDepth).Info(level, record.Message, kvList...)
}
return nil
}
func (l fnlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
kvList := make([]any, 0, 2*len(attrs))
for _, attr := range attrs {
kvList = attrToKVs(attr, kvList)
}
l.AddValues(kvList)
return &l
}
func (l fnlogger) WithGroup(name string) logr.SlogSink {
l.startGroup(name)
return &l
}
// attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups
// and other details of slog.
func attrToKVs(attr slog.Attr, kvList []any) []any {
attrVal := attr.Value.Resolve()
if attrVal.Kind() == slog.KindGroup {
groupVal := attrVal.Group()
grpKVs := make([]any, 0, 2*len(groupVal))
for _, attr := range groupVal {
grpKVs = attrToKVs(attr, grpKVs)
}
if attr.Key == "" {
// slog says we have to inline these
kvList = append(kvList, grpKVs...)
} else {
kvList = append(kvList, attr.Key, PseudoStruct(grpKVs))
}
} else if attr.Key != "" {
kvList = append(kvList, attr.Key, attrVal.Any())
}
return kvList
}
// levelFromSlog adjusts the level by the logger's verbosity and negates it.
// It ensures that the result is >= 0. This is necessary because the result is
// passed to a LogSink and that API did not historically document whether
// levels could be negative or what that meant.
//
// Some example usage:
//
// logrV0 := getMyLogger()
// logrV2 := logrV0.V(2)
// slogV2 := slog.New(logr.ToSlogHandler(logrV2))
// slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
// slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2)
// slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0)
func (l fnlogger) levelFromSlog(level slog.Level) int {
result := -level
if result < 0 {
result = 0 // because LogSink doesn't expect negative V levels
}
return int(result)
}

View File

@ -207,10 +207,6 @@ limitations under the License.
// those. // those.
package logr package logr
import (
"context"
)
// New returns a new Logger instance. This is primarily used by libraries // New returns a new Logger instance. This is primarily used by libraries
// implementing LogSink, rather than end users. Passing a nil sink will create // implementing LogSink, rather than end users. Passing a nil sink will create
// a Logger which discards all log lines. // a Logger which discards all log lines.
@ -410,45 +406,6 @@ func (l Logger) IsZero() bool {
return l.sink == nil return l.sink == nil
} }
// contextKey is how we find Loggers in a context.Context.
type contextKey struct{}
// FromContext returns a Logger from ctx or an error if no Logger is found.
func FromContext(ctx context.Context) (Logger, error) {
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
return v, nil
}
return Logger{}, notFoundError{}
}
// notFoundError exists to carry an IsNotFound method.
type notFoundError struct{}
func (notFoundError) Error() string {
return "no logr.Logger was present"
}
func (notFoundError) IsNotFound() bool {
return true
}
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
// returns a Logger that discards all log messages.
func FromContextOrDiscard(ctx context.Context) Logger {
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
return v
}
return Discard()
}
// NewContext returns a new Context, derived from ctx, which carries the
// provided Logger.
func NewContext(ctx context.Context, logger Logger) context.Context {
return context.WithValue(ctx, contextKey{}, logger)
}
// RuntimeInfo holds information that the logr "core" library knows which // RuntimeInfo holds information that the logr "core" library knows which
// LogSinks might want to know. // LogSinks might want to know.
type RuntimeInfo struct { type RuntimeInfo struct {

192
vendor/github.com/go-logr/logr/sloghandler.go generated vendored Normal file
View File

@ -0,0 +1,192 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 The logr Authors.
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
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logr
import (
"context"
"log/slog"
)
type slogHandler struct {
// May be nil, in which case all logs get discarded.
sink LogSink
// Non-nil if sink is non-nil and implements SlogSink.
slogSink SlogSink
// groupPrefix collects values from WithGroup calls. It gets added as
// prefix to value keys when handling a log record.
groupPrefix string
// levelBias can be set when constructing the handler to influence the
// slog.Level of log records. A positive levelBias reduces the
// slog.Level value. slog has no API to influence this value after the
// handler got created, so it can only be set indirectly through
// Logger.V.
levelBias slog.Level
}
var _ slog.Handler = &slogHandler{}
// groupSeparator is used to concatenate WithGroup names and attribute keys.
const groupSeparator = "."
// GetLevel is used for black box unit testing.
func (l *slogHandler) GetLevel() slog.Level {
return l.levelBias
}
func (l *slogHandler) Enabled(_ context.Context, level slog.Level) bool {
return l.sink != nil && (level >= slog.LevelError || l.sink.Enabled(l.levelFromSlog(level)))
}
func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error {
if l.slogSink != nil {
// Only adjust verbosity level of log entries < slog.LevelError.
if record.Level < slog.LevelError {
record.Level -= l.levelBias
}
return l.slogSink.Handle(ctx, record)
}
// No need to check for nil sink here because Handle will only be called
// when Enabled returned true.
kvList := make([]any, 0, 2*record.NumAttrs())
record.Attrs(func(attr slog.Attr) bool {
kvList = attrToKVs(attr, l.groupPrefix, kvList)
return true
})
if record.Level >= slog.LevelError {
l.sinkWithCallDepth().Error(nil, record.Message, kvList...)
} else {
level := l.levelFromSlog(record.Level)
l.sinkWithCallDepth().Info(level, record.Message, kvList...)
}
return nil
}
// sinkWithCallDepth adjusts the stack unwinding so that when Error or Info
// are called by Handle, code in slog gets skipped.
//
// This offset currently (Go 1.21.0) works for calls through
// slog.New(ToSlogHandler(...)). There's no guarantee that the call
// chain won't change. Wrapping the handler will also break unwinding. It's
// still better than not adjusting at all....
//
// This cannot be done when constructing the handler because FromSlogHandler needs
// access to the original sink without this adjustment. A second copy would
// work, but then WithAttrs would have to be called for both of them.
func (l *slogHandler) sinkWithCallDepth() LogSink {
if sink, ok := l.sink.(CallDepthLogSink); ok {
return sink.WithCallDepth(2)
}
return l.sink
}
func (l *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
if l.sink == nil || len(attrs) == 0 {
return l
}
clone := *l
if l.slogSink != nil {
clone.slogSink = l.slogSink.WithAttrs(attrs)
clone.sink = clone.slogSink
} else {
kvList := make([]any, 0, 2*len(attrs))
for _, attr := range attrs {
kvList = attrToKVs(attr, l.groupPrefix, kvList)
}
clone.sink = l.sink.WithValues(kvList...)
}
return &clone
}
func (l *slogHandler) WithGroup(name string) slog.Handler {
if l.sink == nil {
return l
}
if name == "" {
// slog says to inline empty groups
return l
}
clone := *l
if l.slogSink != nil {
clone.slogSink = l.slogSink.WithGroup(name)
clone.sink = clone.slogSink
} else {
clone.groupPrefix = addPrefix(clone.groupPrefix, name)
}
return &clone
}
// attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups
// and other details of slog.
func attrToKVs(attr slog.Attr, groupPrefix string, kvList []any) []any {
attrVal := attr.Value.Resolve()
if attrVal.Kind() == slog.KindGroup {
groupVal := attrVal.Group()
grpKVs := make([]any, 0, 2*len(groupVal))
prefix := groupPrefix
if attr.Key != "" {
prefix = addPrefix(groupPrefix, attr.Key)
}
for _, attr := range groupVal {
grpKVs = attrToKVs(attr, prefix, grpKVs)
}
kvList = append(kvList, grpKVs...)
} else if attr.Key != "" {
kvList = append(kvList, addPrefix(groupPrefix, attr.Key), attrVal.Any())
}
return kvList
}
func addPrefix(prefix, name string) string {
if prefix == "" {
return name
}
if name == "" {
return prefix
}
return prefix + groupSeparator + name
}
// levelFromSlog adjusts the level by the logger's verbosity and negates it.
// It ensures that the result is >= 0. This is necessary because the result is
// passed to a LogSink and that API did not historically document whether
// levels could be negative or what that meant.
//
// Some example usage:
//
// logrV0 := getMyLogger()
// logrV2 := logrV0.V(2)
// slogV2 := slog.New(logr.ToSlogHandler(logrV2))
// slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
// slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2)
// slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0)
func (l *slogHandler) levelFromSlog(level slog.Level) int {
result := -level
result += l.levelBias // in case the original Logger had a V level
if result < 0 {
result = 0 // because LogSink doesn't expect negative V levels
}
return int(result)
}

100
vendor/github.com/go-logr/logr/slogr.go generated vendored Normal file
View File

@ -0,0 +1,100 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 The logr Authors.
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
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logr
import (
"context"
"log/slog"
)
// FromSlogHandler returns a Logger which writes to the slog.Handler.
//
// The logr verbosity level is mapped to slog levels such that V(0) becomes
// slog.LevelInfo and V(4) becomes slog.LevelDebug.
func FromSlogHandler(handler slog.Handler) Logger {
if handler, ok := handler.(*slogHandler); ok {
if handler.sink == nil {
return Discard()
}
return New(handler.sink).V(int(handler.levelBias))
}
return New(&slogSink{handler: handler})
}
// ToSlogHandler returns a slog.Handler which writes to the same sink as the Logger.
//
// The returned logger writes all records with level >= slog.LevelError as
// error log entries with LogSink.Error, regardless of the verbosity level of
// the Logger:
//
// logger := <some Logger with 0 as verbosity level>
// slog.New(ToSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...)
//
// The level of all other records gets reduced by the verbosity
// level of the Logger and the result is negated. If it happens
// to be negative, then it gets replaced by zero because a LogSink
// is not expected to handled negative levels:
//
// slog.New(ToSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...)
// slog.New(ToSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...)
// slog.New(ToSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...)
// slog.New(ToSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...)
func ToSlogHandler(logger Logger) slog.Handler {
if sink, ok := logger.GetSink().(*slogSink); ok && logger.GetV() == 0 {
return sink.handler
}
handler := &slogHandler{sink: logger.GetSink(), levelBias: slog.Level(logger.GetV())}
if slogSink, ok := handler.sink.(SlogSink); ok {
handler.slogSink = slogSink
}
return handler
}
// SlogSink is an optional interface that a LogSink can implement to support
// logging through the slog.Logger or slog.Handler APIs better. It then should
// also support special slog values like slog.Group. When used as a
// slog.Handler, the advantages are:
//
// - stack unwinding gets avoided in favor of logging the pre-recorded PC,
// as intended by slog
// - proper grouping of key/value pairs via WithGroup
// - verbosity levels > slog.LevelInfo can be recorded
// - less overhead
//
// Both APIs (Logger and slog.Logger/Handler) then are supported equally
// well. Developers can pick whatever API suits them better and/or mix
// packages which use either API in the same binary with a common logging
// implementation.
//
// This interface is necessary because the type implementing the LogSink
// interface cannot also implement the slog.Handler interface due to the
// different prototype of the common Enabled method.
//
// An implementation could support both interfaces in two different types, but then
// additional interfaces would be needed to convert between those types in FromSlogHandler
// and ToSlogHandler.
type SlogSink interface {
LogSink
Handle(ctx context.Context, record slog.Record) error
WithAttrs(attrs []slog.Attr) SlogSink
WithGroup(name string) SlogSink
}

120
vendor/github.com/go-logr/logr/slogsink.go generated vendored Normal file
View File

@ -0,0 +1,120 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 The logr Authors.
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
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logr
import (
"context"
"log/slog"
"runtime"
"time"
)
var (
_ LogSink = &slogSink{}
_ CallDepthLogSink = &slogSink{}
_ Underlier = &slogSink{}
)
// Underlier is implemented by the LogSink returned by NewFromLogHandler.
type Underlier interface {
// GetUnderlying returns the Handler used by the LogSink.
GetUnderlying() slog.Handler
}
const (
// nameKey is used to log the `WithName` values as an additional attribute.
nameKey = "logger"
// errKey is used to log the error parameter of Error as an additional attribute.
errKey = "err"
)
type slogSink struct {
callDepth int
name string
handler slog.Handler
}
func (l *slogSink) Init(info RuntimeInfo) {
l.callDepth = info.CallDepth
}
func (l *slogSink) GetUnderlying() slog.Handler {
return l.handler
}
func (l *slogSink) WithCallDepth(depth int) LogSink {
newLogger := *l
newLogger.callDepth += depth
return &newLogger
}
func (l *slogSink) Enabled(level int) bool {
return l.handler.Enabled(context.Background(), slog.Level(-level))
}
func (l *slogSink) Info(level int, msg string, kvList ...interface{}) {
l.log(nil, msg, slog.Level(-level), kvList...)
}
func (l *slogSink) Error(err error, msg string, kvList ...interface{}) {
l.log(err, msg, slog.LevelError, kvList...)
}
func (l *slogSink) log(err error, msg string, level slog.Level, kvList ...interface{}) {
var pcs [1]uintptr
// skip runtime.Callers, this function, Info/Error, and all helper functions above that.
runtime.Callers(3+l.callDepth, pcs[:])
record := slog.NewRecord(time.Now(), level, msg, pcs[0])
if l.name != "" {
record.AddAttrs(slog.String(nameKey, l.name))
}
if err != nil {
record.AddAttrs(slog.Any(errKey, err))
}
record.Add(kvList...)
_ = l.handler.Handle(context.Background(), record)
}
func (l slogSink) WithName(name string) LogSink {
if l.name != "" {
l.name += "/"
}
l.name += name
return &l
}
func (l slogSink) WithValues(kvList ...interface{}) LogSink {
l.handler = l.handler.WithAttrs(kvListToAttrs(kvList...))
return &l
}
func kvListToAttrs(kvList ...interface{}) []slog.Attr {
// We don't need the record itself, only its Add method.
record := slog.NewRecord(time.Time{}, 0, "", 0)
record.Add(kvList...)
attrs := make([]slog.Attr, 0, record.NumAttrs())
record.Attrs(func(attr slog.Attr) bool {
attrs = append(attrs, attr)
return true
})
return attrs
}

View File

@ -1,15 +0,0 @@
# This is the official list of GoGo authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS file, which
# lists people. For example, employees are listed in CONTRIBUTORS,
# but not in AUTHORS, because the employer holds the copyright.
# Names should be added to this file as one of
# Organization's name
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
# Please keep the list sorted.
Sendgrid, Inc
Vastech SA (PTY) LTD
Walter Schulze <awalterschulze@gmail.com>

View File

@ -1,23 +0,0 @@
Anton Povarov <anton.povarov@gmail.com>
Brian Goff <cpuguy83@gmail.com>
Clayton Coleman <ccoleman@redhat.com>
Denis Smirnov <denis.smirnov.91@gmail.com>
DongYun Kang <ceram1000@gmail.com>
Dwayne Schultz <dschultz@pivotal.io>
Georg Apitz <gapitz@pivotal.io>
Gustav Paul <gustav.paul@gmail.com>
Johan Brandhorst <johan.brandhorst@gmail.com>
John Shahid <jvshahid@gmail.com>
John Tuley <john@tuley.org>
Laurent <laurent@adyoulike.com>
Patrick Lee <patrick@dropbox.com>
Peter Edge <peter.edge@gmail.com>
Roger Johansson <rogeralsing@gmail.com>
Sam Nguyen <sam.nguyen@sendgrid.com>
Sergio Arbeo <serabe@gmail.com>
Stephen J Day <stephen.day@docker.com>
Tamir Duberstein <tamird@gmail.com>
Todd Eisenberger <teisenberger@dropbox.com>
Tormod Erevik Lea <tormodlea@gmail.com>
Vyacheslav Kim <kane@sendgrid.com>
Walter Schulze <awalterschulze@gmail.com>

View File

@ -1,35 +0,0 @@
Copyright (c) 2013, The GoGo Authors. All rights reserved.
Protocol Buffers for Go with Gadgets
Go support for Protocol Buffers - Google's data interchange format
Copyright 2010 The Go Authors. All rights reserved.
https://github.com/golang/protobuf
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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