Update go dependencies
This commit is contained in:
parent
16fce7444f
commit
fe616fc9d7
130 changed files with 14029 additions and 3767 deletions
313
Gopkg.lock
generated
313
Gopkg.lock
generated
|
@ -2,152 +2,209 @@
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:5c3894b2aa4d6bead0ceeea6831b305d62879c871780e7b76296ded1b004bc57"
|
||||||
name = "cloud.google.com/go"
|
name = "cloud.google.com/go"
|
||||||
packages = ["compute/metadata"]
|
packages = ["compute/metadata"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "aad3f485ee528456e0768f20397b4d9dd941e755"
|
revision = "aad3f485ee528456e0768f20397b4d9dd941e755"
|
||||||
version = "v0.25.0"
|
version = "v0.25.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:08c1b357bb84a9b598b63cc61175ed89e6569516ba4e05e29c9a140045fbc873"
|
||||||
name = "github.com/Azure/go-autorest"
|
name = "github.com/Azure/go-autorest"
|
||||||
packages = [
|
packages = [
|
||||||
"autorest",
|
"autorest",
|
||||||
"autorest/adal",
|
"autorest/adal",
|
||||||
"autorest/azure",
|
"autorest/azure",
|
||||||
"autorest/date"
|
"autorest/date",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "1f7cd6cfe0adea687ad44a512dfe76140f804318"
|
revision = "1f7cd6cfe0adea687ad44a512dfe76140f804318"
|
||||||
version = "v10.12.0"
|
version = "v10.12.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:b2339e83ce9b5c4f79405f949429a7f68a9a904fed903c672aac1e7ceb7f5f02"
|
||||||
|
name = "github.com/Sirupsen/logrus"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
|
||||||
|
version = "v1.0.6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:b6cc321776d10ed1179d183a585e0f2e65bc31de070f8df6c07604c4cc13c80f"
|
||||||
name = "github.com/armon/go-proxyproto"
|
name = "github.com/armon/go-proxyproto"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "5b7edb60ff5f69b60d1950397f7bde6171f1807d"
|
revision = "5b7edb60ff5f69b60d1950397f7bde6171f1807d"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd"
|
||||||
name = "github.com/beorn7/perks"
|
name = "github.com/beorn7/perks"
|
||||||
packages = ["quantile"]
|
packages = ["quantile"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:7a6852b35eb5bbc184561443762d225116ae630c26a7c4d90546619f1e7d2ad2"
|
||||||
name = "github.com/dgrijalva/jwt-go"
|
name = "github.com/dgrijalva/jwt-go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||||
version = "v3.2.0"
|
version = "v3.2.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:4189ee6a3844f555124d9d2656fe7af02fca961c2a9bad9074789df13a0c62e0"
|
||||||
name = "github.com/docker/distribution"
|
name = "github.com/docker/distribution"
|
||||||
packages = [
|
packages = [
|
||||||
"digestset",
|
"digestset",
|
||||||
"reference"
|
"reference",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
|
revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:4340101f42556a9cb2f7a360a0e95a019bfef6247d92e6c4c46f2433cf86a482"
|
||||||
|
name = "github.com/docker/go-units"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
|
||||||
|
version = "v0.3.3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:da25cf063072a10461c19320e82117d85f9d60be4c95a62bc8d5a49acf7d0ca5"
|
||||||
name = "github.com/docker/spdystream"
|
name = "github.com/docker/spdystream"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"spdy"
|
"spdy",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "bc6354cbbc295e925e4c611ffe90c1f287ee54db"
|
revision = "bc6354cbbc295e925e4c611ffe90c1f287ee54db"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:4f0ac2a7cc350b14c72a9de0de046b908482d6f16183c8570ee4d6e17adc6a94"
|
||||||
name = "github.com/eapache/channels"
|
name = "github.com/eapache/channels"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "47238d5aae8c0fefd518ef2bee46290909cf8263"
|
revision = "47238d5aae8c0fefd518ef2bee46290909cf8263"
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:0d36a2b325b9e75f8057f7f9fbe778d348d70ba652cb9335485b69d1a5c4e038"
|
||||||
name = "github.com/eapache/queue"
|
name = "github.com/eapache/queue"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98"
|
revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:1b91ae0dc69a41d4c2ed23ea5cffb721ea63f5037ca4b81e6d6771fbb8f45129"
|
||||||
name = "github.com/fsnotify/fsnotify"
|
name = "github.com/fsnotify/fsnotify"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||||
version = "v1.4.7"
|
version = "v1.4.7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:a727fcc2698d180fc2b48fe97e1a2d02f4f7e958358cb7b81befa80406923348"
|
||||||
name = "github.com/fullsailor/pkcs7"
|
name = "github.com/fullsailor/pkcs7"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "8306686428a5fe132eac8cb7c4848af725098bd4"
|
revision = "8306686428a5fe132eac8cb7c4848af725098bd4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756"
|
||||||
name = "github.com/ghodss/yaml"
|
name = "github.com/ghodss/yaml"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:acba7f3d06725b78c6999c2ba6c40b80539c10346fe37b4681072f1666c87d0c"
|
||||||
name = "github.com/gogo/protobuf"
|
name = "github.com/gogo/protobuf"
|
||||||
packages = [
|
packages = [
|
||||||
"gogoproto",
|
"gogoproto",
|
||||||
"proto",
|
"proto",
|
||||||
"protoc-gen-gogo/descriptor",
|
"protoc-gen-gogo/descriptor",
|
||||||
"sortkeys"
|
"sortkeys",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a"
|
||||||
name = "github.com/golang/glog"
|
name = "github.com/golang/glog"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:3fb07f8e222402962fa190eb060608b34eddfb64562a18e2167df2de0ece85d8"
|
||||||
name = "github.com/golang/groupcache"
|
name = "github.com/golang/groupcache"
|
||||||
packages = ["lru"]
|
packages = ["lru"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "24b0969c4cb722950103eed87108c8d291a8df00"
|
revision = "24b0969c4cb722950103eed87108c8d291a8df00"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:03e14cff610a8a58b774e36bd337fa979482be86aab01be81fb8bbd6d0f07fc8"
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
packages = [
|
packages = [
|
||||||
"proto",
|
"proto",
|
||||||
"ptypes",
|
"ptypes",
|
||||||
"ptypes/any",
|
"ptypes/any",
|
||||||
"ptypes/duration",
|
"ptypes/duration",
|
||||||
"ptypes/timestamp"
|
"ptypes/timestamp",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:245bd4eb633039cd66106a5d340ae826d87f4e36a8602fcc940e14176fd26ea7"
|
||||||
name = "github.com/google/btree"
|
name = "github.com/google/btree"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc"
|
||||||
name = "github.com/google/gofuzz"
|
name = "github.com/google/gofuzz"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:06a7dadb7b760767341ffb6c8d377238d68a1226f2b21b5d497d2e3f6ecf6b4e"
|
||||||
name = "github.com/googleapis/gnostic"
|
name = "github.com/googleapis/gnostic"
|
||||||
packages = [
|
packages = [
|
||||||
"OpenAPIv2",
|
"OpenAPIv2",
|
||||||
"compiler",
|
"compiler",
|
||||||
"extensions"
|
"extensions",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
|
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
|
||||||
version = "v0.2.0"
|
version = "v0.2.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:3c8047ba26b645714ddc51f8def8d127bc2bbc066a6d6b86a8312e2f4c2189cd"
|
||||||
name = "github.com/gophercloud/gophercloud"
|
name = "github.com/gophercloud/gophercloud"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -156,102 +213,130 @@
|
||||||
"openstack/identity/v2/tokens",
|
"openstack/identity/v2/tokens",
|
||||||
"openstack/identity/v3/tokens",
|
"openstack/identity/v3/tokens",
|
||||||
"openstack/utils",
|
"openstack/utils",
|
||||||
"pagination"
|
"pagination",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "45c2d035713fa44f366921035a44610ac5d45beb"
|
revision = "45c2d035713fa44f366921035a44610ac5d45beb"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:7fdf3223c7372d1ced0b98bf53457c5e89d89aecbad9a77ba9fcc6e01f9e5621"
|
||||||
name = "github.com/gregjones/httpcache"
|
name = "github.com/gregjones/httpcache"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"diskcache"
|
"diskcache",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "9cad4c3443a7200dd6400aef47183728de563a38"
|
revision = "9cad4c3443a7200dd6400aef47183728de563a38"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:13e2fa5735a82a5fb044f290cfd0dba633d1c5e516b27da0509e0dbb3515a18e"
|
||||||
name = "github.com/hashicorp/golang-lru"
|
name = "github.com/hashicorp/golang-lru"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"simplelru"
|
"simplelru",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:9b9758cc00f332bd95147c2e343dccd662ab02cb716a50a89e6b6563ea2df314"
|
||||||
name = "github.com/imdario/mergo"
|
name = "github.com/imdario/mergo"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "7fe0c75c13abdee74b09fcacef5ea1c6bba6a874"
|
revision = "7fe0c75c13abdee74b09fcacef5ea1c6bba6a874"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:0243cffa4a3410f161ee613dfdd903a636d07e838a42d341da95d81f42cd1d41"
|
||||||
name = "github.com/json-iterator/go"
|
name = "github.com/json-iterator/go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82"
|
revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:c5a4770f74cb34f5edc854c4b06a030d567a4e3d5bf502973852dbbbcb749eb8"
|
||||||
name = "github.com/kylelemons/godebug"
|
name = "github.com/kylelemons/godebug"
|
||||||
packages = [
|
packages = [
|
||||||
"diff",
|
"diff",
|
||||||
"pretty"
|
"pretty",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "d65d576e9348f5982d7f6d83682b694e731a45c6"
|
revision = "d65d576e9348f5982d7f6d83682b694e731a45c6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6"
|
||||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||||
packages = ["pbutil"]
|
packages = ["pbutil"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||||
version = "v1.0.1"
|
version = "v1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:2d209eb757bb4101584c2618becd4026593509343ce55491233fd090faa1f1da"
|
||||||
name = "github.com/mitchellh/go-ps"
|
name = "github.com/mitchellh/go-ps"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "4fdf99ab29366514c69ccccddab5dc58b8d84062"
|
revision = "4fdf99ab29366514c69ccccddab5dc58b8d84062"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:e34decedbcec12332c5836d16a6838f864e0b43c5b4f9aa9d9a85101015f87c2"
|
||||||
name = "github.com/mitchellh/hashstructure"
|
name = "github.com/mitchellh/hashstructure"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "2bca23e0e452137f789efbc8610126fd8b94f73b"
|
revision = "2bca23e0e452137f789efbc8610126fd8b94f73b"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:ffbb5e45e7e69a915767cc2fcff99525645afdfefffee72c551940e4b055e538"
|
||||||
name = "github.com/mitchellh/mapstructure"
|
name = "github.com/mitchellh/mapstructure"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b"
|
revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
|
||||||
name = "github.com/modern-go/concurrent"
|
name = "github.com/modern-go/concurrent"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:c6aca19413b13dc59c220ad7430329e2ec454cc310bc6d8de2c7e2b93c18a0f6"
|
||||||
name = "github.com/modern-go/reflect2"
|
name = "github.com/modern-go/reflect2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:af70f88d68d35881112c13fa94ae3fcb53cfe14c4b8fb3d87a345bbf442d2747"
|
||||||
name = "github.com/moul/http2curl"
|
name = "github.com/moul/http2curl"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d"
|
revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:7b782f694db508189ee26915bb12dcb74aefd5d96411ad8b04a12d880df1b810"
|
||||||
name = "github.com/ncabatoff/process-exporter"
|
name = "github.com/ncabatoff/process-exporter"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"proc"
|
"proc",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "5917bc766b95a1fa3c2ae85340f4de02a6b7e15e"
|
revision = "5917bc766b95a1fa3c2ae85340f4de02a6b7e15e"
|
||||||
source = "github.com/aledbf/process-exporter"
|
source = "github.com/aledbf/process-exporter"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:ca8e828bab5a396380d8db6f91b01fe848f91639c3784b9eb5a893ba63815fac"
|
||||||
name = "github.com/onsi/ginkgo"
|
name = "github.com/onsi/ginkgo"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -271,12 +356,14 @@
|
||||||
"reporters/stenographer",
|
"reporters/stenographer",
|
||||||
"reporters/stenographer/support/go-colorable",
|
"reporters/stenographer/support/go-colorable",
|
||||||
"reporters/stenographer/support/go-isatty",
|
"reporters/stenographer/support/go-isatty",
|
||||||
"types"
|
"types",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "fa5fabab2a1bfbd924faf4c067d07ae414e2aedf"
|
revision = "fa5fabab2a1bfbd924faf4c067d07ae414e2aedf"
|
||||||
version = "v1.5.0"
|
version = "v1.5.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:450495ee4de4bf794a5c92db6530c9248227031a69beca1382454ca41b0f92ca"
|
||||||
name = "github.com/onsi/gomega"
|
name = "github.com/onsi/gomega"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -290,122 +377,165 @@
|
||||||
"matchers/support/goraph/edge",
|
"matchers/support/goraph/edge",
|
||||||
"matchers/support/goraph/node",
|
"matchers/support/goraph/node",
|
||||||
"matchers/support/goraph/util",
|
"matchers/support/goraph/util",
|
||||||
"types"
|
"types",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "62bff4df71bdbc266561a0caee19f0594b17c240"
|
revision = "62bff4df71bdbc266561a0caee19f0594b17c240"
|
||||||
version = "v1.4.0"
|
version = "v1.4.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:e0cc8395ea893c898ff5eb0850f4d9851c1f57c78c232304a026379a47a552d0"
|
||||||
name = "github.com/opencontainers/go-digest"
|
name = "github.com/opencontainers/go-digest"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
|
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
|
||||||
version = "v1.0.0-rc1"
|
version = "v1.0.0-rc1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:c9e0e109a897ef306f865e55e07ecb1c3024edafd103e1c2b1a06a852dc91cb3"
|
||||||
|
name = "github.com/opencontainers/runc"
|
||||||
|
packages = [
|
||||||
|
"libcontainer/cgroups",
|
||||||
|
"libcontainer/configs",
|
||||||
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "baf6536d6259209c3edfa2b22237af82942d3dfa"
|
||||||
|
version = "v0.1.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:aa236b5778f70fb400809f622813a2feb01142ea32b95f4ab23f98c5ad3cee18"
|
||||||
name = "github.com/parnurzeal/gorequest"
|
name = "github.com/parnurzeal/gorequest"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "a578a48e8d6ca8b01a3b18314c43c6716bb5f5a3"
|
revision = "a578a48e8d6ca8b01a3b18314c43c6716bb5f5a3"
|
||||||
version = "v0.2.15"
|
version = "v0.2.15"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:9bbaf04041be474c84746e75a1e404409eeac06a8a24af382fd93da20150b95d"
|
||||||
name = "github.com/paultag/sniff"
|
name = "github.com/paultag/sniff"
|
||||||
packages = ["parser"]
|
packages = ["parser"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "87325c3dddf408cfb71f5044873d34ac426d5a59"
|
revision = "87325c3dddf408cfb71f5044873d34ac426d5a59"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:cce3a18fb0b96b5015cd8ca03a57d20a662679de03c4dc4b6ff5f17ea2050fa6"
|
||||||
name = "github.com/pborman/uuid"
|
name = "github.com/pborman/uuid"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
|
revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
|
||||||
version = "v1.1"
|
version = "v1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2"
|
||||||
name = "github.com/petar/GoLLRB"
|
name = "github.com/petar/GoLLRB"
|
||||||
packages = ["llrb"]
|
packages = ["llrb"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
|
revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:6c6d91dc326ed6778783cff869c49fb2f61303cdd2ebbcf90abe53505793f3b6"
|
||||||
name = "github.com/peterbourgon/diskv"
|
name = "github.com/peterbourgon/diskv"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
|
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
|
||||||
version = "v2.0.1"
|
version = "v2.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
|
||||||
name = "github.com/pkg/errors"
|
name = "github.com/pkg/errors"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||||
version = "v0.8.0"
|
version = "v0.8.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:a16a1797cf7077c68a7a312ab68f1349b5ba2ca926a383d4b17c272b6ee97e15"
|
||||||
name = "github.com/prometheus/client_golang"
|
name = "github.com/prometheus/client_golang"
|
||||||
packages = [
|
packages = [
|
||||||
"prometheus",
|
"prometheus",
|
||||||
"prometheus/promhttp"
|
"prometheus/promhttp",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "ee1c9d7e23df7f011bdf6f12a5c9e7f0ae10a1fe"
|
revision = "ee1c9d7e23df7f011bdf6f12a5c9e7f0ae10a1fe"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
|
||||||
name = "github.com/prometheus/client_model"
|
name = "github.com/prometheus/client_model"
|
||||||
packages = ["go"]
|
packages = ["go"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
|
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:768b555b86742de2f28beb37f1dedce9a75f91f871d75b5717c96399c1a78c08"
|
||||||
name = "github.com/prometheus/common"
|
name = "github.com/prometheus/common"
|
||||||
packages = [
|
packages = [
|
||||||
"expfmt",
|
"expfmt",
|
||||||
"internal/bitbucket.org/ww/goautoneg",
|
"internal/bitbucket.org/ww/goautoneg",
|
||||||
"model"
|
"model",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
|
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:6621142cd60b7150ab66f38ff36303ca55843dc5a635c1f9a28f95ecddab72b4"
|
||||||
name = "github.com/prometheus/procfs"
|
name = "github.com/prometheus/procfs"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"internal/util",
|
"internal/util",
|
||||||
"nfs",
|
"nfs",
|
||||||
"xfs"
|
"xfs",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a"
|
revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:330e9062b308ac597e28485699c02223bd052437a6eed32a173c9227dcb9d95a"
|
||||||
name = "github.com/spf13/afero"
|
name = "github.com/spf13/afero"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"mem"
|
"mem",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
|
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
|
||||||
version = "v1.1.1"
|
version = "v1.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:15e5c398fbd9d2c439b635a08ac161b13d04f0c2aa587fe256b65dc0c3efe8b7"
|
||||||
name = "github.com/spf13/pflag"
|
name = "github.com/spf13/pflag"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||||
version = "v1.0.1"
|
version = "v1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:c10994a08ed2ff2cc7611d03ded8bb5f782096880b2daab391adbd9ab95a1764"
|
||||||
name = "github.com/zakjan/cert-chain-resolver"
|
name = "github.com/zakjan/cert-chain-resolver"
|
||||||
packages = ["certUtil"]
|
packages = ["certUtil"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "6076e1ded27284d6df12668dba6bee76fc17f84e"
|
revision = "6076e1ded27284d6df12668dba6bee76fc17f84e"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:583f2f436bab7b59a7bd3e759f1375b06f460760ed1f9235604d143eaab83009"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = [
|
packages = [
|
||||||
"ed25519",
|
"ed25519",
|
||||||
"ed25519/internal/edwards25519",
|
"ed25519/internal/edwards25519",
|
||||||
"ssh/terminal"
|
"ssh/terminal",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602"
|
revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:896f9f0251bc2cbe9f09c9a8f785ca6073f5c7d57c14f003ebe75e87bd66dc81"
|
||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
packages = [
|
packages = [
|
||||||
"context",
|
"context",
|
||||||
|
@ -419,32 +549,38 @@
|
||||||
"idna",
|
"idna",
|
||||||
"internal/timeseries",
|
"internal/timeseries",
|
||||||
"publicsuffix",
|
"publicsuffix",
|
||||||
"trace"
|
"trace",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "cffdcf672aee934982473246bc7e9a8ba446aa9b"
|
revision = "cffdcf672aee934982473246bc7e9a8ba446aa9b"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:20be1c62ac412f0f743bb5afdf2b3e343cb2f5b6dbcfb5ea09dac0d3d476dc2f"
|
||||||
name = "golang.org/x/oauth2"
|
name = "golang.org/x/oauth2"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"google",
|
"google",
|
||||||
"internal",
|
"internal",
|
||||||
"jws",
|
"jws",
|
||||||
"jwt"
|
"jwt",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "ef147856a6ddbb60760db74283d2424e98c87bff"
|
revision = "ef147856a6ddbb60760db74283d2424e98c87bff"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:5420733d35e5e562fffc7c5b99660f3587af042b5420f19b17fe0426e6a5259d"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = [
|
packages = [
|
||||||
"unix",
|
"unix",
|
||||||
"windows"
|
"windows",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "1b2967e3c290b7c545b3db0deeda16e9be4f98a2"
|
revision = "1b2967e3c290b7c545b3db0deeda16e9be4f98a2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:8a12cbc891b7130d3f660f8a309e5c0b083f831e6ac38cdaa1f12e63c12d6bea"
|
||||||
name = "golang.org/x/text"
|
name = "golang.org/x/text"
|
||||||
packages = [
|
packages = [
|
||||||
"collate",
|
"collate",
|
||||||
|
@ -472,18 +608,22 @@
|
||||||
"unicode/bidi",
|
"unicode/bidi",
|
||||||
"unicode/cldr",
|
"unicode/cldr",
|
||||||
"unicode/norm",
|
"unicode/norm",
|
||||||
"unicode/rangetable"
|
"unicode/rangetable",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
version = "v0.3.0"
|
version = "v0.3.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:c9e7a4b4d47c0ed205d257648b0e5b0440880cb728506e318f8ac7cd36270bc4"
|
||||||
name = "golang.org/x/time"
|
name = "golang.org/x/time"
|
||||||
packages = ["rate"]
|
packages = ["rate"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:b5aeceb800276be69165a745328380c8a4b6f2edc7a6bac112e22c8596ed8a49"
|
||||||
name = "google.golang.org/appengine"
|
name = "google.golang.org/appengine"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -495,18 +635,22 @@
|
||||||
"internal/modules",
|
"internal/modules",
|
||||||
"internal/remote_api",
|
"internal/remote_api",
|
||||||
"internal/urlfetch",
|
"internal/urlfetch",
|
||||||
"urlfetch"
|
"urlfetch",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
|
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:601e63e7d4577f907118bec825902505291918859d223bce015539e79f1160e3"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "e92b116572682a5b432ddd840aeaba2a559eeff1"
|
revision = "e92b116572682a5b432ddd840aeaba2a559eeff1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:d0e5846da58e4e20d1bbd7502b24f31d37e3ac1e02a9042d95bd93c2d0c139dd"
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -533,47 +677,59 @@
|
||||||
"stats",
|
"stats",
|
||||||
"status",
|
"status",
|
||||||
"tap",
|
"tap",
|
||||||
"transport"
|
"transport",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
||||||
version = "v1.13.0"
|
version = "v1.13.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:1b91ae0dc69a41d4c2ed23ea5cffb721ea63f5037ca4b81e6d6771fbb8f45129"
|
||||||
name = "gopkg.in/fsnotify/fsnotify.v1"
|
name = "gopkg.in/fsnotify/fsnotify.v1"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||||
version = "v1.4.7"
|
version = "v1.4.7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:9813adbf9dc6eed483747f2e687aba16c907be6bc8d76218409a84ff4ee17807"
|
||||||
name = "gopkg.in/go-playground/pool.v3"
|
name = "gopkg.in/go-playground/pool.v3"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "e73cd3a5ded835540c5cf4778488579c5b357d68"
|
revision = "e73cd3a5ded835540c5cf4778488579c5b357d68"
|
||||||
version = "v3.1.1"
|
version = "v3.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a"
|
||||||
name = "gopkg.in/inf.v0"
|
name = "gopkg.in/inf.v0"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
||||||
version = "v0.9.1"
|
version = "v0.9.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:0e2cd794fe5ba71f7cb5e16a8f327e886f1a8c1663d1c88e2eab5ae913310ed2"
|
||||||
name = "gopkg.in/square/go-jose.v2"
|
name = "gopkg.in/square/go-jose.v2"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"cipher",
|
"cipher",
|
||||||
"json",
|
"json",
|
||||||
"jwt"
|
"jwt",
|
||||||
]
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "76dd09796242edb5b897103a75df2645c028c960"
|
revision = "76dd09796242edb5b897103a75df2645c028c960"
|
||||||
version = "v2.1.6"
|
version = "v2.1.6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082"
|
||||||
name = "gopkg.in/yaml.v2"
|
name = "gopkg.in/yaml.v2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||||
version = "v2.2.1"
|
version = "v2.2.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:ef716a2116d8a040e16fbcd7fca71d3354915a94720de6af22c7a09970234296"
|
||||||
name = "k8s.io/api"
|
name = "k8s.io/api"
|
||||||
packages = [
|
packages = [
|
||||||
"admissionregistration/v1alpha1",
|
"admissionregistration/v1alpha1",
|
||||||
|
@ -604,11 +760,13 @@
|
||||||
"settings/v1alpha1",
|
"settings/v1alpha1",
|
||||||
"storage/v1",
|
"storage/v1",
|
||||||
"storage/v1alpha1",
|
"storage/v1alpha1",
|
||||||
"storage/v1beta1"
|
"storage/v1beta1",
|
||||||
]
|
]
|
||||||
revision = "kubernetes-1.11.0"
|
pruneopts = "NUT"
|
||||||
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:51e1d04096765a19202e8735ba297d10dadd5c7efd26673fb43da72501dc2159"
|
||||||
name = "k8s.io/apiextensions-apiserver"
|
name = "k8s.io/apiextensions-apiserver"
|
||||||
packages = [
|
packages = [
|
||||||
"pkg/apis/apiextensions",
|
"pkg/apis/apiextensions",
|
||||||
|
@ -616,11 +774,13 @@
|
||||||
"pkg/client/clientset/clientset",
|
"pkg/client/clientset/clientset",
|
||||||
"pkg/client/clientset/clientset/scheme",
|
"pkg/client/clientset/clientset/scheme",
|
||||||
"pkg/client/clientset/clientset/typed/apiextensions/v1beta1",
|
"pkg/client/clientset/clientset/typed/apiextensions/v1beta1",
|
||||||
"pkg/features"
|
"pkg/features",
|
||||||
]
|
]
|
||||||
revision = "kubernetes-1.11.0"
|
pruneopts = "NUT"
|
||||||
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:ea8e8a9b975dacea7de93f43a8a7b03c89541cf2dfb1877f7303e95f01e7009f"
|
||||||
name = "k8s.io/apimachinery"
|
name = "k8s.io/apimachinery"
|
||||||
packages = [
|
packages = [
|
||||||
"pkg/api/equality",
|
"pkg/api/equality",
|
||||||
|
@ -671,11 +831,13 @@
|
||||||
"pkg/watch",
|
"pkg/watch",
|
||||||
"third_party/forked/golang/json",
|
"third_party/forked/golang/json",
|
||||||
"third_party/forked/golang/netutil",
|
"third_party/forked/golang/netutil",
|
||||||
"third_party/forked/golang/reflect"
|
"third_party/forked/golang/reflect",
|
||||||
]
|
]
|
||||||
revision = "kubernetes-1.11.0"
|
pruneopts = "NUT"
|
||||||
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:ed6505a2f44edc286822ec76bd98abb73aad8a9834f087d7ee63905b57426b93"
|
||||||
name = "k8s.io/apiserver"
|
name = "k8s.io/apiserver"
|
||||||
packages = [
|
packages = [
|
||||||
"pkg/authentication/authenticator",
|
"pkg/authentication/authenticator",
|
||||||
|
@ -684,11 +846,13 @@
|
||||||
"pkg/features",
|
"pkg/features",
|
||||||
"pkg/server/healthz",
|
"pkg/server/healthz",
|
||||||
"pkg/util/feature",
|
"pkg/util/feature",
|
||||||
"pkg/util/logs"
|
"pkg/util/logs",
|
||||||
]
|
]
|
||||||
revision = "kubernetes-1.11.0"
|
pruneopts = "NUT"
|
||||||
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:650a2a2ee8403c8563a11d150e38fe2eb77dac17811c40317f5c0790fdfec121"
|
||||||
name = "k8s.io/client-go"
|
name = "k8s.io/client-go"
|
||||||
packages = [
|
packages = [
|
||||||
"discovery",
|
"discovery",
|
||||||
|
@ -859,17 +1023,21 @@
|
||||||
"util/integer",
|
"util/integer",
|
||||||
"util/jsonpath",
|
"util/jsonpath",
|
||||||
"util/retry",
|
"util/retry",
|
||||||
"util/workqueue"
|
"util/workqueue",
|
||||||
]
|
]
|
||||||
revision = "kubernetes-1.11.0"
|
pruneopts = "NUT"
|
||||||
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:a2c842a1e0aed96fd732b535514556323a6f5edfded3b63e5e0ab1bce188aa54"
|
||||||
name = "k8s.io/kube-openapi"
|
name = "k8s.io/kube-openapi"
|
||||||
packages = ["pkg/util/proto"]
|
packages = ["pkg/util/proto"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "0cf8f7e6ed1d2e3d47d02e3b6e559369af24d803"
|
revision = "0cf8f7e6ed1d2e3d47d02e3b6e559369af24d803"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
digest = "1:28d69c2b64a30d75769ee3e9b1d815f646e8263f667d6158b56f1bc7902bc911"
|
||||||
name = "k8s.io/kubernetes"
|
name = "k8s.io/kubernetes"
|
||||||
packages = [
|
packages = [
|
||||||
"pkg/api/legacyscheme",
|
"pkg/api/legacyscheme",
|
||||||
|
@ -919,19 +1087,86 @@
|
||||||
"pkg/volume",
|
"pkg/volume",
|
||||||
"pkg/volume/util/fs",
|
"pkg/volume/util/fs",
|
||||||
"pkg/volume/util/recyclerclient",
|
"pkg/volume/util/recyclerclient",
|
||||||
"third_party/forked/golang/expansion"
|
"third_party/forked/golang/expansion",
|
||||||
]
|
]
|
||||||
revision = "v1.11.0"
|
pruneopts = "NUT"
|
||||||
|
revision = "v1.11.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:867d98a27033c52150eb4c01ca0f9be938d3010e9a4909ea3a881a83c3ac1b3f"
|
||||||
name = "k8s.io/utils"
|
name = "k8s.io/utils"
|
||||||
packages = ["exec"]
|
packages = ["exec"]
|
||||||
|
pruneopts = "NUT"
|
||||||
revision = "ab9069044f32ba0c6da081bb46bb0b12e3862c21"
|
revision = "ab9069044f32ba0c6da081bb46bb0b12e3862c21"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "a9315cc322f719f566fce26b3f0de8594ab9d4a074febfc1f0de3c99371c37c4"
|
input-imports = [
|
||||||
|
"github.com/armon/go-proxyproto",
|
||||||
|
"github.com/eapache/channels",
|
||||||
|
"github.com/golang/glog",
|
||||||
|
"github.com/imdario/mergo",
|
||||||
|
"github.com/kylelemons/godebug/pretty",
|
||||||
|
"github.com/mitchellh/go-ps",
|
||||||
|
"github.com/mitchellh/hashstructure",
|
||||||
|
"github.com/mitchellh/mapstructure",
|
||||||
|
"github.com/ncabatoff/process-exporter",
|
||||||
|
"github.com/ncabatoff/process-exporter/proc",
|
||||||
|
"github.com/onsi/ginkgo",
|
||||||
|
"github.com/onsi/ginkgo/config",
|
||||||
|
"github.com/onsi/gomega",
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups",
|
||||||
|
"github.com/parnurzeal/gorequest",
|
||||||
|
"github.com/paultag/sniff/parser",
|
||||||
|
"github.com/pkg/errors",
|
||||||
|
"github.com/prometheus/client_golang/prometheus",
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||||
|
"github.com/prometheus/client_model/go",
|
||||||
|
"github.com/prometheus/common/expfmt",
|
||||||
|
"github.com/spf13/pflag",
|
||||||
|
"github.com/zakjan/cert-chain-resolver/certUtil",
|
||||||
|
"gopkg.in/fsnotify/fsnotify.v1",
|
||||||
|
"gopkg.in/go-playground/pool.v3",
|
||||||
|
"k8s.io/api/apps/v1beta1",
|
||||||
|
"k8s.io/api/core/v1",
|
||||||
|
"k8s.io/api/extensions/v1beta1",
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset",
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors",
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
|
"k8s.io/apimachinery/pkg/fields",
|
||||||
|
"k8s.io/apimachinery/pkg/labels",
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema",
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr",
|
||||||
|
"k8s.io/apimachinery/pkg/util/runtime",
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets",
|
||||||
|
"k8s.io/apimachinery/pkg/util/uuid",
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait",
|
||||||
|
"k8s.io/apimachinery/pkg/version",
|
||||||
|
"k8s.io/apiserver/pkg/server/healthz",
|
||||||
|
"k8s.io/apiserver/pkg/util/logs",
|
||||||
|
"k8s.io/client-go/informers",
|
||||||
|
"k8s.io/client-go/kubernetes",
|
||||||
|
"k8s.io/client-go/kubernetes/fake",
|
||||||
|
"k8s.io/client-go/kubernetes/scheme",
|
||||||
|
"k8s.io/client-go/kubernetes/typed/core/v1",
|
||||||
|
"k8s.io/client-go/plugin/pkg/client/auth",
|
||||||
|
"k8s.io/client-go/rest",
|
||||||
|
"k8s.io/client-go/tools/cache",
|
||||||
|
"k8s.io/client-go/tools/clientcmd",
|
||||||
|
"k8s.io/client-go/tools/clientcmd/api",
|
||||||
|
"k8s.io/client-go/tools/leaderelection",
|
||||||
|
"k8s.io/client-go/tools/leaderelection/resourcelock",
|
||||||
|
"k8s.io/client-go/tools/record",
|
||||||
|
"k8s.io/client-go/util/cert",
|
||||||
|
"k8s.io/client-go/util/cert/triple",
|
||||||
|
"k8s.io/client-go/util/flowcontrol",
|
||||||
|
"k8s.io/client-go/util/workqueue",
|
||||||
|
"k8s.io/kubernetes/pkg/api/v1/pod",
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/util/sliceutils",
|
||||||
|
"k8s.io/kubernetes/pkg/util/filesystem",
|
||||||
|
"k8s.io/kubernetes/pkg/util/sysctl",
|
||||||
|
]
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
12
Gopkg.toml
12
Gopkg.toml
|
@ -98,24 +98,24 @@
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/kubernetes"
|
name = "k8s.io/kubernetes"
|
||||||
revision = "v1.11.0"
|
revision = "v1.11.2"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/api"
|
name = "k8s.io/api"
|
||||||
revision = "kubernetes-1.11.0"
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/apimachinery"
|
name = "k8s.io/apimachinery"
|
||||||
revision = "kubernetes-1.11.0"
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/client-go"
|
name = "k8s.io/client-go"
|
||||||
revision = "kubernetes-1.11.0"
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/apiextensions-apiserver"
|
name = "k8s.io/apiextensions-apiserver"
|
||||||
revision = "kubernetes-1.11.0"
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/apiserver"
|
name = "k8s.io/apiserver"
|
||||||
revision = "kubernetes-1.11.0"
|
revision = "kubernetes-1.11.2"
|
||||||
|
|
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
Normal file
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
logrus
|
13
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
Normal file
13
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.9.x
|
||||||
|
- 1.10.x
|
||||||
|
env:
|
||||||
|
- GOMAXPROCS=4 GORACE=halt_on_error=1
|
||||||
|
install:
|
||||||
|
- go get github.com/stretchr/testify/assert
|
||||||
|
- go get gopkg.in/gemnasium/logrus-airbrake-hook.v2
|
||||||
|
- go get golang.org/x/sys/unix
|
||||||
|
- go get golang.org/x/sys/windows
|
||||||
|
script:
|
||||||
|
- go test -race -v ./...
|
123
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
123
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
# 1.0.5
|
||||||
|
|
||||||
|
* Fix hooks race (#707)
|
||||||
|
* Fix panic deadlock (#695)
|
||||||
|
|
||||||
|
# 1.0.4
|
||||||
|
|
||||||
|
* Fix race when adding hooks (#612)
|
||||||
|
* Fix terminal check in AppEngine (#635)
|
||||||
|
|
||||||
|
# 1.0.3
|
||||||
|
|
||||||
|
* Replace example files with testable examples
|
||||||
|
|
||||||
|
# 1.0.2
|
||||||
|
|
||||||
|
* bug: quote non-string values in text formatter (#583)
|
||||||
|
* Make (*Logger) SetLevel a public method
|
||||||
|
|
||||||
|
# 1.0.1
|
||||||
|
|
||||||
|
* bug: fix escaping in text formatter (#575)
|
||||||
|
|
||||||
|
# 1.0.0
|
||||||
|
|
||||||
|
* Officially changed name to lower-case
|
||||||
|
* bug: colors on Windows 10 (#541)
|
||||||
|
* bug: fix race in accessing level (#512)
|
||||||
|
|
||||||
|
# 0.11.5
|
||||||
|
|
||||||
|
* feature: add writer and writerlevel to entry (#372)
|
||||||
|
|
||||||
|
# 0.11.4
|
||||||
|
|
||||||
|
* bug: fix undefined variable on solaris (#493)
|
||||||
|
|
||||||
|
# 0.11.3
|
||||||
|
|
||||||
|
* formatter: configure quoting of empty values (#484)
|
||||||
|
* formatter: configure quoting character (default is `"`) (#484)
|
||||||
|
* bug: fix not importing io correctly in non-linux environments (#481)
|
||||||
|
|
||||||
|
# 0.11.2
|
||||||
|
|
||||||
|
* bug: fix windows terminal detection (#476)
|
||||||
|
|
||||||
|
# 0.11.1
|
||||||
|
|
||||||
|
* bug: fix tty detection with custom out (#471)
|
||||||
|
|
||||||
|
# 0.11.0
|
||||||
|
|
||||||
|
* performance: Use bufferpool to allocate (#370)
|
||||||
|
* terminal: terminal detection for app-engine (#343)
|
||||||
|
* feature: exit handler (#375)
|
||||||
|
|
||||||
|
# 0.10.0
|
||||||
|
|
||||||
|
* feature: Add a test hook (#180)
|
||||||
|
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||||
|
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||||
|
* performance: avoid re-allocations on `WithFields` (#335)
|
||||||
|
|
||||||
|
# 0.9.0
|
||||||
|
|
||||||
|
* logrus/text_formatter: don't emit empty msg
|
||||||
|
* logrus/hooks/airbrake: move out of main repository
|
||||||
|
* logrus/hooks/sentry: move out of main repository
|
||||||
|
* logrus/hooks/papertrail: move out of main repository
|
||||||
|
* logrus/hooks/bugsnag: move out of main repository
|
||||||
|
* logrus/core: run tests with `-race`
|
||||||
|
* logrus/core: detect TTY based on `stderr`
|
||||||
|
* logrus/core: support `WithError` on logger
|
||||||
|
* logrus/core: Solaris support
|
||||||
|
|
||||||
|
# 0.8.7
|
||||||
|
|
||||||
|
* logrus/core: fix possible race (#216)
|
||||||
|
* logrus/doc: small typo fixes and doc improvements
|
||||||
|
|
||||||
|
|
||||||
|
# 0.8.6
|
||||||
|
|
||||||
|
* hooks/raven: allow passing an initialized client
|
||||||
|
|
||||||
|
# 0.8.5
|
||||||
|
|
||||||
|
* logrus/core: revert #208
|
||||||
|
|
||||||
|
# 0.8.4
|
||||||
|
|
||||||
|
* formatter/text: fix data race (#218)
|
||||||
|
|
||||||
|
# 0.8.3
|
||||||
|
|
||||||
|
* logrus/core: fix entry log level (#208)
|
||||||
|
* logrus/core: improve performance of text formatter by 40%
|
||||||
|
* logrus/core: expose `LevelHooks` type
|
||||||
|
* logrus/core: add support for DragonflyBSD and NetBSD
|
||||||
|
* formatter/text: print structs more verbosely
|
||||||
|
|
||||||
|
# 0.8.2
|
||||||
|
|
||||||
|
* logrus: fix more Fatal family functions
|
||||||
|
|
||||||
|
# 0.8.1
|
||||||
|
|
||||||
|
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
||||||
|
|
||||||
|
# 0.8.0
|
||||||
|
|
||||||
|
* logrus: defaults to stderr instead of stdout
|
||||||
|
* hooks/sentry: add special field for `*http.Request`
|
||||||
|
* formatter/text: ignore Windows for colors
|
||||||
|
|
||||||
|
# 0.7.3
|
||||||
|
|
||||||
|
* formatter/\*: allow configuration of timestamp layout
|
||||||
|
|
||||||
|
# 0.7.2
|
||||||
|
|
||||||
|
* formatter/text: Add configuration option for time format (#158)
|
21
vendor/github.com/Sirupsen/logrus/LICENSE
generated
vendored
Normal file
21
vendor/github.com/Sirupsen/logrus/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Simon Eskildsen
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
461
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
Normal file
461
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/sirupsen/logrus) [](https://godoc.org/github.com/sirupsen/logrus)
|
||||||
|
|
||||||
|
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||||
|
the standard library logger.
|
||||||
|
|
||||||
|
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
||||||
|
import Logrus as both upper- and lower-case. Due to the Go package environment,
|
||||||
|
this caused issues in the community and we needed a standard. Some environments
|
||||||
|
experienced problems with the upper-case variant, so the lower-case was decided.
|
||||||
|
Everything using `logrus` will need to use the lower-case:
|
||||||
|
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
|
||||||
|
|
||||||
|
To fix Glide, see [these
|
||||||
|
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
||||||
|
For an in-depth explanation of the casing issue, see [this
|
||||||
|
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
||||||
|
|
||||||
|
**Are you interested in assisting in maintaining Logrus?** Currently I have a
|
||||||
|
lot of obligations, and I am unable to provide Logrus with the maintainership it
|
||||||
|
needs. If you'd like to help, please reach out to me at `simon at author's
|
||||||
|
username dot com`.
|
||||||
|
|
||||||
|
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||||
|
plain text):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||||
|
or Splunk:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||||
|
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||||
|
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||||
|
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||||
|
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||||
|
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||||
|
```
|
||||||
|
|
||||||
|
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||||
|
attached, the output is compatible with the
|
||||||
|
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||||
|
|
||||||
|
```text
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
||||||
|
exit status 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Case-sensitivity
|
||||||
|
|
||||||
|
The organization's name was changed to lower-case--and this will not be changed
|
||||||
|
back. If you are getting import conflicts due to case sensitivity, please use
|
||||||
|
the lower-case import: `github.com/sirupsen/logrus`.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||||
|
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
|
||||||
|
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||||
|
want:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Log as JSON instead of the default ASCII formatter.
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
|
||||||
|
// Output to stdout instead of the default stderr
|
||||||
|
// Can be any io.Writer, see below for File example
|
||||||
|
log.SetOutput(os.Stdout)
|
||||||
|
|
||||||
|
// Only log the warning severity or above.
|
||||||
|
log.SetLevel(log.WarnLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 122,
|
||||||
|
}).Warn("The group's number increased tremendously!")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 100,
|
||||||
|
}).Fatal("The ice breaks!")
|
||||||
|
|
||||||
|
// A common pattern is to re-use fields between logging statements by re-using
|
||||||
|
// the logrus.Entry returned from WithFields()
|
||||||
|
contextLogger := log.WithFields(log.Fields{
|
||||||
|
"common": "this is a common field",
|
||||||
|
"other": "I also should be logged always",
|
||||||
|
})
|
||||||
|
|
||||||
|
contextLogger.Info("I'll be logged with common and other field")
|
||||||
|
contextLogger.Info("Me too")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more advanced usage such as logging to multiple locations from the same
|
||||||
|
application, you can also create an instance of the `logrus` Logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new instance of the logger. You can have any number of instances.
|
||||||
|
var log = logrus.New()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// The API for setting attributes is a little different than the package level
|
||||||
|
// exported logger. See Godoc.
|
||||||
|
log.Out = os.Stdout
|
||||||
|
|
||||||
|
// You could set this to any `io.Writer` such as a file
|
||||||
|
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
|
||||||
|
// if err == nil {
|
||||||
|
// log.Out = file
|
||||||
|
// } else {
|
||||||
|
// log.Info("Failed to log to file, using default stderr")
|
||||||
|
// }
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
Logrus encourages careful, structured logging through logging fields instead of
|
||||||
|
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
||||||
|
to send event %s to topic %s with key %d")`, you should log the much more
|
||||||
|
discoverable:
|
||||||
|
|
||||||
|
```go
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"event": event,
|
||||||
|
"topic": topic,
|
||||||
|
"key": key,
|
||||||
|
}).Fatal("Failed to send event")
|
||||||
|
```
|
||||||
|
|
||||||
|
We've found this API forces you to think about logging in a way that produces
|
||||||
|
much more useful logging messages. We've been in countless situations where just
|
||||||
|
a single added field to a log statement that was already there would've saved us
|
||||||
|
hours. The `WithFields` call is optional.
|
||||||
|
|
||||||
|
In general, with Logrus using any of the `printf`-family functions should be
|
||||||
|
seen as a hint you should add a field, however, you can still use the
|
||||||
|
`printf`-family functions with Logrus.
|
||||||
|
|
||||||
|
#### Default Fields
|
||||||
|
|
||||||
|
Often it's helpful to have fields _always_ attached to log statements in an
|
||||||
|
application or parts of one. For example, you may want to always log the
|
||||||
|
`request_id` and `user_ip` in the context of a request. Instead of writing
|
||||||
|
`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
|
||||||
|
every line, you can create a `logrus.Entry` to pass around instead:
|
||||||
|
|
||||||
|
```go
|
||||||
|
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
|
||||||
|
requestLogger.Info("something happened on that request") # will log request_id and user_ip
|
||||||
|
requestLogger.Warn("something not great happened")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Hooks
|
||||||
|
|
||||||
|
You can add hooks for logging levels. For example to send errors to an exception
|
||||||
|
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||||
|
multiple places simultaneously, e.g. syslog.
|
||||||
|
|
||||||
|
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||||
|
`init`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
|
||||||
|
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
||||||
|
"log/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||||
|
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||||
|
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||||
|
|
||||||
|
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to connect to local syslog daemon")
|
||||||
|
} else {
|
||||||
|
log.AddHook(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
||||||
|
|
||||||
|
A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
||||||
|
|
||||||
|
|
||||||
|
#### Level logging
|
||||||
|
|
||||||
|
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
||||||
|
|
||||||
|
```go
|
||||||
|
log.Debug("Useful debugging information.")
|
||||||
|
log.Info("Something noteworthy happened!")
|
||||||
|
log.Warn("You should probably take a look at this.")
|
||||||
|
log.Error("Something failed but I'm not quitting.")
|
||||||
|
// Calls os.Exit(1) after logging
|
||||||
|
log.Fatal("Bye.")
|
||||||
|
// Calls panic() after logging
|
||||||
|
log.Panic("I'm bailing.")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the logging level on a `Logger`, then it will only log entries with
|
||||||
|
that severity or anything above it:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||||
|
log.SetLevel(log.InfoLevel)
|
||||||
|
```
|
||||||
|
|
||||||
|
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||||
|
environment if your application has that.
|
||||||
|
|
||||||
|
#### Entries
|
||||||
|
|
||||||
|
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||||
|
automatically added to all logging events:
|
||||||
|
|
||||||
|
1. `time`. The timestamp when the entry was created.
|
||||||
|
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||||
|
the `AddFields` call. E.g. `Failed to send event.`
|
||||||
|
3. `level`. The logging level. E.g. `info`.
|
||||||
|
|
||||||
|
#### Environments
|
||||||
|
|
||||||
|
Logrus has no notion of environment.
|
||||||
|
|
||||||
|
If you wish for hooks and formatters to only be used in specific environments,
|
||||||
|
you should handle that yourself. For example, if your application has a global
|
||||||
|
variable `Environment`, which is a string representation of the environment you
|
||||||
|
could do:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// do something here to set environment depending on an environment variable
|
||||||
|
// or command-line flag
|
||||||
|
if Environment == "production" {
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
} else {
|
||||||
|
// The TextFormatter is default, you don't actually have to do this.
|
||||||
|
log.SetFormatter(&log.TextFormatter{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration is how `logrus` was intended to be used, but JSON in
|
||||||
|
production is mostly only useful if you do log aggregation with tools like
|
||||||
|
Splunk or Logstash.
|
||||||
|
|
||||||
|
#### Formatters
|
||||||
|
|
||||||
|
The built-in logging formatters are:
|
||||||
|
|
||||||
|
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||||
|
without colors.
|
||||||
|
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||||
|
field to `true`. To force no colored output even if there is a TTY set the
|
||||||
|
`DisableColors` field to `true`. For Windows, see
|
||||||
|
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
||||||
|
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
||||||
|
truncation set the `DisableLevelTruncation` field to `true`.
|
||||||
|
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
||||||
|
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||||
|
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
||||||
|
|
||||||
|
Third party logging formatters:
|
||||||
|
|
||||||
|
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
|
||||||
|
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
||||||
|
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||||
|
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||||
|
|
||||||
|
You can define your formatter by implementing the `Formatter` interface,
|
||||||
|
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||||
|
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||||
|
default ones (see Entries section above):
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyJSONFormatter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetFormatter(new(MyJSONFormatter))
|
||||||
|
|
||||||
|
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
// Note this doesn't include Time, Level and Message which are available on
|
||||||
|
// the Entry. Consult `godoc` on information about those fields or read the
|
||||||
|
// source of the official loggers.
|
||||||
|
serialized, err := json.Marshal(entry.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||||
|
}
|
||||||
|
return append(serialized, '\n'), nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logger as an `io.Writer`
|
||||||
|
|
||||||
|
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
||||||
|
|
||||||
|
```go
|
||||||
|
w := logger.Writer()
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
// create a stdlib log.Logger that writes to
|
||||||
|
// logrus.Logger.
|
||||||
|
ErrorLog: log.New(w, "", 0),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line written to that writer will be printed the usual way, using formatters
|
||||||
|
and hooks. The level for those entries is `info`.
|
||||||
|
|
||||||
|
This means that we can override the standard library logger easily:
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger := logrus.New()
|
||||||
|
logger.Formatter = &logrus.JSONFormatter{}
|
||||||
|
|
||||||
|
// Use logrus for standard log output
|
||||||
|
// Note that `log` here references stdlib's log
|
||||||
|
// Not logrus imported under the name `log`.
|
||||||
|
log.SetOutput(logger.Writer())
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rotation
|
||||||
|
|
||||||
|
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||||
|
external program (like `logrotate(8)`) that can compress and delete old log
|
||||||
|
entries. It should not be a feature of the application-level logger.
|
||||||
|
|
||||||
|
#### Tools
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
||||||
|
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
||||||
|
|
||||||
|
#### Testing
|
||||||
|
|
||||||
|
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||||
|
|
||||||
|
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
||||||
|
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||||
|
|
||||||
|
```go
|
||||||
|
import(
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/sirupsen/logrus/hooks/test"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSomething(t*testing.T){
|
||||||
|
logger, hook := test.NewNullLogger()
|
||||||
|
logger.Error("Helloerror")
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(hook.Entries))
|
||||||
|
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
|
||||||
|
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
|
||||||
|
|
||||||
|
hook.Reset()
|
||||||
|
assert.Nil(t, hook.LastEntry())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fatal handlers
|
||||||
|
|
||||||
|
Logrus can register one or more functions that will be called when any `fatal`
|
||||||
|
level message is logged. The registered handlers will be executed before
|
||||||
|
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
|
||||||
|
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
handler := func() {
|
||||||
|
// gracefully shutdown something...
|
||||||
|
}
|
||||||
|
logrus.RegisterExitHandler(handler)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Thread safety
|
||||||
|
|
||||||
|
By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
|
||||||
|
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
||||||
|
|
||||||
|
Situation when locking is not needed includes:
|
||||||
|
|
||||||
|
* You have no hooks registered, or hooks calling is already thread-safe.
|
||||||
|
|
||||||
|
* Writing to logger.Out is already thread-safe, for example:
|
||||||
|
|
||||||
|
1) logger.Out is protected by locks.
|
||||||
|
|
||||||
|
2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
|
||||||
|
|
||||||
|
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
// The following code was sourced and modified from the
|
||||||
|
// https://github.com/tebeka/atexit package governed by the following license:
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var handlers = []func(){}
|
||||||
|
|
||||||
|
func runHandler(handler func()) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHandlers() {
|
||||||
|
for _, handler := range handlers {
|
||||||
|
runHandler(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||||
|
func Exit(code int) {
|
||||||
|
runHandlers()
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||||
|
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||||
|
// made.
|
||||||
|
//
|
||||||
|
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||||
|
// message but also needs to gracefully shutdown. An example usecase could be
|
||||||
|
// closing database connections, or sending a alert that the application is
|
||||||
|
// closing.
|
||||||
|
func RegisterExitHandler(handler func()) {
|
||||||
|
handlers = append(handlers, handler)
|
||||||
|
}
|
14
vendor/github.com/Sirupsen/logrus/appveyor.yml
generated
vendored
Normal file
14
vendor/github.com/Sirupsen/logrus/appveyor.yml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
version: "{build}"
|
||||||
|
platform: x64
|
||||||
|
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
||||||
|
environment:
|
||||||
|
GOPATH: c:\gopath
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
install:
|
||||||
|
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||||
|
- go version
|
||||||
|
build_script:
|
||||||
|
- go get -t
|
||||||
|
- go test
|
26
vendor/github.com/Sirupsen/logrus/doc.go
generated
vendored
Normal file
26
vendor/github.com/Sirupsen/logrus/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
||||||
|
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"number": 1,
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
||||||
|
|
||||||
|
For a full guide visit https://github.com/sirupsen/logrus
|
||||||
|
*/
|
||||||
|
package logrus
|
300
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
Normal file
300
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufferPool *sync.Pool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bufferPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines the key when adding errors using WithError.
|
||||||
|
var ErrorKey = "error"
|
||||||
|
|
||||||
|
// An entry is the final or intermediate Logrus logging entry. It contains all
|
||||||
|
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
||||||
|
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
||||||
|
// passed around as much as you wish to avoid field duplication.
|
||||||
|
type Entry struct {
|
||||||
|
Logger *Logger
|
||||||
|
|
||||||
|
// Contains all the fields set by the user.
|
||||||
|
Data Fields
|
||||||
|
|
||||||
|
// Time at which the log entry was created
|
||||||
|
Time time.Time
|
||||||
|
|
||||||
|
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
|
||||||
|
Level Level
|
||||||
|
|
||||||
|
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
Message string
|
||||||
|
|
||||||
|
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||||
|
Buffer *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEntry(logger *Logger) *Entry {
|
||||||
|
return &Entry{
|
||||||
|
Logger: logger,
|
||||||
|
// Default is five fields, give a little extra room
|
||||||
|
Data: make(Fields, 5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the string representation from the reader and ultimately the
|
||||||
|
// formatter.
|
||||||
|
func (entry *Entry) String() (string, error) {
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
str := string(serialized)
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||||
|
func (entry *Entry) WithError(err error) *Entry {
|
||||||
|
return entry.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a single field to the Entry.
|
||||||
|
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||||
|
return entry.WithFields(Fields{key: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a map of fields to the Entry.
|
||||||
|
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
|
data := make(Fields, len(entry.Data)+len(fields))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range fields {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overrides the time of the Entry.
|
||||||
|
func (entry *Entry) WithTime(t time.Time) *Entry {
|
||||||
|
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is not declared with a pointer value because otherwise
|
||||||
|
// race conditions will occur when using multiple goroutines
|
||||||
|
func (entry Entry) log(level Level, msg string) {
|
||||||
|
var buffer *bytes.Buffer
|
||||||
|
|
||||||
|
// Default to now, but allow users to override if they want.
|
||||||
|
//
|
||||||
|
// We don't have to worry about polluting future calls to Entry#log()
|
||||||
|
// with this assignment because this function is declared with a
|
||||||
|
// non-pointer receiver.
|
||||||
|
if entry.Time.IsZero() {
|
||||||
|
entry.Time = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Level = level
|
||||||
|
entry.Message = msg
|
||||||
|
|
||||||
|
entry.fireHooks()
|
||||||
|
|
||||||
|
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||||
|
buffer.Reset()
|
||||||
|
defer bufferPool.Put(buffer)
|
||||||
|
entry.Buffer = buffer
|
||||||
|
|
||||||
|
entry.write()
|
||||||
|
|
||||||
|
entry.Buffer = nil
|
||||||
|
|
||||||
|
// To avoid Entry#log() returning a value that only would make sense for
|
||||||
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
|
// directly here.
|
||||||
|
if level <= PanicLevel {
|
||||||
|
panic(&entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) fireHooks() {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
defer entry.Logger.mu.Unlock()
|
||||||
|
err := entry.Logger.Hooks.Fire(entry.Level, entry)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) write() {
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
defer entry.Logger.mu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
|
} else {
|
||||||
|
_, err = entry.Logger.Out.Write(serialized)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Debug(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= DebugLevel {
|
||||||
|
entry.log(DebugLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Print(args ...interface{}) {
|
||||||
|
entry.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Info(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= InfoLevel {
|
||||||
|
entry.log(InfoLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warn(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= WarnLevel {
|
||||||
|
entry.log(WarnLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warning(args ...interface{}) {
|
||||||
|
entry.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Error(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= ErrorLevel {
|
||||||
|
entry.log(ErrorLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatal(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= FatalLevel {
|
||||||
|
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= PanicLevel {
|
||||||
|
entry.log(PanicLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
panic(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Printf family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= DebugLevel {
|
||||||
|
entry.Debug(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infof(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= InfoLevel {
|
||||||
|
entry.Info(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||||
|
entry.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= WarnLevel {
|
||||||
|
entry.Warn(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= ErrorLevel {
|
||||||
|
entry.Error(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= FatalLevel {
|
||||||
|
entry.Fatal(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= PanicLevel {
|
||||||
|
entry.Panic(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Println family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Debugln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= DebugLevel {
|
||||||
|
entry.Debug(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infoln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= InfoLevel {
|
||||||
|
entry.Info(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Println(args ...interface{}) {
|
||||||
|
entry.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= WarnLevel {
|
||||||
|
entry.Warn(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningln(args ...interface{}) {
|
||||||
|
entry.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= ErrorLevel {
|
||||||
|
entry.Error(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= FatalLevel {
|
||||||
|
entry.Fatal(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= PanicLevel {
|
||||||
|
entry.Panic(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
||||||
|
// fmt.Sprintln where spaces are always added between operands, regardless of
|
||||||
|
// their type. Instead of vendoring the Sprintln implementation to spare a
|
||||||
|
// string allocation, we do the simplest thing.
|
||||||
|
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
||||||
|
msg := fmt.Sprintln(args...)
|
||||||
|
return msg[:len(msg)-1]
|
||||||
|
}
|
201
vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
Normal file
201
vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// std is the name of the standard logger in stdlib `log`
|
||||||
|
std = New()
|
||||||
|
)
|
||||||
|
|
||||||
|
func StandardLogger() *Logger {
|
||||||
|
return std
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the standard logger output.
|
||||||
|
func SetOutput(out io.Writer) {
|
||||||
|
std.SetOutput(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormatter sets the standard logger formatter.
|
||||||
|
func SetFormatter(formatter Formatter) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Formatter = formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the standard logger level.
|
||||||
|
func SetLevel(level Level) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.SetLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns the standard logger level.
|
||||||
|
func GetLevel() Level {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
return std.level()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHook adds a hook to the standard logger hooks.
|
||||||
|
func AddHook(hook Hook) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
||||||
|
func WithError(err error) *Entry {
|
||||||
|
return std.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithField creates an entry from the standard logger and adds a field to
|
||||||
|
// it. If you want multiple fields, use `WithFields`.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithField(key string, value interface{}) *Entry {
|
||||||
|
return std.WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFields creates an entry from the standard logger and adds multiple
|
||||||
|
// fields to it. This is simply a helper for `WithField`, invoking it
|
||||||
|
// once for each field.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithFields(fields Fields) *Entry {
|
||||||
|
return std.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTime creats an entry from the standard logger and overrides the time of
|
||||||
|
// logs generated with it.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithTime(t time.Time) *Entry {
|
||||||
|
return std.WithTime(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs a message at level Debug on the standard logger.
|
||||||
|
func Debug(args ...interface{}) {
|
||||||
|
std.Debug(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print logs a message at level Info on the standard logger.
|
||||||
|
func Print(args ...interface{}) {
|
||||||
|
std.Print(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs a message at level Info on the standard logger.
|
||||||
|
func Info(args ...interface{}) {
|
||||||
|
std.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a message at level Warn on the standard logger.
|
||||||
|
func Warn(args ...interface{}) {
|
||||||
|
std.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning logs a message at level Warn on the standard logger.
|
||||||
|
func Warning(args ...interface{}) {
|
||||||
|
std.Warning(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs a message at level Error on the standard logger.
|
||||||
|
func Error(args ...interface{}) {
|
||||||
|
std.Error(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic logs a message at level Panic on the standard logger.
|
||||||
|
func Panic(args ...interface{}) {
|
||||||
|
std.Panic(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func Fatal(args ...interface{}) {
|
||||||
|
std.Fatal(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a message at level Debug on the standard logger.
|
||||||
|
func Debugf(format string, args ...interface{}) {
|
||||||
|
std.Debugf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf logs a message at level Info on the standard logger.
|
||||||
|
func Printf(format string, args ...interface{}) {
|
||||||
|
std.Printf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a message at level Info on the standard logger.
|
||||||
|
func Infof(format string, args ...interface{}) {
|
||||||
|
std.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs a message at level Warn on the standard logger.
|
||||||
|
func Warnf(format string, args ...interface{}) {
|
||||||
|
std.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf logs a message at level Warn on the standard logger.
|
||||||
|
func Warningf(format string, args ...interface{}) {
|
||||||
|
std.Warningf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs a message at level Error on the standard logger.
|
||||||
|
func Errorf(format string, args ...interface{}) {
|
||||||
|
std.Errorf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicf logs a message at level Panic on the standard logger.
|
||||||
|
func Panicf(format string, args ...interface{}) {
|
||||||
|
std.Panicf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func Fatalf(format string, args ...interface{}) {
|
||||||
|
std.Fatalf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugln logs a message at level Debug on the standard logger.
|
||||||
|
func Debugln(args ...interface{}) {
|
||||||
|
std.Debugln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println logs a message at level Info on the standard logger.
|
||||||
|
func Println(args ...interface{}) {
|
||||||
|
std.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infoln logs a message at level Info on the standard logger.
|
||||||
|
func Infoln(args ...interface{}) {
|
||||||
|
std.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnln logs a message at level Warn on the standard logger.
|
||||||
|
func Warnln(args ...interface{}) {
|
||||||
|
std.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningln logs a message at level Warn on the standard logger.
|
||||||
|
func Warningln(args ...interface{}) {
|
||||||
|
std.Warningln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorln logs a message at level Error on the standard logger.
|
||||||
|
func Errorln(args ...interface{}) {
|
||||||
|
std.Errorln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicln logs a message at level Panic on the standard logger.
|
||||||
|
func Panicln(args ...interface{}) {
|
||||||
|
std.Panicln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func Fatalln(args ...interface{}) {
|
||||||
|
std.Fatalln(args...)
|
||||||
|
}
|
51
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
Normal file
51
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const defaultTimestampFormat = time.RFC3339
|
||||||
|
|
||||||
|
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||||
|
// `Entry`. It exposes all the fields, including the default ones:
|
||||||
|
//
|
||||||
|
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
||||||
|
// * `entry.Data["time"]`. The timestamp.
|
||||||
|
// * `entry.Data["level"]. The level the entry was logged at.
|
||||||
|
//
|
||||||
|
// Any additional fields added with `WithField` or `WithFields` are also in
|
||||||
|
// `entry.Data`. Format is expected to return an array of bytes which are then
|
||||||
|
// logged to `logger.Out`.
|
||||||
|
type Formatter interface {
|
||||||
|
Format(*Entry) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is to not silently overwrite `time`, `msg` and `level` fields when
|
||||||
|
// dumping it. If this code wasn't there doing:
|
||||||
|
//
|
||||||
|
// logrus.WithField("level", 1).Info("hello")
|
||||||
|
//
|
||||||
|
// Would just silently drop the user provided level. Instead with this code
|
||||||
|
// it'll logged as:
|
||||||
|
//
|
||||||
|
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
||||||
|
//
|
||||||
|
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||||
|
// avoid code duplication between the two default formatters.
|
||||||
|
func prefixFieldClashes(data Fields, fieldMap FieldMap) {
|
||||||
|
timeKey := fieldMap.resolve(FieldKeyTime)
|
||||||
|
if t, ok := data[timeKey]; ok {
|
||||||
|
data["fields."+timeKey] = t
|
||||||
|
delete(data, timeKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgKey := fieldMap.resolve(FieldKeyMsg)
|
||||||
|
if m, ok := data[msgKey]; ok {
|
||||||
|
data["fields."+msgKey] = m
|
||||||
|
delete(data, msgKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
levelKey := fieldMap.resolve(FieldKeyLevel)
|
||||||
|
if l, ok := data[levelKey]; ok {
|
||||||
|
data["fields."+levelKey] = l
|
||||||
|
delete(data, levelKey)
|
||||||
|
}
|
||||||
|
}
|
34
vendor/github.com/Sirupsen/logrus/hooks.go
generated
vendored
Normal file
34
vendor/github.com/Sirupsen/logrus/hooks.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
// A hook to be fired when logging on the logging levels returned from
|
||||||
|
// `Levels()` on your implementation of the interface. Note that this is not
|
||||||
|
// fired in a goroutine or a channel with workers, you should handle such
|
||||||
|
// functionality yourself if your call is non-blocking and you don't wish for
|
||||||
|
// the logging calls for levels returned from `Levels()` to block.
|
||||||
|
type Hook interface {
|
||||||
|
Levels() []Level
|
||||||
|
Fire(*Entry) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal type for storing the hooks on a logger instance.
|
||||||
|
type LevelHooks map[Level][]Hook
|
||||||
|
|
||||||
|
// Add a hook to an instance of logger. This is called with
|
||||||
|
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
||||||
|
func (hooks LevelHooks) Add(hook Hook) {
|
||||||
|
for _, level := range hook.Levels() {
|
||||||
|
hooks[level] = append(hooks[level], hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
||||||
|
// appropriate hooks for a log entry.
|
||||||
|
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
||||||
|
for _, hook := range hooks[level] {
|
||||||
|
if err := hook.Fire(entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
89
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
89
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fieldKey string
|
||||||
|
|
||||||
|
// FieldMap allows customization of the key names for default fields.
|
||||||
|
type FieldMap map[fieldKey]string
|
||||||
|
|
||||||
|
// Default key names for the default fields
|
||||||
|
const (
|
||||||
|
FieldKeyMsg = "msg"
|
||||||
|
FieldKeyLevel = "level"
|
||||||
|
FieldKeyTime = "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f FieldMap) resolve(key fieldKey) string {
|
||||||
|
if k, ok := f[key]; ok {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONFormatter formats logs into parsable json
|
||||||
|
type JSONFormatter struct {
|
||||||
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// DisableTimestamp allows disabling automatic timestamps in output
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
||||||
|
DataKey string
|
||||||
|
|
||||||
|
// FieldMap allows users to customize the names of keys for default fields.
|
||||||
|
// As an example:
|
||||||
|
// formatter := &JSONFormatter{
|
||||||
|
// FieldMap: FieldMap{
|
||||||
|
// FieldKeyTime: "@timestamp",
|
||||||
|
// FieldKeyLevel: "@level",
|
||||||
|
// FieldKeyMsg: "@message",
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
FieldMap FieldMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format renders a single log entry
|
||||||
|
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
data := make(Fields, len(entry.Data)+3)
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case error:
|
||||||
|
// Otherwise errors are ignored by `encoding/json`
|
||||||
|
// https://github.com/sirupsen/logrus/issues/137
|
||||||
|
data[k] = v.Error()
|
||||||
|
default:
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DataKey != "" {
|
||||||
|
newData := make(Fields, 4)
|
||||||
|
newData[f.DataKey] = data
|
||||||
|
data = newData
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixFieldClashes(data, f.FieldMap)
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = defaultTimestampFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.DisableTimestamp {
|
||||||
|
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||||
|
}
|
||||||
|
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||||
|
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||||
|
|
||||||
|
serialized, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||||
|
}
|
||||||
|
return append(serialized, '\n'), nil
|
||||||
|
}
|
337
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
Normal file
337
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||||
|
// file, or leave it default which is `os.Stderr`. You can also set this to
|
||||||
|
// something more adventorous, such as logging to Kafka.
|
||||||
|
Out io.Writer
|
||||||
|
// Hooks for the logger instance. These allow firing events based on logging
|
||||||
|
// levels and log entries. For example, to send errors to an error tracking
|
||||||
|
// service, log to StatsD or dump the core on fatal errors.
|
||||||
|
Hooks LevelHooks
|
||||||
|
// All log entries pass through the formatter before logged to Out. The
|
||||||
|
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
||||||
|
// TextFormatter is the default. In development (when a TTY is attached) it
|
||||||
|
// logs with colors, but to a file it wouldn't. You can easily implement your
|
||||||
|
// own that implements the `Formatter` interface, see the `README` or included
|
||||||
|
// formatters for examples.
|
||||||
|
Formatter Formatter
|
||||||
|
// The logging level the logger should log at. This is typically (and defaults
|
||||||
|
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||||
|
// logged.
|
||||||
|
Level Level
|
||||||
|
// Used to sync writing to the log. Locking is enabled by Default
|
||||||
|
mu MutexWrap
|
||||||
|
// Reusable empty entry
|
||||||
|
entryPool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
type MutexWrap struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
disabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Lock() {
|
||||||
|
if !mw.disabled {
|
||||||
|
mw.lock.Lock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Unlock() {
|
||||||
|
if !mw.disabled {
|
||||||
|
mw.lock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Disable() {
|
||||||
|
mw.disabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||||
|
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
||||||
|
// instantiate your own:
|
||||||
|
//
|
||||||
|
// var log = &Logger{
|
||||||
|
// Out: os.Stderr,
|
||||||
|
// Formatter: new(JSONFormatter),
|
||||||
|
// Hooks: make(LevelHooks),
|
||||||
|
// Level: logrus.DebugLevel,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// It's recommended to make this a global instance called `log`.
|
||||||
|
func New() *Logger {
|
||||||
|
return &Logger{
|
||||||
|
Out: os.Stderr,
|
||||||
|
Formatter: new(TextFormatter),
|
||||||
|
Hooks: make(LevelHooks),
|
||||||
|
Level: InfoLevel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) newEntry() *Entry {
|
||||||
|
entry, ok := logger.entryPool.Get().(*Entry)
|
||||||
|
if ok {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
return NewEntry(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||||
|
logger.entryPool.Put(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a field to the log entry, note that it doesn't log until you call
|
||||||
|
// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry.
|
||||||
|
// If you want multiple fields, use `WithFields`.
|
||||||
|
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||||
|
// each `Field`.
|
||||||
|
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an error as single field to the log entry. All it does is call
|
||||||
|
// `WithError` for the given `error`.
|
||||||
|
func (logger *Logger) WithError(err error) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overrides the time of the log entry.
|
||||||
|
func (logger *Logger) WithTime(t time.Time) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithTime(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= DebugLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Debugf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= InfoLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Infof(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Printf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= ErrorLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Errorf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= FatalLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Fatalf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= PanicLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Panicf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debug(args ...interface{}) {
|
||||||
|
if logger.level() >= DebugLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Debug(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Info(args ...interface{}) {
|
||||||
|
if logger.level() >= InfoLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Info(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Print(args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Info(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warn(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warn(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warning(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warn(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Error(args ...interface{}) {
|
||||||
|
if logger.level() >= ErrorLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Error(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatal(args ...interface{}) {
|
||||||
|
if logger.level() >= FatalLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Fatal(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panic(args ...interface{}) {
|
||||||
|
if logger.level() >= PanicLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Panic(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugln(args ...interface{}) {
|
||||||
|
if logger.level() >= DebugLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Debugln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infoln(args ...interface{}) {
|
||||||
|
if logger.level() >= InfoLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Infoln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Println(args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Println(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnln(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningln(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorln(args ...interface{}) {
|
||||||
|
if logger.level() >= ErrorLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Errorln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||||
|
if logger.level() >= FatalLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Fatalln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicln(args ...interface{}) {
|
||||||
|
if logger.level() >= PanicLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Panicln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//When file is opened with appending mode, it's safe to
|
||||||
|
//write concurrently to a file (within 4k message on Linux).
|
||||||
|
//In these cases user can choose to disable the lock.
|
||||||
|
func (logger *Logger) SetNoLock() {
|
||||||
|
logger.mu.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) level() Level {
|
||||||
|
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) SetLevel(level Level) {
|
||||||
|
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) SetOutput(out io.Writer) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.Out = out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) AddHook(hook Hook) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.Hooks.Add(hook)
|
||||||
|
}
|
143
vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
Normal file
143
vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fields type, used to pass to `WithFields`.
|
||||||
|
type Fields map[string]interface{}
|
||||||
|
|
||||||
|
// Level type
|
||||||
|
type Level uint32
|
||||||
|
|
||||||
|
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
||||||
|
func (level Level) String() string {
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
return "debug"
|
||||||
|
case InfoLevel:
|
||||||
|
return "info"
|
||||||
|
case WarnLevel:
|
||||||
|
return "warning"
|
||||||
|
case ErrorLevel:
|
||||||
|
return "error"
|
||||||
|
case FatalLevel:
|
||||||
|
return "fatal"
|
||||||
|
case PanicLevel:
|
||||||
|
return "panic"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||||
|
func ParseLevel(lvl string) (Level, error) {
|
||||||
|
switch strings.ToLower(lvl) {
|
||||||
|
case "panic":
|
||||||
|
return PanicLevel, nil
|
||||||
|
case "fatal":
|
||||||
|
return FatalLevel, nil
|
||||||
|
case "error":
|
||||||
|
return ErrorLevel, nil
|
||||||
|
case "warn", "warning":
|
||||||
|
return WarnLevel, nil
|
||||||
|
case "info":
|
||||||
|
return InfoLevel, nil
|
||||||
|
case "debug":
|
||||||
|
return DebugLevel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var l Level
|
||||||
|
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A constant exposing all logging levels
|
||||||
|
var AllLevels = []Level{
|
||||||
|
PanicLevel,
|
||||||
|
FatalLevel,
|
||||||
|
ErrorLevel,
|
||||||
|
WarnLevel,
|
||||||
|
InfoLevel,
|
||||||
|
DebugLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the different logging levels. You can set the logging level to log
|
||||||
|
// on your instance of logger, obtained with `logrus.New()`.
|
||||||
|
const (
|
||||||
|
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||||
|
// message passed to Debug, Info, ...
|
||||||
|
PanicLevel Level = iota
|
||||||
|
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
|
||||||
|
// logging level is set to Panic.
|
||||||
|
FatalLevel
|
||||||
|
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||||
|
// Commonly used for hooks to send errors to an error tracking service.
|
||||||
|
ErrorLevel
|
||||||
|
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||||
|
WarnLevel
|
||||||
|
// InfoLevel level. General operational entries about what's going on inside the
|
||||||
|
// application.
|
||||||
|
InfoLevel
|
||||||
|
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||||
|
DebugLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
// Won't compile if StdLogger can't be realized by a log.Logger
|
||||||
|
var (
|
||||||
|
_ StdLogger = &log.Logger{}
|
||||||
|
_ StdLogger = &Entry{}
|
||||||
|
_ StdLogger = &Logger{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// StdLogger is what your logrus-enabled library should take, that way
|
||||||
|
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
||||||
|
// interface, this is the closest we get, unfortunately.
|
||||||
|
type StdLogger interface {
|
||||||
|
Print(...interface{})
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
Println(...interface{})
|
||||||
|
|
||||||
|
Fatal(...interface{})
|
||||||
|
Fatalf(string, ...interface{})
|
||||||
|
Fatalln(...interface{})
|
||||||
|
|
||||||
|
Panic(...interface{})
|
||||||
|
Panicf(string, ...interface{})
|
||||||
|
Panicln(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// The FieldLogger interface generalizes the Entry and Logger types
|
||||||
|
type FieldLogger interface {
|
||||||
|
WithField(key string, value interface{}) *Entry
|
||||||
|
WithFields(fields Fields) *Entry
|
||||||
|
WithError(err error) *Entry
|
||||||
|
|
||||||
|
Debugf(format string, args ...interface{})
|
||||||
|
Infof(format string, args ...interface{})
|
||||||
|
Printf(format string, args ...interface{})
|
||||||
|
Warnf(format string, args ...interface{})
|
||||||
|
Warningf(format string, args ...interface{})
|
||||||
|
Errorf(format string, args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
Panicf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Debug(args ...interface{})
|
||||||
|
Info(args ...interface{})
|
||||||
|
Print(args ...interface{})
|
||||||
|
Warn(args ...interface{})
|
||||||
|
Warning(args ...interface{})
|
||||||
|
Error(args ...interface{})
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
Panic(args ...interface{})
|
||||||
|
|
||||||
|
Debugln(args ...interface{})
|
||||||
|
Infoln(args ...interface{})
|
||||||
|
Println(args ...interface{})
|
||||||
|
Warnln(args ...interface{})
|
||||||
|
Warningln(args ...interface{})
|
||||||
|
Errorln(args ...interface{})
|
||||||
|
Fatalln(args ...interface{})
|
||||||
|
Panicln(args ...interface{})
|
||||||
|
}
|
10
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
Normal file
10
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// +build darwin freebsd openbsd netbsd dragonfly
|
||||||
|
// +build !appengine,!gopherjs
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TIOCGETA
|
||||||
|
|
||||||
|
type Termios unix.Termios
|
11
vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go
generated
vendored
Normal file
11
vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// +build appengine gopherjs
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
return true
|
||||||
|
}
|
19
vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
Normal file
19
vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// +build !appengine,!gopherjs
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
switch v := w.(type) {
|
||||||
|
case *os.File:
|
||||||
|
return terminal.IsTerminal(int(v.Fd()))
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
14
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
14
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Based on ssh/terminal:
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !appengine,!gopherjs
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TCGETS
|
||||||
|
|
||||||
|
type Termios unix.Termios
|
195
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
195
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nocolor = 0
|
||||||
|
red = 31
|
||||||
|
green = 32
|
||||||
|
yellow = 33
|
||||||
|
blue = 36
|
||||||
|
gray = 37
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseTimestamp time.Time
|
||||||
|
emptyFieldMap FieldMap
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
baseTimestamp = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextFormatter formats logs into text
|
||||||
|
type TextFormatter struct {
|
||||||
|
// Set to true to bypass checking for a TTY before outputting colors.
|
||||||
|
ForceColors bool
|
||||||
|
|
||||||
|
// Force disabling colors.
|
||||||
|
DisableColors bool
|
||||||
|
|
||||||
|
// Disable timestamp logging. useful when output is redirected to logging
|
||||||
|
// system that already adds timestamps.
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// Enable logging the full timestamp when a TTY is attached instead of just
|
||||||
|
// the time passed since beginning of execution.
|
||||||
|
FullTimestamp bool
|
||||||
|
|
||||||
|
// TimestampFormat to use for display when a full timestamp is printed
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// The fields are sorted by default for a consistent output. For applications
|
||||||
|
// that log extremely frequently and don't use the JSON formatter this may not
|
||||||
|
// be desired.
|
||||||
|
DisableSorting bool
|
||||||
|
|
||||||
|
// Disables the truncation of the level text to 4 characters.
|
||||||
|
DisableLevelTruncation bool
|
||||||
|
|
||||||
|
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||||
|
QuoteEmptyFields bool
|
||||||
|
|
||||||
|
// Whether the logger's out is to a terminal
|
||||||
|
isTerminal bool
|
||||||
|
|
||||||
|
// FieldMap allows users to customize the names of keys for default fields.
|
||||||
|
// As an example:
|
||||||
|
// formatter := &TextFormatter{
|
||||||
|
// FieldMap: FieldMap{
|
||||||
|
// FieldKeyTime: "@timestamp",
|
||||||
|
// FieldKeyLevel: "@level",
|
||||||
|
// FieldKeyMsg: "@message"}}
|
||||||
|
FieldMap FieldMap
|
||||||
|
|
||||||
|
sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) init(entry *Entry) {
|
||||||
|
if entry.Logger != nil {
|
||||||
|
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format renders a single log entry
|
||||||
|
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
prefixFieldClashes(entry.Data, f.FieldMap)
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(entry.Data))
|
||||||
|
for k := range entry.Data {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.DisableSorting {
|
||||||
|
sort.Strings(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b *bytes.Buffer
|
||||||
|
if entry.Buffer != nil {
|
||||||
|
b = entry.Buffer
|
||||||
|
} else {
|
||||||
|
b = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Do(func() { f.init(entry) })
|
||||||
|
|
||||||
|
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = defaultTimestampFormat
|
||||||
|
}
|
||||||
|
if isColored {
|
||||||
|
f.printColored(b, entry, keys, timestampFormat)
|
||||||
|
} else {
|
||||||
|
if !f.DisableTimestamp {
|
||||||
|
f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyTime), entry.Time.Format(timestampFormat))
|
||||||
|
}
|
||||||
|
f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyLevel), entry.Level.String())
|
||||||
|
if entry.Message != "" {
|
||||||
|
f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyMsg), entry.Message)
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
f.appendKeyValue(b, key, entry.Data[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte('\n')
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
|
||||||
|
var levelColor int
|
||||||
|
switch entry.Level {
|
||||||
|
case DebugLevel:
|
||||||
|
levelColor = gray
|
||||||
|
case WarnLevel:
|
||||||
|
levelColor = yellow
|
||||||
|
case ErrorLevel, FatalLevel, PanicLevel:
|
||||||
|
levelColor = red
|
||||||
|
default:
|
||||||
|
levelColor = blue
|
||||||
|
}
|
||||||
|
|
||||||
|
levelText := strings.ToUpper(entry.Level.String())
|
||||||
|
if !f.DisableLevelTruncation {
|
||||||
|
levelText = levelText[0:4]
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DisableTimestamp {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
|
||||||
|
} else if !f.FullTimestamp {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
v := entry.Data[k]
|
||||||
|
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||||
|
f.appendValue(b, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||||
|
if f.QuoteEmptyFields && len(text) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, ch := range text {
|
||||||
|
if !((ch >= 'a' && ch <= 'z') ||
|
||||||
|
(ch >= 'A' && ch <= 'Z') ||
|
||||||
|
(ch >= '0' && ch <= '9') ||
|
||||||
|
ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||||
|
if b.Len() > 0 {
|
||||||
|
b.WriteByte(' ')
|
||||||
|
}
|
||||||
|
b.WriteString(key)
|
||||||
|
b.WriteByte('=')
|
||||||
|
f.appendValue(b, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||||
|
stringVal, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
stringVal = fmt.Sprint(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.needsQuoting(stringVal) {
|
||||||
|
b.WriteString(stringVal)
|
||||||
|
} else {
|
||||||
|
b.WriteString(fmt.Sprintf("%q", stringVal))
|
||||||
|
}
|
||||||
|
}
|
62
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
Normal file
62
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (logger *Logger) Writer() *io.PipeWriter {
|
||||||
|
return logger.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||||
|
return NewEntry(logger).WriterLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Writer() *io.PipeWriter {
|
||||||
|
return entry.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||||
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
|
var printFunc func(args ...interface{})
|
||||||
|
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
printFunc = entry.Debug
|
||||||
|
case InfoLevel:
|
||||||
|
printFunc = entry.Info
|
||||||
|
case WarnLevel:
|
||||||
|
printFunc = entry.Warn
|
||||||
|
case ErrorLevel:
|
||||||
|
printFunc = entry.Error
|
||||||
|
case FatalLevel:
|
||||||
|
printFunc = entry.Fatal
|
||||||
|
case PanicLevel:
|
||||||
|
printFunc = entry.Panic
|
||||||
|
default:
|
||||||
|
printFunc = entry.Print
|
||||||
|
}
|
||||||
|
|
||||||
|
go entry.writerScanner(reader, printFunc)
|
||||||
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
for scanner.Scan() {
|
||||||
|
printFunc(scanner.Text())
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
entry.Errorf("Error while reading from Writer: %s", err)
|
||||||
|
}
|
||||||
|
reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writerFinalizer(writer *io.PipeWriter) {
|
||||||
|
writer.Close()
|
||||||
|
}
|
67
vendor/github.com/docker/go-units/CONTRIBUTING.md
generated
vendored
Normal file
67
vendor/github.com/docker/go-units/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Contributing to go-units
|
||||||
|
|
||||||
|
Want to hack on go-units? Awesome! Here are instructions to get you started.
|
||||||
|
|
||||||
|
go-units is a part of the [Docker](https://www.docker.com) project, and follows
|
||||||
|
the same rules and principles. If you're already familiar with the way
|
||||||
|
Docker does things, you'll feel right at home.
|
||||||
|
|
||||||
|
Otherwise, go read Docker's
|
||||||
|
[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md),
|
||||||
|
[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md),
|
||||||
|
[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and
|
||||||
|
[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md).
|
||||||
|
|
||||||
|
### Sign your work
|
||||||
|
|
||||||
|
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||||
|
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||||
|
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||||
|
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||||
|
|
||||||
|
```
|
||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
660 York Street, Suite 102,
|
||||||
|
San Francisco, CA 94110 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you just add a line to every git commit message:
|
||||||
|
|
||||||
|
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||||
|
|
||||||
|
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||||
|
|
||||||
|
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||||
|
commit automatically with `git commit -s`.
|
191
vendor/github.com/docker/go-units/LICENSE
generated
vendored
Normal file
191
vendor/github.com/docker/go-units/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2015 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
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.
|
46
vendor/github.com/docker/go-units/MAINTAINERS
generated
vendored
Normal file
46
vendor/github.com/docker/go-units/MAINTAINERS
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# go-units maintainers file
|
||||||
|
#
|
||||||
|
# This file describes who runs the docker/go-units project and how.
|
||||||
|
# This is a living document - if you see something out of date or missing, speak up!
|
||||||
|
#
|
||||||
|
# It is structured to be consumable by both humans and programs.
|
||||||
|
# To extract its contents programmatically, use any TOML-compliant parser.
|
||||||
|
#
|
||||||
|
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||||
|
#
|
||||||
|
[Org]
|
||||||
|
[Org."Core maintainers"]
|
||||||
|
people = [
|
||||||
|
"akihirosuda",
|
||||||
|
"dnephin",
|
||||||
|
"thajeztah",
|
||||||
|
"vdemeester",
|
||||||
|
]
|
||||||
|
|
||||||
|
[people]
|
||||||
|
|
||||||
|
# A reference list of all people associated with the project.
|
||||||
|
# All other sections should refer to people by their canonical key
|
||||||
|
# in the people section.
|
||||||
|
|
||||||
|
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||||
|
|
||||||
|
[people.akihirosuda]
|
||||||
|
Name = "Akihiro Suda"
|
||||||
|
Email = "suda.akihiro@lab.ntt.co.jp"
|
||||||
|
GitHub = "AkihiroSuda"
|
||||||
|
|
||||||
|
[people.dnephin]
|
||||||
|
Name = "Daniel Nephin"
|
||||||
|
Email = "dnephin@gmail.com"
|
||||||
|
GitHub = "dnephin"
|
||||||
|
|
||||||
|
[people.thajeztah]
|
||||||
|
Name = "Sebastiaan van Stijn"
|
||||||
|
Email = "github@gone.nl"
|
||||||
|
GitHub = "thaJeztah"
|
||||||
|
|
||||||
|
[people.vdemeester]
|
||||||
|
Name = "Vincent Demeester"
|
||||||
|
Email = "vincent@sbr.pm"
|
||||||
|
GitHub = "vdemeester"
|
16
vendor/github.com/docker/go-units/README.md
generated
vendored
Normal file
16
vendor/github.com/docker/go-units/README.md
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[](https://godoc.org/github.com/docker/go-units)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
go-units is a library to transform human friendly measurements into machine friendly values.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation.
|
||||||
|
|
||||||
|
## Copyright and license
|
||||||
|
|
||||||
|
Copyright © 2015 Docker, Inc.
|
||||||
|
|
||||||
|
go-units is licensed under the Apache License, Version 2.0.
|
||||||
|
See [LICENSE](LICENSE) for the full text of the license.
|
11
vendor/github.com/docker/go-units/circle.yml
generated
vendored
Normal file
11
vendor/github.com/docker/go-units/circle.yml
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
dependencies:
|
||||||
|
post:
|
||||||
|
# install golint
|
||||||
|
- go get github.com/golang/lint/golint
|
||||||
|
|
||||||
|
test:
|
||||||
|
pre:
|
||||||
|
# run analysis before tests
|
||||||
|
- go vet ./...
|
||||||
|
- test -z "$(golint ./... | tee /dev/stderr)"
|
||||||
|
- test -z "$(gofmt -s -l . | tee /dev/stderr)"
|
35
vendor/github.com/docker/go-units/duration.go
generated
vendored
Normal file
35
vendor/github.com/docker/go-units/duration.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Package units provides helper function to parse and print size and time units
|
||||||
|
// in human-readable format.
|
||||||
|
package units
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HumanDuration returns a human-readable approximation of a duration
|
||||||
|
// (eg. "About a minute", "4 hours ago", etc.).
|
||||||
|
func HumanDuration(d time.Duration) string {
|
||||||
|
if seconds := int(d.Seconds()); seconds < 1 {
|
||||||
|
return "Less than a second"
|
||||||
|
} else if seconds == 1 {
|
||||||
|
return "1 second"
|
||||||
|
} else if seconds < 60 {
|
||||||
|
return fmt.Sprintf("%d seconds", seconds)
|
||||||
|
} else if minutes := int(d.Minutes()); minutes == 1 {
|
||||||
|
return "About a minute"
|
||||||
|
} else if minutes < 46 {
|
||||||
|
return fmt.Sprintf("%d minutes", minutes)
|
||||||
|
} else if hours := int(d.Hours() + 0.5); hours == 1 {
|
||||||
|
return "About an hour"
|
||||||
|
} else if hours < 48 {
|
||||||
|
return fmt.Sprintf("%d hours", hours)
|
||||||
|
} else if hours < 24*7*2 {
|
||||||
|
return fmt.Sprintf("%d days", hours/24)
|
||||||
|
} else if hours < 24*30*2 {
|
||||||
|
return fmt.Sprintf("%d weeks", hours/24/7)
|
||||||
|
} else if hours < 24*365*2 {
|
||||||
|
return fmt.Sprintf("%d months", hours/24/30)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d years", int(d.Hours())/24/365)
|
||||||
|
}
|
108
vendor/github.com/docker/go-units/size.go
generated
vendored
Normal file
108
vendor/github.com/docker/go-units/size.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package units
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See: http://en.wikipedia.org/wiki/Binary_prefix
|
||||||
|
const (
|
||||||
|
// Decimal
|
||||||
|
|
||||||
|
KB = 1000
|
||||||
|
MB = 1000 * KB
|
||||||
|
GB = 1000 * MB
|
||||||
|
TB = 1000 * GB
|
||||||
|
PB = 1000 * TB
|
||||||
|
|
||||||
|
// Binary
|
||||||
|
|
||||||
|
KiB = 1024
|
||||||
|
MiB = 1024 * KiB
|
||||||
|
GiB = 1024 * MiB
|
||||||
|
TiB = 1024 * GiB
|
||||||
|
PiB = 1024 * TiB
|
||||||
|
)
|
||||||
|
|
||||||
|
type unitMap map[string]int64
|
||||||
|
|
||||||
|
var (
|
||||||
|
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
||||||
|
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
|
||||||
|
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||||
|
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
||||||
|
|
||||||
|
func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) {
|
||||||
|
i := 0
|
||||||
|
unitsLimit := len(_map) - 1
|
||||||
|
for size >= base && i < unitsLimit {
|
||||||
|
size = size / base
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return size, _map[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomSize returns a human-readable approximation of a size
|
||||||
|
// using custom format.
|
||||||
|
func CustomSize(format string, size float64, base float64, _map []string) string {
|
||||||
|
size, unit := getSizeAndUnit(size, base, _map)
|
||||||
|
return fmt.Sprintf(format, size, unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HumanSizeWithPrecision allows the size to be in any precision,
|
||||||
|
// instead of 4 digit precision used in units.HumanSize.
|
||||||
|
func HumanSizeWithPrecision(size float64, precision int) string {
|
||||||
|
size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs)
|
||||||
|
return fmt.Sprintf("%.*g%s", precision, size, unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HumanSize returns a human-readable approximation of a size
|
||||||
|
// capped at 4 valid numbers (eg. "2.746 MB", "796 KB").
|
||||||
|
func HumanSize(size float64) string {
|
||||||
|
return HumanSizeWithPrecision(size, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesSize returns a human-readable size in bytes, kibibytes,
|
||||||
|
// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB").
|
||||||
|
func BytesSize(size float64) string {
|
||||||
|
return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromHumanSize returns an integer from a human-readable specification of a
|
||||||
|
// size using SI standard (eg. "44kB", "17MB").
|
||||||
|
func FromHumanSize(size string) (int64, error) {
|
||||||
|
return parseSize(size, decimalMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAMInBytes parses a human-readable string representing an amount of RAM
|
||||||
|
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
|
||||||
|
// returns the number of bytes, or -1 if the string is unparseable.
|
||||||
|
// Units are case-insensitive, and the 'b' suffix is optional.
|
||||||
|
func RAMInBytes(size string) (int64, error) {
|
||||||
|
return parseSize(size, binaryMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses the human-readable size string into the amount it represents.
|
||||||
|
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
|
||||||
|
matches := sizeRegex.FindStringSubmatch(sizeStr)
|
||||||
|
if len(matches) != 4 {
|
||||||
|
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := strconv.ParseFloat(matches[1], 64)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unitPrefix := strings.ToLower(matches[3])
|
||||||
|
if mul, ok := uMap[unitPrefix]; ok {
|
||||||
|
size *= float64(mul)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(size), nil
|
||||||
|
}
|
118
vendor/github.com/docker/go-units/ulimit.go
generated
vendored
Normal file
118
vendor/github.com/docker/go-units/ulimit.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package units
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ulimit is a human friendly version of Rlimit.
|
||||||
|
type Ulimit struct {
|
||||||
|
Name string
|
||||||
|
Hard int64
|
||||||
|
Soft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rlimit specifies the resource limits, such as max open files.
|
||||||
|
type Rlimit struct {
|
||||||
|
Type int `json:"type,omitempty"`
|
||||||
|
Hard uint64 `json:"hard,omitempty"`
|
||||||
|
Soft uint64 `json:"soft,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// magic numbers for making the syscall
|
||||||
|
// some of these are defined in the syscall package, but not all.
|
||||||
|
// Also since Windows client doesn't get access to the syscall package, need to
|
||||||
|
// define these here
|
||||||
|
rlimitAs = 9
|
||||||
|
rlimitCore = 4
|
||||||
|
rlimitCPU = 0
|
||||||
|
rlimitData = 2
|
||||||
|
rlimitFsize = 1
|
||||||
|
rlimitLocks = 10
|
||||||
|
rlimitMemlock = 8
|
||||||
|
rlimitMsgqueue = 12
|
||||||
|
rlimitNice = 13
|
||||||
|
rlimitNofile = 7
|
||||||
|
rlimitNproc = 6
|
||||||
|
rlimitRss = 5
|
||||||
|
rlimitRtprio = 14
|
||||||
|
rlimitRttime = 15
|
||||||
|
rlimitSigpending = 11
|
||||||
|
rlimitStack = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var ulimitNameMapping = map[string]int{
|
||||||
|
//"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
|
||||||
|
"core": rlimitCore,
|
||||||
|
"cpu": rlimitCPU,
|
||||||
|
"data": rlimitData,
|
||||||
|
"fsize": rlimitFsize,
|
||||||
|
"locks": rlimitLocks,
|
||||||
|
"memlock": rlimitMemlock,
|
||||||
|
"msgqueue": rlimitMsgqueue,
|
||||||
|
"nice": rlimitNice,
|
||||||
|
"nofile": rlimitNofile,
|
||||||
|
"nproc": rlimitNproc,
|
||||||
|
"rss": rlimitRss,
|
||||||
|
"rtprio": rlimitRtprio,
|
||||||
|
"rttime": rlimitRttime,
|
||||||
|
"sigpending": rlimitSigpending,
|
||||||
|
"stack": rlimitStack,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUlimit parses and returns a Ulimit from the specified string.
|
||||||
|
func ParseUlimit(val string) (*Ulimit, error) {
|
||||||
|
parts := strings.SplitN(val, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid ulimit argument: %s", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := ulimitNameMapping[parts[0]]; !exists {
|
||||||
|
return nil, fmt.Errorf("invalid ulimit type: %s", parts[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
soft int64
|
||||||
|
hard = &soft // default to soft in case no hard was set
|
||||||
|
temp int64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
switch limitVals := strings.Split(parts[1], ":"); len(limitVals) {
|
||||||
|
case 2:
|
||||||
|
temp, err = strconv.ParseInt(limitVals[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hard = &temp
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
soft, err = strconv.ParseInt(limitVals[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if soft > *hard {
|
||||||
|
return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRlimit returns the RLimit corresponding to Ulimit.
|
||||||
|
func (u *Ulimit) GetRlimit() (*Rlimit, error) {
|
||||||
|
t, exists := ulimitNameMapping[u.Name]
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("invalid ulimit name %s", u.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Ulimit) String() string {
|
||||||
|
return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard)
|
||||||
|
}
|
4
vendor/github.com/opencontainers/runc/.gitignore
generated
vendored
Normal file
4
vendor/github.com/opencontainers/runc/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
vendor/pkg
|
||||||
|
/runc
|
||||||
|
Godeps/_workspace/src/github.com/opencontainers/runc
|
||||||
|
man/man8
|
117
vendor/github.com/opencontainers/runc/CONTRIBUTING.md
generated
vendored
Normal file
117
vendor/github.com/opencontainers/runc/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
## Contribution Guidelines
|
||||||
|
|
||||||
|
### Pull requests are always welcome
|
||||||
|
|
||||||
|
We are always thrilled to receive pull requests, and do our best to
|
||||||
|
process them as fast as possible. Not sure if that typo is worth a pull
|
||||||
|
request? Do it! We will appreciate it.
|
||||||
|
|
||||||
|
If your pull request is not accepted on the first try, don't be
|
||||||
|
discouraged! If there's a problem with the implementation, hopefully you
|
||||||
|
received feedback on what to improve.
|
||||||
|
|
||||||
|
We're trying very hard to keep runc lean and focused. We don't want it
|
||||||
|
to do everything for everybody. This means that we might decide against
|
||||||
|
incorporating a new feature. However, there might be a way to implement
|
||||||
|
that feature *on top of* runc.
|
||||||
|
|
||||||
|
|
||||||
|
### Conventions
|
||||||
|
|
||||||
|
Fork the repo and make changes on your fork in a feature branch:
|
||||||
|
|
||||||
|
- If it's a bugfix branch, name it XXX-something where XXX is the number of the
|
||||||
|
issue
|
||||||
|
- If it's a feature branch, create an enhancement issue to announce your
|
||||||
|
intentions, and name it XXX-something where XXX is the number of the issue.
|
||||||
|
|
||||||
|
Submit unit tests for your changes. Go has a great test framework built in; use
|
||||||
|
it! Take a look at existing tests for inspiration. Run the full test suite on
|
||||||
|
your branch before submitting a pull request.
|
||||||
|
|
||||||
|
Update the documentation when creating or modifying features. Test
|
||||||
|
your documentation changes for clarity, concision, and correctness, as
|
||||||
|
well as a clean documentation build. See ``docs/README.md`` for more
|
||||||
|
information on building the docs and how docs get released.
|
||||||
|
|
||||||
|
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||||
|
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
|
||||||
|
committing your changes. Most editors have plugins that do this automatically.
|
||||||
|
|
||||||
|
Pull requests descriptions should be as clear as possible and include a
|
||||||
|
reference to all the issues that they address.
|
||||||
|
|
||||||
|
Pull requests must not contain commits from other users or branches.
|
||||||
|
|
||||||
|
Commit messages must start with a capitalized and short summary (max. 50
|
||||||
|
chars) written in the imperative, followed by an optional, more detailed
|
||||||
|
explanatory text which is separated from the summary by an empty line.
|
||||||
|
|
||||||
|
Code review comments may be added to your pull request. Discuss, then make the
|
||||||
|
suggested modifications and push additional commits to your feature branch. Be
|
||||||
|
sure to post a comment after pushing. The new commits will show up in the pull
|
||||||
|
request automatically, but the reviewers will not be notified unless you
|
||||||
|
comment.
|
||||||
|
|
||||||
|
Before the pull request is merged, make sure that you squash your commits into
|
||||||
|
logical units of work using `git rebase -i` and `git push -f`. After every
|
||||||
|
commit the test suite should be passing. Include documentation changes in the
|
||||||
|
same commit so that a revert would remove all traces of the feature or fix.
|
||||||
|
|
||||||
|
Commits that fix or close an issue should include a reference like `Closes #XXX`
|
||||||
|
or `Fixes #XXX`, which will automatically close the issue when merged.
|
||||||
|
|
||||||
|
### Sign your work
|
||||||
|
|
||||||
|
The sign-off is a simple line at the end of the explanation for the
|
||||||
|
patch, which certifies that you wrote it or otherwise have the right to
|
||||||
|
pass it on as an open-source patch. The rules are pretty simple: if you
|
||||||
|
can certify the below (from
|
||||||
|
[developercertificate.org](http://developercertificate.org/)):
|
||||||
|
|
||||||
|
```
|
||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
660 York Street, Suite 102,
|
||||||
|
San Francisco, CA 94110 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
|
then you just add a line to every git commit message:
|
||||||
|
|
||||||
|
Signed-off-by: Joe Smith <joe@gmail.com>
|
||||||
|
|
||||||
|
using your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||||
|
|
||||||
|
You can add the sign off when creating the git commit via `git commit -s`.
|
3
vendor/github.com/opencontainers/runc/Dockerfile
generated
vendored
Normal file
3
vendor/github.com/opencontainers/runc/Dockerfile
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
FROM runc_test
|
||||||
|
ADD . /go/src/github.com/opencontainers/runc
|
||||||
|
RUN make
|
191
vendor/github.com/opencontainers/runc/LICENSE
generated
vendored
Normal file
191
vendor/github.com/opencontainers/runc/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2014 Docker, Inc.
|
||||||
|
|
||||||
|
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.
|
9
vendor/github.com/opencontainers/runc/MAINTAINERS
generated
vendored
Normal file
9
vendor/github.com/opencontainers/runc/MAINTAINERS
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Michael Crosby <michael@docker.com> (@crosbymichael)
|
||||||
|
Rohit Jnagal <jnagal@google.com> (@rjnagal)
|
||||||
|
Victor Marmol <vmarmol@google.com> (@vmarmol)
|
||||||
|
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
|
||||||
|
Alexander Morozov <lk4d4@docker.com> (@LK4D4)
|
||||||
|
Daniel, Dao Quang Minh <dqminh89@gmail.com> (@dqminh)
|
||||||
|
Andrey Vagin <avagin@virtuozzo.com> (@avagin)
|
||||||
|
Qiang Huang <h.huangqiang@huawei.com> (@hqhq)
|
||||||
|
Aleksa Sarai <asarai@suse.de> (@cyphar)
|
120
vendor/github.com/opencontainers/runc/MAINTAINERS_GUIDE.md
generated
vendored
Normal file
120
vendor/github.com/opencontainers/runc/MAINTAINERS_GUIDE.md
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Dear maintainer. Thank you for investing the time and energy to help
|
||||||
|
make runc as useful as possible. Maintaining a project is difficult,
|
||||||
|
sometimes unrewarding work. Sure, you will get to contribute cool
|
||||||
|
features to the project. But most of your time will be spent reviewing,
|
||||||
|
cleaning up, documenting, answering questions, justifying design
|
||||||
|
decisions - while everyone has all the fun! But remember - the quality
|
||||||
|
of the maintainers work is what distinguishes the good projects from the
|
||||||
|
great. So please be proud of your work, even the unglamourous parts,
|
||||||
|
and encourage a culture of appreciation and respect for *every* aspect
|
||||||
|
of improving the project - not just the hot new features.
|
||||||
|
|
||||||
|
This document is a manual for maintainers old and new. It explains what
|
||||||
|
is expected of maintainers, how they should work, and what tools are
|
||||||
|
available to them.
|
||||||
|
|
||||||
|
This is a living document - if you see something out of date or missing,
|
||||||
|
speak up!
|
||||||
|
|
||||||
|
## What are a maintainer's responsibility?
|
||||||
|
|
||||||
|
It is every maintainer's responsibility to:
|
||||||
|
|
||||||
|
* 1) Expose a clear roadmap for improving their component.
|
||||||
|
* 2) Deliver prompt feedback and decisions on pull requests.
|
||||||
|
* 3) Be available to anyone with questions, bug reports, criticism etc.
|
||||||
|
on their component. This includes IRC and GitHub issues and pull requests.
|
||||||
|
* 4) Make sure their component respects the philosophy, design and
|
||||||
|
roadmap of the project.
|
||||||
|
|
||||||
|
## How are decisions made?
|
||||||
|
|
||||||
|
Short answer: with pull requests to the runc repository.
|
||||||
|
|
||||||
|
runc is an open-source project with an open design philosophy. This
|
||||||
|
means that the repository is the source of truth for EVERY aspect of the
|
||||||
|
project, including its philosophy, design, roadmap and APIs. *If it's
|
||||||
|
part of the project, it's in the repo. It's in the repo, it's part of
|
||||||
|
the project.*
|
||||||
|
|
||||||
|
As a result, all decisions can be expressed as changes to the
|
||||||
|
repository. An implementation change is a change to the source code. An
|
||||||
|
API change is a change to the API specification. A philosophy change is
|
||||||
|
a change to the philosophy manifesto. And so on.
|
||||||
|
|
||||||
|
All decisions affecting runc, big and small, follow the same 3 steps:
|
||||||
|
|
||||||
|
* Step 1: Open a pull request. Anyone can do this.
|
||||||
|
|
||||||
|
* Step 2: Discuss the pull request. Anyone can do this.
|
||||||
|
|
||||||
|
* Step 3: Accept (`LGTM`) or refuse a pull request. The relevant maintainers do
|
||||||
|
this (see below "Who decides what?")
|
||||||
|
|
||||||
|
### I'm a maintainer, should I make pull requests too?
|
||||||
|
|
||||||
|
Yes. Nobody should ever push to master directly. All changes should be
|
||||||
|
made through a pull request.
|
||||||
|
|
||||||
|
## Who decides what?
|
||||||
|
|
||||||
|
All decisions are pull requests, and the relevant maintainers make
|
||||||
|
decisions by accepting or refusing the pull request. Review and acceptance
|
||||||
|
by anyone is denoted by adding a comment in the pull request: `LGTM`.
|
||||||
|
However, only currently listed `MAINTAINERS` are counted towards the required
|
||||||
|
two LGTMs.
|
||||||
|
|
||||||
|
Overall the maintainer system works because of mutual respect across the
|
||||||
|
maintainers of the project. The maintainers trust one another to make decisions
|
||||||
|
in the best interests of the project. Sometimes maintainers can disagree and
|
||||||
|
this is part of a healthy project to represent the point of views of various people.
|
||||||
|
In the case where maintainers cannot find agreement on a specific change the
|
||||||
|
role of a Chief Maintainer comes into play.
|
||||||
|
|
||||||
|
The Chief Maintainer for the project is responsible for overall architecture
|
||||||
|
of the project to maintain conceptual integrity. Large decisions and
|
||||||
|
architecture changes should be reviewed by the chief maintainer.
|
||||||
|
The current chief maintainer for the project is Michael Crosby (@crosbymichael).
|
||||||
|
|
||||||
|
Even though the maintainer system is built on trust, if there is a conflict
|
||||||
|
with the chief maintainer on a decision, their decision can be challenged
|
||||||
|
and brought to the technical oversight board if two-thirds of the
|
||||||
|
maintainers vote for an appeal. It is expected that this would be a
|
||||||
|
very exceptional event.
|
||||||
|
|
||||||
|
|
||||||
|
### How are maintainers added?
|
||||||
|
|
||||||
|
The best maintainers have a vested interest in the project. Maintainers
|
||||||
|
are first and foremost contributors that have shown they are committed to
|
||||||
|
the long term success of the project. Contributors wanting to become
|
||||||
|
maintainers are expected to be deeply involved in contributing code,
|
||||||
|
pull request review, and triage of issues in the project for more than two months.
|
||||||
|
|
||||||
|
Just contributing does not make you a maintainer, it is about building trust
|
||||||
|
with the current maintainers of the project and being a person that they can
|
||||||
|
depend on and trust to make decisions in the best interest of the project. The
|
||||||
|
final vote to add a new maintainer should be approved by over 66% of the current
|
||||||
|
maintainers with the chief maintainer having veto power. In case of a veto,
|
||||||
|
conflict resolution rules expressed above apply. The voting period is
|
||||||
|
five business days on the Pull Request to add the new maintainer.
|
||||||
|
|
||||||
|
|
||||||
|
### What is expected of maintainers?
|
||||||
|
|
||||||
|
Part of a healthy project is to have active maintainers to support the community
|
||||||
|
in contributions and perform tasks to keep the project running. Maintainers are
|
||||||
|
expected to be able to respond in a timely manner if their help is required on specific
|
||||||
|
issues where they are pinged. Being a maintainer is a time consuming commitment and should
|
||||||
|
not be taken lightly.
|
||||||
|
|
||||||
|
When a maintainer is unable to perform the required duties they can be removed with
|
||||||
|
a vote by 66% of the current maintainers with the chief maintainer having veto power.
|
||||||
|
The voting period is ten business days. Issues related to a maintainer's performance should
|
||||||
|
be discussed with them among the other maintainers so that they are not surprised by
|
||||||
|
a pull request removing them.
|
||||||
|
|
||||||
|
|
||||||
|
|
57
vendor/github.com/opencontainers/runc/Makefile
generated
vendored
Normal file
57
vendor/github.com/opencontainers/runc/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
RUNC_IMAGE=runc_dev
|
||||||
|
RUNC_TEST_IMAGE=runc_test
|
||||||
|
PROJECT=github.com/opencontainers/runc
|
||||||
|
TEST_DOCKERFILE=script/test_Dockerfile
|
||||||
|
BUILDTAGS=seccomp
|
||||||
|
RUNC_BUILD_PATH=/go/src/github.com/opencontainers/runc/runc
|
||||||
|
RUNC_INSTANCE=runc_dev
|
||||||
|
COMMIT=$(shell git rev-parse HEAD 2> /dev/null || true)
|
||||||
|
RUNC_LINK=$(CURDIR)/Godeps/_workspace/src/github.com/opencontainers/runc
|
||||||
|
export GOPATH:=$(CURDIR)/Godeps/_workspace:$(GOPATH)
|
||||||
|
|
||||||
|
.PHONY=dbuild
|
||||||
|
|
||||||
|
all:
|
||||||
|
ifneq ($(RUNC_LINK), $(wildcard $(RUNC_LINK)))
|
||||||
|
ln -sfn $(CURDIR) $(RUNC_LINK)
|
||||||
|
endif
|
||||||
|
go build -ldflags "-X main.gitCommit=${COMMIT}" -tags "$(BUILDTAGS)" -o runc .
|
||||||
|
|
||||||
|
static:
|
||||||
|
CGO_ENABLED=1 go build -tags "$(BUILDTAGS) cgo static_build" -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT}" -o runc .
|
||||||
|
|
||||||
|
lint:
|
||||||
|
go vet ./...
|
||||||
|
go fmt ./...
|
||||||
|
|
||||||
|
runctestimage:
|
||||||
|
docker build -t $(RUNC_TEST_IMAGE) -f $(TEST_DOCKERFILE) .
|
||||||
|
|
||||||
|
test: runctestimage
|
||||||
|
docker run -e TESTFLAGS -ti --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_TEST_IMAGE) make localtest
|
||||||
|
tests/sniffTest
|
||||||
|
|
||||||
|
localtest: all
|
||||||
|
go test -tags "$(BUILDTAGS)" ${TESTFLAGS} -v ./...
|
||||||
|
|
||||||
|
dbuild: runctestimage
|
||||||
|
docker build -t $(RUNC_IMAGE) .
|
||||||
|
docker create --name=$(RUNC_INSTANCE) $(RUNC_IMAGE)
|
||||||
|
docker cp $(RUNC_INSTANCE):$(RUNC_BUILD_PATH) .
|
||||||
|
docker rm $(RUNC_INSTANCE)
|
||||||
|
|
||||||
|
install:
|
||||||
|
install -D -m0755 runc /usr/local/sbin/runc
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f /usr/local/sbin/runc
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f runc
|
||||||
|
rm -f $(RUNC_LINK)
|
||||||
|
|
||||||
|
validate:
|
||||||
|
script/validate-gofmt
|
||||||
|
go vet ./...
|
||||||
|
|
||||||
|
ci: validate localtest
|
17
vendor/github.com/opencontainers/runc/NOTICE
generated
vendored
Normal file
17
vendor/github.com/opencontainers/runc/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
runc
|
||||||
|
|
||||||
|
Copyright 2012-2015 Docker, Inc.
|
||||||
|
|
||||||
|
This product includes software developed at Docker, Inc. (http://www.docker.com).
|
||||||
|
|
||||||
|
The following is courtesy of our legal counsel:
|
||||||
|
|
||||||
|
|
||||||
|
Use and transfer of Docker may be subject to certain restrictions by the
|
||||||
|
United States and other governments.
|
||||||
|
It is your responsibility to ensure that your use and/or transfer does not
|
||||||
|
violate applicable laws.
|
||||||
|
|
||||||
|
For more information, please see http://www.bis.doc.gov
|
||||||
|
|
||||||
|
See also http://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
19
vendor/github.com/opencontainers/runc/PRINCIPLES.md
generated
vendored
Normal file
19
vendor/github.com/opencontainers/runc/PRINCIPLES.md
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# runc principles
|
||||||
|
|
||||||
|
In the design and development of runc and libcontainer we try to follow these principles:
|
||||||
|
|
||||||
|
(Work in progress)
|
||||||
|
|
||||||
|
* Don't try to replace every tool. Instead, be an ingredient to improve them.
|
||||||
|
* Less code is better.
|
||||||
|
* Fewer components are better. Do you really need to add one more class?
|
||||||
|
* 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand.
|
||||||
|
* Don't do later what you can do now. "//TODO: refactor" is not acceptable in new code.
|
||||||
|
* When hesitating between two options, choose the one that is easier to reverse.
|
||||||
|
* "No" is temporary; "Yes" is forever. If you're not sure about a new feature, say no. You can change your mind later.
|
||||||
|
* Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable.
|
||||||
|
* The fewer moving parts in a container, the better.
|
||||||
|
* Don't merge it unless you document it.
|
||||||
|
* Don't document it unless you can keep it up-to-date.
|
||||||
|
* Don't merge it unless you test it!
|
||||||
|
* Everyone's problem is slightly different. Focus on the part that is the same for everyone, and solve that.
|
144
vendor/github.com/opencontainers/runc/README.md
generated
vendored
Normal file
144
vendor/github.com/opencontainers/runc/README.md
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
[](https://jenkins.dockerproject.org/job/runc Master)
|
||||||
|
|
||||||
|
## runc
|
||||||
|
|
||||||
|
`runc` is a CLI tool for spawning and running containers according to the OCF specification.
|
||||||
|
|
||||||
|
## State of the project
|
||||||
|
|
||||||
|
Currently `runc` is an implementation of the OCI specification. We are currently sprinting
|
||||||
|
to have a v1 of the spec out. So the `runc` config format will be constantly changing until
|
||||||
|
the spec is finalized. However, we encourage you to try out the tool and give feedback.
|
||||||
|
|
||||||
|
### OCF
|
||||||
|
|
||||||
|
How does `runc` integrate with the Open Container Initiative Specification?
|
||||||
|
`runc` depends on the types specified in the
|
||||||
|
[specs](https://github.com/opencontainers/runtime-spec) repository. Whenever the
|
||||||
|
specification is updated and ready to be versioned `runc` will update its dependency
|
||||||
|
on the specs repository and support the update spec.
|
||||||
|
|
||||||
|
### Building:
|
||||||
|
|
||||||
|
At the time of writing, runc only builds on the Linux platform.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create a 'github.com/opencontainers' in your GOPATH/src
|
||||||
|
cd github.com/opencontainers
|
||||||
|
git clone https://github.com/opencontainers/runc
|
||||||
|
cd runc
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to enable seccomp support you will need to install libseccomp on your platform.
|
||||||
|
If you do not want to build `runc` with seccomp support you can add `BUILDTAGS=""` when running make.
|
||||||
|
|
||||||
|
#### Build Tags
|
||||||
|
|
||||||
|
`runc` supports optional build tags for compiling in support for various features.
|
||||||
|
|
||||||
|
|
||||||
|
| Build Tag | Feature | Dependency |
|
||||||
|
|-----------|------------------------------------|-------------|
|
||||||
|
| seccomp | Syscall filtering | libseccomp |
|
||||||
|
| selinux | selinux process and mount labeling | <none> |
|
||||||
|
| apparmor | apparmor profile support | libapparmor |
|
||||||
|
|
||||||
|
### Testing:
|
||||||
|
|
||||||
|
You can run tests for runC by using command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that test cases are run in Docker container, so you need to install
|
||||||
|
`docker` first. And test requires mounting cgroups inside container, it's
|
||||||
|
done by docker now, so you need a docker version newer than 1.8.0-rc2.
|
||||||
|
|
||||||
|
You can also run specific test cases by:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# make test TESTFLAGS="-run=SomeTestFunction"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using:
|
||||||
|
|
||||||
|
To run a container with the id "test", execute `runc start` with the containers id as arg one
|
||||||
|
in the bundle's root directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
runc start test
|
||||||
|
/ $ ps
|
||||||
|
PID USER COMMAND
|
||||||
|
1 daemon sh
|
||||||
|
5 daemon sh
|
||||||
|
/ $
|
||||||
|
```
|
||||||
|
|
||||||
|
### OCI Container JSON Format:
|
||||||
|
|
||||||
|
OCI container JSON format is based on OCI [specs](https://github.com/opencontainers/runtime-spec).
|
||||||
|
You can generate JSON files by using `runc spec`.
|
||||||
|
It assumes that the file-system is found in a directory called
|
||||||
|
`rootfs` and there is a user with uid and gid of `0` defined within that file-system.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
|
||||||
|
#### Using a Docker image (requires version 1.3 or later)
|
||||||
|
|
||||||
|
To test using Docker's `busybox` image follow these steps:
|
||||||
|
* Install `docker` and download the `busybox` image: `docker pull busybox`
|
||||||
|
* Create a container from that image and export its contents to a tar file:
|
||||||
|
`docker export $(docker create busybox) > busybox.tar`
|
||||||
|
* Untar the contents to create your filesystem directory:
|
||||||
|
```
|
||||||
|
mkdir rootfs
|
||||||
|
tar -C rootfs -xf busybox.tar
|
||||||
|
```
|
||||||
|
* Create `config.json` by using `runc spec`.
|
||||||
|
* Execute `runc start` and you should be placed into a shell where you can run `ps`:
|
||||||
|
```
|
||||||
|
$ runc start test
|
||||||
|
/ # ps
|
||||||
|
PID USER COMMAND
|
||||||
|
1 root sh
|
||||||
|
9 root ps
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using runc with systemd
|
||||||
|
|
||||||
|
To use runc with systemd, you can create a unit file
|
||||||
|
`/usr/lib/systemd/system/minecraft.service` as below (edit your
|
||||||
|
own Description or WorkingDirectory or service name as you need).
|
||||||
|
|
||||||
|
```service
|
||||||
|
[Unit]
|
||||||
|
Description=Minecraft Build Server
|
||||||
|
Documentation=http://minecraft.net
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
CPUQuota=200%
|
||||||
|
MemoryLimit=1536M
|
||||||
|
ExecStart=/usr/local/bin/runc start minecraft
|
||||||
|
Restart=on-failure
|
||||||
|
WorkingDirectory=/containers/minecraftbuild
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure you have the bundle's root directory and JSON configs in
|
||||||
|
your WorkingDirectory, then use systemd commands to start the service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl start minecraft.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that if you use JSON configs by `runc spec`, you need to modify
|
||||||
|
`config.json` and change `process.terminal` to false so runc won't
|
||||||
|
create tty, because we can't set terminal from the stdin when using
|
||||||
|
systemd service.
|
89
vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
Normal file
89
vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var checkpointCommand = cli.Command{
|
||||||
|
Name: "checkpoint",
|
||||||
|
Usage: "checkpoint a running container",
|
||||||
|
ArgsUsage: `<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.`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
|
||||||
|
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||||||
|
cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
|
||||||
|
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
||||||
|
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
||||||
|
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
||||||
|
cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
|
||||||
|
cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"},
|
||||||
|
cli.StringFlag{Name: "manage-cgroups-mode", Value: "", Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'."},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
defer destroy(container)
|
||||||
|
options := criuOptions(context)
|
||||||
|
// these are the mandatory criu options for a container
|
||||||
|
setPageServer(context, options)
|
||||||
|
setManageCgroupsMode(context, options)
|
||||||
|
if err := container.Checkpoint(options); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCheckpointImagePath(context *cli.Context) string {
|
||||||
|
imagePath := context.String("image-path")
|
||||||
|
if imagePath == "" {
|
||||||
|
imagePath = getDefaultImagePath(context)
|
||||||
|
}
|
||||||
|
return imagePath
|
||||||
|
}
|
||||||
|
|
||||||
|
func setPageServer(context *cli.Context, options *libcontainer.CriuOpts) {
|
||||||
|
// xxx following criu opts are optional
|
||||||
|
// The dump image can be sent to a criu page server
|
||||||
|
if psOpt := context.String("page-server"); psOpt != "" {
|
||||||
|
addressPort := strings.Split(psOpt, ":")
|
||||||
|
if len(addressPort) != 2 {
|
||||||
|
fatal(fmt.Errorf("Use --page-server ADDRESS:PORT to specify page server"))
|
||||||
|
}
|
||||||
|
portInt, err := strconv.Atoi(addressPort[1])
|
||||||
|
if err != nil {
|
||||||
|
fatal(fmt.Errorf("Invalid port number"))
|
||||||
|
}
|
||||||
|
options.PageServer = libcontainer.CriuPageServerInfo{
|
||||||
|
Address: addressPort[0],
|
||||||
|
Port: int32(portInt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setManageCgroupsMode(context *cli.Context, options *libcontainer.CriuOpts) {
|
||||||
|
if cgOpt := context.String("manage-cgroups-mode"); cgOpt != "" {
|
||||||
|
switch cgOpt {
|
||||||
|
case "soft":
|
||||||
|
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_SOFT
|
||||||
|
case "full":
|
||||||
|
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_FULL
|
||||||
|
case "strict":
|
||||||
|
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_STRICT
|
||||||
|
default:
|
||||||
|
fatal(fmt.Errorf("Invalid manage cgroups mode"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
vendor/github.com/opencontainers/runc/delete.go
generated
vendored
Normal file
38
vendor/github.com/opencontainers/runc/delete.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var deleteCommand = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Usage: "delete any resources held by the container often used with detached containers",
|
||||||
|
ArgsUsage: `<container-id>
|
||||||
|
|
||||||
|
Where "<container-id>" is the name for the instance of the container.
|
||||||
|
|
||||||
|
For example, if the container id is "ubuntu01" and runc list currently shows the
|
||||||
|
status of "ubuntu01" as "destroyed" the following will delete resources held for
|
||||||
|
"ubuntu01" removing "ubuntu01" from the runc list of containers:
|
||||||
|
|
||||||
|
# runc delete ubuntu01`,
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
if lerr, ok := err.(libcontainer.Error); ok && lerr.Code() == libcontainer.ContainerNotExists {
|
||||||
|
// if there was an aborted start or something of the sort then the container's directory could exist but
|
||||||
|
// libcontainer does not see it because the state.json file inside that directory was never created.
|
||||||
|
path := filepath.Join(context.GlobalString("root"), context.Args().First())
|
||||||
|
if err := os.RemoveAll(path); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
destroy(container)
|
||||||
|
},
|
||||||
|
}
|
100
vendor/github.com/opencontainers/runc/events.go
generated
vendored
Normal file
100
vendor/github.com/opencontainers/runc/events.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// event struct for encoding the event data to json.
|
||||||
|
type event struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Data interface{} `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventsCommand = cli.Command{
|
||||||
|
Name: "events",
|
||||||
|
Usage: "display container events such as OOM notifications, cpu, memory, IO and network stats",
|
||||||
|
ArgsUsage: `<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.`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.DurationFlag{Name: "interval", Value: 5 * time.Second, Usage: "set the stats collection interval"},
|
||||||
|
cli.BoolFlag{Name: "stats", Usage: "display the container's stats then exit"},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
stats = make(chan *libcontainer.Stats, 1)
|
||||||
|
events = make(chan *event, 1024)
|
||||||
|
group = &sync.WaitGroup{}
|
||||||
|
)
|
||||||
|
group.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer group.Done()
|
||||||
|
enc := json.NewEncoder(os.Stdout)
|
||||||
|
for e := range events {
|
||||||
|
if err := enc.Encode(e); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if context.Bool("stats") {
|
||||||
|
s, err := container.Stats()
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
events <- &event{Type: "stats", ID: container.ID(), Data: s}
|
||||||
|
close(events)
|
||||||
|
group.Wait()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(context.Duration("interval")) {
|
||||||
|
s, err := container.Stats()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stats <- s
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
n, err := container.NotifyOOM()
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case _, ok := <-n:
|
||||||
|
if ok {
|
||||||
|
// this means an oom event was received, if it is !ok then
|
||||||
|
// the channel was closed because the container stopped and
|
||||||
|
// the cgroups no longer exist.
|
||||||
|
events <- &event{Type: "oom", ID: container.ID()}
|
||||||
|
} else {
|
||||||
|
n = nil
|
||||||
|
}
|
||||||
|
case s := <-stats:
|
||||||
|
events <- &event{Type: "stats", ID: container.ID(), Data: s}
|
||||||
|
}
|
||||||
|
if n == nil {
|
||||||
|
close(events)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.Wait()
|
||||||
|
},
|
||||||
|
}
|
187
vendor/github.com/opencontainers/runc/exec.go
generated
vendored
Normal file
187
vendor/github.com/opencontainers/runc/exec.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var execCommand = cli.Command{
|
||||||
|
Name: "exec",
|
||||||
|
Usage: "execute new process inside the container",
|
||||||
|
ArgsUsage: `<container-id> <container command>
|
||||||
|
|
||||||
|
Where "<container-id>" is the name for the instance of the container and
|
||||||
|
"<container command>" is the command to be executed in the container.
|
||||||
|
|
||||||
|
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`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "console",
|
||||||
|
Usage: "specify the pty slave path for use with the container",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "cwd",
|
||||||
|
Usage: "current working directory in the container",
|
||||||
|
},
|
||||||
|
cli.StringSliceFlag{
|
||||||
|
Name: "env, e",
|
||||||
|
Usage: "set environment variables",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "tty, t",
|
||||||
|
Usage: "allocate a pseudo-TTY",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "user, u",
|
||||||
|
Usage: "UID (format: <uid>[:<gid>])",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "process, p",
|
||||||
|
Usage: "path to the process.json",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "detach,d",
|
||||||
|
Usage: "detach from the container's process",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "pid-file",
|
||||||
|
Value: "",
|
||||||
|
Usage: "specify the file to write the process id to",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "process-label",
|
||||||
|
Usage: "set the asm process label for the process commonly used with selinux",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "apparmor",
|
||||||
|
Usage: "set the apparmor profile for the process",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "no-new-privs",
|
||||||
|
Usage: "set the no new privileges value for the process",
|
||||||
|
},
|
||||||
|
cli.StringSliceFlag{
|
||||||
|
Name: "cap, c",
|
||||||
|
Value: &cli.StringSlice{},
|
||||||
|
Usage: "add a capability to the bounding set for the process",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "no-subreaper",
|
||||||
|
Usage: "disable the use of the subreaper used to reap reparented processes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
if os.Geteuid() != 0 {
|
||||||
|
fatalf("runc should be run as root")
|
||||||
|
}
|
||||||
|
status, err := execProcess(context)
|
||||||
|
if err != nil {
|
||||||
|
fatalf("exec failed: %v", err)
|
||||||
|
}
|
||||||
|
os.Exit(status)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func execProcess(context *cli.Context) (int, error) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
detach := context.Bool("detach")
|
||||||
|
state, err := container.State()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
bundle := utils.SearchLabels(state.Config.Labels, "bundle")
|
||||||
|
p, err := getProcess(context, bundle)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
r := &runner{
|
||||||
|
enableSubreaper: !context.Bool("no-subreaper"),
|
||||||
|
shouldDestroy: false,
|
||||||
|
container: container,
|
||||||
|
console: context.String("console"),
|
||||||
|
detach: detach,
|
||||||
|
pidFile: context.String("pid-file"),
|
||||||
|
}
|
||||||
|
return r.run(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProcess(context *cli.Context, bundle string) (*specs.Process, error) {
|
||||||
|
if path := context.String("process"); path != "" {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var p specs.Process
|
||||||
|
if err := json.NewDecoder(f).Decode(&p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &p, validateProcessSpec(&p)
|
||||||
|
}
|
||||||
|
// process via cli flags
|
||||||
|
if err := os.Chdir(bundle); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
spec, err := loadSpec(specConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := spec.Process
|
||||||
|
p.Args = context.Args()[1:]
|
||||||
|
// override the cwd, if passed
|
||||||
|
if context.String("cwd") != "" {
|
||||||
|
p.Cwd = context.String("cwd")
|
||||||
|
}
|
||||||
|
if ap := context.String("apparmor"); ap != "" {
|
||||||
|
p.ApparmorProfile = ap
|
||||||
|
}
|
||||||
|
if l := context.String("process-label"); l != "" {
|
||||||
|
p.SelinuxLabel = l
|
||||||
|
}
|
||||||
|
if caps := context.StringSlice("cap"); len(caps) > 0 {
|
||||||
|
p.Capabilities = caps
|
||||||
|
}
|
||||||
|
// append the passed env variables
|
||||||
|
for _, e := range context.StringSlice("env") {
|
||||||
|
p.Env = append(p.Env, e)
|
||||||
|
}
|
||||||
|
// set the tty
|
||||||
|
if context.IsSet("tty") {
|
||||||
|
p.Terminal = context.Bool("tty")
|
||||||
|
}
|
||||||
|
if context.IsSet("no-new-privs") {
|
||||||
|
p.NoNewPrivileges = context.Bool("no-new-privs")
|
||||||
|
}
|
||||||
|
// override the user, if passed
|
||||||
|
if context.String("user") != "" {
|
||||||
|
u := strings.SplitN(context.String("user"), ":", 2)
|
||||||
|
if len(u) > 1 {
|
||||||
|
gid, err := strconv.Atoi(u[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing %s as int for gid failed: %v", u[1], err)
|
||||||
|
}
|
||||||
|
p.User.GID = uint32(gid)
|
||||||
|
}
|
||||||
|
uid, err := strconv.Atoi(u[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing %s as int for uid failed: %v", u[0], err)
|
||||||
|
}
|
||||||
|
p.User.UID = uint32(uid)
|
||||||
|
}
|
||||||
|
return &p, nil
|
||||||
|
}
|
96
vendor/github.com/opencontainers/runc/kill.go
generated
vendored
Normal file
96
vendor/github.com/opencontainers/runc/kill.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var signalMap = map[string]syscall.Signal{
|
||||||
|
"ABRT": syscall.SIGABRT,
|
||||||
|
"ALRM": syscall.SIGALRM,
|
||||||
|
"BUS": syscall.SIGBUS,
|
||||||
|
"CHLD": syscall.SIGCHLD,
|
||||||
|
"CLD": syscall.SIGCLD,
|
||||||
|
"CONT": syscall.SIGCONT,
|
||||||
|
"FPE": syscall.SIGFPE,
|
||||||
|
"HUP": syscall.SIGHUP,
|
||||||
|
"ILL": syscall.SIGILL,
|
||||||
|
"INT": syscall.SIGINT,
|
||||||
|
"IO": syscall.SIGIO,
|
||||||
|
"IOT": syscall.SIGIOT,
|
||||||
|
"KILL": syscall.SIGKILL,
|
||||||
|
"PIPE": syscall.SIGPIPE,
|
||||||
|
"POLL": syscall.SIGPOLL,
|
||||||
|
"PROF": syscall.SIGPROF,
|
||||||
|
"PWR": syscall.SIGPWR,
|
||||||
|
"QUIT": syscall.SIGQUIT,
|
||||||
|
"SEGV": syscall.SIGSEGV,
|
||||||
|
"STKFLT": syscall.SIGSTKFLT,
|
||||||
|
"STOP": syscall.SIGSTOP,
|
||||||
|
"SYS": syscall.SIGSYS,
|
||||||
|
"TERM": syscall.SIGTERM,
|
||||||
|
"TRAP": syscall.SIGTRAP,
|
||||||
|
"TSTP": syscall.SIGTSTP,
|
||||||
|
"TTIN": syscall.SIGTTIN,
|
||||||
|
"TTOU": syscall.SIGTTOU,
|
||||||
|
"UNUSED": syscall.SIGUNUSED,
|
||||||
|
"URG": syscall.SIGURG,
|
||||||
|
"USR1": syscall.SIGUSR1,
|
||||||
|
"USR2": syscall.SIGUSR2,
|
||||||
|
"VTALRM": syscall.SIGVTALRM,
|
||||||
|
"WINCH": syscall.SIGWINCH,
|
||||||
|
"XCPU": syscall.SIGXCPU,
|
||||||
|
"XFSZ": syscall.SIGXFSZ,
|
||||||
|
}
|
||||||
|
|
||||||
|
var killCommand = cli.Command{
|
||||||
|
Name: "kill",
|
||||||
|
Usage: "kill sends the specified signal (default: SIGTERM) to the container's init process",
|
||||||
|
ArgsUsage: `<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.
|
||||||
|
|
||||||
|
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`,
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigstr := context.Args().Get(1)
|
||||||
|
if sigstr == "" {
|
||||||
|
sigstr = "SIGTERM"
|
||||||
|
}
|
||||||
|
|
||||||
|
signal, err := parseSignal(sigstr)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := container.Signal(signal); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSignal(rawSignal string) (syscall.Signal, error) {
|
||||||
|
s, err := strconv.Atoi(rawSignal)
|
||||||
|
if err == nil {
|
||||||
|
return syscall.Signal(s), nil
|
||||||
|
}
|
||||||
|
signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
|
||||||
|
if !ok {
|
||||||
|
return -1, fmt.Errorf("unknown signal %q", rawSignal)
|
||||||
|
}
|
||||||
|
return signal, nil
|
||||||
|
}
|
241
vendor/github.com/opencontainers/runc/libcontainer/README.md
generated
vendored
Normal file
241
vendor/github.com/opencontainers/runc/libcontainer/README.md
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
Libcontainer provides a native Go implementation for creating containers
|
||||||
|
with namespaces, cgroups, capabilities, and filesystem access controls.
|
||||||
|
It allows you to manage the lifecycle of the container performing additional operations
|
||||||
|
after the container is created.
|
||||||
|
|
||||||
|
|
||||||
|
#### Container
|
||||||
|
A container is a self contained execution environment that shares the kernel of the
|
||||||
|
host system and which is (optionally) isolated from other containers in the system.
|
||||||
|
|
||||||
|
#### Using libcontainer
|
||||||
|
|
||||||
|
Because containers are spawned in a two step process you will need a binary that
|
||||||
|
will be executed as the init process for the container. In libcontainer, we use
|
||||||
|
the current binary (/proc/self/exe) to be executed as the init process, and use
|
||||||
|
arg "init", we call the first step process "bootstrap", so you always need a "init"
|
||||||
|
function as the entry of "bootstrap".
|
||||||
|
|
||||||
|
```go
|
||||||
|
func init() {
|
||||||
|
if len(os.Args) > 1 && os.Args[1] == "init" {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
runtime.LockOSThread()
|
||||||
|
factory, _ := libcontainer.New("")
|
||||||
|
if err := factory.StartInitialization(); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
panic("--this line should have never been executed, congratulations--")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then to create a container you first have to initialize an instance of a factory
|
||||||
|
that will handle the creation and initialization for a container.
|
||||||
|
|
||||||
|
```go
|
||||||
|
factory, err := libcontainer.New("/var/lib/container", libcontainer.Cgroupfs, libcontainer.InitArgs(os.Args[0], "init"))
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have an instance of the factory created we can create a configuration
|
||||||
|
struct describing how the container is to be created. A sample would look similar to this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
||||||
|
config := &configs.Config{
|
||||||
|
Rootfs: "/your/path/to/rootfs",
|
||||||
|
Capabilities: []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.NEWUSER},
|
||||||
|
{Type: configs.NEWNET},
|
||||||
|
}),
|
||||||
|
Cgroups: &configs.Cgroup{
|
||||||
|
Name: "test-container",
|
||||||
|
Parent: "system",
|
||||||
|
Resources: &configs.Resources{
|
||||||
|
MemorySwappiness: nil,
|
||||||
|
AllowAllDevices: false,
|
||||||
|
AllowedDevices: configs.DefaultAllowedDevices,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaskPaths: []string{
|
||||||
|
"/proc/kcore",
|
||||||
|
},
|
||||||
|
ReadonlyPaths: []string{
|
||||||
|
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||||
|
},
|
||||||
|
Devices: configs.DefaultAutoCreatedDevices,
|
||||||
|
Hostname: "testing",
|
||||||
|
Mounts: []*configs.Mount{
|
||||||
|
{
|
||||||
|
Source: "proc",
|
||||||
|
Destination: "/proc",
|
||||||
|
Device: "proc",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "tmpfs",
|
||||||
|
Destination: "/dev",
|
||||||
|
Device: "tmpfs",
|
||||||
|
Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME,
|
||||||
|
Data: "mode=755",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "devpts",
|
||||||
|
Destination: "/dev/pts",
|
||||||
|
Device: "devpts",
|
||||||
|
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
|
||||||
|
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Device: "tmpfs",
|
||||||
|
Source: "shm",
|
||||||
|
Destination: "/dev/shm",
|
||||||
|
Data: "mode=1777,size=65536k",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "mqueue",
|
||||||
|
Destination: "/dev/mqueue",
|
||||||
|
Device: "mqueue",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "sysfs",
|
||||||
|
Destination: "/sys",
|
||||||
|
Device: "sysfs",
|
||||||
|
Flags: defaultMountFlags | syscall.MS_RDONLY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UidMappings: []configs.IDMap{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 1000,
|
||||||
|
Size: 65536,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GidMappings: []configs.IDMap{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 1000,
|
||||||
|
Size: 65536,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Networks: []*configs.Network{
|
||||||
|
{
|
||||||
|
Type: "loopback",
|
||||||
|
Address: "127.0.0.1/0",
|
||||||
|
Gateway: "localhost",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Rlimits: []configs.Rlimit{
|
||||||
|
{
|
||||||
|
Type: syscall.RLIMIT_NOFILE,
|
||||||
|
Hard: uint64(1025),
|
||||||
|
Soft: uint64(1025),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have the configuration populated you can create a container:
|
||||||
|
|
||||||
|
```go
|
||||||
|
container, err := factory.Create("container-id", config)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To spawn bash as the initial process inside the container and have the
|
||||||
|
processes pid returned in order to wait, signal, or kill the process:
|
||||||
|
|
||||||
|
```go
|
||||||
|
process := &libcontainer.Process{
|
||||||
|
Args: []string{"/bin/bash"},
|
||||||
|
Env: []string{"PATH=/bin"},
|
||||||
|
User: "daemon",
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := container.Start(process)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
container.Destroy()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the process to finish.
|
||||||
|
_, err := process.Wait()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroy the container.
|
||||||
|
container.Destroy()
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional ways to interact with a running container are:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// return all the pids for all processes running inside the container.
|
||||||
|
processes, err := container.Processes()
|
||||||
|
|
||||||
|
// get detailed cpu, memory, io, and network statistics for the container and
|
||||||
|
// it's processes.
|
||||||
|
stats, err := container.Stats()
|
||||||
|
|
||||||
|
// pause all processes inside the container.
|
||||||
|
container.Pause()
|
||||||
|
|
||||||
|
// resume all paused processes.
|
||||||
|
container.Resume()
|
||||||
|
|
||||||
|
// send signal to container's init process.
|
||||||
|
container.Signal(signal)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Checkpoint & Restore
|
||||||
|
|
||||||
|
libcontainer now integrates [CRIU](http://criu.org/) for checkpointing and restoring containers.
|
||||||
|
This let's you save the state of a process running inside a container to disk, and then restore
|
||||||
|
that state into a new process, on the same machine or on another machine.
|
||||||
|
|
||||||
|
`criu` version 1.5.2 or higher is required to use checkpoint and restore.
|
||||||
|
If you don't already have `criu` installed, you can build it from source, following the
|
||||||
|
[online instructions](http://criu.org/Installation). `criu` is also installed in the docker image
|
||||||
|
generated when building libcontainer with docker.
|
||||||
|
|
||||||
|
|
||||||
|
## Copyright and license
|
||||||
|
|
||||||
|
Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license.
|
||||||
|
Docs released under Creative commons.
|
||||||
|
|
335
vendor/github.com/opencontainers/runc/libcontainer/SPEC.md
generated
vendored
Normal file
335
vendor/github.com/opencontainers/runc/libcontainer/SPEC.md
generated
vendored
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
## Container Specification - v1
|
||||||
|
|
||||||
|
This is the standard configuration for version 1 containers. It includes
|
||||||
|
namespaces, standard filesystem setup, a default Linux capability set, and
|
||||||
|
information about resource reservations. It also has information about any
|
||||||
|
populated environment settings for the processes running inside a container.
|
||||||
|
|
||||||
|
Along with the configuration of how a container is created the standard also
|
||||||
|
discusses actions that can be performed on a container to manage and inspect
|
||||||
|
information about the processes running inside.
|
||||||
|
|
||||||
|
The v1 profile is meant to be able to accommodate the majority of applications
|
||||||
|
with a strong security configuration.
|
||||||
|
|
||||||
|
### System Requirements and Compatibility
|
||||||
|
|
||||||
|
Minimum requirements:
|
||||||
|
* Kernel version - 3.10 recommended 2.6.2x minimum(with backported patches)
|
||||||
|
* Mounted cgroups with each subsystem in its own hierarchy
|
||||||
|
|
||||||
|
|
||||||
|
### Namespaces
|
||||||
|
|
||||||
|
| Flag | Enabled |
|
||||||
|
| ------------ | ------- |
|
||||||
|
| CLONE_NEWPID | 1 |
|
||||||
|
| CLONE_NEWUTS | 1 |
|
||||||
|
| CLONE_NEWIPC | 1 |
|
||||||
|
| CLONE_NEWNET | 1 |
|
||||||
|
| CLONE_NEWNS | 1 |
|
||||||
|
| CLONE_NEWUSER | 1 |
|
||||||
|
|
||||||
|
Namespaces are created for the container via the `clone` syscall.
|
||||||
|
|
||||||
|
|
||||||
|
### Filesystem
|
||||||
|
|
||||||
|
A root filesystem must be provided to a container for execution. The container
|
||||||
|
will use this root filesystem (rootfs) to jail and spawn processes inside where
|
||||||
|
the binaries and system libraries are local to that directory. Any binaries
|
||||||
|
to be executed must be contained within this rootfs.
|
||||||
|
|
||||||
|
Mounts that happen inside the container are automatically cleaned up when the
|
||||||
|
container exits as the mount namespace is destroyed and the kernel will
|
||||||
|
unmount all the mounts that were setup within that namespace.
|
||||||
|
|
||||||
|
For a container to execute properly there are certain filesystems that
|
||||||
|
are required to be mounted within the rootfs that the runtime will setup.
|
||||||
|
|
||||||
|
| Path | Type | Flags | Data |
|
||||||
|
| ----------- | ------ | -------------------------------------- | ---------------------------------------- |
|
||||||
|
| /proc | proc | MS_NOEXEC,MS_NOSUID,MS_NODEV | |
|
||||||
|
| /dev | tmpfs | MS_NOEXEC,MS_STRICTATIME | mode=755 |
|
||||||
|
| /dev/shm | tmpfs | MS_NOEXEC,MS_NOSUID,MS_NODEV | mode=1777,size=65536k |
|
||||||
|
| /dev/mqueue | mqueue | MS_NOEXEC,MS_NOSUID,MS_NODEV | |
|
||||||
|
| /dev/pts | devpts | MS_NOEXEC,MS_NOSUID | newinstance,ptmxmode=0666,mode=620,gid=5 |
|
||||||
|
| /sys | sysfs | MS_NOEXEC,MS_NOSUID,MS_NODEV,MS_RDONLY | |
|
||||||
|
|
||||||
|
|
||||||
|
After a container's filesystems are mounted within the newly created
|
||||||
|
mount namespace `/dev` will need to be populated with a set of device nodes.
|
||||||
|
It is expected that a rootfs does not need to have any device nodes specified
|
||||||
|
for `/dev` within the rootfs as the container will setup the correct devices
|
||||||
|
that are required for executing a container's process.
|
||||||
|
|
||||||
|
| Path | Mode | Access |
|
||||||
|
| ------------ | ---- | ---------- |
|
||||||
|
| /dev/null | 0666 | rwm |
|
||||||
|
| /dev/zero | 0666 | rwm |
|
||||||
|
| /dev/full | 0666 | rwm |
|
||||||
|
| /dev/tty | 0666 | rwm |
|
||||||
|
| /dev/random | 0666 | rwm |
|
||||||
|
| /dev/urandom | 0666 | rwm |
|
||||||
|
| /dev/fuse | 0666 | rwm |
|
||||||
|
|
||||||
|
|
||||||
|
**ptmx**
|
||||||
|
`/dev/ptmx` will need to be a symlink to the host's `/dev/ptmx` within
|
||||||
|
the container.
|
||||||
|
|
||||||
|
The use of a pseudo TTY is optional within a container and it should support both.
|
||||||
|
If a pseudo is provided to the container `/dev/console` will need to be
|
||||||
|
setup by binding the console in `/dev/` after it has been populated and mounted
|
||||||
|
in tmpfs.
|
||||||
|
|
||||||
|
| Source | Destination | UID GID | Mode | Type |
|
||||||
|
| --------------- | ------------ | ------- | ---- | ---- |
|
||||||
|
| *pty host path* | /dev/console | 0 0 | 0600 | bind |
|
||||||
|
|
||||||
|
|
||||||
|
After `/dev/null` has been setup we check for any external links between
|
||||||
|
the container's io, STDIN, STDOUT, STDERR. If the container's io is pointing
|
||||||
|
to `/dev/null` outside the container we close and `dup2` the the `/dev/null`
|
||||||
|
that is local to the container's rootfs.
|
||||||
|
|
||||||
|
|
||||||
|
After the container has `/proc` mounted a few standard symlinks are setup
|
||||||
|
within `/dev/` for the io.
|
||||||
|
|
||||||
|
| Source | Destination |
|
||||||
|
| --------------- | ----------- |
|
||||||
|
| /proc/self/fd | /dev/fd |
|
||||||
|
| /proc/self/fd/0 | /dev/stdin |
|
||||||
|
| /proc/self/fd/1 | /dev/stdout |
|
||||||
|
| /proc/self/fd/2 | /dev/stderr |
|
||||||
|
|
||||||
|
A `pivot_root` is used to change the root for the process, effectively
|
||||||
|
jailing the process inside the rootfs.
|
||||||
|
|
||||||
|
```c
|
||||||
|
put_old = mkdir(...);
|
||||||
|
pivot_root(rootfs, put_old);
|
||||||
|
chdir("/");
|
||||||
|
unmount(put_old, MS_DETACH);
|
||||||
|
rmdir(put_old);
|
||||||
|
```
|
||||||
|
|
||||||
|
For container's running with a rootfs inside `ramfs` a `MS_MOVE` combined
|
||||||
|
with a `chroot` is required as `pivot_root` is not supported in `ramfs`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
mount(rootfs, "/", NULL, MS_MOVE, NULL);
|
||||||
|
chroot(".");
|
||||||
|
chdir("/");
|
||||||
|
```
|
||||||
|
|
||||||
|
The `umask` is set back to `0022` after the filesystem setup has been completed.
|
||||||
|
|
||||||
|
### Resources
|
||||||
|
|
||||||
|
Cgroups are used to handle resource allocation for containers. This includes
|
||||||
|
system resources like cpu, memory, and device access.
|
||||||
|
|
||||||
|
| Subsystem | Enabled |
|
||||||
|
| ---------- | ------- |
|
||||||
|
| devices | 1 |
|
||||||
|
| memory | 1 |
|
||||||
|
| cpu | 1 |
|
||||||
|
| cpuacct | 1 |
|
||||||
|
| cpuset | 1 |
|
||||||
|
| blkio | 1 |
|
||||||
|
| perf_event | 1 |
|
||||||
|
| freezer | 1 |
|
||||||
|
| hugetlb | 1 |
|
||||||
|
| pids | 1 |
|
||||||
|
|
||||||
|
|
||||||
|
All cgroup subsystem are joined so that statistics can be collected from
|
||||||
|
each of the subsystems. Freezer does not expose any stats but is joined
|
||||||
|
so that containers can be paused and resumed.
|
||||||
|
|
||||||
|
The parent process of the container's init must place the init pid inside
|
||||||
|
the correct cgroups before the initialization begins. This is done so
|
||||||
|
that no processes or threads escape the cgroups. This sync is
|
||||||
|
done via a pipe ( specified in the runtime section below ) that the container's
|
||||||
|
init process will block waiting for the parent to finish setup.
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
The standard set of Linux capabilities that are set in a container
|
||||||
|
provide a good default for security and flexibility for the applications.
|
||||||
|
|
||||||
|
|
||||||
|
| Capability | Enabled |
|
||||||
|
| -------------------- | ------- |
|
||||||
|
| CAP_NET_RAW | 1 |
|
||||||
|
| CAP_NET_BIND_SERVICE | 1 |
|
||||||
|
| CAP_AUDIT_READ | 1 |
|
||||||
|
| CAP_AUDIT_WRITE | 1 |
|
||||||
|
| CAP_DAC_OVERRIDE | 1 |
|
||||||
|
| CAP_SETFCAP | 1 |
|
||||||
|
| CAP_SETPCAP | 1 |
|
||||||
|
| CAP_SETGID | 1 |
|
||||||
|
| CAP_SETUID | 1 |
|
||||||
|
| CAP_MKNOD | 1 |
|
||||||
|
| CAP_CHOWN | 1 |
|
||||||
|
| CAP_FOWNER | 1 |
|
||||||
|
| CAP_FSETID | 1 |
|
||||||
|
| CAP_KILL | 1 |
|
||||||
|
| CAP_SYS_CHROOT | 1 |
|
||||||
|
| CAP_NET_BROADCAST | 0 |
|
||||||
|
| CAP_SYS_MODULE | 0 |
|
||||||
|
| CAP_SYS_RAWIO | 0 |
|
||||||
|
| CAP_SYS_PACCT | 0 |
|
||||||
|
| CAP_SYS_ADMIN | 0 |
|
||||||
|
| CAP_SYS_NICE | 0 |
|
||||||
|
| CAP_SYS_RESOURCE | 0 |
|
||||||
|
| CAP_SYS_TIME | 0 |
|
||||||
|
| CAP_SYS_TTY_CONFIG | 0 |
|
||||||
|
| CAP_AUDIT_CONTROL | 0 |
|
||||||
|
| CAP_MAC_OVERRIDE | 0 |
|
||||||
|
| CAP_MAC_ADMIN | 0 |
|
||||||
|
| CAP_NET_ADMIN | 0 |
|
||||||
|
| CAP_SYSLOG | 0 |
|
||||||
|
| CAP_DAC_READ_SEARCH | 0 |
|
||||||
|
| CAP_LINUX_IMMUTABLE | 0 |
|
||||||
|
| CAP_IPC_LOCK | 0 |
|
||||||
|
| CAP_IPC_OWNER | 0 |
|
||||||
|
| CAP_SYS_PTRACE | 0 |
|
||||||
|
| CAP_SYS_BOOT | 0 |
|
||||||
|
| CAP_LEASE | 0 |
|
||||||
|
| CAP_WAKE_ALARM | 0 |
|
||||||
|
| CAP_BLOCK_SUSPEND | 0 |
|
||||||
|
|
||||||
|
|
||||||
|
Additional security layers like [apparmor](https://wiki.ubuntu.com/AppArmor)
|
||||||
|
and [selinux](http://selinuxproject.org/page/Main_Page) can be used with
|
||||||
|
the containers. A container should support setting an apparmor profile or
|
||||||
|
selinux process and mount labels if provided in the configuration.
|
||||||
|
|
||||||
|
Standard apparmor profile:
|
||||||
|
```c
|
||||||
|
#include <tunables/global>
|
||||||
|
profile <profile_name> flags=(attach_disconnected,mediate_deleted) {
|
||||||
|
#include <abstractions/base>
|
||||||
|
network,
|
||||||
|
capability,
|
||||||
|
file,
|
||||||
|
umount,
|
||||||
|
|
||||||
|
deny @{PROC}/sys/fs/** wklx,
|
||||||
|
deny @{PROC}/sysrq-trigger rwklx,
|
||||||
|
deny @{PROC}/mem rwklx,
|
||||||
|
deny @{PROC}/kmem rwklx,
|
||||||
|
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
|
||||||
|
deny @{PROC}/sys/kernel/*/** wklx,
|
||||||
|
|
||||||
|
deny mount,
|
||||||
|
|
||||||
|
deny /sys/[^f]*/** wklx,
|
||||||
|
deny /sys/f[^s]*/** wklx,
|
||||||
|
deny /sys/fs/[^c]*/** wklx,
|
||||||
|
deny /sys/fs/c[^g]*/** wklx,
|
||||||
|
deny /sys/fs/cg[^r]*/** wklx,
|
||||||
|
deny /sys/firmware/efi/efivars/** rwklx,
|
||||||
|
deny /sys/kernel/security/** rwklx,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*TODO: seccomp work is being done to find a good default config*
|
||||||
|
|
||||||
|
### Runtime and Init Process
|
||||||
|
|
||||||
|
During container creation the parent process needs to talk to the container's init
|
||||||
|
process and have a form of synchronization. This is accomplished by creating
|
||||||
|
a pipe that is passed to the container's init. When the init process first spawns
|
||||||
|
it will block on its side of the pipe until the parent closes its side. This
|
||||||
|
allows the parent to have time to set the new process inside a cgroup hierarchy
|
||||||
|
and/or write any uid/gid mappings required for user namespaces.
|
||||||
|
The pipe is passed to the init process via FD 3.
|
||||||
|
|
||||||
|
The application consuming libcontainer should be compiled statically. libcontainer
|
||||||
|
does not define any init process and the arguments provided are used to `exec` the
|
||||||
|
process inside the application. There should be no long running init within the
|
||||||
|
container spec.
|
||||||
|
|
||||||
|
If a pseudo tty is provided to a container it will open and `dup2` the console
|
||||||
|
as the container's STDIN, STDOUT, STDERR as well as mounting the console
|
||||||
|
as `/dev/console`.
|
||||||
|
|
||||||
|
An extra set of mounts are provided to a container and setup for use. A container's
|
||||||
|
rootfs can contain some non portable files inside that can cause side effects during
|
||||||
|
execution of a process. These files are usually created and populated with the container
|
||||||
|
specific information via the runtime.
|
||||||
|
|
||||||
|
**Extra runtime files:**
|
||||||
|
* /etc/hosts
|
||||||
|
* /etc/resolv.conf
|
||||||
|
* /etc/hostname
|
||||||
|
* /etc/localtime
|
||||||
|
|
||||||
|
|
||||||
|
#### Defaults
|
||||||
|
|
||||||
|
There are a few defaults that can be overridden by users, but in their omission
|
||||||
|
these apply to processes within a container.
|
||||||
|
|
||||||
|
| Type | Value |
|
||||||
|
| ------------------- | ------------------------------ |
|
||||||
|
| Parent Death Signal | SIGKILL |
|
||||||
|
| UID | 0 |
|
||||||
|
| GID | 0 |
|
||||||
|
| GROUPS | 0, NULL |
|
||||||
|
| CWD | "/" |
|
||||||
|
| $HOME | Current user's home dir or "/" |
|
||||||
|
| Readonly rootfs | false |
|
||||||
|
| Pseudo TTY | false |
|
||||||
|
|
||||||
|
|
||||||
|
## Actions
|
||||||
|
|
||||||
|
After a container is created there is a standard set of actions that can
|
||||||
|
be done to the container. These actions are part of the public API for
|
||||||
|
a container.
|
||||||
|
|
||||||
|
| Action | Description |
|
||||||
|
| -------------- | ------------------------------------------------------------------ |
|
||||||
|
| Get processes | Return all the pids for processes running inside a container |
|
||||||
|
| Get Stats | Return resource statistics for the container as a whole |
|
||||||
|
| Wait | Wait waits on the container's init process ( pid 1 ) |
|
||||||
|
| Wait Process | Wait on any of the container's processes returning the exit status |
|
||||||
|
| Destroy | Kill the container's init process and remove any filesystem state |
|
||||||
|
| Signal | Send a signal to the container's init process |
|
||||||
|
| Signal Process | Send a signal to any of the container's processes |
|
||||||
|
| Pause | Pause all processes inside the container |
|
||||||
|
| Resume | Resume all processes inside the container if paused |
|
||||||
|
| Exec | Execute a new process inside of the container ( requires setns ) |
|
||||||
|
| Set | Setup configs of the container after it's created |
|
||||||
|
|
||||||
|
### Execute a new process inside of a running container.
|
||||||
|
|
||||||
|
User can execute a new process inside of a running container. Any binaries to be
|
||||||
|
executed must be accessible within the container's rootfs.
|
||||||
|
|
||||||
|
The started process will run inside the container's rootfs. Any changes
|
||||||
|
made by the process to the container's filesystem will persist after the
|
||||||
|
process finished executing.
|
||||||
|
|
||||||
|
The started process will join all the container's existing namespaces. When the
|
||||||
|
container is paused, the process will also be paused and will resume when
|
||||||
|
the container is unpaused. The started process will only run when the container's
|
||||||
|
primary process (PID 1) is running, and will not be restarted when the container
|
||||||
|
is restarted.
|
||||||
|
|
||||||
|
#### Planned additions
|
||||||
|
|
||||||
|
The started process will have its own cgroups nested inside the container's
|
||||||
|
cgroups. This is used for process tracking and optionally resource allocation
|
||||||
|
handling for the new process. Freezer cgroup is required, the rest of the cgroups
|
||||||
|
are optional. The process executor must place its pid inside the correct
|
||||||
|
cgroups before starting the process. This is done so that no child processes or
|
||||||
|
threads can escape the cgroups.
|
||||||
|
|
||||||
|
When the process is stopped, the process executor will try (in a best-effort way)
|
||||||
|
to stop all its children and remove the sub-cgroups.
|
69
vendor/github.com/opencontainers/runc/libcontainer/capabilities_linux.go
generated
vendored
Normal file
69
vendor/github.com/opencontainers/runc/libcontainer/capabilities_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/syndtr/gocapability/capability"
|
||||||
|
)
|
||||||
|
|
||||||
|
const allCapabilityTypes = capability.CAPS | capability.BOUNDS
|
||||||
|
|
||||||
|
var capabilityMap map[string]capability.Cap
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
capabilityMap = make(map[string]capability.Cap)
|
||||||
|
last := capability.CAP_LAST_CAP
|
||||||
|
// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
|
||||||
|
if last == capability.Cap(63) {
|
||||||
|
last = capability.CAP_BLOCK_SUSPEND
|
||||||
|
}
|
||||||
|
for _, cap := range capability.List() {
|
||||||
|
if cap > last {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
capKey := fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))
|
||||||
|
capabilityMap[capKey] = cap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCapWhitelist(caps []string) (*whitelist, error) {
|
||||||
|
l := []capability.Cap{}
|
||||||
|
for _, c := range caps {
|
||||||
|
v, ok := capabilityMap[c]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown capability %q", c)
|
||||||
|
}
|
||||||
|
l = append(l, v)
|
||||||
|
}
|
||||||
|
pid, err := capability.NewPid(os.Getpid())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &whitelist{
|
||||||
|
keep: l,
|
||||||
|
pid: pid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type whitelist struct {
|
||||||
|
pid capability.Capabilities
|
||||||
|
keep []capability.Cap
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropBoundingSet drops the capability bounding set to those specified in the whitelist.
|
||||||
|
func (w *whitelist) dropBoundingSet() error {
|
||||||
|
w.pid.Clear(capability.BOUNDS)
|
||||||
|
w.pid.Set(capability.BOUNDS, w.keep...)
|
||||||
|
return w.pid.Apply(capability.BOUNDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop drops all capabilities for the current process except those specified in the whitelist.
|
||||||
|
func (w *whitelist) drop() error {
|
||||||
|
w.pid.Clear(allCapabilityTypes)
|
||||||
|
w.pid.Set(allCapabilityTypes, w.keep...)
|
||||||
|
return w.pid.Apply(allCapabilityTypes)
|
||||||
|
}
|
64
vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go
generated
vendored
Normal file
64
vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manager interface {
|
||||||
|
// Applies cgroup configuration to the process with the specified pid
|
||||||
|
Apply(pid int) error
|
||||||
|
|
||||||
|
// Returns the PIDs inside the cgroup set
|
||||||
|
GetPids() ([]int, error)
|
||||||
|
|
||||||
|
// Returns the PIDs inside the cgroup set & all sub-cgroups
|
||||||
|
GetAllPids() ([]int, error)
|
||||||
|
|
||||||
|
// Returns statistics for the cgroup set
|
||||||
|
GetStats() (*Stats, error)
|
||||||
|
|
||||||
|
// Toggles the freezer cgroup according with specified state
|
||||||
|
Freeze(state configs.FreezerState) error
|
||||||
|
|
||||||
|
// Destroys the cgroup set
|
||||||
|
Destroy() error
|
||||||
|
|
||||||
|
// NewCgroupManager() and LoadCgroupManager() require following attributes:
|
||||||
|
// Paths map[string]string
|
||||||
|
// Cgroups *cgroups.Cgroup
|
||||||
|
// Paths maps cgroup subsystem to path at which it is mounted.
|
||||||
|
// Cgroups specifies specific cgroup settings for the various subsystems
|
||||||
|
|
||||||
|
// Returns cgroup paths to save in a state file and to be able to
|
||||||
|
// restore the object later.
|
||||||
|
GetPaths() map[string]string
|
||||||
|
|
||||||
|
// Set the cgroup as configured.
|
||||||
|
Set(container *configs.Config) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotFoundError struct {
|
||||||
|
Subsystem string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("mountpoint for %s not found", e.Subsystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNotFoundError(sub string) error {
|
||||||
|
return &NotFoundError{
|
||||||
|
Subsystem: sub,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNotFound(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := err.(*NotFoundError)
|
||||||
|
return ok
|
||||||
|
}
|
3
vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_unsupported.go
generated
vendored
Normal file
3
vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package cgroups
|
105
vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go
generated
vendored
Normal file
105
vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package cgroups
|
||||||
|
|
||||||
|
type ThrottlingData struct {
|
||||||
|
// Number of periods with throttling active
|
||||||
|
Periods uint64 `json:"periods,omitempty"`
|
||||||
|
// Number of periods when the container hit its throttling limit.
|
||||||
|
ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`
|
||||||
|
// Aggregate time the container was throttled for in nanoseconds.
|
||||||
|
ThrottledTime uint64 `json:"throttled_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// All CPU stats are aggregate since container inception.
|
||||||
|
type CpuUsage struct {
|
||||||
|
// Total CPU time consumed.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
TotalUsage uint64 `json:"total_usage,omitempty"`
|
||||||
|
// Total CPU time consumed per core.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
||||||
|
// Time spent by tasks of the cgroup in kernel mode.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||||
|
// Time spent by tasks of the cgroup in user mode.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuStats struct {
|
||||||
|
CpuUsage CpuUsage `json:"cpu_usage,omitempty"`
|
||||||
|
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryData struct {
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||||
|
Failcnt uint64 `json:"failcnt"`
|
||||||
|
Limit uint64 `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryStats struct {
|
||||||
|
// memory used for cache
|
||||||
|
Cache uint64 `json:"cache,omitempty"`
|
||||||
|
// usage of memory
|
||||||
|
Usage MemoryData `json:"usage,omitempty"`
|
||||||
|
// usage of memory + swap
|
||||||
|
SwapUsage MemoryData `json:"swap_usage,omitempty"`
|
||||||
|
// usage of kernel memory
|
||||||
|
KernelUsage MemoryData `json:"kernel_usage,omitempty"`
|
||||||
|
// usage of kernel TCP memory
|
||||||
|
KernelTCPUsage MemoryData `json:"kernel_tcp_usage,omitempty"`
|
||||||
|
|
||||||
|
Stats map[string]uint64 `json:"stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PidsStats struct {
|
||||||
|
// number of pids in the cgroup
|
||||||
|
Current uint64 `json:"current,omitempty"`
|
||||||
|
// active pids hard limit
|
||||||
|
Limit uint64 `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlkioStatEntry struct {
|
||||||
|
Major uint64 `json:"major,omitempty"`
|
||||||
|
Minor uint64 `json:"minor,omitempty"`
|
||||||
|
Op string `json:"op,omitempty"`
|
||||||
|
Value uint64 `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlkioStats struct {
|
||||||
|
// number of bytes tranferred to and from the block device
|
||||||
|
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
|
||||||
|
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"`
|
||||||
|
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"`
|
||||||
|
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive,omitempty"`
|
||||||
|
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive,omitempty"`
|
||||||
|
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive,omitempty"`
|
||||||
|
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive,omitempty"`
|
||||||
|
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HugetlbStats struct {
|
||||||
|
// current res_counter usage for hugetlb
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
// maximum usage ever recorded.
|
||||||
|
MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||||
|
// number of times hugetlb usage allocation failure.
|
||||||
|
Failcnt uint64 `json:"failcnt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
CpuStats CpuStats `json:"cpu_stats,omitempty"`
|
||||||
|
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||||
|
PidsStats PidsStats `json:"pids_stats,omitempty"`
|
||||||
|
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||||
|
// the map is in the format "size of hugepage: stats of the hugepage"
|
||||||
|
HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStats() *Stats {
|
||||||
|
memoryStats := MemoryStats{Stats: make(map[string]uint64)}
|
||||||
|
hugetlbStats := make(map[string]HugetlbStats)
|
||||||
|
return &Stats{MemoryStats: memoryStats, HugetlbStats: hugetlbStats}
|
||||||
|
}
|
378
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
Normal file
378
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,378 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package cgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
const cgroupNamePrefix = "name="
|
||||||
|
|
||||||
|
// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
|
||||||
|
func FindCgroupMountpoint(subsystem string) (string, error) {
|
||||||
|
// We are not using mount.GetMounts() because it's super-inefficient,
|
||||||
|
// parsing it directly sped up x10 times because of not using Sscanf.
|
||||||
|
// It was one of two major performance drawbacks in container start.
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
txt := scanner.Text()
|
||||||
|
fields := strings.Split(txt, " ")
|
||||||
|
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||||
|
if opt == subsystem {
|
||||||
|
return fields[4], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", NewNotFoundError(subsystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindCgroupMountpointAndRoot(subsystem string) (string, string, error) {
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
txt := scanner.Text()
|
||||||
|
fields := strings.Split(txt, " ")
|
||||||
|
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||||
|
if opt == subsystem {
|
||||||
|
return fields[4], fields[3], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", "", NewNotFoundError(subsystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindCgroupMountpointDir() (string, error) {
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
text := scanner.Text()
|
||||||
|
fields := strings.Split(text, " ")
|
||||||
|
// Safe as mountinfo encodes mountpoints with spaces as \040.
|
||||||
|
index := strings.Index(text, " - ")
|
||||||
|
postSeparatorFields := strings.Fields(text[index+3:])
|
||||||
|
numPostFields := len(postSeparatorFields)
|
||||||
|
|
||||||
|
// This is an error as we can't detect if the mount is for "cgroup"
|
||||||
|
if numPostFields == 0 {
|
||||||
|
return "", fmt.Errorf("Found no fields post '-' in %q", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if postSeparatorFields[0] == "cgroup" {
|
||||||
|
// Check that the mount is properly formated.
|
||||||
|
if numPostFields < 3 {
|
||||||
|
return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Dir(fields[4]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", NewNotFoundError("cgroup")
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mount struct {
|
||||||
|
Mountpoint string
|
||||||
|
Root string
|
||||||
|
Subsystems []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Mount) GetThisCgroupDir(cgroups map[string]string) (string, error) {
|
||||||
|
if len(m.Subsystems) == 0 {
|
||||||
|
return "", fmt.Errorf("no subsystem for mount")
|
||||||
|
}
|
||||||
|
|
||||||
|
return getControllerPath(m.Subsystems[0], cgroups)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroupMountsHelper(ss map[string]bool, mi io.Reader) ([]Mount, error) {
|
||||||
|
res := make([]Mount, 0, len(ss))
|
||||||
|
scanner := bufio.NewScanner(mi)
|
||||||
|
for scanner.Scan() {
|
||||||
|
txt := scanner.Text()
|
||||||
|
sepIdx := strings.Index(txt, " - ")
|
||||||
|
if sepIdx == -1 {
|
||||||
|
return nil, fmt.Errorf("invalid mountinfo format")
|
||||||
|
}
|
||||||
|
if txt[sepIdx+3:sepIdx+9] != "cgroup" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Split(txt, " ")
|
||||||
|
m := Mount{
|
||||||
|
Mountpoint: fields[4],
|
||||||
|
Root: fields[3],
|
||||||
|
}
|
||||||
|
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||||
|
if strings.HasPrefix(opt, cgroupNamePrefix) {
|
||||||
|
m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
|
||||||
|
}
|
||||||
|
if ss[opt] {
|
||||||
|
m.Subsystems = append(m.Subsystems, opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = append(res, m)
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCgroupMounts() ([]Mount, error) {
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
all, err := GetAllSubsystems()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allMap := make(map[string]bool)
|
||||||
|
for _, s := range all {
|
||||||
|
allMap[s] = true
|
||||||
|
}
|
||||||
|
return getCgroupMountsHelper(allMap, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all the cgroup subsystems supported by the kernel
|
||||||
|
func GetAllSubsystems() ([]string, error) {
|
||||||
|
f, err := os.Open("/proc/cgroups")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
subsystems := []string{}
|
||||||
|
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
text := s.Text()
|
||||||
|
if text[0] != '#' {
|
||||||
|
parts := strings.Fields(text)
|
||||||
|
if len(parts) >= 4 && parts[3] != "0" {
|
||||||
|
subsystems = append(subsystems, parts[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subsystems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the relative path to the cgroup docker is running in.
|
||||||
|
func GetThisCgroupDir(subsystem string) (string, error) {
|
||||||
|
cgroups, err := ParseCgroupFile("/proc/self/cgroup")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getControllerPath(subsystem, cgroups)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInitCgroupDir(subsystem string) (string, error) {
|
||||||
|
|
||||||
|
cgroups, err := ParseCgroupFile("/proc/1/cgroup")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getControllerPath(subsystem, cgroups)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readProcsFile(dir string) ([]int, error) {
|
||||||
|
f, err := os.Open(filepath.Join(dir, "cgroup.procs"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
s = bufio.NewScanner(f)
|
||||||
|
out = []int{}
|
||||||
|
)
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
|
if t := s.Text(); t != "" {
|
||||||
|
pid, err := strconv.Atoi(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseCgroupFile(path string) (map[string]string, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
cgroups := make(map[string]string)
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
text := s.Text()
|
||||||
|
parts := strings.Split(text, ":")
|
||||||
|
|
||||||
|
for _, subs := range strings.Split(parts[1], ",") {
|
||||||
|
cgroups[subs] = parts[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cgroups, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getControllerPath(subsystem string, cgroups map[string]string) (string, error) {
|
||||||
|
|
||||||
|
if p, ok := cgroups[subsystem]; ok {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p, ok := cgroups[cgroupNamePrefix+subsystem]; ok {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", NewNotFoundError(subsystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PathExists(path string) bool {
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnterPid(cgroupPaths map[string]string, pid int) error {
|
||||||
|
for _, path := range cgroupPaths {
|
||||||
|
if PathExists(path) {
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"),
|
||||||
|
[]byte(strconv.Itoa(pid)), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePaths iterates over the provided paths removing them.
|
||||||
|
// We trying to remove all paths five times with increasing delay between tries.
|
||||||
|
// If after all there are not removed cgroups - appropriate error will be
|
||||||
|
// returned.
|
||||||
|
func RemovePaths(paths map[string]string) (err error) {
|
||||||
|
delay := 10 * time.Millisecond
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if i != 0 {
|
||||||
|
time.Sleep(delay)
|
||||||
|
delay *= 2
|
||||||
|
}
|
||||||
|
for s, p := range paths {
|
||||||
|
os.RemoveAll(p)
|
||||||
|
// TODO: here probably should be logging
|
||||||
|
_, err := os.Stat(p)
|
||||||
|
// We need this strange way of checking cgroups existence because
|
||||||
|
// RemoveAll almost always returns error, even on already removed
|
||||||
|
// cgroups
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
delete(paths, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Failed to remove paths: %v", paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHugePageSize() ([]string, error) {
|
||||||
|
var pageSizes []string
|
||||||
|
sizeList := []string{"B", "kB", "MB", "GB", "TB", "PB"}
|
||||||
|
files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages")
|
||||||
|
if err != nil {
|
||||||
|
return pageSizes, err
|
||||||
|
}
|
||||||
|
for _, st := range files {
|
||||||
|
nameArray := strings.Split(st.Name(), "-")
|
||||||
|
pageSize, err := units.RAMInBytes(nameArray[1])
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
sizeString := units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList)
|
||||||
|
pageSizes = append(pageSizes, sizeString)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageSizes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPids returns all pids, that were added to cgroup at path.
|
||||||
|
func GetPids(path string) ([]int, error) {
|
||||||
|
return readProcsFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllPids returns all pids, that were added to cgroup at path and to all its
|
||||||
|
// subcgroups.
|
||||||
|
func GetAllPids(path string) ([]int, error) {
|
||||||
|
var pids []int
|
||||||
|
// collect pids from all sub-cgroups
|
||||||
|
err := filepath.Walk(path, func(p string, info os.FileInfo, iErr error) error {
|
||||||
|
dir, file := filepath.Split(p)
|
||||||
|
if file != "cgroup.procs" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if iErr != nil {
|
||||||
|
return iErr
|
||||||
|
}
|
||||||
|
cPids, err := readProcsFile(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pids = append(pids, cPids...)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return pids, err
|
||||||
|
}
|
10
vendor/github.com/opencontainers/runc/libcontainer/compat_1.5_linux.go
generated
vendored
Normal file
10
vendor/github.com/opencontainers/runc/libcontainer/compat_1.5_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// +build linux,!go1.5
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// GidMappingsEnableSetgroups was added in Go 1.5, so do nothing when building
|
||||||
|
// with earlier versions
|
||||||
|
func enableSetgroups(sys *syscall.SysProcAttr) {
|
||||||
|
}
|
61
vendor/github.com/opencontainers/runc/libcontainer/configs/blkio_device.go
generated
vendored
Normal file
61
vendor/github.com/opencontainers/runc/libcontainer/configs/blkio_device.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// blockIODevice holds major:minor format supported in blkio cgroup
|
||||||
|
type blockIODevice struct {
|
||||||
|
// Major is the device's major number
|
||||||
|
Major int64 `json:"major"`
|
||||||
|
// Minor is the device's minor number
|
||||||
|
Minor int64 `json:"minor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeightDevice struct holds a `major:minor weight`|`major:minor leaf_weight` pair
|
||||||
|
type WeightDevice struct {
|
||||||
|
blockIODevice
|
||||||
|
// Weight is the bandwidth rate for the device, range is from 10 to 1000
|
||||||
|
Weight uint16 `json:"weight"`
|
||||||
|
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
|
||||||
|
LeafWeight uint16 `json:"leafWeight"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWeightDevice returns a configured WeightDevice pointer
|
||||||
|
func NewWeightDevice(major, minor int64, weight, leafWeight uint16) *WeightDevice {
|
||||||
|
wd := &WeightDevice{}
|
||||||
|
wd.Major = major
|
||||||
|
wd.Minor = minor
|
||||||
|
wd.Weight = weight
|
||||||
|
wd.LeafWeight = leafWeight
|
||||||
|
return wd
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeightString formats the struct to be writable to the cgroup specific file
|
||||||
|
func (wd *WeightDevice) WeightString() string {
|
||||||
|
return fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, wd.Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeafWeightString formats the struct to be writable to the cgroup specific file
|
||||||
|
func (wd *WeightDevice) LeafWeightString() string {
|
||||||
|
return fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, wd.LeafWeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThrottleDevice struct holds a `major:minor rate_per_second` pair
|
||||||
|
type ThrottleDevice struct {
|
||||||
|
blockIODevice
|
||||||
|
// Rate is the IO rate limit per cgroup per device
|
||||||
|
Rate uint64 `json:"rate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewThrottleDevice returns a configured ThrottleDevice pointer
|
||||||
|
func NewThrottleDevice(major, minor int64, rate uint64) *ThrottleDevice {
|
||||||
|
td := &ThrottleDevice{}
|
||||||
|
td.Major = major
|
||||||
|
td.Minor = minor
|
||||||
|
td.Rate = rate
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
|
||||||
|
// String formats the struct to be writable to the cgroup specific file
|
||||||
|
func (td *ThrottleDevice) String() string {
|
||||||
|
return fmt.Sprintf("%d:%d %d", td.Major, td.Minor, td.Rate)
|
||||||
|
}
|
124
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unix.go
generated
vendored
Normal file
124
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
// +build linux freebsd
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
type FreezerState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Undefined FreezerState = ""
|
||||||
|
Frozen FreezerState = "FROZEN"
|
||||||
|
Thawed FreezerState = "THAWED"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cgroup struct {
|
||||||
|
// Deprecated, use Path instead
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// name of parent of cgroup or slice
|
||||||
|
// Deprecated, use Path instead
|
||||||
|
Parent string `json:"parent,omitempty"`
|
||||||
|
|
||||||
|
// Path specifies the path to cgroups that are created and/or joined by the container.
|
||||||
|
// The path is assumed to be relative to the host system cgroup mountpoint.
|
||||||
|
Path string `json:"path"`
|
||||||
|
|
||||||
|
// ScopePrefix decribes prefix for the scope name
|
||||||
|
ScopePrefix string `json:"scope_prefix"`
|
||||||
|
|
||||||
|
// Paths represent the absolute cgroups paths to join.
|
||||||
|
// This takes precedence over Path.
|
||||||
|
Paths map[string]string
|
||||||
|
|
||||||
|
// Resources contains various cgroups settings to apply
|
||||||
|
*Resources
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resources struct {
|
||||||
|
// If this is true allow access to any kind of device within the container. If false, allow access only to devices explicitly listed in the allowed_devices list.
|
||||||
|
// Deprecated
|
||||||
|
AllowAllDevices bool `json:"allow_all_devices,omitempty"`
|
||||||
|
// Deprecated
|
||||||
|
AllowedDevices []*Device `json:"allowed_devices,omitempty"`
|
||||||
|
// Deprecated
|
||||||
|
DeniedDevices []*Device `json:"denied_devices,omitempty"`
|
||||||
|
|
||||||
|
Devices []*Device `json:"devices"`
|
||||||
|
|
||||||
|
// Memory limit (in bytes)
|
||||||
|
Memory int64 `json:"memory"`
|
||||||
|
|
||||||
|
// Memory reservation or soft_limit (in bytes)
|
||||||
|
MemoryReservation int64 `json:"memory_reservation"`
|
||||||
|
|
||||||
|
// Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||||
|
MemorySwap int64 `json:"memory_swap"`
|
||||||
|
|
||||||
|
// Kernel memory limit (in bytes)
|
||||||
|
KernelMemory int64 `json:"kernel_memory"`
|
||||||
|
|
||||||
|
// Kernel memory limit for TCP use (in bytes)
|
||||||
|
KernelMemoryTCP int64 `json:"kernel_memory_tcp"`
|
||||||
|
|
||||||
|
// CPU shares (relative weight vs. other containers)
|
||||||
|
CpuShares int64 `json:"cpu_shares"`
|
||||||
|
|
||||||
|
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
|
||||||
|
CpuQuota int64 `json:"cpu_quota"`
|
||||||
|
|
||||||
|
// CPU period to be used for hardcapping (in usecs). 0 to use system default.
|
||||||
|
CpuPeriod int64 `json:"cpu_period"`
|
||||||
|
|
||||||
|
// How many time CPU will use in realtime scheduling (in usecs).
|
||||||
|
CpuRtRuntime int64 `json:"cpu_quota"`
|
||||||
|
|
||||||
|
// CPU period to be used for realtime scheduling (in usecs).
|
||||||
|
CpuRtPeriod int64 `json:"cpu_period"`
|
||||||
|
|
||||||
|
// CPU to use
|
||||||
|
CpusetCpus string `json:"cpuset_cpus"`
|
||||||
|
|
||||||
|
// MEM to use
|
||||||
|
CpusetMems string `json:"cpuset_mems"`
|
||||||
|
|
||||||
|
// Process limit; set <= `0' to disable limit.
|
||||||
|
PidsLimit int64 `json:"pids_limit"`
|
||||||
|
|
||||||
|
// Specifies per cgroup weight, range is from 10 to 1000.
|
||||||
|
BlkioWeight uint16 `json:"blkio_weight"`
|
||||||
|
|
||||||
|
// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
|
||||||
|
BlkioLeafWeight uint16 `json:"blkio_leaf_weight"`
|
||||||
|
|
||||||
|
// Weight per cgroup per device, can override BlkioWeight.
|
||||||
|
BlkioWeightDevice []*WeightDevice `json:"blkio_weight_device"`
|
||||||
|
|
||||||
|
// IO read rate limit per cgroup per device, bytes per second.
|
||||||
|
BlkioThrottleReadBpsDevice []*ThrottleDevice `json:"blkio_throttle_read_bps_device"`
|
||||||
|
|
||||||
|
// IO write rate limit per cgroup per divice, bytes per second.
|
||||||
|
BlkioThrottleWriteBpsDevice []*ThrottleDevice `json:"blkio_throttle_write_bps_device"`
|
||||||
|
|
||||||
|
// IO read rate limit per cgroup per device, IO per second.
|
||||||
|
BlkioThrottleReadIOPSDevice []*ThrottleDevice `json:"blkio_throttle_read_iops_device"`
|
||||||
|
|
||||||
|
// IO write rate limit per cgroup per device, IO per second.
|
||||||
|
BlkioThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkio_throttle_write_iops_device"`
|
||||||
|
|
||||||
|
// set the freeze value for the process
|
||||||
|
Freezer FreezerState `json:"freezer"`
|
||||||
|
|
||||||
|
// Hugetlb limit (in bytes)
|
||||||
|
HugetlbLimit []*HugepageLimit `json:"hugetlb_limit"`
|
||||||
|
|
||||||
|
// Whether to disable OOM Killer
|
||||||
|
OomKillDisable bool `json:"oom_kill_disable"`
|
||||||
|
|
||||||
|
// Tuning swappiness behaviour per cgroup
|
||||||
|
MemorySwappiness *int64 `json:"memory_swappiness"`
|
||||||
|
|
||||||
|
// Set priority of network traffic for container
|
||||||
|
NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"`
|
||||||
|
|
||||||
|
// Set class identifier for container's network packets
|
||||||
|
NetClsClassid string `json:"net_cls_classid"`
|
||||||
|
}
|
6
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go
generated
vendored
Normal file
6
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// +build !windows,!linux,!freebsd
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
type Cgroup struct {
|
||||||
|
}
|
6
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_windows.go
generated
vendored
Normal file
6
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
// TODO Windows: This can ultimately be entirely factored out on Windows as
|
||||||
|
// cgroups are a Unix-specific construct.
|
||||||
|
type Cgroup struct {
|
||||||
|
}
|
328
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
Normal file
328
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Rlimit struct {
|
||||||
|
Type int `json:"type"`
|
||||||
|
Hard uint64 `json:"hard"`
|
||||||
|
Soft uint64 `json:"soft"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDMap represents UID/GID Mappings for User Namespaces.
|
||||||
|
type IDMap struct {
|
||||||
|
ContainerID int `json:"container_id"`
|
||||||
|
HostID int `json:"host_id"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seccomp represents syscall restrictions
|
||||||
|
// By default, only the native architecture of the kernel is allowed to be used
|
||||||
|
// for syscalls. Additional architectures can be added by specifying them in
|
||||||
|
// Architectures.
|
||||||
|
type Seccomp struct {
|
||||||
|
DefaultAction Action `json:"default_action"`
|
||||||
|
Architectures []string `json:"architectures"`
|
||||||
|
Syscalls []*Syscall `json:"syscalls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// An action to be taken upon rule match in Seccomp
|
||||||
|
type Action int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Kill Action = iota + 1
|
||||||
|
Errno
|
||||||
|
Trap
|
||||||
|
Allow
|
||||||
|
Trace
|
||||||
|
)
|
||||||
|
|
||||||
|
// A comparison operator to be used when matching syscall arguments in Seccomp
|
||||||
|
type Operator int
|
||||||
|
|
||||||
|
const (
|
||||||
|
EqualTo Operator = iota + 1
|
||||||
|
NotEqualTo
|
||||||
|
GreaterThan
|
||||||
|
GreaterThanOrEqualTo
|
||||||
|
LessThan
|
||||||
|
LessThanOrEqualTo
|
||||||
|
MaskEqualTo
|
||||||
|
)
|
||||||
|
|
||||||
|
// A rule to match a specific syscall argument in Seccomp
|
||||||
|
type Arg struct {
|
||||||
|
Index uint `json:"index"`
|
||||||
|
Value uint64 `json:"value"`
|
||||||
|
ValueTwo uint64 `json:"value_two"`
|
||||||
|
Op Operator `json:"op"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// An rule to match a syscall in Seccomp
|
||||||
|
type Syscall struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Action Action `json:"action"`
|
||||||
|
Args []*Arg `json:"args"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Windows. Many of these fields should be factored out into those parts
|
||||||
|
// which are common across platforms, and those which are platform specific.
|
||||||
|
|
||||||
|
// Config defines configuration options for executing a process inside a contained environment.
|
||||||
|
type Config struct {
|
||||||
|
// NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs
|
||||||
|
// This is a common option when the container is running in ramdisk
|
||||||
|
NoPivotRoot bool `json:"no_pivot_root"`
|
||||||
|
|
||||||
|
// ParentDeathSignal specifies the signal that is sent to the container's process in the case
|
||||||
|
// that the parent process dies.
|
||||||
|
ParentDeathSignal int `json:"parent_death_signal"`
|
||||||
|
|
||||||
|
// PivotDir allows a custom directory inside the container's root filesystem to be used as pivot, when NoPivotRoot is not set.
|
||||||
|
// When a custom PivotDir not set, a temporary dir inside the root filesystem will be used. The pivot dir needs to be writeable.
|
||||||
|
// This is required when using read only root filesystems. In these cases, a read/writeable path can be (bind) mounted somewhere inside the root filesystem to act as pivot.
|
||||||
|
PivotDir string `json:"pivot_dir"`
|
||||||
|
|
||||||
|
// Path to a directory containing the container's root filesystem.
|
||||||
|
Rootfs string `json:"rootfs"`
|
||||||
|
|
||||||
|
// Readonlyfs will remount the container's rootfs as readonly where only externally mounted
|
||||||
|
// bind mounts are writtable.
|
||||||
|
Readonlyfs bool `json:"readonlyfs"`
|
||||||
|
|
||||||
|
// Specifies the mount propagation flags to be applied to /.
|
||||||
|
RootPropagation int `json:"rootPropagation"`
|
||||||
|
|
||||||
|
// Mounts specify additional source and destination paths that will be mounted inside the container's
|
||||||
|
// rootfs and mount namespace if specified
|
||||||
|
Mounts []*Mount `json:"mounts"`
|
||||||
|
|
||||||
|
// The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well!
|
||||||
|
Devices []*Device `json:"devices"`
|
||||||
|
|
||||||
|
MountLabel string `json:"mount_label"`
|
||||||
|
|
||||||
|
// Hostname optionally sets the container's hostname if provided
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
|
||||||
|
// Namespaces specifies the container's namespaces that it should setup when cloning the init process
|
||||||
|
// If a namespace is not provided that namespace is shared from the container's parent process
|
||||||
|
Namespaces Namespaces `json:"namespaces"`
|
||||||
|
|
||||||
|
// Capabilities specify the capabilities to keep when executing the process inside the container
|
||||||
|
// All capbilities not specified will be dropped from the processes capability mask
|
||||||
|
Capabilities []string `json:"capabilities"`
|
||||||
|
|
||||||
|
// Networks specifies the container's network setup to be created
|
||||||
|
Networks []*Network `json:"networks"`
|
||||||
|
|
||||||
|
// Routes can be specified to create entries in the route table as the container is started
|
||||||
|
Routes []*Route `json:"routes"`
|
||||||
|
|
||||||
|
// Cgroups specifies specific cgroup settings for the various subsystems that the container is
|
||||||
|
// placed into to limit the resources the container has available
|
||||||
|
Cgroups *Cgroup `json:"cgroups"`
|
||||||
|
|
||||||
|
// AppArmorProfile specifies the profile to apply to the process running in the container and is
|
||||||
|
// change at the time the process is execed
|
||||||
|
AppArmorProfile string `json:"apparmor_profile,omitempty"`
|
||||||
|
|
||||||
|
// ProcessLabel specifies the label to apply to the process running in the container. It is
|
||||||
|
// commonly used by selinux
|
||||||
|
ProcessLabel string `json:"process_label,omitempty"`
|
||||||
|
|
||||||
|
// Rlimits specifies the resource limits, such as max open files, to set in the container
|
||||||
|
// If Rlimits are not set, the container will inherit rlimits from the parent process
|
||||||
|
Rlimits []Rlimit `json:"rlimits,omitempty"`
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// More information about kernel oom score calculation here: https://lwn.net/Articles/317814/
|
||||||
|
OomScoreAdj int `json:"oom_score_adj"`
|
||||||
|
|
||||||
|
// AdditionalGroups specifies the gids that should be added to supplementary groups
|
||||||
|
// in addition to those that the user belongs to.
|
||||||
|
AdditionalGroups []string `json:"additional_groups"`
|
||||||
|
|
||||||
|
// UidMappings is an array of User ID mappings for User Namespaces
|
||||||
|
UidMappings []IDMap `json:"uid_mappings"`
|
||||||
|
|
||||||
|
// GidMappings is an array of Group ID mappings for User Namespaces
|
||||||
|
GidMappings []IDMap `json:"gid_mappings"`
|
||||||
|
|
||||||
|
// MaskPaths specifies paths within the container's rootfs to mask over with a bind
|
||||||
|
// mount pointing to /dev/null as to prevent reads of the file.
|
||||||
|
MaskPaths []string `json:"mask_paths"`
|
||||||
|
|
||||||
|
// ReadonlyPaths specifies paths within the container's rootfs to remount as read-only
|
||||||
|
// so that these files prevent any writes.
|
||||||
|
ReadonlyPaths []string `json:"readonly_paths"`
|
||||||
|
|
||||||
|
// Sysctl is a map of properties and their values. It is the equivalent of using
|
||||||
|
// sysctl -w my.property.name value in Linux.
|
||||||
|
Sysctl map[string]string `json:"sysctl"`
|
||||||
|
|
||||||
|
// Seccomp allows actions to be taken whenever a syscall is made within the container.
|
||||||
|
// A number of rules are given, each having an action to be taken if a syscall matches it.
|
||||||
|
// A default action to be taken if no rules match is also given.
|
||||||
|
Seccomp *Seccomp `json:"seccomp"`
|
||||||
|
|
||||||
|
// NoNewPrivileges controls whether processes in the container can gain additional privileges.
|
||||||
|
NoNewPrivileges bool `json:"no_new_privileges,omitempty"`
|
||||||
|
|
||||||
|
// Hooks are a collection of actions to perform at various container lifecycle events.
|
||||||
|
// CommandHooks are serialized to JSON, but other hooks are not.
|
||||||
|
Hooks *Hooks
|
||||||
|
|
||||||
|
// Version is the version of opencontainer specification that is supported.
|
||||||
|
Version string `json:"version"`
|
||||||
|
|
||||||
|
// Labels are user defined metadata that is stored in the config and populated on the state
|
||||||
|
Labels []string `json:"labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hooks struct {
|
||||||
|
// Prestart commands are executed after the container namespaces are created,
|
||||||
|
// but before the user supplied command is executed from init.
|
||||||
|
Prestart []Hook
|
||||||
|
|
||||||
|
// Poststart commands are executed after the container init process starts.
|
||||||
|
Poststart []Hook
|
||||||
|
|
||||||
|
// Poststop commands are executed after the container init process exits.
|
||||||
|
Poststop []Hook
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hooks *Hooks) UnmarshalJSON(b []byte) error {
|
||||||
|
var state struct {
|
||||||
|
Prestart []CommandHook
|
||||||
|
Poststart []CommandHook
|
||||||
|
Poststop []CommandHook
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize := func(shooks []CommandHook) (hooks []Hook) {
|
||||||
|
for _, shook := range shooks {
|
||||||
|
hooks = append(hooks, shook)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks.Prestart = deserialize(state.Prestart)
|
||||||
|
hooks.Poststart = deserialize(state.Poststart)
|
||||||
|
hooks.Poststop = deserialize(state.Poststop)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hooks Hooks) MarshalJSON() ([]byte, error) {
|
||||||
|
serialize := func(hooks []Hook) (serializableHooks []CommandHook) {
|
||||||
|
for _, hook := range hooks {
|
||||||
|
switch chook := hook.(type) {
|
||||||
|
case CommandHook:
|
||||||
|
serializableHooks = append(serializableHooks, chook)
|
||||||
|
default:
|
||||||
|
logrus.Warnf("cannot serialize hook of type %T, skipping", hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return serializableHooks
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"prestart": serialize(hooks.Prestart),
|
||||||
|
"poststart": serialize(hooks.Poststart),
|
||||||
|
"poststop": serialize(hooks.Poststop),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HookState is the payload provided to a hook on execution.
|
||||||
|
type HookState struct {
|
||||||
|
Version string `json:"ociVersion"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Pid int `json:"pid"`
|
||||||
|
Root string `json:"root"`
|
||||||
|
BundlePath string `json:"bundlePath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hook interface {
|
||||||
|
// Run executes the hook with the provided state.
|
||||||
|
Run(HookState) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFunctionHooks will call the provided function when the hook is run.
|
||||||
|
func NewFunctionHook(f func(HookState) error) FuncHook {
|
||||||
|
return FuncHook{
|
||||||
|
run: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FuncHook struct {
|
||||||
|
run func(HookState) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FuncHook) Run(s HookState) error {
|
||||||
|
return f.run(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Command struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Args []string `json:"args"`
|
||||||
|
Env []string `json:"env"`
|
||||||
|
Dir string `json:"dir"`
|
||||||
|
Timeout *time.Duration `json:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCommandHooks will execute the provided command when the hook is run.
|
||||||
|
func NewCommandHook(cmd Command) CommandHook {
|
||||||
|
return CommandHook{
|
||||||
|
Command: cmd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandHook struct {
|
||||||
|
Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Command) Run(s HookState) error {
|
||||||
|
b, err := json.Marshal(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Cmd{
|
||||||
|
Path: c.Path,
|
||||||
|
Args: c.Args,
|
||||||
|
Env: c.Env,
|
||||||
|
Stdin: bytes.NewReader(b),
|
||||||
|
}
|
||||||
|
errC := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("%s: %s", err, out)
|
||||||
|
}
|
||||||
|
errC <- err
|
||||||
|
}()
|
||||||
|
if c.Timeout != nil {
|
||||||
|
select {
|
||||||
|
case err := <-errC:
|
||||||
|
return err
|
||||||
|
case <-time.After(*c.Timeout):
|
||||||
|
cmd.Process.Kill()
|
||||||
|
cmd.Wait()
|
||||||
|
return fmt.Errorf("hook ran past specified timeout of %.1fs", c.Timeout.Seconds())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <-errC
|
||||||
|
}
|
51
vendor/github.com/opencontainers/runc/libcontainer/configs/config_unix.go
generated
vendored
Normal file
51
vendor/github.com/opencontainers/runc/libcontainer/configs/config_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// +build freebsd linux
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Gets the root uid for the process on host which could be non-zero
|
||||||
|
// when user namespaces are enabled.
|
||||||
|
func (c Config) HostUID() (int, error) {
|
||||||
|
if c.Namespaces.Contains(NEWUSER) {
|
||||||
|
if c.UidMappings == nil {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no user mappings found.")
|
||||||
|
}
|
||||||
|
id, found := c.hostIDFromMapping(0, c.UidMappings)
|
||||||
|
if !found {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no root user mapping found.")
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
// Return default root uid 0
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the root gid for the process on host which could be non-zero
|
||||||
|
// when user namespaces are enabled.
|
||||||
|
func (c Config) HostGID() (int, error) {
|
||||||
|
if c.Namespaces.Contains(NEWUSER) {
|
||||||
|
if c.GidMappings == nil {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no gid mappings found.")
|
||||||
|
}
|
||||||
|
id, found := c.hostIDFromMapping(0, c.GidMappings)
|
||||||
|
if !found {
|
||||||
|
return -1, fmt.Errorf("User namespaces enabled, but no root group mapping found.")
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
// Return default root gid 0
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function that gets a host ID for a container ID from user namespace map
|
||||||
|
// if that ID is present in the map.
|
||||||
|
func (c Config) hostIDFromMapping(containerID int, uMap []IDMap) (int, bool) {
|
||||||
|
for _, m := range uMap {
|
||||||
|
if (containerID >= m.ContainerID) && (containerID <= (m.ContainerID + m.Size - 1)) {
|
||||||
|
hostID := m.HostID + (containerID - m.ContainerID)
|
||||||
|
return hostID, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, false
|
||||||
|
}
|
57
vendor/github.com/opencontainers/runc/libcontainer/configs/device.go
generated
vendored
Normal file
57
vendor/github.com/opencontainers/runc/libcontainer/configs/device.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Wildcard = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO Windows: This can be factored out in the future
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
// Device type, block, char, etc.
|
||||||
|
Type rune `json:"type"`
|
||||||
|
|
||||||
|
// Path to the device.
|
||||||
|
Path string `json:"path"`
|
||||||
|
|
||||||
|
// Major is the device's major number.
|
||||||
|
Major int64 `json:"major"`
|
||||||
|
|
||||||
|
// Minor is the device's minor number.
|
||||||
|
Minor int64 `json:"minor"`
|
||||||
|
|
||||||
|
// Cgroup permissions format, rwm.
|
||||||
|
Permissions string `json:"permissions"`
|
||||||
|
|
||||||
|
// FileMode permission bits for the device.
|
||||||
|
FileMode os.FileMode `json:"file_mode"`
|
||||||
|
|
||||||
|
// Uid of the device.
|
||||||
|
Uid uint32 `json:"uid"`
|
||||||
|
|
||||||
|
// Gid of the device.
|
||||||
|
Gid uint32 `json:"gid"`
|
||||||
|
|
||||||
|
// Write the file to the allowed list
|
||||||
|
Allow bool `json:"allow"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) CgroupString() string {
|
||||||
|
return fmt.Sprintf("%c %s:%s %s", d.Type, deviceNumberString(d.Major), deviceNumberString(d.Minor), d.Permissions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) Mkdev() int {
|
||||||
|
return int((d.Major << 8) | (d.Minor & 0xff) | ((d.Minor & 0xfff00) << 12))
|
||||||
|
}
|
||||||
|
|
||||||
|
// deviceNumberString converts the device number to a string return result.
|
||||||
|
func deviceNumberString(number int64) string {
|
||||||
|
if number == Wildcard {
|
||||||
|
return "*"
|
||||||
|
}
|
||||||
|
return fmt.Sprint(number)
|
||||||
|
}
|
125
vendor/github.com/opencontainers/runc/libcontainer/configs/device_defaults.go
generated
vendored
Normal file
125
vendor/github.com/opencontainers/runc/libcontainer/configs/device_defaults.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// +build linux freebsd
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These are devices that are to be both allowed and created.
|
||||||
|
DefaultSimpleDevices = []*Device{
|
||||||
|
// /dev/null and zero
|
||||||
|
{
|
||||||
|
Path: "/dev/null",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 1,
|
||||||
|
Minor: 3,
|
||||||
|
Permissions: "rwm",
|
||||||
|
FileMode: 0666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dev/zero",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 1,
|
||||||
|
Minor: 5,
|
||||||
|
Permissions: "rwm",
|
||||||
|
FileMode: 0666,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Path: "/dev/full",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 1,
|
||||||
|
Minor: 7,
|
||||||
|
Permissions: "rwm",
|
||||||
|
FileMode: 0666,
|
||||||
|
},
|
||||||
|
|
||||||
|
// consoles and ttys
|
||||||
|
{
|
||||||
|
Path: "/dev/tty",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 5,
|
||||||
|
Minor: 0,
|
||||||
|
Permissions: "rwm",
|
||||||
|
FileMode: 0666,
|
||||||
|
},
|
||||||
|
|
||||||
|
// /dev/urandom,/dev/random
|
||||||
|
{
|
||||||
|
Path: "/dev/urandom",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 1,
|
||||||
|
Minor: 9,
|
||||||
|
Permissions: "rwm",
|
||||||
|
FileMode: 0666,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dev/random",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 1,
|
||||||
|
Minor: 8,
|
||||||
|
Permissions: "rwm",
|
||||||
|
FileMode: 0666,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
DefaultAllowedDevices = append([]*Device{
|
||||||
|
// allow mknod for any device
|
||||||
|
{
|
||||||
|
Type: 'c',
|
||||||
|
Major: Wildcard,
|
||||||
|
Minor: Wildcard,
|
||||||
|
Permissions: "m",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: 'b',
|
||||||
|
Major: Wildcard,
|
||||||
|
Minor: Wildcard,
|
||||||
|
Permissions: "m",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Path: "/dev/console",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 5,
|
||||||
|
Minor: 1,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
// /dev/pts/ - pts namespaces are "coming soon"
|
||||||
|
{
|
||||||
|
Path: "",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 136,
|
||||||
|
Minor: Wildcard,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 5,
|
||||||
|
Minor: 2,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
|
||||||
|
// tuntap
|
||||||
|
{
|
||||||
|
Path: "",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 10,
|
||||||
|
Minor: 200,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
}, DefaultSimpleDevices...)
|
||||||
|
DefaultAutoCreatedDevices = append([]*Device{
|
||||||
|
{
|
||||||
|
// /dev/fuse is created but not allowed.
|
||||||
|
// This is to allow java to work. Because java
|
||||||
|
// Insists on there being a /dev/fuse
|
||||||
|
// https://github.com/docker/docker/issues/514
|
||||||
|
// https://github.com/docker/docker/issues/2393
|
||||||
|
//
|
||||||
|
Path: "/dev/fuse",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 10,
|
||||||
|
Minor: 229,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
}, DefaultSimpleDevices...)
|
||||||
|
)
|
9
vendor/github.com/opencontainers/runc/libcontainer/configs/hugepage_limit.go
generated
vendored
Normal file
9
vendor/github.com/opencontainers/runc/libcontainer/configs/hugepage_limit.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
type HugepageLimit struct {
|
||||||
|
// which type of hugepage to limit.
|
||||||
|
Pagesize string `json:"page_size"`
|
||||||
|
|
||||||
|
// usage limit for hugepage.
|
||||||
|
Limit uint64 `json:"limit"`
|
||||||
|
}
|
14
vendor/github.com/opencontainers/runc/libcontainer/configs/interface_priority_map.go
generated
vendored
Normal file
14
vendor/github.com/opencontainers/runc/libcontainer/configs/interface_priority_map.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IfPrioMap struct {
|
||||||
|
Interface string `json:"interface"`
|
||||||
|
Priority int64 `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IfPrioMap) CgroupString() string {
|
||||||
|
return fmt.Sprintf("%s %d", i.Interface, i.Priority)
|
||||||
|
}
|
30
vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go
generated
vendored
Normal file
30
vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
type Mount struct {
|
||||||
|
// Source path for the mount.
|
||||||
|
Source string `json:"source"`
|
||||||
|
|
||||||
|
// Destination path for the mount inside the container.
|
||||||
|
Destination string `json:"destination"`
|
||||||
|
|
||||||
|
// Device the mount is for.
|
||||||
|
Device string `json:"device"`
|
||||||
|
|
||||||
|
// Mount flags.
|
||||||
|
Flags int `json:"flags"`
|
||||||
|
|
||||||
|
// Propagation Flags
|
||||||
|
PropagationFlags []int `json:"propagation_flags"`
|
||||||
|
|
||||||
|
// Mount data applied to the mount.
|
||||||
|
Data string `json:"data"`
|
||||||
|
|
||||||
|
// Relabel source if set, "z" indicates shared, "Z" indicates unshared.
|
||||||
|
Relabel string `json:"relabel"`
|
||||||
|
|
||||||
|
// Optional Command to be run before Source is mounted.
|
||||||
|
PremountCmds []Command `json:"premount_cmds"`
|
||||||
|
|
||||||
|
// Optional Command to be run after Source is mounted.
|
||||||
|
PostmountCmds []Command `json:"postmount_cmds"`
|
||||||
|
}
|
5
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces.go
generated
vendored
Normal file
5
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
type NamespaceType string
|
||||||
|
|
||||||
|
type Namespaces []Namespace
|
31
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go
generated
vendored
Normal file
31
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func (n *Namespace) Syscall() int {
|
||||||
|
return namespaceInfo[n.Type]
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespaceInfo = map[NamespaceType]int{
|
||||||
|
NEWNET: syscall.CLONE_NEWNET,
|
||||||
|
NEWNS: syscall.CLONE_NEWNS,
|
||||||
|
NEWUSER: syscall.CLONE_NEWUSER,
|
||||||
|
NEWIPC: syscall.CLONE_NEWIPC,
|
||||||
|
NEWUTS: syscall.CLONE_NEWUTS,
|
||||||
|
NEWPID: syscall.CLONE_NEWPID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneFlags parses the container's Namespaces options to set the correct
|
||||||
|
// flags on clone, unshare. This function returns flags only for new namespaces.
|
||||||
|
func (n *Namespaces) CloneFlags() uintptr {
|
||||||
|
var flag int
|
||||||
|
for _, v := range *n {
|
||||||
|
if v.Path != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
flag |= namespaceInfo[v.Type]
|
||||||
|
}
|
||||||
|
return uintptr(flag)
|
||||||
|
}
|
15
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go
generated
vendored
Normal file
15
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// +build !linux,!windows
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
func (n *Namespace) Syscall() int {
|
||||||
|
panic("No namespace syscall support")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneFlags parses the container's Namespaces options to set the correct
|
||||||
|
// flags on clone, unshare. This function returns flags only for new namespaces.
|
||||||
|
func (n *Namespaces) CloneFlags() uintptr {
|
||||||
|
panic("No namespace syscall support")
|
||||||
|
return uintptr(0)
|
||||||
|
}
|
127
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unix.go
generated
vendored
Normal file
127
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
// +build linux freebsd
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NEWNET NamespaceType = "NEWNET"
|
||||||
|
NEWPID NamespaceType = "NEWPID"
|
||||||
|
NEWNS NamespaceType = "NEWNS"
|
||||||
|
NEWUTS NamespaceType = "NEWUTS"
|
||||||
|
NEWIPC NamespaceType = "NEWIPC"
|
||||||
|
NEWUSER NamespaceType = "NEWUSER"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nsLock sync.Mutex
|
||||||
|
supportedNamespaces = make(map[NamespaceType]bool)
|
||||||
|
)
|
||||||
|
|
||||||
|
// nsToFile converts the namespace type to its filename
|
||||||
|
func nsToFile(ns NamespaceType) string {
|
||||||
|
switch ns {
|
||||||
|
case NEWNET:
|
||||||
|
return "net"
|
||||||
|
case NEWNS:
|
||||||
|
return "mnt"
|
||||||
|
case NEWPID:
|
||||||
|
return "pid"
|
||||||
|
case NEWIPC:
|
||||||
|
return "ipc"
|
||||||
|
case NEWUSER:
|
||||||
|
return "user"
|
||||||
|
case NEWUTS:
|
||||||
|
return "uts"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNamespaceSupported returns whether a namespace is available or
|
||||||
|
// not
|
||||||
|
func IsNamespaceSupported(ns NamespaceType) bool {
|
||||||
|
nsLock.Lock()
|
||||||
|
defer nsLock.Unlock()
|
||||||
|
supported, ok := supportedNamespaces[ns]
|
||||||
|
if ok {
|
||||||
|
return supported
|
||||||
|
}
|
||||||
|
nsFile := nsToFile(ns)
|
||||||
|
// if the namespace type is unknown, just return false
|
||||||
|
if nsFile == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err := os.Stat(fmt.Sprintf("/proc/self/ns/%s", nsFile))
|
||||||
|
// a namespace is supported if it exists and we have permissions to read it
|
||||||
|
supported = err == nil
|
||||||
|
supportedNamespaces[ns] = supported
|
||||||
|
return supported
|
||||||
|
}
|
||||||
|
|
||||||
|
func NamespaceTypes() []NamespaceType {
|
||||||
|
return []NamespaceType{
|
||||||
|
NEWNET,
|
||||||
|
NEWPID,
|
||||||
|
NEWNS,
|
||||||
|
NEWUTS,
|
||||||
|
NEWIPC,
|
||||||
|
NEWUSER,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace defines configuration for each namespace. It specifies an
|
||||||
|
// alternate path that is able to be joined via setns.
|
||||||
|
type Namespace struct {
|
||||||
|
Type NamespaceType `json:"type"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) GetPath(pid int) string {
|
||||||
|
if n.Path != "" {
|
||||||
|
return n.Path
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("/proc/%d/ns/%s", pid, nsToFile(n.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) Remove(t NamespaceType) bool {
|
||||||
|
i := n.index(t)
|
||||||
|
if i == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*n = append((*n)[:i], (*n)[i+1:]...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) Add(t NamespaceType, path string) {
|
||||||
|
i := n.index(t)
|
||||||
|
if i == -1 {
|
||||||
|
*n = append(*n, Namespace{Type: t, Path: path})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(*n)[i].Path = path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) index(t NamespaceType) int {
|
||||||
|
for i, ns := range *n {
|
||||||
|
if ns.Type == t {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) Contains(t NamespaceType) bool {
|
||||||
|
return n.index(t) != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) PathOf(t NamespaceType) string {
|
||||||
|
i := n.index(t)
|
||||||
|
if i == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return (*n)[i].Path
|
||||||
|
}
|
8
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go
generated
vendored
Normal file
8
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// +build !linux,!freebsd
|
||||||
|
|
||||||
|
package configs
|
||||||
|
|
||||||
|
// Namespace defines configuration for each namespace. It specifies an
|
||||||
|
// alternate path that is able to be joined via setns.
|
||||||
|
type Namespace struct {
|
||||||
|
}
|
72
vendor/github.com/opencontainers/runc/libcontainer/configs/network.go
generated
vendored
Normal file
72
vendor/github.com/opencontainers/runc/libcontainer/configs/network.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
// Network defines configuration for a container's networking stack
|
||||||
|
//
|
||||||
|
// The network configuration can be omitted from a container causing the
|
||||||
|
// container to be setup with the host's networking stack
|
||||||
|
type Network struct {
|
||||||
|
// Type sets the networks type, commonly veth and loopback
|
||||||
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
// Name of the network interface
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// The bridge to use.
|
||||||
|
Bridge string `json:"bridge"`
|
||||||
|
|
||||||
|
// MacAddress contains the MAC address to set on the network interface
|
||||||
|
MacAddress string `json:"mac_address"`
|
||||||
|
|
||||||
|
// Address contains the IPv4 and mask to set on the network interface
|
||||||
|
Address string `json:"address"`
|
||||||
|
|
||||||
|
// Gateway sets the gateway address that is used as the default for the interface
|
||||||
|
Gateway string `json:"gateway"`
|
||||||
|
|
||||||
|
// IPv6Address contains the IPv6 and mask to set on the network interface
|
||||||
|
IPv6Address string `json:"ipv6_address"`
|
||||||
|
|
||||||
|
// IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface
|
||||||
|
IPv6Gateway string `json:"ipv6_gateway"`
|
||||||
|
|
||||||
|
// Mtu sets the mtu value for the interface and will be mirrored on both the host and
|
||||||
|
// container's interfaces if a pair is created, specifically in the case of type veth
|
||||||
|
// Note: This does not apply to loopback interfaces.
|
||||||
|
Mtu int `json:"mtu"`
|
||||||
|
|
||||||
|
// TxQueueLen sets the tx_queuelen value for the interface and will be mirrored on both the host and
|
||||||
|
// container's interfaces if a pair is created, specifically in the case of type veth
|
||||||
|
// Note: This does not apply to loopback interfaces.
|
||||||
|
TxQueueLen int `json:"txqueuelen"`
|
||||||
|
|
||||||
|
// HostInterfaceName is a unique name of a veth pair that resides on in the host interface of the
|
||||||
|
// container.
|
||||||
|
HostInterfaceName string `json:"host_interface_name"`
|
||||||
|
|
||||||
|
// HairpinMode specifies if hairpin NAT should be enabled on the virtual interface
|
||||||
|
// bridge port in the case of type veth
|
||||||
|
// Note: This is unsupported on some systems.
|
||||||
|
// Note: This does not apply to loopback interfaces.
|
||||||
|
HairpinMode bool `json:"hairpin_mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routes can be specified to create entries in the route table as the container is started
|
||||||
|
//
|
||||||
|
// All of destination, source, and gateway should be either IPv4 or IPv6.
|
||||||
|
// One of the three options must be present, and omitted entries will use their
|
||||||
|
// IP family default for the route table. For IPv4 for example, setting the
|
||||||
|
// gateway to 1.2.3.4 and the interface to eth0 will set up a standard
|
||||||
|
// destination of 0.0.0.0(or *) when viewed in the route table.
|
||||||
|
type Route struct {
|
||||||
|
// Sets the destination and mask, should be a CIDR. Accepts IPv4 and IPv6
|
||||||
|
Destination string `json:"destination"`
|
||||||
|
|
||||||
|
// Sets the source and mask, should be a CIDR. Accepts IPv4 and IPv6
|
||||||
|
Source string `json:"source"`
|
||||||
|
|
||||||
|
// Sets the gateway. Accepts IPv4 and IPv6
|
||||||
|
Gateway string `json:"gateway"`
|
||||||
|
|
||||||
|
// The device to set this route up for, for example: eth0
|
||||||
|
InterfaceName string `json:"interface_name"`
|
||||||
|
}
|
15
vendor/github.com/opencontainers/runc/libcontainer/console.go
generated
vendored
Normal file
15
vendor/github.com/opencontainers/runc/libcontainer/console.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// Console represents a pseudo TTY.
|
||||||
|
type Console interface {
|
||||||
|
io.ReadWriter
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
// Path returns the filesystem path to the slave side of the pty.
|
||||||
|
Path() string
|
||||||
|
|
||||||
|
// Fd returns the fd for the master of the pty.
|
||||||
|
Fd() uintptr
|
||||||
|
}
|
13
vendor/github.com/opencontainers/runc/libcontainer/console_freebsd.go
generated
vendored
Normal file
13
vendor/github.com/opencontainers/runc/libcontainer/console_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// +build freebsd
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewConsole returns an initalized console that can be used within a container by copying bytes
|
||||||
|
// from the master side to the slave that is attached as the tty for the container's init process.
|
||||||
|
func NewConsole(uid, gid int) (Console, error) {
|
||||||
|
return nil, errors.New("libcontainer console is not supported on FreeBSD")
|
||||||
|
}
|
145
vendor/github.com/opencontainers/runc/libcontainer/console_linux.go
generated
vendored
Normal file
145
vendor/github.com/opencontainers/runc/libcontainer/console_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewConsole returns an initalized console that can be used within a container by copying bytes
|
||||||
|
// from the master side to the slave that is attached as the tty for the container's init process.
|
||||||
|
func NewConsole(uid, gid int) (Console, error) {
|
||||||
|
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
console, err := ptsname(master)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := unlockpt(master); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := os.Chmod(console, 0600); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := os.Chown(console, uid, gid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &linuxConsole{
|
||||||
|
slavePath: console,
|
||||||
|
master: master,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConsoleFromPath is an internal function returning an initialized console for use inside
|
||||||
|
// a container's MNT namespace.
|
||||||
|
func newConsoleFromPath(slavePath string) *linuxConsole {
|
||||||
|
return &linuxConsole{
|
||||||
|
slavePath: slavePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// linuxConsole is a linux psuedo TTY for use within a container.
|
||||||
|
type linuxConsole struct {
|
||||||
|
master *os.File
|
||||||
|
slavePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linuxConsole) Fd() uintptr {
|
||||||
|
return c.master.Fd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linuxConsole) Path() string {
|
||||||
|
return c.slavePath
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linuxConsole) Read(b []byte) (int, error) {
|
||||||
|
return c.master.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linuxConsole) Write(b []byte) (int, error) {
|
||||||
|
return c.master.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linuxConsole) Close() error {
|
||||||
|
if m := c.master; m != nil {
|
||||||
|
return m.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mount initializes the console inside the rootfs mounting with the specified mount label
|
||||||
|
// and applying the correct ownership of the console.
|
||||||
|
func (c *linuxConsole) mount(rootfs, mountLabel string) error {
|
||||||
|
oldMask := syscall.Umask(0000)
|
||||||
|
defer syscall.Umask(oldMask)
|
||||||
|
if err := label.SetFileLabel(c.slavePath, mountLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dest := filepath.Join(rootfs, "/dev/console")
|
||||||
|
f, err := os.Create(dest)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
return syscall.Mount(c.slavePath, dest, "bind", syscall.MS_BIND, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// dupStdio opens the slavePath for the console and dups the fds to the current
|
||||||
|
// processes stdio, fd 0,1,2.
|
||||||
|
func (c *linuxConsole) dupStdio() error {
|
||||||
|
slave, err := c.open(syscall.O_RDWR)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fd := int(slave.Fd())
|
||||||
|
for _, i := range []int{0, 1, 2} {
|
||||||
|
if err := syscall.Dup3(fd, i, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// open is a clone of os.OpenFile without the O_CLOEXEC used to open the pty slave.
|
||||||
|
func (c *linuxConsole) open(flag int) (*os.File, error) {
|
||||||
|
r, e := syscall.Open(c.slavePath, flag, 0)
|
||||||
|
if e != nil {
|
||||||
|
return nil, &os.PathError{
|
||||||
|
Op: "open",
|
||||||
|
Path: c.slavePath,
|
||||||
|
Err: e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(r), c.slavePath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctl(fd uintptr, flag, data uintptr) error {
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||||
|
// unlockpt should be called before opening the slave side of a pty.
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
var u int32
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ptsname retrieves the name of the first available pts for the given master.
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
var n int32
|
||||||
|
if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("/dev/pts/%d", n), nil
|
||||||
|
}
|
30
vendor/github.com/opencontainers/runc/libcontainer/console_windows.go
generated
vendored
Normal file
30
vendor/github.com/opencontainers/runc/libcontainer/console_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
// NewConsole returns an initalized console that can be used within a container
|
||||||
|
func NewConsole(uid, gid int) (Console, error) {
|
||||||
|
return &windowsConsole{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// windowsConsole is a Windows psuedo TTY for use within a container.
|
||||||
|
type windowsConsole struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *windowsConsole) Fd() uintptr {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *windowsConsole) Path() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *windowsConsole) Read(b []byte) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *windowsConsole) Write(b []byte) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *windowsConsole) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
144
vendor/github.com/opencontainers/runc/libcontainer/container.go
generated
vendored
Normal file
144
vendor/github.com/opencontainers/runc/libcontainer/container.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// Libcontainer provides a native Go implementation for creating containers
|
||||||
|
// with namespaces, cgroups, capabilities, and filesystem access controls.
|
||||||
|
// It allows you to manage the lifecycle of the container performing additional operations
|
||||||
|
// after the container is created.
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The status of a container.
|
||||||
|
type Status int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The container exists but has not been run yet
|
||||||
|
Created Status = iota
|
||||||
|
|
||||||
|
// The container exists and is running.
|
||||||
|
Running
|
||||||
|
|
||||||
|
// The container exists, it is in the process of being paused.
|
||||||
|
Pausing
|
||||||
|
|
||||||
|
// The container exists, but all its processes are paused.
|
||||||
|
Paused
|
||||||
|
|
||||||
|
// The container does not exist.
|
||||||
|
Destroyed
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Status) String() string {
|
||||||
|
switch s {
|
||||||
|
case Created:
|
||||||
|
return "created"
|
||||||
|
case Running:
|
||||||
|
return "running"
|
||||||
|
case Pausing:
|
||||||
|
return "pausing"
|
||||||
|
case Paused:
|
||||||
|
return "paused"
|
||||||
|
case Destroyed:
|
||||||
|
return "destroyed"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseState represents the platform agnostic pieces relating to a
|
||||||
|
// running container's state
|
||||||
|
type BaseState struct {
|
||||||
|
// ID is the container ID.
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// InitProcessPid is the init process id in the parent namespace.
|
||||||
|
InitProcessPid int `json:"init_process_pid"`
|
||||||
|
|
||||||
|
// InitProcessStartTime is the init process start time in clock cycles since boot time.
|
||||||
|
InitProcessStartTime string `json:"init_process_start"`
|
||||||
|
|
||||||
|
// Created is the unix timestamp for the creation time of the container in UTC
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
|
||||||
|
// Config is the container's configuration.
|
||||||
|
Config configs.Config `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A libcontainer container object.
|
||||||
|
//
|
||||||
|
// Each container is thread-safe within the same process. Since a container can
|
||||||
|
// be destroyed by a separate process, any function may return that the container
|
||||||
|
// was not found. BaseContainer includes methods that are platform agnostic.
|
||||||
|
type BaseContainer interface {
|
||||||
|
// Returns the ID of the container
|
||||||
|
ID() string
|
||||||
|
|
||||||
|
// Returns the current status of the container.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// ContainerDestroyed - Container no longer exists,
|
||||||
|
// Systemerror - System error.
|
||||||
|
Status() (Status, error)
|
||||||
|
|
||||||
|
// State returns the current container's state information.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// Systemerror - System error.
|
||||||
|
State() (*State, error)
|
||||||
|
|
||||||
|
// Returns the current config of the container.
|
||||||
|
Config() configs.Config
|
||||||
|
|
||||||
|
// Returns the PIDs inside this container. The PIDs are in the namespace of the calling process.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// ContainerDestroyed - Container no longer exists,
|
||||||
|
// Systemerror - System error.
|
||||||
|
//
|
||||||
|
// Some of the returned PIDs may no longer refer to processes in the Container, unless
|
||||||
|
// the Container state is PAUSED in which case every PID in the slice is valid.
|
||||||
|
Processes() ([]int, error)
|
||||||
|
|
||||||
|
// Returns statistics for the container.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// ContainerDestroyed - Container no longer exists,
|
||||||
|
// Systemerror - System error.
|
||||||
|
Stats() (*Stats, error)
|
||||||
|
|
||||||
|
// Set resources of container as configured
|
||||||
|
//
|
||||||
|
// We can use this to change resources when containers are running.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// Systemerror - System error.
|
||||||
|
Set(config configs.Config) error
|
||||||
|
|
||||||
|
// Start a process inside the container. Returns error if process fails to
|
||||||
|
// start. You can track process lifecycle with passed Process structure.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// ContainerDestroyed - Container no longer exists,
|
||||||
|
// ConfigInvalid - config is invalid,
|
||||||
|
// ContainerPaused - Container is paused,
|
||||||
|
// Systemerror - System error.
|
||||||
|
Start(process *Process) (err error)
|
||||||
|
|
||||||
|
// Destroys the container after killing all running processes.
|
||||||
|
//
|
||||||
|
// Any event registrations are removed before the container is destroyed.
|
||||||
|
// No error is returned if the container is already destroyed.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// Systemerror - System error.
|
||||||
|
Destroy() error
|
||||||
|
|
||||||
|
// Signal sends the provided signal code to the container's initial process.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// Systemerror - System error.
|
||||||
|
Signal(s os.Signal) error
|
||||||
|
}
|
1228
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
Normal file
1228
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
20
vendor/github.com/opencontainers/runc/libcontainer/container_windows.go
generated
vendored
Normal file
20
vendor/github.com/opencontainers/runc/libcontainer/container_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
// State represents a running container's state
|
||||||
|
type State struct {
|
||||||
|
BaseState
|
||||||
|
|
||||||
|
// Platform specific fields below here
|
||||||
|
}
|
||||||
|
|
||||||
|
// A libcontainer container object.
|
||||||
|
//
|
||||||
|
// Each container is thread-safe within the same process. Since a container can
|
||||||
|
// be destroyed by a separate process, any function may return that the container
|
||||||
|
// was not found.
|
||||||
|
type Container interface {
|
||||||
|
BaseContainer
|
||||||
|
|
||||||
|
// Methods below here are platform specific
|
||||||
|
|
||||||
|
}
|
37
vendor/github.com/opencontainers/runc/libcontainer/criu_opts_unix.go
generated
vendored
Normal file
37
vendor/github.com/opencontainers/runc/libcontainer/criu_opts_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// +build linux freebsd
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
// cgroup restoring strategy provided by criu
|
||||||
|
type cg_mode uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
CRIU_CG_MODE_SOFT cg_mode = 3 + iota // restore cgroup properties if only dir created by criu
|
||||||
|
CRIU_CG_MODE_FULL // always restore all cgroups and their properties
|
||||||
|
CRIU_CG_MODE_STRICT // restore all, requiring them to not present in the system
|
||||||
|
CRIU_CG_MODE_DEFAULT // the same as CRIU_CG_MODE_SOFT
|
||||||
|
)
|
||||||
|
|
||||||
|
type CriuPageServerInfo struct {
|
||||||
|
Address string // IP address of CRIU page server
|
||||||
|
Port int32 // port number of CRIU page server
|
||||||
|
}
|
||||||
|
|
||||||
|
type VethPairName struct {
|
||||||
|
ContainerInterfaceName string
|
||||||
|
HostInterfaceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CriuOpts struct {
|
||||||
|
ImagesDirectory string // directory for storing image files
|
||||||
|
WorkDirectory string // directory to cd and write logs/pidfiles/stats to
|
||||||
|
LeaveRunning bool // leave container in running state after checkpoint
|
||||||
|
TcpEstablished bool // checkpoint/restore established TCP connections
|
||||||
|
ExternalUnixConnections bool // allow external unix connections
|
||||||
|
ShellJob bool // allow to dump and restore shell jobs
|
||||||
|
FileLocks bool // handle file locks, for safety
|
||||||
|
PageServer CriuPageServerInfo // allow to dump to criu page server
|
||||||
|
VethPairs []VethPairName // pass the veth to criu when restore
|
||||||
|
ManageCgroupsMode cg_mode // dump or restore cgroup mode
|
||||||
|
EmptyNs uint32 // don't c/r properties for namespace from this mask
|
||||||
|
}
|
6
vendor/github.com/opencontainers/runc/libcontainer/criu_opts_windows.go
generated
vendored
Normal file
6
vendor/github.com/opencontainers/runc/libcontainer/criu_opts_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
// TODO Windows: This can ultimately be entirely factored out as criu is
|
||||||
|
// a Unix concept not relevant on Windows.
|
||||||
|
type CriuOpts struct {
|
||||||
|
}
|
70
vendor/github.com/opencontainers/runc/libcontainer/error.go
generated
vendored
Normal file
70
vendor/github.com/opencontainers/runc/libcontainer/error.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// API error code type.
|
||||||
|
type ErrorCode int
|
||||||
|
|
||||||
|
// API error codes.
|
||||||
|
const (
|
||||||
|
// Factory errors
|
||||||
|
IdInUse ErrorCode = iota
|
||||||
|
InvalidIdFormat
|
||||||
|
|
||||||
|
// Container errors
|
||||||
|
ContainerNotExists
|
||||||
|
ContainerPaused
|
||||||
|
ContainerNotStopped
|
||||||
|
ContainerNotRunning
|
||||||
|
ContainerNotPaused
|
||||||
|
|
||||||
|
// Process errors
|
||||||
|
NoProcessOps
|
||||||
|
|
||||||
|
// Common errors
|
||||||
|
ConfigInvalid
|
||||||
|
ConsoleExists
|
||||||
|
SystemError
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c ErrorCode) String() string {
|
||||||
|
switch c {
|
||||||
|
case IdInUse:
|
||||||
|
return "Id already in use"
|
||||||
|
case InvalidIdFormat:
|
||||||
|
return "Invalid format"
|
||||||
|
case ContainerPaused:
|
||||||
|
return "Container paused"
|
||||||
|
case ConfigInvalid:
|
||||||
|
return "Invalid configuration"
|
||||||
|
case SystemError:
|
||||||
|
return "System error"
|
||||||
|
case ContainerNotExists:
|
||||||
|
return "Container does not exist"
|
||||||
|
case ContainerNotStopped:
|
||||||
|
return "Container is not stopped"
|
||||||
|
case ContainerNotRunning:
|
||||||
|
return "Container is not running"
|
||||||
|
case ConsoleExists:
|
||||||
|
return "Console exists for process"
|
||||||
|
case ContainerNotPaused:
|
||||||
|
return "Container is not paused"
|
||||||
|
case NoProcessOps:
|
||||||
|
return "No process operations"
|
||||||
|
default:
|
||||||
|
return "Unknown error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Error type.
|
||||||
|
type Error interface {
|
||||||
|
error
|
||||||
|
|
||||||
|
// Returns a verbose string including the error message
|
||||||
|
// and a representation of the stack trace suitable for
|
||||||
|
// printing.
|
||||||
|
Detail(w io.Writer) error
|
||||||
|
|
||||||
|
// Returns the error code for this error.
|
||||||
|
Code() ErrorCode
|
||||||
|
}
|
45
vendor/github.com/opencontainers/runc/libcontainer/factory.go
generated
vendored
Normal file
45
vendor/github.com/opencontainers/runc/libcontainer/factory.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Factory interface {
|
||||||
|
// Creates a new container with the given id and starts the initial process inside it.
|
||||||
|
// id must be a string containing only letters, digits and underscores and must contain
|
||||||
|
// between 1 and 1024 characters, inclusive.
|
||||||
|
//
|
||||||
|
// The id must not already be in use by an existing container. Containers created using
|
||||||
|
// a factory with the same path (and file system) must have distinct ids.
|
||||||
|
//
|
||||||
|
// Returns the new container with a running process.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// IdInUse - id is already in use by a container
|
||||||
|
// InvalidIdFormat - id has incorrect format
|
||||||
|
// ConfigInvalid - config is invalid
|
||||||
|
// Systemerror - System error
|
||||||
|
//
|
||||||
|
// On error, any partially created container parts are cleaned up (the operation is atomic).
|
||||||
|
Create(id string, config *configs.Config) (Container, error)
|
||||||
|
|
||||||
|
// Load takes an ID for an existing container and returns the container information
|
||||||
|
// from the state. This presents a read only view of the container.
|
||||||
|
//
|
||||||
|
// errors:
|
||||||
|
// Path does not exist
|
||||||
|
// Container is stopped
|
||||||
|
// System error
|
||||||
|
Load(id string) (Container, error)
|
||||||
|
|
||||||
|
// StartInitialization is an internal API to libcontainer used during the reexec of the
|
||||||
|
// container.
|
||||||
|
//
|
||||||
|
// Errors:
|
||||||
|
// Pipe connection error
|
||||||
|
// System error
|
||||||
|
StartInitialization() error
|
||||||
|
|
||||||
|
// Type returns info string about factory type (e.g. lxc, libcontainer...)
|
||||||
|
Type() string
|
||||||
|
}
|
290
vendor/github.com/opencontainers/runc/libcontainer/factory_linux.go
generated
vendored
Normal file
290
vendor/github.com/opencontainers/runc/libcontainer/factory_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime/debug"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs/validate"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateFilename = "state.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
idRegex = regexp.MustCompile(`^[\w-\.]+$`)
|
||||||
|
maxIdLen = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitArgs returns an options func to configure a LinuxFactory with the
|
||||||
|
// provided init arguments.
|
||||||
|
func InitArgs(args ...string) func(*LinuxFactory) error {
|
||||||
|
return func(l *LinuxFactory) error {
|
||||||
|
name := args[0]
|
||||||
|
if filepath.Base(name) == name {
|
||||||
|
if lp, err := exec.LookPath(name); err == nil {
|
||||||
|
name = lp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
abs, err := filepath.Abs(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name = abs
|
||||||
|
}
|
||||||
|
l.InitPath = "/proc/self/exe"
|
||||||
|
l.InitArgs = append([]string{name}, args[1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitPath returns an options func to configure a LinuxFactory with the
|
||||||
|
// provided absolute path to the init binary and arguements.
|
||||||
|
func InitPath(path string, args ...string) func(*LinuxFactory) error {
|
||||||
|
return func(l *LinuxFactory) error {
|
||||||
|
l.InitPath = path
|
||||||
|
l.InitArgs = args
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemdCgroups is an options func to configure a LinuxFactory to return
|
||||||
|
// containers that use systemd to create and manage cgroups.
|
||||||
|
func SystemdCgroups(l *LinuxFactory) error {
|
||||||
|
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
|
||||||
|
return &systemd.Manager{
|
||||||
|
Cgroups: config,
|
||||||
|
Paths: paths,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
func Cgroupfs(l *LinuxFactory) error {
|
||||||
|
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
|
||||||
|
return &fs.Manager{
|
||||||
|
Cgroups: config,
|
||||||
|
Paths: paths,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TmpfsRoot is an option func to mount LinuxFactory.Root to tmpfs.
|
||||||
|
func TmpfsRoot(l *LinuxFactory) error {
|
||||||
|
mounted, err := mount.Mounted(l.Root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !mounted {
|
||||||
|
if err := syscall.Mount("tmpfs", l.Root, "tmpfs", 0, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a linux based container factory based in the root directory and
|
||||||
|
// configures the factory with the provided option funcs.
|
||||||
|
func New(root string, options ...func(*LinuxFactory) error) (Factory, error) {
|
||||||
|
if root != "" {
|
||||||
|
if err := os.MkdirAll(root, 0700); err != nil {
|
||||||
|
return nil, newGenericError(err, SystemError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l := &LinuxFactory{
|
||||||
|
Root: root,
|
||||||
|
Validator: validate.New(),
|
||||||
|
CriuPath: "criu",
|
||||||
|
}
|
||||||
|
InitArgs(os.Args[0], "init")(l)
|
||||||
|
Cgroupfs(l)
|
||||||
|
for _, opt := range options {
|
||||||
|
if err := opt(l); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinuxFactory implements the default factory interface for linux based systems.
|
||||||
|
type LinuxFactory struct {
|
||||||
|
// Root directory for the factory to store state.
|
||||||
|
Root string
|
||||||
|
|
||||||
|
// InitPath is the absolute path to the init binary.
|
||||||
|
InitPath string
|
||||||
|
|
||||||
|
// InitArgs are arguments for calling the init responsibilities for spawning
|
||||||
|
// a container.
|
||||||
|
InitArgs []string
|
||||||
|
|
||||||
|
// CriuPath is the path to the criu binary used for checkpoint and restore of
|
||||||
|
// containers.
|
||||||
|
CriuPath string
|
||||||
|
|
||||||
|
// Validator provides validation to container configurations.
|
||||||
|
Validator validate.Validator
|
||||||
|
|
||||||
|
// NewCgroupsManager returns an initialized cgroups manager for a single container.
|
||||||
|
NewCgroupsManager func(config *configs.Cgroup, paths map[string]string) cgroups.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) {
|
||||||
|
if l.Root == "" {
|
||||||
|
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
|
||||||
|
}
|
||||||
|
if err := l.validateID(id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := l.Validator.Validate(config); err != nil {
|
||||||
|
return nil, newGenericError(err, ConfigInvalid)
|
||||||
|
}
|
||||||
|
containerRoot := filepath.Join(l.Root, id)
|
||||||
|
if _, err := os.Stat(containerRoot); err == nil {
|
||||||
|
return nil, newGenericError(fmt.Errorf("container with id exists: %v", id), IdInUse)
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return nil, newGenericError(err, SystemError)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(containerRoot, 0700); err != nil {
|
||||||
|
return nil, newGenericError(err, SystemError)
|
||||||
|
}
|
||||||
|
c := &linuxContainer{
|
||||||
|
id: id,
|
||||||
|
root: containerRoot,
|
||||||
|
config: config,
|
||||||
|
initPath: l.InitPath,
|
||||||
|
initArgs: l.InitArgs,
|
||||||
|
criuPath: l.CriuPath,
|
||||||
|
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
|
||||||
|
}
|
||||||
|
c.state = &stoppedState{c: c}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
state, err := l.loadState(containerRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := &nonChildProcess{
|
||||||
|
processPid: state.InitProcessPid,
|
||||||
|
processStartTime: state.InitProcessStartTime,
|
||||||
|
fds: state.ExternalDescriptors,
|
||||||
|
}
|
||||||
|
c := &linuxContainer{
|
||||||
|
initProcess: r,
|
||||||
|
id: id,
|
||||||
|
config: &state.Config,
|
||||||
|
initPath: l.InitPath,
|
||||||
|
initArgs: l.InitArgs,
|
||||||
|
criuPath: l.CriuPath,
|
||||||
|
cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
|
||||||
|
root: containerRoot,
|
||||||
|
created: state.Created,
|
||||||
|
}
|
||||||
|
c.state = &createdState{c: c, s: Created}
|
||||||
|
if err := c.refreshState(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LinuxFactory) Type() string {
|
||||||
|
return "libcontainer"
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
|
||||||
|
// This is a low level implementation detail of the reexec and should not be consumed externally
|
||||||
|
func (l *LinuxFactory) StartInitialization() (err error) {
|
||||||
|
fdStr := os.Getenv("_LIBCONTAINER_INITPIPE")
|
||||||
|
pipefd, err := strconv.Atoi(fdStr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error converting env var _LIBCONTAINER_INITPIPE(%q) to an int: %s", fdStr, err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
pipe = os.NewFile(uintptr(pipefd), "pipe")
|
||||||
|
it = initType(os.Getenv("_LIBCONTAINER_INITTYPE"))
|
||||||
|
)
|
||||||
|
// clear the current process's environment to clean any libcontainer
|
||||||
|
// specific env vars.
|
||||||
|
os.Clearenv()
|
||||||
|
var i initer
|
||||||
|
defer func() {
|
||||||
|
// We have an error during the initialization of the container's init,
|
||||||
|
// send it back to the parent process in the form of an initError.
|
||||||
|
// If container's init successed, syscall.Exec will not return, hence
|
||||||
|
// this defer function will never be called.
|
||||||
|
if _, ok := i.(*linuxStandardInit); ok {
|
||||||
|
// Synchronisation only necessary for standard init.
|
||||||
|
if err := utils.WriteJSON(pipe, syncT{procError}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := utils.WriteJSON(pipe, newSystemError(err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// ensure that this pipe is always closed
|
||||||
|
pipe.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = fmt.Errorf("panic from initialization: %v, %v", e, string(debug.Stack()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
i, err = newContainerInit(it, pipe)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return i.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LinuxFactory) loadState(root string) (*State, error) {
|
||||||
|
f, err := os.Open(filepath.Join(root, stateFilename))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, newGenericError(err, ContainerNotExists)
|
||||||
|
}
|
||||||
|
return nil, newGenericError(err, SystemError)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var state *State
|
||||||
|
if err := json.NewDecoder(f).Decode(&state); err != nil {
|
||||||
|
return nil, newGenericError(err, SystemError)
|
||||||
|
}
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LinuxFactory) validateID(id string) error {
|
||||||
|
if !idRegex.MatchString(id) {
|
||||||
|
return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
|
||||||
|
}
|
||||||
|
if len(id) > maxIdLen {
|
||||||
|
return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
87
vendor/github.com/opencontainers/runc/libcontainer/generic_error.go
generated
vendored
Normal file
87
vendor/github.com/opencontainers/runc/libcontainer/generic_error.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/stacktrace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type syncType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
procReady syncType = iota
|
||||||
|
procError
|
||||||
|
procRun
|
||||||
|
procHooks
|
||||||
|
procResume
|
||||||
|
)
|
||||||
|
|
||||||
|
type syncT struct {
|
||||||
|
Type syncType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}}
|
||||||
|
Code: {{.ECode}}
|
||||||
|
{{if .Message }}
|
||||||
|
Message: {{.Message}}
|
||||||
|
{{end}}
|
||||||
|
Frames:{{range $i, $frame := .Stack.Frames}}
|
||||||
|
---
|
||||||
|
{{$i}}: {{$frame.Function}}
|
||||||
|
Package: {{$frame.Package}}
|
||||||
|
File: {{$frame.File}}@{{$frame.Line}}{{end}}
|
||||||
|
`))
|
||||||
|
|
||||||
|
func newGenericError(err error, c ErrorCode) Error {
|
||||||
|
if le, ok := err.(Error); ok {
|
||||||
|
return le
|
||||||
|
}
|
||||||
|
gerr := &genericError{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Err: err,
|
||||||
|
ECode: c,
|
||||||
|
Stack: stacktrace.Capture(1),
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
gerr.Message = err.Error()
|
||||||
|
}
|
||||||
|
return gerr
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSystemError(err error) Error {
|
||||||
|
if le, ok := err.(Error); ok {
|
||||||
|
return le
|
||||||
|
}
|
||||||
|
gerr := &genericError{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Err: err,
|
||||||
|
ECode: SystemError,
|
||||||
|
Stack: stacktrace.Capture(1),
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
gerr.Message = err.Error()
|
||||||
|
}
|
||||||
|
return gerr
|
||||||
|
}
|
||||||
|
|
||||||
|
type genericError struct {
|
||||||
|
Timestamp time.Time
|
||||||
|
ECode ErrorCode
|
||||||
|
Err error `json:"-"`
|
||||||
|
Message string
|
||||||
|
Stack stacktrace.Stacktrace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *genericError) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *genericError) Code() ErrorCode {
|
||||||
|
return e.ECode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *genericError) Detail(w io.Writer) error {
|
||||||
|
return errorTemplate.Execute(w, e)
|
||||||
|
}
|
367
vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
generated
vendored
Normal file
367
vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/user"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
type initType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
initSetns initType = "setns"
|
||||||
|
initStandard initType = "standard"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pid struct {
|
||||||
|
Pid int `json:"pid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// network is an internal struct used to setup container networks.
|
||||||
|
type network struct {
|
||||||
|
configs.Network
|
||||||
|
|
||||||
|
// TempVethPeerName is a unique temporary veth peer name that was placed into
|
||||||
|
// the container's namespace.
|
||||||
|
TempVethPeerName string `json:"temp_veth_peer_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// initConfig is used for transferring parameters from Exec() to Init()
|
||||||
|
type initConfig struct {
|
||||||
|
Args []string `json:"args"`
|
||||||
|
Env []string `json:"env"`
|
||||||
|
Cwd string `json:"cwd"`
|
||||||
|
Capabilities []string `json:"capabilities"`
|
||||||
|
ProcessLabel string `json:"process_label"`
|
||||||
|
AppArmorProfile string `json:"apparmor_profile"`
|
||||||
|
NoNewPrivileges bool `json:"no_new_privileges"`
|
||||||
|
User string `json:"user"`
|
||||||
|
Config *configs.Config `json:"config"`
|
||||||
|
Console string `json:"console"`
|
||||||
|
Networks []*network `json:"network"`
|
||||||
|
PassedFilesCount int `json:"passed_files_count"`
|
||||||
|
ContainerId string `json:"containerid"`
|
||||||
|
Rlimits []configs.Rlimit `json:"rlimits"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type initer interface {
|
||||||
|
Init() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContainerInit(t initType, pipe *os.File) (initer, error) {
|
||||||
|
var config *initConfig
|
||||||
|
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := populateProcessEnvironment(config.Env); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case initSetns:
|
||||||
|
return &linuxSetnsInit{
|
||||||
|
config: config,
|
||||||
|
}, nil
|
||||||
|
case initStandard:
|
||||||
|
return &linuxStandardInit{
|
||||||
|
pipe: pipe,
|
||||||
|
parentPid: syscall.Getppid(),
|
||||||
|
config: config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unknown init type %q", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// populateProcessEnvironment loads the provided environment variables into the
|
||||||
|
// current processes's environment.
|
||||||
|
func populateProcessEnvironment(env []string) error {
|
||||||
|
for _, pair := range env {
|
||||||
|
p := strings.SplitN(pair, "=", 2)
|
||||||
|
if len(p) < 2 {
|
||||||
|
return fmt.Errorf("invalid environment '%v'", pair)
|
||||||
|
}
|
||||||
|
if err := os.Setenv(p[0], p[1]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalizeNamespace drops the caps, sets the correct user
|
||||||
|
// and working dir, and closes any leaked file descriptors
|
||||||
|
// before executing the command inside the namespace
|
||||||
|
func finalizeNamespace(config *initConfig) error {
|
||||||
|
// Ensure that all unwanted fds we may have accidentally
|
||||||
|
// inherited are marked close-on-exec so they stay out of the
|
||||||
|
// container
|
||||||
|
if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
capabilities := config.Config.Capabilities
|
||||||
|
if config.Capabilities != nil {
|
||||||
|
capabilities = config.Capabilities
|
||||||
|
}
|
||||||
|
w, err := newCapWhitelist(capabilities)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// drop capabilities in bounding set before changing user
|
||||||
|
if err := w.dropBoundingSet(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// preserve existing capabilities while we change users
|
||||||
|
if err := system.SetKeepCaps(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := setupUser(config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := system.ClearKeepCaps(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// drop all other capabilities
|
||||||
|
if err := w.drop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if config.Cwd != "" {
|
||||||
|
if err := syscall.Chdir(config.Cwd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncParentReady sends to the given pipe a JSON payload which indicates that
|
||||||
|
// the init is ready to Exec the child process. It then waits for the parent to
|
||||||
|
// indicate that it is cleared to Exec.
|
||||||
|
func syncParentReady(pipe io.ReadWriter) error {
|
||||||
|
// Tell parent.
|
||||||
|
if err := utils.WriteJSON(pipe, syncT{procReady}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Wait for parent to give the all-clear.
|
||||||
|
var procSync syncT
|
||||||
|
if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return fmt.Errorf("parent closed synchronisation channel")
|
||||||
|
}
|
||||||
|
if procSync.Type != procRun {
|
||||||
|
return fmt.Errorf("invalid synchronisation flag from parent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncParentHooks sends to the given pipe a JSON payload which indicates that
|
||||||
|
// the parent should execute pre-start hooks. It then waits for the parent to
|
||||||
|
// indicate that it is cleared to resume.
|
||||||
|
func syncParentHooks(pipe io.ReadWriter) error {
|
||||||
|
// Tell parent.
|
||||||
|
if err := utils.WriteJSON(pipe, syncT{procHooks}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Wait for parent to give the all-clear.
|
||||||
|
var procSync syncT
|
||||||
|
if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return fmt.Errorf("parent closed synchronisation channel")
|
||||||
|
}
|
||||||
|
if procSync.Type != procResume {
|
||||||
|
return fmt.Errorf("invalid synchronisation flag from parent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupUser changes the groups, gid, and uid for the user inside the container
|
||||||
|
func setupUser(config *initConfig) error {
|
||||||
|
// Set up defaults.
|
||||||
|
defaultExecUser := user.ExecUser{
|
||||||
|
Uid: syscall.Getuid(),
|
||||||
|
Gid: syscall.Getgid(),
|
||||||
|
Home: "/",
|
||||||
|
}
|
||||||
|
passwdPath, err := user.GetPasswdPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
groupPath, err := user.GetGroupPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
execUser, err := user.GetExecUserPath(config.User, &defaultExecUser, passwdPath, groupPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var addGroups []int
|
||||||
|
if len(config.Config.AdditionalGroups) > 0 {
|
||||||
|
addGroups, err = user.GetAdditionalGroupsPath(config.Config.AdditionalGroups, groupPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// before we change to the container's user make sure that the processes STDIO
|
||||||
|
// is correctly owned by the user that we are switching to.
|
||||||
|
if err := fixStdioPermissions(execUser); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
suppGroups := append(execUser.Sgids, addGroups...)
|
||||||
|
if err := syscall.Setgroups(suppGroups); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := system.Setgid(execUser.Gid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := system.Setuid(execUser.Uid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// if we didn't get HOME already, set it based on the user's HOME
|
||||||
|
if envHome := os.Getenv("HOME"); envHome == "" {
|
||||||
|
if err := os.Setenv("HOME", execUser.Home); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixStdioPermissions fixes the permissions of PID 1's STDIO within the container to the specified user.
|
||||||
|
// The ownership needs to match because it is created outside of the container and needs to be
|
||||||
|
// localized.
|
||||||
|
func fixStdioPermissions(u *user.ExecUser) error {
|
||||||
|
var null syscall.Stat_t
|
||||||
|
if err := syscall.Stat("/dev/null", &null); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, fd := range []uintptr{
|
||||||
|
os.Stdin.Fd(),
|
||||||
|
os.Stderr.Fd(),
|
||||||
|
os.Stdout.Fd(),
|
||||||
|
} {
|
||||||
|
var s syscall.Stat_t
|
||||||
|
if err := syscall.Fstat(int(fd), &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// skip chown of /dev/null if it was used as one of the STDIO fds.
|
||||||
|
if s.Rdev == null.Rdev {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := syscall.Fchown(int(fd), u.Uid, u.Gid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupNetwork sets up and initializes any network interface inside the container.
|
||||||
|
func setupNetwork(config *initConfig) error {
|
||||||
|
for _, config := range config.Networks {
|
||||||
|
strategy, err := getStrategy(config.Type)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := strategy.initialize(config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRoute(config *configs.Config) error {
|
||||||
|
for _, config := range config.Routes {
|
||||||
|
_, dst, err := net.ParseCIDR(config.Destination)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
src := net.ParseIP(config.Source)
|
||||||
|
if src == nil {
|
||||||
|
return fmt.Errorf("Invalid source for route: %s", config.Source)
|
||||||
|
}
|
||||||
|
gw := net.ParseIP(config.Gateway)
|
||||||
|
if gw == nil {
|
||||||
|
return fmt.Errorf("Invalid gateway for route: %s", config.Gateway)
|
||||||
|
}
|
||||||
|
l, err := netlink.LinkByName(config.InterfaceName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
route := &netlink.Route{
|
||||||
|
Scope: netlink.SCOPE_UNIVERSE,
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
Gw: gw,
|
||||||
|
LinkIndex: l.Attrs().Index,
|
||||||
|
}
|
||||||
|
if err := netlink.RouteAdd(route); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRlimits(limits []configs.Rlimit, pid int) error {
|
||||||
|
for _, rlimit := range limits {
|
||||||
|
if err := system.Prlimit(pid, rlimit.Type, syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}); err != nil {
|
||||||
|
return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setOomScoreAdj(oomScoreAdj int, pid int) error {
|
||||||
|
path := fmt.Sprintf("/proc/%d/oom_score_adj", pid)
|
||||||
|
|
||||||
|
return ioutil.WriteFile(path, []byte(strconv.Itoa(oomScoreAdj)), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
// killCgroupProcesses freezes then iterates over all the processes inside the
|
||||||
|
// manager's cgroups sending a SIGKILL to each process then waiting for them to
|
||||||
|
// exit.
|
||||||
|
func killCgroupProcesses(m cgroups.Manager) error {
|
||||||
|
var procs []*os.Process
|
||||||
|
if err := m.Freeze(configs.Frozen); err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
pids, err := m.GetAllPids()
|
||||||
|
if err != nil {
|
||||||
|
m.Freeze(configs.Thawed)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, pid := range pids {
|
||||||
|
p, err := os.FindProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procs = append(procs, p)
|
||||||
|
if err := p.Kill(); err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := m.Freeze(configs.Thawed); err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
for _, p := range procs {
|
||||||
|
if _, err := p.Wait(); err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
88
vendor/github.com/opencontainers/runc/libcontainer/message_linux.go
generated
vendored
Normal file
88
vendor/github.com/opencontainers/runc/libcontainer/message_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// list of known message types we want to send to bootstrap program
|
||||||
|
// The number is randomly chosen to not conflict with known netlink types
|
||||||
|
const (
|
||||||
|
InitMsg uint16 = 62000
|
||||||
|
CloneFlagsAttr uint16 = 27281
|
||||||
|
ConsolePathAttr uint16 = 27282
|
||||||
|
NsPathsAttr uint16 = 27283
|
||||||
|
UidmapAttr uint16 = 27284
|
||||||
|
GidmapAttr uint16 = 27285
|
||||||
|
SetgroupAttr uint16 = 27286
|
||||||
|
// When syscall.NLA_HDRLEN is in gccgo, take this out.
|
||||||
|
syscall_NLA_HDRLEN = (syscall.SizeofNlAttr + syscall.NLA_ALIGNTO - 1) & ^(syscall.NLA_ALIGNTO - 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Int32msg struct {
|
||||||
|
Type uint16
|
||||||
|
Value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// int32msg has the following representation
|
||||||
|
// | nlattr len | nlattr type |
|
||||||
|
// | uint32 value |
|
||||||
|
func (msg *Int32msg) Serialize() []byte {
|
||||||
|
buf := make([]byte, msg.Len())
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
native.PutUint16(buf[0:2], uint16(msg.Len()))
|
||||||
|
native.PutUint16(buf[2:4], msg.Type)
|
||||||
|
native.PutUint32(buf[4:8], msg.Value)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Int32msg) Len() int {
|
||||||
|
return syscall_NLA_HDRLEN + 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytemsg has the following representation
|
||||||
|
// | nlattr len | nlattr type |
|
||||||
|
// | value | pad |
|
||||||
|
type Bytemsg struct {
|
||||||
|
Type uint16
|
||||||
|
Value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Bytemsg) Serialize() []byte {
|
||||||
|
l := msg.Len()
|
||||||
|
buf := make([]byte, (l+syscall.NLA_ALIGNTO-1) & ^(syscall.NLA_ALIGNTO-1))
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
native.PutUint16(buf[0:2], uint16(l))
|
||||||
|
native.PutUint16(buf[2:4], msg.Type)
|
||||||
|
copy(buf[4:], msg.Value)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Bytemsg) Len() int {
|
||||||
|
return syscall_NLA_HDRLEN + len(msg.Value) + 1 // null-terminated
|
||||||
|
}
|
||||||
|
|
||||||
|
type Boolmsg struct {
|
||||||
|
Type uint16
|
||||||
|
Value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Boolmsg) Serialize() []byte {
|
||||||
|
buf := make([]byte, msg.Len())
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
native.PutUint16(buf[0:2], uint16(msg.Len()))
|
||||||
|
native.PutUint16(buf[2:4], msg.Type)
|
||||||
|
if msg.Value {
|
||||||
|
buf[4] = 1
|
||||||
|
} else {
|
||||||
|
buf[4] = 0
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Boolmsg) Len() int {
|
||||||
|
return syscall_NLA_HDRLEN + 1
|
||||||
|
}
|
259
vendor/github.com/opencontainers/runc/libcontainer/network_linux.go
generated
vendored
Normal file
259
vendor/github.com/opencontainers/runc/libcontainer/network_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
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{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// networkStrategy represents a specific network configuration for
|
||||||
|
// a container's networking stack
|
||||||
|
type networkStrategy interface {
|
||||||
|
create(*network, int) error
|
||||||
|
initialize(*network) error
|
||||||
|
detach(*configs.Network) error
|
||||||
|
attach(*configs.Network) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStrategy returns the specific network strategy for the
|
||||||
|
// provided type.
|
||||||
|
func getStrategy(tpe string) (networkStrategy, error) {
|
||||||
|
s, exists := strategies[tpe]
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("unknown strategy type %q", tpe)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
|
||||||
|
func getNetworkInterfaceStats(interfaceName string) (*NetworkInterface, error) {
|
||||||
|
out := &NetworkInterface{Name: interfaceName}
|
||||||
|
// This can happen if the network runtime information is missing - possible if the
|
||||||
|
// container was created by an old version of libcontainer.
|
||||||
|
if interfaceName == "" {
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
type netStatsPair struct {
|
||||||
|
// Where to write the output.
|
||||||
|
Out *uint64
|
||||||
|
// The network stats file to read.
|
||||||
|
File string
|
||||||
|
}
|
||||||
|
// Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
|
||||||
|
netStats := []netStatsPair{
|
||||||
|
{Out: &out.RxBytes, File: "tx_bytes"},
|
||||||
|
{Out: &out.RxPackets, File: "tx_packets"},
|
||||||
|
{Out: &out.RxErrors, File: "tx_errors"},
|
||||||
|
{Out: &out.RxDropped, File: "tx_dropped"},
|
||||||
|
|
||||||
|
{Out: &out.TxBytes, File: "rx_bytes"},
|
||||||
|
{Out: &out.TxPackets, File: "rx_packets"},
|
||||||
|
{Out: &out.TxErrors, File: "rx_errors"},
|
||||||
|
{Out: &out.TxDropped, File: "rx_dropped"},
|
||||||
|
}
|
||||||
|
for _, netStat := range netStats {
|
||||||
|
data, err := readSysfsNetworkStats(interfaceName, netStat.File)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
*(netStat.Out) = data
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics
|
||||||
|
func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loopback is a network strategy that provides a basic loopback device
|
||||||
|
type loopback struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loopback) create(n *network, nspid int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loopback) initialize(config *network) error {
|
||||||
|
return netlink.LinkSetUp(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: "lo"}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loopback) attach(n *configs.Network) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
89
vendor/github.com/opencontainers/runc/libcontainer/notify_linux.go
generated
vendored
Normal file
89
vendor/github.com/opencontainers/runc/libcontainer/notify_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const oomCgroupName = "memory"
|
||||||
|
|
||||||
|
type PressureLevel uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
LowPressure PressureLevel = iota
|
||||||
|
MediumPressure
|
||||||
|
CriticalPressure
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerMemoryEvent(cgDir string, evName string, arg string) (<-chan struct{}, error) {
|
||||||
|
evFile, err := os.Open(filepath.Join(cgDir, evName))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
|
||||||
|
if syserr != 0 {
|
||||||
|
evFile.Close()
|
||||||
|
return nil, syserr
|
||||||
|
}
|
||||||
|
|
||||||
|
eventfd := os.NewFile(fd, "eventfd")
|
||||||
|
|
||||||
|
eventControlPath := filepath.Join(cgDir, "cgroup.event_control")
|
||||||
|
data := fmt.Sprintf("%d %d %s", eventfd.Fd(), evFile.Fd(), arg)
|
||||||
|
if err := ioutil.WriteFile(eventControlPath, []byte(data), 0700); err != nil {
|
||||||
|
eventfd.Close()
|
||||||
|
evFile.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ch := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
close(ch)
|
||||||
|
eventfd.Close()
|
||||||
|
evFile.Close()
|
||||||
|
}()
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
for {
|
||||||
|
if _, err := eventfd.Read(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// When a cgroup is destroyed, an event is sent to eventfd.
|
||||||
|
// So if the control path is gone, return instead of notifying.
|
||||||
|
if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch <- struct{}{}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifyOnOOM returns channel on which you can expect event about OOM,
|
||||||
|
// if process died without OOM this channel will be closed.
|
||||||
|
func notifyOnOOM(paths map[string]string) (<-chan struct{}, error) {
|
||||||
|
dir := paths[oomCgroupName]
|
||||||
|
if dir == "" {
|
||||||
|
return nil, fmt.Errorf("path %q missing", oomCgroupName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return registerMemoryEvent(dir, "memory.oom_control", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyMemoryPressure(paths map[string]string, level PressureLevel) (<-chan struct{}, error) {
|
||||||
|
dir := paths[oomCgroupName]
|
||||||
|
if dir == "" {
|
||||||
|
return nil, fmt.Errorf("path %q missing", oomCgroupName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if level > CriticalPressure {
|
||||||
|
return nil, fmt.Errorf("invalid pressure level %d", level)
|
||||||
|
}
|
||||||
|
|
||||||
|
levelStr := []string{"low", "medium", "critical"}[level]
|
||||||
|
return registerMemoryEvent(dir, "memory.pressure_level", levelStr)
|
||||||
|
}
|
121
vendor/github.com/opencontainers/runc/libcontainer/process.go
generated
vendored
Normal file
121
vendor/github.com/opencontainers/runc/libcontainer/process.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type processOperations interface {
|
||||||
|
wait() (*os.ProcessState, error)
|
||||||
|
signal(sig os.Signal) error
|
||||||
|
pid() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process specifies the configuration and IO for a process inside
|
||||||
|
// a container.
|
||||||
|
type Process struct {
|
||||||
|
// The command to be run followed by any arguments.
|
||||||
|
Args []string
|
||||||
|
|
||||||
|
// Env specifies the environment variables for the process.
|
||||||
|
Env []string
|
||||||
|
|
||||||
|
// User will set the uid and gid of the executing process running inside the container
|
||||||
|
// local to the container's user and group configuration.
|
||||||
|
User string
|
||||||
|
|
||||||
|
// Cwd will change the processes current working directory inside the container's rootfs.
|
||||||
|
Cwd string
|
||||||
|
|
||||||
|
// Stdin is a pointer to a reader which provides the standard input stream.
|
||||||
|
Stdin io.Reader
|
||||||
|
|
||||||
|
// Stdout is a pointer to a writer which receives the standard output stream.
|
||||||
|
Stdout io.Writer
|
||||||
|
|
||||||
|
// Stderr is a pointer to a writer which receives the standard error stream.
|
||||||
|
Stderr io.Writer
|
||||||
|
|
||||||
|
// ExtraFiles specifies additional open files to be inherited by the container
|
||||||
|
ExtraFiles []*os.File
|
||||||
|
|
||||||
|
// consolePath is the path to the console allocated to the container.
|
||||||
|
consolePath string
|
||||||
|
|
||||||
|
// Capabilities specify the capabilities to keep when executing the process inside the container
|
||||||
|
// All capabilities not specified will be dropped from the processes capability mask
|
||||||
|
Capabilities []string
|
||||||
|
|
||||||
|
// AppArmorProfile specifies the profile to apply to the process and is
|
||||||
|
// changed at the time the process is execed
|
||||||
|
AppArmorProfile string
|
||||||
|
|
||||||
|
// Label specifies the label to apply to the process. It is commonly used by selinux
|
||||||
|
Label string
|
||||||
|
|
||||||
|
// NoNewPrivileges controls whether processes can gain additional privileges.
|
||||||
|
NoNewPrivileges *bool
|
||||||
|
|
||||||
|
// Rlimits specifies the resource limits, such as max open files, to set in the container
|
||||||
|
// If Rlimits are not set, the container will inherit rlimits from the parent process
|
||||||
|
Rlimits []configs.Rlimit
|
||||||
|
|
||||||
|
ops processOperations
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the process to exit.
|
||||||
|
// Wait releases any resources associated with the Process
|
||||||
|
func (p Process) Wait() (*os.ProcessState, error) {
|
||||||
|
if p.ops == nil {
|
||||||
|
return nil, newGenericError(fmt.Errorf("invalid process"), NoProcessOps)
|
||||||
|
}
|
||||||
|
return p.ops.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pid returns the process ID
|
||||||
|
func (p Process) Pid() (int, error) {
|
||||||
|
// math.MinInt32 is returned here, because it's invalid value
|
||||||
|
// for the kill() system call.
|
||||||
|
if p.ops == nil {
|
||||||
|
return math.MinInt32, newGenericError(fmt.Errorf("invalid process"), NoProcessOps)
|
||||||
|
}
|
||||||
|
return p.ops.pid(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal sends a signal to the Process.
|
||||||
|
func (p Process) Signal(sig os.Signal) error {
|
||||||
|
if p.ops == nil {
|
||||||
|
return newGenericError(fmt.Errorf("invalid process"), NoProcessOps)
|
||||||
|
}
|
||||||
|
return p.ops.signal(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IO holds the process's STDIO
|
||||||
|
type IO struct {
|
||||||
|
Stdin io.WriteCloser
|
||||||
|
Stdout io.ReadCloser
|
||||||
|
Stderr io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConsole creates new console for process and returns it
|
||||||
|
func (p *Process) NewConsole(rootuid int) (Console, error) {
|
||||||
|
console, err := NewConsole(rootuid, rootuid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.consolePath = console.Path()
|
||||||
|
return console, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsoleFromPath sets the process's console with the path provided
|
||||||
|
func (p *Process) ConsoleFromPath(path string) error {
|
||||||
|
if p.consolePath != "" {
|
||||||
|
return newGenericError(fmt.Errorf("console path already exists for process"), ConsoleExists)
|
||||||
|
}
|
||||||
|
p.consolePath = path
|
||||||
|
return nil
|
||||||
|
}
|
487
vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
generated
vendored
Normal file
487
vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,487 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type parentProcess interface {
|
||||||
|
// pid returns the pid for the running process.
|
||||||
|
pid() int
|
||||||
|
|
||||||
|
// start starts the process execution.
|
||||||
|
start() error
|
||||||
|
|
||||||
|
// send a SIGKILL to the process and wait for the exit.
|
||||||
|
terminate() error
|
||||||
|
|
||||||
|
// wait waits on the process returning the process state.
|
||||||
|
wait() (*os.ProcessState, error)
|
||||||
|
|
||||||
|
// startTime return's the process start time.
|
||||||
|
startTime() (string, error)
|
||||||
|
|
||||||
|
signal(os.Signal) error
|
||||||
|
|
||||||
|
externalDescriptors() []string
|
||||||
|
|
||||||
|
setExternalDescriptors(fds []string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type setnsProcess struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
parentPipe *os.File
|
||||||
|
childPipe *os.File
|
||||||
|
cgroupPaths map[string]string
|
||||||
|
config *initConfig
|
||||||
|
fds []string
|
||||||
|
process *Process
|
||||||
|
bootstrapData io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *setnsProcess) startTime() (string, error) {
|
||||||
|
return system.GetProcessStartTime(p.pid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *setnsProcess) signal(sig os.Signal) error {
|
||||||
|
s, ok := sig.(syscall.Signal)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("os: unsupported signal type")
|
||||||
|
}
|
||||||
|
return syscall.Kill(p.pid(), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *setnsProcess) start() (err error) {
|
||||||
|
defer p.parentPipe.Close()
|
||||||
|
err = p.cmd.Start()
|
||||||
|
p.childPipe.Close()
|
||||||
|
if err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if p.bootstrapData != nil {
|
||||||
|
if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = p.execSetns(); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if len(p.cgroupPaths) > 0 {
|
||||||
|
if err := cgroups.EnterPid(p.cgroupPaths, p.pid()); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set oom_score_adj
|
||||||
|
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// set rlimits, this has to be done here because we lose permissions
|
||||||
|
// to raise the limits once we enter a user-namespace
|
||||||
|
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if err := utils.WriteJSON(p.parentPipe, p.config); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// wait for the child process to fully complete and receive an error message
|
||||||
|
// if one was encoutered
|
||||||
|
var ierr *genericError
|
||||||
|
if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// Must be done after Shutdown so the child will exit and we can wait for it.
|
||||||
|
if ierr != nil {
|
||||||
|
p.wait()
|
||||||
|
return newSystemError(ierr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// execSetns runs the process that executes C code to perform the setns calls
|
||||||
|
// because setns support requires the C process to fork off a child and perform the setns
|
||||||
|
// before the go runtime boots, we wait on the process to die and receive the child's pid
|
||||||
|
// over the provided pipe.
|
||||||
|
func (p *setnsProcess) execSetns() error {
|
||||||
|
status, err := p.cmd.Process.Wait()
|
||||||
|
if err != nil {
|
||||||
|
p.cmd.Wait()
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if !status.Success() {
|
||||||
|
p.cmd.Wait()
|
||||||
|
return newSystemError(&exec.ExitError{ProcessState: status})
|
||||||
|
}
|
||||||
|
var pid *pid
|
||||||
|
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
|
||||||
|
p.cmd.Wait()
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
process, err := os.FindProcess(pid.Pid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.cmd.Process = process
|
||||||
|
p.process.ops = p
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminate sends a SIGKILL to the forked process for the setns routine then waits to
|
||||||
|
// avoid the process becomming a zombie.
|
||||||
|
func (p *setnsProcess) terminate() error {
|
||||||
|
if p.cmd.Process == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := p.cmd.Process.Kill()
|
||||||
|
if _, werr := p.wait(); err == nil {
|
||||||
|
err = werr
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *setnsProcess) wait() (*os.ProcessState, error) {
|
||||||
|
err := p.cmd.Wait()
|
||||||
|
|
||||||
|
// Return actual ProcessState even on Wait error
|
||||||
|
return p.cmd.ProcessState, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *setnsProcess) pid() int {
|
||||||
|
return p.cmd.Process.Pid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *setnsProcess) externalDescriptors() []string {
|
||||||
|
return p.fds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *setnsProcess) setExternalDescriptors(newFds []string) {
|
||||||
|
p.fds = newFds
|
||||||
|
}
|
||||||
|
|
||||||
|
type initProcess struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
parentPipe *os.File
|
||||||
|
childPipe *os.File
|
||||||
|
config *initConfig
|
||||||
|
manager cgroups.Manager
|
||||||
|
container *linuxContainer
|
||||||
|
fds []string
|
||||||
|
process *Process
|
||||||
|
bootstrapData io.Reader
|
||||||
|
sharePidns bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) pid() int {
|
||||||
|
return p.cmd.Process.Pid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) externalDescriptors() []string {
|
||||||
|
return p.fds
|
||||||
|
}
|
||||||
|
|
||||||
|
// execSetns runs the process that executes C code to perform the setns calls
|
||||||
|
// because setns support requires the C process to fork off a child and perform the setns
|
||||||
|
// before the go runtime boots, we wait on the process to die and receive the child's pid
|
||||||
|
// over the provided pipe.
|
||||||
|
// This is called by initProcess.start function
|
||||||
|
func (p *initProcess) execSetns() error {
|
||||||
|
status, err := p.cmd.Process.Wait()
|
||||||
|
if err != nil {
|
||||||
|
p.cmd.Wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !status.Success() {
|
||||||
|
p.cmd.Wait()
|
||||||
|
return &exec.ExitError{ProcessState: status}
|
||||||
|
}
|
||||||
|
var pid *pid
|
||||||
|
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
|
||||||
|
p.cmd.Wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
process, err := os.FindProcess(pid.Pid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.cmd.Process = process
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) start() error {
|
||||||
|
defer p.parentPipe.Close()
|
||||||
|
err := p.cmd.Start()
|
||||||
|
p.process.ops = p
|
||||||
|
p.childPipe.Close()
|
||||||
|
if err != nil {
|
||||||
|
p.process.ops = nil
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.execSetns(); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// Save the standard descriptor names before the container process
|
||||||
|
// can potentially move them (e.g., via dup2()). If we don't do this now,
|
||||||
|
// we won't know at checkpoint time which file descriptor to look up.
|
||||||
|
fds, err := getPipeFds(p.pid())
|
||||||
|
if err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
p.setExternalDescriptors(fds)
|
||||||
|
// Do this before syncing with child so that no children
|
||||||
|
// can escape the cgroup
|
||||||
|
if err := p.manager.Apply(p.pid()); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
// TODO: should not be the responsibility to call here
|
||||||
|
p.manager.Destroy()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := p.createNetworkInterfaces(); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if err := p.sendConfig(); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
procSync syncT
|
||||||
|
sentRun bool
|
||||||
|
sentResume bool
|
||||||
|
ierr *genericError
|
||||||
|
)
|
||||||
|
|
||||||
|
dec := json.NewDecoder(p.parentPipe)
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
if err := dec.Decode(&procSync); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
switch procSync.Type {
|
||||||
|
case procReady:
|
||||||
|
if err := p.manager.Set(p.config.Config); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// set oom_score_adj
|
||||||
|
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// set rlimits, this has to be done here because we lose permissions
|
||||||
|
// to raise the limits once we enter a user-namespace
|
||||||
|
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// call prestart hooks
|
||||||
|
if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
|
||||||
|
if p.config.Config.Hooks != nil {
|
||||||
|
s := configs.HookState{
|
||||||
|
Version: p.container.config.Version,
|
||||||
|
ID: p.container.id,
|
||||||
|
Pid: p.pid(),
|
||||||
|
Root: p.config.Config.Rootfs,
|
||||||
|
}
|
||||||
|
for _, hook := range p.config.Config.Hooks.Prestart {
|
||||||
|
if err := hook.Run(s); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sync with child.
|
||||||
|
if err := utils.WriteJSON(p.parentPipe, syncT{procRun}); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
sentRun = true
|
||||||
|
case procHooks:
|
||||||
|
if p.config.Config.Hooks != nil {
|
||||||
|
s := configs.HookState{
|
||||||
|
Version: p.container.config.Version,
|
||||||
|
ID: p.container.id,
|
||||||
|
Pid: p.pid(),
|
||||||
|
Root: p.config.Config.Rootfs,
|
||||||
|
BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"),
|
||||||
|
}
|
||||||
|
for _, hook := range p.config.Config.Hooks.Prestart {
|
||||||
|
if err := hook.Run(s); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sync with child.
|
||||||
|
if err := utils.WriteJSON(p.parentPipe, syncT{procResume}); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
sentResume = true
|
||||||
|
case procError:
|
||||||
|
// wait for the child process to fully complete and receive an error message
|
||||||
|
// if one was encoutered
|
||||||
|
if err := dec.Decode(&ierr); err != nil && err != io.EOF {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if ierr != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
// Programmer error.
|
||||||
|
panic("No error following JSON procError payload.")
|
||||||
|
default:
|
||||||
|
return newSystemError(fmt.Errorf("invalid JSON synchronisation payload from child"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !sentRun {
|
||||||
|
return newSystemError(fmt.Errorf("could not synchronise with container process: %v", ierr))
|
||||||
|
}
|
||||||
|
if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume {
|
||||||
|
return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process"))
|
||||||
|
}
|
||||||
|
if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
// Must be done after Shutdown so the child will exit and we can wait for it.
|
||||||
|
if ierr != nil {
|
||||||
|
p.wait()
|
||||||
|
return newSystemError(ierr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) wait() (*os.ProcessState, error) {
|
||||||
|
err := p.cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return p.cmd.ProcessState, err
|
||||||
|
}
|
||||||
|
// we should kill all processes in cgroup when init is died if we use host PID namespace
|
||||||
|
if p.sharePidns {
|
||||||
|
killCgroupProcesses(p.manager)
|
||||||
|
}
|
||||||
|
return p.cmd.ProcessState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) terminate() error {
|
||||||
|
if p.cmd.Process == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := p.cmd.Process.Kill()
|
||||||
|
if _, werr := p.wait(); err == nil {
|
||||||
|
err = werr
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) startTime() (string, error) {
|
||||||
|
return system.GetProcessStartTime(p.pid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) sendConfig() error {
|
||||||
|
// send the config to the container's init process, we don't use JSON Encode
|
||||||
|
// here because there might be a problem in JSON decoder in some cases, see:
|
||||||
|
// https://github.com/docker/docker/issues/14203#issuecomment-174177790
|
||||||
|
return utils.WriteJSON(p.parentPipe, p.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) createNetworkInterfaces() error {
|
||||||
|
for _, config := range p.config.Config.Networks {
|
||||||
|
strategy, err := getStrategy(config.Type)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n := &network{
|
||||||
|
Network: *config,
|
||||||
|
}
|
||||||
|
if err := strategy.create(n, p.pid()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.config.Networks = append(p.config.Networks, n)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) signal(sig os.Signal) error {
|
||||||
|
s, ok := sig.(syscall.Signal)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("os: unsupported signal type")
|
||||||
|
}
|
||||||
|
return syscall.Kill(p.pid(), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *initProcess) setExternalDescriptors(newFds []string) {
|
||||||
|
p.fds = newFds
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPipeFds(pid int) ([]string, error) {
|
||||||
|
fds := make([]string, 3)
|
||||||
|
|
||||||
|
dirPath := filepath.Join("/proc", strconv.Itoa(pid), "/fd")
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
f := filepath.Join(dirPath, strconv.Itoa(i))
|
||||||
|
target, err := os.Readlink(f)
|
||||||
|
if err != nil {
|
||||||
|
return fds, err
|
||||||
|
}
|
||||||
|
fds[i] = target
|
||||||
|
}
|
||||||
|
return fds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeIO creates pipes for use with the process's STDIO
|
||||||
|
// and returns the opposite side for each
|
||||||
|
func (p *Process) InitializeIO(rootuid int) (i *IO, err error) {
|
||||||
|
var fds []uintptr
|
||||||
|
i = &IO{}
|
||||||
|
// cleanup in case of an error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
for _, fd := range fds {
|
||||||
|
syscall.Close(int(fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// STDIN
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fds = append(fds, r.Fd(), w.Fd())
|
||||||
|
p.Stdin, i.Stdin = r, w
|
||||||
|
// STDOUT
|
||||||
|
if r, w, err = os.Pipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fds = append(fds, r.Fd(), w.Fd())
|
||||||
|
p.Stdout, i.Stdout = w, r
|
||||||
|
// STDERR
|
||||||
|
if r, w, err = os.Pipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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
|
||||||
|
for _, fd := range fds {
|
||||||
|
if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
122
vendor/github.com/opencontainers/runc/libcontainer/restored_process.go
generated
vendored
Normal file
122
vendor/github.com/opencontainers/runc/libcontainer/restored_process.go
generated
vendored
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRestoredProcess(pid int, fds []string) (*restoredProcess, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
proc, err := os.FindProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
started, err := system.GetProcessStartTime(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &restoredProcess{
|
||||||
|
proc: proc,
|
||||||
|
processStartTime: started,
|
||||||
|
fds: fds,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type restoredProcess struct {
|
||||||
|
proc *os.Process
|
||||||
|
processStartTime string
|
||||||
|
fds []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) start() error {
|
||||||
|
return newGenericError(fmt.Errorf("restored process cannot be started"), SystemError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) pid() int {
|
||||||
|
return p.proc.Pid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) terminate() error {
|
||||||
|
err := p.proc.Kill()
|
||||||
|
if _, werr := p.wait(); err == nil {
|
||||||
|
err = werr
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) wait() (*os.ProcessState, error) {
|
||||||
|
// TODO: how do we wait on the actual process?
|
||||||
|
// maybe use --exec-cmd in criu
|
||||||
|
st, err := p.proc.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) startTime() (string, error) {
|
||||||
|
return p.processStartTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) signal(s os.Signal) error {
|
||||||
|
return p.proc.Signal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) externalDescriptors() []string {
|
||||||
|
return p.fds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *restoredProcess) setExternalDescriptors(newFds []string) {
|
||||||
|
p.fds = newFds
|
||||||
|
}
|
||||||
|
|
||||||
|
// nonChildProcess represents a process where the calling process is not
|
||||||
|
// the parent process. This process is created when a factory loads a container from
|
||||||
|
// a persisted state.
|
||||||
|
type nonChildProcess struct {
|
||||||
|
processPid int
|
||||||
|
processStartTime string
|
||||||
|
fds []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) start() error {
|
||||||
|
return newGenericError(fmt.Errorf("restored process cannot be started"), SystemError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) pid() int {
|
||||||
|
return p.processPid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) terminate() error {
|
||||||
|
return newGenericError(fmt.Errorf("restored process cannot be terminated"), SystemError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) wait() (*os.ProcessState, error) {
|
||||||
|
return nil, newGenericError(fmt.Errorf("restored process cannot be waited on"), SystemError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) startTime() (string, error) {
|
||||||
|
return p.processStartTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) signal(s os.Signal) error {
|
||||||
|
proc, err := os.FindProcess(p.processPid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return proc.Signal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) externalDescriptors() []string {
|
||||||
|
return p.fds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *nonChildProcess) setExternalDescriptors(newFds []string) {
|
||||||
|
p.fds = newFds
|
||||||
|
}
|
725
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
generated
vendored
Normal file
725
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,725 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
"github.com/docker/docker/pkg/symlink"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
|
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
||||||
|
|
||||||
|
// setupDev returns true if /dev needs to be set up.
|
||||||
|
func needsSetupDev(config *configs.Config) bool {
|
||||||
|
for _, m := range config.Mounts {
|
||||||
|
if m.Device == "bind" && (m.Destination == "/dev" || m.Destination == "/dev/") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupRootfs sets up the devices, mount points, and filesystems for use inside a
|
||||||
|
// new mount namespace.
|
||||||
|
func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWriter) (err error) {
|
||||||
|
if err := prepareRoot(config); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
setupDev := needsSetupDev(config)
|
||||||
|
for _, m := range config.Mounts {
|
||||||
|
for _, precmd := range m.PremountCmds {
|
||||||
|
if err := mountCmd(precmd); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, postcmd := range m.PostmountCmds {
|
||||||
|
if err := mountCmd(postcmd); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if setupDev {
|
||||||
|
if err := createDevices(config); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if err := setupPtmx(config, console); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if err := setupDevSymlinks(config.Rootfs); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Signal the parent to run the pre-start hooks.
|
||||||
|
// The hooks are run after the mounts are setup, but before we switch to the new
|
||||||
|
// root, so that the old root is still available in the hooks for any mount
|
||||||
|
// manipulations.
|
||||||
|
if err := syncParentHooks(pipe); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.Chdir(config.Rootfs); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if config.NoPivotRoot {
|
||||||
|
err = msMoveRoot(config.Rootfs)
|
||||||
|
} else {
|
||||||
|
err = pivotRoot(config.Rootfs, config.PivotDir)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
if setupDev {
|
||||||
|
if err := reOpenDevNull(); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remount dev as ro if specifed
|
||||||
|
for _, m := range config.Mounts {
|
||||||
|
if m.Destination == "/dev" {
|
||||||
|
if m.Flags&syscall.MS_RDONLY != 0 {
|
||||||
|
if err := remountReadonly(m.Destination); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set rootfs ( / ) as readonly
|
||||||
|
if config.Readonlyfs {
|
||||||
|
if err := setReadonly(); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syscall.Umask(0022)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mountCmd(cmd configs.Command) error {
|
||||||
|
|
||||||
|
command := exec.Command(cmd.Path, cmd.Args[:]...)
|
||||||
|
command.Env = cmd.Env
|
||||||
|
command.Dir = cmd.Dir
|
||||||
|
if out, err := command.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
|
||||||
|
var (
|
||||||
|
dest = m.Destination
|
||||||
|
)
|
||||||
|
if !strings.HasPrefix(dest, rootfs) {
|
||||||
|
dest = filepath.Join(rootfs, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m.Device {
|
||||||
|
case "proc", "sysfs":
|
||||||
|
if err := os.MkdirAll(dest, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Selinux kernels do not support labeling of /proc or /sys
|
||||||
|
return mountPropagate(m, rootfs, "")
|
||||||
|
case "mqueue":
|
||||||
|
if err := os.MkdirAll(dest, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mountPropagate(m, rootfs, mountLabel); err != nil {
|
||||||
|
// older kernels do not support labeling of /dev/mqueue
|
||||||
|
if err := mountPropagate(m, rootfs, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return label.SetFileLabel(dest, mountLabel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case "tmpfs":
|
||||||
|
stat, err := os.Stat(dest)
|
||||||
|
if err != nil {
|
||||||
|
if err := os.MkdirAll(dest, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := mountPropagate(m, rootfs, mountLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if stat != nil {
|
||||||
|
if err = os.Chmod(dest, stat.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case "bind":
|
||||||
|
stat, err := os.Stat(m.Source)
|
||||||
|
if err != nil {
|
||||||
|
// error out if the source of a bind mount does not exist as we will be
|
||||||
|
// unable to bind anything to it.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// ensure that the destination of the bind mount is resolved of symlinks at mount time because
|
||||||
|
// any previous mounts can invalidate the next mount's destination.
|
||||||
|
// this can happen when a user specifies mounts within other mounts to cause breakouts or other
|
||||||
|
// evil stuff to try to escape the container's rootfs.
|
||||||
|
if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := checkMountDestination(rootfs, dest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// update the mount with the correct dest after symlinks are resolved.
|
||||||
|
m.Destination = dest
|
||||||
|
if err := createIfNotExists(dest, stat.IsDir()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mountPropagate(m, rootfs, mountLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// bind mount won't change mount options, we need remount to make mount options effective.
|
||||||
|
// first check that we have non-default options required before attempting a remount
|
||||||
|
if m.Flags&^(syscall.MS_REC|syscall.MS_REMOUNT|syscall.MS_BIND) != 0 {
|
||||||
|
// only remount if unique mount options are set
|
||||||
|
if err := remount(m, rootfs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Relabel != "" {
|
||||||
|
if err := label.Validate(m.Relabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
shared := label.IsShared(m.Relabel)
|
||||||
|
if err := label.Relabel(m.Source, mountLabel, shared); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "cgroup":
|
||||||
|
binds, err := getCgroupMounts(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var merged []string
|
||||||
|
for _, b := range binds {
|
||||||
|
ss := filepath.Base(b.Destination)
|
||||||
|
if strings.Contains(ss, ",") {
|
||||||
|
merged = append(merged, ss)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpfs := &configs.Mount{
|
||||||
|
Source: "tmpfs",
|
||||||
|
Device: "tmpfs",
|
||||||
|
Destination: m.Destination,
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
Data: "mode=755",
|
||||||
|
PropagationFlags: m.PropagationFlags,
|
||||||
|
}
|
||||||
|
if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, b := range binds {
|
||||||
|
if err := mountToRootfs(b, rootfs, mountLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create symlinks for merged cgroups
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Chdir(filepath.Join(rootfs, m.Destination)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, mc := range merged {
|
||||||
|
for _, ss := range strings.Split(mc, ",") {
|
||||||
|
if err := os.Symlink(mc, ss); err != nil {
|
||||||
|
// if cgroup already exists, then okay(it could have been created before)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
os.Chdir(cwd)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := os.Chdir(cwd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if m.Flags&syscall.MS_RDONLY != 0 {
|
||||||
|
// remount cgroup root as readonly
|
||||||
|
mcgrouproot := &configs.Mount{
|
||||||
|
Destination: m.Destination,
|
||||||
|
Flags: defaultMountFlags | syscall.MS_RDONLY,
|
||||||
|
}
|
||||||
|
if err := remount(mcgrouproot, rootfs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if err := os.MkdirAll(dest, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return mountPropagate(m, rootfs, mountLabel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) {
|
||||||
|
mounts, err := cgroups.GetCgroupMounts()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cgroupPaths, err := cgroups.ParseCgroupFile("/proc/self/cgroup")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var binds []*configs.Mount
|
||||||
|
|
||||||
|
for _, mm := range mounts {
|
||||||
|
dir, err := mm.GetThisCgroupDir(cgroupPaths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
relDir, err := filepath.Rel(mm.Root, dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
binds = append(binds, &configs.Mount{
|
||||||
|
Device: "bind",
|
||||||
|
Source: filepath.Join(mm.Mountpoint, relDir),
|
||||||
|
Destination: filepath.Join(m.Destination, strings.Join(mm.Subsystems, ",")),
|
||||||
|
Flags: syscall.MS_BIND | syscall.MS_REC | m.Flags,
|
||||||
|
PropagationFlags: m.PropagationFlags,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return binds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkMountDestination checks to ensure that the mount destination is not over the top of /proc.
|
||||||
|
// dest is required to be an abs path and have any symlinks resolved before calling this function.
|
||||||
|
func checkMountDestination(rootfs, dest string) error {
|
||||||
|
if libcontainerUtils.CleanPath(rootfs) == libcontainerUtils.CleanPath(dest) {
|
||||||
|
return fmt.Errorf("mounting into / is prohibited")
|
||||||
|
}
|
||||||
|
invalidDestinations := []string{
|
||||||
|
"/proc",
|
||||||
|
}
|
||||||
|
// White list, it should be sub directories of invalid destinations
|
||||||
|
validDestinations := []string{
|
||||||
|
// These entries can be bind mounted by files emulated by fuse,
|
||||||
|
// so commands like top, free displays stats in container.
|
||||||
|
"/proc/cpuinfo",
|
||||||
|
"/proc/diskstats",
|
||||||
|
"/proc/meminfo",
|
||||||
|
"/proc/stat",
|
||||||
|
"/proc/net/dev",
|
||||||
|
}
|
||||||
|
for _, valid := range validDestinations {
|
||||||
|
path, err := filepath.Rel(filepath.Join(rootfs, valid), dest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if path == "." {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, invalid := range invalidDestinations {
|
||||||
|
path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if path == "." || !strings.HasPrefix(path, "..") {
|
||||||
|
return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupDevSymlinks(rootfs string) error {
|
||||||
|
var links = [][2]string{
|
||||||
|
{"/proc/self/fd", "/dev/fd"},
|
||||||
|
{"/proc/self/fd/0", "/dev/stdin"},
|
||||||
|
{"/proc/self/fd/1", "/dev/stdout"},
|
||||||
|
{"/proc/self/fd/2", "/dev/stderr"},
|
||||||
|
}
|
||||||
|
// kcore support can be toggled with CONFIG_PROC_KCORE; only create a symlink
|
||||||
|
// in /dev if it exists in /proc.
|
||||||
|
if _, err := os.Stat("/proc/kcore"); err == nil {
|
||||||
|
links = append(links, [2]string{"/proc/kcore", "/dev/core"})
|
||||||
|
}
|
||||||
|
for _, link := range links {
|
||||||
|
var (
|
||||||
|
src = link[0]
|
||||||
|
dst = filepath.Join(rootfs, link[1])
|
||||||
|
)
|
||||||
|
if err := os.Symlink(src, dst); err != nil && !os.IsExist(err) {
|
||||||
|
return fmt.Errorf("symlink %s %s %s", src, dst, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If stdin, stdout, and/or stderr are pointing to `/dev/null` in the parent's rootfs
|
||||||
|
// this method will make them point to `/dev/null` in this container's rootfs. This
|
||||||
|
// needs to be called after we chroot/pivot into the container's rootfs so that any
|
||||||
|
// symlinks are resolved locally.
|
||||||
|
func reOpenDevNull() error {
|
||||||
|
var stat, devNullStat syscall.Stat_t
|
||||||
|
file, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to open /dev/null - %s", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
if err := syscall.Fstat(int(file.Fd()), &devNullStat); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for fd := 0; fd < 3; fd++ {
|
||||||
|
if err := syscall.Fstat(fd, &stat); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if stat.Rdev == devNullStat.Rdev {
|
||||||
|
// Close and re-open the fd.
|
||||||
|
if err := syscall.Dup3(int(file.Fd()), fd, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the device nodes in the container.
|
||||||
|
func createDevices(config *configs.Config) error {
|
||||||
|
useBindMount := system.RunningInUserNS() || config.Namespaces.Contains(configs.NEWUSER)
|
||||||
|
oldMask := syscall.Umask(0000)
|
||||||
|
for _, node := range config.Devices {
|
||||||
|
// containers running in a user namespace are not allowed to mknod
|
||||||
|
// devices so we can just bind mount it from the host.
|
||||||
|
if err := createDeviceNode(config.Rootfs, node, useBindMount); err != nil {
|
||||||
|
syscall.Umask(oldMask)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syscall.Umask(oldMask)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bindMountDeviceNode(dest string, node *configs.Device) error {
|
||||||
|
f, err := os.Create(dest)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the device node in the rootfs of the container.
|
||||||
|
func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
|
||||||
|
dest := filepath.Join(rootfs, node.Path)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bind {
|
||||||
|
return bindMountDeviceNode(dest, node)
|
||||||
|
}
|
||||||
|
if err := mknodDevice(dest, node); err != nil {
|
||||||
|
if os.IsExist(err) {
|
||||||
|
return nil
|
||||||
|
} else if os.IsPermission(err) {
|
||||||
|
return bindMountDeviceNode(dest, node)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mknodDevice(dest string, node *configs.Device) error {
|
||||||
|
fileMode := node.FileMode
|
||||||
|
switch node.Type {
|
||||||
|
case 'c':
|
||||||
|
fileMode |= syscall.S_IFCHR
|
||||||
|
case 'b':
|
||||||
|
fileMode |= syscall.S_IFBLK
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
|
||||||
|
}
|
||||||
|
if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return syscall.Chown(dest, int(node.Uid), int(node.Gid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info {
|
||||||
|
for _, m := range mountinfo {
|
||||||
|
if m.Mountpoint == dir {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the parent mount point of directory passed in as argument. Also return
|
||||||
|
// optional fields.
|
||||||
|
func getParentMount(rootfs string) (string, string, error) {
|
||||||
|
var path string
|
||||||
|
|
||||||
|
mountinfos, err := mount.GetMounts()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
mountinfo := getMountInfo(mountinfos, rootfs)
|
||||||
|
if mountinfo != nil {
|
||||||
|
return rootfs, mountinfo.Optional, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
path = rootfs
|
||||||
|
for {
|
||||||
|
path = filepath.Dir(path)
|
||||||
|
|
||||||
|
mountinfo = getMountInfo(mountinfos, path)
|
||||||
|
if mountinfo != nil {
|
||||||
|
return path, mountinfo.Optional, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == "/" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are here, we did not find parent mount. Something is wrong.
|
||||||
|
return "", "", fmt.Errorf("Could not find parent mount of %s", rootfs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make parent mount private if it was shared
|
||||||
|
func rootfsParentMountPrivate(config *configs.Config) error {
|
||||||
|
sharedMount := false
|
||||||
|
|
||||||
|
parentMount, optionalOpts, err := getParentMount(config.Rootfs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
optsSplit := strings.Split(optionalOpts, " ")
|
||||||
|
for _, opt := range optsSplit {
|
||||||
|
if strings.HasPrefix(opt, "shared:") {
|
||||||
|
sharedMount = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make parent mount PRIVATE if it was shared. It is needed for two
|
||||||
|
// reasons. First of all pivot_root() will fail if parent mount is
|
||||||
|
// shared. Secondly when we bind mount rootfs it will propagate to
|
||||||
|
// parent namespace and we don't want that to happen.
|
||||||
|
if sharedMount {
|
||||||
|
return syscall.Mount("", parentMount, "", syscall.MS_PRIVATE, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareRoot(config *configs.Config) error {
|
||||||
|
flag := syscall.MS_SLAVE | syscall.MS_REC
|
||||||
|
if config.RootPropagation != 0 {
|
||||||
|
flag = config.RootPropagation
|
||||||
|
}
|
||||||
|
if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rootfsParentMountPrivate(config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setReadonly() error {
|
||||||
|
return syscall.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupPtmx(config *configs.Config, console *linuxConsole) error {
|
||||||
|
ptmx := filepath.Join(config.Rootfs, "dev/ptmx")
|
||||||
|
if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Symlink("pts/ptmx", ptmx); err != nil {
|
||||||
|
return fmt.Errorf("symlink dev ptmx %s", err)
|
||||||
|
}
|
||||||
|
if console != nil {
|
||||||
|
return console.mount(config.Rootfs, config.MountLabel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pivotRoot(rootfs, pivotBaseDir string) (err error) {
|
||||||
|
if pivotBaseDir == "" {
|
||||||
|
pivotBaseDir = "/"
|
||||||
|
}
|
||||||
|
tmpDir := filepath.Join(rootfs, pivotBaseDir)
|
||||||
|
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("can't create tmp dir %s, error %v", tmpDir, err)
|
||||||
|
}
|
||||||
|
pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
errVal := os.Remove(pivotDir)
|
||||||
|
if err == nil {
|
||||||
|
err = errVal
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := syscall.PivotRoot(rootfs, pivotDir); err != nil {
|
||||||
|
return fmt.Errorf("pivot_root %s", err)
|
||||||
|
}
|
||||||
|
if err := syscall.Chdir("/"); err != nil {
|
||||||
|
return fmt.Errorf("chdir / %s", err)
|
||||||
|
}
|
||||||
|
// path to pivot dir now changed, update
|
||||||
|
pivotDir = filepath.Join(pivotBaseDir, filepath.Base(pivotDir))
|
||||||
|
|
||||||
|
// Make pivotDir rprivate to make sure any of the unmounts don't
|
||||||
|
// propagate to parent.
|
||||||
|
if err := syscall.Mount("", pivotDir, "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
|
||||||
|
return fmt.Errorf("unmount pivot_root dir %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func msMoveRoot(rootfs string) error {
|
||||||
|
if err := syscall.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.Chroot("."); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return syscall.Chdir("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// createIfNotExists creates a file or a directory only if it does not already exist.
|
||||||
|
func createIfNotExists(path string, isDir bool) error {
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if isDir {
|
||||||
|
return os.MkdirAll(path, 0755)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(path, os.O_CREATE, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// remountReadonly will bind over the top of an existing path and ensure that it is read-only.
|
||||||
|
func remountReadonly(path string) error {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if err := syscall.Mount("", path, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil && !os.IsNotExist(err) {
|
||||||
|
switch err {
|
||||||
|
case syscall.EINVAL:
|
||||||
|
// Probably not a mountpoint, use bind-mount
|
||||||
|
if err := syscall.Mount(path, path, "", syscall.MS_BIND, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC|defaultMountFlags, "")
|
||||||
|
case syscall.EBUSY:
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unable to mount %s as readonly max retries reached", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskFile bind mounts /dev/null over the top of the specified path inside a container
|
||||||
|
// to avoid security issues from processes reading information from non-namespace aware mounts ( proc/kcore ).
|
||||||
|
func maskFile(path string) error {
|
||||||
|
if err := syscall.Mount("/dev/null", path, "", syscall.MS_BIND, ""); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSystemProperty writes the value to a path under /proc/sys as determined from the key.
|
||||||
|
// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward.
|
||||||
|
func writeSystemProperty(key, value string) error {
|
||||||
|
keyPath := strings.Replace(key, ".", "/", -1)
|
||||||
|
return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func remount(m *configs.Mount, rootfs string) error {
|
||||||
|
var (
|
||||||
|
dest = m.Destination
|
||||||
|
)
|
||||||
|
if !strings.HasPrefix(dest, rootfs) {
|
||||||
|
dest = filepath.Join(rootfs, dest)
|
||||||
|
}
|
||||||
|
if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the mount operation followed by additional mounts required to take care
|
||||||
|
// of propagation flags.
|
||||||
|
func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error {
|
||||||
|
var (
|
||||||
|
dest = m.Destination
|
||||||
|
data = label.FormatMountLabel(m.Data, mountLabel)
|
||||||
|
flags = m.Flags
|
||||||
|
)
|
||||||
|
if dest == "/dev" {
|
||||||
|
flags &= ^syscall.MS_RDONLY
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(dest, rootfs) {
|
||||||
|
dest = filepath.Join(rootfs, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pflag := range m.PropagationFlags {
|
||||||
|
if err := syscall.Mount("", dest, "", uintptr(pflag), ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
11
vendor/github.com/opencontainers/runc/libcontainer/setgroups_linux.go
generated
vendored
Normal file
11
vendor/github.com/opencontainers/runc/libcontainer/setgroups_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// +build linux,go1.5
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// Set the GidMappingsEnableSetgroups member to true, so the process's
|
||||||
|
// setgroups proc entry wont be set to 'deny' if GidMappings are set
|
||||||
|
func enableSetgroups(sys *syscall.SysProcAttr) {
|
||||||
|
sys.GidMappingsEnableSetgroups = true
|
||||||
|
}
|
53
vendor/github.com/opencontainers/runc/libcontainer/setns_init_linux.go
generated
vendored
Normal file
53
vendor/github.com/opencontainers/runc/libcontainer/setns_init_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/keys"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/seccomp"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
// linuxSetnsInit performs the container's initialization for running a new process
|
||||||
|
// inside an existing container.
|
||||||
|
type linuxSetnsInit struct {
|
||||||
|
config *initConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linuxSetnsInit) getSessionRingName() string {
|
||||||
|
return fmt.Sprintf("_ses.%s", l.config.ContainerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linuxSetnsInit) Init() error {
|
||||||
|
// do not inherit the parent's session keyring
|
||||||
|
if _, err := keyctl.JoinSessionKeyring(l.getSessionRingName()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if l.config.NoNewPrivileges {
|
||||||
|
if err := system.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.config.Config.Seccomp != nil {
|
||||||
|
if err := seccomp.InitSeccomp(l.config.Config.Seccomp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := finalizeNamespace(l.config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if l.config.ProcessLabel != "" {
|
||||||
|
if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
|
||||||
|
}
|
147
vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
generated
vendored
Normal file
147
vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/keys"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/seccomp"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
type linuxStandardInit struct {
|
||||||
|
pipe io.ReadWriter
|
||||||
|
parentPid int
|
||||||
|
config *initConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linuxStandardInit) getSessionRingParams() (string, uint32, uint32) {
|
||||||
|
var newperms uint32
|
||||||
|
|
||||||
|
if l.config.Config.Namespaces.Contains(configs.NEWUSER) {
|
||||||
|
// with user ns we need 'other' search permissions
|
||||||
|
newperms = 0x8
|
||||||
|
} else {
|
||||||
|
// without user ns we need 'UID' search permissions
|
||||||
|
newperms = 0x80000
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a unique per session container name that we can
|
||||||
|
// join in setns; however, other containers can also join it
|
||||||
|
return fmt.Sprintf("_ses.%s", l.config.ContainerId), 0xffffffff, newperms
|
||||||
|
}
|
||||||
|
|
||||||
|
// PR_SET_NO_NEW_PRIVS isn't exposed in Golang so we define it ourselves copying the value
|
||||||
|
// the kernel
|
||||||
|
const PR_SET_NO_NEW_PRIVS = 0x26
|
||||||
|
|
||||||
|
func (l *linuxStandardInit) Init() error {
|
||||||
|
ringname, keepperms, newperms := l.getSessionRingParams()
|
||||||
|
|
||||||
|
// do not inherit the parent's session keyring
|
||||||
|
sessKeyId, err := keyctl.JoinSessionKeyring(ringname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// make session keyring searcheable
|
||||||
|
if err := keyctl.ModKeyringPerm(sessKeyId, keepperms, newperms); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var console *linuxConsole
|
||||||
|
if l.config.Console != "" {
|
||||||
|
console = newConsoleFromPath(l.config.Console)
|
||||||
|
if err := console.dupStdio(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if console != nil {
|
||||||
|
if err := system.Setctty(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := setupNetwork(l.config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := setupRoute(l.config.Config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
label.Init()
|
||||||
|
// InitializeMountNamespace() can be executed only for a new mount namespace
|
||||||
|
if l.config.Config.Namespaces.Contains(configs.NEWNS) {
|
||||||
|
if err := setupRootfs(l.config.Config, console, l.pipe); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hostname := l.config.Config.Hostname; hostname != "" {
|
||||||
|
if err := syscall.Sethostname([]byte(hostname)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range l.config.Config.Sysctl {
|
||||||
|
if err := writeSystemProperty(key, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, path := range l.config.Config.ReadonlyPaths {
|
||||||
|
if err := remountReadonly(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, path := range l.config.Config.MaskPaths {
|
||||||
|
if err := maskFile(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pdeath, err := system.GetParentDeathSignal()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if l.config.NoNewPrivileges {
|
||||||
|
if err := system.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
if l.config.Config.Seccomp != nil {
|
||||||
|
if err := seccomp.InitSeccomp(l.config.Config.Seccomp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := finalizeNamespace(l.config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// finalizeNamespace can change user/group which clears the parent death
|
||||||
|
// signal, so we restore it here.
|
||||||
|
if err := pdeath.Restore(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// compare the parent from the inital start of the init process and make sure that it did not change.
|
||||||
|
// if the parent changes that means it died and we were reparened to something else so we should
|
||||||
|
// just kill ourself and not cause problems for someone else.
|
||||||
|
if syscall.Getppid() != l.parentPid {
|
||||||
|
return syscall.Kill(syscall.Getpid(), syscall.SIGKILL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
|
||||||
|
}
|
228
vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
generated
vendored
Normal file
228
vendor/github.com/opencontainers/runc/libcontainer/state_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newStateTransitionError(from, to containerState) error {
|
||||||
|
return &stateTransitionError{
|
||||||
|
From: from.status().String(),
|
||||||
|
To: to.status().String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateTransitionError is returned when an invalid state transition happens from one
|
||||||
|
// state to another.
|
||||||
|
type stateTransitionError struct {
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateTransitionError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid state transition from %s to %s", s.From, s.To)
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerState interface {
|
||||||
|
transition(containerState) error
|
||||||
|
destroy() error
|
||||||
|
status() Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroy(c *linuxContainer) error {
|
||||||
|
if !c.config.Namespaces.Contains(configs.NEWPID) {
|
||||||
|
if err := killCgroupProcesses(c.cgroupManager); err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := c.cgroupManager.Destroy()
|
||||||
|
if rerr := os.RemoveAll(c.root); err == nil {
|
||||||
|
err = rerr
|
||||||
|
}
|
||||||
|
c.initProcess = nil
|
||||||
|
if herr := runPoststopHooks(c); err == nil {
|
||||||
|
err = herr
|
||||||
|
}
|
||||||
|
c.state = &stoppedState{c: c}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPoststopHooks(c *linuxContainer) error {
|
||||||
|
if c.config.Hooks != nil {
|
||||||
|
s := configs.HookState{
|
||||||
|
Version: c.config.Version,
|
||||||
|
ID: c.id,
|
||||||
|
Root: c.config.Rootfs,
|
||||||
|
BundlePath: utils.SearchLabels(c.config.Labels, "bundle"),
|
||||||
|
}
|
||||||
|
for _, hook := range c.config.Hooks.Poststop {
|
||||||
|
if err := hook.Run(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stoppedState represents a container is a stopped/destroyed state.
|
||||||
|
type stoppedState struct {
|
||||||
|
c *linuxContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *stoppedState) status() Status {
|
||||||
|
return Destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *stoppedState) transition(s containerState) error {
|
||||||
|
switch s.(type) {
|
||||||
|
case *runningState:
|
||||||
|
b.c.state = s
|
||||||
|
return nil
|
||||||
|
case *restoredState:
|
||||||
|
b.c.state = s
|
||||||
|
return nil
|
||||||
|
case *stoppedState:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newStateTransitionError(b, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *stoppedState) destroy() error {
|
||||||
|
return destroy(b.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runningState represents a container that is currently running.
|
||||||
|
type runningState struct {
|
||||||
|
c *linuxContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runningState) status() Status {
|
||||||
|
return Running
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runningState) transition(s containerState) error {
|
||||||
|
switch s.(type) {
|
||||||
|
case *stoppedState:
|
||||||
|
running, err := r.c.isRunning()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if running {
|
||||||
|
return newGenericError(fmt.Errorf("container still running"), ContainerNotStopped)
|
||||||
|
}
|
||||||
|
r.c.state = s
|
||||||
|
return nil
|
||||||
|
case *pausedState:
|
||||||
|
r.c.state = s
|
||||||
|
return nil
|
||||||
|
case *runningState:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newStateTransitionError(r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runningState) destroy() error {
|
||||||
|
running, err := r.c.isRunning()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if running {
|
||||||
|
return newGenericError(fmt.Errorf("container is not destroyed"), ContainerNotStopped)
|
||||||
|
}
|
||||||
|
return destroy(r.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pausedState represents a container that is currently pause. It cannot be destroyed in a
|
||||||
|
// paused state and must transition back to running first.
|
||||||
|
type pausedState struct {
|
||||||
|
c *linuxContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pausedState) status() Status {
|
||||||
|
return Paused
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pausedState) transition(s containerState) error {
|
||||||
|
switch s.(type) {
|
||||||
|
case *runningState, *stoppedState:
|
||||||
|
p.c.state = s
|
||||||
|
return nil
|
||||||
|
case *pausedState:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newStateTransitionError(p, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pausedState) destroy() error {
|
||||||
|
isRunning, err := p.c.isRunning()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !isRunning {
|
||||||
|
if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return destroy(p.c)
|
||||||
|
}
|
||||||
|
return newGenericError(fmt.Errorf("container is paused"), ContainerPaused)
|
||||||
|
}
|
||||||
|
|
||||||
|
// restoredState is the same as the running state but also has accociated checkpoint
|
||||||
|
// information that maybe need destroyed when the container is stopped and destory is called.
|
||||||
|
type restoredState struct {
|
||||||
|
imageDir string
|
||||||
|
c *linuxContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *restoredState) status() Status {
|
||||||
|
return Running
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *restoredState) transition(s containerState) error {
|
||||||
|
switch s.(type) {
|
||||||
|
case *stoppedState:
|
||||||
|
return nil
|
||||||
|
case *runningState:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newStateTransitionError(r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *restoredState) destroy() error {
|
||||||
|
if _, err := os.Stat(filepath.Join(r.c.root, "checkpoint")); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return destroy(r.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createdState is used whenever a container is restored, loaded, or setting additional
|
||||||
|
// processes inside and it should not be destroyed when it is exiting.
|
||||||
|
type createdState struct {
|
||||||
|
c *linuxContainer
|
||||||
|
s Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *createdState) status() Status {
|
||||||
|
return n.s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *createdState) transition(s containerState) error {
|
||||||
|
n.c.state = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *createdState) destroy() error {
|
||||||
|
if err := n.c.refreshState(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return n.c.state.destroy()
|
||||||
|
}
|
15
vendor/github.com/opencontainers/runc/libcontainer/stats.go
generated
vendored
Normal file
15
vendor/github.com/opencontainers/runc/libcontainer/stats.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
type NetworkInterface struct {
|
||||||
|
// Name is the name of the network interface.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
RxBytes uint64
|
||||||
|
RxPackets uint64
|
||||||
|
RxErrors uint64
|
||||||
|
RxDropped uint64
|
||||||
|
TxBytes uint64
|
||||||
|
TxPackets uint64
|
||||||
|
TxErrors uint64
|
||||||
|
TxDropped uint64
|
||||||
|
}
|
5
vendor/github.com/opencontainers/runc/libcontainer/stats_freebsd.go
generated
vendored
Normal file
5
vendor/github.com/opencontainers/runc/libcontainer/stats_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
Interfaces []*NetworkInterface
|
||||||
|
}
|
8
vendor/github.com/opencontainers/runc/libcontainer/stats_linux.go
generated
vendored
Normal file
8
vendor/github.com/opencontainers/runc/libcontainer/stats_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import "github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
Interfaces []*NetworkInterface
|
||||||
|
CgroupStats *cgroups.Stats
|
||||||
|
}
|
5
vendor/github.com/opencontainers/runc/libcontainer/stats_windows.go
generated
vendored
Normal file
5
vendor/github.com/opencontainers/runc/libcontainer/stats_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
Interfaces []*NetworkInterface
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue