update vendor
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
19a32db84d
commit
94d1cfbfbf
10501 changed files with 2307943 additions and 29279 deletions
16
vendor/github.com/opencontainers/image-spec/.tool/check-license
generated
vendored
Executable file
16
vendor/github.com/opencontainers/image-spec/.tool/check-license
generated
vendored
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
ret=0
|
||||
|
||||
for file in $(find . -type f -iname '*.go' ! -path './vendor/*'); do
|
||||
if ! head -n3 "${file}" | grep -Eq "(Copyright|generated|GENERATED)"; then
|
||||
echo "${file}:missing license header"
|
||||
ret=1
|
||||
fi
|
||||
done
|
||||
|
||||
exit $ret
|
54
vendor/github.com/opencontainers/image-spec/.tool/genheader.go
generated
vendored
Normal file
54
vendor/github.com/opencontainers/image-spec/.tool/genheader.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2017 The Linux Foundation
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go"
|
||||
)
|
||||
|
||||
var headerTemplate = template.Must(template.New("gen").Parse(`<title>image-spec {{.Version}}</title>
|
||||
<base href="https://raw.githubusercontent.com/opencontainers/image-spec/{{.Branch}}/">`))
|
||||
|
||||
type Obj struct {
|
||||
Version string
|
||||
Branch string
|
||||
}
|
||||
|
||||
func main() {
|
||||
obj := Obj{
|
||||
Version: specs.Version,
|
||||
Branch: specs.Version,
|
||||
}
|
||||
if strings.HasSuffix(specs.Version, "-dev") {
|
||||
cmd := exec.Command("git", "log", "-1", `--pretty=%H`, "HEAD")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
obj.Branch = strings.Trim(out.String(), " \n\r")
|
||||
}
|
||||
headerTemplate.Execute(os.Stdout, obj)
|
||||
}
|
24
vendor/github.com/opencontainers/image-spec/.tool/lint
generated
vendored
Executable file
24
vendor/github.com/opencontainers/image-spec/.tool/lint
generated
vendored
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
if [ ! $(command -v gometalinter) ]; then
|
||||
go get -u github.com/alecthomas/gometalinter
|
||||
gometalinter --install
|
||||
fi
|
||||
|
||||
for d in $(find . -type d -not -iwholename '*.git*' -a -not -iname '.tool' -a -not -iwholename '*vendor*'); do
|
||||
gometalinter \
|
||||
--exclude='error return value not checked.*(Close|Log|Print).*\(errcheck\)$' \
|
||||
--exclude='.*_test\.go:.*error return value not checked.*\(errcheck\)$' \
|
||||
--exclude='duplicate of.*_test.go.*\(dupl\)$' \
|
||||
--exclude='schema/fs.go' \
|
||||
--disable=aligncheck \
|
||||
--disable=gotype \
|
||||
--disable=gas \
|
||||
--cyclo-over=35 \
|
||||
--tests \
|
||||
--deadline=60s "${d}"
|
||||
done
|
67
vendor/github.com/opencontainers/image-spec/identity/chainid.go
generated
vendored
Normal file
67
vendor/github.com/opencontainers/image-spec/identity/chainid.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 identity provides implementations of subtle calculations pertaining
|
||||
// to image and layer identity. The primary item present here is the ChainID
|
||||
// calculation used in identifying the result of subsequent layer applications.
|
||||
//
|
||||
// Helpers are also provided here to ease transition to the
|
||||
// github.com/opencontainers/go-digest package, but that package may be used
|
||||
// directly.
|
||||
package identity
|
||||
|
||||
import "github.com/opencontainers/go-digest"
|
||||
|
||||
// ChainID takes a slice of digests and returns the ChainID corresponding to
|
||||
// the last entry. Typically, these are a list of layer DiffIDs, with the
|
||||
// result providing the ChainID identifying the result of sequential
|
||||
// application of the preceding layers.
|
||||
func ChainID(dgsts []digest.Digest) digest.Digest {
|
||||
chainIDs := make([]digest.Digest, len(dgsts))
|
||||
copy(chainIDs, dgsts)
|
||||
ChainIDs(chainIDs)
|
||||
|
||||
if len(chainIDs) == 0 {
|
||||
return ""
|
||||
}
|
||||
return chainIDs[len(chainIDs)-1]
|
||||
}
|
||||
|
||||
// ChainIDs calculates the recursively applied chain id for each identifier in
|
||||
// the slice. The result is written direcly back into the slice such that the
|
||||
// ChainID for each item will be in the respective position.
|
||||
//
|
||||
// By definition of ChainID, the zeroth element will always be the same before
|
||||
// and after the call.
|
||||
//
|
||||
// As an example, given the chain of ids `[A, B, C]`, the result `[A,
|
||||
// ChainID(A|B), ChainID(A|B|C)]` will be written back to the slice.
|
||||
//
|
||||
// The input is provided as a return value for convenience.
|
||||
//
|
||||
// Typically, these are a list of layer DiffIDs, with the
|
||||
// result providing the ChainID for each the result of each layer application
|
||||
// sequentially.
|
||||
func ChainIDs(dgsts []digest.Digest) []digest.Digest {
|
||||
if len(dgsts) < 2 {
|
||||
return dgsts
|
||||
}
|
||||
|
||||
parent := digest.FromBytes([]byte(dgsts[0] + " " + dgsts[1]))
|
||||
next := dgsts[1:]
|
||||
next[0] = parent
|
||||
ChainIDs(next)
|
||||
|
||||
return dgsts
|
||||
}
|
95
vendor/github.com/opencontainers/image-spec/identity/chainid_test.go
generated
vendored
Normal file
95
vendor/github.com/opencontainers/image-spec/identity/chainid_test.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 identity
|
||||
|
||||
import (
|
||||
_ "crypto/sha256" // required to install sha256 digest support
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func TestChainID(t *testing.T) {
|
||||
// To provide a good testing base, we define the individual links in a
|
||||
// chain recursively, illustrating the calculations for each chain.
|
||||
//
|
||||
// Note that we use invalid digests for the unmodified identifiers here to
|
||||
// make the computation more readable.
|
||||
chainDigestAB := digest.FromString("sha256:a" + " " + "sha256:b") // chain for A|B
|
||||
chainDigestABC := digest.FromString(chainDigestAB.String() + " " + "sha256:c") // chain for A|B|C
|
||||
|
||||
for _, testcase := range []struct {
|
||||
Name string
|
||||
Digests []digest.Digest
|
||||
Expected []digest.Digest
|
||||
}{
|
||||
{
|
||||
Name: "nil",
|
||||
},
|
||||
{
|
||||
Name: "empty",
|
||||
Digests: []digest.Digest{},
|
||||
Expected: []digest.Digest{},
|
||||
},
|
||||
{
|
||||
Name: "identity",
|
||||
Digests: []digest.Digest{"sha256:a"},
|
||||
Expected: []digest.Digest{"sha256:a"},
|
||||
},
|
||||
{
|
||||
Name: "two",
|
||||
Digests: []digest.Digest{"sha256:a", "sha256:b"},
|
||||
Expected: []digest.Digest{"sha256:a", chainDigestAB},
|
||||
},
|
||||
{
|
||||
Name: "three",
|
||||
Digests: []digest.Digest{"sha256:a", "sha256:b", "sha256:c"},
|
||||
Expected: []digest.Digest{"sha256:a", chainDigestAB, chainDigestABC},
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.Name, func(t *testing.T) {
|
||||
t.Log("before", testcase.Digests)
|
||||
|
||||
var ids []digest.Digest
|
||||
|
||||
if testcase.Digests != nil {
|
||||
ids = make([]digest.Digest, len(testcase.Digests))
|
||||
copy(ids, testcase.Digests)
|
||||
}
|
||||
|
||||
ids = ChainIDs(ids)
|
||||
t.Log("after", ids)
|
||||
if !reflect.DeepEqual(ids, testcase.Expected) {
|
||||
t.Errorf("unexpected chain: %v != %v", ids, testcase.Expected)
|
||||
}
|
||||
|
||||
if len(testcase.Digests) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure parent stays stable
|
||||
if ids[0] != testcase.Digests[0] {
|
||||
t.Errorf("parent changed: %v != %v", ids[0], testcase.Digests[0])
|
||||
}
|
||||
|
||||
// make sure that the ChainID function takes the last element
|
||||
id := ChainID(testcase.Digests)
|
||||
if id != ids[len(ids)-1] {
|
||||
t.Errorf("incorrect chain id returned from ChainID: %v != %v", id, ids[len(ids)-1])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
40
vendor/github.com/opencontainers/image-spec/identity/helpers.go
generated
vendored
Normal file
40
vendor/github.com/opencontainers/image-spec/identity/helpers.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 identity
|
||||
|
||||
import (
|
||||
_ "crypto/sha256" // side-effect to install impls, sha256
|
||||
_ "crypto/sha512" // side-effect to install impls, sha384/sh512
|
||||
|
||||
"io"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// FromReader consumes the content of rd until io.EOF, returning canonical
|
||||
// digest.
|
||||
func FromReader(rd io.Reader) (digest.Digest, error) {
|
||||
return digest.Canonical.FromReader(rd)
|
||||
}
|
||||
|
||||
// FromBytes digests the input and returns a Digest.
|
||||
func FromBytes(p []byte) digest.Digest {
|
||||
return digest.Canonical.FromBytes(p)
|
||||
}
|
||||
|
||||
// FromString digests the input and returns a Digest.
|
||||
func FromString(s string) digest.Digest {
|
||||
return digest.Canonical.FromString(s)
|
||||
}
|
BIN
vendor/github.com/opencontainers/image-spec/img/build-diagram.png
generated
vendored
Normal file
BIN
vendor/github.com/opencontainers/image-spec/img/build-diagram.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
12
vendor/github.com/opencontainers/image-spec/img/media-types.dot
generated
vendored
Normal file
12
vendor/github.com/opencontainers/image-spec/img/media-types.dot
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
digraph G {
|
||||
{
|
||||
imageIndex [shape=note, label="Image Index\n<<optional>>\napplication/vnd.oci.image.index.v1+json"]
|
||||
manifest [shape=note, label="Image manifest\napplication/vnd.oci.image.manifest.v1+json"]
|
||||
config [shape=note, label="Image JSON\napplication/vnd.oci.image.config.v1+json"]
|
||||
layer [shape=note, label="Layer tar archive\napplication/vnd.oci.image.layer.v1.tar\napplication/vnd.oci.image.layer.v1.tar+gzip\napplication/vnd.oci.image.layer.nondistributable.v1.tar\napplication/vnd.oci.image.layer.nondistributable.v1.tar+gzip"]
|
||||
}
|
||||
|
||||
imageIndex -> manifest [label="1..*"]
|
||||
manifest -> config [label="1..1"]
|
||||
manifest -> layer [label="1..*"]
|
||||
}
|
BIN
vendor/github.com/opencontainers/image-spec/img/media-types.png
generated
vendored
Normal file
BIN
vendor/github.com/opencontainers/image-spec/img/media-types.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
vendor/github.com/opencontainers/image-spec/img/run-diagram.png
generated
vendored
Normal file
BIN
vendor/github.com/opencontainers/image-spec/img/run-diagram.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
223
vendor/github.com/opencontainers/image-spec/schema/backwards_compatibility_test.go
generated
vendored
Normal file
223
vendor/github.com/opencontainers/image-spec/schema/backwards_compatibility_test.go
generated
vendored
Normal file
|
@ -0,0 +1,223 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema_test
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
var compatMap = map[string]string{
|
||||
"application/vnd.docker.distribution.manifest.list.v2+json": v1.MediaTypeImageIndex,
|
||||
"application/vnd.docker.distribution.manifest.v2+json": v1.MediaTypeImageManifest,
|
||||
"application/vnd.docker.image.rootfs.diff.tar.gzip": v1.MediaTypeImageLayerGzip,
|
||||
"application/vnd.docker.container.image.v1+json": v1.MediaTypeImageConfig,
|
||||
}
|
||||
|
||||
// convertFormats converts Docker v2.2 image format JSON documents to OCI
|
||||
// format by simply replacing instances of the strings found in the compatMap
|
||||
// found in the input string.
|
||||
func convertFormats(input string) string {
|
||||
out := input
|
||||
for k, v := range compatMap {
|
||||
out = strings.Replace(out, k, v, -1)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestBackwardsCompatibilityImageIndex(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
imageIndex string
|
||||
digest digest.Digest
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
digest: "sha256:4ffd0883f25635999f04ea543240a27c9a4341979ff7d46a9774f71512eebb1f",
|
||||
imageIndex: `{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 2094,
|
||||
"digest": "sha256:7820f9a86d4ad15a2c4f0c0e5479298df2aa7c2f6871288e2ef8546f3e7b6783",
|
||||
"platform": {
|
||||
"architecture": "ppc64le",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 1922,
|
||||
"digest": "sha256:ae1b0e06e8ade3a11267564a26e750585ba2259c0ecab59ab165ad1af41d1bdd",
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 2084,
|
||||
"digest": "sha256:e4c0df75810b953d6717b8f8f28298d73870e8aa2a0d5e77b8391f16fdfbbbe2",
|
||||
"platform": {
|
||||
"architecture": "s390x",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 2084,
|
||||
"digest": "sha256:07ebe243465ef4a667b78154ae6c3ea46fdb1582936aac3ac899ea311a701b40",
|
||||
"platform": {
|
||||
"architecture": "arm",
|
||||
"os": "linux",
|
||||
"variant": "v7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"size": 2090,
|
||||
"digest": "sha256:fb2fc0707b86dafa9959fe3d29e66af8787aee4d9a23581714be65db4265ad8a",
|
||||
"platform": {
|
||||
"architecture": "arm64",
|
||||
"os": "linux",
|
||||
"variant": "v8"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
fail: false,
|
||||
},
|
||||
} {
|
||||
got := digest.FromString(tt.imageIndex)
|
||||
if tt.digest != got {
|
||||
t.Errorf("test %d: expected digest %s but got %s", i, tt.digest, got)
|
||||
}
|
||||
|
||||
imageIndex := convertFormats(tt.imageIndex)
|
||||
r := strings.NewReader(imageIndex)
|
||||
err := schema.ValidatorMediaTypeImageIndex.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackwardsCompatibilityManifest(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
manifest string
|
||||
digest digest.Digest
|
||||
fail bool
|
||||
}{
|
||||
// manifest pulled from docker hub using hash value
|
||||
//
|
||||
// curl -L -H "Authorization: Bearer ..." -H \
|
||||
// "Accept: application/vnd.docker.distribution.manifest.v2+json" \
|
||||
// https://registry-1.docker.io/v2/library/docker/manifests/sha256:888206c77cd2811ec47e752ba291e5b7734e3ef137dfd222daadaca39a9f17bc
|
||||
{
|
||||
digest: "sha256:888206c77cd2811ec47e752ba291e5b7734e3ef137dfd222daadaca39a9f17bc",
|
||||
manifest: `{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/octet-stream",
|
||||
"size": 3210,
|
||||
"digest": "sha256:5359a4f250650c20227055957e353e8f8a74152f35fe36f00b6b1f9fc19c8861"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 2310272,
|
||||
"digest": "sha256:fae91920dcd4542f97c9350b3157139a5d901362c2abec284de5ebd1b45b4957"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 913022,
|
||||
"digest": "sha256:f384f6ab36adad485192f09379c0b58dc612a3cde82c551e082a7c29a87c95da"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 9861668,
|
||||
"digest": "sha256:ed0d2dd5e1a0e5e650a330a864c8a122e9aa91fa6ba9ac6f0bd1882e59df55e7"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 465,
|
||||
"digest": "sha256:ec4d00b58417c45f7ddcfde7bcad8c9d62a7d6d5d17cdc1f7d79bcb2e22c1491"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
fail: false,
|
||||
},
|
||||
} {
|
||||
got := digest.FromString(tt.manifest)
|
||||
if tt.digest != got {
|
||||
t.Errorf("test %d: expected digest %s but got %s", i, tt.digest, got)
|
||||
}
|
||||
|
||||
manifest := convertFormats(tt.manifest)
|
||||
r := strings.NewReader(manifest)
|
||||
err := schema.ValidatorMediaTypeManifest.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackwardsCompatibilityConfig(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
config string
|
||||
digest digest.Digest
|
||||
fail bool
|
||||
}{
|
||||
// config pulled from docker hub blob store
|
||||
//
|
||||
// $ TOKEN=$(curl https://auth.docker.io/token\?service\=registry.docker.io\&scope\=repository:library/docker:pull | jq -r .token)
|
||||
// $ CONFIG_DIGEST=$(curl -H "Authorization: Bearer ${TOKEN}" -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' https://index.docker.io/v2/library/docker/manifests/1.12.1 | jq -r .config.digest)
|
||||
// $ curl -LH "Authorization: Bearer ${TOKEN}" https://index.docker.io/v2/library/docker/blobs/${CONFIG_DIGEST}
|
||||
{
|
||||
digest: "sha256:a059ea7356d5b5a9e0f6352bfa463e7bd4721c2ade3ef168603826e0de6fe54b",
|
||||
config: `{"architecture":"amd64","config":{"Hostname":"09713501c176","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","DOCKER_BUCKET=get.docker.com","DOCKER_VERSION=1.12.1","DOCKER_SHA256=05ceec7fd937e1416e5dce12b0b6e1c655907d349d52574319a1e875077ccb79"],"Cmd":["sh"],"Image":"sha256:32e2e3ccf2a4fbaa75b078bf539cd5ea2e374a4242665a5ec3f3c01e7a3eefb8","Volumes":null,"WorkingDir":"","Entrypoint":["docker-entrypoint.sh"],"OnBuild":[],"Labels":{}},"container":"15a30be053fb3069a7879b4ea537e84689d8e8e8ba94dc4dd499271506803ba1","container_config":{"Hostname":"09713501c176","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","DOCKER_BUCKET=get.docker.com","DOCKER_VERSION=1.12.1","DOCKER_SHA256=05ceec7fd937e1416e5dce12b0b6e1c655907d349d52574319a1e875077ccb79"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"sh\"]"],"Image":"sha256:32e2e3ccf2a4fbaa75b078bf539cd5ea2e374a4242665a5ec3f3c01e7a3eefb8","Volumes":null,"WorkingDir":"","Entrypoint":["docker-entrypoint.sh"],"OnBuild":[],"Labels":{}},"created":"2016-10-10T23:04:00.821781828Z","docker_version":"1.12.1","history":[{"created":"2016-09-23T16:29:57.276868291Z","created_by":"/bin/sh -c #(nop) ADD file:d6ee3ba7a4d59b161917082cc7242c660c61bb3f3cc1549c7e2dfff2b0de7104 in / "},{"created":"2016-09-23T16:36:54.024611637Z","created_by":"/bin/sh -c apk add --no-cache \t\tca-certificates \t\tcurl \t\topenssl"},{"created":"2016-09-23T16:36:54.365914519Z","created_by":"/bin/sh -c #(nop) ENV DOCKER_BUCKET=get.docker.com","empty_layer":true},{"created":"2016-09-23T16:36:54.662005049Z","created_by":"/bin/sh -c #(nop) ENV DOCKER_VERSION=1.12.1","empty_layer":true},{"created":"2016-09-23T16:36:54.946033025Z","created_by":"/bin/sh -c #(nop) ENV DOCKER_SHA256=05ceec7fd937e1416e5dce12b0b6e1c655907d349d52574319a1e875077ccb79","empty_layer":true},{"created":"2016-09-23T16:36:58.535084011Z","created_by":"/bin/sh -c set -x \t\u0026\u0026 curl -fSL \"https://${DOCKER_BUCKET}/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz\" -o docker.tgz \t\u0026\u0026 echo \"${DOCKER_SHA256} *docker.tgz\" | sha256sum -c - \t\u0026\u0026 tar -xzvf docker.tgz \t\u0026\u0026 mv docker/* /usr/local/bin/ \t\u0026\u0026 rmdir docker \t\u0026\u0026 rm docker.tgz \t\u0026\u0026 docker -v"},{"created":"2016-10-10T23:04:00.334158993Z","created_by":"/bin/sh -c #(nop) COPY file:399605dc1850a60a586b5494ab538bad495fd6f94eabca0c5f8a26468ce6030f in /usr/local/bin/ "},{"created":"2016-10-10T23:04:00.577900192Z","created_by":"/bin/sh -c #(nop) ENTRYPOINT [\"docker-entrypoint.sh\"]","empty_layer":true},{"created":"2016-10-10T23:04:00.821781828Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:9007f5987db353ec398a223bc5a135c5a9601798ba20a1abba537ea2f8ac765f","sha256:1b06990ff0df8dad281fad7e6e4c5e91f32f8f8c095d6c74cf1e90a6f4407e28","sha256:9d12251ce74aac7619a83641ab72431a8d82e58bcd8a262c2bb0cdb280f1f3b5","sha256:17a7f292c2427adfc75c3a789bab8efec925dc38c5437bf83d2f528013ab80e2"]}}`,
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
// fedora:23 from docker hub
|
||||
// both Entrypoint and Cmd can be nullable
|
||||
digest: "sha256:a20665eb1fe2912accb3d5dadaed360430df0d1aa46874875886947d61d3d4ee",
|
||||
config: `{"architecture":"amd64","author":"Patrick Uiterwijk \u003cpatrick@puiterwijk.org\u003e","config":{"Hostname":"8dfe43d80430","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":null,"Image":"sha256:6986ae504bbf843512d680cc959484452034965db15f75ee8bdd1b107f61500b","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"container":"6249cd2c4b1d6b1bf05903364cbcb95781508994d6407c1564d494e748ea1b41","container_config":{"Hostname":"8dfe43d80430","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ADD file:293a6e463aa402bb8f80eb5cfc937f375cedc6843abaeb9eccfe3923bb3fc80b in /"],"Image":"sha256:6986ae504bbf843512d680cc959484452034965db15f75ee8bdd1b107f61500b","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2016-06-10T18:44:31.784795904Z","docker_version":"1.10.3","history":[{"created":"2016-06-10T18:44:03.360264073Z","author":"Patrick Uiterwijk \u003cpatrick@puiterwijk.org\u003e","created_by":"/bin/sh -c #(nop) MAINTAINER Patrick Uiterwijk \u003cpatrick@puiterwijk.org\u003e","empty_layer":true},{"created":"2016-06-10T18:44:31.784795904Z","author":"Patrick Uiterwijk \u003cpatrick@puiterwijk.org\u003e","created_by":"/bin/sh -c #(nop) ADD file:293a6e463aa402bb8f80eb5cfc937f375cedc6843abaeb9eccfe3923bb3fc80b in /"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:d43f38155a799dc53d8fbb9f3bc11f51805f4027cd5c3d10b9823201cd5b9400"]}}`,
|
||||
fail: false,
|
||||
},
|
||||
} {
|
||||
got := digest.FromString(tt.config)
|
||||
if tt.digest != got {
|
||||
t.Errorf("test %d: expected digest %s but got %s", i, tt.digest, got)
|
||||
}
|
||||
|
||||
config := convertFormats(tt.config)
|
||||
r := strings.NewReader(config)
|
||||
err := schema.ValidatorMediaTypeImageConfig.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
140
vendor/github.com/opencontainers/image-spec/schema/config-schema.json
generated
vendored
Normal file
140
vendor/github.com/opencontainers/image-spec/schema/config-schema.json
generated
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
{
|
||||
"description": "OpenContainer Config Specification",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://opencontainers.org/schema/image/config",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"architecture": {
|
||||
"type": "string"
|
||||
},
|
||||
"os": {
|
||||
"type": "string"
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"User": {
|
||||
"type": "string"
|
||||
},
|
||||
"ExposedPorts": {
|
||||
"$ref": "defs.json#/definitions/mapStringObject"
|
||||
},
|
||||
"Env": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"Entrypoint": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Cmd": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Volumes": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "defs.json#/definitions/mapStringObject"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"WorkingDir": {
|
||||
"type": "string"
|
||||
},
|
||||
"Labels": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "defs.json#/definitions/mapStringString"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"StopSignal": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rootfs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"diff_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"layers"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"diff_ids",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"history": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "string"
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
},
|
||||
"empty_layer": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"architecture",
|
||||
"os",
|
||||
"rootfs"
|
||||
]
|
||||
}
|
242
vendor/github.com/opencontainers/image-spec/schema/config_test.go
generated
vendored
Normal file
242
vendor/github.com/opencontainers/image-spec/schema/config_test.go
generated
vendored
Normal file
|
@ -0,0 +1,242 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
config string
|
||||
fail bool
|
||||
}{
|
||||
// expected failure: field "os" has numeric value, must be string
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"architecture": "amd64",
|
||||
"os": 123,
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
],
|
||||
"type": "layers"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: field "config.User" has numeric value, must be string
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"created": "2015-10-31T22:22:56.015925234Z",
|
||||
"author": "Alyssa P. Hacker <alyspdev@example.com>",
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"config": {
|
||||
"User": 1234
|
||||
},
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
],
|
||||
"type": "layers"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failue: history has string value, must be an array
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"history": "should be an array",
|
||||
"architecture": "amd64",
|
||||
"os": 123,
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
],
|
||||
"type": "layers"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: Env has numeric value, must be a string
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"architecture": "amd64",
|
||||
"os": 123,
|
||||
"config": {
|
||||
"Env": [
|
||||
7353
|
||||
]
|
||||
},
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
],
|
||||
"type": "layers"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: config.Volumes has string array, must be an object (string set)
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"architecture": "amd64",
|
||||
"os": 123,
|
||||
"config": {
|
||||
"Volumes": [
|
||||
"/var/job-result-data",
|
||||
"/var/log/my-app-logs"
|
||||
]
|
||||
},
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
],
|
||||
"type": "layers"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failue: invalid JSON
|
||||
{
|
||||
config: `invalid JSON`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// valid config with optional fields
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"created": "2015-10-31T22:22:56.015925234Z",
|
||||
"author": "Alyssa P. Hacker <alyspdev@example.com>",
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"config": {
|
||||
"User": "1:1",
|
||||
"ExposedPorts": {
|
||||
"8080/tcp": {}
|
||||
},
|
||||
"Env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"FOO=docker_is_a_really",
|
||||
"BAR=great_tool_you_know"
|
||||
],
|
||||
"Entrypoint": [
|
||||
"/bin/sh"
|
||||
],
|
||||
"Cmd": [
|
||||
"--foreground",
|
||||
"--config",
|
||||
"/etc/my-app.d/default.cfg"
|
||||
],
|
||||
"Volumes": {
|
||||
"/var/job-result-data": {},
|
||||
"/var/log/my-app-logs": {}
|
||||
},
|
||||
"StopSignal": "SIGKILL",
|
||||
"WorkingDir": "/home/alice",
|
||||
"Labels": {
|
||||
"com.example.project.git.url": "https://example.com/project.git",
|
||||
"com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
|
||||
}
|
||||
},
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:9d3dd9504c685a304985025df4ed0283e47ac9ffa9bd0326fddf4d59513f0827",
|
||||
"sha256:2b689805fbd00b2db1df73fae47562faac1a626d5f61744bfe29946ecff5d73d"
|
||||
],
|
||||
"type": "layers"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
"created": "2015-10-31T22:22:54.690851953Z",
|
||||
"created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
|
||||
},
|
||||
{
|
||||
"created": "2015-10-31T22:22:55.613815829Z",
|
||||
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
|
||||
"empty_layer": true
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// valid config with only required fields
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
],
|
||||
"type": "layers"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
// expected failure: Env is invalid
|
||||
{
|
||||
config: `
|
||||
{
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"config": {
|
||||
"Env": [
|
||||
"foo"
|
||||
]
|
||||
},
|
||||
"rootfs": {
|
||||
"diff_ids": [
|
||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
],
|
||||
"type": "layers"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
} {
|
||||
r := strings.NewReader(tt.config)
|
||||
err := schema.ValidatorMediaTypeImageConfig.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
33
vendor/github.com/opencontainers/image-spec/schema/content-descriptor.json
generated
vendored
Normal file
33
vendor/github.com/opencontainers/image-spec/schema/content-descriptor.json
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"description": "OpenContainer Content Descriptor Specification",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://opencontainers.org/schema/descriptor",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mediaType": {
|
||||
"description": "the mediatype of the referenced object",
|
||||
"$ref": "defs-descriptor.json#/definitions/mediaType"
|
||||
},
|
||||
"size": {
|
||||
"description": "the size in bytes of the referenced object",
|
||||
"$ref": "defs.json#/definitions/int64"
|
||||
},
|
||||
"digest": {
|
||||
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
|
||||
"$ref": "defs-descriptor.json#/definitions/digest"
|
||||
},
|
||||
"urls": {
|
||||
"description": "a list of urls from which this object may be downloaded",
|
||||
"$ref": "defs-descriptor.json#/definitions/urls"
|
||||
},
|
||||
"annotations": {
|
||||
"id": "https://opencontainers.org/schema/image/descriptor/annotations",
|
||||
"$ref": "defs-descriptor.json#/definitions/annotations"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"mediaType",
|
||||
"size",
|
||||
"digest"
|
||||
]
|
||||
}
|
27
vendor/github.com/opencontainers/image-spec/schema/defs-descriptor.json
generated
vendored
Normal file
27
vendor/github.com/opencontainers/image-spec/schema/defs-descriptor.json
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"description": "Definitions particular to OpenContainer Descriptor Specification",
|
||||
"definitions": {
|
||||
"mediaType": {
|
||||
"id": "https://opencontainers.org/schema/image/descriptor/mediaType",
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}/[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}$"
|
||||
},
|
||||
"digest": {
|
||||
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9]+(?:[+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$"
|
||||
},
|
||||
"urls": {
|
||||
"description": "a list of urls from which this object may be downloaded",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"id": "https://opencontainers.org/schema/image/descriptor/annotations",
|
||||
"$ref": "defs.json#/definitions/mapStringString"
|
||||
}
|
||||
}
|
||||
}
|
91
vendor/github.com/opencontainers/image-spec/schema/defs.json
generated
vendored
Normal file
91
vendor/github.com/opencontainers/image-spec/schema/defs.json
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"description": "Definitions used throughout the OpenContainer Specification",
|
||||
"definitions": {
|
||||
"int8": {
|
||||
"type": "integer",
|
||||
"minimum": -128,
|
||||
"maximum": 127
|
||||
},
|
||||
"int16": {
|
||||
"type": "integer",
|
||||
"minimum": -32768,
|
||||
"maximum": 32767
|
||||
},
|
||||
"int32": {
|
||||
"type": "integer",
|
||||
"minimum": -2147483648,
|
||||
"maximum": 2147483647
|
||||
},
|
||||
"int64": {
|
||||
"type": "integer",
|
||||
"minimum": -9223372036854776000,
|
||||
"maximum": 9223372036854776000
|
||||
},
|
||||
"uint8": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
"uint16": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 65535
|
||||
},
|
||||
"uint32": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4294967295
|
||||
},
|
||||
"uint64": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 18446744073709552000
|
||||
},
|
||||
"uint16Pointer": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/uint16"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uint64Pointer": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/uint64"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stringPointer": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mapStringString": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".{1,}": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapStringObject": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".{1,}": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
303
vendor/github.com/opencontainers/image-spec/schema/descriptor_test.go
generated
vendored
Normal file
303
vendor/github.com/opencontainers/image-spec/schema/descriptor_test.go
generated
vendored
Normal file
|
@ -0,0 +1,303 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
)
|
||||
|
||||
func TestDescriptor(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
descriptor string
|
||||
fail bool
|
||||
}{
|
||||
// valid descriptor
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// expected failure: mediaType missing
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: mediaType does not match pattern (no subtype)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: mediaType does not match pattern (invalid first type character)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": ".foo/bar",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: mediaType does not match pattern (invalid first subtype character)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "foo/.bar",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected success: mediaType has type and subtype as long as possible
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567/1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// expected success: mediaType does not match pattern (type too long)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678/bar",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected success: mediaType does not match pattern (subtype too long)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "foo/12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: size missing
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: size is a string, expected integer
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": "7682",
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: digest missing
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: digest does not match pattern (no algorithm)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": ":5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: digest does not match pattern (no hash)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: digest does not match pattern (invalid aglorithm characters)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "SHA256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: digest does not match pattern (characters needs to be lower for sha256)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5B0BCABD1ED22E9FB1310CF6C2DEC7CDEF19F0AD69EFA1F392E94A4333501270"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected success: valid URL entry
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"urls": [
|
||||
"https://example.com/foo"
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// expected failure: urls does not match format (invalide url characters)
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"urls": [
|
||||
"value"
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
descriptor: `{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
descriptor: `{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
descriptor: `{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
descriptor: `
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256.foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
descriptor: `{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "multihash+base58:QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
// fail: repeated separators in algorithm
|
||||
descriptor: `{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+foo+-b:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
}`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
descriptor: `{
|
||||
"digest": "sha256+b64u:LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564",
|
||||
"size": 1000000,
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
// test for those who cannot use modulo arithmetic to recover padding.
|
||||
descriptor: `{
|
||||
"digest": "sha256+b64u.unknownlength:LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564=",
|
||||
"size": 1000000,
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json"
|
||||
}`,
|
||||
},
|
||||
} {
|
||||
r := strings.NewReader(tt.descriptor)
|
||||
err := schema.ValidatorMediaTypeDescriptor.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
16
vendor/github.com/opencontainers/image-spec/schema/doc.go
generated
vendored
Normal file
16
vendor/github.com/opencontainers/image-spec/schema/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema defines the OCI image media types, schema definitions and validation functions.
|
||||
package schema
|
44
vendor/github.com/opencontainers/image-spec/schema/error.go
generated
vendored
Normal file
44
vendor/github.com/opencontainers/image-spec/schema/error.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"go4.org/errorutil"
|
||||
)
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error
|
||||
// including line, column and offset in the JSON file.
|
||||
type SyntaxError struct {
|
||||
msg string
|
||||
Line, Col int
|
||||
Offset int64
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string { return e.msg }
|
||||
|
||||
// WrapSyntaxError checks whether the given error is a *json.SyntaxError
|
||||
// and converts it into a *schema.SyntaxError containing line/col information using the given reader.
|
||||
// If the given error is not a *json.SyntaxError it is returned unchanged.
|
||||
func WrapSyntaxError(r io.Reader, err error) error {
|
||||
if serr, ok := err.(*json.SyntaxError); ok {
|
||||
line, col, _ := errorutil.HighlightBytePosition(r, serr.Offset)
|
||||
return &SyntaxError{serr.Error(), line, col, serr.Offset}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
321
vendor/github.com/opencontainers/image-spec/schema/fs.go
generated
vendored
Normal file
321
vendor/github.com/opencontainers/image-spec/schema/fs.go
generated
vendored
Normal file
|
@ -0,0 +1,321 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type _escLocalFS struct{}
|
||||
|
||||
var _escLocal _escLocalFS
|
||||
|
||||
type _escStaticFS struct{}
|
||||
|
||||
var _escStatic _escStaticFS
|
||||
|
||||
type _escDirectory struct {
|
||||
fs http.FileSystem
|
||||
name string
|
||||
}
|
||||
|
||||
type _escFile struct {
|
||||
compressed string
|
||||
size int64
|
||||
modtime int64
|
||||
local string
|
||||
isDir bool
|
||||
|
||||
once sync.Once
|
||||
data []byte
|
||||
name string
|
||||
}
|
||||
|
||||
func (_escLocalFS) Open(name string) (http.File, error) {
|
||||
f, present := _escData[path.Clean(name)]
|
||||
if !present {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return os.Open(f.local)
|
||||
}
|
||||
|
||||
func (_escStaticFS) prepare(name string) (*_escFile, error) {
|
||||
f, present := _escData[path.Clean(name)]
|
||||
if !present {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
var err error
|
||||
f.once.Do(func() {
|
||||
f.name = path.Base(name)
|
||||
if f.size == 0 {
|
||||
return
|
||||
}
|
||||
var gr *gzip.Reader
|
||||
b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(f.compressed))
|
||||
gr, err = gzip.NewReader(b64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.data, err = ioutil.ReadAll(gr)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (fs _escStaticFS) Open(name string) (http.File, error) {
|
||||
f, err := fs.prepare(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.File()
|
||||
}
|
||||
|
||||
func (dir _escDirectory) Open(name string) (http.File, error) {
|
||||
return dir.fs.Open(dir.name + name)
|
||||
}
|
||||
|
||||
func (f *_escFile) File() (http.File, error) {
|
||||
type httpFile struct {
|
||||
*bytes.Reader
|
||||
*_escFile
|
||||
}
|
||||
return &httpFile{
|
||||
Reader: bytes.NewReader(f.data),
|
||||
_escFile: f,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Stat() (os.FileInfo, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *_escFile) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f *_escFile) Size() int64 {
|
||||
return f.size
|
||||
}
|
||||
|
||||
func (f *_escFile) Mode() os.FileMode {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *_escFile) ModTime() time.Time {
|
||||
return time.Unix(f.modtime, 0)
|
||||
}
|
||||
|
||||
func (f *_escFile) IsDir() bool {
|
||||
return f.isDir
|
||||
}
|
||||
|
||||
func (f *_escFile) Sys() interface{} {
|
||||
return f
|
||||
}
|
||||
|
||||
// _escFS returns a http.Filesystem for the embedded assets. If useLocal is true,
|
||||
// the filesystem's contents are instead used.
|
||||
func _escFS(useLocal bool) http.FileSystem {
|
||||
if useLocal {
|
||||
return _escLocal
|
||||
}
|
||||
return _escStatic
|
||||
}
|
||||
|
||||
// _escDir returns a http.Filesystem for the embedded assets on a given prefix dir.
|
||||
// If useLocal is true, the filesystem's contents are instead used.
|
||||
func _escDir(useLocal bool, name string) http.FileSystem {
|
||||
if useLocal {
|
||||
return _escDirectory{fs: _escLocal, name: name}
|
||||
}
|
||||
return _escDirectory{fs: _escStatic, name: name}
|
||||
}
|
||||
|
||||
// _escFSByte returns the named file from the embedded assets. If useLocal is
|
||||
// true, the filesystem's contents are instead used.
|
||||
func _escFSByte(useLocal bool, name string) ([]byte, error) {
|
||||
if useLocal {
|
||||
f, err := _escLocal.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := ioutil.ReadAll(f)
|
||||
f.Close()
|
||||
return b, err
|
||||
}
|
||||
f, err := _escStatic.prepare(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.data, nil
|
||||
}
|
||||
|
||||
// _escFSMustByte is the same as _escFSByte, but panics if name is not present.
|
||||
func _escFSMustByte(useLocal bool, name string) []byte {
|
||||
b, err := _escFSByte(useLocal, name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// _escFSString is the string version of _escFSByte.
|
||||
func _escFSString(useLocal bool, name string) (string, error) {
|
||||
b, err := _escFSByte(useLocal, name)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
// _escFSMustString is the string version of _escFSMustByte.
|
||||
func _escFSMustString(useLocal bool, name string) string {
|
||||
return string(_escFSMustByte(useLocal, name))
|
||||
}
|
||||
|
||||
var _escData = map[string]*_escFile{
|
||||
|
||||
"/config-schema.json": {
|
||||
local: "config-schema.json",
|
||||
size: 2771,
|
||||
modtime: 1498025574,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/+RWQW/bPAy9+1cYbo9t/R2+U67dbgMyINh2KIZAsemEnSVqFD3MGPLfB8vJZtmym3XI
|
||||
aScDFB/f4xMl60eSplkJrmC0gmSyVZqtLZhHMqLQAKePZCrcpxsLBVZYKJ9118FuXXEArTrIQcSu8vzZ
|
||||
kbnvow/E+7xkVcn9f//nfeymx2F5hrhVnpMFU5zZnIf12TlqtYe88Pw9UloLHZZ2z1BIH7NMFlgQXLZK
|
||||
u3bSNCsYlED5KzCAOmE0fTkfr4i1km6lVAL3ghoyv3bsUzLVyIF4oVSYzcUBBQppGC7FkLs08+RFJHvg
|
||||
iI9HXPHxDw44iMwwDlh9ztvvlhyU74nFjfG3DJU3ECr30I3ATV5ChQa7UXG5VnbjK697jfH65tucLMWs
|
||||
2uxuuIQCeixjoZE0Pc6QCreW0MiYmwysu56eAoKQblHigswXpIZyR5IXVZimrsNKwzqfoxY86vKf7f0j
|
||||
1Y2GyThf2P9rp/7aXX0i/oJm/wZfdc7fqR3U17ZkE9n4a1qyEbIb3BtVX2xJMvyer18mkip6WV96/ZZY
|
||||
VVssJwZf/6475S91H9CCafRkx7NatcAuizuejFgzhq8Nsv8PP0U8GKtLhhXPnh/QCXEbMz00K2LU3PbM
|
||||
b1D07fCyW0vviMlWxN4UcYpZ/Enjdtf+RQ3SGiZ/vj8oANpKu/UTMV9kR1SDMjPzGZ6y5MQwnZvwWfX7
|
||||
2RSey6SbnWPyMwAA//9KY9sL0woAAA==
|
||||
`,
|
||||
},
|
||||
|
||||
"/content-descriptor.json": {
|
||||
local: "content-descriptor.json",
|
||||
size: 1085,
|
||||
modtime: 1498025574,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/5yTwW7UMBCG73mKUVqpl27NoeIQVb3AnQPcEAevPY6nbGwznlW1oL47mniXJoAo3Vsy
|
||||
+r+Zz8n4RwfQe6yOqQjl1A/QfyiY3uUklhIy6BMmgffHUGb4WNBRIGdn4lpbXFYXcbKKR5EyGPNQc9q0
|
||||
6k3m0Xi2QTZvbk2rXTSO/AmpgzG5YHKnyXXGWtr4X9MbJ4eCSubtAzpptcK5IAth7QfQgwH0E3qyn1q4
|
||||
lf48r0SEOadNIQfQAmNAxuTQw2LGjF8yBuU8hrp5FrvRE18Yj4ESae9qnqfP7FNr0Vf6/pKPRoASbA+C
|
||||
9ZVOfxGhJG9v1xKeRqzygobjQ5E8si2RHLiI7mvdT9DYk1ZzuVZdfS1WBDnB1Z3djZlJ4nQ/3OmP9ejv
|
||||
r875jkfXlf+ed/Uf9hZ21BQ1CIHzBI+RXASJVI/OMNkDbBF8fky7bD36c+xmk5WbTSnLfDtWiv+77DTZ
|
||||
ERcrb5b9zhBc4s2zO7r2jN/2xKhin3+/McttXS9NB/Cle+p+BgAA///HjexwPQQAAA==
|
||||
`,
|
||||
},
|
||||
|
||||
"/defs-descriptor.json": {
|
||||
local: "defs-descriptor.json",
|
||||
size: 922,
|
||||
modtime: 1498025574,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/6STX2/TMBTF3/spLl7FgDZN4QFp0Ria2DsP42lTV93ZN/Ed8R/ZrqYy9bsjJ1naFYFA
|
||||
PCSyj67Pub8b52kCIBRFGdgndlZUIK6oZst5F8FjSCw3LQZIDr56sl+cTciWAlwNx1yAa0+Sa5bYecx7
|
||||
09FFVJBzAIQhxfht62mUAASrnKpT8rEqS+fJyueMuHChKaPUZLBkgw2Vakwt927zZ6/Ue4uYAttmr3tM
|
||||
iUKHd3d7Wdxg8WNZnK32y1cn09fF3XoxWz0t5+8/fNyVf1c2FV3Erk8SihuK6ZDuaLhJE8iw9ck1Ab1m
|
||||
CVKT/B43Bvqz4GrIRe7+gWSaA9tuOwDA6Tm2jQuctLmozvOoFKmL03+cwMA1e/O5up0t1sVqVN6+q/L6
|
||||
srhZFmef1sVqdkS4CW38Ax9Cyz1ELoQ6OAOPmqWGpDkOVGBwC/cEyj3a1qEi9Wv/GAJu9zInMoe5vycF
|
||||
ELULBvNXEJvAYtB3LzDQWpfw5fX8n7t46Dc2PQ1UZz9FdVw8RGdPyoPfojTor7ve+/cw50l+dpOfAQAA
|
||||
//8aH/C2mgMAAA==
|
||||
`,
|
||||
},
|
||||
|
||||
"/defs.json": {
|
||||
local: "defs.json",
|
||||
size: 1670,
|
||||
modtime: 1498025574,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/7STza6bMBCF9zzFyO2S9oJtbGDb7hMpy6oLSiaJq2AjY6RWEe9e8RNChFuJKneRgGc8
|
||||
3zmeMbcAgByxKa2qnTKa5EC+4klp1a8aaBs8grtY054vpnXgLgi7GvUXo12hNFo41FiqkyqLoTwceTOA
|
||||
5NBLABClXTqvAIj7XWOvprTDM9qhckhUSquqrUgOn2KaPsLFrykcUzkEu3Amx2IrmlEpfPA+vsIzuhVP
|
||||
Yy55ygT3aczJlZDgW4UyShmTNGIiTbiUIooij6Jn15N0+x/T8enQJFlxN8/GBxZJwtbozXPxoTnNeCYk
|
||||
zdb8zePw8eOUcyE5jySTUZYk1Nf8WOxNz7VLQaNxdyI5fJsCMKeG9EeLfZZ8eFt8cG9Ty+eNXeivvp9G
|
||||
t9frYvf09t3Ti1c6FPy1DhtnlT5vd3jXGOtf66kq6sOAHf99V8n8+Imle9ykunAOrd5bU6N1CptFEQD5
|
||||
fIvD7in0ryMEy+fK1G6UfmdTE+tvpoL+1wV/AgAA//96IpqyhgYAAA==
|
||||
`,
|
||||
},
|
||||
|
||||
"/image-index-schema.json": {
|
||||
local: "image-index-schema.json",
|
||||
size: 2993,
|
||||
modtime: 1498025574,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/6yWv27bMBDGdz/FQQmQJYmKIuhgBFnaJVOHBl2KDAx5ki61SPVIJ3ELv3tBMrIlUXZt
|
||||
1Zt95H33+07892cGkCm0kqlxZHQ2h+xrg/qz0U6QRob7WpQI91rhG3xrUFJBUoSplz733MoKa+HzKuea
|
||||
eZ4/W6OvYvTacJkrFoW7+nCTx9hZzCPVpth5npsGtWxL2pAWZ+fky+fky8dEt2rQp5qnZ5Quxho2DbIj
|
||||
tNkcvCWALOZ/R7bRVgynbh8qslAQLhTYaA8tuAohVIZQGaIYvEQ1EBaEBtIOS+SAEJQneMr7mBup1mVS
|
||||
oyZN9bLO5vBxGxNvbSyE1nEkq4WmAq2zXfutsmAWqw67w7o772g7bbEv7+01W+jxr/Y+wvhrSYy+1o9N
|
||||
1MOjIvHg0y67YUu/BxFFJVqXbUKPHfGRhZHI9wfSBeLXQpjtPYApwuJgLJBRS1SQWAoi54yFz1ZY2Cu1
|
||||
6cm13x1nucKCNPkKNt+SdBTWqelDOP1EIA1PK4d2EusIIGn36WY33Hv/D8GTvGqcKVk0FUmQFcqfdllD
|
||||
VGhxI+Olt+H/NsI5ZA0Xt2JRGiZX1XfzW78WFaq7i+l9H66boa8lL4arJnUlYEER3U+Hgk0NrxXJCpw/
|
||||
V6IXqMUKnhCUedULIxSq6dSBaidzsxCuMFyn3Mdt5o3OgHPnNoY9WzmMCZYVOZRuyTjIA8hMz1NvD8Pe
|
||||
fZxqp+OT3ed7oTvtsI5Jl9lgwnrM5inxjD0N1PVLckueAm4jexrIAoX/Dqdu4VZ3D2b/suyWTa7Ng00C
|
||||
rP9p+0UwCZ0erof0cLbrX//IEFobFx50I6fdcV3dHlx5V3XyWdcVmY15aX+te8+ecUeTXmdjNv7HgAcN
|
||||
mOlZmY29BDtPuBnA42w9+xsAAP//IKe/nbELAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/image-layout-schema.json": {
|
||||
local: "image-layout-schema.json",
|
||||
size: 439,
|
||||
modtime: 1498025574,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/2yPQUvEMBCF7/0VQ/Sg4DYVPOW6pwVhD4IX8VDTaTvLNonJVFik/12SaRXRU5g38+W9
|
||||
91kBqA6TjRSYvFMG1DGg23vHLTmMcJjaAeGxvfiZ4cmOOLXqLlPXSQYDamQORutT8m4nau3joLvY9rxr
|
||||
HrRoV8JRtyHJaO0DOruZpYLJtaZsrM/FWEi+BMysfzuhXbUQfcDIhEkZyG2yQyYl8TPGJLVk97fth1yA
|
||||
74FHhOP+8LvyDbmy8JZ2EgZ6OuNtsS8fbrESR3LDj45unpSBl3UGUPd1UzdqnV/Lu1QAS2kS8X2miN03
|
||||
8l+PKnNL9RUAAP//k31n5bcBAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/image-manifest-schema.json": {
|
||||
local: "image-manifest-schema.json",
|
||||
size: 921,
|
||||
modtime: 1498025574,
|
||||
compressed: `
|
||||
H4sIAAAJbogA/5ySMW8iMRCF+/0VI0MJ+O501bZXUZxSJEoTpXB2x7uDWNsZmygo4r9HtnHAkCKifTvv
|
||||
zTdv/dEAiB59x+QCWSNaEHcOzT9rgiKDDOtJDQj/lSGNPsC9w440dSpNL6J97rsRJxWtYwiulXLjrVlm
|
||||
dWV5kD0rHZa//sqszbKP+mLxrZTWoenKVp9seVpSJJDTkSB7w95hdNuXDXZHzbF1yIHQixbiYQAiRzwi
|
||||
+3xclq9vfhjJgybc9uDzheghjAhpOZTlkPPgLQeC8qAMkAk4ICeKFH7bZbKG/Uort16tmcjQtJtEC39O
|
||||
mnovWpIO+YvorNE0nDcwZ9QxNqKhCcvSiOVV/H+ism/VHtmf2wuVYlb7imkdcIqjv099HJVi/ul2gENF
|
||||
oYyxIb28CuXGus/TFpet9Kj9JdRM9qjJULJU9qawJlLB+Lojxoj19N07rP9JXXED8Nwcms8AAAD//7u3
|
||||
Dj+ZAwAA
|
||||
`,
|
||||
},
|
||||
|
||||
"/": {
|
||||
isDir: true,
|
||||
local: "/",
|
||||
},
|
||||
}
|
21
vendor/github.com/opencontainers/image-spec/schema/gen.go
generated
vendored
Normal file
21
vendor/github.com/opencontainers/image-spec/schema/gen.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema
|
||||
|
||||
// Generates an embbedded http.FileSystem for all schema files
|
||||
// using esc (https://github.com/mjibson/esc).
|
||||
|
||||
// This should generally be invoked with `make schema-fs`
|
||||
//go:generate esc -private -pkg=schema -include=.*\.json$ .
|
89
vendor/github.com/opencontainers/image-spec/schema/image-index-schema.json
generated
vendored
Normal file
89
vendor/github.com/opencontainers/image-spec/schema/image-index-schema.json
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"description": "OpenContainer Image Index Specification",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://opencontainers.org/schema/image/index",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"description": "This field specifies the image index schema version as an integer",
|
||||
"id": "https://opencontainers.org/schema/image/index/schemaVersion",
|
||||
"type": "integer",
|
||||
"minimum": 2,
|
||||
"maximum": 2
|
||||
},
|
||||
"manifests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"id": "https://opencontainers.org/schema/image/manifestDescriptor",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"mediaType",
|
||||
"size",
|
||||
"digest"
|
||||
],
|
||||
"properties": {
|
||||
"mediaType": {
|
||||
"description": "the mediatype of the referenced object",
|
||||
"$ref": "defs-descriptor.json#/definitions/mediaType"
|
||||
},
|
||||
"size": {
|
||||
"description": "the size in bytes of the referenced object",
|
||||
"$ref": "defs.json#/definitions/int64"
|
||||
},
|
||||
"digest": {
|
||||
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
|
||||
"$ref": "defs-descriptor.json#/definitions/digest"
|
||||
},
|
||||
"urls": {
|
||||
"description": "a list of urls from which this object may be downloaded",
|
||||
"$ref": "defs-descriptor.json#/definitions/urls"
|
||||
},
|
||||
"platform": {
|
||||
"id": "https://opencontainers.org/schema/image/platform",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"architecture",
|
||||
"os"
|
||||
],
|
||||
"properties": {
|
||||
"architecture": {
|
||||
"id": "https://opencontainers.org/schema/image/platform/architecture",
|
||||
"type": "string"
|
||||
},
|
||||
"os": {
|
||||
"id": "https://opencontainers.org/schema/image/platform/os",
|
||||
"type": "string"
|
||||
},
|
||||
"os.version": {
|
||||
"id": "https://opencontainers.org/schema/image/platform/os.version",
|
||||
"type": "string"
|
||||
},
|
||||
"os.features": {
|
||||
"id": "https://opencontainers.org/schema/image/platform/os.features",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"variant": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"id": "https://opencontainers.org/schema/image/descriptor/annotations",
|
||||
"$ref": "defs-descriptor.json#/definitions/annotations"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"id": "https://opencontainers.org/schema/image/index/annotations",
|
||||
"$ref": "defs-descriptor.json#/definitions/annotations"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"manifests"
|
||||
]
|
||||
}
|
18
vendor/github.com/opencontainers/image-spec/schema/image-layout-schema.json
generated
vendored
Normal file
18
vendor/github.com/opencontainers/image-spec/schema/image-layout-schema.json
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"description": "OpenContainer Image Layout Schema",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://opencontainers.org/schema/image/layout",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"imageLayoutVersion": {
|
||||
"description": "version of the OCI Image Layout (in the oci-layout file)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"1.0.0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"imageLayoutVersion"
|
||||
]
|
||||
}
|
34
vendor/github.com/opencontainers/image-spec/schema/image-manifest-schema.json
generated
vendored
Normal file
34
vendor/github.com/opencontainers/image-spec/schema/image-manifest-schema.json
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"description": "OpenContainer Image Manifest Specification",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "https://opencontainers.org/schema/image/manifest",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"description": "This field specifies the image manifest schema version as an integer",
|
||||
"id": "https://opencontainers.org/schema/image/manifest/schemaVersion",
|
||||
"type": "integer",
|
||||
"minimum": 2,
|
||||
"maximum": 2
|
||||
},
|
||||
"config": {
|
||||
"$ref": "content-descriptor.json"
|
||||
},
|
||||
"layers": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "content-descriptor.json"
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"id": "https://opencontainers.org/schema/image/manifest/annotations",
|
||||
"$ref": "defs-descriptor.json#/definitions/annotations"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"config",
|
||||
"layers"
|
||||
]
|
||||
}
|
232
vendor/github.com/opencontainers/image-spec/schema/imageindex_test.go
generated
vendored
Normal file
232
vendor/github.com/opencontainers/image-spec/schema/imageindex_test.go
generated
vendored
Normal file
|
@ -0,0 +1,232 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
)
|
||||
|
||||
func TestImageIndex(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
imageIndex string
|
||||
fail bool
|
||||
}{
|
||||
// expected failure: mediaType does not match pattern
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "invalid",
|
||||
"size": 7143,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||||
"platform": {
|
||||
"architecture": "ppc64le",
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: manifest.size is string, expected integer
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": "7682",
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: manifest.digest is missing, expected required
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: in the optional field platform platform.architecture is missing, expected required
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"platform": {
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: invalid referenced manifest media type
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "invalid",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: empty referenced manifest media type
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// valid image index, with optional fields
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7143,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||||
"platform": {
|
||||
"architecture": "ppc64le",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"com.example.key1": "value1",
|
||||
"com.example.key2": "value2"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// valid image index, with required fields only
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"size": 7143,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// valid image index, with customized media type of referenced manifest
|
||||
{
|
||||
imageIndex: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/customized.manifest+json",
|
||||
"size": 7143,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||||
"platform": {
|
||||
"architecture": "ppc64le",
|
||||
"os": "linux"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
} {
|
||||
r := strings.NewReader(tt.imageIndex)
|
||||
err := schema.ValidatorMediaTypeImageIndex.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
56
vendor/github.com/opencontainers/image-spec/schema/imagelayout_test.go
generated
vendored
Normal file
56
vendor/github.com/opencontainers/image-spec/schema/imagelayout_test.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
)
|
||||
|
||||
func TestImageLayout(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
imageLayout string
|
||||
fail bool
|
||||
}{
|
||||
// expected faulure: imageLayoutVersion does not match pattern
|
||||
{
|
||||
imageLayout: `
|
||||
{
|
||||
"imageLayoutVersion": 1.0.0
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// validate layout
|
||||
{
|
||||
imageLayout: `
|
||||
{
|
||||
"imageLayoutVersion": "1.0.0"
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
} {
|
||||
r := strings.NewReader(tt.imageLayout)
|
||||
err := schema.ValidatorMediaTypeLayoutHeader.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
239
vendor/github.com/opencontainers/image-spec/schema/manifest_test.go
generated
vendored
Normal file
239
vendor/github.com/opencontainers/image-spec/schema/manifest_test.go
generated
vendored
Normal file
|
@ -0,0 +1,239 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
)
|
||||
|
||||
func TestManifest(t *testing.T) {
|
||||
for i, tt := range []struct {
|
||||
manifest string
|
||||
fail bool
|
||||
}{
|
||||
// expected failure: mediaType does not match pattern
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "invalid",
|
||||
"size": 1470,
|
||||
"digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 148,
|
||||
"digest": "sha256:c57089565e894899735d458f0fd4bb17a0f1e0df8d72da392b85c9b35ee777cd"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: config.size is a string, expected integer
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": "1470",
|
||||
"digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 148,
|
||||
"digest": "sha256:c57089565e894899735d458f0fd4bb17a0f1e0df8d72da392b85c9b35ee777cd"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected failure: layers.size is string, expected integer
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": "675598",
|
||||
"digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// valid manifest with optional fields
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 675598,
|
||||
"digest": "sha256:9d3dd9504c685a304985025df4ed0283e47ac9ffa9bd0326fddf4d59513f0827"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 156,
|
||||
"digest": "sha256:2b689805fbd00b2db1df73fae47562faac1a626d5f61744bfe29946ecff5d73d"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 148,
|
||||
"digest": "sha256:c57089565e894899735d458f0fd4bb17a0f1e0df8d72da392b85c9b35ee777cd"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"key1": "value1",
|
||||
"key2": "value2"
|
||||
}
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// valid manifest with only required fields
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 675598,
|
||||
"digest": "sha256:9d3dd9504c685a304985025df4ed0283e47ac9ffa9bd0326fddf4d59513f0827"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 156,
|
||||
"digest": "sha256:2b689805fbd00b2db1df73fae47562faac1a626d5f61744bfe29946ecff5d73d"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"size": 148,
|
||||
"digest": "sha256:c57089565e894899735d458f0fd4bb17a0f1e0df8d72da392b85c9b35ee777cd"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: false,
|
||||
},
|
||||
|
||||
// expected failure: empty layer, expected at least one
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": []
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
|
||||
// expected pass: test bounds of algorithm field in digest.
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256.foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "multihash+base58:QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
// expected failure: push bounds of algorithm field in digest too far.
|
||||
{
|
||||
manifest: `
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"size": 1470,
|
||||
"digest": "sha256+foo+-b:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
},
|
||||
} {
|
||||
r := strings.NewReader(tt.manifest)
|
||||
err := schema.ValidatorMediaTypeManifest.Validate(r)
|
||||
|
||||
if got := err != nil; tt.fail != got {
|
||||
t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
|
||||
}
|
||||
}
|
||||
}
|
52
vendor/github.com/opencontainers/image-spec/schema/schema.go
generated
vendored
Normal file
52
vendor/github.com/opencontainers/image-spec/schema/schema.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// Media types for the OCI image formats
|
||||
const (
|
||||
ValidatorMediaTypeDescriptor Validator = v1.MediaTypeDescriptor
|
||||
ValidatorMediaTypeLayoutHeader Validator = v1.MediaTypeLayoutHeader
|
||||
ValidatorMediaTypeManifest Validator = v1.MediaTypeImageManifest
|
||||
ValidatorMediaTypeImageIndex Validator = v1.MediaTypeImageIndex
|
||||
ValidatorMediaTypeImageConfig Validator = v1.MediaTypeImageConfig
|
||||
ValidatorMediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer
|
||||
)
|
||||
|
||||
var (
|
||||
// fs stores the embedded http.FileSystem
|
||||
// having the OCI JSON schema files in root "/".
|
||||
fs = _escFS(false)
|
||||
|
||||
// specs maps OCI schema media types to schema files.
|
||||
specs = map[Validator]string{
|
||||
ValidatorMediaTypeDescriptor: "content-descriptor.json",
|
||||
ValidatorMediaTypeLayoutHeader: "image-layout-schema.json",
|
||||
ValidatorMediaTypeManifest: "image-manifest-schema.json",
|
||||
ValidatorMediaTypeImageIndex: "image-index-schema.json",
|
||||
ValidatorMediaTypeImageConfig: "config-schema.json",
|
||||
}
|
||||
)
|
||||
|
||||
// FileSystem returns an in-memory filesystem including the schema files.
|
||||
// The schema files are located at the root directory.
|
||||
func FileSystem() http.FileSystem {
|
||||
return fs
|
||||
}
|
191
vendor/github.com/opencontainers/image-spec/schema/spec_test.go
generated
vendored
Normal file
191
vendor/github.com/opencontainers/image-spec/schema/spec_test.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/image-spec/schema"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
var (
|
||||
errFormatInvalid = errors.New("format: invalid")
|
||||
)
|
||||
|
||||
func TestValidateDescriptor(t *testing.T) {
|
||||
validate(t, "../descriptor.md")
|
||||
}
|
||||
|
||||
func TestValidateManifest(t *testing.T) {
|
||||
validate(t, "../manifest.md")
|
||||
}
|
||||
|
||||
func TestValidateImageIndex(t *testing.T) {
|
||||
validate(t, "../image-index.md")
|
||||
}
|
||||
|
||||
func TestValidateImageLayout(t *testing.T) {
|
||||
validate(t, "../image-layout.md")
|
||||
}
|
||||
|
||||
func TestValidateConfig(t *testing.T) {
|
||||
validate(t, "../config.md")
|
||||
}
|
||||
|
||||
// TODO(sur): include examples from all specification files
|
||||
func validate(t *testing.T, name string) {
|
||||
m, err := os.Open(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer m.Close()
|
||||
|
||||
examples, err := extractExamples(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, example := range examples {
|
||||
if example.Err == errFormatInvalid && example.Mediatype == "" { // ignore
|
||||
continue
|
||||
}
|
||||
|
||||
if example.Err != nil {
|
||||
printFields(t, "error", example.Mediatype, example.Title, example.Err)
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = schema.Validator(example.Mediatype).Validate(strings.NewReader(example.Body))
|
||||
if err == nil {
|
||||
printFields(t, "ok", example.Mediatype, example.Title)
|
||||
t.Log(example.Body, "---")
|
||||
continue
|
||||
}
|
||||
|
||||
var errs []error
|
||||
if verr, ok := errors.Cause(err).(schema.ValidationError); ok {
|
||||
errs = verr.Errs
|
||||
} else {
|
||||
printFields(t, "error", example.Mediatype, example.Title, err)
|
||||
t.Error(err)
|
||||
t.Log(example.Body, "---")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, err := range errs {
|
||||
// TOOD(stevvooe): This is nearly useless without file, line no.
|
||||
printFields(t, "invalid", example.Mediatype, example.Title)
|
||||
t.Error(err)
|
||||
fmt.Println(example.Body, "---")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// renderer allows one to incercept fenced blocks in markdown documents.
|
||||
type renderer struct {
|
||||
blackfriday.Renderer
|
||||
fn func(text []byte, lang string)
|
||||
}
|
||||
|
||||
func (r *renderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
|
||||
r.fn(text, lang)
|
||||
r.Renderer.BlockCode(out, text, lang)
|
||||
}
|
||||
|
||||
type example struct {
|
||||
Lang string // gets raw "lang" field
|
||||
Title string
|
||||
Mediatype string
|
||||
Body string
|
||||
Err error
|
||||
|
||||
// TODO(stevvooe): Figure out how to keep track of revision, file, line so
|
||||
// that we can trace back verification output.
|
||||
}
|
||||
|
||||
// parseExample treats the field as a syntax,attribute tuple separated by a comma.
|
||||
// Attributes are encoded as a url values.
|
||||
//
|
||||
// An example of this is `json,title=Foo%20Bar&mediatype=application/json. We
|
||||
// get that the "lang" is json, the title is "Foo Bar" and the mediatype is
|
||||
// "application/json".
|
||||
//
|
||||
// This preserves syntax highlighting and lets us tag examples with further
|
||||
// metadata.
|
||||
func parseExample(lang, body string) (e example) {
|
||||
e.Lang = lang
|
||||
e.Body = body
|
||||
|
||||
parts := strings.SplitN(lang, ",", 2)
|
||||
if len(parts) < 2 {
|
||||
e.Err = errFormatInvalid
|
||||
return
|
||||
}
|
||||
|
||||
m, err := url.ParseQuery(parts[1])
|
||||
if err != nil {
|
||||
e.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
e.Mediatype = m.Get("mediatype")
|
||||
e.Title = m.Get("title")
|
||||
return
|
||||
}
|
||||
|
||||
func extractExamples(rd io.Reader) ([]example, error) {
|
||||
p, err := ioutil.ReadAll(rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var examples []example
|
||||
renderer := &renderer{
|
||||
Renderer: blackfriday.HtmlRenderer(0, "test test", ""),
|
||||
fn: func(text []byte, lang string) {
|
||||
examples = append(examples, parseExample(lang, string(text)))
|
||||
},
|
||||
}
|
||||
|
||||
// just pass over the markdown and ignore the rendered result. We just want
|
||||
// the side-effect of calling back for each code block.
|
||||
// TODO(stevvooe): Consider just parsing these with a scanner. It will be
|
||||
// faster and we can retain file, line no.
|
||||
blackfriday.MarkdownOptions(p, renderer, blackfriday.Options{
|
||||
Extensions: blackfriday.EXTENSION_FENCED_CODE,
|
||||
})
|
||||
|
||||
return examples, nil
|
||||
}
|
||||
|
||||
// printFields prints each value tab separated.
|
||||
func printFields(t *testing.T, vs ...interface{}) {
|
||||
var ss []string
|
||||
for _, f := range vs {
|
||||
ss = append(ss, fmt.Sprint(f))
|
||||
}
|
||||
t.Log(strings.Join(ss, "\t"))
|
||||
}
|
224
vendor/github.com/opencontainers/image-spec/schema/validator.go
generated
vendored
Normal file
224
vendor/github.com/opencontainers/image-spec/schema/validator.go
generated
vendored
Normal file
|
@ -0,0 +1,224 @@
|
|||
// Copyright 2016 The Linux Foundation
|
||||
//
|
||||
// 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 schema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
// Validator wraps a media type string identifier
|
||||
// and implements validation against a JSON schema.
|
||||
type Validator string
|
||||
|
||||
type validateFunc func(r io.Reader) error
|
||||
|
||||
var mapValidate = map[Validator]validateFunc{
|
||||
ValidatorMediaTypeImageConfig: validateConfig,
|
||||
ValidatorMediaTypeDescriptor: validateDescriptor,
|
||||
ValidatorMediaTypeImageIndex: validateIndex,
|
||||
ValidatorMediaTypeManifest: validateManifest,
|
||||
}
|
||||
|
||||
// ValidationError contains all the errors that happened during validation.
|
||||
type ValidationError struct {
|
||||
Errs []error
|
||||
}
|
||||
|
||||
func (e ValidationError) Error() string {
|
||||
return fmt.Sprintf("%v", e.Errs)
|
||||
}
|
||||
|
||||
// Validate validates the given reader against the schema of the wrapped media type.
|
||||
func (v Validator) Validate(src io.Reader) error {
|
||||
buf, err := ioutil.ReadAll(src)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to read the document file")
|
||||
}
|
||||
|
||||
if f, ok := mapValidate[v]; ok {
|
||||
if f == nil {
|
||||
return fmt.Errorf("internal error: mapValidate[%q] is nil", v)
|
||||
}
|
||||
err = f(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sl := gojsonschema.NewReferenceLoaderFileSystem("file:///"+specs[v], fs)
|
||||
ml := gojsonschema.NewStringLoader(string(buf))
|
||||
|
||||
result, err := gojsonschema.Validate(sl, ml)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
WrapSyntaxError(bytes.NewReader(buf), err),
|
||||
"schema %s: unable to validate", v)
|
||||
}
|
||||
|
||||
if result.Valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs := make([]error, 0, len(result.Errors()))
|
||||
for _, desc := range result.Errors() {
|
||||
errs = append(errs, fmt.Errorf("%s", desc))
|
||||
}
|
||||
|
||||
return ValidationError{
|
||||
Errs: errs,
|
||||
}
|
||||
}
|
||||
|
||||
type unimplemented string
|
||||
|
||||
func (v unimplemented) Validate(src io.Reader) error {
|
||||
return fmt.Errorf("%s: unimplemented", v)
|
||||
}
|
||||
|
||||
func validateManifest(r io.Reader) error {
|
||||
header := v1.Manifest{}
|
||||
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading the io stream")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "manifest format mismatch")
|
||||
}
|
||||
|
||||
if header.Config.MediaType != string(v1.MediaTypeImageConfig) {
|
||||
fmt.Printf("warning: config %s has an unknown media type: %s\n", header.Config.Digest, header.Config.MediaType)
|
||||
}
|
||||
|
||||
for _, layer := range header.Layers {
|
||||
if layer.MediaType != string(v1.MediaTypeImageLayer) &&
|
||||
layer.MediaType != string(v1.MediaTypeImageLayerGzip) &&
|
||||
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) &&
|
||||
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableGzip) {
|
||||
fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDescriptor(r io.Reader) error {
|
||||
header := v1.Descriptor{}
|
||||
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading the io stream")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "descriptor format mismatch")
|
||||
}
|
||||
|
||||
err = header.Digest.Validate()
|
||||
if err == digest.ErrDigestUnsupported {
|
||||
// we ignore unsupported algorithms
|
||||
fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func validateIndex(r io.Reader) error {
|
||||
header := v1.Index{}
|
||||
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading the io stream")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "index format mismatch")
|
||||
}
|
||||
|
||||
for _, manifest := range header.Manifests {
|
||||
if manifest.MediaType != string(v1.MediaTypeImageManifest) {
|
||||
fmt.Printf("warning: manifest %s has an unknown media type: %s\n", manifest.Digest, manifest.MediaType)
|
||||
}
|
||||
if manifest.Platform != nil {
|
||||
checkPlatform(manifest.Platform.OS, manifest.Platform.Architecture)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateConfig(r io.Reader) error {
|
||||
header := v1.Image{}
|
||||
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading the io stream")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf, &header)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "config format mismatch")
|
||||
}
|
||||
|
||||
checkPlatform(header.OS, header.Architecture)
|
||||
|
||||
envRegexp := regexp.MustCompile(`^[^=]+=.*$`)
|
||||
for _, e := range header.Config.Env {
|
||||
if !envRegexp.MatchString(e) {
|
||||
return errors.Errorf("unexpected env: %q", e)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPlatform(OS string, Architecture string) {
|
||||
validCombins := map[string][]string{
|
||||
"android": {"arm"},
|
||||
"darwin": {"386", "amd64", "arm", "arm64"},
|
||||
"dragonfly": {"amd64"},
|
||||
"freebsd": {"386", "amd64", "arm"},
|
||||
"linux": {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le", "s390x"},
|
||||
"netbsd": {"386", "amd64", "arm"},
|
||||
"openbsd": {"386", "amd64", "arm"},
|
||||
"plan9": {"386", "amd64"},
|
||||
"solaris": {"amd64"},
|
||||
"windows": {"386", "amd64"}}
|
||||
for os, archs := range validCombins {
|
||||
if os == OS {
|
||||
for _, arch := range archs {
|
||||
if arch == Architecture {
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Printf("warning: combination of %q and %q is invalid.", OS, Architecture)
|
||||
}
|
||||
}
|
||||
fmt.Printf("warning: operating system %q of the bundle is not supported yet.", OS)
|
||||
}
|
1
vendor/github.com/opencontainers/runc/.gitignore
generated
vendored
1
vendor/github.com/opencontainers/runc/.gitignore
generated
vendored
|
@ -1,5 +1,6 @@
|
|||
vendor/pkg
|
||||
/runc
|
||||
/runc-*
|
||||
contrib/cmd/recvtty/recvtty
|
||||
man/man8
|
||||
release
|
||||
|
|
2
vendor/github.com/opencontainers/runc/.travis.yml
generated
vendored
2
vendor/github.com/opencontainers/runc/.travis.yml
generated
vendored
|
@ -32,4 +32,4 @@ before_install:
|
|||
script:
|
||||
- git-validation -run DCO,short-subject -v
|
||||
- make BUILDTAGS="${BUILDTAGS}"
|
||||
- make BUILDTAGS="${BUILDTAGS}" clean ci
|
||||
- make BUILDTAGS="${BUILDTAGS}" clean ci cross
|
||||
|
|
14
vendor/github.com/opencontainers/runc/Dockerfile
generated
vendored
14
vendor/github.com/opencontainers/runc/Dockerfile
generated
vendored
|
@ -1,6 +1,10 @@
|
|||
FROM golang:1.10-stretch
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
RUN dpkg --add-architecture armel \
|
||||
&& dpkg --add-architecture armhf \
|
||||
&& dpkg --add-architecture arm64 \
|
||||
&& dpkg --add-architecture ppc64el \
|
||||
&& apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
curl \
|
||||
sudo \
|
||||
|
@ -21,6 +25,8 @@ RUN apt-get update && apt-get install -y \
|
|||
python-minimal \
|
||||
uidmap \
|
||||
kmod \
|
||||
crossbuild-essential-armel crossbuild-essential-armhf crossbuild-essential-arm64 crossbuild-essential-ppc64el \
|
||||
libseccomp-dev:armel libseccomp-dev:armhf libseccomp-dev:arm64 libseccomp-dev:ppc64el \
|
||||
--no-install-recommends \
|
||||
&& apt-get clean
|
||||
|
||||
|
@ -39,10 +45,14 @@ RUN cd /tmp \
|
|||
&& rm -rf /tmp/bats
|
||||
|
||||
# install criu
|
||||
ENV CRIU_VERSION v3.7
|
||||
# For criu v3.10, a patch is needed for `@test "checkpoint --lazy-pages and restore"`.
|
||||
# Starting with v3.11, the patch will no longer be needed.
|
||||
# See https://github.com/opencontainers/runc/issues/1863#issuecomment-412074413
|
||||
ENV CRIU_VERSION v3.10
|
||||
RUN mkdir -p /usr/src/criu \
|
||||
&& curl -sSL https://github.com/checkpoint-restore/criu/archive/${CRIU_VERSION}.tar.gz | tar -v -C /usr/src/criu/ -xz --strip-components=1 \
|
||||
&& cd /usr/src/criu \
|
||||
&& curl https://github.com/checkpoint-restore/criu/commit/27034e7c64b00a1f2467afb5ebb1d5b9b1a06ce1.patch | patch -p1 \
|
||||
&& make install-criu \
|
||||
&& rm -rf /usr/src/criu
|
||||
|
||||
|
|
32
vendor/github.com/opencontainers/runc/Makefile
generated
vendored
32
vendor/github.com/opencontainers/runc/Makefile
generated
vendored
|
@ -1,6 +1,7 @@
|
|||
.PHONY: all shell dbuild man release \
|
||||
localtest localunittest localintegration \
|
||||
test unittest integration
|
||||
test unittest integration \
|
||||
cross localcross
|
||||
|
||||
GO := go
|
||||
|
||||
|
@ -39,14 +40,14 @@ contrib/cmd/recvtty/recvtty: $(SOURCES)
|
|||
$(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
|
||||
|
||||
static: $(SOURCES)
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o runc .
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo osusergo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o runc .
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo osusergo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
|
||||
|
||||
release:
|
||||
script/release.sh -r release/$(VERSION) -v $(VERSION)
|
||||
|
||||
dbuild: runcimage
|
||||
docker run --rm -v $(CURDIR):/go/src/$(PROJECT) --privileged $(RUNC_IMAGE) make clean all
|
||||
docker run ${DOCKER_RUN_PROXY} --rm -v $(CURDIR):/go/src/$(PROJECT) --privileged $(RUNC_IMAGE) make clean all
|
||||
|
||||
lint:
|
||||
$(GO) vet $(allpackages)
|
||||
|
@ -56,7 +57,7 @@ man:
|
|||
man/md2man-all.sh
|
||||
|
||||
runcimage:
|
||||
docker build -t $(RUNC_IMAGE) .
|
||||
docker build ${DOCKER_BUILD_PROXY} -t $(RUNC_IMAGE) .
|
||||
|
||||
test:
|
||||
make unittest integration rootlessintegration
|
||||
|
@ -65,25 +66,25 @@ localtest:
|
|||
make localunittest localintegration localrootlessintegration
|
||||
|
||||
unittest: runcimage
|
||||
docker run -e TESTFLAGS -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localunittest
|
||||
docker run ${DOCKER_RUN_PROXY} -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localunittest TESTFLAGS=${TESTFLAGS}
|
||||
|
||||
localunittest: all
|
||||
$(GO) test -timeout 3m -tags "$(BUILDTAGS)" ${TESTFLAGS} -v $(allpackages)
|
||||
|
||||
integration: runcimage
|
||||
docker run -e TESTFLAGS -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localintegration
|
||||
docker run ${DOCKER_RUN_PROXY} -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localintegration TESTPATH=${TESTPATH}
|
||||
|
||||
localintegration: all
|
||||
bats -t tests/integration${TESTFLAGS}
|
||||
bats -t tests/integration${TESTPATH}
|
||||
|
||||
rootlessintegration: runcimage
|
||||
docker run -e TESTFLAGS -t --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localrootlessintegration
|
||||
docker run ${DOCKER_RUN_PROXY} -t --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localrootlessintegration
|
||||
|
||||
localrootlessintegration: all
|
||||
tests/rootless.sh
|
||||
|
||||
shell: runcimage
|
||||
docker run -e TESTFLAGS -ti --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) bash
|
||||
docker run ${DOCKER_RUN_PROXY} -ti --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) bash
|
||||
|
||||
install:
|
||||
install -D -m0755 runc $(BINDIR)/runc
|
||||
|
@ -105,7 +106,7 @@ uninstall-man:
|
|||
rm -f $(addprefix $(MAN_INSTALL_PATH),$(MAN_PAGES_BASE))
|
||||
|
||||
clean:
|
||||
rm -f runc
|
||||
rm -f runc runc-*
|
||||
rm -f contrib/cmd/recvtty/recvtty
|
||||
rm -rf $(RELEASE_DIR)
|
||||
rm -rf $(MAN_DIR)
|
||||
|
@ -117,6 +118,15 @@ validate:
|
|||
|
||||
ci: validate test release
|
||||
|
||||
cross: runcimage
|
||||
docker run ${DOCKER_RUN_PROXY} -e BUILDTAGS="$(BUILDTAGS)" --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localcross
|
||||
|
||||
localcross:
|
||||
CGO_ENABLED=1 GOARCH=arm GOARM=6 CC=arm-linux-gnueabi-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-armel .
|
||||
CGO_ENABLED=1 GOARCH=arm GOARM=7 CC=arm-linux-gnueabihf-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-armhf .
|
||||
CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-arm64 .
|
||||
CGO_ENABLED=1 GOARCH=ppc64le CC=powerpc64le-linux-gnu-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-ppc64le .
|
||||
|
||||
# memoize allpackages, so that it's executed only once and only if used
|
||||
_allpackages = $(shell $(GO) list ./... | grep -v vendor)
|
||||
allpackages = $(if $(__allpackages),,$(eval __allpackages := $$(_allpackages)))$(__allpackages)
|
||||
|
|
12
vendor/github.com/opencontainers/runc/README.md
generated
vendored
12
vendor/github.com/opencontainers/runc/README.md
generated
vendored
|
@ -87,6 +87,18 @@ You can run a specific test case by setting the `TESTFLAGS` variable.
|
|||
# make test TESTFLAGS="-run=SomeTestFunction"
|
||||
```
|
||||
|
||||
You can run a specific integration test by setting the `TESTPATH` variable.
|
||||
|
||||
```bash
|
||||
# make test TESTPATH="/checkpoint.bats"
|
||||
```
|
||||
|
||||
You can run a test in your proxy environment by setting `DOCKER_BUILD_PROXY` and `DOCKER_RUN_PROXY` variables.
|
||||
|
||||
```bash
|
||||
# make test DOCKER_BUILD_PROXY="--build-arg HTTP_PROXY=http://yourproxy/" DOCKER_RUN_PROXY="-e HTTP_PROXY=http://yourproxy/"
|
||||
```
|
||||
|
||||
### Dependencies Management
|
||||
|
||||
`runc` uses [vndr](https://github.com/LK4D4/vndr) for dependencies management.
|
||||
|
|
9
vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
9
vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
|
@ -44,7 +44,11 @@ checkpointed.`,
|
|||
return err
|
||||
}
|
||||
// XXX: Currently this is untested with rootless containers.
|
||||
if isRootless() {
|
||||
rootless, err := isRootless(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rootless {
|
||||
return fmt.Errorf("runc checkpoint requires root")
|
||||
}
|
||||
|
||||
|
@ -121,7 +125,8 @@ var namespaceMapping = map[specs.LinuxNamespaceType]int{
|
|||
}
|
||||
|
||||
func setEmptyNsMask(context *cli.Context, options *libcontainer.CriuOpts) error {
|
||||
var nsmask int
|
||||
/* Runc doesn't manage network devices and their configuration */
|
||||
nsmask := unix.CLONE_NEWNET
|
||||
|
||||
for _, ns := range context.StringSlice("empty-ns") {
|
||||
f, exists := namespaceMapping[specs.LinuxNamespaceType(ns)]
|
||||
|
|
238
vendor/github.com/opencontainers/runc/contrib/cmd/recvtty/recvtty.go
generated
vendored
Normal file
238
vendor/github.com/opencontainers/runc/contrib/cmd/recvtty/recvtty.go
generated
vendored
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright 2016 SUSE LLC
|
||||
*
|
||||
* 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// version will be populated by the Makefile, read from
|
||||
// VERSION file of the source code.
|
||||
var version = ""
|
||||
|
||||
// gitCommit will be the hash that the binary was built from
|
||||
// and will be populated by the Makefile
|
||||
var gitCommit = ""
|
||||
|
||||
const (
|
||||
usage = `Open Container Initiative contrib/cmd/recvtty
|
||||
|
||||
recvtty is a reference implementation of a consumer of runC's --console-socket
|
||||
API. It has two main modes of operation:
|
||||
|
||||
* single: Only permit one terminal to be sent to the socket, which is
|
||||
then hooked up to the stdio of the recvtty process. This is useful
|
||||
for rudimentary shell management of a container.
|
||||
|
||||
* null: Permit as many terminals to be sent to the socket, but they
|
||||
are read to /dev/null. This is used for testing, and imitates the
|
||||
old runC API's --console=/dev/pts/ptmx hack which would allow for a
|
||||
similar trick. This is probably not what you want to use, unless
|
||||
you're doing something like our bats integration tests.
|
||||
|
||||
To use recvtty, just specify a socket path at which you want to receive
|
||||
terminals:
|
||||
|
||||
$ recvtty [--mode <single|null>] socket.sock
|
||||
`
|
||||
)
|
||||
|
||||
func bail(err error) {
|
||||
fmt.Fprintf(os.Stderr, "[recvtty] fatal error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func handleSingle(path string) error {
|
||||
// Open a socket.
|
||||
ln, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
// We only accept a single connection, since we can only really have
|
||||
// one reader for os.Stdin. Plus this is all a PoC.
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Close ln, to allow for other instances to take over.
|
||||
ln.Close()
|
||||
|
||||
// Get the fd of the connection.
|
||||
unixconn, ok := conn.(*net.UnixConn)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to cast to unixconn")
|
||||
}
|
||||
|
||||
socket, err := unixconn.File()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer socket.Close()
|
||||
|
||||
// Get the master file descriptor from runC.
|
||||
master, err := utils.RecvFd(socket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := console.ConsoleFromFile(master)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
console.ClearONLCR(c.Fd())
|
||||
|
||||
// Copy from our stdio to the master fd.
|
||||
quitChan := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(os.Stdout, c)
|
||||
quitChan <- struct{}{}
|
||||
}()
|
||||
go func() {
|
||||
io.Copy(c, os.Stdin)
|
||||
quitChan <- struct{}{}
|
||||
}()
|
||||
|
||||
// Only close the master fd once we've stopped copying.
|
||||
<-quitChan
|
||||
c.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleNull(path string) error {
|
||||
// Open a socket.
|
||||
ln, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
// As opposed to handleSingle we accept as many connections as we get, but
|
||||
// we don't interact with Stdin at all (and we copy stdout to /dev/null).
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func(conn net.Conn) {
|
||||
// Don't leave references lying around.
|
||||
defer conn.Close()
|
||||
|
||||
// Get the fd of the connection.
|
||||
unixconn, ok := conn.(*net.UnixConn)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
socket, err := unixconn.File()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer socket.Close()
|
||||
|
||||
// Get the master file descriptor from runC.
|
||||
master, err := utils.RecvFd(socket)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Just do a dumb copy to /dev/null.
|
||||
devnull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
// TODO: Handle this nicely.
|
||||
return
|
||||
}
|
||||
|
||||
io.Copy(devnull, master)
|
||||
devnull.Close()
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "recvtty"
|
||||
app.Usage = usage
|
||||
|
||||
// Set version to be the same as runC.
|
||||
var v []string
|
||||
if version != "" {
|
||||
v = append(v, version)
|
||||
}
|
||||
if gitCommit != "" {
|
||||
v = append(v, fmt.Sprintf("commit: %s", gitCommit))
|
||||
}
|
||||
app.Version = strings.Join(v, "\n")
|
||||
|
||||
// Set the flags.
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "mode, m",
|
||||
Value: "single",
|
||||
Usage: "Mode of operation (single or null)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pid-file",
|
||||
Value: "",
|
||||
Usage: "Path to write daemon process ID to",
|
||||
},
|
||||
}
|
||||
|
||||
app.Action = func(ctx *cli.Context) error {
|
||||
args := ctx.Args()
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("need to specify a single socket path")
|
||||
}
|
||||
path := ctx.Args()[0]
|
||||
|
||||
pidPath := ctx.String("pid-file")
|
||||
if pidPath != "" {
|
||||
pid := fmt.Sprintf("%d\n", os.Getpid())
|
||||
if err := ioutil.WriteFile(pidPath, []byte(pid), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch ctx.String("mode") {
|
||||
case "single":
|
||||
if err := handleSingle(path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "null":
|
||||
if err := handleNull(path); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("need to select a valid mode: %s", ctx.String("mode"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
bail(err)
|
||||
}
|
||||
}
|
805
vendor/github.com/opencontainers/runc/contrib/completions/bash/runc
generated
vendored
Normal file
805
vendor/github.com/opencontainers/runc/contrib/completions/bash/runc
generated
vendored
Normal file
|
@ -0,0 +1,805 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# bash completion file for runc command
|
||||
#
|
||||
# This script provides completion of:
|
||||
# - commands and their options
|
||||
# - filepaths
|
||||
#
|
||||
# To enable the completions either:
|
||||
# - place this file in /usr/share/bash-completion/completions
|
||||
# or
|
||||
# - copy this file to e.g. ~/.runc-completion.sh and add the line
|
||||
# below to your .bashrc after bash completion features are loaded
|
||||
# . ~/.runc-completion.sh
|
||||
#
|
||||
# Configuration:
|
||||
#
|
||||
|
||||
# Note for developers:
|
||||
# Please arrange options sorted alphabetically by long name with the short
|
||||
# options immediately following their corresponding long form.
|
||||
# This order should be applied to lists, alternatives and code blocks.
|
||||
|
||||
__runc_previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
|
||||
__runc_list_all() {
|
||||
COMPREPLY=($(compgen -W "$(runc list -q)" -- $cur))
|
||||
}
|
||||
|
||||
__runc_pos_first_nonflag() {
|
||||
local argument_flags=$1
|
||||
|
||||
local counter=$((${subcommand_pos:-${command_pos}} + 1))
|
||||
while [ $counter -le $cword ]; do
|
||||
if [ -n "$argument_flags" ] && eval "case '${words[$counter]}' in $argument_flags) true ;; *) false ;; esac"; then
|
||||
((counter++))
|
||||
else
|
||||
case "${words[$counter]}" in
|
||||
-*) ;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
((counter++))
|
||||
done
|
||||
|
||||
echo $counter
|
||||
}
|
||||
|
||||
# Transforms a multiline list of strings into a single line string
|
||||
# with the words separated by "|".
|
||||
# This is used to prepare arguments to __runc_pos_first_nonflag().
|
||||
__runc_to_alternatives() {
|
||||
local parts=($1)
|
||||
local IFS='|'
|
||||
echo "${parts[*]}"
|
||||
}
|
||||
|
||||
# Transforms a multiline list of options into an extglob pattern
|
||||
# suitable for use in case statements.
|
||||
__runc_to_extglob() {
|
||||
local extglob=$(__runc_to_alternatives "$1")
|
||||
echo "@($extglob)"
|
||||
}
|
||||
|
||||
# Subcommand processing.
|
||||
# Locates the first occurrence of any of the subcommands contained in the
|
||||
# first argument. In case of a match, calls the corresponding completion
|
||||
# function and returns 0.
|
||||
# If no match is found, 1 is returned. The calling function can then
|
||||
# continue processing its completion.
|
||||
#
|
||||
# TODO if the preceding command has options that accept arguments and an
|
||||
# argument is equal to one of the subcommands, this is falsely detected as
|
||||
# a match.
|
||||
__runc_subcommands() {
|
||||
local subcommands="$1"
|
||||
|
||||
local counter=$(($command_pos + 1))
|
||||
while [ $counter -lt $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
$(__runc_to_extglob "$subcommands"))
|
||||
subcommand_pos=$counter
|
||||
local subcommand=${words[$counter]}
|
||||
local completions_func=_runc_${command}_${subcommand}
|
||||
declare -F $completions_func >/dev/null && $completions_func
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
((counter++))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# List all Signals
|
||||
__runc_list_signals() {
|
||||
COMPREPLY=($(compgen -W "$(for i in $(kill -l | xargs); do echo $i; done | grep SIG)"))
|
||||
}
|
||||
|
||||
# suppress trailing whitespace
|
||||
__runc_nospace() {
|
||||
# compopt is not available in ancient bash versions
|
||||
type compopt &>/dev/null && compopt -o nospace
|
||||
}
|
||||
|
||||
# The list of capabilities is defined in types.go, ALL was added manually.
|
||||
__runc_complete_capabilities() {
|
||||
COMPREPLY=($(compgen -W "
|
||||
ALL
|
||||
AUDIT_CONTROL
|
||||
AUDIT_WRITE
|
||||
AUDIT_READ
|
||||
BLOCK_SUSPEND
|
||||
CHOWN
|
||||
DAC_OVERRIDE
|
||||
DAC_READ_SEARCH
|
||||
FOWNER
|
||||
FSETID
|
||||
IPC_LOCK
|
||||
IPC_OWNER
|
||||
KILL
|
||||
LEASE
|
||||
LINUX_IMMUTABLE
|
||||
MAC_ADMIN
|
||||
MAC_OVERRIDE
|
||||
MKNOD
|
||||
NET_ADMIN
|
||||
NET_BIND_SERVICE
|
||||
NET_BROADCAST
|
||||
NET_RAW
|
||||
SETFCAP
|
||||
SETGID
|
||||
SETPCAP
|
||||
SETUID
|
||||
SYS_ADMIN
|
||||
SYS_BOOT
|
||||
SYS_CHROOT
|
||||
SYSLOG
|
||||
SYS_MODULE
|
||||
SYS_NICE
|
||||
SYS_PACCT
|
||||
SYS_PTRACE
|
||||
SYS_RAWIO
|
||||
SYS_RESOURCE
|
||||
SYS_TIME
|
||||
SYS_TTY_CONFIG
|
||||
WAKE_ALARM
|
||||
" -- "$cur"))
|
||||
}
|
||||
|
||||
_runc_exec() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--no-new-privs
|
||||
--tty, -t
|
||||
--detach, -d
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--console
|
||||
--cwd
|
||||
--env, -e
|
||||
--user, -u
|
||||
--process, -p
|
||||
--pid-file
|
||||
--process-label
|
||||
--apparmor
|
||||
--cap, -c
|
||||
"
|
||||
|
||||
local all_options="$options_with_args $boolean_options"
|
||||
|
||||
case "$prev" in
|
||||
--cap | -c)
|
||||
__runc_complete_capabilities
|
||||
return
|
||||
;;
|
||||
|
||||
--console | --cwd | --process | --apparmor)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
--env | -e)
|
||||
COMPREPLY=($(compgen -e -- "$cur"))
|
||||
__runc_nospace
|
||||
return
|
||||
;;
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$all_options" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# global options that may appear after the runc command
|
||||
_runc_runc() {
|
||||
local boolean_options="
|
||||
$global_boolean_options
|
||||
--help
|
||||
--version -v
|
||||
--debug
|
||||
"
|
||||
local options_with_args="
|
||||
--log
|
||||
--log-format
|
||||
--root
|
||||
--criu
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--log | --root | --criu)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
--log-format)
|
||||
COMPREPLY=($(compgen -W 'text json' -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
local counter=$(__runc_pos_first_nonflag $(__runc_to_extglob "$options_with_args"))
|
||||
if [ $cword -eq $counter ]; then
|
||||
COMPREPLY=($(compgen -W "${commands[*]} help" -- "$cur"))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_pause() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_ps() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_delete() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_kill() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
--all
|
||||
-a
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
"kill")
|
||||
__runc_list_all
|
||||
return
|
||||
;;
|
||||
*)
|
||||
__runc_list_signals
|
||||
return
|
||||
;;
|
||||
esac
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_events() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--stats
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--interval
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_list() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--quiet
|
||||
-q
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--format
|
||||
-f
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--format | -f)
|
||||
COMPREPLY=($(compgen -W 'text json' -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
local counter=$(__runc_pos_first_nonflag $(__runc_to_extglob "$options_with_args"))
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_spec() {
|
||||
local boolean_options="
|
||||
--help
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--bundle
|
||||
-b
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--bundle | -b)
|
||||
case "$cur" in
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
local counter=$(__runc_pos_first_nonflag $(__runc_to_extglob "$options_with_args"))
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_run() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--detatch
|
||||
-d
|
||||
--no-subreaper
|
||||
--no-pivot
|
||||
--no-new-keyring
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--bundle
|
||||
-b
|
||||
--console
|
||||
--pid-file
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--bundle | -b | --console | --pid-file)
|
||||
case "$cur" in
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_checkpoint() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
--leave-running
|
||||
--tcp-established
|
||||
--ext-unix-sk
|
||||
--shell-job
|
||||
--file-locks
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--image-path
|
||||
--work-path
|
||||
--page-server
|
||||
--manage-cgroups-mode
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--page-server) ;;
|
||||
|
||||
--manage-cgroups-mode)
|
||||
COMPREPLY=($(compgen -W "soft full strict" -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
--image-path | --work-path)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
_runc_create() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--no-pivot
|
||||
--no-new-keyring
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--bundle
|
||||
-b
|
||||
--console
|
||||
--pid-file
|
||||
"
|
||||
case "$prev" in
|
||||
--bundle | -b | --console | --pid-file)
|
||||
case "$cur" in
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
_runc_help() {
|
||||
local counter=$(__runc_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
COMPREPLY=($(compgen -W "${commands[*]}" -- "$cur"))
|
||||
fi
|
||||
}
|
||||
|
||||
_runc_restore() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--tcp-established
|
||||
--ext-unix-sk
|
||||
--shell-job
|
||||
--file-locks
|
||||
--detach
|
||||
-d
|
||||
--no-subreaper
|
||||
--no-pivot
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
-b
|
||||
--bundle
|
||||
--image-path
|
||||
--work-path
|
||||
--manage-cgroups-mode
|
||||
--pid-file
|
||||
"
|
||||
|
||||
local all_options="$options_with_args $boolean_options"
|
||||
|
||||
case "$prev" in
|
||||
--manage-cgroups-mode)
|
||||
COMPREPLY=($(compgen -W "soft full strict" -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
--pid-file | --image-path | --work-path | --bundle | -b)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$all_options" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_resume() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_state() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
_runc_start() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
_runc_update() {
|
||||
local boolean_options="
|
||||
--help
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--blkio-weight
|
||||
--cpu-period
|
||||
--cpu-quota
|
||||
--cpu-rt-period
|
||||
--cpu-rt-runtime
|
||||
--cpu-share
|
||||
--cpuset-cpus
|
||||
--cpuset-mems
|
||||
--kernel-memory
|
||||
--kernel-memory-tcp
|
||||
--memory
|
||||
--memory-reservation
|
||||
--memory-swap
|
||||
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc() {
|
||||
local previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
|
||||
local commands=(
|
||||
checkpoint
|
||||
create
|
||||
delete
|
||||
events
|
||||
exec
|
||||
init
|
||||
kill
|
||||
list
|
||||
pause
|
||||
ps
|
||||
restore
|
||||
resume
|
||||
run
|
||||
spec
|
||||
start
|
||||
state
|
||||
update
|
||||
help
|
||||
h
|
||||
)
|
||||
|
||||
# These options are valid as global options for all client commands
|
||||
# and valid as command options for `runc daemon`
|
||||
local global_boolean_options="
|
||||
--help -h
|
||||
--version -v
|
||||
"
|
||||
|
||||
COMPREPLY=()
|
||||
local cur prev words cword
|
||||
_get_comp_words_by_ref -n : cur prev words cword
|
||||
|
||||
local command='runc' command_pos=0 subcommand_pos
|
||||
local counter=1
|
||||
while [ $counter -lt $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-*) ;;
|
||||
=)
|
||||
((counter++))
|
||||
;;
|
||||
*)
|
||||
command="${words[$counter]}"
|
||||
command_pos=$counter
|
||||
break
|
||||
;;
|
||||
esac
|
||||
((counter++))
|
||||
done
|
||||
|
||||
local completions_func=_runc_${command}
|
||||
declare -F $completions_func >/dev/null && $completions_func
|
||||
|
||||
eval "$previous_extglob_setting"
|
||||
return 0
|
||||
}
|
||||
|
||||
eval "$__runc_previous_extglob_setting"
|
||||
unset __runc_previous_extglob_setting
|
||||
|
||||
complete -F _runc runc
|
314
vendor/github.com/opencontainers/runc/docs/terminals.md
generated
vendored
Normal file
314
vendor/github.com/opencontainers/runc/docs/terminals.md
generated
vendored
Normal file
|
@ -0,0 +1,314 @@
|
|||
# Terminals and Standard IO #
|
||||
|
||||
*Note that the default configuration of `runc` (foreground, new terminal) is
|
||||
generally the best option for most users. This document exists to help explain
|
||||
what the purpose of the different modes is, and to try to steer users away from
|
||||
common mistakes and misunderstandings.*
|
||||
|
||||
In general, most processes on Unix (and Unix-like) operating systems have 3
|
||||
standard file descriptors provided at the start, collectively referred to as
|
||||
"standard IO" (`stdio`):
|
||||
|
||||
* `0`: standard-in (`stdin`), the input stream into the process
|
||||
* `1`: standard-out (`stdout`), the output stream from the process
|
||||
* `2`: standard-error (`stderr`), the error stream from the process
|
||||
|
||||
When creating and running a container via `runc`, it is important to take care
|
||||
to structure the `stdio` the new container's process receives. In some ways
|
||||
containers are just regular processes, while in other ways they're an isolated
|
||||
sub-partition of your machine (in a similar sense to a VM). This means that the
|
||||
structure of IO is not as simple as with ordinary programs (which generally
|
||||
just use the file descriptors you give them).
|
||||
|
||||
## Other File Descriptors ##
|
||||
|
||||
Before we continue, it is important to note that processes can have more file
|
||||
descriptors than just `stdio`. By default in `runc` no other file descriptors
|
||||
will be passed to the spawned container process. If you wish to explicitly pass
|
||||
file descriptors to the container you have to use the `--preserve-fds` option.
|
||||
These ancillary file descriptors don't have any of the strange semantics
|
||||
discussed further in this document (those only apply to `stdio`) -- they are
|
||||
passed untouched by `runc`.
|
||||
|
||||
It should be noted that `--preserve-fds` does not take individual file
|
||||
descriptors to preserve. Instead, it takes how many file descriptors (not
|
||||
including `stdio` or `LISTEN_FDS`) should be passed to the container. In the
|
||||
following example:
|
||||
|
||||
```
|
||||
% runc run --preserve-fds 5 <container>
|
||||
```
|
||||
|
||||
`runc` will pass the first `5` file descriptors (`3`, `4`, `5`, `6`, and `7` --
|
||||
assuming that `LISTEN_FDS` has not been configured) to the container.
|
||||
|
||||
In addition to `--preserve-fds`, `LISTEN_FDS` file descriptors are passed
|
||||
automatically to allow for `systemd`-style socket activation. To extend the
|
||||
above example:
|
||||
|
||||
```
|
||||
% LISTEN_PID=$pid_of_runc LISTEN_FDS=3 runc run --preserve-fds 5 <container>
|
||||
```
|
||||
|
||||
`runc` will now pass the first `8` file descriptors (and it will also pass
|
||||
`LISTEN_FDS=3` and `LISTEN_PID=1` to the container). The first `3` (`3`, `4`,
|
||||
and `5`) were passed due to `LISTEN_FDS` and the other `5` (`6`, `7`, `8`, `9`,
|
||||
and `10`) were passed due to `--preserve-fds`. You should keep this in mind if
|
||||
you use `runc` directly in something like a `systemd` unit file. To disable
|
||||
this `LISTEN_FDS`-style passing just unset `LISTEN_FDS`.
|
||||
|
||||
**Be very careful when passing file descriptors to a container process.** Due
|
||||
to some Linux kernel (mis)features, a container with access to certain types of
|
||||
file descriptors (such as `O_PATH` descriptors) outside of the container's root
|
||||
file system can use these to break out of the container's pivoted mount
|
||||
namespace. [This has resulted in CVEs in the past.][CVE-2016-9962]
|
||||
|
||||
[CVE-2016-9962]: https://nvd.nist.gov/vuln/detail/CVE-2016-9962
|
||||
|
||||
## <a name="terminal-modes" /> Terminal Modes ##
|
||||
|
||||
`runc` supports two distinct methods for passing `stdio` to the container's
|
||||
primary process:
|
||||
|
||||
* [new terminal](#new-terminal) (`terminal: true`)
|
||||
* [pass-through](#pass-through) (`terminal: false`)
|
||||
|
||||
When first using `runc` these two modes will look incredibly similar, but this
|
||||
can be quite deceptive as these different modes have quite different
|
||||
characteristics.
|
||||
|
||||
By default, `runc spec` will create a configuration that will create a new
|
||||
terminal (`terminal: true`). However, if the `terminal: ...` line is not
|
||||
present in `config.json` then pass-through is the default.
|
||||
|
||||
*In general we recommend using new terminal, because it means that tools like
|
||||
`sudo` will work inside your container. But pass-through can be useful if you
|
||||
know what you're doing, or if you're using `runc` as part of a non-interactive
|
||||
pipeline.*
|
||||
|
||||
### <a name="new-terminal"> New Terminal ###
|
||||
|
||||
In new terminal mode, `runc` will create a brand-new "console" (or more
|
||||
precisely, a new pseudo-terminal using the container's namespaced
|
||||
`/dev/pts/ptmx`) for your contained process to use as its `stdio`.
|
||||
|
||||
When you start a process in new terminal mode, `runc` will do the following:
|
||||
|
||||
1. Create a new pseudo-terminal.
|
||||
2. Pass the slave end to the container's primary process as its `stdio`.
|
||||
3. Send the master end to a process to interact with the `stdio` for the
|
||||
container's primary process ([details below](#runc-modes)).
|
||||
|
||||
It should be noted that since a new pseudo-terminal is being used for
|
||||
communication with the container, some strange properties of pseudo-terminals
|
||||
might surprise you. For instance, by default, all new pseudo-terminals
|
||||
translate the byte `'\n'` to the sequence `'\r\n'` on both `stdout` and
|
||||
`stderr`. In addition there are [a whole range of `ioctls(2)` that can only
|
||||
interact with pseudo-terminal `stdio`][tty_ioctl(4)].
|
||||
|
||||
> **NOTE**: In new terminal mode, all three `stdio` file descriptors are the
|
||||
> same underlying file. The reason for this is to match how a shell's `stdio`
|
||||
> looks to a process (as well as remove race condition issues with having to
|
||||
> deal with multiple master pseudo-terminal file descriptors). However this
|
||||
> means that it is not really possible to uniquely distinguish between `stdout`
|
||||
> and `stderr` from the caller's perspective.
|
||||
|
||||
[tty_ioctl(4)]: https://linux.die.net/man/4/tty_ioctl
|
||||
|
||||
### <a name="pass-through"> Pass-Through ###
|
||||
|
||||
If you have already set up some file handles that you wish your contained
|
||||
process to use as its `stdio`, then you can ask `runc` to pass them through to
|
||||
the contained process (this is not necessarily the same as `--preserve-fds`'s
|
||||
passing of file descriptors -- [details below](#runc-modes)). As an example
|
||||
(assuming that `terminal: false` is set in `config.json`):
|
||||
|
||||
```
|
||||
% echo input | runc run some_container > /tmp/log.out 2>& /tmp/log.err
|
||||
```
|
||||
|
||||
Here the container's various `stdio` file descriptors will be substituted with
|
||||
the following:
|
||||
|
||||
* `stdin` will be sourced from the `echo input` pipeline.
|
||||
* `stdout` will be output into `/tmp/log.out` on the host.
|
||||
* `stderr` will be output into `/tmp/log.err` on the host.
|
||||
|
||||
It should be noted that the actual file handles seen inside the container may
|
||||
be different [based on the mode `runc` is being used in](#runc-modes) (for
|
||||
instance, the file referenced by `1` could be `/tmp/log.out` directly or a pipe
|
||||
which `runc` is using to buffer output, based on the mode). However the net
|
||||
result will be the same in either case. In principle you could use the [new
|
||||
terminal mode](#new-terminal) in a pipeline, but the difference will become
|
||||
more clear when you are introduced to [`runc`'s detached mode](#runc-modes).
|
||||
|
||||
## <a name="runc-modes" /> `runc` Modes ##
|
||||
|
||||
`runc` itself runs in two modes:
|
||||
|
||||
* [foreground](#foreground)
|
||||
* [detached](#detached)
|
||||
|
||||
You can use either [terminal mode](#terminal-modes) with either `runc` mode.
|
||||
However, there are considerations that may indicate preference for one mode
|
||||
over another. It should be noted that while two types of modes (terminal and
|
||||
`runc`) are conceptually independent from each other, you should be aware of
|
||||
the intricacies of which combination you are using.
|
||||
|
||||
*In general we recommend using foreground because it's the most
|
||||
straight-forward to use, with the only downside being that you will have a
|
||||
long-running `runc` process. Detached mode is difficult to get right and
|
||||
generally requires having your own `stdio` management.*
|
||||
|
||||
### Foreground ###
|
||||
|
||||
The default (and most straight-forward) mode of `runc`. In this mode, your
|
||||
`runc` command remains in the foreground with the container process as a child.
|
||||
All `stdio` is buffered through the foreground `runc` process (irrespective of
|
||||
which terminal mode you are using). This is conceptually quite similar to
|
||||
running a normal process interactively in a shell (and if you are using `runc`
|
||||
in a shell interactively, this is what you should use).
|
||||
|
||||
Because the `stdio` will be buffered in this mode, some very important
|
||||
peculiarities of this mode should be kept in mind:
|
||||
|
||||
* With [new terminal mode](#new-terminal), the container will see a
|
||||
pseudo-terminal as its `stdio` (as you might expect). However, the `stdio` of
|
||||
the foreground `runc` process will remain the `stdio` that the process was
|
||||
started with -- and `runc` will copy all `stdio` between its `stdio` and the
|
||||
container's `stdio`. This means that while a new pseudo-terminal has been
|
||||
created, the foreground `runc` process manages it over the lifetime of the
|
||||
container.
|
||||
|
||||
* With [pass-through mode](#pass-through), the foreground `runc`'s `stdio` is
|
||||
**not** passed to the container. Instead, the container's `stdio` is a set of
|
||||
pipes which are used to copy data between `runc`'s `stdio` and the
|
||||
container's `stdio`. This means that the container never has direct access to
|
||||
host file descriptors (aside from the pipes created by the container runtime,
|
||||
but that shouldn't be an issue).
|
||||
|
||||
The main drawback of the foreground mode of operation is that it requires a
|
||||
long-running foreground `runc` process. If you kill the foreground `runc`
|
||||
process then you will no longer have access to the `stdio` of the container
|
||||
(and in most cases this will result in the container dying abnormally due to
|
||||
`SIGPIPE` or some other error). By extension this means that any bug in the
|
||||
long-running foreground `runc` process (such as a memory leak) or a stray
|
||||
OOM-kill sweep could result in your container being killed **through no fault
|
||||
of the user**. In addition, there is no way in foreground mode of passing a
|
||||
file descriptor directly to the container process as its `stdio` (like
|
||||
`--preserve-fds` does).
|
||||
|
||||
These shortcomings are obviously sub-optimal and are the reason that `runc` has
|
||||
an additional mode called "detached mode".
|
||||
|
||||
### Detached ###
|
||||
|
||||
In contrast to foreground mode, in detached mode there is no long-running
|
||||
foreground `runc` process once the container has started. In fact, there is no
|
||||
long-running `runc` process at all. However, this means that it is up to the
|
||||
caller to handle the `stdio` after `runc` has set it up for you. In a shell
|
||||
this means that the `runc` command will exit and control will return to the
|
||||
shell, after the container has been set up.
|
||||
|
||||
You can run `runc` in detached mode in one of the following ways:
|
||||
|
||||
* `runc run -d ...` which operates similar to `runc run` but is detached.
|
||||
* `runc create` followed by `runc start` which is the standard container
|
||||
lifecycle defined by the OCI runtime specification (`runc create` sets up the
|
||||
container completely, waiting for `runc start` to begin execution of user
|
||||
code).
|
||||
|
||||
The main use-case of detached mode is for higher-level tools that want to be
|
||||
wrappers around `runc`. By running `runc` in detached mode, those tools have
|
||||
far more control over the container's `stdio` without `runc` getting in the
|
||||
way (most wrappers around `runc` like `cri-o` or `containerd` use detached mode
|
||||
for this reason).
|
||||
|
||||
Unfortunately using detached mode is a bit more complicated and requires more
|
||||
care than the foreground mode -- mainly because it is now up to the caller to
|
||||
handle the `stdio` of the container.
|
||||
|
||||
#### Detached Pass-Through ####
|
||||
|
||||
In detached mode, pass-through actually does what it says on the tin -- the
|
||||
`stdio` file descriptors of the `runc` process are passed through (untouched)
|
||||
to the container's `stdio`. The purpose of this option is to allow a user to
|
||||
set up `stdio` for a container themselves and then force `runc` to just use
|
||||
their pre-prepared `stdio` (without any pseudo-terminal funny business). *If
|
||||
you don't see why this would be useful, don't use this option.*
|
||||
|
||||
**You must be incredibly careful when using detached pass-through (especially
|
||||
in a shell).** The reason for this is that by using detached pass-through you
|
||||
are passing host file descriptors to the container. In the case of a shell,
|
||||
usually your `stdio` is going to be a pseudo-terminal (on your host). A
|
||||
malicious container could take advantage of TTY-specific `ioctls` like
|
||||
`TIOCSTI` to fake input into the **host** shell (remember that in detached
|
||||
mode, control is returned to your shell and so the terminal you've given the
|
||||
container is being read by a shell prompt).
|
||||
|
||||
There are also several other issues with running non-malicious containers in a
|
||||
shell with detached pass-through (where you pass your shell's `stdio` to the
|
||||
container):
|
||||
|
||||
* Output from the container will be interleaved with output from your shell (in
|
||||
a non-deterministic way), without any real way of distinguishing from where a
|
||||
particular piece of output came from.
|
||||
|
||||
* Any input to `stdin` will be non-deterministically split and given to either
|
||||
the container or the shell (because both are blocked on a `read(2)` of the
|
||||
same FIFO-style file descriptor).
|
||||
|
||||
They are all related to the fact that there is going to be a race when either
|
||||
your host or the container tries to read from (or write to) `stdio`. This
|
||||
problem is especially obvious when in a shell, where usually the terminal has
|
||||
been put into raw mode (where each individual key-press should cause `read(2)`
|
||||
to return).
|
||||
|
||||
> **NOTE**: There is also currently a [known problem][issue-1721] where using
|
||||
> detached pass-through will result in the container hanging if the `stdout` or
|
||||
> `stderr` is a pipe (though this should be a temporary issue).
|
||||
|
||||
[issue-1721]: https://github.com/opencontainers/runc/issues/1721
|
||||
|
||||
#### Detached New Terminal ####
|
||||
|
||||
When creating a new pseudo-terminal in detached mode, and fairly obvious
|
||||
problem appears -- how do we use the new terminal that `runc` created? Unlike
|
||||
in pass-through, `runc` has created a new set of file descriptors that need to
|
||||
be used by *something* in order for container communication to work.
|
||||
|
||||
The way this problem is resolved is through the use of Unix domain sockets.
|
||||
There is a feature of Unix sockets called `SCM_RIGHTS` which allows a file
|
||||
descriptor to be sent through a Unix socket to a completely separate process
|
||||
(which can then use that file descriptor as though they opened it). When using
|
||||
`runc` in detached new terminal mode, this is how a user gets access to the
|
||||
pseudo-terminal's master file descriptor.
|
||||
|
||||
To this end, there is a new option (which is required if you want to use `runc`
|
||||
in detached new terminal mode): `--console-socket`. This option takes the path
|
||||
to a Unix domain socket which `runc` will connect to and send the
|
||||
pseudo-terminal master file descriptor down. The general process for getting
|
||||
the pseudo-terminal master is as follows:
|
||||
|
||||
1. Create a Unix domain socket at some path, `$socket_path`.
|
||||
2. Call `runc run` or `runc create` with the argument `--console-socket
|
||||
$socket_path`.
|
||||
3. Using `recvmsg(2)` retrieve the file descriptor sent using `SCM_RIGHTS` by
|
||||
`runc`.
|
||||
4. Now the manager can interact with the `stdio` of the container, using the
|
||||
retrieved pseudo-terminal master.
|
||||
|
||||
After `runc` exits, the only process with a copy of the pseudo-terminal master
|
||||
file descriptor is whoever read the file descriptor from the socket.
|
||||
|
||||
> **NOTE**: Currently `runc` doesn't support abstract socket addresses (due to
|
||||
> it not being possible to pass an `argv` with a null-byte as the first
|
||||
> character). In the future this may change, but currently you must use a valid
|
||||
> path name.
|
||||
|
||||
In order to help users make use of detached new terminal mode, we have provided
|
||||
a [Go implementation in the `go-runc` bindings][containerd/go-runc.Socket], as
|
||||
well as [a simple client][recvtty].
|
||||
|
||||
[containerd/go-runc.Socket]: https://godoc.org/github.com/containerd/go-runc#Socket
|
||||
[recvtty]: /contrib/cmd/recvtty
|
1
vendor/github.com/opencontainers/runc/exec.go
generated
vendored
1
vendor/github.com/opencontainers/runc/exec.go
generated
vendored
|
@ -140,6 +140,7 @@ func execProcess(context *cli.Context) (int, error) {
|
|||
detach: detach,
|
||||
pidFile: context.String("pid-file"),
|
||||
action: CT_ACT_RUN,
|
||||
init: false,
|
||||
}
|
||||
return r.run(p)
|
||||
}
|
||||
|
|
54
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
54
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
|
@ -3,7 +3,6 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -14,6 +13,8 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -35,7 +36,7 @@ var (
|
|||
HugePageSizes, _ = cgroups.GetHugePageSize()
|
||||
)
|
||||
|
||||
var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
|
||||
var errSubsystemDoesNotExist = fmt.Errorf("cgroup: subsystem does not exist")
|
||||
|
||||
type subsystemSet []subsystem
|
||||
|
||||
|
@ -62,9 +63,10 @@ type subsystem interface {
|
|||
}
|
||||
|
||||
type Manager struct {
|
||||
mu sync.Mutex
|
||||
Cgroups *configs.Cgroup
|
||||
Paths map[string]string
|
||||
mu sync.Mutex
|
||||
Cgroups *configs.Cgroup
|
||||
Rootless bool
|
||||
Paths map[string]string
|
||||
}
|
||||
|
||||
// The absolute path to the root of the cgroup hierarchies.
|
||||
|
@ -100,6 +102,33 @@ type cgroupData struct {
|
|||
pid int
|
||||
}
|
||||
|
||||
// isIgnorableError returns whether err is a permission error (in the loose
|
||||
// sense of the word). This includes EROFS (which for an unprivileged user is
|
||||
// basically a permission error) and EACCES (for similar reasons) as well as
|
||||
// the normal EPERM.
|
||||
func isIgnorableError(rootless bool, err error) bool {
|
||||
// We do not ignore errors if we are root.
|
||||
if !rootless {
|
||||
return false
|
||||
}
|
||||
// Is it an ordinary EPERM?
|
||||
if os.IsPermission(errors.Cause(err)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Try to handle other errnos.
|
||||
var errno error
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *os.PathError:
|
||||
errno = err.Err
|
||||
case *os.LinkError:
|
||||
errno = err.Err
|
||||
case *os.SyscallError:
|
||||
errno = err.Err
|
||||
}
|
||||
return errno == unix.EROFS || errno == unix.EPERM || errno == unix.EACCES
|
||||
}
|
||||
|
||||
func (m *Manager) Apply(pid int) (err error) {
|
||||
if m.Cgroups == nil {
|
||||
return nil
|
||||
|
@ -145,11 +174,11 @@ func (m *Manager) Apply(pid int) (err error) {
|
|||
m.Paths[sys.Name()] = p
|
||||
|
||||
if err := sys.Apply(d); err != nil {
|
||||
if os.IsPermission(err) && m.Cgroups.Path == "" {
|
||||
// If we didn't set a cgroup path, then let's defer the error here
|
||||
// until we know whether we have set limits or not.
|
||||
// If we hadn't set limits, then it's ok that we couldn't join this cgroup, because
|
||||
// it will have the same limits as its parent.
|
||||
// In the case of rootless, where an explicit cgroup path hasn't
|
||||
// been set, we don't bail on error in case of permission problems.
|
||||
// Cases where limits have been set (and we couldn't create our own
|
||||
// cgroup) are handled by Set.
|
||||
if isIgnorableError(m.Rootless, err) && m.Cgroups.Path == "" {
|
||||
delete(m.Paths, sys.Name())
|
||||
continue
|
||||
}
|
||||
|
@ -208,8 +237,9 @@ func (m *Manager) Set(container *configs.Config) error {
|
|||
path := paths[sys.Name()]
|
||||
if err := sys.Set(path, container.Cgroups); err != nil {
|
||||
if path == "" {
|
||||
// cgroup never applied
|
||||
return fmt.Errorf("cannot set limits on the %s cgroup, as the container has not joined it", sys.Name())
|
||||
// We never created a path for this cgroup, so we cannot set
|
||||
// limits for it (though we have already tried at this point).
|
||||
return fmt.Errorf("cannot set %s limit: container could not join or create cgroup", sys.Name())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
generated
vendored
|
@ -77,7 +77,7 @@ func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) erro
|
|||
// The logic is, if user specified cpuset configs, use these
|
||||
// specified configs, otherwise, inherit from parent. This makes
|
||||
// cpuset configs work correctly with 'cpuset.cpu_exclusive', and
|
||||
// keep backward compatbility.
|
||||
// keep backward compatibility.
|
||||
if err := s.ensureCpusAndMems(dir, cgroup); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go
generated
vendored
|
@ -86,7 +86,7 @@ func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats)
|
|||
expectMemoryDataEquals(t, expected.KernelUsage, actual.KernelUsage)
|
||||
|
||||
if expected.UseHierarchy != actual.UseHierarchy {
|
||||
logrus.Printf("Expected memory use hiearchy %v, but found %v\n", expected.UseHierarchy, actual.UseHierarchy)
|
||||
logrus.Printf("Expected memory use hierarchy %v, but found %v\n", expected.UseHierarchy, actual.UseHierarchy)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
|
|
77
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
77
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
|
@ -5,6 +5,7 @@ package systemd
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -75,7 +76,8 @@ var (
|
|||
hasStartTransientUnit bool
|
||||
hasStartTransientSliceUnit bool
|
||||
hasTransientDefaultDependencies bool
|
||||
hasDelegate bool
|
||||
hasDelegateScope bool
|
||||
hasDelegateSlice bool
|
||||
)
|
||||
|
||||
func newProp(name string, units interface{}) systemdDbus.Property {
|
||||
|
@ -150,12 +152,12 @@ func UseSystemd() bool {
|
|||
theConn.StopUnit(scope, "replace", nil)
|
||||
|
||||
// Assume StartTransientUnit on a scope allows Delegate
|
||||
hasDelegate = true
|
||||
dl := newProp("Delegate", true)
|
||||
if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dl}, nil); err != nil {
|
||||
hasDelegateScope = true
|
||||
dlScope := newProp("Delegate", true)
|
||||
if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dlScope}, nil); err != nil {
|
||||
if dbusError, ok := err.(dbus.Error); ok {
|
||||
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") {
|
||||
hasDelegate = false
|
||||
hasDelegateScope = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +189,22 @@ func UseSystemd() bool {
|
|||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
// Not critical because of the stop unit logic above.
|
||||
theConn.StopUnit(slice, "replace", nil)
|
||||
|
||||
// Assume StartTransientUnit on a slice allows Delegate
|
||||
hasDelegateSlice = true
|
||||
dlSlice := newProp("Delegate", true)
|
||||
if _, err := theConn.StartTransientUnit(slice, "replace", []systemdDbus.Property{dlSlice}, nil); err != nil {
|
||||
if dbusError, ok := err.(dbus.Error); ok {
|
||||
// Starting with systemd v237, Delegate is not even a property of slices anymore,
|
||||
// so the D-Bus call fails with "InvalidArgs" error.
|
||||
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") || strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.InvalidArgs") {
|
||||
hasDelegateSlice = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not critical because of the stop unit logic above.
|
||||
theConn.StopUnit(scope, "replace", nil)
|
||||
theConn.StopUnit(slice, "replace", nil)
|
||||
|
@ -242,9 +260,16 @@ func (m *Manager) Apply(pid int) error {
|
|||
properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
|
||||
}
|
||||
|
||||
if hasDelegate {
|
||||
// This is only supported on systemd versions 218 and above.
|
||||
properties = append(properties, newProp("Delegate", true))
|
||||
// Check if we can delegate. This is only supported on systemd versions 218 and above.
|
||||
if strings.HasSuffix(unitName, ".slice") {
|
||||
if hasDelegateSlice {
|
||||
// systemd 237 and above no longer allows delegation on a slice
|
||||
properties = append(properties, newProp("Delegate", true))
|
||||
}
|
||||
} else {
|
||||
if hasDelegateScope {
|
||||
properties = append(properties, newProp("Delegate", true))
|
||||
}
|
||||
}
|
||||
|
||||
// Always enable accounting, this gets us the same behaviour as the fs implementation,
|
||||
|
@ -271,13 +296,19 @@ func (m *Manager) Apply(pid int) error {
|
|||
|
||||
// cpu.cfs_quota_us and cpu.cfs_period_us are controlled by systemd.
|
||||
if c.Resources.CpuQuota != 0 && c.Resources.CpuPeriod != 0 {
|
||||
cpuQuotaPerSecUSec := uint64(c.Resources.CpuQuota*1000000) / c.Resources.CpuPeriod
|
||||
// systemd converts CPUQuotaPerSecUSec (microseconds per CPU second) to CPUQuota
|
||||
// (integer percentage of CPU) internally. This means that if a fractional percent of
|
||||
// CPU is indicated by Resources.CpuQuota, we need to round up to the nearest
|
||||
// 10ms (1% of a second) such that child cgroups can set the cpu.cfs_quota_us they expect.
|
||||
if cpuQuotaPerSecUSec%10000 != 0 {
|
||||
cpuQuotaPerSecUSec = ((cpuQuotaPerSecUSec / 10000) + 1) * 10000
|
||||
// corresponds to USEC_INFINITY in systemd
|
||||
// if USEC_INFINITY is provided, CPUQuota is left unbound by systemd
|
||||
// always setting a property value ensures we can apply a quota and remove it later
|
||||
cpuQuotaPerSecUSec := uint64(math.MaxUint64)
|
||||
if c.Resources.CpuQuota > 0 {
|
||||
// systemd converts CPUQuotaPerSecUSec (microseconds per CPU second) to CPUQuota
|
||||
// (integer percentage of CPU) internally. This means that if a fractional percent of
|
||||
// CPU is indicated by Resources.CpuQuota, we need to round up to the nearest
|
||||
// 10ms (1% of a second) such that child cgroups can set the cpu.cfs_quota_us they expect.
|
||||
cpuQuotaPerSecUSec = uint64(c.Resources.CpuQuota*1000000) / c.Resources.CpuPeriod
|
||||
if cpuQuotaPerSecUSec%10000 != 0 {
|
||||
cpuQuotaPerSecUSec = ((cpuQuotaPerSecUSec / 10000) + 1) * 10000
|
||||
}
|
||||
}
|
||||
properties = append(properties,
|
||||
newProp("CPUQuotaPerSecUSec", cpuQuotaPerSecUSec))
|
||||
|
@ -296,17 +327,17 @@ func (m *Manager) Apply(pid int) error {
|
|||
}
|
||||
}
|
||||
|
||||
statusChan := make(chan string)
|
||||
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err != nil && !isUnitExists(err) {
|
||||
statusChan := make(chan string, 1)
|
||||
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err == nil {
|
||||
select {
|
||||
case <-statusChan:
|
||||
case <-time.After(time.Second):
|
||||
logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", unitName)
|
||||
}
|
||||
} else if !isUnitExists(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-statusChan:
|
||||
case <-time.After(time.Second):
|
||||
logrus.Warnf("Timed out while waiting for StartTransientUnit completion signal from dbus. Continuing...")
|
||||
}
|
||||
|
||||
if err := joinCgroups(c, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
25
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
25
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
|
@ -13,7 +13,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -103,7 +103,7 @@ func FindCgroupMountpointDir() (string, error) {
|
|||
}
|
||||
|
||||
if postSeparatorFields[0] == "cgroup" {
|
||||
// Check that the mount is properly formated.
|
||||
// Check that the mount is properly formatted.
|
||||
if numPostFields < 3 {
|
||||
return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||
}
|
||||
|
@ -151,19 +151,20 @@ func getCgroupMountsHelper(ss map[string]bool, mi io.Reader, all bool) ([]Mount,
|
|||
Root: fields[3],
|
||||
}
|
||||
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||
if !ss[opt] {
|
||||
seen, known := ss[opt]
|
||||
if !known || (!all && seen) {
|
||||
continue
|
||||
}
|
||||
ss[opt] = true
|
||||
if strings.HasPrefix(opt, cgroupNamePrefix) {
|
||||
m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
|
||||
} else {
|
||||
m.Subsystems = append(m.Subsystems, opt)
|
||||
}
|
||||
if !all {
|
||||
numFound++
|
||||
opt = opt[len(cgroupNamePrefix):]
|
||||
}
|
||||
m.Subsystems = append(m.Subsystems, opt)
|
||||
numFound++
|
||||
}
|
||||
if len(m.Subsystems) > 0 || all {
|
||||
res = append(res, m)
|
||||
}
|
||||
res = append(res, m)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
|
@ -187,7 +188,7 @@ func GetCgroupMounts(all bool) ([]Mount, error) {
|
|||
|
||||
allMap := make(map[string]bool)
|
||||
for s := range allSubsystems {
|
||||
allMap[s] = true
|
||||
allMap[s] = false
|
||||
}
|
||||
return getCgroupMountsHelper(allMap, f, all)
|
||||
}
|
||||
|
@ -262,7 +263,7 @@ func getCgroupPathHelper(subsystem, cgroup string) (string, error) {
|
|||
}
|
||||
|
||||
// This is needed for nested containers, because in /proc/self/cgroup we
|
||||
// see pathes from host, which don't exist in container.
|
||||
// see paths from host, which don't exist in container.
|
||||
relCgroup, err := filepath.Rel(root, cgroup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
151
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils_test.go
generated
vendored
151
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils_test.go
generated
vendored
|
@ -93,6 +93,62 @@ const systemdMountinfo = `115 83 0:32 / / rw,relatime - aufs none rw,si=c0bd3d3,
|
|||
136 117 0:12 /1 /dev/console rw,nosuid,noexec,relatime - devpts none rw,gid=5,mode=620,ptmxmode=000
|
||||
84 115 0:40 / /tmp rw,relatime - tmpfs none rw`
|
||||
|
||||
const bedrockMountinfo = `120 17 0:28 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
124 28 0:28 / /bedrock/strata/arch/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
123 53 0:28 / /bedrock/strata/fallback/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
122 71 0:28 / /bedrock/strata/gentoo/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
121 89 0:28 / /bedrock/strata/kde/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
125 120 0:29 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
129 124 0:29 / /bedrock/strata/arch/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
128 123 0:29 / /bedrock/strata/fallback/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
127 122 0:29 / /bedrock/strata/gentoo/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
126 121 0:29 / /bedrock/strata/kde/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
140 120 0:32 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
144 124 0:32 / /bedrock/strata/arch/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
143 123 0:32 / /bedrock/strata/fallback/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
142 122 0:32 / /bedrock/strata/gentoo/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
141 121 0:32 / /bedrock/strata/kde/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
145 120 0:33 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
149 124 0:33 / /bedrock/strata/arch/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
148 123 0:33 / /bedrock/strata/fallback/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
147 122 0:33 / /bedrock/strata/gentoo/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
146 121 0:33 / /bedrock/strata/kde/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
150 120 0:34 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
154 124 0:34 / /bedrock/strata/arch/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
153 123 0:34 / /bedrock/strata/fallback/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
152 122 0:34 / /bedrock/strata/gentoo/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
151 121 0:34 / /bedrock/strata/kde/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
155 120 0:35 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
159 124 0:35 / /bedrock/strata/arch/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
158 123 0:35 / /bedrock/strata/fallback/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
157 122 0:35 / /bedrock/strata/gentoo/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
156 121 0:35 / /bedrock/strata/kde/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
160 120 0:36 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
164 124 0:36 / /bedrock/strata/arch/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
163 123 0:36 / /bedrock/strata/fallback/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
162 122 0:36 / /bedrock/strata/gentoo/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
161 121 0:36 / /bedrock/strata/kde/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
165 120 0:37 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
169 124 0:37 / /bedrock/strata/arch/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
168 123 0:37 / /bedrock/strata/fallback/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
167 122 0:37 / /bedrock/strata/gentoo/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
166 121 0:37 / /bedrock/strata/kde/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
170 120 0:38 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
174 124 0:38 / /bedrock/strata/arch/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
173 123 0:38 / /bedrock/strata/fallback/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
172 122 0:38 / /bedrock/strata/gentoo/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
171 121 0:38 / /bedrock/strata/kde/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
175 120 0:39 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
179 124 0:39 / /bedrock/strata/arch/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
178 123 0:39 / /bedrock/strata/fallback/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
177 122 0:39 / /bedrock/strata/gentoo/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
176 121 0:39 / /bedrock/strata/kde/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
180 120 0:40 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
184 124 0:40 / /bedrock/strata/arch/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
183 123 0:40 / /bedrock/strata/fallback/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
182 122 0:40 / /bedrock/strata/gentoo/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
181 121 0:40 / /bedrock/strata/kde/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event`
|
||||
|
||||
const cgroup2Mountinfo = `18 64 0:18 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel
|
||||
19 64 0:4 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
|
||||
20 64 0:6 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=8171204k,nr_inodes=2042801,mode=755
|
||||
|
@ -132,31 +188,46 @@ func TestGetCgroupMounts(t *testing.T) {
|
|||
mountInfo: fedoraMountinfo,
|
||||
root: "/",
|
||||
subsystems: map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"hugetlb": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
"hugetlb": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
mountInfo: systemdMountinfo,
|
||||
root: "/system.slice/docker-dc4eaa1a34ec4d593bc0125d31eea823a1d76ae483aeb1409cca80304e34da2e.scope",
|
||||
subsystems: map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
mountInfo: bedrockMountinfo,
|
||||
root: "/",
|
||||
subsystems: map[string]bool{
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -199,16 +270,16 @@ func TestGetCgroupMounts(t *testing.T) {
|
|||
|
||||
func BenchmarkGetCgroupMounts(b *testing.B) {
|
||||
subsystems := map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"hugetlb": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
"hugetlb": false,
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
@ -276,17 +347,17 @@ func TestParseCgroupString(t *testing.T) {
|
|||
|
||||
func TestIgnoreCgroup2Mount(t *testing.T) {
|
||||
subsystems := map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"pids": true,
|
||||
"name=systemd": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
"pids": false,
|
||||
"name=systemd": false,
|
||||
}
|
||||
|
||||
mi := bytes.NewBufferString(cgroup2Mountinfo)
|
||||
|
|
5
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
5
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
|
@ -141,9 +141,10 @@ type Config struct {
|
|||
|
||||
// OomScoreAdj specifies the adjustment to be made by the kernel when calculating oom scores
|
||||
// for a process. Valid values are between the range [-1000, '1000'], where processes with
|
||||
// higher scores are preferred for being killed.
|
||||
// higher scores are preferred for being killed. If it is unset then we don't touch the current
|
||||
// value.
|
||||
// More information about kernel oom score calculation here: https://lwn.net/Articles/317814/
|
||||
OomScoreAdj int `json:"oom_score_adj"`
|
||||
OomScoreAdj *int `json:"oom_score_adj,omitempty"`
|
||||
|
||||
// UidMappings is an array of User ID mappings for User Namespaces
|
||||
UidMappings []IDMap `json:"uid_mappings"`
|
||||
|
|
13
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/rootless.go
generated
vendored
13
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/rootless.go
generated
vendored
|
@ -43,13 +43,12 @@ func rootlessMappings(config *configs.Config) error {
|
|||
if !config.Namespaces.Contains(configs.NEWUSER) {
|
||||
return fmt.Errorf("rootless containers require user namespaces")
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.UidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one UID mapping")
|
||||
}
|
||||
if len(config.GidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one UID mapping")
|
||||
if len(config.UidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one UID mapping")
|
||||
}
|
||||
if len(config.GidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one GID mapping")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
10
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/validator.go
generated
vendored
10
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/validator.go
generated
vendored
|
@ -151,6 +151,16 @@ func (v *ConfigValidator) sysctl(config *configs.Config) error {
|
|||
return fmt.Errorf("sysctl %q is not allowed in the hosts network namespace", s)
|
||||
}
|
||||
}
|
||||
if config.Namespaces.Contains(configs.NEWUTS) {
|
||||
switch s {
|
||||
case "kernel.domainname":
|
||||
// This is namespaced and there's no explicit OCI field for it.
|
||||
continue
|
||||
case "kernel.hostname":
|
||||
// This is namespaced but there's a conflicting (dedicated) OCI field for it.
|
||||
return fmt.Errorf("sysctl %q is not allowed as it conflicts with the OCI %q field", s, "hostname")
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s)
|
||||
}
|
||||
|
||||
|
|
146
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
146
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
|
@ -28,7 +28,6 @@ import (
|
|||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -225,17 +224,13 @@ func (c *linuxContainer) Set(config configs.Config) error {
|
|||
func (c *linuxContainer) Start(process *Process) error {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
status, err := c.currentStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status == Stopped {
|
||||
if process.Init {
|
||||
if err := c.createExecFifo(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := c.start(process, status == Stopped); err != nil {
|
||||
if status == Stopped {
|
||||
if err := c.start(process); err != nil {
|
||||
if process.Init {
|
||||
c.deleteExecFifo()
|
||||
}
|
||||
return err
|
||||
|
@ -244,17 +239,10 @@ func (c *linuxContainer) Start(process *Process) error {
|
|||
}
|
||||
|
||||
func (c *linuxContainer) Run(process *Process) error {
|
||||
c.m.Lock()
|
||||
status, err := c.currentStatus()
|
||||
if err != nil {
|
||||
c.m.Unlock()
|
||||
return err
|
||||
}
|
||||
c.m.Unlock()
|
||||
if err := c.Start(process); err != nil {
|
||||
return err
|
||||
}
|
||||
if status == Stopped {
|
||||
if process.Init {
|
||||
return c.exec()
|
||||
}
|
||||
return nil
|
||||
|
@ -335,8 +323,8 @@ type openResult struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (c *linuxContainer) start(process *Process, isInit bool) error {
|
||||
parent, err := c.newParentProcess(process, isInit)
|
||||
func (c *linuxContainer) start(process *Process) error {
|
||||
parent, err := c.newParentProcess(process)
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "creating new parent process")
|
||||
}
|
||||
|
@ -349,7 +337,7 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
|
|||
}
|
||||
// generate a timestamp indicating when the container was started
|
||||
c.created = time.Now().UTC()
|
||||
if isInit {
|
||||
if process.Init {
|
||||
c.state = &createdState{
|
||||
c: c,
|
||||
}
|
||||
|
@ -377,10 +365,6 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.state = &runningState{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -443,7 +427,7 @@ func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
|
||||
func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) {
|
||||
parentPipe, childPipe, err := utils.NewSockPair("init")
|
||||
if err != nil {
|
||||
return nil, newSystemErrorWithCause(err, "creating new init pipe")
|
||||
|
@ -452,7 +436,7 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
|
|||
if err != nil {
|
||||
return nil, newSystemErrorWithCause(err, "creating new command template")
|
||||
}
|
||||
if !doInit {
|
||||
if !p.Init {
|
||||
return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
|
||||
}
|
||||
|
||||
|
@ -477,6 +461,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
|
|||
if cmd.SysProcAttr == nil {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GOMAXPROCS=%s", os.Getenv("GOMAXPROCS")))
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, p.ExtraFiles...)
|
||||
if p.ConsoleSocket != nil {
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, p.ConsoleSocket)
|
||||
|
@ -672,7 +657,7 @@ func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.
|
|||
Features: criuFeat,
|
||||
}
|
||||
|
||||
err := c.criuSwrk(nil, req, criuOpts, false)
|
||||
err := c.criuSwrk(nil, req, criuOpts, false, nil)
|
||||
if err != nil {
|
||||
logrus.Debugf("%s", err)
|
||||
return fmt.Errorf("CRIU feature check failed")
|
||||
|
@ -785,7 +770,7 @@ func (c *linuxContainer) checkCriuVersion(minVersion int) error {
|
|||
Type: &t,
|
||||
}
|
||||
|
||||
err := c.criuSwrk(nil, req, nil, false)
|
||||
err := c.criuSwrk(nil, req, nil, false, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CRIU version check failed: %s", err)
|
||||
}
|
||||
|
@ -943,6 +928,33 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
LazyPages: proto.Bool(criuOpts.LazyPages),
|
||||
}
|
||||
|
||||
// If the container is running in a network namespace and has
|
||||
// a path to the network namespace configured, we will dump
|
||||
// that network namespace as an external namespace and we
|
||||
// will expect that the namespace exists during restore.
|
||||
// This basically means that CRIU will ignore the namespace
|
||||
// and expect to be setup correctly.
|
||||
nsPath := c.config.Namespaces.PathOf(configs.NEWNET)
|
||||
if nsPath != "" {
|
||||
// For this to work we need at least criu 3.11.0 => 31100.
|
||||
// As there was already a successful version check we will
|
||||
// not error out if it fails. runc will just behave as it used
|
||||
// to do and ignore external network namespaces.
|
||||
err := c.checkCriuVersion(31100)
|
||||
if err == nil {
|
||||
// CRIU expects the information about an external namespace
|
||||
// like this: --external net[<inode>]:<key>
|
||||
// This <key> is always 'extRootNetNS'.
|
||||
var netns syscall.Stat_t
|
||||
err = syscall.Stat(nsPath, &netns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
criuExternal := fmt.Sprintf("net[%d]:extRootNetNS", netns.Ino)
|
||||
rpcOpts.External = append(rpcOpts.External, criuExternal)
|
||||
}
|
||||
}
|
||||
|
||||
fcg := c.cgroupManager.GetPaths()["freezer"]
|
||||
if fcg != "" {
|
||||
rpcOpts.FreezeCgroup = proto.String(fcg)
|
||||
|
@ -1047,7 +1059,7 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
}
|
||||
}
|
||||
|
||||
err = c.criuSwrk(nil, req, criuOpts, false)
|
||||
err = c.criuSwrk(nil, req, criuOpts, false, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1091,6 +1103,8 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
var extraFiles []*os.File
|
||||
|
||||
// TODO(avagin): Figure out how to make this work nicely. CRIU doesn't have
|
||||
// support for unprivileged restore at the moment.
|
||||
if c.config.Rootless {
|
||||
|
@ -1165,6 +1179,38 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
},
|
||||
}
|
||||
|
||||
// Same as during checkpointing. If the container has a specific network namespace
|
||||
// assigned to it, this now expects that the checkpoint will be restored in a
|
||||
// already created network namespace.
|
||||
nsPath := c.config.Namespaces.PathOf(configs.NEWNET)
|
||||
if nsPath != "" {
|
||||
// For this to work we need at least criu 3.11.0 => 31100.
|
||||
// As there was already a successful version check we will
|
||||
// not error out if it fails. runc will just behave as it used
|
||||
// to do and ignore external network namespaces.
|
||||
err := c.checkCriuVersion(31100)
|
||||
if err == nil {
|
||||
// CRIU wants the information about an existing network namespace
|
||||
// like this: --inherit-fd fd[<fd>]:<key>
|
||||
// The <key> needs to be the same as during checkpointing.
|
||||
// We are always using 'extRootNetNS' as the key in this.
|
||||
netns, err := os.Open(nsPath)
|
||||
defer netns.Close()
|
||||
if err != nil {
|
||||
logrus.Error("If a specific network namespace is defined it must exist: %s", err)
|
||||
return fmt.Errorf("Requested network namespace %v does not exist", nsPath)
|
||||
}
|
||||
inheritFd := new(criurpc.InheritFd)
|
||||
inheritFd.Key = proto.String("extRootNetNS")
|
||||
// The offset of four is necessary because 0, 1, 2 and 3 is already
|
||||
// used by stdin, stdout, stderr, 'criu swrk' socket.
|
||||
inheritFd.Fd = proto.Int32(int32(4 + len(extraFiles)))
|
||||
req.Opts.InheritFd = append(req.Opts.InheritFd, inheritFd)
|
||||
// All open FDs need to be transferred to CRIU via extraFiles
|
||||
extraFiles = append(extraFiles, netns)
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range c.config.Mounts {
|
||||
switch m.Device {
|
||||
case "bind":
|
||||
|
@ -1223,7 +1269,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
req.Opts.InheritFd = append(req.Opts.InheritFd, inheritFd)
|
||||
}
|
||||
}
|
||||
return c.criuSwrk(process, req, criuOpts, true)
|
||||
return c.criuSwrk(process, req, criuOpts, true, extraFiles)
|
||||
}
|
||||
|
||||
func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
|
||||
|
@ -1253,7 +1299,7 @@ func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, applyCgroups bool) error {
|
||||
func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, applyCgroups bool, extraFiles []*os.File) error {
|
||||
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1294,6 +1340,9 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
|
|||
cmd.Stderr = process.Stderr
|
||||
}
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, criuServer)
|
||||
if extraFiles != nil {
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, extraFiles...)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
|
@ -1801,28 +1850,22 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
|
|||
Value: []byte(c.newgidmapPath),
|
||||
})
|
||||
}
|
||||
// The following only applies if we are root.
|
||||
if !c.config.Rootless {
|
||||
// check if we have CAP_SETGID to setgroup properly
|
||||
pid, err := capability.NewPid(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !pid.Get(capability.EFFECTIVE, capability.CAP_SETGID) {
|
||||
r.AddData(&Boolmsg{
|
||||
Type: SetgroupAttr,
|
||||
Value: true,
|
||||
})
|
||||
}
|
||||
if requiresRootOrMappingTool(c.config) {
|
||||
r.AddData(&Boolmsg{
|
||||
Type: SetgroupAttr,
|
||||
Value: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write oom_score_adj
|
||||
r.AddData(&Bytemsg{
|
||||
Type: OomScoreAdjAttr,
|
||||
Value: []byte(fmt.Sprintf("%d", c.config.OomScoreAdj)),
|
||||
})
|
||||
if c.config.OomScoreAdj != nil {
|
||||
// write oom_score_adj
|
||||
r.AddData(&Bytemsg{
|
||||
Type: OomScoreAdjAttr,
|
||||
Value: []byte(fmt.Sprintf("%d", *c.config.OomScoreAdj)),
|
||||
})
|
||||
}
|
||||
|
||||
// write rootless
|
||||
r.AddData(&Boolmsg{
|
||||
|
@ -1847,3 +1890,10 @@ func ignoreTerminateErrors(err error) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func requiresRootOrMappingTool(c *configs.Config) bool {
|
||||
gidMap := []configs.IDMap{
|
||||
{ContainerID: 0, HostID: os.Getegid(), Size: 1},
|
||||
}
|
||||
return !reflect.DeepEqual(c.GidMappings, gidMap)
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/container_linux_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/container_linux_test.go
generated
vendored
|
@ -159,7 +159,7 @@ func TestGetContainerStats(t *testing.T) {
|
|||
t.Fatal("cgroup stats are nil")
|
||||
}
|
||||
if stats.CgroupStats.MemoryStats.Usage.Usage != 1024 {
|
||||
t.Fatalf("expected memory usage 1024 but recevied %d", stats.CgroupStats.MemoryStats.Usage.Usage)
|
||||
t.Fatalf("expected memory usage 1024 but received %d", stats.CgroupStats.MemoryStats.Usage.Usage)
|
||||
}
|
||||
if intelrdt.IsEnabled() {
|
||||
if stats.IntelRdtStats == nil {
|
||||
|
|
105
vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go
generated
vendored
Normal file
105
vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
package devices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotADevice = errors.New("not a device node")
|
||||
)
|
||||
|
||||
// Testing dependencies
|
||||
var (
|
||||
unixLstat = unix.Lstat
|
||||
ioutilReadDir = ioutil.ReadDir
|
||||
)
|
||||
|
||||
// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
|
||||
func DeviceFromPath(path, permissions string) (*configs.Device, error) {
|
||||
var stat unix.Stat_t
|
||||
err := unixLstat(path, &stat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
devNumber = uint64(stat.Rdev)
|
||||
major = unix.Major(devNumber)
|
||||
minor = unix.Minor(devNumber)
|
||||
)
|
||||
if major == 0 {
|
||||
return nil, ErrNotADevice
|
||||
}
|
||||
|
||||
var (
|
||||
devType rune
|
||||
mode = stat.Mode
|
||||
)
|
||||
switch {
|
||||
case mode&unix.S_IFBLK == unix.S_IFBLK:
|
||||
devType = 'b'
|
||||
case mode&unix.S_IFCHR == unix.S_IFCHR:
|
||||
devType = 'c'
|
||||
}
|
||||
return &configs.Device{
|
||||
Type: devType,
|
||||
Path: path,
|
||||
Major: int64(major),
|
||||
Minor: int64(minor),
|
||||
Permissions: permissions,
|
||||
FileMode: os.FileMode(mode),
|
||||
Uid: stat.Uid,
|
||||
Gid: stat.Gid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func HostDevices() ([]*configs.Device, error) {
|
||||
return getDevices("/dev")
|
||||
}
|
||||
|
||||
func getDevices(path string) ([]*configs.Device, error) {
|
||||
files, err := ioutilReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []*configs.Device{}
|
||||
for _, f := range files {
|
||||
switch {
|
||||
case f.IsDir():
|
||||
switch f.Name() {
|
||||
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
|
||||
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
|
||||
continue
|
||||
default:
|
||||
sub, err := getDevices(filepath.Join(path, f.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out = append(out, sub...)
|
||||
continue
|
||||
}
|
||||
case f.Name() == "console":
|
||||
continue
|
||||
}
|
||||
device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
|
||||
if err != nil {
|
||||
if err == ErrNotADevice {
|
||||
continue
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, device)
|
||||
}
|
||||
return out, nil
|
||||
}
|
63
vendor/github.com/opencontainers/runc/libcontainer/devices/devices_test.go
generated
vendored
Normal file
63
vendor/github.com/opencontainers/runc/libcontainer/devices/devices_test.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package devices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestDeviceFromPathLstatFailure(t *testing.T) {
|
||||
testError := errors.New("test error")
|
||||
|
||||
// Override unix.Lstat to inject error.
|
||||
unixLstat = func(path string, stat *unix.Stat_t) error {
|
||||
return testError
|
||||
}
|
||||
|
||||
_, err := DeviceFromPath("", "")
|
||||
if err != testError {
|
||||
t.Fatalf("Unexpected error %v, expected %v", err, testError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostDevicesIoutilReadDirFailure(t *testing.T) {
|
||||
testError := errors.New("test error")
|
||||
|
||||
// Override ioutil.ReadDir to inject error.
|
||||
ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
|
||||
return nil, testError
|
||||
}
|
||||
|
||||
_, err := HostDevices()
|
||||
if err != testError {
|
||||
t.Fatalf("Unexpected error %v, expected %v", err, testError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostDevicesIoutilReadDirDeepFailure(t *testing.T) {
|
||||
testError := errors.New("test error")
|
||||
called := false
|
||||
|
||||
// Override ioutil.ReadDir to inject error after the first call.
|
||||
ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
|
||||
if called {
|
||||
return nil, testError
|
||||
}
|
||||
called = true
|
||||
|
||||
// Provoke a second call.
|
||||
fi, err := os.Lstat("/tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
return []os.FileInfo{fi}, nil
|
||||
}
|
||||
|
||||
_, err := HostDevices()
|
||||
if err != testError {
|
||||
t.Fatalf("Unexpected error %v, expected %v", err, testError)
|
||||
}
|
||||
}
|
46
vendor/github.com/opencontainers/runc/libcontainer/factory_linux.go
generated
vendored
46
vendor/github.com/opencontainers/runc/libcontainer/factory_linux.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
|||
"runtime/debug"
|
||||
"strconv"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||
|
@ -59,9 +60,9 @@ func SystemdCgroups(l *LinuxFactory) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Cgroupfs is an options func to configure a LinuxFactory to return
|
||||
// containers that use the native cgroups filesystem implementation to
|
||||
// create and manage cgroups.
|
||||
// Cgroupfs is an options func to configure a LinuxFactory to return containers
|
||||
// that use the native cgroups filesystem implementation to create and manage
|
||||
// cgroups.
|
||||
func Cgroupfs(l *LinuxFactory) error {
|
||||
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
|
||||
return &fs.Manager{
|
||||
|
@ -72,6 +73,23 @@ func Cgroupfs(l *LinuxFactory) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RootlessCgroupfs is an options func to configure a LinuxFactory to return
|
||||
// containers that use the native cgroups filesystem implementation to create
|
||||
// and manage cgroups. The difference between RootlessCgroupfs and Cgroupfs is
|
||||
// that RootlessCgroupfs can transparently handle permission errors that occur
|
||||
// during rootless container setup (while still allowing cgroup usage if
|
||||
// they've been set up properly).
|
||||
func RootlessCgroupfs(l *LinuxFactory) error {
|
||||
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
|
||||
return &fs.Manager{
|
||||
Cgroups: config,
|
||||
Rootless: true,
|
||||
Paths: paths,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IntelRdtfs is an options func to configure a LinuxFactory to return
|
||||
// containers that use the Intel RDT "resource control" filesystem to
|
||||
// create and manage Intel Xeon platform shared resources (e.g., L3 cache).
|
||||
|
@ -178,7 +196,10 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
|
|||
if err := l.Validator.Validate(config); err != nil {
|
||||
return nil, newGenericError(err, ConfigInvalid)
|
||||
}
|
||||
containerRoot := filepath.Join(l.Root, id)
|
||||
containerRoot, err := securejoin.SecureJoin(l.Root, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := os.Stat(containerRoot); err == nil {
|
||||
return nil, newGenericError(fmt.Errorf("container with id exists: %v", id), IdInUse)
|
||||
} else if !os.IsNotExist(err) {
|
||||
|
@ -212,7 +233,14 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
|
|||
if l.Root == "" {
|
||||
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
|
||||
}
|
||||
containerRoot := filepath.Join(l.Root, id)
|
||||
//when load, we need to check id is valid or not.
|
||||
if err := l.validateID(id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerRoot, err := securejoin.SecureJoin(l.Root, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state, err := l.loadState(containerRoot, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -322,7 +350,11 @@ func (l *LinuxFactory) StartInitialization() (err error) {
|
|||
}
|
||||
|
||||
func (l *LinuxFactory) loadState(root, id string) (*State, error) {
|
||||
f, err := os.Open(filepath.Join(root, stateFilename))
|
||||
stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(stateFilePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, newGenericError(fmt.Errorf("container %q does not exist", id), ContainerNotExists)
|
||||
|
@ -338,7 +370,7 @@ func (l *LinuxFactory) loadState(root, id string) (*State, error) {
|
|||
}
|
||||
|
||||
func (l *LinuxFactory) validateID(id string) error {
|
||||
if !idRegex.MatchString(id) {
|
||||
if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {
|
||||
return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
|
||||
}
|
||||
|
||||
|
|
13
vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
generated
vendored
13
vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
generated
vendored
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
@ -121,7 +122,7 @@ func finalizeNamespace(config *initConfig) error {
|
|||
// inherited are marked close-on-exec so they stay out of the
|
||||
// container
|
||||
if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "close exec fds")
|
||||
}
|
||||
|
||||
capabilities := &configs.Capabilities{}
|
||||
|
@ -136,20 +137,20 @@ func finalizeNamespace(config *initConfig) error {
|
|||
}
|
||||
// drop capabilities in bounding set before changing user
|
||||
if err := w.ApplyBoundingSet(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "apply bounding set")
|
||||
}
|
||||
// preserve existing capabilities while we change users
|
||||
if err := system.SetKeepCaps(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "set keep caps")
|
||||
}
|
||||
if err := setupUser(config); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "setup user")
|
||||
}
|
||||
if err := system.ClearKeepCaps(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "clear keep caps")
|
||||
}
|
||||
if err := w.ApplyCaps(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "apply caps")
|
||||
}
|
||||
if config.Cwd != "" {
|
||||
if err := unix.Chdir(config.Cwd); err != nil {
|
||||
|
|
257
vendor/github.com/opencontainers/runc/libcontainer/integration/checkpoint_test.go
generated
vendored
Normal file
257
vendor/github.com/opencontainers/runc/libcontainer/integration/checkpoint_test.go
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func showFile(t *testing.T, fname string) error {
|
||||
t.Logf("=== %s ===\n", fname)
|
||||
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
t.Log(scanner.Text())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Logf("=== END ===\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUsernsCheckpoint(t *testing.T) {
|
||||
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
|
||||
t.Skip("userns is unsupported")
|
||||
}
|
||||
cmd := exec.Command("criu", "check", "--feature", "userns")
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Skip("Unable to c/r a container with userns")
|
||||
}
|
||||
testCheckpoint(t, true)
|
||||
}
|
||||
|
||||
func TestCheckpoint(t *testing.T) {
|
||||
testCheckpoint(t, false)
|
||||
}
|
||||
|
||||
func testCheckpoint(t *testing.T, userns bool) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
root, err := newTestRoot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
|
||||
config.Mounts = append(config.Mounts, &configs.Mount{
|
||||
Destination: "/sys/fs/cgroup",
|
||||
Device: "cgroup",
|
||||
Flags: defaultMountFlags | unix.MS_RDONLY,
|
||||
})
|
||||
|
||||
if userns {
|
||||
config.UidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.GidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
|
||||
}
|
||||
|
||||
factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container, err := factory.Create("test", config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
|
||||
pconfig := libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Stdout: &stdout,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(&pconfig)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pid, err := pconfig.Pid()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentDir, err := ioutil.TempDir("", "criu-parent")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(parentDir)
|
||||
|
||||
preDumpOpts := &libcontainer.CriuOpts{
|
||||
ImagesDirectory: parentDir,
|
||||
WorkDirectory: parentDir,
|
||||
PreDump: true,
|
||||
}
|
||||
preDumpLog := filepath.Join(preDumpOpts.WorkDirectory, "dump.log")
|
||||
|
||||
if err := container.Checkpoint(preDumpOpts); err != nil {
|
||||
showFile(t, preDumpLog)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err := container.Status()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if state != libcontainer.Running {
|
||||
t.Fatal("Unexpected preDump state: ", state)
|
||||
}
|
||||
|
||||
imagesDir, err := ioutil.TempDir("", "criu")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(imagesDir)
|
||||
|
||||
checkpointOpts := &libcontainer.CriuOpts{
|
||||
ImagesDirectory: imagesDir,
|
||||
WorkDirectory: imagesDir,
|
||||
ParentImage: "../criu-parent",
|
||||
}
|
||||
dumpLog := filepath.Join(checkpointOpts.WorkDirectory, "dump.log")
|
||||
restoreLog := filepath.Join(checkpointOpts.WorkDirectory, "restore.log")
|
||||
|
||||
if err := container.Checkpoint(checkpointOpts); err != nil {
|
||||
showFile(t, dumpLog)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err = container.Status()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if state != libcontainer.Stopped {
|
||||
t.Fatal("Unexpected state checkpoint: ", state)
|
||||
}
|
||||
|
||||
stdinW.Close()
|
||||
_, err = process.Wait()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// reload the container
|
||||
container, err = factory.Load("test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoreStdinR, restoreStdinW, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoreProcessConfig := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Stdin: restoreStdinR,
|
||||
Stdout: &stdout,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Restore(restoreProcessConfig, checkpointOpts)
|
||||
restoreStdinR.Close()
|
||||
defer restoreStdinW.Close()
|
||||
if err != nil {
|
||||
showFile(t, restoreLog)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err = container.Status()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if state != libcontainer.Running {
|
||||
t.Fatal("Unexpected restore state: ", state)
|
||||
}
|
||||
|
||||
pid, err = restoreProcessConfig.Pid()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
process, err = os.FindProcess(pid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = restoreStdinW.WriteString("Hello!")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoreStdinW.Close()
|
||||
s, err := process.Wait()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !s.Success() {
|
||||
t.Fatal(s.String(), pid)
|
||||
}
|
||||
|
||||
output := string(stdout.Bytes())
|
||||
if !strings.Contains(output, "Hello!") {
|
||||
t.Fatal("Did not restore the pipe correctly:", output)
|
||||
}
|
||||
}
|
2
vendor/github.com/opencontainers/runc/libcontainer/integration/doc.go
generated
vendored
Normal file
2
vendor/github.com/opencontainers/runc/libcontainer/integration/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// integration is used for integration testing of libcontainer
|
||||
package integration
|
1778
vendor/github.com/opencontainers/runc/libcontainer/integration/exec_test.go
generated
vendored
Normal file
1778
vendor/github.com/opencontainers/runc/libcontainer/integration/exec_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
608
vendor/github.com/opencontainers/runc/libcontainer/integration/execin_test.go
generated
vendored
Normal file
608
vendor/github.com/opencontainers/runc/libcontainer/integration/execin_test.go
generated
vendored
Normal file
|
@ -0,0 +1,608 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestExecIn(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"ps"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
if !strings.Contains(out, "cat") || !strings.Contains(out, "ps") {
|
||||
t.Fatalf("unexpected running process, output %q", out)
|
||||
}
|
||||
if strings.Contains(out, "\r") {
|
||||
t.Fatalf("unexpected carriage-return in output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInUsernsRlimit(t *testing.T) {
|
||||
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
|
||||
t.Skip("userns is unsupported")
|
||||
}
|
||||
|
||||
testExecInRlimit(t, true)
|
||||
}
|
||||
|
||||
func TestExecInRlimit(t *testing.T) {
|
||||
testExecInRlimit(t, false)
|
||||
}
|
||||
|
||||
func testExecInRlimit(t *testing.T, userns bool) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
if userns {
|
||||
config.UidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.GidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"/bin/sh", "-c", "ulimit -n"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Rlimits: []configs.Rlimit{
|
||||
// increase process rlimit higher than container rlimit to test per-process limit
|
||||
{Type: unix.RLIMIT_NOFILE, Hard: 1026, Soft: 1026},
|
||||
},
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
if limit := strings.TrimSpace(out); limit != "1026" {
|
||||
t.Fatalf("expected rlimit to be 1026, got %s", limit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInAdditionalGroups(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
pconfig := libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"sh", "-c", "id", "-Gn"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
AdditionalGroups: []string{"plugdev", "audio"},
|
||||
}
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
waitProcess(&pconfig, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
outputGroups := string(stdout.Bytes())
|
||||
|
||||
// Check that the groups output has the groups that we specified
|
||||
if !strings.Contains(outputGroups, "audio") {
|
||||
t.Fatalf("Listed groups do not contain the audio group as expected: %v", outputGroups)
|
||||
}
|
||||
|
||||
if !strings.Contains(outputGroups, "plugdev") {
|
||||
t.Fatalf("Listed groups do not contain the plugdev group as expected: %v", outputGroups)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInError(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer func() {
|
||||
stdinW.Close()
|
||||
if _, err := process.Wait(); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
}()
|
||||
ok(t, err)
|
||||
|
||||
for i := 0; i < 42; i++ {
|
||||
var out bytes.Buffer
|
||||
unexistent := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"unexistent"},
|
||||
Env: standardEnvironment,
|
||||
Stderr: &out,
|
||||
}
|
||||
err = container.Run(unexistent)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "executable file not found") {
|
||||
t.Fatalf("Should be error about not found executable, got %s", err)
|
||||
}
|
||||
if !bytes.Contains(out.Bytes(), []byte("executable file not found")) {
|
||||
t.Fatalf("executable file not found error not delivered to stdio:\n%s", out.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInTTY(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"ps"},
|
||||
Env: standardEnvironment,
|
||||
}
|
||||
parent, child, err := utils.NewSockPair("console")
|
||||
if err != nil {
|
||||
ok(t, err)
|
||||
}
|
||||
defer parent.Close()
|
||||
defer child.Close()
|
||||
ps.ConsoleSocket = child
|
||||
type cdata struct {
|
||||
c console.Console
|
||||
err error
|
||||
}
|
||||
dc := make(chan *cdata, 1)
|
||||
go func() {
|
||||
f, err := utils.RecvFd(parent)
|
||||
if err != nil {
|
||||
dc <- &cdata{
|
||||
err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
c, err := console.ConsoleFromFile(f)
|
||||
if err != nil {
|
||||
dc <- &cdata{
|
||||
err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
console.ClearONLCR(c.Fd())
|
||||
dc <- &cdata{
|
||||
c: c,
|
||||
}
|
||||
}()
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
data := <-dc
|
||||
if data.err != nil {
|
||||
ok(t, data.err)
|
||||
}
|
||||
console := data.c
|
||||
copy := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(&stdout, console)
|
||||
close(copy)
|
||||
}()
|
||||
ok(t, err)
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Waiting for copy timed out")
|
||||
case <-copy:
|
||||
}
|
||||
waitProcess(ps, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := stdout.String()
|
||||
if !strings.Contains(out, "cat") || !strings.Contains(out, "ps") {
|
||||
t.Fatalf("unexpected running process, output %q", out)
|
||||
}
|
||||
if strings.Contains(out, "\r") {
|
||||
t.Fatalf("unexpected carriage-return in output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInEnvironment(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
process2 := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"env"},
|
||||
Env: []string{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"DEBUG=true",
|
||||
"DEBUG=false",
|
||||
"ENV=test",
|
||||
},
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process2)
|
||||
ok(t, err)
|
||||
waitProcess(process2, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
// check execin's process environment
|
||||
if !strings.Contains(out, "DEBUG=false") ||
|
||||
!strings.Contains(out, "ENV=test") ||
|
||||
!strings.Contains(out, "HOME=/root") ||
|
||||
!strings.Contains(out, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") ||
|
||||
strings.Contains(out, "DEBUG=true") {
|
||||
t.Fatalf("unexpected running process, output %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecinPassExtraFiles(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
pipeout1, pipein1, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pipeout2, pipein2, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
inprocess := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"},
|
||||
Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
||||
ExtraFiles: []*os.File{pipein1, pipein2},
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Run(inprocess)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
waitProcess(inprocess, t)
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := string(stdout.Bytes())
|
||||
// fd 5 is the directory handle for /proc/$$/fd
|
||||
if out != "0 1 2 3 4 5" {
|
||||
t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to exec, got '%s'", out)
|
||||
}
|
||||
var buf = []byte{0}
|
||||
_, err = pipeout1.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out1 := string(buf)
|
||||
if out1 != "1" {
|
||||
t.Fatalf("expected first pipe to receive '1', got '%s'", out1)
|
||||
}
|
||||
|
||||
_, err = pipeout2.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out2 := string(buf)
|
||||
if out2 != "2" {
|
||||
t.Fatalf("expected second pipe to receive '2', got '%s'", out2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInOomScoreAdj(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.OomScoreAdj = ptrInt(200)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"/bin/sh", "-c", "cat /proc/self/oom_score_adj"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
if oomScoreAdj := strings.TrimSpace(out); oomScoreAdj != strconv.Itoa(*config.OomScoreAdj) {
|
||||
t.Fatalf("expected oomScoreAdj to be %d, got %s", *config.OomScoreAdj, oomScoreAdj)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInUserns(t *testing.T) {
|
||||
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
|
||||
t.Skip("userns is unsupported")
|
||||
}
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.UidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.GidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
initPID, err := process.Pid()
|
||||
ok(t, err)
|
||||
initUserns, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/user", initPID))
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
process2 := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"readlink", "/proc/self/ns/user"},
|
||||
Env: []string{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
},
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
err = container.Run(process2)
|
||||
ok(t, err)
|
||||
waitProcess(process2, t)
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
if out := strings.TrimSpace(buffers.Stdout.String()); out != initUserns {
|
||||
t.Errorf("execin userns(%s), wanted %s", out, initUserns)
|
||||
}
|
||||
}
|
61
vendor/github.com/opencontainers/runc/libcontainer/integration/init_test.go
generated
vendored
Normal file
61
vendor/github.com/opencontainers/runc/libcontainer/integration/init_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// init runs the libcontainer initialization code because of the busybox style needs
|
||||
// to work around the go runtime and the issues with forking
|
||||
func init() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "init" {
|
||||
return
|
||||
}
|
||||
runtime.GOMAXPROCS(1)
|
||||
runtime.LockOSThread()
|
||||
factory, err := libcontainer.New("")
|
||||
if err != nil {
|
||||
logrus.Fatalf("unable to initialize for container: %s", err)
|
||||
}
|
||||
if err := factory.StartInitialization(); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
factory libcontainer.Factory
|
||||
systemdFactory libcontainer.Factory
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var (
|
||||
err error
|
||||
ret int
|
||||
)
|
||||
|
||||
logrus.SetOutput(os.Stderr)
|
||||
logrus.SetLevel(logrus.InfoLevel)
|
||||
|
||||
factory, err = libcontainer.New("/run/libctTests", libcontainer.Cgroupfs)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if systemd.UseSystemd() {
|
||||
systemdFactory, err = libcontainer.New("/run/libctTests", libcontainer.SystemdCgroups)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
ret = m.Run()
|
||||
os.Exit(ret)
|
||||
}
|
422
vendor/github.com/opencontainers/runc/libcontainer/integration/seccomp_test.go
generated
vendored
Normal file
422
vendor/github.com/opencontainers/runc/libcontainer/integration/seccomp_test.go
generated
vendored
Normal file
|
@ -0,0 +1,422 @@
|
|||
// +build linux,cgo,seccomp
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||
)
|
||||
|
||||
func TestSeccompDenyGetcwd(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "getcwd",
|
||||
Action: configs.Errno,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
buffers := newStdBuffers()
|
||||
pwd := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"pwd"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(pwd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ps, err := pwd.Wait()
|
||||
if err == nil {
|
||||
t.Fatal("Expecting error (negative return code); instead exited cleanly!")
|
||||
}
|
||||
|
||||
var exitCode int
|
||||
status := ps.Sys().(syscall.WaitStatus)
|
||||
if status.Exited() {
|
||||
exitCode = status.ExitStatus()
|
||||
} else if status.Signaled() {
|
||||
exitCode = -int(status.Signal())
|
||||
} else {
|
||||
t.Fatalf("Unrecognized exit reason!")
|
||||
}
|
||||
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Getcwd should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
|
||||
expected := "pwd: getcwd: Operation not permitted"
|
||||
actual := strings.Trim(buffers.Stderr.String(), "\n")
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected output %s but got %s\n", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompPermitWriteConditional(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
buffers := newStdBuffers()
|
||||
dmesg := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"busybox", "ls", "/"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(dmesg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := dmesg.Wait(); err != nil {
|
||||
t.Fatalf("%s: %s", err, buffers.Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompDenyWriteConditional(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
// Only test if library version is v2.2.1 or higher
|
||||
// Conditional filtering will always error in v2.2.0 and lower
|
||||
major, minor, micro := libseccomp.GetLibraryVersion()
|
||||
if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
buffers := newStdBuffers()
|
||||
dmesg := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"busybox", "ls", "does_not_exist"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(dmesg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ps, err := dmesg.Wait()
|
||||
if err == nil {
|
||||
t.Fatal("Expecting negative return, instead got 0!")
|
||||
}
|
||||
|
||||
var exitCode int
|
||||
status := ps.Sys().(syscall.WaitStatus)
|
||||
if status.Exited() {
|
||||
exitCode = status.ExitStatus()
|
||||
} else if status.Signaled() {
|
||||
exitCode = -int(status.Signal())
|
||||
} else {
|
||||
t.Fatalf("Unrecognized exit reason!")
|
||||
}
|
||||
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
|
||||
// We're denying write to stderr, so we expect an empty buffer
|
||||
expected := ""
|
||||
actual := strings.Trim(buffers.Stderr.String(), "\n")
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected output %s but got %s\n", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompPermitWriteMultipleConditions(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Value: 0,
|
||||
Op: configs.NotEqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/")
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", buffers, err)
|
||||
}
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers)
|
||||
}
|
||||
// We don't need to verify the actual thing printed
|
||||
// Just that something was written to stdout
|
||||
if len(buffers.Stdout.String()) == 0 {
|
||||
t.Fatalf("Nothing was written to stdout, write call failed!\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompDenyWriteMultipleConditions(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
// Only test if library version is v2.2.1 or higher
|
||||
// Conditional filtering will always error in v2.2.0 and lower
|
||||
major, minor, micro := libseccomp.GetLibraryVersion()
|
||||
if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Value: 0,
|
||||
Op: configs.NotEqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist")
|
||||
if err == nil {
|
||||
t.Fatalf("Expecting error return, instead got 0")
|
||||
}
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
|
||||
expected := ""
|
||||
actual := strings.Trim(buffers.Stderr.String(), "\n")
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected output %s but got %s\n", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompMultipleConditionSameArgDeniesStdout(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
// Prevent writing to both stdout and stderr
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 1,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/")
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", buffers, err)
|
||||
}
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers)
|
||||
}
|
||||
// Verify that nothing was printed
|
||||
if len(buffers.Stdout.String()) != 0 {
|
||||
t.Fatalf("Something was written to stdout, write call succeeded!\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompMultipleConditionSameArgDeniesStderr(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
// Prevent writing to both stdout and stderr
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 1,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist")
|
||||
if err == nil {
|
||||
t.Fatalf("Expecting error return, instead got 0")
|
||||
}
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
// Verify nothing was printed
|
||||
if len(buffers.Stderr.String()) != 0 {
|
||||
t.Fatalf("Something was written to stderr, write call succeeded!\n")
|
||||
}
|
||||
}
|
191
vendor/github.com/opencontainers/runc/libcontainer/integration/template_test.go
generated
vendored
Normal file
191
vendor/github.com/opencontainers/runc/libcontainer/integration/template_test.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var standardEnvironment = []string{
|
||||
"HOME=/root",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=integration",
|
||||
"TERM=xterm",
|
||||
}
|
||||
|
||||
const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
|
||||
|
||||
// newTemplateConfig returns a base template for running a container
|
||||
//
|
||||
// it uses a network strategy of just setting a loopback interface
|
||||
// and the default setup for devices
|
||||
func newTemplateConfig(rootfs string) *configs.Config {
|
||||
allowAllDevices := false
|
||||
return &configs.Config{
|
||||
Rootfs: rootfs,
|
||||
Capabilities: &configs.Capabilities{
|
||||
Bounding: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Permitted: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Inheritable: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Ambient: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Effective: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
},
|
||||
Namespaces: configs.Namespaces([]configs.Namespace{
|
||||
{Type: configs.NEWNS},
|
||||
{Type: configs.NEWUTS},
|
||||
{Type: configs.NEWIPC},
|
||||
{Type: configs.NEWPID},
|
||||
{Type: configs.NEWNET},
|
||||
}),
|
||||
Cgroups: &configs.Cgroup{
|
||||
Path: "integration/test",
|
||||
Resources: &configs.Resources{
|
||||
MemorySwappiness: nil,
|
||||
AllowAllDevices: &allowAllDevices,
|
||||
AllowedDevices: configs.DefaultAllowedDevices,
|
||||
},
|
||||
},
|
||||
MaskPaths: []string{
|
||||
"/proc/kcore",
|
||||
"/sys/firmware",
|
||||
},
|
||||
ReadonlyPaths: []string{
|
||||
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||
},
|
||||
Devices: configs.DefaultAutoCreatedDevices,
|
||||
Hostname: "integration",
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "proc",
|
||||
Destination: "/proc",
|
||||
Device: "proc",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
{
|
||||
Source: "tmpfs",
|
||||
Destination: "/dev",
|
||||
Device: "tmpfs",
|
||||
Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
|
||||
Data: "mode=755",
|
||||
},
|
||||
{
|
||||
Source: "devpts",
|
||||
Destination: "/dev/pts",
|
||||
Device: "devpts",
|
||||
Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
|
||||
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||
},
|
||||
{
|
||||
Device: "tmpfs",
|
||||
Source: "shm",
|
||||
Destination: "/dev/shm",
|
||||
Data: "mode=1777,size=65536k",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
/*
|
||||
CI is broken on the debian based kernels with this
|
||||
{
|
||||
Source: "mqueue",
|
||||
Destination: "/dev/mqueue",
|
||||
Device: "mqueue",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
*/
|
||||
{
|
||||
Source: "sysfs",
|
||||
Destination: "/sys",
|
||||
Device: "sysfs",
|
||||
Flags: defaultMountFlags | unix.MS_RDONLY,
|
||||
},
|
||||
},
|
||||
Networks: []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
Address: "127.0.0.1/0",
|
||||
Gateway: "localhost",
|
||||
},
|
||||
},
|
||||
Rlimits: []configs.Rlimit{
|
||||
{
|
||||
Type: unix.RLIMIT_NOFILE,
|
||||
Hard: uint64(1025),
|
||||
Soft: uint64(1025),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
175
vendor/github.com/opencontainers/runc/libcontainer/integration/utils_test.go
generated
vendored
Normal file
175
vendor/github.com/opencontainers/runc/libcontainer/integration/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
)
|
||||
|
||||
func ptrInt(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
func newStdBuffers() *stdBuffers {
|
||||
return &stdBuffers{
|
||||
Stdin: bytes.NewBuffer(nil),
|
||||
Stdout: bytes.NewBuffer(nil),
|
||||
Stderr: bytes.NewBuffer(nil),
|
||||
}
|
||||
}
|
||||
|
||||
type stdBuffers struct {
|
||||
Stdin *bytes.Buffer
|
||||
Stdout *bytes.Buffer
|
||||
Stderr *bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *stdBuffers) String() string {
|
||||
s := []string{}
|
||||
if b.Stderr != nil {
|
||||
s = append(s, b.Stderr.String())
|
||||
}
|
||||
if b.Stdout != nil {
|
||||
s = append(s, b.Stdout.String())
|
||||
}
|
||||
return strings.Join(s, "|")
|
||||
}
|
||||
|
||||
// ok fails the test if an err is not nil.
|
||||
func ok(t testing.TB, err error) {
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func waitProcess(p *libcontainer.Process, t *testing.T) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
status, err := p.Wait()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error())
|
||||
}
|
||||
|
||||
if !status.Success() {
|
||||
t.Fatalf("%s:%d: unexpected status: %s\n\n", filepath.Base(file), line, status.String())
|
||||
}
|
||||
}
|
||||
|
||||
func newTestRoot() (string, error) {
|
||||
dir, err := ioutil.TempDir("", "libcontainer")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func newTestBundle() (string, error) {
|
||||
dir, err := ioutil.TempDir("", "bundle")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// newRootfs creates a new tmp directory and copies the busybox root filesystem
|
||||
func newRootfs() (string, error) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := copyBusybox(dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func remove(dir string) {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
// copyBusybox copies the rootfs for a busybox container created for the test image
|
||||
// into the new directory for the specific test
|
||||
func copyBusybox(dest string) error {
|
||||
out, err := exec.Command("sh", "-c", fmt.Sprintf("cp -a /busybox/* %s/", dest)).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy error %q: %q", err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newContainer(config *configs.Config) (libcontainer.Container, error) {
|
||||
h := md5.New()
|
||||
h.Write([]byte(time.Now().String()))
|
||||
return newContainerWithName(hex.EncodeToString(h.Sum(nil)), config)
|
||||
}
|
||||
|
||||
func newContainerWithName(name string, config *configs.Config) (libcontainer.Container, error) {
|
||||
f := factory
|
||||
if config.Cgroups != nil && config.Cgroups.Parent == "system.slice" {
|
||||
f = systemdFactory
|
||||
}
|
||||
return f.Create(name, config)
|
||||
}
|
||||
|
||||
// runContainer runs the container with the specific config and arguments
|
||||
//
|
||||
// buffers are returned containing the STDOUT and STDERR output for the run
|
||||
// along with the exit code and any go error
|
||||
func runContainer(config *configs.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) {
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
defer container.Destroy()
|
||||
buffers = newStdBuffers()
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: args,
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(process)
|
||||
if err != nil {
|
||||
return buffers, -1, err
|
||||
}
|
||||
ps, err := process.Wait()
|
||||
if err != nil {
|
||||
return buffers, -1, err
|
||||
}
|
||||
status := ps.Sys().(syscall.WaitStatus)
|
||||
if status.Exited() {
|
||||
exitCode = status.ExitStatus()
|
||||
} else if status.Signaled() {
|
||||
exitCode = -int(status.Signal())
|
||||
} else {
|
||||
return buffers, -1, err
|
||||
}
|
||||
return
|
||||
}
|
2
vendor/github.com/opencontainers/runc/libcontainer/intelrdt/intelrdt.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/intelrdt/intelrdt.go
generated
vendored
|
@ -177,7 +177,7 @@ func findIntelRdtMountpointDir() (string, error) {
|
|||
}
|
||||
|
||||
if postSeparatorFields[0] == "resctrl" {
|
||||
// Check that the mount is properly formated.
|
||||
// Check that the mount is properly formatted.
|
||||
if numPostFields < 3 {
|
||||
return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||
}
|
||||
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/message_linux.go
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/message_linux.go
generated
vendored
|
@ -77,13 +77,13 @@ func (msg *Boolmsg) Serialize() []byte {
|
|||
native.PutUint16(buf[0:2], uint16(msg.Len()))
|
||||
native.PutUint16(buf[2:4], msg.Type)
|
||||
if msg.Value {
|
||||
buf[4] = 1
|
||||
native.PutUint32(buf[4:8], uint32(1))
|
||||
} else {
|
||||
buf[4] = 0
|
||||
native.PutUint32(buf[4:8], uint32(0))
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func (msg *Boolmsg) Len() int {
|
||||
return unix.NLA_HDRLEN + 1
|
||||
return unix.NLA_HDRLEN + 4 // alignment
|
||||
}
|
||||
|
|
157
vendor/github.com/opencontainers/runc/libcontainer/network_linux.go
generated
vendored
157
vendor/github.com/opencontainers/runc/libcontainer/network_linux.go
generated
vendored
|
@ -5,18 +5,15 @@ package libcontainer
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
var strategies = map[string]networkStrategy{
|
||||
"veth": &veth{},
|
||||
"loopback": &loopback{},
|
||||
}
|
||||
|
||||
|
@ -103,157 +100,3 @@ func (l *loopback) attach(n *configs.Network) (err error) {
|
|||
func (l *loopback) detach(n *configs.Network) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// veth is a network strategy that uses a bridge and creates
|
||||
// a veth pair, one that is attached to the bridge on the host and the other
|
||||
// is placed inside the container's namespace
|
||||
type veth struct {
|
||||
}
|
||||
|
||||
func (v *veth) detach(n *configs.Network) (err error) {
|
||||
return netlink.LinkSetMaster(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: n.HostInterfaceName}}, nil)
|
||||
}
|
||||
|
||||
// attach a container network interface to an external network
|
||||
func (v *veth) attach(n *configs.Network) (err error) {
|
||||
brl, err := netlink.LinkByName(n.Bridge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
br, ok := brl.(*netlink.Bridge)
|
||||
if !ok {
|
||||
return fmt.Errorf("Wrong device type %T", brl)
|
||||
}
|
||||
host, err := netlink.LinkByName(n.HostInterfaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetMaster(host, br); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetMTU(host, n.Mtu); err != nil {
|
||||
return err
|
||||
}
|
||||
if n.HairpinMode {
|
||||
if err := netlink.LinkSetHairpin(host, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := netlink.LinkSetUp(host); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *veth) create(n *network, nspid int) (err error) {
|
||||
tmpName, err := v.generateTempPeerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.TempVethPeerName = tmpName
|
||||
if n.Bridge == "" {
|
||||
return fmt.Errorf("bridge is not specified")
|
||||
}
|
||||
veth := &netlink.Veth{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: n.HostInterfaceName,
|
||||
TxQLen: n.TxQueueLen,
|
||||
},
|
||||
PeerName: n.TempVethPeerName,
|
||||
}
|
||||
if err := netlink.LinkAdd(veth); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
netlink.LinkDel(veth)
|
||||
}
|
||||
}()
|
||||
if err := v.attach(&n.Network); err != nil {
|
||||
return err
|
||||
}
|
||||
child, err := netlink.LinkByName(n.TempVethPeerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkSetNsPid(child, nspid)
|
||||
}
|
||||
|
||||
func (v *veth) generateTempPeerName() (string, error) {
|
||||
return utils.GenerateRandomName("veth", 7)
|
||||
}
|
||||
|
||||
func (v *veth) initialize(config *network) error {
|
||||
peer := config.TempVethPeerName
|
||||
if peer == "" {
|
||||
return fmt.Errorf("peer is not specified")
|
||||
}
|
||||
child, err := netlink.LinkByName(peer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetDown(child); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetName(child, config.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
// get the interface again after we changed the name as the index also changes.
|
||||
if child, err = netlink.LinkByName(config.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.MacAddress != "" {
|
||||
mac, err := net.ParseMAC(config.MacAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetHardwareAddr(child, mac); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ip, err := netlink.ParseAddr(config.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.AddrAdd(child, ip); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.IPv6Address != "" {
|
||||
ip6, err := netlink.ParseAddr(config.IPv6Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.AddrAdd(child, ip6); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := netlink.LinkSetMTU(child, config.Mtu); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetUp(child); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Gateway != "" {
|
||||
gw := net.ParseIP(config.Gateway)
|
||||
if err := netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: child.Attrs().Index,
|
||||
Gw: gw,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if config.IPv6Gateway != "" {
|
||||
gw := net.ParseIP(config.IPv6Gateway)
|
||||
if err := netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: child.Attrs().Index,
|
||||
Gw: gw,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md
generated
vendored
|
@ -10,8 +10,8 @@ The `nsenter` package will `import "C"` and it uses [cgo](https://golang.org/cmd
|
|||
package. In cgo, if the import of "C" is immediately preceded by a comment, that comment,
|
||||
called the preamble, is used as a header when compiling the C parts of the package.
|
||||
So every time we import package `nsenter`, the C code function `nsexec()` would be
|
||||
called. And package `nsenter` is now only imported in `main_unix.go`, so every time
|
||||
before we call `cmd.Start` on linux, that C code would run.
|
||||
called. And package `nsenter` is only imported in `init.go`, so every time the runc
|
||||
`init` command is invoked, that C code is run.
|
||||
|
||||
Because `nsexec()` must be run before the Go runtime in order to use the
|
||||
Linux kernel namespace, you must `import` this library into a package if
|
||||
|
@ -37,7 +37,7 @@ the parent `nsexec()` will exit and the child `nsexec()` process will
|
|||
return to allow the Go runtime take over.
|
||||
|
||||
NOTE: We do both `setns(2)` and `clone(2)` even if we don't have any
|
||||
CLONE_NEW* clone flags because we must fork a new process in order to
|
||||
`CLONE_NEW*` clone flags because we must fork a new process in order to
|
||||
enter the PID namespace.
|
||||
|
||||
|
||||
|
|
65
vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c
generated
vendored
65
vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c
generated
vendored
|
@ -211,7 +211,7 @@ static int try_mapping_tool(const char *app, int pid, char *map, size_t map_len)
|
|||
|
||||
/*
|
||||
* If @app is NULL, execve will segfault. Just check it here and bail (if
|
||||
* we're in this path, the caller is already getting desparate and there
|
||||
* we're in this path, the caller is already getting desperate and there
|
||||
* isn't a backup to this failing). This usually would be a configuration
|
||||
* or programming issue.
|
||||
*/
|
||||
|
@ -505,7 +505,8 @@ void join_namespaces(char *nslist)
|
|||
|
||||
ns->fd = fd;
|
||||
ns->ns = nsflag(namespace);
|
||||
strncpy(ns->path, path, PATH_MAX);
|
||||
strncpy(ns->path, path, PATH_MAX - 1);
|
||||
ns->path[PATH_MAX - 1] = '\0';
|
||||
} while ((namespace = strtok_r(NULL, ",", &saveptr)) != NULL);
|
||||
|
||||
/*
|
||||
|
@ -678,17 +679,15 @@ void nsexec(void)
|
|||
/*
|
||||
* Enable setgroups(2) if we've been asked to. But we also
|
||||
* have to explicitly disable setgroups(2) if we're
|
||||
* creating a rootless container (this is required since
|
||||
* Linux 3.19).
|
||||
* creating a rootless container for single-entry mapping.
|
||||
* i.e. config.is_setgroup == false.
|
||||
* (this is required since Linux 3.19).
|
||||
*
|
||||
* For rootless multi-entry mapping, config.is_setgroup shall be true and
|
||||
* newuidmap/newgidmap shall be used.
|
||||
*/
|
||||
if (config.is_rootless && config.is_setgroup) {
|
||||
kill(child, SIGKILL);
|
||||
bail("cannot allow setgroup in an unprivileged user namespace setup");
|
||||
}
|
||||
|
||||
if (config.is_setgroup)
|
||||
update_setgroups(child, SETGROUPS_ALLOW);
|
||||
if (config.is_rootless)
|
||||
if (config.is_rootless && !config.is_setgroup)
|
||||
update_setgroups(child, SETGROUPS_DENY);
|
||||
|
||||
/* Set up mappings. */
|
||||
|
@ -809,25 +808,30 @@ void nsexec(void)
|
|||
if (config.namespaces)
|
||||
join_namespaces(config.namespaces);
|
||||
|
||||
/*
|
||||
* Unshare all of the namespaces. Now, it should be noted that this
|
||||
* ordering might break in the future (especially with rootless
|
||||
* containers). But for now, it's not possible to split this into
|
||||
* CLONE_NEWUSER + [the rest] because of some RHEL SELinux issues.
|
||||
*
|
||||
* Note that we don't merge this with clone() because there were
|
||||
* some old kernel versions where clone(CLONE_PARENT | CLONE_NEWPID)
|
||||
* was broken, so we'll just do it the long way anyway.
|
||||
*/
|
||||
if (unshare(config.cloneflags) < 0)
|
||||
bail("failed to unshare namespaces");
|
||||
|
||||
/*
|
||||
* Deal with user namespaces first. They are quite special, as they
|
||||
* affect our ability to unshare other namespaces and are used as
|
||||
* context for privilege checks.
|
||||
*
|
||||
* We don't unshare all namespaces in one go. The reason for this
|
||||
* is that, while the kernel documentation may claim otherwise,
|
||||
* there are certain cases where unsharing all namespaces at once
|
||||
* will result in namespace objects being owned incorrectly.
|
||||
* Ideally we should just fix these kernel bugs, but it's better to
|
||||
* be safe than sorry, and fix them separately.
|
||||
*
|
||||
* A specific case of this is that the SELinux label of the
|
||||
* internal kern-mount that mqueue uses will be incorrect if the
|
||||
* UTS namespace is cloned before the USER namespace is mapped.
|
||||
* I've also heard of similar problems with the network namespace
|
||||
* in some scenarios. This also mirrors how LXC deals with this
|
||||
* problem.
|
||||
*/
|
||||
if (config.cloneflags & CLONE_NEWUSER) {
|
||||
if (unshare(CLONE_NEWUSER) < 0)
|
||||
bail("failed to unshare user namespace");
|
||||
config.cloneflags &= ~CLONE_NEWUSER;
|
||||
|
||||
/*
|
||||
* We don't have the privileges to do any mapping here (see the
|
||||
* clone_parent rant). So signal our parent to hook us up.
|
||||
|
@ -853,8 +857,21 @@ void nsexec(void)
|
|||
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) < 0)
|
||||
bail("failed to set process as dumpable");
|
||||
}
|
||||
|
||||
/* Become root in the namespace proper. */
|
||||
if (setresuid(0, 0, 0) < 0)
|
||||
bail("failed to become root in user namespace");
|
||||
}
|
||||
|
||||
/*
|
||||
* Unshare all of the namespaces. Note that we don't merge this
|
||||
* with clone() because there were some old kernel versions where
|
||||
* clone(CLONE_PARENT | CLONE_NEWPID) was broken, so we'll just do
|
||||
* it the long way.
|
||||
*/
|
||||
if (unshare(config.cloneflags) < 0)
|
||||
bail("failed to unshare namespaces");
|
||||
|
||||
/*
|
||||
* TODO: What about non-namespace clone flags that we're dropping here?
|
||||
*
|
||||
|
|
3
vendor/github.com/opencontainers/runc/libcontainer/process.go
generated
vendored
3
vendor/github.com/opencontainers/runc/libcontainer/process.go
generated
vendored
|
@ -72,6 +72,9 @@ type Process struct {
|
|||
// ConsoleSocket provides the masterfd console.
|
||||
ConsoleSocket *os.File
|
||||
|
||||
// Init specifies whether the process is the first process in the container.
|
||||
Init bool
|
||||
|
||||
ops processOperations
|
||||
}
|
||||
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
generated
vendored
|
@ -537,7 +537,7 @@ func (p *Process) InitializeIO(rootuid, rootgid int) (i *IO, err error) {
|
|||
}
|
||||
fds = append(fds, r.Fd(), w.Fd())
|
||||
p.Stderr, i.Stderr = w, r
|
||||
// change ownership of the pipes incase we are in a user namespace
|
||||
// change ownership of the pipes in case we are in a user namespace
|
||||
for _, fd := range fds {
|
||||
if err := unix.Fchown(int(fd), rootuid, rootgid); err != nil {
|
||||
return nil, err
|
||||
|
|
30
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
generated
vendored
30
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
generated
vendored
|
@ -152,6 +152,26 @@ func finalizeRootfs(config *configs.Config) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// /tmp has to be mounted as private to allow MS_MOVE to work in all situations
|
||||
func prepareTmp(topTmpDir string) (string, error) {
|
||||
tmpdir, err := ioutil.TempDir(topTmpDir, "runctop")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := unix.Mount(tmpdir, tmpdir, "bind", unix.MS_BIND, ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := unix.Mount("", tmpdir, "", uintptr(unix.MS_PRIVATE), ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpdir, nil
|
||||
}
|
||||
|
||||
func cleanupTmp(tmpdir string) error {
|
||||
unix.Unmount(tmpdir, 0)
|
||||
return os.RemoveAll(tmpdir)
|
||||
}
|
||||
|
||||
func mountCmd(cmd configs.Command) error {
|
||||
command := exec.Command(cmd.Path, cmd.Args[:]...)
|
||||
command.Env = cmd.Env
|
||||
|
@ -199,7 +219,12 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
|
|||
}
|
||||
}
|
||||
if copyUp {
|
||||
tmpDir, err = ioutil.TempDir("/tmp", "runctmpdir")
|
||||
tmpdir, err := prepareTmp("/tmp")
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "tmpcopyup: failed to setup tmpdir")
|
||||
}
|
||||
defer cleanupTmp(tmpdir)
|
||||
tmpDir, err = ioutil.TempDir(tmpdir, "runctmpdir")
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir")
|
||||
}
|
||||
|
@ -396,6 +421,7 @@ func checkMountDestination(rootfs, dest string) error {
|
|||
"/proc/stat",
|
||||
"/proc/swaps",
|
||||
"/proc/uptime",
|
||||
"/proc/loadavg",
|
||||
"/proc/net/dev",
|
||||
}
|
||||
for _, valid := range validDestinations {
|
||||
|
@ -412,7 +438,7 @@ func checkMountDestination(rootfs, dest string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == "." || !strings.HasPrefix(path, "..") {
|
||||
if path != "." && !strings.HasPrefix(path, "..") {
|
||||
return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid)
|
||||
}
|
||||
}
|
||||
|
|
10
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux_test.go
generated
vendored
10
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux_test.go
generated
vendored
|
@ -9,13 +9,21 @@ import (
|
|||
)
|
||||
|
||||
func TestCheckMountDestOnProc(t *testing.T) {
|
||||
dest := "/rootfs/proc/"
|
||||
dest := "/rootfs/proc/sys"
|
||||
err := checkMountDestination("/rootfs", dest)
|
||||
if err == nil {
|
||||
t.Fatal("destination inside proc should return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMountDestOnProcChroot(t *testing.T) {
|
||||
dest := "/rootfs/proc/"
|
||||
err := checkMountDestination("/rootfs", dest)
|
||||
if err != nil {
|
||||
t.Fatal("destination inside proc when using chroot should not return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMountDestInSys(t *testing.T) {
|
||||
dest := "/rootfs//sys/fs/cgroup"
|
||||
err := checkMountDestination("/rootfs", dest)
|
||||
|
|
47
vendor/github.com/opencontainers/runc/libcontainer/seccomp/fixtures/proc_self_status
generated
vendored
Normal file
47
vendor/github.com/opencontainers/runc/libcontainer/seccomp/fixtures/proc_self_status
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
Name: cat
|
||||
State: R (running)
|
||||
Tgid: 19383
|
||||
Ngid: 0
|
||||
Pid: 19383
|
||||
PPid: 19275
|
||||
TracerPid: 0
|
||||
Uid: 1000 1000 1000 1000
|
||||
Gid: 1000 1000 1000 1000
|
||||
FDSize: 256
|
||||
Groups: 24 25 27 29 30 44 46 102 104 108 111 1000 1001
|
||||
NStgid: 19383
|
||||
NSpid: 19383
|
||||
NSpgid: 19383
|
||||
NSsid: 19275
|
||||
VmPeak: 5944 kB
|
||||
VmSize: 5944 kB
|
||||
VmLck: 0 kB
|
||||
VmPin: 0 kB
|
||||
VmHWM: 744 kB
|
||||
VmRSS: 744 kB
|
||||
VmData: 324 kB
|
||||
VmStk: 136 kB
|
||||
VmExe: 48 kB
|
||||
VmLib: 1776 kB
|
||||
VmPTE: 32 kB
|
||||
VmPMD: 12 kB
|
||||
VmSwap: 0 kB
|
||||
Threads: 1
|
||||
SigQ: 0/30067
|
||||
SigPnd: 0000000000000000
|
||||
ShdPnd: 0000000000000000
|
||||
SigBlk: 0000000000000000
|
||||
SigIgn: 0000000000000080
|
||||
SigCgt: 0000000000000000
|
||||
CapInh: 0000000000000000
|
||||
CapPrm: 0000000000000000
|
||||
CapEff: 0000000000000000
|
||||
CapBnd: 0000003fffffffff
|
||||
CapAmb: 0000000000000000
|
||||
Seccomp: 0
|
||||
Cpus_allowed: f
|
||||
Cpus_allowed_list: 0-3
|
||||
Mems_allowed: 00000000,00000001
|
||||
Mems_allowed_list: 0
|
||||
voluntary_ctxt_switches: 0
|
||||
nonvoluntary_ctxt_switches: 1
|
18
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux.go
generated
vendored
18
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux.go
generated
vendored
|
@ -216,7 +216,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
}
|
||||
config.Namespaces.Add(t, ns.Path)
|
||||
}
|
||||
if config.Namespaces.Contains(configs.NEWNET) {
|
||||
if config.Namespaces.Contains(configs.NEWNET) && config.Namespaces.PathOf(configs.NEWNET) == "" {
|
||||
config.Networks = []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
|
@ -233,7 +233,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
config.MountLabel = spec.Linux.MountLabel
|
||||
config.Sysctl = spec.Linux.Sysctl
|
||||
if spec.Linux.Seccomp != nil {
|
||||
seccomp, err := setupSeccomp(spec.Linux.Seccomp)
|
||||
seccomp, err := SetupSeccomp(spec.Linux.Seccomp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -243,8 +243,8 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
if spec.Process.SelinuxLabel != "" {
|
||||
config.ProcessLabel = spec.Process.SelinuxLabel
|
||||
}
|
||||
if spec.Process != nil && spec.Process.OOMScoreAdj != nil {
|
||||
config.OomScoreAdj = *spec.Process.OOMScoreAdj
|
||||
if spec.Process != nil {
|
||||
config.OomScoreAdj = spec.Process.OOMScoreAdj
|
||||
}
|
||||
if spec.Process.Capabilities != nil {
|
||||
config.Capabilities = &configs.Capabilities{
|
||||
|
@ -269,13 +269,17 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
func createLibcontainerMount(cwd string, m specs.Mount) *configs.Mount {
|
||||
flags, pgflags, data, ext := parseMountOptions(m.Options)
|
||||
source := m.Source
|
||||
if m.Type == "bind" {
|
||||
device := m.Type
|
||||
if flags&unix.MS_BIND != 0 {
|
||||
if device == "" {
|
||||
device = "bind"
|
||||
}
|
||||
if !filepath.IsAbs(source) {
|
||||
source = filepath.Join(cwd, m.Source)
|
||||
}
|
||||
}
|
||||
return &configs.Mount{
|
||||
Device: m.Type,
|
||||
Device: device,
|
||||
Source: source,
|
||||
Destination: m.Destination,
|
||||
Data: data,
|
||||
|
@ -732,7 +736,7 @@ func parseMountOptions(options []string) (int, []int, string, int) {
|
|||
return flag, pgflag, strings.Join(data, ","), extFlags
|
||||
}
|
||||
|
||||
func setupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) {
|
||||
func SetupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) {
|
||||
if config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux_test.go
generated
vendored
|
@ -123,7 +123,7 @@ func TestSetupSeccomp(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
seccomp, err := setupSeccomp(conf)
|
||||
seccomp, err := SetupSeccomp(conf)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create Seccomp config: %v", err)
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/stacktrace/capture_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/stacktrace/capture_test.go
generated
vendored
|
@ -19,7 +19,7 @@ func TestCaptureTestFunc(t *testing.T) {
|
|||
// the first frame is the caller
|
||||
frame := stack.Frames[0]
|
||||
if expected := "captureFunc"; frame.Function != expected {
|
||||
t.Fatalf("expected function %q but recevied %q", expected, frame.Function)
|
||||
t.Fatalf("expected function %q but received %q", expected, frame.Function)
|
||||
}
|
||||
expected := "/runc/libcontainer/stacktrace"
|
||||
if !strings.HasSuffix(frame.Package, expected) {
|
||||
|
|
27
vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
generated
vendored
27
vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/seccomp"
|
||||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -49,11 +50,11 @@ func (l *linuxStandardInit) Init() error {
|
|||
// Do not inherit the parent's session keyring.
|
||||
sessKeyId, err := keys.JoinSessionKeyring(ringname)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "join session keyring")
|
||||
}
|
||||
// Make session keyring searcheable.
|
||||
if err := keys.ModKeyringPerm(sessKeyId, keepperms, newperms); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "mod keyring permissions")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +77,7 @@ func (l *linuxStandardInit) Init() error {
|
|||
return err
|
||||
}
|
||||
if err := system.Setctty(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "setctty")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,45 +90,45 @@ func (l *linuxStandardInit) Init() error {
|
|||
|
||||
if hostname := l.config.Config.Hostname; hostname != "" {
|
||||
if err := unix.Sethostname([]byte(hostname)); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "sethostname")
|
||||
}
|
||||
}
|
||||
if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "apply apparmor profile")
|
||||
}
|
||||
if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "set process label")
|
||||
}
|
||||
|
||||
for key, value := range l.config.Config.Sysctl {
|
||||
if err := writeSystemProperty(key, value); err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "write sysctl key %s", key)
|
||||
}
|
||||
}
|
||||
for _, path := range l.config.Config.ReadonlyPaths {
|
||||
if err := readonlyPath(path); err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "readonly path %s", path)
|
||||
}
|
||||
}
|
||||
for _, path := range l.config.Config.MaskPaths {
|
||||
if err := maskPath(path, l.config.Config.MountLabel); err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "mask path %s", path)
|
||||
}
|
||||
}
|
||||
pdeath, err := system.GetParentDeathSignal()
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "get pdeath signal")
|
||||
}
|
||||
if l.config.NoNewPrivileges {
|
||||
if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "set nonewprivileges")
|
||||
}
|
||||
}
|
||||
// Tell our parent that we're ready to Execv. This must be done before the
|
||||
// Seccomp rules have been applied, because we need to be able to read and
|
||||
// write to a socket.
|
||||
if err := syncParentReady(l.pipe); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "sync ready")
|
||||
}
|
||||
// Without NoNewPrivileges seccomp is a privileged operation, so we need to
|
||||
// do this before dropping capabilities; otherwise do it as late as possible
|
||||
|
@ -143,7 +144,7 @@ func (l *linuxStandardInit) Init() error {
|
|||
// finalizeNamespace can change user/group which clears the parent death
|
||||
// signal, so we restore it here.
|
||||
if err := pdeath.Restore(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "restore pdeath signal")
|
||||
}
|
||||
// Compare the parent from the initial start of the init process and make
|
||||
// sure that it did not change. if the parent changes that means it died
|
||||
|
|
38
vendor/github.com/opencontainers/runc/libcontainer/system/linux.go
generated
vendored
38
vendor/github.com/opencontainers/runc/libcontainer/system/linux.go
generated
vendored
|
@ -3,13 +3,12 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall" // only for exec
|
||||
"unsafe"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
@ -102,34 +101,43 @@ func Setctty() error {
|
|||
}
|
||||
|
||||
// RunningInUserNS detects whether we are currently running in a user namespace.
|
||||
// Copied from github.com/lxc/lxd/shared/util.go
|
||||
// Originally copied from github.com/lxc/lxd/shared/util.go
|
||||
func RunningInUserNS() bool {
|
||||
file, err := os.Open("/proc/self/uid_map")
|
||||
uidmap, err := user.CurrentProcessUIDMap()
|
||||
if err != nil {
|
||||
// This kernel-provided file only exists if user namespaces are supported
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
return UIDMapInUserNS(uidmap)
|
||||
}
|
||||
|
||||
buf := bufio.NewReader(file)
|
||||
l, _, err := buf.ReadLine()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
line := string(l)
|
||||
var a, b, c int64
|
||||
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
|
||||
func UIDMapInUserNS(uidmap []user.IDMap) bool {
|
||||
/*
|
||||
* We assume we are in the initial user namespace if we have a full
|
||||
* range - 4294967295 uids starting at uid 0.
|
||||
*/
|
||||
if a == 0 && b == 0 && c == 4294967295 {
|
||||
if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetParentNSeuid returns the euid within the parent user namespace
|
||||
func GetParentNSeuid() int64 {
|
||||
euid := int64(os.Geteuid())
|
||||
uidmap, err := user.CurrentProcessUIDMap()
|
||||
if err != nil {
|
||||
// This kernel-provided file only exists if user namespaces are supported
|
||||
return euid
|
||||
}
|
||||
for _, um := range uidmap {
|
||||
if um.ID <= euid && euid <= um.ID+um.Count-1 {
|
||||
return um.ParentID + euid - um.ID
|
||||
}
|
||||
}
|
||||
return euid
|
||||
}
|
||||
|
||||
// SetSubreaper sets the value i as the subreaper setting for the calling process
|
||||
func SetSubreaper(i int) error {
|
||||
return unix.Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0)
|
||||
|
|
45
vendor/github.com/opencontainers/runc/libcontainer/system/linux_test.go
generated
vendored
Normal file
45
vendor/github.com/opencontainers/runc/libcontainer/system/linux_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// +build linux
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
)
|
||||
|
||||
func TestUIDMapInUserNS(t *testing.T) {
|
||||
cases := []struct {
|
||||
s string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
s: " 0 0 4294967295\n",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
s: " 0 0 1\n",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
s: " 0 1001 1\n 1 231072 65536\n",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// file exist but empty (the initial state when userns is created. see man 7 user_namespaces)
|
||||
s: "",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
uidmap, err := user.ParseIDMap(strings.NewReader(c.s))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual := UIDMapInUserNS(uidmap)
|
||||
if c.expected != actual {
|
||||
t.Fatalf("expected %v, got %v for %q", c.expected, actual, c.s)
|
||||
}
|
||||
}
|
||||
}
|
18
vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go
generated
vendored
18
vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go
generated
vendored
|
@ -2,8 +2,26 @@
|
|||
|
||||
package system
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
)
|
||||
|
||||
// RunningInUserNS is a stub for non-Linux systems
|
||||
// Always returns false
|
||||
func RunningInUserNS() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// UIDMapInUserNS is a stub for non-Linux systems
|
||||
// Always returns false
|
||||
func UIDMapInUserNS(uidmap []user.IDMap) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetParentNSeuid returns the euid within the parent user namespace
|
||||
// Always returns os.Geteuid on non-linux
|
||||
func GetParentNSeuid() int {
|
||||
return os.Geteuid()
|
||||
}
|
||||
|
|
26
vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
generated
vendored
26
vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
generated
vendored
|
@ -114,3 +114,29 @@ func CurrentUser() (User, error) {
|
|||
func CurrentGroup() (Group, error) {
|
||||
return LookupGid(unix.Getgid())
|
||||
}
|
||||
|
||||
func CurrentUserSubUIDs() ([]SubID, error) {
|
||||
u, err := CurrentUser()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseSubIDFileFilter("/etc/subuid",
|
||||
func(entry SubID) bool { return entry.Name == u.Name })
|
||||
}
|
||||
|
||||
func CurrentGroupSubGIDs() ([]SubID, error) {
|
||||
g, err := CurrentGroup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseSubIDFileFilter("/etc/subgid",
|
||||
func(entry SubID) bool { return entry.Name == g.Name })
|
||||
}
|
||||
|
||||
func CurrentProcessUIDMap() ([]IDMap, error) {
|
||||
return ParseIDMapFile("/proc/self/uid_map")
|
||||
}
|
||||
|
||||
func CurrentProcessGIDMap() ([]IDMap, error) {
|
||||
return ParseIDMapFile("/proc/self/gid_map")
|
||||
}
|
||||
|
|
133
vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
133
vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
|
@ -75,12 +75,29 @@ func groupFromOS(g *user.Group) (Group, error) {
|
|||
return newGroup, nil
|
||||
}
|
||||
|
||||
// SubID represents an entry in /etc/sub{u,g}id
|
||||
type SubID struct {
|
||||
Name string
|
||||
SubID int64
|
||||
Count int64
|
||||
}
|
||||
|
||||
// IDMap represents an entry in /proc/PID/{u,g}id_map
|
||||
type IDMap struct {
|
||||
ID int64
|
||||
ParentID int64
|
||||
Count int64
|
||||
}
|
||||
|
||||
func parseLine(line string, v ...interface{}) {
|
||||
if line == "" {
|
||||
parseParts(strings.Split(line, ":"), v...)
|
||||
}
|
||||
|
||||
func parseParts(parts []string, v ...interface{}) {
|
||||
if len(parts) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.Split(line, ":")
|
||||
for i, p := range parts {
|
||||
// Ignore cases where we don't have enough fields to populate the arguments.
|
||||
// Some configuration files like to misbehave.
|
||||
|
@ -96,6 +113,8 @@ func parseLine(line string, v ...interface{}) {
|
|||
case *int:
|
||||
// "numbers", with conversion errors ignored because of some misbehaving configuration files.
|
||||
*e, _ = strconv.Atoi(p)
|
||||
case *int64:
|
||||
*e, _ = strconv.ParseInt(p, 10, 64)
|
||||
case *[]string:
|
||||
// Comma-separated lists.
|
||||
if p != "" {
|
||||
|
@ -105,7 +124,7 @@ func parseLine(line string, v ...interface{}) {
|
|||
}
|
||||
default:
|
||||
// Someone goof'd when writing code using this function. Scream so they can hear us.
|
||||
panic(fmt.Sprintf("parseLine only accepts {*string, *int, *[]string} as arguments! %#v is not a pointer!", e))
|
||||
panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -479,3 +498,111 @@ func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int
|
|||
}
|
||||
return GetAdditionalGroups(additionalGroups, group)
|
||||
}
|
||||
|
||||
func ParseSubIDFile(path string) ([]SubID, error) {
|
||||
subid, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer subid.Close()
|
||||
return ParseSubID(subid)
|
||||
}
|
||||
|
||||
func ParseSubID(subid io.Reader) ([]SubID, error) {
|
||||
return ParseSubIDFilter(subid, nil)
|
||||
}
|
||||
|
||||
func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) {
|
||||
subid, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer subid.Close()
|
||||
return ParseSubIDFilter(subid, filter)
|
||||
}
|
||||
|
||||
func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("nil source for subid-formatted data")
|
||||
}
|
||||
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []SubID{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 5 subuid
|
||||
p := SubID{}
|
||||
parseLine(line, &p.Name, &p.SubID, &p.Count)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func ParseIDMapFile(path string) ([]IDMap, error) {
|
||||
r, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
return ParseIDMap(r)
|
||||
}
|
||||
|
||||
func ParseIDMap(r io.Reader) ([]IDMap, error) {
|
||||
return ParseIDMapFilter(r, nil)
|
||||
}
|
||||
|
||||
func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) {
|
||||
r, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
return ParseIDMapFilter(r, filter)
|
||||
}
|
||||
|
||||
func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("nil source for idmap-formatted data")
|
||||
}
|
||||
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []IDMap{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 7 user_namespaces
|
||||
p := IDMap{}
|
||||
parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
|
15
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
15
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
|
@ -1,8 +1,6 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -17,19 +15,6 @@ const (
|
|||
exitSignalOffset = 128
|
||||
)
|
||||
|
||||
// GenerateRandomName returns a new name joined with a prefix. This size
|
||||
// specified is used to truncate the randomly generated value
|
||||
func GenerateRandomName(prefix string, size int) (string, error) {
|
||||
id := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if size > 64 {
|
||||
size = 64
|
||||
}
|
||||
return prefix + hex.EncodeToString(id)[:size], nil
|
||||
}
|
||||
|
||||
// ResolveRootfs ensures that the current working directory is
|
||||
// not a symlink and returns the absolute path to the rootfs
|
||||
func ResolveRootfs(uncleanRootfs string) (string, error) {
|
||||
|
|
32
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_test.go
generated
vendored
32
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_test.go
generated
vendored
|
@ -10,28 +10,6 @@ import (
|
|||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestGenerateName(t *testing.T) {
|
||||
name, err := GenerateRandomName("veth", 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := 5 + len("veth")
|
||||
if len(name) != expected {
|
||||
t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
|
||||
}
|
||||
|
||||
name, err = GenerateRandomName("veth", 65)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected = 64 + len("veth")
|
||||
if len(name) != expected {
|
||||
t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
|
||||
}
|
||||
}
|
||||
|
||||
var labelTest = []struct {
|
||||
labels []string
|
||||
query string
|
||||
|
@ -151,4 +129,14 @@ func TestCleanPath(t *testing.T) {
|
|||
if path != "/var" {
|
||||
t.Errorf("expected to receive '/var' and received %s", path)
|
||||
}
|
||||
|
||||
path = CleanPath("/foo/bar/")
|
||||
if path != "/foo/bar" {
|
||||
t.Errorf("expected to receive '/foo/bar' and received %s", path)
|
||||
}
|
||||
|
||||
path = CleanPath("/foo/bar/../")
|
||||
if path != "/foo" {
|
||||
t.Errorf("expected to receive '/foo' and received %s", path)
|
||||
}
|
||||
}
|
||||
|
|
11
vendor/github.com/opencontainers/runc/main.go
generated
vendored
11
vendor/github.com/opencontainers/runc/main.go
generated
vendored
|
@ -63,7 +63,11 @@ func main() {
|
|||
app.Version = strings.Join(v, "\n")
|
||||
|
||||
root := "/run/runc"
|
||||
if os.Geteuid() != 0 {
|
||||
rootless, err := isRootless(nil)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if rootless {
|
||||
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||
if runtimeDir != "" {
|
||||
root = runtimeDir + "/runc"
|
||||
|
@ -108,6 +112,11 @@ func main() {
|
|||
Name: "systemd-cgroup",
|
||||
Usage: "enable systemd cgroup support, expects cgroupsPath to be of form \"slice:prefix:name\" for e.g. \"system.slice:runc:434234\"",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "rootless",
|
||||
Value: "auto",
|
||||
Usage: "enable rootless mode ('true', 'false', or 'auto')",
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
checkpointCommand,
|
||||
|
|
11
vendor/github.com/opencontainers/runc/man/README.md
generated
vendored
Normal file
11
vendor/github.com/opencontainers/runc/man/README.md
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
runc man pages
|
||||
====================
|
||||
|
||||
This directory contains man pages for runc in markdown format.
|
||||
|
||||
To generate man pages from it, use this command
|
||||
|
||||
./md2man-all.sh
|
||||
|
||||
You will see man pages generated under the man8 directory.
|
||||
|
27
vendor/github.com/opencontainers/runc/man/md2man-all.sh
generated
vendored
Executable file
27
vendor/github.com/opencontainers/runc/man/md2man-all.sh
generated
vendored
Executable file
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# get into this script's directory
|
||||
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||
|
||||
[ "$1" = '-q' ] || {
|
||||
set -x
|
||||
pwd
|
||||
}
|
||||
|
||||
if ! ( which go-md2man &>/dev/null ); then
|
||||
echo "To install man pages, please install 'go-md2man'."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for FILE in *.md; do
|
||||
base="$(basename "$FILE")"
|
||||
name="${base%.md}"
|
||||
num="${name##*.}"
|
||||
if [ -z "$num" -o "$name" = "$num" ]; then
|
||||
# skip files that aren't of the format xxxx.N.md (like README.md)
|
||||
continue
|
||||
fi
|
||||
mkdir -p "./man${num}"
|
||||
go-md2man -in "$FILE" -out "./man${num}/${name}"
|
||||
done
|
25
vendor/github.com/opencontainers/runc/man/runc-checkpoint.8.md
generated
vendored
Normal file
25
vendor/github.com/opencontainers/runc/man/runc-checkpoint.8.md
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
# NAME
|
||||
runc checkpoint - checkpoint a running container
|
||||
|
||||
# SYNOPSIS
|
||||
runc checkpoint [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
checkpointed.
|
||||
|
||||
# DESCRIPTION
|
||||
The checkpoint command saves the state of the container instance.
|
||||
|
||||
# OPTIONS
|
||||
--image-path value path for saving criu image files
|
||||
--work-path value path for saving work files and logs
|
||||
--parent-path value path for previous criu image files in pre-dump
|
||||
--leave-running leave the process running after checkpointing
|
||||
--tcp-established allow open tcp connections
|
||||
--ext-unix-sk allow external unix sockets
|
||||
--shell-job allow shell jobs
|
||||
--page-server value ADDRESS:PORT of the page server
|
||||
--file-locks handle file locks, for safety
|
||||
--pre-dump dump container's memory information only, leave the container running after this
|
||||
--manage-cgroups-mode value cgroups mode: 'soft' (default), 'full' and 'strict'
|
||||
--empty-ns value create a namespace, but don't restore its properties
|
27
vendor/github.com/opencontainers/runc/man/runc-create.8.md
generated
vendored
Normal file
27
vendor/github.com/opencontainers/runc/man/runc-create.8.md
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# NAME
|
||||
runc create - create a container
|
||||
|
||||
# SYNOPSIS
|
||||
runc create [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is your name for the instance of the container that you
|
||||
are starting. The name you provide for the container instance must be unique on
|
||||
your host.
|
||||
|
||||
# DESCRIPTION
|
||||
The create command creates an instance of a container for a bundle. The bundle
|
||||
is a directory with a specification file named "config.json" and a root
|
||||
filesystem.
|
||||
|
||||
The specification file includes an args parameter. The args parameter is used
|
||||
to specify command(s) that get run when the container is started. To change the
|
||||
command(s) that get executed on start, edit the args parameter of the spec. See
|
||||
"runc spec --help" for more explanation.
|
||||
|
||||
# OPTIONS
|
||||
--bundle value, -b value path to the root of the bundle directory, defaults to the current directory
|
||||
--console-socket value path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal
|
||||
--pid-file value specify the file to write the process id to
|
||||
--no-pivot do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk
|
||||
--no-new-keyring do not create a new session keyring for the container. This will cause the container to inherit the calling processes session key
|
||||
--preserve-fds value Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) (default: 0)
|
17
vendor/github.com/opencontainers/runc/man/runc-delete.8.md
generated
vendored
Normal file
17
vendor/github.com/opencontainers/runc/man/runc-delete.8.md
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# NAME
|
||||
runc delete - delete any resources held by the container often used with detached container
|
||||
|
||||
# SYNOPSIS
|
||||
runc delete [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container.
|
||||
|
||||
# OPTIONS
|
||||
--force, -f Forcibly deletes the container if it is still running (uses SIGKILL)
|
||||
|
||||
# EXAMPLE
|
||||
For example, if the container id is "ubuntu01" and runc list currently shows the
|
||||
status of "ubuntu01" as "stopped" the following will delete resources held for
|
||||
"ubuntu01" removing "ubuntu01" from the runc list of containers:
|
||||
|
||||
# runc delete ubuntu01
|
15
vendor/github.com/opencontainers/runc/man/runc-events.8.md
generated
vendored
Normal file
15
vendor/github.com/opencontainers/runc/man/runc-events.8.md
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
# NAME
|
||||
runc events - display container events such as OOM notifications, cpu, memory, and IO usage statistics
|
||||
|
||||
# SYNOPSIS
|
||||
runc events [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container.
|
||||
|
||||
# DESCRIPTION
|
||||
The events command displays information about the container. By default the
|
||||
information is displayed once every 5 seconds.
|
||||
|
||||
# OPTIONS
|
||||
--interval value set the stats collection interval (default: 5s)
|
||||
--stats display the container's stats then exit
|
30
vendor/github.com/opencontainers/runc/man/runc-exec.8.md
generated
vendored
Normal file
30
vendor/github.com/opencontainers/runc/man/runc-exec.8.md
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
# NAME
|
||||
runc exec - execute new process inside the container
|
||||
|
||||
# SYNOPSIS
|
||||
runc exec [command options] <container-id> -- <container command> [args...]
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container and
|
||||
"<container command>" is the command to be executed in the container.
|
||||
|
||||
# EXAMPLE
|
||||
For example, if the container is configured to run the linux ps command the
|
||||
following will output a list of processes running in the container:
|
||||
|
||||
# runc exec <container-id> ps
|
||||
|
||||
# OPTIONS
|
||||
--console value specify the pty slave path for use with the container
|
||||
--cwd value current working directory in the container
|
||||
--env value, -e value set environment variables
|
||||
--tty, -t allocate a pseudo-TTY
|
||||
--user value, -u value UID (format: <uid>[:<gid>])
|
||||
--additional-gids value, -g value additional gids
|
||||
--process value, -p value path to the process.json
|
||||
--detach, -d detach from the container's process
|
||||
--pid-file value specify the file to write the process id to
|
||||
--process-label value set the asm process label for the process commonly used with selinux
|
||||
--apparmor value set the apparmor profile for the process
|
||||
--no-new-privs set the no new privileges value for the process
|
||||
--cap value, -c value add a capability to the bounding set for the process
|
||||
--no-subreaper disable the use of the subreaper used to reap reparented processes
|
18
vendor/github.com/opencontainers/runc/man/runc-kill.8.md
generated
vendored
Normal file
18
vendor/github.com/opencontainers/runc/man/runc-kill.8.md
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# NAME
|
||||
runc kill - kill sends the specified signal (default: SIGTERM) to the container's init process
|
||||
|
||||
# SYNOPSIS
|
||||
runc kill [command options] <container-id> <signal>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container and
|
||||
"<signal>" is the signal to be sent to the init process.
|
||||
|
||||
# OPTIONS
|
||||
--all, -a send the specified signal to all processes inside the container
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
For example, if the container id is "ubuntu01" the following will send a "KILL"
|
||||
signal to the init process of the "ubuntu01" container:
|
||||
|
||||
# runc kill ubuntu01 KILL
|
19
vendor/github.com/opencontainers/runc/man/runc-list.8.md
generated
vendored
Normal file
19
vendor/github.com/opencontainers/runc/man/runc-list.8.md
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
# NAME
|
||||
runc list - lists containers started by runc with the given root
|
||||
|
||||
# SYNOPSIS
|
||||
runc list [command options]
|
||||
|
||||
# EXAMPLE
|
||||
Where the given root is specified via the global option "--root"
|
||||
(default: "/run/runc").
|
||||
|
||||
To list containers created via the default "--root":
|
||||
# runc list
|
||||
|
||||
To list containers created using a non-default value for "--root":
|
||||
# runc --root value list
|
||||
|
||||
# OPTIONS
|
||||
--format value, -f value select one of: table or json (default: "table")
|
||||
--quiet, -q display only container IDs
|
12
vendor/github.com/opencontainers/runc/man/runc-pause.8.md
generated
vendored
Normal file
12
vendor/github.com/opencontainers/runc/man/runc-pause.8.md
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# NAME
|
||||
runc pause - pause suspends all processes inside the container
|
||||
|
||||
# SYNOPSIS
|
||||
runc pause <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
paused.
|
||||
|
||||
# DESCRIPTION
|
||||
The pause command suspends all processes in the instance of the container.
|
||||
Use runc list to identiy instances of containers and their current status.
|
13
vendor/github.com/opencontainers/runc/man/runc-ps.8.md
generated
vendored
Normal file
13
vendor/github.com/opencontainers/runc/man/runc-ps.8.md
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# NAME
|
||||
runc ps - ps displays the processes running inside a container
|
||||
|
||||
# SYNOPSIS
|
||||
runc ps [command options] <container-id> [ps options]
|
||||
|
||||
# OPTIONS
|
||||
--format value, -f value select one of: table(default) or json
|
||||
|
||||
The default format is table. The following will output the processes of a container
|
||||
in json format:
|
||||
|
||||
# runc ps -f json <container-id>
|
26
vendor/github.com/opencontainers/runc/man/runc-restore.8.md
generated
vendored
Normal file
26
vendor/github.com/opencontainers/runc/man/runc-restore.8.md
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
# NAME
|
||||
runc restore - restore a container from a previous checkpoint
|
||||
|
||||
# SYNOPSIS
|
||||
runc restore [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
restored.
|
||||
|
||||
# DESCRIPTION
|
||||
Restores the saved state of the container instance that was previously saved
|
||||
using the runc checkpoint command.
|
||||
|
||||
# OPTIONS
|
||||
--image-path value path to criu image files for restoring
|
||||
--work-path value path for saving work files and logs
|
||||
--tcp-established allow open tcp connections
|
||||
--ext-unix-sk allow external unix sockets
|
||||
--shell-job allow shell jobs
|
||||
--file-locks handle file locks, for safety
|
||||
--manage-cgroups-mode value cgroups mode: 'soft' (default), 'full' and 'strict'
|
||||
--bundle value, -b value path to the root of the bundle directory
|
||||
--detach, -d detach from the container's process
|
||||
--pid-file value specify the file to write the process id to
|
||||
--no-subreaper disable the use of the subreaper used to reap reparented processes
|
||||
--no-pivot do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk
|
12
vendor/github.com/opencontainers/runc/man/runc-resume.8.md
generated
vendored
Normal file
12
vendor/github.com/opencontainers/runc/man/runc-resume.8.md
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# NAME
|
||||
runc resume - resumes all processes that have been previously paused
|
||||
|
||||
# SYNOPSIS
|
||||
runc resume <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
resumed.
|
||||
|
||||
# DESCRIPTION
|
||||
The resume command resumes all processes in the instance of the container.
|
||||
Use runc list to identiy instances of containers and their current status.
|
29
vendor/github.com/opencontainers/runc/man/runc-run.8.md
generated
vendored
Normal file
29
vendor/github.com/opencontainers/runc/man/runc-run.8.md
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
# NAME
|
||||
runc run - create and run a container
|
||||
|
||||
# SYNOPSIS
|
||||
runc run [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is your name for the instance of the container that you
|
||||
are starting. The name you provide for the container instance must be unique on
|
||||
your host.
|
||||
|
||||
# DESCRIPTION
|
||||
The run command creates an instance of a container for a bundle. The bundle
|
||||
is a directory with a specification file named "config.json" and a root
|
||||
filesystem.
|
||||
|
||||
The specification file includes an args parameter. The args parameter is used
|
||||
to specify command(s) that get run when the container is started. To change the
|
||||
command(s) that get executed on start, edit the args parameter of the spec. See
|
||||
"runc spec --help" for more explanation.
|
||||
|
||||
# OPTIONS
|
||||
--bundle value, -b value path to the root of the bundle directory, defaults to the current directory
|
||||
--console-socket value path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal
|
||||
--detach, -d detach from the container's process
|
||||
--pid-file value specify the file to write the process id to
|
||||
--no-subreaper disable the use of the subreaper used to reap reparented processes
|
||||
--no-pivot do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk
|
||||
--no-new-keyring do not create a new session keyring for the container. This will cause the container to inherit the calling processes session key
|
||||
--preserve-fds value Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) (default: 0)
|
52
vendor/github.com/opencontainers/runc/man/runc-spec.8.md
generated
vendored
Normal file
52
vendor/github.com/opencontainers/runc/man/runc-spec.8.md
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
# NAME
|
||||
runc spec - create a new specification file
|
||||
|
||||
# SYNOPSIS
|
||||
runc spec [command options] [arguments...]
|
||||
|
||||
# DESCRIPTION
|
||||
The spec command creates the new specification file named "config.json" for
|
||||
the bundle.
|
||||
|
||||
The spec generated is just a starter file. Editing of the spec is required to
|
||||
achieve desired results. For example, the newly generated spec includes an args
|
||||
parameter that is initially set to call the "sh" command when the container is
|
||||
started. Calling "sh" may work for an ubuntu container or busybox, but will not
|
||||
work for containers that do not include the "sh" program.
|
||||
|
||||
# EXAMPLE
|
||||
To run docker's hello-world container one needs to set the args parameter
|
||||
in the spec to call hello. This can be done using the sed command or a text
|
||||
editor. The following commands create a bundle for hello-world, change the
|
||||
default args parameter in the spec from "sh" to "/hello", then run the hello
|
||||
command in a new hello-world container named container1:
|
||||
|
||||
mkdir hello
|
||||
cd hello
|
||||
docker pull hello-world
|
||||
docker export $(docker create hello-world) > hello-world.tar
|
||||
mkdir rootfs
|
||||
tar -C rootfs -xf hello-world.tar
|
||||
runc spec
|
||||
sed -i 's;"sh";"/hello";' config.json
|
||||
runc start container1
|
||||
|
||||
In the start command above, "container1" is the name for the instance of the
|
||||
container that you are starting. The name you provide for the container instance
|
||||
must be unique on your host.
|
||||
|
||||
An alternative for generating a customized spec config is to use "oci-runtime-tool", the
|
||||
sub-command "oci-runtime-tool generate" has lots of options that can be used to do any
|
||||
customizations as you want, see [runtime-tools](https://github.com/opencontainers/runtime-tools)
|
||||
to get more information.
|
||||
|
||||
When starting a container through runc, runc needs root privilege. If not
|
||||
already running as root, you can use sudo to give runc root privilege. For
|
||||
example: "sudo runc start container1" will give runc root privilege to start the
|
||||
container on your host.
|
||||
|
||||
Alternatively, you can start a rootless container, which has the ability to run without root privileges. For this to work, the specification file needs to be adjusted accordingly. You can pass the parameter --rootless to this command to generate a proper rootless spec file.
|
||||
|
||||
# OPTIONS
|
||||
--bundle value, -b value path to the root of the bundle directory
|
||||
--rootless generate a configuration for a rootless container
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue