Improve process addition and removal
Signed-off-by: Michael Crosby <crosbymichael@gmail.com> implement pause and resume Add godeps Signed-off-by: Michael Crosby <crosbymichael@gmail.com> Add readme Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
17d9c10e2d
commit
f9ad7970d2
397 changed files with 48104 additions and 22 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
containerd/containerd
|
containerd/containerd
|
||||||
|
bin/
|
||||||
|
|
79
README.md
Normal file
79
README.md
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# containerd
|
||||||
|
|
||||||
|
another container runtime
|
||||||
|
|
||||||
|
Start a container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -XPOST localhost:8888/containers/redis -d '{"bundlePath": "/containers/redis"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a process:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -XPUT localhost:8888/containers/redis/process -d@process.json | json_pp
|
||||||
|
{
|
||||||
|
"pid" : 25671,
|
||||||
|
"user" : {
|
||||||
|
"gid" : 0,
|
||||||
|
"uid" : 0
|
||||||
|
},
|
||||||
|
"args" : [
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"sleep 10"
|
||||||
|
],
|
||||||
|
"env" : [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Get containers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s localhost:8888/containers | json_pp
|
||||||
|
{
|
||||||
|
"containers" : [
|
||||||
|
{
|
||||||
|
"processes" : [
|
||||||
|
{
|
||||||
|
"args" : [
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"sleep 60"
|
||||||
|
],
|
||||||
|
"user" : {
|
||||||
|
"gid" : 0,
|
||||||
|
"uid" : 0
|
||||||
|
},
|
||||||
|
"pid" : 25743,
|
||||||
|
"env" : [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id" : "redis",
|
||||||
|
"state" : {
|
||||||
|
"status" : "running"
|
||||||
|
},
|
||||||
|
"bundlePath" : "/containers/redis"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Other stuff:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# pause and resume a container
|
||||||
|
curl -XPATCH localhost:8888/containers/redis -d '{"status": "paused"}'
|
||||||
|
curl -XPATCH localhost:8888/containers/redis -d '{"status": "running"}'
|
||||||
|
|
||||||
|
# send signal to a container's specific process
|
||||||
|
curl -XPOST localhost:8888/containers/redis/process/18306 -d '{"signal": 9}'
|
||||||
|
```
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/crosbymichael/containerd"
|
"github.com/docker/containerd"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/opencontainers/specs"
|
"github.com/opencontainers/specs"
|
||||||
)
|
)
|
||||||
|
@ -18,9 +18,14 @@ func NewServer(supervisor *containerd.Supervisor) http.Handler {
|
||||||
supervisor: supervisor,
|
supervisor: supervisor,
|
||||||
r: r,
|
r: r,
|
||||||
}
|
}
|
||||||
|
// TODO: add container stats
|
||||||
|
// TODO: add container checkpoint
|
||||||
|
// TODO: add container restore
|
||||||
|
// TODO: set prctl child subreaper
|
||||||
r.HandleFunc("/containers/{id:.*}/process/{pid:.*}", s.signalPid).Methods("POST")
|
r.HandleFunc("/containers/{id:.*}/process/{pid:.*}", s.signalPid).Methods("POST")
|
||||||
r.HandleFunc("/containers/{id:.*}/process", s.addProcess).Methods("PUT")
|
r.HandleFunc("/containers/{id:.*}/process", s.addProcess).Methods("PUT")
|
||||||
r.HandleFunc("/containers/{id:.*}", s.createContainer).Methods("POST")
|
r.HandleFunc("/containers/{id:.*}", s.createContainer).Methods("POST")
|
||||||
|
r.HandleFunc("/containers/{id:.*}", s.updateContainer).Methods("PATCH")
|
||||||
r.HandleFunc("/event", s.event).Methods("POST")
|
r.HandleFunc("/event", s.event).Methods("POST")
|
||||||
r.HandleFunc("/containers", s.containers).Methods("GET")
|
r.HandleFunc("/containers", s.containers).Methods("GET")
|
||||||
return s
|
return s
|
||||||
|
@ -31,10 +36,30 @@ type server struct {
|
||||||
supervisor *containerd.Supervisor
|
supervisor *containerd.Supervisor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement correct shutdown
|
||||||
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
s.r.ServeHTTP(w, r)
|
s.r.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *server) updateContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
var state ContainerState
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&state); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e := containerd.NewEvent(containerd.UpdateContainerEventType)
|
||||||
|
e.ID = id
|
||||||
|
e.State = &containerd.State{
|
||||||
|
Status: containerd.Status(string(state.Status)),
|
||||||
|
}
|
||||||
|
s.supervisor.SendEvent(e)
|
||||||
|
if err := <-e.Err; err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *server) event(w http.ResponseWriter, r *http.Request) {
|
func (s *server) event(w http.ResponseWriter, r *http.Request) {
|
||||||
var e containerd.Event
|
var e containerd.Event
|
||||||
if err := json.NewDecoder(r.Body).Decode(&e); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&e); err != nil {
|
||||||
|
@ -151,6 +176,9 @@ func writeContainers(w http.ResponseWriter, e *containerd.Event) error {
|
||||||
ID: c.ID(),
|
ID: c.ID(),
|
||||||
BundlePath: c.Path(),
|
BundlePath: c.Path(),
|
||||||
Processes: pids,
|
Processes: pids,
|
||||||
|
State: &ContainerState{
|
||||||
|
Status: Status(c.State().Status),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return json.NewEncoder(w).Encode(&state)
|
return json.NewEncoder(w).Encode(&state)
|
||||||
|
|
|
@ -4,10 +4,22 @@ type State struct {
|
||||||
Containers []Container `json:"containers"`
|
Containers []Container `json:"containers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Paused Status = "paused"
|
||||||
|
Running Status = "running"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContainerState struct {
|
||||||
|
Status Status `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
BundlePath string `json:"bundlePath,omitempty"`
|
BundlePath string `json:"bundlePath,omitempty"`
|
||||||
Processes []Process `json:"processes,omitempty"`
|
Processes []Process `json:"processes,omitempty"`
|
||||||
|
State *ContainerState `json:"state,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
|
|
25
container.go
25
container.go
|
@ -11,13 +11,38 @@ type Process interface {
|
||||||
Spec() specs.Process
|
Spec() specs.Process
|
||||||
Signal(os.Signal) error
|
Signal(os.Signal) error
|
||||||
}
|
}
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Paused Status = "paused"
|
||||||
|
Running Status = "running"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Status Status `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Container interface {
|
type Container interface {
|
||||||
|
// ID returns the container ID
|
||||||
ID() string
|
ID() string
|
||||||
|
// Start starts the init process of the container
|
||||||
Start() error
|
Start() error
|
||||||
|
// Path returns the path to the bundle
|
||||||
Path() string
|
Path() string
|
||||||
|
// Pid returns the container's init process id
|
||||||
Pid() (int, error)
|
Pid() (int, error)
|
||||||
|
// SetExited sets the exit status of the container after it's init dies
|
||||||
SetExited(status int)
|
SetExited(status int)
|
||||||
|
// Delete deletes the container
|
||||||
Delete() error
|
Delete() error
|
||||||
|
// Processes returns all the containers processes that have been added
|
||||||
Processes() ([]Process, error)
|
Processes() ([]Process, error)
|
||||||
|
// RemoveProcess removes a specific process for the container because it exited
|
||||||
|
RemoveProcess(pid int) error
|
||||||
|
// State returns the containers runtime state
|
||||||
|
State() State
|
||||||
|
// Resume resumes a paused container
|
||||||
|
Resume() error
|
||||||
|
// Pause pauses a running container
|
||||||
|
Pause() error
|
||||||
}
|
}
|
||||||
|
|
89
containerd/Godeps/Godeps.json
generated
Normal file
89
containerd/Godeps/Godeps.json
generated
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/containerd/containerd",
|
||||||
|
"GoVersion": "go1.5",
|
||||||
|
"Deps": [
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Sirupsen/logrus",
|
||||||
|
"Comment": "v0.7.3-2-g26709e2",
|
||||||
|
"Rev": "26709e2714106fb8ad40b773b711ebce25b78914"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/codegangsta/cli",
|
||||||
|
"Comment": "1.2.0-107-g942282e",
|
||||||
|
"Rev": "942282e931e8286aa802a30b01fa7e16befb50f3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/coreos/go-systemd/dbus",
|
||||||
|
"Comment": "v3",
|
||||||
|
"Rev": "be94bc700879ae8217780e9d141789a2defa302b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/coreos/go-systemd/util",
|
||||||
|
"Comment": "v3",
|
||||||
|
"Rev": "be94bc700879ae8217780e9d141789a2defa302b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/containerd",
|
||||||
|
"Rev": "1ee4428b003baadb16729459e678973618b5e5f4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/docker/pkg/mount",
|
||||||
|
"Comment": "v1.4.1-7549-gcc207aa",
|
||||||
|
"Rev": "cc207aa136fd5e01164d245de94fb900ca7212a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/docker/pkg/symlink",
|
||||||
|
"Comment": "v1.4.1-7549-gcc207aa",
|
||||||
|
"Rev": "cc207aa136fd5e01164d245de94fb900ca7212a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/docker/pkg/system",
|
||||||
|
"Comment": "v1.4.1-7549-gcc207aa",
|
||||||
|
"Rev": "cc207aa136fd5e01164d245de94fb900ca7212a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/docker/pkg/units",
|
||||||
|
"Comment": "v1.4.1-7549-gcc207aa",
|
||||||
|
"Rev": "cc207aa136fd5e01164d245de94fb900ca7212a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/godbus/dbus",
|
||||||
|
"Comment": "v2",
|
||||||
|
"Rev": "88765d85c0fdadcd98a54e30694fa4e4f5b51133"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/golang/protobuf/proto",
|
||||||
|
"Rev": "f7137ae6b19afbfd61a94b746fda3b3fe0491874"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/gorilla/context",
|
||||||
|
"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/gorilla/mux",
|
||||||
|
"Rev": "ad4d7a5882b961e07e2626045eb995c022ac6664"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/opencontainers/runc/libcontainer",
|
||||||
|
"Comment": "v0.0.4-159-gc4e5288",
|
||||||
|
"Rev": "c4e528889ab781cc84ba17a99f12a311016f0eed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/opencontainers/specs",
|
||||||
|
"Comment": "v0.1.1-76-g46d949e",
|
||||||
|
"Rev": "46d949ea81080c5f60dfb72ee91468b1e9fb2998"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/rcrowley/go-metrics",
|
||||||
|
"Rev": "1ce93efbc8f9c568886b2ef85ce305b2217b3de3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/syndtr/gocapability/capability",
|
||||||
|
"Rev": "2c00daeb6c3b45114c80ac44119e7b8801fdd852"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/vishvananda/netlink",
|
||||||
|
"Rev": "ecf47fd5739b3d2c3daf7c89c4b9715a2605c21b"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
containerd/Godeps/Readme
generated
Normal file
5
containerd/Godeps/Readme
generated
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
This directory tree is generated automatically by godep.
|
||||||
|
|
||||||
|
Please do not edit.
|
||||||
|
|
||||||
|
See https://github.com/tools/godep for more information.
|
6
containerd/Makefile
Normal file
6
containerd/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
all:
|
||||||
|
godep go build -tags libcontainer
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/crosbymichael/containerd"
|
"github.com/docker/containerd"
|
||||||
"github.com/crosbymichael/containerd/api/v1"
|
"github.com/docker/containerd/api/v1"
|
||||||
"github.com/opencontainers/runc/libcontainer/utils"
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
"github.com/rcrowley/go-metrics"
|
"github.com/rcrowley/go-metrics"
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/crosbymichael/containerd"
|
"github.com/docker/containerd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var JournalCommand = cli.Command{
|
var JournalCommand = cli.Command{
|
||||||
|
|
1
containerd/vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
Normal file
1
containerd/vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
logrus
|
8
containerd/vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
Normal file
8
containerd/vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.2
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- tip
|
||||||
|
install:
|
||||||
|
- go get -t ./...
|
7
containerd/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
7
containerd/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# 0.7.3
|
||||||
|
|
||||||
|
formatter/\*: allow configuration of timestamp layout
|
||||||
|
|
||||||
|
# 0.7.2
|
||||||
|
|
||||||
|
formatter/text: Add configuration option for time format (#158)
|
21
containerd/vendor/github.com/Sirupsen/logrus/LICENSE
generated
vendored
Normal file
21
containerd/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.
|
349
containerd/vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
Normal file
349
containerd/vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![godoc reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc]
|
||||||
|
|
||||||
|
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||||
|
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
||||||
|
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
|
||||||
|
many large deployments. The core API is unlikely to change much but please
|
||||||
|
version control your Logrus to make sure you aren't fetching latest `master` on
|
||||||
|
every build.**
|
||||||
|
|
||||||
|
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||||
|
plain text):
|
||||||
|
|
||||||
|
![Colored](http://i.imgur.com/PY7qMwd.png)
|
||||||
|
|
||||||
|
With `log.Formatter = new(logrus.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.Formatter = new(logrus.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
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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"
|
||||||
|
"github.com/Sirupsen/logrus/hooks/airbrake"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Log as JSON instead of the default ASCII formatter.
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
|
||||||
|
// 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("https://example.com", "xyz", "development"))
|
||||||
|
|
||||||
|
// Output to stderr instead of stdout, could also be a file.
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
// 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 (
|
||||||
|
"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.Stderr
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
Logrus encourages careful, structured logging though 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.
|
||||||
|
|
||||||
|
#### 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"
|
||||||
|
"github.com/Sirupsen/logrus/hooks/airbrake"
|
||||||
|
"github.com/Sirupsen/logrus/hooks/syslog"
|
||||||
|
"log/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
| Hook | Description |
|
||||||
|
| ----- | ----------- |
|
||||||
|
| [Airbrake](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) | Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
|
||||||
|
| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. |
|
||||||
|
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
|
||||||
|
| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
||||||
|
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
|
||||||
|
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
|
||||||
|
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
|
||||||
|
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
|
||||||
|
| [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) |
|
||||||
|
|
||||||
|
#### 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(logrus.JSONFormatter)
|
||||||
|
} else {
|
||||||
|
// The TextFormatter is default, you don't actually have to do this.
|
||||||
|
log.SetFormatter(logrus.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`
|
||||||
|
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||||
|
* `logrus_logstash.LogstashFormatter`. Logs fields as Logstash Events (http://logstash.net).
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.SetFormatter(&logrus_logstash.LogstashFormatter{Type: “application_name"})
|
||||||
|
```
|
||||||
|
|
||||||
|
Third party logging formatters:
|
||||||
|
|
||||||
|
* [`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 *JSONFormatter) 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 transormed 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`.
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
|
||||||
|
[godoc]: https://godoc.org/github.com/Sirupsen/logrus
|
252
containerd/vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
Normal file
252
containerd/vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
Level Level
|
||||||
|
|
||||||
|
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEntry(logger *Logger) *Entry {
|
||||||
|
return &Entry{
|
||||||
|
Logger: logger,
|
||||||
|
// Default is three fields, give a little extra room
|
||||||
|
Data: make(Fields, 5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||||
|
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
|
return bytes.NewBuffer(serialized), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the string representation from the reader and ultimately the
|
||||||
|
// formatter.
|
||||||
|
func (entry *Entry) String() (string, error) {
|
||||||
|
reader, err := entry.Reader()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader.String(), 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 := 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}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) log(level Level, msg string) {
|
||||||
|
entry.Time = time.Now()
|
||||||
|
entry.Level = level
|
||||||
|
entry.Message = msg
|
||||||
|
|
||||||
|
if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, err := entry.Reader()
|
||||||
|
if err != nil {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
defer entry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
_, err = io.Copy(entry.Logger.Out, reader)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) 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...))
|
||||||
|
}
|
||||||
|
os.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...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
50
containerd/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
Normal file
50
containerd/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logrus.New()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.Formatter = new(logrus.JSONFormatter)
|
||||||
|
log.Formatter = new(logrus.TextFormatter) // default
|
||||||
|
log.Level = logrus.DebugLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer func() {
|
||||||
|
err := recover()
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"err": err,
|
||||||
|
"number": 100,
|
||||||
|
}).Fatal("The ice breaks!")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"number": 8,
|
||||||
|
}).Debug("Started observing beach")
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 122,
|
||||||
|
}).Warn("The group's number increased tremendously!")
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"temperature": -4,
|
||||||
|
}).Debug("Temperature changes")
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "orca",
|
||||||
|
"size": 9009,
|
||||||
|
}).Panic("It's over 9000!")
|
||||||
|
}
|
30
containerd/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
generated
vendored
Normal file
30
containerd/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/Sirupsen/logrus/hooks/airbrake"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logrus.New()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.Formatter = new(logrus.TextFormatter) // default
|
||||||
|
log.Hooks.Add(airbrake.NewHook("https://example.com", "xyz", "development"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 122,
|
||||||
|
}).Warn("The group's number increased tremendously!")
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 100,
|
||||||
|
}).Fatal("The ice breaks!")
|
||||||
|
}
|
188
containerd/vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
Normal file
188
containerd/vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
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.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Out = 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.Level = 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
func Fatalln(args ...interface{}) {
|
||||||
|
std.Fatalln(args...)
|
||||||
|
}
|
48
containerd/vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
Normal file
48
containerd/vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
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) {
|
||||||
|
_, ok := data["time"]
|
||||||
|
if ok {
|
||||||
|
data["fields.time"] = data["time"]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok = data["msg"]
|
||||||
|
if ok {
|
||||||
|
data["fields.msg"] = data["msg"]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok = data["level"]
|
||||||
|
if ok {
|
||||||
|
data["fields.level"] = data["level"]
|
||||||
|
}
|
||||||
|
}
|
56
containerd/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
Normal file
56
containerd/vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package logstash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Formatter generates json in logstash format.
|
||||||
|
// Logstash site: http://logstash.net/
|
||||||
|
type LogstashFormatter struct {
|
||||||
|
Type string // if not empty use for logstash type field.
|
||||||
|
|
||||||
|
// TimestampFormat sets the format used for timestamps.
|
||||||
|
TimestampFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||||
|
entry.Data["@version"] = 1
|
||||||
|
|
||||||
|
if f.TimestampFormat == "" {
|
||||||
|
f.TimestampFormat = logrus.DefaultTimestampFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat)
|
||||||
|
|
||||||
|
// set message field
|
||||||
|
v, ok := entry.Data["message"]
|
||||||
|
if ok {
|
||||||
|
entry.Data["fields.message"] = v
|
||||||
|
}
|
||||||
|
entry.Data["message"] = entry.Message
|
||||||
|
|
||||||
|
// set level field
|
||||||
|
v, ok = entry.Data["level"]
|
||||||
|
if ok {
|
||||||
|
entry.Data["fields.level"] = v
|
||||||
|
}
|
||||||
|
entry.Data["level"] = entry.Level.String()
|
||||||
|
|
||||||
|
// set type field
|
||||||
|
if f.Type != "" {
|
||||||
|
v, ok = entry.Data["type"]
|
||||||
|
if ok {
|
||||||
|
entry.Data["fields.type"] = v
|
||||||
|
}
|
||||||
|
entry.Data["type"] = f.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
34
containerd/vendor/github.com/Sirupsen/logrus/hooks.go
generated
vendored
Normal file
34
containerd/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
|
||||||
|
}
|
54
containerd/vendor/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
generated
vendored
Normal file
54
containerd/vendor/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package airbrake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/tobi/airbrake-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AirbrakeHook to send exceptions to an exception-tracking service compatible
|
||||||
|
// with the Airbrake API.
|
||||||
|
type airbrakeHook struct {
|
||||||
|
APIKey string
|
||||||
|
Endpoint string
|
||||||
|
Environment string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHook(endpoint, apiKey, env string) *airbrakeHook {
|
||||||
|
return &airbrakeHook{
|
||||||
|
APIKey: apiKey,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
Environment: env,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *airbrakeHook) Fire(entry *logrus.Entry) error {
|
||||||
|
airbrake.ApiKey = hook.APIKey
|
||||||
|
airbrake.Endpoint = hook.Endpoint
|
||||||
|
airbrake.Environment = hook.Environment
|
||||||
|
|
||||||
|
var notifyErr error
|
||||||
|
err, ok := entry.Data["error"].(error)
|
||||||
|
if ok {
|
||||||
|
notifyErr = err
|
||||||
|
} else {
|
||||||
|
notifyErr = errors.New(entry.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
airErr := airbrake.Notify(notifyErr)
|
||||||
|
if airErr != nil {
|
||||||
|
return fmt.Errorf("Failed to send error to Airbrake: %s", airErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *airbrakeHook) Levels() []logrus.Level {
|
||||||
|
return []logrus.Level{
|
||||||
|
logrus.ErrorLevel,
|
||||||
|
logrus.FatalLevel,
|
||||||
|
logrus.PanicLevel,
|
||||||
|
}
|
||||||
|
}
|
68
containerd/vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go
generated
vendored
Normal file
68
containerd/vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package logrus_bugsnag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/bugsnag/bugsnag-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bugsnagHook struct{}
|
||||||
|
|
||||||
|
// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before
|
||||||
|
// bugsnag.Configure. Bugsnag must be configured before the hook.
|
||||||
|
var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook")
|
||||||
|
|
||||||
|
// ErrBugsnagSendFailed indicates that the hook failed to submit an error to
|
||||||
|
// bugsnag. The error was successfully generated, but `bugsnag.Notify()`
|
||||||
|
// failed.
|
||||||
|
type ErrBugsnagSendFailed struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrBugsnagSendFailed) Error() string {
|
||||||
|
return "failed to send error to Bugsnag: " + e.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBugsnagHook initializes a logrus hook which sends exceptions to an
|
||||||
|
// exception-tracking service compatible with the Bugsnag API. Before using
|
||||||
|
// this hook, you must call bugsnag.Configure(). The returned object should be
|
||||||
|
// registered with a log via `AddHook()`
|
||||||
|
//
|
||||||
|
// Entries that trigger an Error, Fatal or Panic should now include an "error"
|
||||||
|
// field to send to Bugsnag.
|
||||||
|
func NewBugsnagHook() (*bugsnagHook, error) {
|
||||||
|
if bugsnag.Config.APIKey == "" {
|
||||||
|
return nil, ErrBugsnagUnconfigured
|
||||||
|
}
|
||||||
|
return &bugsnagHook{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
|
||||||
|
// "error" field (or the Message if the error isn't present) and sends it off.
|
||||||
|
func (hook *bugsnagHook) Fire(entry *logrus.Entry) error {
|
||||||
|
var notifyErr error
|
||||||
|
err, ok := entry.Data["error"].(error)
|
||||||
|
if ok {
|
||||||
|
notifyErr = err
|
||||||
|
} else {
|
||||||
|
notifyErr = errors.New(entry.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
bugsnagErr := bugsnag.Notify(notifyErr)
|
||||||
|
if bugsnagErr != nil {
|
||||||
|
return ErrBugsnagSendFailed{bugsnagErr}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Levels enumerates the log levels on which the error should be forwarded to
|
||||||
|
// bugsnag: everything at or above the "Error" level.
|
||||||
|
func (hook *bugsnagHook) Levels() []logrus.Level {
|
||||||
|
return []logrus.Level{
|
||||||
|
logrus.ErrorLevel,
|
||||||
|
logrus.FatalLevel,
|
||||||
|
logrus.PanicLevel,
|
||||||
|
}
|
||||||
|
}
|
28
containerd/vendor/github.com/Sirupsen/logrus/hooks/papertrail/README.md
generated
vendored
Normal file
28
containerd/vendor/github.com/Sirupsen/logrus/hooks/papertrail/README.md
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Papertrail Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" />
|
||||||
|
|
||||||
|
[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts).
|
||||||
|
|
||||||
|
In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`.
|
||||||
|
|
||||||
|
For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"log/syslog"
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/Sirupsen/logrus/hooks/papertrail"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := logrus.New()
|
||||||
|
hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
55
containerd/vendor/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go
generated
vendored
Normal file
55
containerd/vendor/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package logrus_papertrail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
format = "Jan 2 15:04:05"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PapertrailHook to send logs to a logging service compatible with the Papertrail API.
|
||||||
|
type PapertrailHook struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
AppName string
|
||||||
|
UDPConn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPapertrailHook creates a hook to be added to an instance of logger.
|
||||||
|
func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) {
|
||||||
|
conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port))
|
||||||
|
return &PapertrailHook{host, port, appName, conn}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire is called when a log event is fired.
|
||||||
|
func (hook *PapertrailHook) Fire(entry *logrus.Entry) error {
|
||||||
|
date := time.Now().Format(format)
|
||||||
|
msg, _ := entry.String()
|
||||||
|
payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg)
|
||||||
|
|
||||||
|
bytesWritten, err := hook.UDPConn.Write([]byte(payload))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Levels returns the available logging levels.
|
||||||
|
func (hook *PapertrailHook) Levels() []logrus.Level {
|
||||||
|
return []logrus.Level{
|
||||||
|
logrus.PanicLevel,
|
||||||
|
logrus.FatalLevel,
|
||||||
|
logrus.ErrorLevel,
|
||||||
|
logrus.WarnLevel,
|
||||||
|
logrus.InfoLevel,
|
||||||
|
logrus.DebugLevel,
|
||||||
|
}
|
||||||
|
}
|
61
containerd/vendor/github.com/Sirupsen/logrus/hooks/sentry/README.md
generated
vendored
Normal file
61
containerd/vendor/github.com/Sirupsen/logrus/hooks/sentry/README.md
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Sentry Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" />
|
||||||
|
|
||||||
|
[Sentry](https://getsentry.com) provides both self-hosted and hosted
|
||||||
|
solutions for exception tracking.
|
||||||
|
Both client and server are
|
||||||
|
[open source](https://github.com/getsentry/sentry).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Every sentry application defined on the server gets a different
|
||||||
|
[DSN](https://www.getsentry.com/docs/). In the example below replace
|
||||||
|
`YOUR_DSN` with the one created for your application.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/Sirupsen/logrus/hooks/sentry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := logrus.New()
|
||||||
|
hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{
|
||||||
|
logrus.PanicLevel,
|
||||||
|
logrus.FatalLevel,
|
||||||
|
logrus.ErrorLevel,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Special fields
|
||||||
|
|
||||||
|
Some logrus fields have a special meaning in this hook,
|
||||||
|
these are server_name and logger.
|
||||||
|
When logs are sent to sentry these fields are treated differently.
|
||||||
|
- server_name (also known as hostname) is the name of the server which
|
||||||
|
is logging the event (hostname.example.com)
|
||||||
|
- logger is the part of the application which is logging the event.
|
||||||
|
In go this usually means setting it to the name of the package.
|
||||||
|
|
||||||
|
## Timeout
|
||||||
|
|
||||||
|
`Timeout` is the time the sentry hook will wait for a response
|
||||||
|
from the sentry server.
|
||||||
|
|
||||||
|
If this time elapses with no response from
|
||||||
|
the server an error will be returned.
|
||||||
|
|
||||||
|
If `Timeout` is set to 0 the SentryHook will not wait for a reply
|
||||||
|
and will assume a correct delivery.
|
||||||
|
|
||||||
|
The SentryHook has a default timeout of `100 milliseconds` when created
|
||||||
|
with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field:
|
||||||
|
|
||||||
|
```go
|
||||||
|
hook, _ := logrus_sentry.NewSentryHook(...)
|
||||||
|
hook.Timeout = 20*time.Second
|
||||||
|
```
|
100
containerd/vendor/github.com/Sirupsen/logrus/hooks/sentry/sentry.go
generated
vendored
Normal file
100
containerd/vendor/github.com/Sirupsen/logrus/hooks/sentry/sentry.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package logrus_sentry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/getsentry/raven-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
severityMap = map[logrus.Level]raven.Severity{
|
||||||
|
logrus.DebugLevel: raven.DEBUG,
|
||||||
|
logrus.InfoLevel: raven.INFO,
|
||||||
|
logrus.WarnLevel: raven.WARNING,
|
||||||
|
logrus.ErrorLevel: raven.ERROR,
|
||||||
|
logrus.FatalLevel: raven.FATAL,
|
||||||
|
logrus.PanicLevel: raven.FATAL,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getAndDel(d logrus.Fields, key string) (string, bool) {
|
||||||
|
var (
|
||||||
|
ok bool
|
||||||
|
v interface{}
|
||||||
|
val string
|
||||||
|
)
|
||||||
|
if v, ok = d[key]; !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok = v.(string); !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
delete(d, key)
|
||||||
|
return val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentryHook delivers logs to a sentry server.
|
||||||
|
type SentryHook struct {
|
||||||
|
// Timeout sets the time to wait for a delivery error from the sentry server.
|
||||||
|
// If this is set to zero the server will not wait for any response and will
|
||||||
|
// consider the message correctly sent
|
||||||
|
Timeout time.Duration
|
||||||
|
|
||||||
|
client *raven.Client
|
||||||
|
levels []logrus.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSentryHook creates a hook to be added to an instance of logger
|
||||||
|
// and initializes the raven client.
|
||||||
|
// This method sets the timeout to 100 milliseconds.
|
||||||
|
func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
|
||||||
|
client, err := raven.NewClient(DSN, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SentryHook{100 * time.Millisecond, client, levels}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when an event should be sent to sentry
|
||||||
|
// Special fields that sentry uses to give more information to the server
|
||||||
|
// are extracted from entry.Data (if they are found)
|
||||||
|
// These fields are: logger and server_name
|
||||||
|
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
|
||||||
|
packet := &raven.Packet{
|
||||||
|
Message: entry.Message,
|
||||||
|
Timestamp: raven.Timestamp(entry.Time),
|
||||||
|
Level: severityMap[entry.Level],
|
||||||
|
Platform: "go",
|
||||||
|
}
|
||||||
|
|
||||||
|
d := entry.Data
|
||||||
|
|
||||||
|
if logger, ok := getAndDel(d, "logger"); ok {
|
||||||
|
packet.Logger = logger
|
||||||
|
}
|
||||||
|
if serverName, ok := getAndDel(d, "server_name"); ok {
|
||||||
|
packet.ServerName = serverName
|
||||||
|
}
|
||||||
|
packet.Extra = map[string]interface{}(d)
|
||||||
|
|
||||||
|
_, errCh := hook.client.Capture(packet, nil)
|
||||||
|
timeout := hook.Timeout
|
||||||
|
if timeout != 0 {
|
||||||
|
timeoutCh := time.After(timeout)
|
||||||
|
select {
|
||||||
|
case err := <-errCh:
|
||||||
|
return err
|
||||||
|
case <-timeoutCh:
|
||||||
|
return fmt.Errorf("no response from sentry server in %s", timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Levels returns the available logging levels.
|
||||||
|
func (hook *SentryHook) Levels() []logrus.Level {
|
||||||
|
return hook.levels
|
||||||
|
}
|
20
containerd/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
generated
vendored
Normal file
20
containerd/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"log/syslog"
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := logrus.New()
|
||||||
|
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
59
containerd/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
generated
vendored
Normal file
59
containerd/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package logrus_syslog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"log/syslog"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SyslogHook to send logs via syslog.
|
||||||
|
type SyslogHook struct {
|
||||||
|
Writer *syslog.Writer
|
||||||
|
SyslogNetwork string
|
||||||
|
SyslogRaddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a hook to be added to an instance of logger. This is called with
|
||||||
|
// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
|
||||||
|
// `if err == nil { log.Hooks.Add(hook) }`
|
||||||
|
func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
|
||||||
|
w, err := syslog.Dial(network, raddr, priority, tag)
|
||||||
|
return &SyslogHook{w, network, raddr}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
|
||||||
|
line, err := entry.String()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch entry.Level {
|
||||||
|
case logrus.PanicLevel:
|
||||||
|
return hook.Writer.Crit(line)
|
||||||
|
case logrus.FatalLevel:
|
||||||
|
return hook.Writer.Crit(line)
|
||||||
|
case logrus.ErrorLevel:
|
||||||
|
return hook.Writer.Err(line)
|
||||||
|
case logrus.WarnLevel:
|
||||||
|
return hook.Writer.Warning(line)
|
||||||
|
case logrus.InfoLevel:
|
||||||
|
return hook.Writer.Info(line)
|
||||||
|
case logrus.DebugLevel:
|
||||||
|
return hook.Writer.Debug(line)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hook *SyslogHook) Levels() []logrus.Level {
|
||||||
|
return []logrus.Level{
|
||||||
|
logrus.PanicLevel,
|
||||||
|
logrus.FatalLevel,
|
||||||
|
logrus.ErrorLevel,
|
||||||
|
logrus.WarnLevel,
|
||||||
|
logrus.InfoLevel,
|
||||||
|
logrus.DebugLevel,
|
||||||
|
}
|
||||||
|
}
|
40
containerd/vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
40
containerd/vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSONFormatter struct {
|
||||||
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
|
TimestampFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefixFieldClashes(data)
|
||||||
|
|
||||||
|
if f.TimestampFormat == "" {
|
||||||
|
f.TimestampFormat = DefaultTimestampFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
data["time"] = entry.Time.Format(f.TimestampFormat)
|
||||||
|
data["msg"] = entry.Message
|
||||||
|
data["level"] = 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
|
||||||
|
}
|
203
containerd/vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
Normal file
203
containerd/vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
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.Stdout`. 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. `logrus.Debug` is useful in
|
||||||
|
Level Level
|
||||||
|
// Used to sync writing to the log.
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.Stdout,
|
||||||
|
Formatter: new(TextFormatter),
|
||||||
|
Hooks: make(levelHooks),
|
||||||
|
Level: InfoLevel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||||
|
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||||
|
// Ff you want multiple fields, use `WithFields`.
|
||||||
|
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||||
|
return NewEntry(logger).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 {
|
||||||
|
return NewEntry(logger).WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= DebugLevel {
|
||||||
|
NewEntry(logger).Debugf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= InfoLevel {
|
||||||
|
NewEntry(logger).Infof(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||||
|
NewEntry(logger).Printf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= ErrorLevel {
|
||||||
|
NewEntry(logger).Errorf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= FatalLevel {
|
||||||
|
NewEntry(logger).Fatalf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||||
|
if logger.Level >= PanicLevel {
|
||||||
|
NewEntry(logger).Panicf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debug(args ...interface{}) {
|
||||||
|
if logger.Level >= DebugLevel {
|
||||||
|
NewEntry(logger).Debug(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Info(args ...interface{}) {
|
||||||
|
if logger.Level >= InfoLevel {
|
||||||
|
NewEntry(logger).Info(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Print(args ...interface{}) {
|
||||||
|
NewEntry(logger).Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warn(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warn(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warning(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warn(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Error(args ...interface{}) {
|
||||||
|
if logger.Level >= ErrorLevel {
|
||||||
|
NewEntry(logger).Error(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatal(args ...interface{}) {
|
||||||
|
if logger.Level >= FatalLevel {
|
||||||
|
NewEntry(logger).Fatal(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panic(args ...interface{}) {
|
||||||
|
if logger.Level >= PanicLevel {
|
||||||
|
NewEntry(logger).Panic(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugln(args ...interface{}) {
|
||||||
|
if logger.Level >= DebugLevel {
|
||||||
|
NewEntry(logger).Debugln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infoln(args ...interface{}) {
|
||||||
|
if logger.Level >= InfoLevel {
|
||||||
|
NewEntry(logger).Infoln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Println(args ...interface{}) {
|
||||||
|
NewEntry(logger).Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnln(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningln(args ...interface{}) {
|
||||||
|
if logger.Level >= WarnLevel {
|
||||||
|
NewEntry(logger).Warnln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorln(args ...interface{}) {
|
||||||
|
if logger.Level >= ErrorLevel {
|
||||||
|
NewEntry(logger).Errorln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||||
|
if logger.Level >= FatalLevel {
|
||||||
|
NewEntry(logger).Fatalln(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicln(args ...interface{}) {
|
||||||
|
if logger.Level >= PanicLevel {
|
||||||
|
NewEntry(logger).Panicln(args...)
|
||||||
|
}
|
||||||
|
}
|
94
containerd/vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
Normal file
94
containerd/vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fields type, used to pass to `WithFields`.
|
||||||
|
type Fields map[string]interface{}
|
||||||
|
|
||||||
|
// Level type
|
||||||
|
type Level uint8
|
||||||
|
|
||||||
|
// 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 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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{})
|
||||||
|
}
|
12
containerd/vendor/github.com/Sirupsen/logrus/terminal_darwin.go
generated
vendored
Normal file
12
containerd/vendor/github.com/Sirupsen/logrus/terminal_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TIOCGETA
|
||||||
|
|
||||||
|
type Termios syscall.Termios
|
20
containerd/vendor/github.com/Sirupsen/logrus/terminal_freebsd.go
generated
vendored
Normal file
20
containerd/vendor/github.com/Sirupsen/logrus/terminal_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
|
||||||
|
*/
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TIOCGETA
|
||||||
|
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint32
|
||||||
|
Oflag uint32
|
||||||
|
Cflag uint32
|
||||||
|
Lflag uint32
|
||||||
|
Cc [20]uint8
|
||||||
|
Ispeed uint32
|
||||||
|
Ospeed uint32
|
||||||
|
}
|
12
containerd/vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
12
containerd/vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TCGETS
|
||||||
|
|
||||||
|
type Termios syscall.Termios
|
21
containerd/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
Normal file
21
containerd/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Based on ssh/terminal:
|
||||||
|
// Copyright 2011 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 linux darwin freebsd openbsd
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal() bool {
|
||||||
|
fd := syscall.Stdout
|
||||||
|
var termios Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
7
containerd/vendor/github.com/Sirupsen/logrus/terminal_openbsd.go
generated
vendored
Normal file
7
containerd/vendor/github.com/Sirupsen/logrus/terminal_openbsd.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TIOCGETA
|
||||||
|
|
||||||
|
type Termios syscall.Termios
|
27
containerd/vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
Normal file
27
containerd/vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Based on ssh/terminal:
|
||||||
|
// Copyright 2011 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 windows
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
|
||||||
|
var (
|
||||||
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal() bool {
|
||||||
|
fd := syscall.Stdout
|
||||||
|
var st uint32
|
||||||
|
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||||
|
return r != 0 && e == 0
|
||||||
|
}
|
149
containerd/vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
149
containerd/vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nocolor = 0
|
||||||
|
red = 31
|
||||||
|
green = 32
|
||||||
|
yellow = 33
|
||||||
|
blue = 34
|
||||||
|
gray = 37
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseTimestamp time.Time
|
||||||
|
isTerminal bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
baseTimestamp = time.Now()
|
||||||
|
isTerminal = IsTerminal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func miniTS() int {
|
||||||
|
return int(time.Since(baseTimestamp) / time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
var keys []string = make([]string, 0, len(entry.Data))
|
||||||
|
for k := range entry.Data {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.DisableSorting {
|
||||||
|
sort.Strings(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
|
||||||
|
prefixFieldClashes(entry.Data)
|
||||||
|
|
||||||
|
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
|
||||||
|
|
||||||
|
if f.TimestampFormat == "" {
|
||||||
|
f.TimestampFormat = DefaultTimestampFormat
|
||||||
|
}
|
||||||
|
if isColored {
|
||||||
|
f.printColored(b, entry, keys)
|
||||||
|
} else {
|
||||||
|
if !f.DisableTimestamp {
|
||||||
|
f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat))
|
||||||
|
}
|
||||||
|
f.appendKeyValue(b, "level", entry.Level.String())
|
||||||
|
f.appendKeyValue(b, "msg", 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) {
|
||||||
|
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())[0:4]
|
||||||
|
|
||||||
|
if !f.FullTimestamp {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
v := entry.Data[k]
|
||||||
|
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func needsQuoting(text string) bool {
|
||||||
|
for _, ch := range text {
|
||||||
|
if !((ch >= 'a' && ch <= 'z') ||
|
||||||
|
(ch >= 'A' && ch <= 'Z') ||
|
||||||
|
(ch >= '0' && ch <= '9') ||
|
||||||
|
ch == '-' || ch == '.') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
|
||||||
|
switch value.(type) {
|
||||||
|
case string:
|
||||||
|
if needsQuoting(value.(string)) {
|
||||||
|
fmt.Fprintf(b, "%v=%s ", key, value)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "%v=%q ", key, value)
|
||||||
|
}
|
||||||
|
case error:
|
||||||
|
if needsQuoting(value.(error).Error()) {
|
||||||
|
fmt.Fprintf(b, "%v=%s ", key, value)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "%v=%q ", key, value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(b, "%v=%v ", key, value)
|
||||||
|
}
|
||||||
|
}
|
31
containerd/vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
Normal file
31
containerd/vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (logger *Logger) Writer() *io.PipeWriter {
|
||||||
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
|
go logger.writerScanner(reader)
|
||||||
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
for scanner.Scan() {
|
||||||
|
logger.Print(scanner.Text())
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.Errorf("Error while reading from Writer: %s", err)
|
||||||
|
}
|
||||||
|
reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writerFinalizer(writer *io.PipeWriter) {
|
||||||
|
writer.Close()
|
||||||
|
}
|
6
containerd/vendor/github.com/codegangsta/cli/.travis.yml
generated
vendored
Normal file
6
containerd/vendor/github.com/codegangsta/cli/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
language: go
|
||||||
|
go: 1.1
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go vet ./...
|
||||||
|
- go test -v ./...
|
21
containerd/vendor/github.com/codegangsta/cli/LICENSE
generated
vendored
Normal file
21
containerd/vendor/github.com/codegangsta/cli/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Copyright (C) 2013 Jeremy Saenz
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
MIT LICENSE
|
||||||
|
|
||||||
|
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.
|
306
containerd/vendor/github.com/codegangsta/cli/README.md
generated
vendored
Normal file
306
containerd/vendor/github.com/codegangsta/cli/README.md
generated
vendored
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli)
|
||||||
|
|
||||||
|
# cli.go
|
||||||
|
cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way.
|
||||||
|
|
||||||
|
You can view the API docs here:
|
||||||
|
http://godoc.org/github.com/codegangsta/cli
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app.
|
||||||
|
|
||||||
|
**This is where cli.go comes into play.** cli.go makes command line programming fun, organized, and expressive!
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html).
|
||||||
|
|
||||||
|
To install `cli.go`, simply run:
|
||||||
|
```
|
||||||
|
$ go get github.com/codegangsta/cli
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used:
|
||||||
|
```
|
||||||
|
export PATH=$PATH:$GOPATH/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cli.NewApp().Run(os.Args)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "boom"
|
||||||
|
app.Usage = "make an explosive entrance"
|
||||||
|
app.Action = func(c *cli.Context) {
|
||||||
|
println("boom! I say!")
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Run(os.Args)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness!
|
||||||
|
|
||||||
|
Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it:
|
||||||
|
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "greet"
|
||||||
|
app.Usage = "fight the loneliness!"
|
||||||
|
app.Action = func(c *cli.Context) {
|
||||||
|
println("Hello friend!")
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Run(os.Args)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Install our command to the `$GOPATH/bin` directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go install
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally run our new command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ greet
|
||||||
|
Hello friend!
|
||||||
|
```
|
||||||
|
|
||||||
|
cli.go also generates some bitchass help text:
|
||||||
|
```
|
||||||
|
$ greet help
|
||||||
|
NAME:
|
||||||
|
greet - fight the loneliness!
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
greet [global options] command [command options] [arguments...]
|
||||||
|
|
||||||
|
VERSION:
|
||||||
|
0.0.0
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
help, h Shows a list of commands or help for one command
|
||||||
|
|
||||||
|
GLOBAL OPTIONS
|
||||||
|
--version Shows version information
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arguments
|
||||||
|
You can lookup arguments by calling the `Args` function on `cli.Context`.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
...
|
||||||
|
app.Action = func(c *cli.Context) {
|
||||||
|
println("Hello", c.Args()[0])
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
Setting and querying flags is simple.
|
||||||
|
``` go
|
||||||
|
...
|
||||||
|
app.Flags = []cli.Flag {
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "lang",
|
||||||
|
Value: "english",
|
||||||
|
Usage: "language for the greeting",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Action = func(c *cli.Context) {
|
||||||
|
name := "someone"
|
||||||
|
if len(c.Args()) > 0 {
|
||||||
|
name = c.Args()[0]
|
||||||
|
}
|
||||||
|
if c.String("lang") == "spanish" {
|
||||||
|
println("Hola", name)
|
||||||
|
} else {
|
||||||
|
println("Hello", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Alternate Names
|
||||||
|
|
||||||
|
You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
app.Flags = []cli.Flag {
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "lang, l",
|
||||||
|
Value: "english",
|
||||||
|
Usage: "language for the greeting",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error.
|
||||||
|
|
||||||
|
#### Values from the Environment
|
||||||
|
|
||||||
|
You can also have the default value set from the environment via `EnvVar`. e.g.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
app.Flags = []cli.Flag {
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "lang, l",
|
||||||
|
Value: "english",
|
||||||
|
Usage: "language for the greeting",
|
||||||
|
EnvVar: "APP_LANG",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default.
|
||||||
|
|
||||||
|
``` go
|
||||||
|
app.Flags = []cli.Flag {
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "lang, l",
|
||||||
|
Value: "english",
|
||||||
|
Usage: "language for the greeting",
|
||||||
|
EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subcommands
|
||||||
|
|
||||||
|
Subcommands can be defined for a more git-like command line app.
|
||||||
|
```go
|
||||||
|
...
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "add",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "add a task to the list",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
println("added task: ", c.Args().First())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "complete",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "complete a task on the list",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
println("completed task: ", c.Args().First())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "template",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "options for task templates",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "add",
|
||||||
|
Usage: "add a new template",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
println("new task template: ", c.Args().First())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "remove",
|
||||||
|
Usage: "remove an existing template",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
println("removed task template: ", c.Args().First())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bash Completion
|
||||||
|
|
||||||
|
You can enable completion commands by setting the `EnableBashCompletion`
|
||||||
|
flag on the `App` object. By default, this setting will only auto-complete to
|
||||||
|
show an app's subcommands, but you can write your own completion methods for
|
||||||
|
the App or its subcommands.
|
||||||
|
```go
|
||||||
|
...
|
||||||
|
var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.EnableBashCompletion = true
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "complete",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "complete a task on the list",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
println("completed task: ", c.Args().First())
|
||||||
|
},
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
// This will complete if no args are passed
|
||||||
|
if len(c.Args()) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, t := range tasks {
|
||||||
|
fmt.Println(t)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### To Enable
|
||||||
|
|
||||||
|
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
|
||||||
|
setting the `PROG` variable to the name of your program:
|
||||||
|
|
||||||
|
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
|
||||||
|
|
||||||
|
#### To Distribute
|
||||||
|
|
||||||
|
Copy and modify `autocomplete/bash_autocomplete` to use your program name
|
||||||
|
rather than `$PROG` and have the user copy the file into
|
||||||
|
`/etc/bash_completion.d/` (or automatically install it there if you are
|
||||||
|
distributing a package). Alternatively you can just document that users should
|
||||||
|
source the generic `autocomplete/bash_autocomplete` with `$PROG` set to your
|
||||||
|
program name in their bash configuration.
|
||||||
|
|
||||||
|
## Contribution Guidelines
|
||||||
|
Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch.
|
||||||
|
|
||||||
|
If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together.
|
||||||
|
|
||||||
|
If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out.
|
298
containerd/vendor/github.com/codegangsta/cli/app.go
generated
vendored
Normal file
298
containerd/vendor/github.com/codegangsta/cli/app.go
generated
vendored
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// App is the main structure of a cli application. It is recomended that
|
||||||
|
// and app be created with the cli.NewApp() function
|
||||||
|
type App struct {
|
||||||
|
// The name of the program. Defaults to os.Args[0]
|
||||||
|
Name string
|
||||||
|
// Description of the program.
|
||||||
|
Usage string
|
||||||
|
// Version of the program
|
||||||
|
Version string
|
||||||
|
// List of commands to execute
|
||||||
|
Commands []Command
|
||||||
|
// List of flags to parse
|
||||||
|
Flags []Flag
|
||||||
|
// Boolean to enable bash completion commands
|
||||||
|
EnableBashCompletion bool
|
||||||
|
// Boolean to hide built-in help command
|
||||||
|
HideHelp bool
|
||||||
|
// Boolean to hide built-in version flag
|
||||||
|
HideVersion bool
|
||||||
|
// An action to execute when the bash-completion flag is set
|
||||||
|
BashComplete func(context *Context)
|
||||||
|
// An action to execute before any subcommands are run, but after the context is ready
|
||||||
|
// If a non-nil error is returned, no subcommands are run
|
||||||
|
Before func(context *Context) error
|
||||||
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||||
|
// It is run even if Action() panics
|
||||||
|
After func(context *Context) error
|
||||||
|
// The action to execute when no subcommands are specified
|
||||||
|
Action func(context *Context)
|
||||||
|
// Execute this function if the proper command cannot be found
|
||||||
|
CommandNotFound func(context *Context, command string)
|
||||||
|
// Compilation date
|
||||||
|
Compiled time.Time
|
||||||
|
// List of all authors who contributed
|
||||||
|
Authors []Author
|
||||||
|
// Name of Author (Note: Use App.Authors, this is deprecated)
|
||||||
|
Author string
|
||||||
|
// Email of Author (Note: Use App.Authors, this is deprecated)
|
||||||
|
Email string
|
||||||
|
// Writer writer to write output to
|
||||||
|
Writer io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tries to find out when this binary was compiled.
|
||||||
|
// Returns the current time if it fails to find it.
|
||||||
|
func compileTime() time.Time {
|
||||||
|
info, err := os.Stat(os.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
return info.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
|
||||||
|
func NewApp() *App {
|
||||||
|
return &App{
|
||||||
|
Name: os.Args[0],
|
||||||
|
Usage: "A new cli application",
|
||||||
|
Version: "0.0.0",
|
||||||
|
BashComplete: DefaultAppComplete,
|
||||||
|
Action: helpCommand.Action,
|
||||||
|
Compiled: compileTime(),
|
||||||
|
Writer: os.Stdout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
|
||||||
|
func (a *App) Run(arguments []string) (err error) {
|
||||||
|
if a.Author != "" || a.Email != "" {
|
||||||
|
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
|
||||||
|
}
|
||||||
|
|
||||||
|
// append help to commands
|
||||||
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||||
|
a.Commands = append(a.Commands, helpCommand)
|
||||||
|
if (HelpFlag != BoolFlag{}) {
|
||||||
|
a.appendFlag(HelpFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//append version/help flags
|
||||||
|
if a.EnableBashCompletion {
|
||||||
|
a.appendFlag(BashCompletionFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.HideVersion {
|
||||||
|
a.appendFlag(VersionFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse flags
|
||||||
|
set := flagSet(a.Name, a.Flags)
|
||||||
|
set.SetOutput(ioutil.Discard)
|
||||||
|
err = set.Parse(arguments[1:])
|
||||||
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
|
if nerr != nil {
|
||||||
|
fmt.Fprintln(a.Writer, nerr)
|
||||||
|
context := NewContext(a, set, set)
|
||||||
|
ShowAppHelp(context)
|
||||||
|
fmt.Fprintln(a.Writer)
|
||||||
|
return nerr
|
||||||
|
}
|
||||||
|
context := NewContext(a, set, set)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
|
||||||
|
ShowAppHelp(context)
|
||||||
|
fmt.Fprintln(a.Writer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkCompletions(context) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkHelp(context) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkVersion(context) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.After != nil {
|
||||||
|
defer func() {
|
||||||
|
// err is always nil here.
|
||||||
|
// There is a check to see if it is non-nil
|
||||||
|
// just few lines before.
|
||||||
|
err = a.After(context)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Before != nil {
|
||||||
|
err := a.Before(context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args := context.Args()
|
||||||
|
if args.Present() {
|
||||||
|
name := args.First()
|
||||||
|
c := a.Command(name)
|
||||||
|
if c != nil {
|
||||||
|
return c.Run(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run default Action
|
||||||
|
a.Action(context)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another entry point to the cli app, takes care of passing arguments and error handling
|
||||||
|
func (a *App) RunAndExitOnError() {
|
||||||
|
if err := a.Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
|
||||||
|
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||||
|
// append help to commands
|
||||||
|
if len(a.Commands) > 0 {
|
||||||
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||||
|
a.Commands = append(a.Commands, helpCommand)
|
||||||
|
if (HelpFlag != BoolFlag{}) {
|
||||||
|
a.appendFlag(HelpFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// append flags
|
||||||
|
if a.EnableBashCompletion {
|
||||||
|
a.appendFlag(BashCompletionFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse flags
|
||||||
|
set := flagSet(a.Name, a.Flags)
|
||||||
|
set.SetOutput(ioutil.Discard)
|
||||||
|
err = set.Parse(ctx.Args().Tail())
|
||||||
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
|
context := NewContext(a, set, ctx.globalSet)
|
||||||
|
|
||||||
|
if nerr != nil {
|
||||||
|
fmt.Fprintln(a.Writer, nerr)
|
||||||
|
if len(a.Commands) > 0 {
|
||||||
|
ShowSubcommandHelp(context)
|
||||||
|
} else {
|
||||||
|
ShowCommandHelp(ctx, context.Args().First())
|
||||||
|
}
|
||||||
|
fmt.Fprintln(a.Writer)
|
||||||
|
return nerr
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
|
||||||
|
ShowSubcommandHelp(context)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkCompletions(context) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.Commands) > 0 {
|
||||||
|
if checkSubcommandHelp(context) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if checkCommandHelp(ctx, context.Args().First()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.After != nil {
|
||||||
|
defer func() {
|
||||||
|
// err is always nil here.
|
||||||
|
// There is a check to see if it is non-nil
|
||||||
|
// just few lines before.
|
||||||
|
err = a.After(context)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Before != nil {
|
||||||
|
err := a.Before(context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args := context.Args()
|
||||||
|
if args.Present() {
|
||||||
|
name := args.First()
|
||||||
|
c := a.Command(name)
|
||||||
|
if c != nil {
|
||||||
|
return c.Run(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run default Action
|
||||||
|
a.Action(context)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the named command on App. Returns nil if the command does not exist
|
||||||
|
func (a *App) Command(name string) *Command {
|
||||||
|
for _, c := range a.Commands {
|
||||||
|
if c.HasName(name) {
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) hasFlag(flag Flag) bool {
|
||||||
|
for _, f := range a.Flags {
|
||||||
|
if flag == f {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) appendFlag(flag Flag) {
|
||||||
|
if !a.hasFlag(flag) {
|
||||||
|
a.Flags = append(a.Flags, flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author represents someone who has contributed to a cli project.
|
||||||
|
type Author struct {
|
||||||
|
Name string // The Authors name
|
||||||
|
Email string // The Authors email
|
||||||
|
}
|
||||||
|
|
||||||
|
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
|
||||||
|
func (a Author) String() string {
|
||||||
|
e := ""
|
||||||
|
if a.Email != "" {
|
||||||
|
e = "<" + a.Email + "> "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%v %v", a.Name, e)
|
||||||
|
}
|
13
containerd/vendor/github.com/codegangsta/cli/autocomplete/bash_autocomplete
generated
vendored
Normal file
13
containerd/vendor/github.com/codegangsta/cli/autocomplete/bash_autocomplete
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
_cli_bash_autocomplete() {
|
||||||
|
local cur prev opts base
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _cli_bash_autocomplete $PROG
|
5
containerd/vendor/github.com/codegangsta/cli/autocomplete/zsh_autocomplete
generated
vendored
Normal file
5
containerd/vendor/github.com/codegangsta/cli/autocomplete/zsh_autocomplete
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
autoload -U compinit && compinit
|
||||||
|
autoload -U bashcompinit && bashcompinit
|
||||||
|
|
||||||
|
script_dir=$(dirname $0)
|
||||||
|
source ${script_dir}/bash_autocomplete
|
19
containerd/vendor/github.com/codegangsta/cli/cli.go
generated
vendored
Normal file
19
containerd/vendor/github.com/codegangsta/cli/cli.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// Package cli provides a minimal framework for creating and organizing command line
|
||||||
|
// Go applications. cli is designed to be easy to understand and write, the most simple
|
||||||
|
// cli application can be written as follows:
|
||||||
|
// func main() {
|
||||||
|
// cli.NewApp().Run(os.Args)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Of course this application does not do much, so let's make this an actual application:
|
||||||
|
// func main() {
|
||||||
|
// app := cli.NewApp()
|
||||||
|
// app.Name = "greet"
|
||||||
|
// app.Usage = "say a greeting"
|
||||||
|
// app.Action = func(c *cli.Context) {
|
||||||
|
// println("Greetings")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// app.Run(os.Args)
|
||||||
|
// }
|
||||||
|
package cli
|
184
containerd/vendor/github.com/codegangsta/cli/command.go
generated
vendored
Normal file
184
containerd/vendor/github.com/codegangsta/cli/command.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command is a subcommand for a cli.App.
|
||||||
|
type Command struct {
|
||||||
|
// The name of the command
|
||||||
|
Name string
|
||||||
|
// short name of the command. Typically one character (deprecated, use `Aliases`)
|
||||||
|
ShortName string
|
||||||
|
// A list of aliases for the command
|
||||||
|
Aliases []string
|
||||||
|
// A short description of the usage of this command
|
||||||
|
Usage string
|
||||||
|
// A longer explanation of how the command works
|
||||||
|
Description string
|
||||||
|
// The function to call when checking for bash command completions
|
||||||
|
BashComplete func(context *Context)
|
||||||
|
// An action to execute before any sub-subcommands are run, but after the context is ready
|
||||||
|
// If a non-nil error is returned, no sub-subcommands are run
|
||||||
|
Before func(context *Context) error
|
||||||
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||||
|
// It is run even if Action() panics
|
||||||
|
After func(context *Context) error
|
||||||
|
// The function to call when this command is invoked
|
||||||
|
Action func(context *Context)
|
||||||
|
// List of child commands
|
||||||
|
Subcommands []Command
|
||||||
|
// List of flags to parse
|
||||||
|
Flags []Flag
|
||||||
|
// Treat all flags as normal arguments if true
|
||||||
|
SkipFlagParsing bool
|
||||||
|
// Boolean to hide built-in help command
|
||||||
|
HideHelp bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
||||||
|
func (c Command) Run(ctx *Context) error {
|
||||||
|
|
||||||
|
if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil {
|
||||||
|
return c.startApp(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.HideHelp && (HelpFlag != BoolFlag{}) {
|
||||||
|
// append help to flags
|
||||||
|
c.Flags = append(
|
||||||
|
c.Flags,
|
||||||
|
HelpFlag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.App.EnableBashCompletion {
|
||||||
|
c.Flags = append(c.Flags, BashCompletionFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
set := flagSet(c.Name, c.Flags)
|
||||||
|
set.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
|
firstFlagIndex := -1
|
||||||
|
terminatorIndex := -1
|
||||||
|
for index, arg := range ctx.Args() {
|
||||||
|
if arg == "--" {
|
||||||
|
terminatorIndex = index
|
||||||
|
break
|
||||||
|
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
|
||||||
|
firstFlagIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if firstFlagIndex > -1 && !c.SkipFlagParsing {
|
||||||
|
args := ctx.Args()
|
||||||
|
regularArgs := make([]string, len(args[1:firstFlagIndex]))
|
||||||
|
copy(regularArgs, args[1:firstFlagIndex])
|
||||||
|
|
||||||
|
var flagArgs []string
|
||||||
|
if terminatorIndex > -1 {
|
||||||
|
flagArgs = args[firstFlagIndex:terminatorIndex]
|
||||||
|
regularArgs = append(regularArgs, args[terminatorIndex:]...)
|
||||||
|
} else {
|
||||||
|
flagArgs = args[firstFlagIndex:]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = set.Parse(append(flagArgs, regularArgs...))
|
||||||
|
} else {
|
||||||
|
err = set.Parse(ctx.Args().Tail())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprint(ctx.App.Writer, "Incorrect Usage.\n\n")
|
||||||
|
ShowCommandHelp(ctx, c.Name)
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nerr := normalizeFlags(c.Flags, set)
|
||||||
|
if nerr != nil {
|
||||||
|
fmt.Fprintln(ctx.App.Writer, nerr)
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
ShowCommandHelp(ctx, c.Name)
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
return nerr
|
||||||
|
}
|
||||||
|
context := NewContext(ctx.App, set, ctx.globalSet)
|
||||||
|
|
||||||
|
if checkCommandCompletions(context, c.Name) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkCommandHelp(context, c.Name) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
context.Command = c
|
||||||
|
c.Action(context)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Command) Names() []string {
|
||||||
|
names := []string{c.Name}
|
||||||
|
|
||||||
|
if c.ShortName != "" {
|
||||||
|
names = append(names, c.ShortName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(names, c.Aliases...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if Command.Name or Command.ShortName matches given name
|
||||||
|
func (c Command) HasName(name string) bool {
|
||||||
|
for _, n := range c.Names() {
|
||||||
|
if n == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Command) startApp(ctx *Context) error {
|
||||||
|
app := NewApp()
|
||||||
|
|
||||||
|
// set the name and usage
|
||||||
|
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
|
||||||
|
if c.Description != "" {
|
||||||
|
app.Usage = c.Description
|
||||||
|
} else {
|
||||||
|
app.Usage = c.Usage
|
||||||
|
}
|
||||||
|
|
||||||
|
// set CommandNotFound
|
||||||
|
app.CommandNotFound = ctx.App.CommandNotFound
|
||||||
|
|
||||||
|
// set the flags and commands
|
||||||
|
app.Commands = c.Subcommands
|
||||||
|
app.Flags = c.Flags
|
||||||
|
app.HideHelp = c.HideHelp
|
||||||
|
|
||||||
|
app.Version = ctx.App.Version
|
||||||
|
app.HideVersion = ctx.App.HideVersion
|
||||||
|
app.Compiled = ctx.App.Compiled
|
||||||
|
app.Author = ctx.App.Author
|
||||||
|
app.Email = ctx.App.Email
|
||||||
|
app.Writer = ctx.App.Writer
|
||||||
|
|
||||||
|
// bash completion
|
||||||
|
app.EnableBashCompletion = ctx.App.EnableBashCompletion
|
||||||
|
if c.BashComplete != nil {
|
||||||
|
app.BashComplete = c.BashComplete
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the actions
|
||||||
|
app.Before = c.Before
|
||||||
|
app.After = c.After
|
||||||
|
if c.Action != nil {
|
||||||
|
app.Action = c.Action
|
||||||
|
} else {
|
||||||
|
app.Action = helpSubcommand.Action
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.RunAsSubcommand(ctx)
|
||||||
|
}
|
344
containerd/vendor/github.com/codegangsta/cli/context.go
generated
vendored
Normal file
344
containerd/vendor/github.com/codegangsta/cli/context.go
generated
vendored
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context is a type that is passed through to
|
||||||
|
// each Handler action in a cli application. Context
|
||||||
|
// can be used to retrieve context-specific Args and
|
||||||
|
// parsed command-line options.
|
||||||
|
type Context struct {
|
||||||
|
App *App
|
||||||
|
Command Command
|
||||||
|
flagSet *flag.FlagSet
|
||||||
|
globalSet *flag.FlagSet
|
||||||
|
setFlags map[string]bool
|
||||||
|
globalSetFlags map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new context. For use in when invoking an App or Command action.
|
||||||
|
func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context {
|
||||||
|
return &Context{App: app, flagSet: set, globalSet: globalSet}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local int flag, returns 0 if no int flag exists
|
||||||
|
func (c *Context) Int(name string) int {
|
||||||
|
return lookupInt(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists
|
||||||
|
func (c *Context) Duration(name string) time.Duration {
|
||||||
|
return lookupDuration(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists
|
||||||
|
func (c *Context) Float64(name string) float64 {
|
||||||
|
return lookupFloat64(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local bool flag, returns false if no bool flag exists
|
||||||
|
func (c *Context) Bool(name string) bool {
|
||||||
|
return lookupBool(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local boolT flag, returns false if no bool flag exists
|
||||||
|
func (c *Context) BoolT(name string) bool {
|
||||||
|
return lookupBoolT(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local string flag, returns "" if no string flag exists
|
||||||
|
func (c *Context) String(name string) string {
|
||||||
|
return lookupString(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local string slice flag, returns nil if no string slice flag exists
|
||||||
|
func (c *Context) StringSlice(name string) []string {
|
||||||
|
return lookupStringSlice(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local int slice flag, returns nil if no int slice flag exists
|
||||||
|
func (c *Context) IntSlice(name string) []int {
|
||||||
|
return lookupIntSlice(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a local generic flag, returns nil if no generic flag exists
|
||||||
|
func (c *Context) Generic(name string) interface{} {
|
||||||
|
return lookupGeneric(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a global int flag, returns 0 if no int flag exists
|
||||||
|
func (c *Context) GlobalInt(name string) int {
|
||||||
|
return lookupInt(name, c.globalSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists
|
||||||
|
func (c *Context) GlobalDuration(name string) time.Duration {
|
||||||
|
return lookupDuration(name, c.globalSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a global bool flag, returns false if no bool flag exists
|
||||||
|
func (c *Context) GlobalBool(name string) bool {
|
||||||
|
return lookupBool(name, c.globalSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a global string flag, returns "" if no string flag exists
|
||||||
|
func (c *Context) GlobalString(name string) string {
|
||||||
|
return lookupString(name, c.globalSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a global string slice flag, returns nil if no string slice flag exists
|
||||||
|
func (c *Context) GlobalStringSlice(name string) []string {
|
||||||
|
return lookupStringSlice(name, c.globalSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a global int slice flag, returns nil if no int slice flag exists
|
||||||
|
func (c *Context) GlobalIntSlice(name string) []int {
|
||||||
|
return lookupIntSlice(name, c.globalSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the value of a global generic flag, returns nil if no generic flag exists
|
||||||
|
func (c *Context) GlobalGeneric(name string) interface{} {
|
||||||
|
return lookupGeneric(name, c.globalSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of flags set
|
||||||
|
func (c *Context) NumFlags() int {
|
||||||
|
return c.flagSet.NFlag()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if the flag was actually set
|
||||||
|
func (c *Context) IsSet(name string) bool {
|
||||||
|
if c.setFlags == nil {
|
||||||
|
c.setFlags = make(map[string]bool)
|
||||||
|
c.flagSet.Visit(func(f *flag.Flag) {
|
||||||
|
c.setFlags[f.Name] = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c.setFlags[name] == true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if the global flag was actually set
|
||||||
|
func (c *Context) GlobalIsSet(name string) bool {
|
||||||
|
if c.globalSetFlags == nil {
|
||||||
|
c.globalSetFlags = make(map[string]bool)
|
||||||
|
c.globalSet.Visit(func(f *flag.Flag) {
|
||||||
|
c.globalSetFlags[f.Name] = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c.globalSetFlags[name] == true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a slice of flag names used in this context.
|
||||||
|
func (c *Context) FlagNames() (names []string) {
|
||||||
|
for _, flag := range c.Command.Flags {
|
||||||
|
name := strings.Split(flag.getName(), ",")[0]
|
||||||
|
if name == "help" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a slice of global flag names used by the app.
|
||||||
|
func (c *Context) GlobalFlagNames() (names []string) {
|
||||||
|
for _, flag := range c.App.Flags {
|
||||||
|
name := strings.Split(flag.getName(), ",")[0]
|
||||||
|
if name == "help" || name == "version" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Args []string
|
||||||
|
|
||||||
|
// Returns the command line arguments associated with the context.
|
||||||
|
func (c *Context) Args() Args {
|
||||||
|
args := Args(c.flagSet.Args())
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the nth argument, or else a blank string
|
||||||
|
func (a Args) Get(n int) string {
|
||||||
|
if len(a) > n {
|
||||||
|
return a[n]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the first argument, or else a blank string
|
||||||
|
func (a Args) First() string {
|
||||||
|
return a.Get(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the rest of the arguments (not the first one)
|
||||||
|
// or else an empty string slice
|
||||||
|
func (a Args) Tail() []string {
|
||||||
|
if len(a) >= 2 {
|
||||||
|
return []string(a)[1:]
|
||||||
|
}
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if there are any arguments present
|
||||||
|
func (a Args) Present() bool {
|
||||||
|
return len(a) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swaps arguments at the given indexes
|
||||||
|
func (a Args) Swap(from, to int) error {
|
||||||
|
if from >= len(a) || to >= len(a) {
|
||||||
|
return errors.New("index out of range")
|
||||||
|
}
|
||||||
|
a[from], a[to] = a[to], a[from]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupInt(name string, set *flag.FlagSet) int {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
val, err := strconv.Atoi(f.Value.String())
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
val, err := time.ParseDuration(f.Value.String())
|
||||||
|
if err == nil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupFloat64(name string, set *flag.FlagSet) float64 {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
val, err := strconv.ParseFloat(f.Value.String(), 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupString(name string, set *flag.FlagSet) string {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
return f.Value.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupStringSlice(name string, set *flag.FlagSet) []string {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
return (f.Value.(*StringSlice)).Value()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupIntSlice(name string, set *flag.FlagSet) []int {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
return (f.Value.(*IntSlice)).Value()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
return f.Value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupBool(name string, set *flag.FlagSet) bool {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
val, err := strconv.ParseBool(f.Value.String())
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupBoolT(name string, set *flag.FlagSet) bool {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
val, err := strconv.ParseBool(f.Value.String())
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
||||||
|
switch ff.Value.(type) {
|
||||||
|
case *StringSlice:
|
||||||
|
default:
|
||||||
|
set.Set(name, ff.Value.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
|
||||||
|
visited := make(map[string]bool)
|
||||||
|
set.Visit(func(f *flag.Flag) {
|
||||||
|
visited[f.Name] = true
|
||||||
|
})
|
||||||
|
for _, f := range flags {
|
||||||
|
parts := strings.Split(f.getName(), ",")
|
||||||
|
if len(parts) == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var ff *flag.Flag
|
||||||
|
for _, name := range parts {
|
||||||
|
name = strings.Trim(name, " ")
|
||||||
|
if visited[name] {
|
||||||
|
if ff != nil {
|
||||||
|
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
|
||||||
|
}
|
||||||
|
ff = set.Lookup(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ff == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, name := range parts {
|
||||||
|
name = strings.Trim(name, " ")
|
||||||
|
if !visited[name] {
|
||||||
|
copyFlag(name, ff, set)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
454
containerd/vendor/github.com/codegangsta/cli/flag.go
generated
vendored
Normal file
454
containerd/vendor/github.com/codegangsta/cli/flag.go
generated
vendored
Normal file
|
@ -0,0 +1,454 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This flag enables bash-completion for all commands and subcommands
|
||||||
|
var BashCompletionFlag = BoolFlag{
|
||||||
|
Name: "generate-bash-completion",
|
||||||
|
}
|
||||||
|
|
||||||
|
// This flag prints the version for the application
|
||||||
|
var VersionFlag = BoolFlag{
|
||||||
|
Name: "version, v",
|
||||||
|
Usage: "print the version",
|
||||||
|
}
|
||||||
|
|
||||||
|
// This flag prints the help for all commands and subcommands
|
||||||
|
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
||||||
|
// unless HideHelp is set to true)
|
||||||
|
var HelpFlag = BoolFlag{
|
||||||
|
Name: "help, h",
|
||||||
|
Usage: "show help",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag is a common interface related to parsing flags in cli.
|
||||||
|
// For more advanced flag parsing techniques, it is recomended that
|
||||||
|
// this interface be implemented.
|
||||||
|
type Flag interface {
|
||||||
|
fmt.Stringer
|
||||||
|
// Apply Flag settings to the given flag set
|
||||||
|
Apply(*flag.FlagSet)
|
||||||
|
getName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
||||||
|
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||||
|
|
||||||
|
for _, f := range flags {
|
||||||
|
f.Apply(set)
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func eachName(longName string, fn func(string)) {
|
||||||
|
parts := strings.Split(longName, ",")
|
||||||
|
for _, name := range parts {
|
||||||
|
name = strings.Trim(name, " ")
|
||||||
|
fn(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic is a generic parseable type identified by a specific flag
|
||||||
|
type Generic interface {
|
||||||
|
Set(value string) error
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericFlag is the flag type for types implementing Generic
|
||||||
|
type GenericFlag struct {
|
||||||
|
Name string
|
||||||
|
Value Generic
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the generic flag to display the
|
||||||
|
// help text to the user (uses the String() method of the generic flag to show
|
||||||
|
// the value)
|
||||||
|
func (f GenericFlag) String() string {
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s \"%v\"\t%v", prefixFor(f.Name), f.Name, f.Value, f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||||
|
// provided by the user for parsing by the flag
|
||||||
|
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
||||||
|
val := f.Value
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
val.Set(envVal)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Var(f.Value, name, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f GenericFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringSlice []string
|
||||||
|
|
||||||
|
func (f *StringSlice) Set(value string) error {
|
||||||
|
*f = append(*f, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *StringSlice) String() string {
|
||||||
|
return fmt.Sprintf("%s", *f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *StringSlice) Value() []string {
|
||||||
|
return *f
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringSliceFlag struct {
|
||||||
|
Name string
|
||||||
|
Value *StringSlice
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f StringSliceFlag) String() string {
|
||||||
|
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
||||||
|
pref := prefixFor(firstName)
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
newVal := &StringSlice{}
|
||||||
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
newVal.Set(s)
|
||||||
|
}
|
||||||
|
f.Value = newVal
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Var(f.Value, name, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f StringSliceFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntSlice []int
|
||||||
|
|
||||||
|
func (f *IntSlice) Set(value string) error {
|
||||||
|
|
||||||
|
tmp, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
*f = append(*f, tmp)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *IntSlice) String() string {
|
||||||
|
return fmt.Sprintf("%d", *f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *IntSlice) Value() []int {
|
||||||
|
return *f
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntSliceFlag struct {
|
||||||
|
Name string
|
||||||
|
Value *IntSlice
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f IntSliceFlag) String() string {
|
||||||
|
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
||||||
|
pref := prefixFor(firstName)
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
newVal := &IntSlice{}
|
||||||
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
err := newVal.Set(s)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.Value = newVal
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Var(f.Value, name, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f IntSliceFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoolFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BoolFlag) String() string {
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
||||||
|
val := false
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
envValBool, err := strconv.ParseBool(envVal)
|
||||||
|
if err == nil {
|
||||||
|
val = envValBool
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Bool(name, val, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BoolFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoolTFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BoolTFlag) String() string {
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
||||||
|
val := true
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
envValBool, err := strconv.ParseBool(envVal)
|
||||||
|
if err == nil {
|
||||||
|
val = envValBool
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Bool(name, val, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BoolTFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringFlag struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f StringFlag) String() string {
|
||||||
|
var fmtString string
|
||||||
|
fmtString = "%s %v\t%v"
|
||||||
|
|
||||||
|
if len(f.Value) > 0 {
|
||||||
|
fmtString = "%s \"%v\"\t%v"
|
||||||
|
} else {
|
||||||
|
fmtString = "%s %v\t%v"
|
||||||
|
}
|
||||||
|
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f StringFlag) Apply(set *flag.FlagSet) {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
f.Value = envVal
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.String(name, f.Value, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f StringFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntFlag struct {
|
||||||
|
Name string
|
||||||
|
Value int
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f IntFlag) String() string {
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f IntFlag) Apply(set *flag.FlagSet) {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||||
|
if err == nil {
|
||||||
|
f.Value = int(envValInt)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Int(name, f.Value, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f IntFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type DurationFlag struct {
|
||||||
|
Name string
|
||||||
|
Value time.Duration
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f DurationFlag) String() string {
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
envValDuration, err := time.ParseDuration(envVal)
|
||||||
|
if err == nil {
|
||||||
|
f.Value = envValDuration
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Duration(name, f.Value, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f DurationFlag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float64Flag struct {
|
||||||
|
Name string
|
||||||
|
Value float64
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Float64Flag) String() string {
|
||||||
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal := os.Getenv(envVar); envVal != "" {
|
||||||
|
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
||||||
|
if err == nil {
|
||||||
|
f.Value = float64(envValFloat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Float64(name, f.Value, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Float64Flag) getName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func prefixFor(name string) (prefix string) {
|
||||||
|
if len(name) == 1 {
|
||||||
|
prefix = "-"
|
||||||
|
} else {
|
||||||
|
prefix = "--"
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func prefixedNames(fullName string) (prefixed string) {
|
||||||
|
parts := strings.Split(fullName, ",")
|
||||||
|
for i, name := range parts {
|
||||||
|
name = strings.Trim(name, " ")
|
||||||
|
prefixed += prefixFor(name) + name
|
||||||
|
if i < len(parts)-1 {
|
||||||
|
prefixed += ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func withEnvHint(envVar, str string) string {
|
||||||
|
envText := ""
|
||||||
|
if envVar != "" {
|
||||||
|
envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $"))
|
||||||
|
}
|
||||||
|
return str + envText
|
||||||
|
}
|
235
containerd/vendor/github.com/codegangsta/cli/help.go
generated
vendored
Normal file
235
containerd/vendor/github.com/codegangsta/cli/help.go
generated
vendored
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The text template for the Default help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
var AppHelpTemplate = `NAME:
|
||||||
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]
|
||||||
|
|
||||||
|
VERSION:
|
||||||
|
{{.Version}}{{if len .Authors}}
|
||||||
|
|
||||||
|
AUTHOR(S):
|
||||||
|
{{range .Authors}}{{ . }}{{end}}{{end}}
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||||
|
{{end}}{{if .Flags}}
|
||||||
|
GLOBAL OPTIONS:
|
||||||
|
{{range .Flags}}{{.}}
|
||||||
|
{{end}}{{end}}
|
||||||
|
`
|
||||||
|
|
||||||
|
// The text template for the command help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
var CommandHelpTemplate = `NAME:
|
||||||
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
command {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}}
|
||||||
|
|
||||||
|
DESCRIPTION:
|
||||||
|
{{.Description}}{{end}}{{if .Flags}}
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
{{range .Flags}}{{.}}
|
||||||
|
{{end}}{{ end }}
|
||||||
|
`
|
||||||
|
|
||||||
|
// The text template for the subcommand help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
var SubcommandHelpTemplate = `NAME:
|
||||||
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
{{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...]
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||||
|
{{end}}{{if .Flags}}
|
||||||
|
OPTIONS:
|
||||||
|
{{range .Flags}}{{.}}
|
||||||
|
{{end}}{{end}}
|
||||||
|
`
|
||||||
|
|
||||||
|
var helpCommand = Command{
|
||||||
|
Name: "help",
|
||||||
|
Aliases: []string{"h"},
|
||||||
|
Usage: "Shows a list of commands or help for one command",
|
||||||
|
Action: func(c *Context) {
|
||||||
|
args := c.Args()
|
||||||
|
if args.Present() {
|
||||||
|
ShowCommandHelp(c, args.First())
|
||||||
|
} else {
|
||||||
|
ShowAppHelp(c)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var helpSubcommand = Command{
|
||||||
|
Name: "help",
|
||||||
|
Aliases: []string{"h"},
|
||||||
|
Usage: "Shows a list of commands or help for one command",
|
||||||
|
Action: func(c *Context) {
|
||||||
|
args := c.Args()
|
||||||
|
if args.Present() {
|
||||||
|
ShowCommandHelp(c, args.First())
|
||||||
|
} else {
|
||||||
|
ShowSubcommandHelp(c)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints help for the App or Command
|
||||||
|
type helpPrinter func(w io.Writer, templ string, data interface{})
|
||||||
|
|
||||||
|
var HelpPrinter helpPrinter = printHelp
|
||||||
|
|
||||||
|
// Prints version for the App
|
||||||
|
var VersionPrinter = printVersion
|
||||||
|
|
||||||
|
func ShowAppHelp(c *Context) {
|
||||||
|
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the list of subcommands as the default app completion method
|
||||||
|
func DefaultAppComplete(c *Context) {
|
||||||
|
for _, command := range c.App.Commands {
|
||||||
|
for _, name := range command.Names() {
|
||||||
|
fmt.Fprintln(c.App.Writer, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints help for the given command
|
||||||
|
func ShowCommandHelp(ctx *Context, command string) {
|
||||||
|
// show the subcommand help for a command with subcommands
|
||||||
|
if command == "" {
|
||||||
|
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range ctx.App.Commands {
|
||||||
|
if c.HasName(command) {
|
||||||
|
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.App.CommandNotFound != nil {
|
||||||
|
ctx.App.CommandNotFound(ctx, command)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints help for the given subcommand
|
||||||
|
func ShowSubcommandHelp(c *Context) {
|
||||||
|
ShowCommandHelp(c, c.Command.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the version number of the App
|
||||||
|
func ShowVersion(c *Context) {
|
||||||
|
VersionPrinter(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printVersion(c *Context) {
|
||||||
|
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the lists of commands within a given context
|
||||||
|
func ShowCompletions(c *Context) {
|
||||||
|
a := c.App
|
||||||
|
if a != nil && a.BashComplete != nil {
|
||||||
|
a.BashComplete(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the custom completions for a given command
|
||||||
|
func ShowCommandCompletions(ctx *Context, command string) {
|
||||||
|
c := ctx.App.Command(command)
|
||||||
|
if c != nil && c.BashComplete != nil {
|
||||||
|
c.BashComplete(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||||
|
funcMap := template.FuncMap{
|
||||||
|
"join": strings.Join,
|
||||||
|
}
|
||||||
|
|
||||||
|
w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0)
|
||||||
|
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||||
|
err := t.Execute(w, data)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkVersion(c *Context) bool {
|
||||||
|
if c.GlobalBool("version") {
|
||||||
|
ShowVersion(c)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHelp(c *Context) bool {
|
||||||
|
if c.GlobalBool("h") || c.GlobalBool("help") {
|
||||||
|
ShowAppHelp(c)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCommandHelp(c *Context, name string) bool {
|
||||||
|
if c.Bool("h") || c.Bool("help") {
|
||||||
|
ShowCommandHelp(c, name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSubcommandHelp(c *Context) bool {
|
||||||
|
if c.GlobalBool("h") || c.GlobalBool("help") {
|
||||||
|
ShowSubcommandHelp(c)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCompletions(c *Context) bool {
|
||||||
|
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion {
|
||||||
|
ShowCompletions(c)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCommandCompletions(c *Context, name string) bool {
|
||||||
|
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
|
||||||
|
ShowCommandCompletions(c, name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
163
containerd/vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
163
containerd/vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright 2015 CoreOS, 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.
|
||||||
|
|
||||||
|
// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`
|
||||||
|
num = `0123456789`
|
||||||
|
alphanum = alpha + num
|
||||||
|
signalBuffer = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped
|
||||||
|
func needsEscape(i int, b byte) bool {
|
||||||
|
// Escape everything that is not a-z-A-Z-0-9
|
||||||
|
// Also escape 0-9 if it's the first character
|
||||||
|
return strings.IndexByte(alphanum, b) == -1 ||
|
||||||
|
(i == 0 && strings.IndexByte(num, b) != -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the
|
||||||
|
// rules that systemd uses for serializing special characters.
|
||||||
|
func PathBusEscape(path string) string {
|
||||||
|
// Special case the empty string
|
||||||
|
if len(path) == 0 {
|
||||||
|
return "_"
|
||||||
|
}
|
||||||
|
n := []byte{}
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
c := path[i]
|
||||||
|
if needsEscape(i, c) {
|
||||||
|
e := fmt.Sprintf("_%x", c)
|
||||||
|
n = append(n, []byte(e)...)
|
||||||
|
} else {
|
||||||
|
n = append(n, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn is a connection to systemd's dbus endpoint.
|
||||||
|
type Conn struct {
|
||||||
|
// sysconn/sysobj are only used to call dbus methods
|
||||||
|
sysconn *dbus.Conn
|
||||||
|
sysobj *dbus.Object
|
||||||
|
|
||||||
|
// sigconn/sigobj are only used to receive dbus signals
|
||||||
|
sigconn *dbus.Conn
|
||||||
|
sigobj *dbus.Object
|
||||||
|
|
||||||
|
jobListener struct {
|
||||||
|
jobs map[dbus.ObjectPath]chan<- string
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
subscriber struct {
|
||||||
|
updateCh chan<- *SubStateUpdate
|
||||||
|
errCh chan<- error
|
||||||
|
sync.Mutex
|
||||||
|
ignore map[dbus.ObjectPath]int64
|
||||||
|
cleanIgnore int64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New establishes a connection to the system bus and authenticates.
|
||||||
|
// Callers should call Close() when done with the connection.
|
||||||
|
func New() (*Conn, error) {
|
||||||
|
return newConnection(dbus.SystemBusPrivate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserConnection establishes a connection to the session bus and
|
||||||
|
// authenticates. This can be used to connect to systemd user instances.
|
||||||
|
// Callers should call Close() when done with the connection.
|
||||||
|
func NewUserConnection() (*Conn, error) {
|
||||||
|
return newConnection(dbus.SessionBusPrivate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes an established connection
|
||||||
|
func (c *Conn) Close() {
|
||||||
|
c.sysconn.Close()
|
||||||
|
c.sigconn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
|
||||||
|
sysconn, err := dbusConnection(createBus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sigconn, err := dbusConnection(createBus)
|
||||||
|
if err != nil {
|
||||||
|
sysconn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Conn{
|
||||||
|
sysconn: sysconn,
|
||||||
|
sysobj: systemdObject(sysconn),
|
||||||
|
sigconn: sigconn,
|
||||||
|
sigobj: systemdObject(sigconn),
|
||||||
|
}
|
||||||
|
|
||||||
|
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
|
||||||
|
c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string)
|
||||||
|
|
||||||
|
// Setup the listeners on jobs so that we can get completions
|
||||||
|
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
|
||||||
|
|
||||||
|
c.dispatch()
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbusConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
||||||
|
conn, err := createBus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only use EXTERNAL method, and hardcode the uid (not username)
|
||||||
|
// to avoid a username lookup (which requires a dynamically linked
|
||||||
|
// libc)
|
||||||
|
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
|
||||||
|
|
||||||
|
err = conn.Auth(methods)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.Hello()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemdObject(conn *dbus.Conn) *dbus.Object {
|
||||||
|
return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
||||||
|
}
|
410
containerd/vendor/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
410
containerd/vendor/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
// Copyright 2015 CoreOS, 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) jobComplete(signal *dbus.Signal) {
|
||||||
|
var id uint32
|
||||||
|
var job dbus.ObjectPath
|
||||||
|
var unit string
|
||||||
|
var result string
|
||||||
|
dbus.Store(signal.Body, &id, &job, &unit, &result)
|
||||||
|
c.jobListener.Lock()
|
||||||
|
out, ok := c.jobListener.jobs[job]
|
||||||
|
if ok {
|
||||||
|
out <- result
|
||||||
|
delete(c.jobListener.jobs, job)
|
||||||
|
}
|
||||||
|
c.jobListener.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) {
|
||||||
|
if ch != nil {
|
||||||
|
c.jobListener.Lock()
|
||||||
|
defer c.jobListener.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
var p dbus.ObjectPath
|
||||||
|
err := c.sysobj.Call(job, 0, args...).Store(&p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch != nil {
|
||||||
|
c.jobListener.jobs[p] = ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore error since 0 is fine if conversion fails
|
||||||
|
jobID, _ := strconv.Atoi(path.Base(string(p)))
|
||||||
|
|
||||||
|
return jobID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartUnit enqueues a start job and depending jobs, if any (unless otherwise
|
||||||
|
// specified by the mode string).
|
||||||
|
//
|
||||||
|
// Takes the unit to activate, plus a mode string. The mode needs to be one of
|
||||||
|
// replace, fail, isolate, ignore-dependencies, ignore-requirements. If
|
||||||
|
// "replace" the call will start the unit and its dependencies, possibly
|
||||||
|
// replacing already queued jobs that conflict with this. If "fail" the call
|
||||||
|
// will start the unit and its dependencies, but will fail if this would change
|
||||||
|
// an already queued job. If "isolate" the call will start the unit in question
|
||||||
|
// and terminate all units that aren't dependencies of it. If
|
||||||
|
// "ignore-dependencies" it will start a unit but ignore all its dependencies.
|
||||||
|
// If "ignore-requirements" it will start a unit but only ignore the
|
||||||
|
// requirement dependencies. It is not recommended to make use of the latter
|
||||||
|
// two options.
|
||||||
|
//
|
||||||
|
// If the provided channel is non-nil, a result string will be sent to it upon
|
||||||
|
// job completion: one of done, canceled, timeout, failed, dependency, skipped.
|
||||||
|
// done indicates successful execution of a job. canceled indicates that a job
|
||||||
|
// has been canceled before it finished execution. timeout indicates that the
|
||||||
|
// job timeout was reached. failed indicates that the job failed. dependency
|
||||||
|
// indicates that a job this job has been depending on failed and the job hence
|
||||||
|
// has been removed too. skipped indicates that a job was skipped because it
|
||||||
|
// didn't apply to the units current state.
|
||||||
|
//
|
||||||
|
// If no error occurs, the ID of the underlying systemd job will be returned. There
|
||||||
|
// does exist the possibility for no error to be returned, but for the returned job
|
||||||
|
// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint
|
||||||
|
// should not be considered authoritative.
|
||||||
|
//
|
||||||
|
// If an error does occur, it will be returned to the user alongside a job ID of 0.
|
||||||
|
func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopUnit is similar to StartUnit but stops the specified unit rather
|
||||||
|
// than starting it.
|
||||||
|
func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
|
||||||
|
func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartUnit restarts a service. If a service is restarted that isn't
|
||||||
|
// running it will be started.
|
||||||
|
func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRestartUnit is like RestartUnit, except that a service that isn't running
|
||||||
|
// is not affected by the restart.
|
||||||
|
func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadOrRestart attempts a reload if the unit supports it and use a restart
|
||||||
|
// otherwise.
|
||||||
|
func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
|
||||||
|
// flavored restart otherwise.
|
||||||
|
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartTransientUnit() may be used to create and start a transient unit, which
|
||||||
|
// will be released as soon as it is not running or referenced anymore or the
|
||||||
|
// system is rebooted. name is the unit name including suffix, and must be
|
||||||
|
// unique. mode is the same as in StartUnit(), properties contains properties
|
||||||
|
// of the unit.
|
||||||
|
func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
|
||||||
|
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
|
||||||
|
// processes are killed.
|
||||||
|
func (c *Conn) KillUnit(name string, signal int32) {
|
||||||
|
c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetFailedUnit resets the "failed" state of a specific unit.
|
||||||
|
func (c *Conn) ResetFailedUnit(name string) error {
|
||||||
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
|
||||||
|
func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
var props map[string]dbus.Variant
|
||||||
|
|
||||||
|
path := unitPath(unit)
|
||||||
|
if !path.IsValid() {
|
||||||
|
return nil, errors.New("invalid unit name: " + unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]interface{}, len(props))
|
||||||
|
for k, v := range props {
|
||||||
|
out[k] = v.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
|
||||||
|
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
|
||||||
|
var err error
|
||||||
|
var prop dbus.Variant
|
||||||
|
|
||||||
|
path := unitPath(unit)
|
||||||
|
if !path.IsValid() {
|
||||||
|
return nil, errors.New("invalid unit name: " + unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Property{Name: propertyName, Value: prop}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
|
||||||
|
// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
|
||||||
|
// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
|
||||||
|
func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnitProperties() may be used to modify certain unit properties at runtime.
|
||||||
|
// Not all properties may be changed at runtime, but many resource management
|
||||||
|
// settings (primarily those in systemd.cgroup(5)) may. The changes are applied
|
||||||
|
// instantly, and stored on disk for future boots, unless runtime is true, in which
|
||||||
|
// case the settings only apply until the next reboot. name is the name of the unit
|
||||||
|
// to modify. properties are the settings to set, encoded as an array of property
|
||||||
|
// name and value pairs.
|
||||||
|
func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
|
||||||
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUnits returns an array with all currently loaded units. Note that
|
||||||
|
// units may be known by multiple names at the same time, and hence there might
|
||||||
|
// be more unit names loaded than actual units behind them.
|
||||||
|
func (c *Conn) ListUnits() ([]UnitStatus, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
status := make([]UnitStatus, len(result))
|
||||||
|
statusInterface := make([]interface{}, len(status))
|
||||||
|
for i := range status {
|
||||||
|
statusInterface[i] = &status[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, statusInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnitStatus struct {
|
||||||
|
Name string // The primary unit name as string
|
||||||
|
Description string // The human readable description string
|
||||||
|
LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
|
||||||
|
ActiveState string // The active state (i.e. whether the unit is currently started or not)
|
||||||
|
SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
|
||||||
|
Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
|
||||||
|
Path dbus.ObjectPath // The unit object path
|
||||||
|
JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
|
||||||
|
JobType string // The job type as string
|
||||||
|
JobPath dbus.ObjectPath // The job object path
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinkUnitFileChange EnableUnitFileChange
|
||||||
|
|
||||||
|
// LinkUnitFiles() links unit files (that are located outside of the
|
||||||
|
// usual unit search paths) into the unit search path.
|
||||||
|
//
|
||||||
|
// It takes a list of absolute paths to unit files to link and two
|
||||||
|
// booleans. The first boolean controls whether the unit shall be
|
||||||
|
// enabled for runtime only (true, /run), or persistently (false,
|
||||||
|
// /etc).
|
||||||
|
// The second controls whether symlinks pointing to other units shall
|
||||||
|
// be replaced if necessary.
|
||||||
|
//
|
||||||
|
// This call returns a list of the changes made. The list consists of
|
||||||
|
// structures with three strings: the type of the change (one of symlink
|
||||||
|
// or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]LinkUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableUnitFiles() may be used to enable one or more units in the system (by
|
||||||
|
// creating symlinks to them in /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to enable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and two booleans: the first controls whether the unit shall
|
||||||
|
// be enabled for runtime only (true, /run), or persistently (false, /etc).
|
||||||
|
// The second one controls whether symlinks pointing to other units shall
|
||||||
|
// be replaced if necessary.
|
||||||
|
//
|
||||||
|
// This call returns one boolean and an array with the changes made. The
|
||||||
|
// boolean signals whether the unit files contained any enablement
|
||||||
|
// information (i.e. an [Install]) section. The changes list consists of
|
||||||
|
// structures with three strings: the type of the change (one of symlink
|
||||||
|
// or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
|
||||||
|
var carries_install_info bool
|
||||||
|
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]EnableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return carries_install_info, changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableUnitFiles() may be used to disable one or more units in the system (by
|
||||||
|
// removing symlinks to them from /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to disable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and one boolean: whether the unit was enabled for runtime
|
||||||
|
// only (true, /run), or persistently (false, /etc).
|
||||||
|
//
|
||||||
|
// This call returns an array with the changes made. The changes list
|
||||||
|
// consists of structures with three strings: the type of the change (one of
|
||||||
|
// symlink or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]DisableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload instructs systemd to scan for and reload unit files. This is
|
||||||
|
// equivalent to a 'systemctl daemon-reload'.
|
||||||
|
func (c *Conn) Reload() error {
|
||||||
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
func unitPath(name string) dbus.ObjectPath {
|
||||||
|
return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name))
|
||||||
|
}
|
218
containerd/vendor/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
218
containerd/vendor/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
// Copyright 2015 CoreOS, 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// From the systemd docs:
|
||||||
|
//
|
||||||
|
// The properties array of StartTransientUnit() may take many of the settings
|
||||||
|
// that may also be configured in unit files. Not all parameters are currently
|
||||||
|
// accepted though, but we plan to cover more properties with future release.
|
||||||
|
// Currently you may set the Description, Slice and all dependency types of
|
||||||
|
// units, as well as RemainAfterExit, ExecStart for service units,
|
||||||
|
// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares,
|
||||||
|
// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth,
|
||||||
|
// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit,
|
||||||
|
// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map
|
||||||
|
// directly to their counterparts in unit files and as normal D-Bus object
|
||||||
|
// properties. The exception here is the PIDs field of scope units which is
|
||||||
|
// used for construction of the scope only and specifies the initial PIDs to
|
||||||
|
// add to the scope object.
|
||||||
|
|
||||||
|
type Property struct {
|
||||||
|
Name string
|
||||||
|
Value dbus.Variant
|
||||||
|
}
|
||||||
|
|
||||||
|
type PropertyCollection struct {
|
||||||
|
Name string
|
||||||
|
Properties []Property
|
||||||
|
}
|
||||||
|
|
||||||
|
type execStart struct {
|
||||||
|
Path string // the binary path to execute
|
||||||
|
Args []string // an array with all arguments to pass to the executed command, starting with argument 0
|
||||||
|
UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropExecStart sets the ExecStart service property. The first argument is a
|
||||||
|
// slice with the binary path to execute followed by the arguments to pass to
|
||||||
|
// the executed command. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
|
||||||
|
func PropExecStart(command []string, uncleanIsFailure bool) Property {
|
||||||
|
execStarts := []execStart{
|
||||||
|
execStart{
|
||||||
|
Path: command[0],
|
||||||
|
Args: command,
|
||||||
|
UncleanIsFailure: uncleanIsFailure,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return Property{
|
||||||
|
Name: "ExecStart",
|
||||||
|
Value: dbus.MakeVariant(execStarts),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRemainAfterExit sets the RemainAfterExit service property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
|
||||||
|
func PropRemainAfterExit(b bool) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "RemainAfterExit",
|
||||||
|
Value: dbus.MakeVariant(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropDescription sets the Description unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
|
||||||
|
func PropDescription(desc string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Description",
|
||||||
|
Value: dbus.MakeVariant(desc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func propDependency(name string, units []string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: name,
|
||||||
|
Value: dbus.MakeVariant(units),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequires sets the Requires unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires=
|
||||||
|
func PropRequires(units ...string) Property {
|
||||||
|
return propDependency("Requires", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiresOverridable sets the RequiresOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable=
|
||||||
|
func PropRequiresOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequiresOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequisite sets the Requisite unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite=
|
||||||
|
func PropRequisite(units ...string) Property {
|
||||||
|
return propDependency("Requisite", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable=
|
||||||
|
func PropRequisiteOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequisiteOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropWants sets the Wants unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants=
|
||||||
|
func PropWants(units ...string) Property {
|
||||||
|
return propDependency("Wants", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBindsTo sets the BindsTo unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=
|
||||||
|
func PropBindsTo(units ...string) Property {
|
||||||
|
return propDependency("BindsTo", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiredBy sets the RequiredBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy=
|
||||||
|
func PropRequiredBy(units ...string) Property {
|
||||||
|
return propDependency("RequiredBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable=
|
||||||
|
func PropRequiredByOverridable(units ...string) Property {
|
||||||
|
return propDependency("RequiredByOverridable", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropWantedBy sets the WantedBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
|
||||||
|
func PropWantedBy(units ...string) Property {
|
||||||
|
return propDependency("WantedBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBoundBy sets the BoundBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy=
|
||||||
|
func PropBoundBy(units ...string) Property {
|
||||||
|
return propDependency("BoundBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropConflicts sets the Conflicts unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
|
||||||
|
func PropConflicts(units ...string) Property {
|
||||||
|
return propDependency("Conflicts", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropConflictedBy sets the ConflictedBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy=
|
||||||
|
func PropConflictedBy(units ...string) Property {
|
||||||
|
return propDependency("ConflictedBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropBefore sets the Before unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
|
||||||
|
func PropBefore(units ...string) Property {
|
||||||
|
return propDependency("Before", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropAfter sets the After unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After=
|
||||||
|
func PropAfter(units ...string) Property {
|
||||||
|
return propDependency("After", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropOnFailure sets the OnFailure unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure=
|
||||||
|
func PropOnFailure(units ...string) Property {
|
||||||
|
return propDependency("OnFailure", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropTriggers sets the Triggers unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers=
|
||||||
|
func PropTriggers(units ...string) Property {
|
||||||
|
return propDependency("Triggers", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropTriggeredBy sets the TriggeredBy unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy=
|
||||||
|
func PropTriggeredBy(units ...string) Property {
|
||||||
|
return propDependency("TriggeredBy", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo=
|
||||||
|
func PropPropagatesReloadTo(units ...string) Property {
|
||||||
|
return propDependency("PropagatesReloadTo", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=
|
||||||
|
func PropRequiresMountsFor(units ...string) Property {
|
||||||
|
return propDependency("RequiresMountsFor", units)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropSlice sets the Slice unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice=
|
||||||
|
func PropSlice(slice string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Slice",
|
||||||
|
Value: dbus.MakeVariant(slice),
|
||||||
|
}
|
||||||
|
}
|
47
containerd/vendor/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
47
containerd/vendor/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2015 CoreOS, 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
type set struct {
|
||||||
|
data map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Add(value string) {
|
||||||
|
s.data[value] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Remove(value string) {
|
||||||
|
delete(s.data, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Contains(value string) (exists bool) {
|
||||||
|
_, exists = s.data[value]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Length() int {
|
||||||
|
return len(s.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *set) Values() (values []string) {
|
||||||
|
for val, _ := range s.data {
|
||||||
|
values = append(values, val)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSet() *set {
|
||||||
|
return &set{make(map[string]bool)}
|
||||||
|
}
|
250
containerd/vendor/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
250
containerd/vendor/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
// Copyright 2015 CoreOS, 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cleanIgnoreInterval = int64(10 * time.Second)
|
||||||
|
ignoreInterval = int64(30 * time.Millisecond)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Subscribe sets up this connection to subscribe to all systemd dbus events.
|
||||||
|
// This is required before calling SubscribeUnits. When the connection closes
|
||||||
|
// systemd will automatically stop sending signals so there is no need to
|
||||||
|
// explicitly call Unsubscribe().
|
||||||
|
func (c *Conn) Subscribe() error {
|
||||||
|
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
|
||||||
|
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||||
|
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
|
||||||
|
|
||||||
|
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsubscribe this connection from systemd dbus events.
|
||||||
|
func (c *Conn) Unsubscribe() error {
|
||||||
|
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) dispatch() {
|
||||||
|
ch := make(chan *dbus.Signal, signalBuffer)
|
||||||
|
|
||||||
|
c.sigconn.Signal(ch)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
signal, ok := <-ch
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
|
||||||
|
c.jobComplete(signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.subscriber.updateCh == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var unitPath dbus.ObjectPath
|
||||||
|
switch signal.Name {
|
||||||
|
case "org.freedesktop.systemd1.Manager.JobRemoved":
|
||||||
|
unitName := signal.Body[2].(string)
|
||||||
|
c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
|
||||||
|
case "org.freedesktop.systemd1.Manager.UnitNew":
|
||||||
|
unitPath = signal.Body[1].(dbus.ObjectPath)
|
||||||
|
case "org.freedesktop.DBus.Properties.PropertiesChanged":
|
||||||
|
if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
|
||||||
|
unitPath = signal.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unitPath == dbus.ObjectPath("") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.sendSubStateUpdate(unitPath)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns two unbuffered channels which will receive all changed units every
|
||||||
|
// interval. Deleted units are sent as nil.
|
||||||
|
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
|
||||||
|
// size of the channels, the comparison function for detecting changes and a filter
|
||||||
|
// function for cutting down on the noise that your channel receives.
|
||||||
|
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
old := make(map[string]*UnitStatus)
|
||||||
|
statusChan := make(chan map[string]*UnitStatus, buffer)
|
||||||
|
errChan := make(chan error, buffer)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
timerChan := time.After(interval)
|
||||||
|
|
||||||
|
units, err := c.ListUnits()
|
||||||
|
if err == nil {
|
||||||
|
cur := make(map[string]*UnitStatus)
|
||||||
|
for i := range units {
|
||||||
|
if filterUnit != nil && filterUnit(units[i].Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cur[units[i].Name] = &units[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all new or changed units
|
||||||
|
changed := make(map[string]*UnitStatus)
|
||||||
|
for n, u := range cur {
|
||||||
|
if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
|
||||||
|
changed[n] = u
|
||||||
|
}
|
||||||
|
delete(old, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all deleted units
|
||||||
|
for oldN := range old {
|
||||||
|
changed[oldN] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
old = cur
|
||||||
|
|
||||||
|
if len(changed) != 0 {
|
||||||
|
statusChan <- changed
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
<-timerChan
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return statusChan, errChan
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubStateUpdate struct {
|
||||||
|
UnitName string
|
||||||
|
SubState string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
|
||||||
|
// Although this writes to updateCh on every state change, the reported state
|
||||||
|
// may be more recent than the change that generated it (due to an unavoidable
|
||||||
|
// race in the systemd dbus interface). That is, this method provides a good
|
||||||
|
// way to keep a current view of all units' states, but is not guaranteed to
|
||||||
|
// show every state transition they go through. Furthermore, state changes
|
||||||
|
// will only be written to the channel with non-blocking writes. If updateCh
|
||||||
|
// is full, it attempts to write an error to errCh; if errCh is full, the error
|
||||||
|
// passes silently.
|
||||||
|
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
|
||||||
|
c.subscriber.Lock()
|
||||||
|
defer c.subscriber.Unlock()
|
||||||
|
c.subscriber.updateCh = updateCh
|
||||||
|
c.subscriber.errCh = errCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
|
||||||
|
c.subscriber.Lock()
|
||||||
|
defer c.subscriber.Unlock()
|
||||||
|
|
||||||
|
if c.shouldIgnore(path) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := c.GetUnitProperties(string(path))
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case c.subscriber.errCh <- err:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := info["Id"].(string)
|
||||||
|
substate := info["SubState"].(string)
|
||||||
|
|
||||||
|
update := &SubStateUpdate{name, substate}
|
||||||
|
select {
|
||||||
|
case c.subscriber.updateCh <- update:
|
||||||
|
default:
|
||||||
|
select {
|
||||||
|
case c.subscriber.errCh <- errors.New("update channel full!"):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.updateIgnore(path, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ignore functions work around a wart in the systemd dbus interface.
|
||||||
|
// Requesting the properties of an unloaded unit will cause systemd to send a
|
||||||
|
// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's
|
||||||
|
// properties on UnitNew (as that's the only indication of a new unit coming up
|
||||||
|
// for the first time), we would enter an infinite loop if we did not attempt
|
||||||
|
// to detect and ignore these spurious signals. The signal themselves are
|
||||||
|
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
|
||||||
|
// unloaded unit's signals for a short time after requesting its properties.
|
||||||
|
// This means that we will miss e.g. a transient unit being restarted
|
||||||
|
// *immediately* upon failure and also a transient unit being started
|
||||||
|
// immediately after requesting its status (with systemctl status, for example,
|
||||||
|
// because this causes a UnitNew signal to be sent which then causes us to fetch
|
||||||
|
// the properties).
|
||||||
|
|
||||||
|
func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
|
||||||
|
t, ok := c.subscriber.ignore[path]
|
||||||
|
return ok && t >= time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
|
||||||
|
c.cleanIgnore()
|
||||||
|
|
||||||
|
// unit is unloaded - it will trigger bad systemd dbus behavior
|
||||||
|
if info["LoadState"].(string) == "not-found" {
|
||||||
|
c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// without this, ignore would grow unboundedly over time
|
||||||
|
func (c *Conn) cleanIgnore() {
|
||||||
|
now := time.Now().UnixNano()
|
||||||
|
if c.subscriber.cleanIgnore < now {
|
||||||
|
c.subscriber.cleanIgnore = now + cleanIgnoreInterval
|
||||||
|
|
||||||
|
for p, t := range c.subscriber.ignore {
|
||||||
|
if t < now {
|
||||||
|
delete(c.subscriber.ignore, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
containerd/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
57
containerd/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2015 CoreOS, 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.
|
||||||
|
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubscriptionSet returns a subscription set which is like conn.Subscribe but
|
||||||
|
// can filter to only return events for a set of units.
|
||||||
|
type SubscriptionSet struct {
|
||||||
|
*set
|
||||||
|
conn *Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SubscriptionSet) filter(unit string) bool {
|
||||||
|
return !s.Contains(unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe starts listening for dbus events for all of the units in the set.
|
||||||
|
// Returns channels identical to conn.SubscribeUnits.
|
||||||
|
func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) {
|
||||||
|
// TODO: Make fully evented by using systemd 209 with properties changed values
|
||||||
|
return s.conn.SubscribeUnitsCustom(time.Second, 0,
|
||||||
|
mismatchUnitStatus,
|
||||||
|
func(unit string) bool { return s.filter(unit) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubscriptionSet returns a new subscription set.
|
||||||
|
func (conn *Conn) NewSubscriptionSet() *SubscriptionSet {
|
||||||
|
return &SubscriptionSet{newSet(), conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mismatchUnitStatus returns true if the provided UnitStatus objects
|
||||||
|
// are not equivalent. false is returned if the objects are equivalent.
|
||||||
|
// Only the Name, Description and state-related fields are used in
|
||||||
|
// the comparison.
|
||||||
|
func mismatchUnitStatus(u1, u2 *UnitStatus) bool {
|
||||||
|
return u1.Name != u2.Name ||
|
||||||
|
u1.Description != u2.Description ||
|
||||||
|
u1.LoadState != u2.LoadState ||
|
||||||
|
u1.ActiveState != u2.ActiveState ||
|
||||||
|
u1.SubState != u2.SubState
|
||||||
|
}
|
33
containerd/vendor/github.com/coreos/go-systemd/util/util.go
generated
vendored
Normal file
33
containerd/vendor/github.com/coreos/go-systemd/util/util.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2015 CoreOS, 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.
|
||||||
|
|
||||||
|
// Package util contains utility functions related to systemd that applications
|
||||||
|
// can use to check things like whether systemd is running.
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsRunningSystemd checks whether the host was booted with systemd as its init
|
||||||
|
// system. This functions similar to systemd's `sd_booted(3)`: internally, it
|
||||||
|
// checks whether /run/systemd/system/ exists and is a directory.
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
|
||||||
|
func IsRunningSystemd() bool {
|
||||||
|
fi, err := os.Lstat("/run/systemd/system")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fi.IsDir()
|
||||||
|
}
|
2
containerd/vendor/github.com/docker/containerd/.gitignore
generated
vendored
Normal file
2
containerd/vendor/github.com/docker/containerd/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
containerd/containerd
|
||||||
|
bin/
|
79
containerd/vendor/github.com/docker/containerd/README.md
generated
vendored
Normal file
79
containerd/vendor/github.com/docker/containerd/README.md
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# containerd
|
||||||
|
|
||||||
|
another container runtime
|
||||||
|
|
||||||
|
Start a container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -XPOST localhost:8888/containers/redis -d '{"bundlePath": "/containers/redis"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a process:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -XPUT localhost:8888/containers/redis/process -d@process.json | json_pp
|
||||||
|
{
|
||||||
|
"pid" : 25671,
|
||||||
|
"user" : {
|
||||||
|
"gid" : 0,
|
||||||
|
"uid" : 0
|
||||||
|
},
|
||||||
|
"args" : [
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"sleep 10"
|
||||||
|
],
|
||||||
|
"env" : [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Get containers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s localhost:8888/containers | json_pp
|
||||||
|
{
|
||||||
|
"containers" : [
|
||||||
|
{
|
||||||
|
"processes" : [
|
||||||
|
{
|
||||||
|
"args" : [
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"sleep 60"
|
||||||
|
],
|
||||||
|
"user" : {
|
||||||
|
"gid" : 0,
|
||||||
|
"uid" : 0
|
||||||
|
},
|
||||||
|
"pid" : 25743,
|
||||||
|
"env" : [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id" : "redis",
|
||||||
|
"state" : {
|
||||||
|
"status" : "running"
|
||||||
|
},
|
||||||
|
"bundlePath" : "/containers/redis"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Other stuff:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# pause and resume a container
|
||||||
|
curl -XPATCH localhost:8888/containers/redis -d '{"status": "paused"}'
|
||||||
|
curl -XPATCH localhost:8888/containers/redis -d '{"status": "running"}'
|
||||||
|
|
||||||
|
# send signal to a container's specific process
|
||||||
|
curl -XPOST localhost:8888/containers/redis/process/18306 -d '{"signal": 9}'
|
||||||
|
```
|
230
containerd/vendor/github.com/docker/containerd/api/v1/server.go
generated
vendored
Normal file
230
containerd/vendor/github.com/docker/containerd/api/v1/server.go
generated
vendored
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/containerd"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/opencontainers/specs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServer(supervisor *containerd.Supervisor) http.Handler {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := &server{
|
||||||
|
supervisor: supervisor,
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
// TODO: add container stats
|
||||||
|
// TODO: add container checkpoint
|
||||||
|
// TODO: add container restore
|
||||||
|
// TODO: set prctl child subreaper
|
||||||
|
r.HandleFunc("/containers/{id:.*}/process/{pid:.*}", s.signalPid).Methods("POST")
|
||||||
|
r.HandleFunc("/containers/{id:.*}/process", s.addProcess).Methods("PUT")
|
||||||
|
r.HandleFunc("/containers/{id:.*}", s.createContainer).Methods("POST")
|
||||||
|
r.HandleFunc("/containers/{id:.*}", s.updateContainer).Methods("PATCH")
|
||||||
|
r.HandleFunc("/event", s.event).Methods("POST")
|
||||||
|
r.HandleFunc("/containers", s.containers).Methods("GET")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
r *mux.Router
|
||||||
|
supervisor *containerd.Supervisor
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement correct shutdown
|
||||||
|
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.r.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) updateContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
var state ContainerState
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&state); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e := containerd.NewEvent(containerd.UpdateContainerEventType)
|
||||||
|
e.ID = id
|
||||||
|
e.State = &containerd.State{
|
||||||
|
Status: containerd.Status(string(state.Status)),
|
||||||
|
}
|
||||||
|
s.supervisor.SendEvent(e)
|
||||||
|
if err := <-e.Err; err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) event(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var e containerd.Event
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&e); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.Err = make(chan error, 1)
|
||||||
|
s.supervisor.SendEvent(&e)
|
||||||
|
if err := <-e.Err; err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e.Containers != nil && len(e.Containers) > 0 {
|
||||||
|
if err := writeContainers(w, &e); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) addProcess(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
var process specs.Process
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&process); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e := containerd.NewEvent(containerd.AddProcessEventType)
|
||||||
|
e.ID = id
|
||||||
|
e.Process = &process
|
||||||
|
s.supervisor.SendEvent(e)
|
||||||
|
if err := <-e.Err; err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := Process{
|
||||||
|
Pid: e.Pid,
|
||||||
|
Terminal: process.Terminal,
|
||||||
|
Args: process.Args,
|
||||||
|
Env: process.Env,
|
||||||
|
Cwd: process.Cwd,
|
||||||
|
}
|
||||||
|
p.User.UID = process.User.UID
|
||||||
|
p.User.GID = process.User.GID
|
||||||
|
p.User.AdditionalGids = process.User.AdditionalGids
|
||||||
|
if err := json.NewEncoder(w).Encode(p); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) signalPid(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var (
|
||||||
|
vars = mux.Vars(r)
|
||||||
|
id = vars["id"]
|
||||||
|
spid = vars["pid"]
|
||||||
|
)
|
||||||
|
pid, err := strconv.Atoi(spid)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var signal Signal
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&signal); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e := containerd.NewEvent(containerd.SignalEventType)
|
||||||
|
e.ID = id
|
||||||
|
e.Pid = pid
|
||||||
|
e.Signal = syscall.Signal(signal.Signal)
|
||||||
|
s.supervisor.SendEvent(e)
|
||||||
|
if err := <-e.Err; err != nil {
|
||||||
|
status := http.StatusInternalServerError
|
||||||
|
if err == containerd.ErrContainerNotFound {
|
||||||
|
status = http.StatusNotFound
|
||||||
|
}
|
||||||
|
http.Error(w, err.Error(), status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) containers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
e := containerd.NewEvent(containerd.GetContainerEventType)
|
||||||
|
s.supervisor.SendEvent(e)
|
||||||
|
if err := <-e.Err; err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := writeContainers(w, e); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeContainers(w http.ResponseWriter, e *containerd.Event) error {
|
||||||
|
var state State
|
||||||
|
state.Containers = []Container{}
|
||||||
|
for _, c := range e.Containers {
|
||||||
|
processes, err := c.Processes()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
"container": c.ID(),
|
||||||
|
}).Error("get processes for container")
|
||||||
|
}
|
||||||
|
var pids []Process
|
||||||
|
for _, p := range processes {
|
||||||
|
proc := createProcess(p)
|
||||||
|
pids = append(pids, proc)
|
||||||
|
}
|
||||||
|
state.Containers = append(state.Containers, Container{
|
||||||
|
ID: c.ID(),
|
||||||
|
BundlePath: c.Path(),
|
||||||
|
Processes: pids,
|
||||||
|
State: &ContainerState{
|
||||||
|
Status: Status(c.State().Status),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return json.NewEncoder(w).Encode(&state)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createProcess(in containerd.Process) Process {
|
||||||
|
pid, err := in.Pid()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithField("error", err).Error("get process pid")
|
||||||
|
}
|
||||||
|
process := in.Spec()
|
||||||
|
p := Process{
|
||||||
|
Pid: pid,
|
||||||
|
Terminal: process.Terminal,
|
||||||
|
Args: process.Args,
|
||||||
|
Env: process.Env,
|
||||||
|
Cwd: process.Cwd,
|
||||||
|
}
|
||||||
|
p.User.UID = process.User.UID
|
||||||
|
p.User.GID = process.User.GID
|
||||||
|
p.User.AdditionalGids = process.User.AdditionalGids
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) createContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
var c Container
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.BundlePath == "" {
|
||||||
|
http.Error(w, "empty bundle path", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e := containerd.NewEvent(containerd.StartContainerEventType)
|
||||||
|
e.ID = id
|
||||||
|
e.BundlePath = c.BundlePath
|
||||||
|
s.supervisor.SendEvent(e)
|
||||||
|
if err := <-e.Err; err != nil {
|
||||||
|
code := http.StatusInternalServerError
|
||||||
|
if err == containerd.ErrBundleNotFound {
|
||||||
|
code = http.StatusNotFound
|
||||||
|
}
|
||||||
|
http.Error(w, err.Error(), code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
}
|
42
containerd/vendor/github.com/docker/containerd/api/v1/types.go
generated
vendored
Normal file
42
containerd/vendor/github.com/docker/containerd/api/v1/types.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Containers []Container `json:"containers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Paused Status = "paused"
|
||||||
|
Running Status = "running"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContainerState struct {
|
||||||
|
Status Status `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
BundlePath string `json:"bundlePath,omitempty"`
|
||||||
|
Processes []Process `json:"processes,omitempty"`
|
||||||
|
State *ContainerState `json:"state,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
UID uint32 `json:"uid"`
|
||||||
|
GID uint32 `json:"gid"`
|
||||||
|
AdditionalGids []uint32 `json:"additionalGids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
Terminal bool `json:"terminal,omitempty"`
|
||||||
|
User User `json:"user,omitempty"`
|
||||||
|
Args []string `json:"args,omitempty"`
|
||||||
|
Env []string `json:"env,omitempty"`
|
||||||
|
Cwd string `json:"cwd,omitempty"`
|
||||||
|
Pid int `json:"pid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Signal struct {
|
||||||
|
Signal int `json:"signal"`
|
||||||
|
}
|
37
containerd/vendor/github.com/docker/containerd/container.go
generated
vendored
Normal file
37
containerd/vendor/github.com/docker/containerd/container.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/opencontainers/specs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Process interface {
|
||||||
|
Pid() (int, error)
|
||||||
|
Spec() specs.Process
|
||||||
|
Signal(os.Signal) error
|
||||||
|
}
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Paused Status = "paused"
|
||||||
|
Running Status = "running"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Status Status `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Container interface {
|
||||||
|
ID() string
|
||||||
|
Start() error
|
||||||
|
Path() string
|
||||||
|
Pid() (int, error)
|
||||||
|
SetExited(status int)
|
||||||
|
Delete() error
|
||||||
|
Processes() ([]Process, error)
|
||||||
|
RemoveProcess(pid int) error
|
||||||
|
State() State
|
||||||
|
Resume() error
|
||||||
|
Pause() error
|
||||||
|
}
|
6
containerd/vendor/github.com/docker/containerd/containerd/Makefile
generated
vendored
Normal file
6
containerd/vendor/github.com/docker/containerd/containerd/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
all:
|
||||||
|
godep go build -tags libcontainer
|
||||||
|
|
||||||
|
|
109
containerd/vendor/github.com/docker/containerd/containerd/daemon.go
generated
vendored
Normal file
109
containerd/vendor/github.com/docker/containerd/containerd/daemon.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/docker/containerd"
|
||||||
|
"github.com/docker/containerd/api/v1"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
"github.com/rcrowley/go-metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DaemonCommand = cli.Command{
|
||||||
|
Name: "daemon",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "state-dir",
|
||||||
|
Value: "/run/containerd",
|
||||||
|
Usage: "runtime state directory",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "buffer-size",
|
||||||
|
Value: 2048,
|
||||||
|
Usage: "set the channel buffer size for events and signals",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
if context.GlobalBool("debug") {
|
||||||
|
l := log.New(os.Stdout, "[containerd] ", log.LstdFlags)
|
||||||
|
goRoutineCounter := metrics.NewGauge()
|
||||||
|
metrics.DefaultRegistry.Register("goroutines", goRoutineCounter)
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(30 * time.Second) {
|
||||||
|
goRoutineCounter.Update(int64(runtime.NumGoroutine()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go metrics.Log(metrics.DefaultRegistry, 60*time.Second, l)
|
||||||
|
}
|
||||||
|
if err := daemon(context.String("state-dir"), 10, context.Int("buffer-size")); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func daemon(stateDir string, concurrency, bufferSize int) error {
|
||||||
|
supervisor, err := containerd.NewSupervisor(stateDir, concurrency)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
events := make(chan *containerd.Event, bufferSize)
|
||||||
|
// start the signal handler in the background.
|
||||||
|
go startSignalHandler(supervisor, bufferSize)
|
||||||
|
if err := supervisor.Start(events); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
server := v1.NewServer(supervisor)
|
||||||
|
return http.ListenAndServe("localhost:8888", server)
|
||||||
|
}
|
||||||
|
|
||||||
|
func startSignalHandler(supervisor *containerd.Supervisor, bufferSize int) {
|
||||||
|
logrus.Debug("containerd: starting signal handler")
|
||||||
|
signals := make(chan os.Signal, bufferSize)
|
||||||
|
signal.Notify(signals)
|
||||||
|
for s := range signals {
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP:
|
||||||
|
supervisor.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
case syscall.SIGCHLD:
|
||||||
|
exits, err := reap()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithField("error", err).Error("containerd: reaping child processes")
|
||||||
|
}
|
||||||
|
for _, e := range exits {
|
||||||
|
supervisor.SendEvent(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reap() (exits []*containerd.Event, err error) {
|
||||||
|
var (
|
||||||
|
ws syscall.WaitStatus
|
||||||
|
rus syscall.Rusage
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
pid, err := syscall.Wait4(-1, &ws, syscall.WNOHANG, &rus)
|
||||||
|
if err != nil {
|
||||||
|
if err == syscall.ECHILD {
|
||||||
|
return exits, nil
|
||||||
|
}
|
||||||
|
return exits, err
|
||||||
|
}
|
||||||
|
if pid <= 0 {
|
||||||
|
return exits, nil
|
||||||
|
}
|
||||||
|
e := containerd.NewEvent(containerd.ExitEventType)
|
||||||
|
e.Pid = pid
|
||||||
|
e.Status = utils.ExitStatus(ws)
|
||||||
|
exits = append(exits, e)
|
||||||
|
}
|
||||||
|
}
|
85
containerd/vendor/github.com/docker/containerd/containerd/journal.go
generated
vendored
Normal file
85
containerd/vendor/github.com/docker/containerd/containerd/journal.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/docker/containerd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var JournalCommand = cli.Command{
|
||||||
|
Name: "journal",
|
||||||
|
Usage: "interact with the containerd journal",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
JournalReplyCommand,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var JournalReplyCommand = cli.Command{
|
||||||
|
Name: "replay",
|
||||||
|
Usage: "replay a journal to get containerd's state syncronized after a crash",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "addr",
|
||||||
|
Value: "localhost:8888",
|
||||||
|
Usage: "address of the containerd daemon",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
if err := replay(context.Args().First(), context.String("addr")); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func replay(path, addr string) error {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
dec := json.NewDecoder(f)
|
||||||
|
var events []*containerd.Event
|
||||||
|
type entry struct {
|
||||||
|
Event *containerd.Event `json:"event"`
|
||||||
|
}
|
||||||
|
for dec.More() {
|
||||||
|
var e entry
|
||||||
|
if err := dec.Decode(&e); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
events = append(events, e.Event)
|
||||||
|
}
|
||||||
|
c := &http.Client{}
|
||||||
|
for _, e := range events {
|
||||||
|
switch e.Type {
|
||||||
|
case containerd.ExitEventType, containerd.DeleteEventType:
|
||||||
|
// ignore these types of events
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("sending %q event\n", e.Type)
|
||||||
|
r, err := c.Post("http://"+filepath.Join(addr, "event"), "application/json", bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.Body != nil {
|
||||||
|
io.Copy(os.Stdout, r.Body)
|
||||||
|
r.Body.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
43
containerd/vendor/github.com/docker/containerd/containerd/main.go
generated
vendored
Normal file
43
containerd/vendor/github.com/docker/containerd/containerd/main.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = "0.0.1"
|
||||||
|
Usage = `High performance conatiner daemon`
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "containerd"
|
||||||
|
app.Version = Version
|
||||||
|
app.Usage = Usage
|
||||||
|
app.Authors = []cli.Author{
|
||||||
|
{
|
||||||
|
Name: "@crosbymichael",
|
||||||
|
Email: "crosbymichael@gmail.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
DaemonCommand,
|
||||||
|
JournalCommand,
|
||||||
|
}
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
cli.BoolFlag{Name: "debug", Usage: "enable debug output in the logs"},
|
||||||
|
// cli.StringFlag{Name: "metrics", Value: "stdout", Usage: "metrics file"},
|
||||||
|
}
|
||||||
|
app.Before = func(context *cli.Context) error {
|
||||||
|
if context.GlobalBool("debug") {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
20
containerd/vendor/github.com/docker/containerd/errors.go
generated
vendored
Normal file
20
containerd/vendor/github.com/docker/containerd/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// External errors
|
||||||
|
ErrEventChanNil = errors.New("containerd: event channel is nil")
|
||||||
|
ErrBundleNotFound = errors.New("containerd: bundle not found")
|
||||||
|
ErrContainerNotFound = errors.New("containerd: container not found")
|
||||||
|
ErrContainerExists = errors.New("containerd: container already exists")
|
||||||
|
ErrProcessNotFound = errors.New("containerd: processs not found for container")
|
||||||
|
ErrUnknownContainerStatus = errors.New("containerd: unknown container status ")
|
||||||
|
|
||||||
|
// Internal errors
|
||||||
|
errShutdown = errors.New("containerd: supervisor is shutdown")
|
||||||
|
errRootNotAbs = errors.New("containerd: rootfs path is not an absolute path")
|
||||||
|
errNoContainerForPid = errors.New("containerd: pid not registered for any container")
|
||||||
|
errInvalidContainerType = errors.New("containerd: invalid container type for runtime")
|
||||||
|
errNotChildProcess = errors.New("containerd: not a child process for container")
|
||||||
|
)
|
42
containerd/vendor/github.com/docker/containerd/event.go
generated
vendored
Normal file
42
containerd/vendor/github.com/docker/containerd/event.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/opencontainers/specs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EventType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExitEventType EventType = "exit"
|
||||||
|
StartContainerEventType EventType = "startContainer"
|
||||||
|
DeleteEventType EventType = "deleteContainerEvent"
|
||||||
|
GetContainerEventType EventType = "getContainer"
|
||||||
|
SignalEventType EventType = "signal"
|
||||||
|
AddProcessEventType EventType = "addProcess"
|
||||||
|
UpdateContainerEventType EventType = "updateContainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewEvent(t EventType) *Event {
|
||||||
|
return &Event{
|
||||||
|
Type: t,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Err: make(chan error, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Type EventType `json:"type"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
BundlePath string `json:"bundlePath,omitempty"`
|
||||||
|
Pid int `json:"pid,omitempty"`
|
||||||
|
Status int `json:"status,omitempty"`
|
||||||
|
Signal os.Signal `json:"signal,omitempty"`
|
||||||
|
Process *specs.Process `json:"process,omitempty"`
|
||||||
|
State *State `json:"state,omitempty"`
|
||||||
|
Containers []Container `json:"-"`
|
||||||
|
Err chan error `json:"-"`
|
||||||
|
}
|
41
containerd/vendor/github.com/docker/containerd/journal.go
generated
vendored
Normal file
41
containerd/vendor/github.com/docker/containerd/journal.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type entry struct {
|
||||||
|
Event *Event `json:"event"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newJournal(path string) (*journal, error) {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &journal{
|
||||||
|
f: f,
|
||||||
|
enc: json.NewEncoder(f),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type journal struct {
|
||||||
|
f *os.File
|
||||||
|
enc *json.Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) write(e *Event) error {
|
||||||
|
et := &entry{
|
||||||
|
Event: e,
|
||||||
|
}
|
||||||
|
return j.enc.Encode(et)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) Close() error {
|
||||||
|
return j.f.Close()
|
||||||
|
}
|
9
containerd/vendor/github.com/docker/containerd/runtime.go
generated
vendored
Normal file
9
containerd/vendor/github.com/docker/containerd/runtime.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import "github.com/opencontainers/specs"
|
||||||
|
|
||||||
|
// runtime handles containers, containers handle their own actions.
|
||||||
|
type Runtime interface {
|
||||||
|
Create(id, bundlePath string) (Container, error)
|
||||||
|
StartProcess(Container, specs.Process) (Process, error)
|
||||||
|
}
|
755
containerd/vendor/github.com/docker/containerd/runtime_linux.go
generated
vendored
Normal file
755
containerd/vendor/github.com/docker/containerd/runtime_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,755 @@
|
||||||
|
// +build libcontainer
|
||||||
|
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/seccomp"
|
||||||
|
"github.com/opencontainers/specs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RLIMIT_CPU = iota // CPU time in sec
|
||||||
|
RLIMIT_FSIZE // Maximum filesize
|
||||||
|
RLIMIT_DATA // max data size
|
||||||
|
RLIMIT_STACK // max stack size
|
||||||
|
RLIMIT_CORE // max core file size
|
||||||
|
RLIMIT_RSS // max resident set size
|
||||||
|
RLIMIT_NPROC // max number of processes
|
||||||
|
RLIMIT_NOFILE // max number of open files
|
||||||
|
RLIMIT_MEMLOCK // max locked-in-memory address space
|
||||||
|
RLIMIT_AS // address space limit
|
||||||
|
RLIMIT_LOCKS // maximum file locks held
|
||||||
|
RLIMIT_SIGPENDING // max number of pending signals
|
||||||
|
RLIMIT_MSGQUEUE // maximum bytes in POSIX mqueues
|
||||||
|
RLIMIT_NICE // max nice prio allowed to raise to
|
||||||
|
RLIMIT_RTPRIO // maximum realtime priority
|
||||||
|
RLIMIT_RTTIME // timeout for RT tasks in us
|
||||||
|
)
|
||||||
|
|
||||||
|
var rlimitMap = map[string]int{
|
||||||
|
"RLIMIT_CPU": RLIMIT_CPU,
|
||||||
|
"RLIMIT_FSIZE": RLIMIT_FSIZE,
|
||||||
|
"RLIMIT_DATA": RLIMIT_DATA,
|
||||||
|
"RLIMIT_STACK": RLIMIT_STACK,
|
||||||
|
"RLIMIT_CORE": RLIMIT_CORE,
|
||||||
|
"RLIMIT_RSS": RLIMIT_RSS,
|
||||||
|
"RLIMIT_NPROC": RLIMIT_NPROC,
|
||||||
|
"RLIMIT_NOFILE": RLIMIT_NOFILE,
|
||||||
|
"RLIMIT_MEMLOCK": RLIMIT_MEMLOCK,
|
||||||
|
"RLIMIT_AS": RLIMIT_AS,
|
||||||
|
"RLIMIT_LOCKS": RLIMIT_LOCKS,
|
||||||
|
"RLIMIT_SGPENDING": RLIMIT_SIGPENDING,
|
||||||
|
"RLIMIT_MSGQUEUE": RLIMIT_MSGQUEUE,
|
||||||
|
"RLIMIT_NICE": RLIMIT_NICE,
|
||||||
|
"RLIMIT_RTPRIO": RLIMIT_RTPRIO,
|
||||||
|
"RLIMIT_RTTIME": RLIMIT_RTTIME,
|
||||||
|
}
|
||||||
|
|
||||||
|
func strToRlimit(key string) (int, error) {
|
||||||
|
rl, ok := rlimitMap[key]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("Wrong rlimit value: %s", key)
|
||||||
|
}
|
||||||
|
return rl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const wildcard = -1
|
||||||
|
|
||||||
|
var allowedDevices = []*configs.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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dev/tty0",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 4,
|
||||||
|
Minor: 0,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dev/tty1",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 4,
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespaceMapping = map[specs.NamespaceType]configs.NamespaceType{
|
||||||
|
specs.PIDNamespace: configs.NEWPID,
|
||||||
|
specs.NetworkNamespace: configs.NEWNET,
|
||||||
|
specs.MountNamespace: configs.NEWNS,
|
||||||
|
specs.UserNamespace: configs.NEWUSER,
|
||||||
|
specs.IPCNamespace: configs.NEWIPC,
|
||||||
|
specs.UTSNamespace: configs.NEWUTS,
|
||||||
|
}
|
||||||
|
|
||||||
|
var mountPropagationMapping = map[string]int{
|
||||||
|
"rprivate": syscall.MS_PRIVATE | syscall.MS_REC,
|
||||||
|
"private": syscall.MS_PRIVATE,
|
||||||
|
"rslave": syscall.MS_SLAVE | syscall.MS_REC,
|
||||||
|
"slave": syscall.MS_SLAVE,
|
||||||
|
"rshared": syscall.MS_SHARED | syscall.MS_REC,
|
||||||
|
"shared": syscall.MS_SHARED,
|
||||||
|
"": syscall.MS_PRIVATE | syscall.MS_REC,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
fmt.Fprint(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
panic("--this line should have never been executed, congratulations--")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type libcontainerProcess struct {
|
||||||
|
process *libcontainer.Process
|
||||||
|
spec specs.Process
|
||||||
|
}
|
||||||
|
|
||||||
|
// change interface to support an error
|
||||||
|
func (p *libcontainerProcess) Pid() (int, error) {
|
||||||
|
pid, err := p.process.Pid()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return pid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *libcontainerProcess) Spec() specs.Process {
|
||||||
|
return p.spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *libcontainerProcess) Signal(s os.Signal) error {
|
||||||
|
return p.process.Signal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type libcontainerContainer struct {
|
||||||
|
c libcontainer.Container
|
||||||
|
initProcess *libcontainerProcess
|
||||||
|
additionalProcesses map[int]*libcontainerProcess
|
||||||
|
exitStatus int
|
||||||
|
exited bool
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) Resume() error {
|
||||||
|
return c.c.Resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) Pause() error {
|
||||||
|
return c.c.Pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) State() State {
|
||||||
|
s := State{}
|
||||||
|
// TODO: what to do with error
|
||||||
|
state, err := c.c.Status()
|
||||||
|
if err != nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
switch state {
|
||||||
|
case libcontainer.Paused, libcontainer.Pausing:
|
||||||
|
s.Status = Paused
|
||||||
|
default:
|
||||||
|
s.Status = Running
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) ID() string {
|
||||||
|
return c.c.ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) Path() string {
|
||||||
|
return c.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) Pid() (int, error) {
|
||||||
|
return c.initProcess.Pid()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) Start() error {
|
||||||
|
return c.c.Start(c.initProcess.process)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) SetExited(status int) {
|
||||||
|
c.exitStatus = status
|
||||||
|
// meh
|
||||||
|
c.exited = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) Delete() error {
|
||||||
|
return c.c.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) Processes() ([]Process, error) {
|
||||||
|
procs := []Process{
|
||||||
|
c.initProcess,
|
||||||
|
}
|
||||||
|
for _, p := range c.additionalProcesses {
|
||||||
|
procs = append(procs, p)
|
||||||
|
}
|
||||||
|
return procs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcontainerContainer) RemoveProcess(pid int) error {
|
||||||
|
if _, ok := c.additionalProcesses[pid]; !ok {
|
||||||
|
return errNotChildProcess
|
||||||
|
}
|
||||||
|
delete(c.additionalProcesses, pid)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRuntime(stateDir string) (Runtime, error) {
|
||||||
|
f, err := libcontainer.New(stateDir, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
|
||||||
|
//l.CriuPath = context.GlobalString("criu")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &libcontainerRuntime{
|
||||||
|
factory: f,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type libcontainerRuntime struct {
|
||||||
|
factory libcontainer.Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) Create(id, bundlePath string) (Container, error) {
|
||||||
|
spec, rspec, err := r.loadSpec(
|
||||||
|
filepath.Join(bundlePath, "config.json"),
|
||||||
|
filepath.Join(bundlePath, "runtime.json"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config, err := r.createLibcontainerConfig(id, bundlePath, spec, rspec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
container, err := r.factory.Create(id, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
process := r.newProcess(spec.Process)
|
||||||
|
c := &libcontainerContainer{
|
||||||
|
c: container,
|
||||||
|
additionalProcesses: make(map[int]*libcontainerProcess),
|
||||||
|
initProcess: &libcontainerProcess{
|
||||||
|
process: process,
|
||||||
|
spec: spec.Process,
|
||||||
|
},
|
||||||
|
path: bundlePath,
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) StartProcess(ci Container, p specs.Process) (Process, error) {
|
||||||
|
c, ok := ci.(*libcontainerContainer)
|
||||||
|
if !ok {
|
||||||
|
return nil, errInvalidContainerType
|
||||||
|
}
|
||||||
|
process := r.newProcess(p)
|
||||||
|
if err := c.c.Start(process); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lp := &libcontainerProcess{
|
||||||
|
process: process,
|
||||||
|
spec: p,
|
||||||
|
}
|
||||||
|
pid, err := process.Pid()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.additionalProcesses[pid] = lp
|
||||||
|
return lp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newProcess returns a new libcontainer Process with the arguments from the
|
||||||
|
// spec and stdio from the current process.
|
||||||
|
func (r *libcontainerRuntime) newProcess(p specs.Process) *libcontainer.Process {
|
||||||
|
return &libcontainer.Process{
|
||||||
|
Args: p.Args,
|
||||||
|
Env: p.Env,
|
||||||
|
// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
|
||||||
|
User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
|
||||||
|
Cwd: p.Cwd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadSpec loads the specification from the provided path.
|
||||||
|
// If the path is empty then the default path will be "config.json"
|
||||||
|
func (r *libcontainerRuntime) loadSpec(cPath, rPath string) (spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, err error) {
|
||||||
|
cf, err := os.Open(cPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, nil, fmt.Errorf("JSON specification file at %s not found", cPath)
|
||||||
|
}
|
||||||
|
return spec, rspec, err
|
||||||
|
}
|
||||||
|
defer cf.Close()
|
||||||
|
|
||||||
|
rf, err := os.Open(rPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, nil, fmt.Errorf("JSON runtime config file at %s not found", rPath)
|
||||||
|
}
|
||||||
|
return spec, rspec, err
|
||||||
|
}
|
||||||
|
defer rf.Close()
|
||||||
|
|
||||||
|
if err = json.NewDecoder(cf).Decode(&spec); err != nil {
|
||||||
|
return spec, rspec, err
|
||||||
|
}
|
||||||
|
if err = json.NewDecoder(rf).Decode(&rspec); err != nil {
|
||||||
|
return spec, rspec, err
|
||||||
|
}
|
||||||
|
return spec, rspec, r.checkSpecVersion(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkSpecVersion makes sure that the spec version matches runc's while we are in the initial
|
||||||
|
// development period. It is better to hard fail than have missing fields or options in the spec.
|
||||||
|
func (r *libcontainerRuntime) checkSpecVersion(s *specs.LinuxSpec) error {
|
||||||
|
if s.Version != specs.Version {
|
||||||
|
return fmt.Errorf("spec version is not compatible with implemented version %q: spec %q", specs.Version, s.Version)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) createLibcontainerConfig(cgroupName, bundlePath string, spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) (*configs.Config, error) {
|
||||||
|
rootfsPath := spec.Root.Path
|
||||||
|
if !filepath.IsAbs(rootfsPath) {
|
||||||
|
rootfsPath = filepath.Join(bundlePath, rootfsPath)
|
||||||
|
}
|
||||||
|
config := &configs.Config{
|
||||||
|
Rootfs: rootfsPath,
|
||||||
|
Capabilities: spec.Linux.Capabilities,
|
||||||
|
Readonlyfs: spec.Root.Readonly,
|
||||||
|
Hostname: spec.Hostname,
|
||||||
|
}
|
||||||
|
for _, ns := range rspec.Linux.Namespaces {
|
||||||
|
t, exists := namespaceMapping[ns.Type]
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("namespace %q does not exist", ns)
|
||||||
|
}
|
||||||
|
config.Namespaces.Add(t, ns.Path)
|
||||||
|
}
|
||||||
|
if config.Namespaces.Contains(configs.NEWNET) {
|
||||||
|
config.Networks = []*configs.Network{
|
||||||
|
{
|
||||||
|
Type: "loopback",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, mp := range spec.Mounts {
|
||||||
|
m, ok := rspec.Mounts[mp.Name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Mount with Name %q not found in runtime config", mp.Name)
|
||||||
|
}
|
||||||
|
config.Mounts = append(config.Mounts, r.createLibcontainerMount(bundlePath, mp.Path, m))
|
||||||
|
}
|
||||||
|
if err := r.createDevices(rspec, config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := r.setupUserNamespace(rspec, config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, rlimit := range rspec.Linux.Rlimits {
|
||||||
|
rl, err := r.createLibContainerRlimit(rlimit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Rlimits = append(config.Rlimits, rl)
|
||||||
|
}
|
||||||
|
c, err := r.createCgroupConfig(cgroupName, rspec, config.Devices)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Cgroups = c
|
||||||
|
if config.Readonlyfs {
|
||||||
|
r.setReadonly(config)
|
||||||
|
config.MaskPaths = []string{
|
||||||
|
"/proc/kcore",
|
||||||
|
}
|
||||||
|
config.ReadonlyPaths = []string{
|
||||||
|
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seccomp, err := r.setupSeccomp(&rspec.Linux.Seccomp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Seccomp = seccomp
|
||||||
|
config.Sysctl = rspec.Linux.Sysctl
|
||||||
|
config.ProcessLabel = rspec.Linux.SelinuxProcessLabel
|
||||||
|
config.AppArmorProfile = rspec.Linux.ApparmorProfile
|
||||||
|
for _, g := range spec.Process.User.AdditionalGids {
|
||||||
|
config.AdditionalGroups = append(config.AdditionalGroups, strconv.FormatUint(uint64(g), 10))
|
||||||
|
}
|
||||||
|
r.createHooks(rspec, config)
|
||||||
|
config.Version = specs.Version
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) createLibcontainerMount(cwd, dest string, m specs.Mount) *configs.Mount {
|
||||||
|
flags, pgflags, data := parseMountOptions(m.Options)
|
||||||
|
source := m.Source
|
||||||
|
if m.Type == "bind" {
|
||||||
|
if !filepath.IsAbs(source) {
|
||||||
|
source = filepath.Join(cwd, m.Source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &configs.Mount{
|
||||||
|
Device: m.Type,
|
||||||
|
Source: source,
|
||||||
|
Destination: dest,
|
||||||
|
Data: data,
|
||||||
|
Flags: flags,
|
||||||
|
PropagationFlags: pgflags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *libcontainerRuntime) createCgroupConfig(name string, spec *specs.LinuxRuntimeSpec, devices []*configs.Device) (*configs.Cgroup, error) {
|
||||||
|
c := &configs.Cgroup{
|
||||||
|
Name: name,
|
||||||
|
Parent: "/containerd",
|
||||||
|
AllowedDevices: append(devices, allowedDevices...),
|
||||||
|
}
|
||||||
|
r := spec.Linux.Resources
|
||||||
|
c.Memory = int64(r.Memory.Limit)
|
||||||
|
c.MemoryReservation = int64(r.Memory.Reservation)
|
||||||
|
c.MemorySwap = int64(r.Memory.Swap)
|
||||||
|
c.KernelMemory = int64(r.Memory.Kernel)
|
||||||
|
c.MemorySwappiness = int64(r.Memory.Swappiness)
|
||||||
|
c.CpuShares = int64(r.CPU.Shares)
|
||||||
|
c.CpuQuota = int64(r.CPU.Quota)
|
||||||
|
c.CpuPeriod = int64(r.CPU.Period)
|
||||||
|
c.CpuRtRuntime = int64(r.CPU.RealtimeRuntime)
|
||||||
|
c.CpuRtPeriod = int64(r.CPU.RealtimePeriod)
|
||||||
|
c.CpusetCpus = r.CPU.Cpus
|
||||||
|
c.CpusetMems = r.CPU.Mems
|
||||||
|
c.BlkioWeight = r.BlockIO.Weight
|
||||||
|
c.BlkioLeafWeight = r.BlockIO.LeafWeight
|
||||||
|
for _, wd := range r.BlockIO.WeightDevice {
|
||||||
|
weightDevice := configs.NewWeightDevice(wd.Major, wd.Minor, wd.Weight, wd.LeafWeight)
|
||||||
|
c.BlkioWeightDevice = append(c.BlkioWeightDevice, weightDevice)
|
||||||
|
}
|
||||||
|
for _, td := range r.BlockIO.ThrottleReadBpsDevice {
|
||||||
|
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||||
|
c.BlkioThrottleReadBpsDevice = append(c.BlkioThrottleReadBpsDevice, throttleDevice)
|
||||||
|
}
|
||||||
|
for _, td := range r.BlockIO.ThrottleWriteBpsDevice {
|
||||||
|
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||||
|
c.BlkioThrottleWriteBpsDevice = append(c.BlkioThrottleWriteBpsDevice, throttleDevice)
|
||||||
|
}
|
||||||
|
for _, td := range r.BlockIO.ThrottleReadIOPSDevice {
|
||||||
|
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||||
|
c.BlkioThrottleReadIOPSDevice = append(c.BlkioThrottleReadIOPSDevice, throttleDevice)
|
||||||
|
}
|
||||||
|
for _, td := range r.BlockIO.ThrottleWriteIOPSDevice {
|
||||||
|
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||||
|
c.BlkioThrottleWriteIOPSDevice = append(c.BlkioThrottleWriteIOPSDevice, throttleDevice)
|
||||||
|
}
|
||||||
|
for _, l := range r.HugepageLimits {
|
||||||
|
c.HugetlbLimit = append(c.HugetlbLimit, &configs.HugepageLimit{
|
||||||
|
Pagesize: l.Pagesize,
|
||||||
|
Limit: l.Limit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
c.OomKillDisable = r.DisableOOMKiller
|
||||||
|
c.NetClsClassid = r.Network.ClassID
|
||||||
|
for _, m := range r.Network.Priorities {
|
||||||
|
c.NetPrioIfpriomap = append(c.NetPrioIfpriomap, &configs.IfPrioMap{
|
||||||
|
Interface: m.Name,
|
||||||
|
Priority: int64(m.Priority),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) createDevices(spec *specs.LinuxRuntimeSpec, config *configs.Config) error {
|
||||||
|
for _, d := range spec.Linux.Devices {
|
||||||
|
device := &configs.Device{
|
||||||
|
Type: d.Type,
|
||||||
|
Path: d.Path,
|
||||||
|
Major: d.Major,
|
||||||
|
Minor: d.Minor,
|
||||||
|
Permissions: d.Permissions,
|
||||||
|
FileMode: d.FileMode,
|
||||||
|
Uid: d.UID,
|
||||||
|
Gid: d.GID,
|
||||||
|
}
|
||||||
|
config.Devices = append(config.Devices, device)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) setReadonly(config *configs.Config) {
|
||||||
|
for _, m := range config.Mounts {
|
||||||
|
if m.Device == "sysfs" {
|
||||||
|
m.Flags |= syscall.MS_RDONLY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) setupUserNamespace(spec *specs.LinuxRuntimeSpec, config *configs.Config) error {
|
||||||
|
if len(spec.Linux.UIDMappings) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
config.Namespaces.Add(configs.NEWUSER, "")
|
||||||
|
create := func(m specs.IDMapping) configs.IDMap {
|
||||||
|
return configs.IDMap{
|
||||||
|
HostID: int(m.HostID),
|
||||||
|
ContainerID: int(m.ContainerID),
|
||||||
|
Size: int(m.Size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, m := range spec.Linux.UIDMappings {
|
||||||
|
config.UidMappings = append(config.UidMappings, create(m))
|
||||||
|
}
|
||||||
|
for _, m := range spec.Linux.GIDMappings {
|
||||||
|
config.GidMappings = append(config.GidMappings, create(m))
|
||||||
|
}
|
||||||
|
rootUID, err := config.HostUID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rootGID, err := config.HostGID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, node := range config.Devices {
|
||||||
|
node.Uid = uint32(rootUID)
|
||||||
|
node.Gid = uint32(rootGID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) {
|
||||||
|
rl, err := strToRlimit(rlimit.Type)
|
||||||
|
if err != nil {
|
||||||
|
return configs.Rlimit{}, err
|
||||||
|
}
|
||||||
|
return configs.Rlimit{
|
||||||
|
Type: rl,
|
||||||
|
Hard: uint64(rlimit.Hard),
|
||||||
|
Soft: uint64(rlimit.Soft),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMountOptions parses the string and returns the flags, propagation
|
||||||
|
// flags and any mount data that it contains.
|
||||||
|
func parseMountOptions(options []string) (int, []int, string) {
|
||||||
|
var (
|
||||||
|
flag int
|
||||||
|
pgflag []int
|
||||||
|
data []string
|
||||||
|
)
|
||||||
|
flags := map[string]struct {
|
||||||
|
clear bool
|
||||||
|
flag int
|
||||||
|
}{
|
||||||
|
"async": {true, syscall.MS_SYNCHRONOUS},
|
||||||
|
"atime": {true, syscall.MS_NOATIME},
|
||||||
|
"bind": {false, syscall.MS_BIND},
|
||||||
|
"defaults": {false, 0},
|
||||||
|
"dev": {true, syscall.MS_NODEV},
|
||||||
|
"diratime": {true, syscall.MS_NODIRATIME},
|
||||||
|
"dirsync": {false, syscall.MS_DIRSYNC},
|
||||||
|
"exec": {true, syscall.MS_NOEXEC},
|
||||||
|
"mand": {false, syscall.MS_MANDLOCK},
|
||||||
|
"noatime": {false, syscall.MS_NOATIME},
|
||||||
|
"nodev": {false, syscall.MS_NODEV},
|
||||||
|
"nodiratime": {false, syscall.MS_NODIRATIME},
|
||||||
|
"noexec": {false, syscall.MS_NOEXEC},
|
||||||
|
"nomand": {true, syscall.MS_MANDLOCK},
|
||||||
|
"norelatime": {true, syscall.MS_RELATIME},
|
||||||
|
"nostrictatime": {true, syscall.MS_STRICTATIME},
|
||||||
|
"nosuid": {false, syscall.MS_NOSUID},
|
||||||
|
"rbind": {false, syscall.MS_BIND | syscall.MS_REC},
|
||||||
|
"relatime": {false, syscall.MS_RELATIME},
|
||||||
|
"remount": {false, syscall.MS_REMOUNT},
|
||||||
|
"ro": {false, syscall.MS_RDONLY},
|
||||||
|
"rw": {true, syscall.MS_RDONLY},
|
||||||
|
"strictatime": {false, syscall.MS_STRICTATIME},
|
||||||
|
"suid": {true, syscall.MS_NOSUID},
|
||||||
|
"sync": {false, syscall.MS_SYNCHRONOUS},
|
||||||
|
}
|
||||||
|
propagationFlags := map[string]struct {
|
||||||
|
clear bool
|
||||||
|
flag int
|
||||||
|
}{
|
||||||
|
"private": {false, syscall.MS_PRIVATE},
|
||||||
|
"shared": {false, syscall.MS_SHARED},
|
||||||
|
"slave": {false, syscall.MS_SLAVE},
|
||||||
|
"unbindable": {false, syscall.MS_UNBINDABLE},
|
||||||
|
"rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC},
|
||||||
|
"rshared": {false, syscall.MS_SHARED | syscall.MS_REC},
|
||||||
|
"rslave": {false, syscall.MS_SLAVE | syscall.MS_REC},
|
||||||
|
"runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC},
|
||||||
|
}
|
||||||
|
for _, o := range options {
|
||||||
|
// If the option does not exist in the flags table or the flag
|
||||||
|
// is not supported on the platform,
|
||||||
|
// then it is a data value for a specific fs type
|
||||||
|
if f, exists := flags[o]; exists && f.flag != 0 {
|
||||||
|
if f.clear {
|
||||||
|
flag &= ^f.flag
|
||||||
|
} else {
|
||||||
|
flag |= f.flag
|
||||||
|
}
|
||||||
|
} else if f, exists := propagationFlags[o]; exists && f.flag != 0 {
|
||||||
|
pgflag = append(pgflag, f.flag)
|
||||||
|
} else {
|
||||||
|
data = append(data, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag, pgflag, strings.Join(data, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) setupSeccomp(config *specs.Seccomp) (*configs.Seccomp, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No default action specified, no syscalls listed, assume seccomp disabled
|
||||||
|
if config.DefaultAction == "" && len(config.Syscalls) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig := new(configs.Seccomp)
|
||||||
|
newConfig.Syscalls = []*configs.Syscall{}
|
||||||
|
|
||||||
|
if len(config.Architectures) > 0 {
|
||||||
|
newConfig.Architectures = []string{}
|
||||||
|
for _, arch := range config.Architectures {
|
||||||
|
newArch, err := seccomp.ConvertStringToArch(string(arch))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newConfig.Architectures = append(newConfig.Architectures, newArch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert default action from string representation
|
||||||
|
newDefaultAction, err := seccomp.ConvertStringToAction(string(config.DefaultAction))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newConfig.DefaultAction = newDefaultAction
|
||||||
|
|
||||||
|
// Loop through all syscall blocks and convert them to libcontainer format
|
||||||
|
for _, call := range config.Syscalls {
|
||||||
|
newAction, err := seccomp.ConvertStringToAction(string(call.Action))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newCall := configs.Syscall{
|
||||||
|
Name: call.Name,
|
||||||
|
Action: newAction,
|
||||||
|
Args: []*configs.Arg{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all the arguments of the syscall and convert them
|
||||||
|
for _, arg := range call.Args {
|
||||||
|
newOp, err := seccomp.ConvertStringToOperator(string(arg.Op))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newArg := configs.Arg{
|
||||||
|
Index: arg.Index,
|
||||||
|
Value: arg.Value,
|
||||||
|
ValueTwo: arg.ValueTwo,
|
||||||
|
Op: newOp,
|
||||||
|
}
|
||||||
|
|
||||||
|
newCall.Args = append(newCall.Args, &newArg)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig.Syscalls = append(newConfig.Syscalls, &newCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *libcontainerRuntime) createHooks(rspec *specs.LinuxRuntimeSpec, config *configs.Config) {
|
||||||
|
config.Hooks = &configs.Hooks{}
|
||||||
|
for _, h := range rspec.Hooks.Prestart {
|
||||||
|
cmd := configs.Command{
|
||||||
|
Path: h.Path,
|
||||||
|
Args: h.Args,
|
||||||
|
Env: h.Env,
|
||||||
|
}
|
||||||
|
config.Hooks.Prestart = append(config.Hooks.Prestart, configs.NewCommandHook(cmd))
|
||||||
|
}
|
||||||
|
for _, h := range rspec.Hooks.Poststop {
|
||||||
|
cmd := configs.Command{
|
||||||
|
Path: h.Path,
|
||||||
|
Args: h.Args,
|
||||||
|
Env: h.Env,
|
||||||
|
}
|
||||||
|
config.Hooks.Poststop = append(config.Hooks.Poststop, configs.NewCommandHook(cmd))
|
||||||
|
}
|
||||||
|
}
|
234
containerd/vendor/github.com/docker/containerd/supervisor.go
generated
vendored
Normal file
234
containerd/vendor/github.com/docker/containerd/supervisor.go
generated
vendored
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
package containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSupervisor returns an initialized Process supervisor.
|
||||||
|
func NewSupervisor(stateDir string, concurrency int) (*Supervisor, error) {
|
||||||
|
if err := os.MkdirAll(stateDir, 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// register counters
|
||||||
|
runtime, err := NewRuntime(stateDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
j, err := newJournal(filepath.Join(stateDir, "journal.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := &Supervisor{
|
||||||
|
stateDir: stateDir,
|
||||||
|
containers: make(map[string]Container),
|
||||||
|
processes: make(map[int]Container),
|
||||||
|
runtime: runtime,
|
||||||
|
tasks: make(chan *startTask, concurrency*100),
|
||||||
|
journal: j,
|
||||||
|
}
|
||||||
|
for i := 0; i < concurrency; i++ {
|
||||||
|
s.workerGroup.Add(1)
|
||||||
|
go s.startContainerWorker(s.tasks)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Supervisor struct {
|
||||||
|
// stateDir is the directory on the system to store container runtime state information.
|
||||||
|
stateDir string
|
||||||
|
|
||||||
|
containers map[string]Container
|
||||||
|
|
||||||
|
processes map[int]Container
|
||||||
|
|
||||||
|
runtime Runtime
|
||||||
|
|
||||||
|
journal *journal
|
||||||
|
|
||||||
|
events chan *Event
|
||||||
|
tasks chan *startTask
|
||||||
|
workerGroup sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// need proper close logic for jobs and stuff so that sending to the channels dont panic
|
||||||
|
// but can complete jobs
|
||||||
|
func (s *Supervisor) Close() error {
|
||||||
|
return s.journal.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start is a non-blocking call that runs the supervisor for monitoring contianer processes and
|
||||||
|
// executing new containers.
|
||||||
|
//
|
||||||
|
// This event loop is the only thing that is allowed to modify state of containers and processes.
|
||||||
|
func (s *Supervisor) Start(events chan *Event) error {
|
||||||
|
if events == nil {
|
||||||
|
return ErrEventChanNil
|
||||||
|
}
|
||||||
|
s.events = events
|
||||||
|
go func() {
|
||||||
|
for e := range events {
|
||||||
|
if err := s.journal.write(e); err != nil {
|
||||||
|
logrus.WithField("error", err).Error("write journal entry")
|
||||||
|
}
|
||||||
|
switch e.Type {
|
||||||
|
case ExitEventType:
|
||||||
|
logrus.WithFields(logrus.Fields{"pid": e.Pid, "status": e.Status}).
|
||||||
|
Debug("containerd: process exited")
|
||||||
|
// is it the child process of a container
|
||||||
|
if container, ok := s.processes[e.Pid]; ok {
|
||||||
|
if err := container.RemoveProcess(e.Pid); err != nil {
|
||||||
|
logrus.WithField("error", err).Error("containerd: find container for pid")
|
||||||
|
}
|
||||||
|
delete(s.processes, e.Pid)
|
||||||
|
close(e.Err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// is it the main container's process
|
||||||
|
container, err := s.getContainerForPid(e.Pid)
|
||||||
|
if err != nil {
|
||||||
|
if err != errNoContainerForPid {
|
||||||
|
logrus.WithField("error", err).Error("containerd: find container for pid")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
container.SetExited(e.Status)
|
||||||
|
ne := NewEvent(DeleteEventType)
|
||||||
|
ne.ID = container.ID()
|
||||||
|
s.SendEvent(ne)
|
||||||
|
case StartContainerEventType:
|
||||||
|
container, err := s.runtime.Create(e.ID, e.BundlePath)
|
||||||
|
if err != nil {
|
||||||
|
e.Err <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.containers[e.ID] = container
|
||||||
|
s.tasks <- &startTask{
|
||||||
|
err: e.Err,
|
||||||
|
container: container,
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case DeleteEventType:
|
||||||
|
if container, ok := s.containers[e.ID]; ok {
|
||||||
|
if err := s.deleteContainer(container); err != nil {
|
||||||
|
logrus.WithField("error", err).Error("containerd: deleting container")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case GetContainerEventType:
|
||||||
|
for _, c := range s.containers {
|
||||||
|
e.Containers = append(e.Containers, c)
|
||||||
|
}
|
||||||
|
case SignalEventType:
|
||||||
|
container, ok := s.containers[e.ID]
|
||||||
|
if !ok {
|
||||||
|
e.Err <- ErrContainerNotFound
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
processes, err := container.Processes()
|
||||||
|
if err != nil {
|
||||||
|
e.Err <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, p := range processes {
|
||||||
|
if pid, err := p.Pid(); err == nil && pid == e.Pid {
|
||||||
|
e.Err <- p.Signal(e.Signal)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.Err <- ErrProcessNotFound
|
||||||
|
continue
|
||||||
|
case AddProcessEventType:
|
||||||
|
container, ok := s.containers[e.ID]
|
||||||
|
if !ok {
|
||||||
|
e.Err <- ErrContainerNotFound
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p, err := s.runtime.StartProcess(container, *e.Process)
|
||||||
|
if err != nil {
|
||||||
|
e.Err <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if e.Pid, err = p.Pid(); err != nil {
|
||||||
|
e.Err <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.processes[e.Pid] = container
|
||||||
|
case UpdateContainerEventType:
|
||||||
|
container, ok := s.containers[e.ID]
|
||||||
|
if !ok {
|
||||||
|
e.Err <- ErrContainerNotFound
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if e.State.Status != "" {
|
||||||
|
switch e.State.Status {
|
||||||
|
case Running:
|
||||||
|
if err := container.Resume(); err != nil {
|
||||||
|
e.Err <- ErrUnknownContainerStatus
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case Paused:
|
||||||
|
if err := container.Pause(); err != nil {
|
||||||
|
e.Err <- ErrUnknownContainerStatus
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
e.Err <- ErrUnknownContainerStatus
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(e.Err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) deleteContainer(container Container) error {
|
||||||
|
delete(s.containers, container.ID())
|
||||||
|
return container.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) getContainerForPid(pid int) (Container, error) {
|
||||||
|
for _, container := range s.containers {
|
||||||
|
cpid, err := container.Pid()
|
||||||
|
if err != nil {
|
||||||
|
if lerr, ok := err.(libcontainer.Error); ok {
|
||||||
|
if lerr.Code() == libcontainer.ProcessNotExecuted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.WithField("error", err).Error("containerd: get container pid")
|
||||||
|
}
|
||||||
|
if pid == cpid {
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errNoContainerForPid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) SendEvent(evt *Event) {
|
||||||
|
s.events <- evt
|
||||||
|
}
|
||||||
|
|
||||||
|
type startTask struct {
|
||||||
|
container Container
|
||||||
|
err chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Supervisor) startContainerWorker(tasks chan *startTask) {
|
||||||
|
defer s.workerGroup.Done()
|
||||||
|
for t := range tasks {
|
||||||
|
if err := t.container.Start(); err != nil {
|
||||||
|
e := NewEvent(StartContainerEventType)
|
||||||
|
e.ID = t.container.ID()
|
||||||
|
s.SendEvent(e)
|
||||||
|
t.err <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.err <- nil
|
||||||
|
}
|
||||||
|
}
|
69
containerd/vendor/github.com/docker/docker/pkg/mount/flags.go
generated
vendored
Normal file
69
containerd/vendor/github.com/docker/docker/pkg/mount/flags.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse fstab type mount options into mount() flags
|
||||||
|
// and device specific data
|
||||||
|
func parseOptions(options string) (int, string) {
|
||||||
|
var (
|
||||||
|
flag int
|
||||||
|
data []string
|
||||||
|
)
|
||||||
|
|
||||||
|
flags := map[string]struct {
|
||||||
|
clear bool
|
||||||
|
flag int
|
||||||
|
}{
|
||||||
|
"defaults": {false, 0},
|
||||||
|
"ro": {false, RDONLY},
|
||||||
|
"rw": {true, RDONLY},
|
||||||
|
"suid": {true, NOSUID},
|
||||||
|
"nosuid": {false, NOSUID},
|
||||||
|
"dev": {true, NODEV},
|
||||||
|
"nodev": {false, NODEV},
|
||||||
|
"exec": {true, NOEXEC},
|
||||||
|
"noexec": {false, NOEXEC},
|
||||||
|
"sync": {false, SYNCHRONOUS},
|
||||||
|
"async": {true, SYNCHRONOUS},
|
||||||
|
"dirsync": {false, DIRSYNC},
|
||||||
|
"remount": {false, REMOUNT},
|
||||||
|
"mand": {false, MANDLOCK},
|
||||||
|
"nomand": {true, MANDLOCK},
|
||||||
|
"atime": {true, NOATIME},
|
||||||
|
"noatime": {false, NOATIME},
|
||||||
|
"diratime": {true, NODIRATIME},
|
||||||
|
"nodiratime": {false, NODIRATIME},
|
||||||
|
"bind": {false, BIND},
|
||||||
|
"rbind": {false, RBIND},
|
||||||
|
"unbindable": {false, UNBINDABLE},
|
||||||
|
"runbindable": {false, RUNBINDABLE},
|
||||||
|
"private": {false, PRIVATE},
|
||||||
|
"rprivate": {false, RPRIVATE},
|
||||||
|
"shared": {false, SHARED},
|
||||||
|
"rshared": {false, RSHARED},
|
||||||
|
"slave": {false, SLAVE},
|
||||||
|
"rslave": {false, RSLAVE},
|
||||||
|
"relatime": {false, RELATIME},
|
||||||
|
"norelatime": {true, RELATIME},
|
||||||
|
"strictatime": {false, STRICTATIME},
|
||||||
|
"nostrictatime": {true, STRICTATIME},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range strings.Split(options, ",") {
|
||||||
|
// If the option does not exist in the flags table or the flag
|
||||||
|
// is not supported on the platform,
|
||||||
|
// then it is a data value for a specific fs type
|
||||||
|
if f, exists := flags[o]; exists && f.flag != 0 {
|
||||||
|
if f.clear {
|
||||||
|
flag &= ^f.flag
|
||||||
|
} else {
|
||||||
|
flag |= f.flag
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = append(data, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag, strings.Join(data, ",")
|
||||||
|
}
|
48
containerd/vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go
generated
vendored
Normal file
48
containerd/vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// +build freebsd,cgo
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/mount.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RDONLY will mount the filesystem as read-only.
|
||||||
|
RDONLY = C.MNT_RDONLY
|
||||||
|
|
||||||
|
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||||
|
// take effect.
|
||||||
|
NOSUID = C.MNT_NOSUID
|
||||||
|
|
||||||
|
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||||
|
NOEXEC = C.MNT_NOEXEC
|
||||||
|
|
||||||
|
// SYNCHRONOUS will allow any I/O to the file system to be done synchronously.
|
||||||
|
SYNCHRONOUS = C.MNT_SYNCHRONOUS
|
||||||
|
|
||||||
|
// NOATIME will not update the file access time when reading from a file.
|
||||||
|
NOATIME = C.MNT_NOATIME
|
||||||
|
)
|
||||||
|
|
||||||
|
// These flags are unsupported.
|
||||||
|
const (
|
||||||
|
BIND = 0
|
||||||
|
DIRSYNC = 0
|
||||||
|
MANDLOCK = 0
|
||||||
|
NODEV = 0
|
||||||
|
NODIRATIME = 0
|
||||||
|
UNBINDABLE = 0
|
||||||
|
RUNBINDABLE = 0
|
||||||
|
PRIVATE = 0
|
||||||
|
RPRIVATE = 0
|
||||||
|
SHARED = 0
|
||||||
|
RSHARED = 0
|
||||||
|
SLAVE = 0
|
||||||
|
RSLAVE = 0
|
||||||
|
RBIND = 0
|
||||||
|
RELATIVE = 0
|
||||||
|
RELATIME = 0
|
||||||
|
REMOUNT = 0
|
||||||
|
STRICTATIME = 0
|
||||||
|
)
|
85
containerd/vendor/github.com/docker/docker/pkg/mount/flags_linux.go
generated
vendored
Normal file
85
containerd/vendor/github.com/docker/docker/pkg/mount/flags_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RDONLY will mount the file system read-only.
|
||||||
|
RDONLY = syscall.MS_RDONLY
|
||||||
|
|
||||||
|
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||||
|
// take effect.
|
||||||
|
NOSUID = syscall.MS_NOSUID
|
||||||
|
|
||||||
|
// NODEV will not interpret character or block special devices on the file
|
||||||
|
// system.
|
||||||
|
NODEV = syscall.MS_NODEV
|
||||||
|
|
||||||
|
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||||
|
NOEXEC = syscall.MS_NOEXEC
|
||||||
|
|
||||||
|
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
|
||||||
|
SYNCHRONOUS = syscall.MS_SYNCHRONOUS
|
||||||
|
|
||||||
|
// DIRSYNC will force all directory updates within the file system to be done
|
||||||
|
// synchronously. This affects the following system calls: creat, link,
|
||||||
|
// unlink, symlink, mkdir, rmdir, mknod and rename.
|
||||||
|
DIRSYNC = syscall.MS_DIRSYNC
|
||||||
|
|
||||||
|
// REMOUNT will attempt to remount an already-mounted file system. This is
|
||||||
|
// commonly used to change the mount flags for a file system, especially to
|
||||||
|
// make a readonly file system writeable. It does not change device or mount
|
||||||
|
// point.
|
||||||
|
REMOUNT = syscall.MS_REMOUNT
|
||||||
|
|
||||||
|
// MANDLOCK will force mandatory locks on a filesystem.
|
||||||
|
MANDLOCK = syscall.MS_MANDLOCK
|
||||||
|
|
||||||
|
// NOATIME will not update the file access time when reading from a file.
|
||||||
|
NOATIME = syscall.MS_NOATIME
|
||||||
|
|
||||||
|
// NODIRATIME will not update the directory access time.
|
||||||
|
NODIRATIME = syscall.MS_NODIRATIME
|
||||||
|
|
||||||
|
// BIND remounts a subtree somewhere else.
|
||||||
|
BIND = syscall.MS_BIND
|
||||||
|
|
||||||
|
// RBIND remounts a subtree and all possible submounts somewhere else.
|
||||||
|
RBIND = syscall.MS_BIND | syscall.MS_REC
|
||||||
|
|
||||||
|
// UNBINDABLE creates a mount which cannot be cloned through a bind operation.
|
||||||
|
UNBINDABLE = syscall.MS_UNBINDABLE
|
||||||
|
|
||||||
|
// RUNBINDABLE marks the entire mount tree as UNBINDABLE.
|
||||||
|
RUNBINDABLE = syscall.MS_UNBINDABLE | syscall.MS_REC
|
||||||
|
|
||||||
|
// PRIVATE creates a mount which carries no propagation abilities.
|
||||||
|
PRIVATE = syscall.MS_PRIVATE
|
||||||
|
|
||||||
|
// RPRIVATE marks the entire mount tree as PRIVATE.
|
||||||
|
RPRIVATE = syscall.MS_PRIVATE | syscall.MS_REC
|
||||||
|
|
||||||
|
// SLAVE creates a mount which receives propagation from its master, but not
|
||||||
|
// vice versa.
|
||||||
|
SLAVE = syscall.MS_SLAVE
|
||||||
|
|
||||||
|
// RSLAVE marks the entire mount tree as SLAVE.
|
||||||
|
RSLAVE = syscall.MS_SLAVE | syscall.MS_REC
|
||||||
|
|
||||||
|
// SHARED creates a mount which provides the ability to create mirrors of
|
||||||
|
// that mount such that mounts and unmounts within any of the mirrors
|
||||||
|
// propagate to the other mirrors.
|
||||||
|
SHARED = syscall.MS_SHARED
|
||||||
|
|
||||||
|
// RSHARED marks the entire mount tree as SHARED.
|
||||||
|
RSHARED = syscall.MS_SHARED | syscall.MS_REC
|
||||||
|
|
||||||
|
// RELATIME updates inode access times relative to modify or change time.
|
||||||
|
RELATIME = syscall.MS_RELATIME
|
||||||
|
|
||||||
|
// STRICTATIME allows to explicitly request full atime updates. This makes
|
||||||
|
// it possible for the kernel to default to relatime or noatime but still
|
||||||
|
// allow userspace to override it.
|
||||||
|
STRICTATIME = syscall.MS_STRICTATIME
|
||||||
|
)
|
30
containerd/vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go
generated
vendored
Normal file
30
containerd/vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// +build !linux,!freebsd freebsd,!cgo
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
// These flags are unsupported.
|
||||||
|
const (
|
||||||
|
BIND = 0
|
||||||
|
DIRSYNC = 0
|
||||||
|
MANDLOCK = 0
|
||||||
|
NOATIME = 0
|
||||||
|
NODEV = 0
|
||||||
|
NODIRATIME = 0
|
||||||
|
NOEXEC = 0
|
||||||
|
NOSUID = 0
|
||||||
|
UNBINDABLE = 0
|
||||||
|
RUNBINDABLE = 0
|
||||||
|
PRIVATE = 0
|
||||||
|
RPRIVATE = 0
|
||||||
|
SHARED = 0
|
||||||
|
RSHARED = 0
|
||||||
|
SLAVE = 0
|
||||||
|
RSLAVE = 0
|
||||||
|
RBIND = 0
|
||||||
|
RELATIME = 0
|
||||||
|
RELATIVE = 0
|
||||||
|
REMOUNT = 0
|
||||||
|
STRICTATIME = 0
|
||||||
|
SYNCHRONOUS = 0
|
||||||
|
RDONLY = 0
|
||||||
|
)
|
74
containerd/vendor/github.com/docker/docker/pkg/mount/mount.go
generated
vendored
Normal file
74
containerd/vendor/github.com/docker/docker/pkg/mount/mount.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMounts retrieves a list of mounts for the current running process.
|
||||||
|
func GetMounts() ([]*Info, error) {
|
||||||
|
return parseMountTable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounted looks at /proc/self/mountinfo to determine of the specified
|
||||||
|
// mountpoint has been mounted
|
||||||
|
func Mounted(mountpoint string) (bool, error) {
|
||||||
|
entries, err := parseMountTable()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search the table for the mountpoint
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.Mountpoint == mountpoint {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount will mount filesystem according to the specified configuration, on the
|
||||||
|
// condition that the target path is *not* already mounted. Options must be
|
||||||
|
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||||
|
// flags.go for supported option flags.
|
||||||
|
func Mount(device, target, mType, options string) error {
|
||||||
|
flag, _ := parseOptions(options)
|
||||||
|
if flag&REMOUNT != REMOUNT {
|
||||||
|
if mounted, err := Mounted(target); err != nil || mounted {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ForceMount(device, target, mType, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceMount will mount a filesystem according to the specified configuration,
|
||||||
|
// *regardless* if the target path is not already mounted. Options must be
|
||||||
|
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||||
|
// flags.go for supported option flags.
|
||||||
|
func ForceMount(device, target, mType, options string) error {
|
||||||
|
flag, data := parseOptions(options)
|
||||||
|
if err := mount(device, target, mType, uintptr(flag), data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount will unmount the target filesystem, so long as it is mounted.
|
||||||
|
func Unmount(target string) error {
|
||||||
|
if mounted, err := Mounted(target); err != nil || !mounted {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ForceUnmount(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceUnmount will force an unmount of the target filesystem, regardless if
|
||||||
|
// it is mounted or not.
|
||||||
|
func ForceUnmount(target string) (err error) {
|
||||||
|
// Simple retry logic for unmount
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if err = unmount(target, 0); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
59
containerd/vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go
generated
vendored
Normal file
59
containerd/vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/_iovec.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func allocateIOVecs(options []string) []C.struct_iovec {
|
||||||
|
out := make([]C.struct_iovec, len(options))
|
||||||
|
for i, option := range options {
|
||||||
|
out[i].iov_base = unsafe.Pointer(C.CString(option))
|
||||||
|
out[i].iov_len = C.size_t(len(option) + 1)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||||
|
isNullFS := false
|
||||||
|
|
||||||
|
xs := strings.Split(data, ",")
|
||||||
|
for _, x := range xs {
|
||||||
|
if x == "bind" {
|
||||||
|
isNullFS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options := []string{"fspath", target}
|
||||||
|
if isNullFS {
|
||||||
|
options = append(options, "fstype", "nullfs", "target", device)
|
||||||
|
} else {
|
||||||
|
options = append(options, "fstype", mType, "from", device)
|
||||||
|
}
|
||||||
|
rawOptions := allocateIOVecs(options)
|
||||||
|
for _, rawOption := range rawOptions {
|
||||||
|
defer C.free(rawOption.iov_base)
|
||||||
|
}
|
||||||
|
|
||||||
|
if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 {
|
||||||
|
reason := C.GoString(C.strerror(*C.__error()))
|
||||||
|
return fmt.Errorf("Failed to call nmount: %s", reason)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmount(target string, flag int) error {
|
||||||
|
return syscall.Unmount(target, flag)
|
||||||
|
}
|
21
containerd/vendor/github.com/docker/docker/pkg/mount/mounter_linux.go
generated
vendored
Normal file
21
containerd/vendor/github.com/docker/docker/pkg/mount/mounter_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||||
|
if err := syscall.Mount(device, target, mType, flag, data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a bind mount or remount, remount...
|
||||||
|
if flag&syscall.MS_BIND == syscall.MS_BIND && flag&syscall.MS_RDONLY == syscall.MS_RDONLY {
|
||||||
|
return syscall.Mount(device, target, mType, flag|syscall.MS_REMOUNT, data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmount(target string, flag int) error {
|
||||||
|
return syscall.Unmount(target, flag)
|
||||||
|
}
|
11
containerd/vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go
generated
vendored
Normal file
11
containerd/vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// +build !linux,!freebsd freebsd,!cgo
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmount(target string, flag int) error {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
40
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo.go
generated
vendored
Normal file
40
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
// Info reveals information about a particular mounted filesystem. This
|
||||||
|
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
||||||
|
type Info struct {
|
||||||
|
// ID is a unique identifier of the mount (may be reused after umount).
|
||||||
|
ID int
|
||||||
|
|
||||||
|
// Parent indicates the ID of the mount parent (or of self for the top of the
|
||||||
|
// mount tree).
|
||||||
|
Parent int
|
||||||
|
|
||||||
|
// Major indicates one half of the device ID which identifies the device class.
|
||||||
|
Major int
|
||||||
|
|
||||||
|
// Minor indicates one half of the device ID which identifies a specific
|
||||||
|
// instance of device.
|
||||||
|
Minor int
|
||||||
|
|
||||||
|
// Root of the mount within the filesystem.
|
||||||
|
Root string
|
||||||
|
|
||||||
|
// Mountpoint indicates the mount point relative to the process's root.
|
||||||
|
Mountpoint string
|
||||||
|
|
||||||
|
// Opts represents mount-specific options.
|
||||||
|
Opts string
|
||||||
|
|
||||||
|
// Optional represents optional fields.
|
||||||
|
Optional string
|
||||||
|
|
||||||
|
// Fstype indicates the type of filesystem, such as EXT3.
|
||||||
|
Fstype string
|
||||||
|
|
||||||
|
// Source indicates filesystem specific information or "none".
|
||||||
|
Source string
|
||||||
|
|
||||||
|
// VfsOpts represents per super block options.
|
||||||
|
VfsOpts string
|
||||||
|
}
|
41
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go
generated
vendored
Normal file
41
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/ucred.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
||||||
|
// bind mounts.
|
||||||
|
func parseMountTable() ([]*Info, error) {
|
||||||
|
var rawEntries *C.struct_statfs
|
||||||
|
|
||||||
|
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
|
||||||
|
if count == 0 {
|
||||||
|
return nil, fmt.Errorf("Failed to call getmntinfo")
|
||||||
|
}
|
||||||
|
|
||||||
|
var entries []C.struct_statfs
|
||||||
|
header := (*reflect.SliceHeader)(unsafe.Pointer(&entries))
|
||||||
|
header.Cap = count
|
||||||
|
header.Len = count
|
||||||
|
header.Data = uintptr(unsafe.Pointer(rawEntries))
|
||||||
|
|
||||||
|
var out []*Info
|
||||||
|
for _, entry := range entries {
|
||||||
|
var mountinfo Info
|
||||||
|
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
|
||||||
|
mountinfo.Source = C.GoString(&entry.f_mntfromname[0])
|
||||||
|
mountinfo.Fstype = C.GoString(&entry.f_fstypename[0])
|
||||||
|
out = append(out, &mountinfo)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
95
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go
generated
vendored
Normal file
95
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
/* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||||
|
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
|
||||||
|
|
||||||
|
(1) mount ID: unique identifier of the mount (may be reused after umount)
|
||||||
|
(2) parent ID: ID of parent (or of self for the top of the mount tree)
|
||||||
|
(3) major:minor: value of st_dev for files on filesystem
|
||||||
|
(4) root: root of the mount within the filesystem
|
||||||
|
(5) mount point: mount point relative to the process's root
|
||||||
|
(6) mount options: per mount options
|
||||||
|
(7) optional fields: zero or more fields of the form "tag[:value]"
|
||||||
|
(8) separator: marks the end of the optional fields
|
||||||
|
(9) filesystem type: name of filesystem of the form "type[.subtype]"
|
||||||
|
(10) mount source: filesystem specific information or "none"
|
||||||
|
(11) super options: per super block options*/
|
||||||
|
mountinfoFormat = "%d %d %d:%d %s %s %s %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
||||||
|
// bind mounts
|
||||||
|
func parseMountTable() ([]*Info, error) {
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return parseInfoFile(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInfoFile(r io.Reader) ([]*Info, error) {
|
||||||
|
var (
|
||||||
|
s = bufio.NewScanner(r)
|
||||||
|
out = []*Info{}
|
||||||
|
)
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
p = &Info{}
|
||||||
|
text = s.Text()
|
||||||
|
optionalFields string
|
||||||
|
)
|
||||||
|
|
||||||
|
if _, err := fmt.Sscanf(text, mountinfoFormat,
|
||||||
|
&p.ID, &p.Parent, &p.Major, &p.Minor,
|
||||||
|
&p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil {
|
||||||
|
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
|
||||||
|
}
|
||||||
|
// Safe as mountinfo encodes mountpoints with spaces as \040.
|
||||||
|
index := strings.Index(text, " - ")
|
||||||
|
postSeparatorFields := strings.Fields(text[index+3:])
|
||||||
|
if len(postSeparatorFields) < 3 {
|
||||||
|
return nil, fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if optionalFields != "-" {
|
||||||
|
p.Optional = optionalFields
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Fstype = postSeparatorFields[0]
|
||||||
|
p.Source = postSeparatorFields[1]
|
||||||
|
p.VfsOpts = strings.Join(postSeparatorFields[2:], " ")
|
||||||
|
out = append(out, p)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidMountInfo collects the mounts for a specific process ID. If the process
|
||||||
|
// ID is unknown, it is better to use `GetMounts` which will inspect
|
||||||
|
// "/proc/self/mountinfo" instead.
|
||||||
|
func PidMountInfo(pid int) ([]*Info, error) {
|
||||||
|
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return parseInfoFile(f)
|
||||||
|
}
|
12
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go
generated
vendored
Normal file
12
containerd/vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// +build !linux,!freebsd freebsd,!cgo
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseMountTable() ([]*Info, error) {
|
||||||
|
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
70
containerd/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go
generated
vendored
Normal file
70
containerd/vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeShared(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "shared")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeRShared(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "rshared")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakePrivate(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "private")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
|
||||||
|
// enabled. See the supported options in flags.go for further reference.
|
||||||
|
func MakeRPrivate(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "rprivate")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeSlave(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "slave")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeRSlave(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "rslave")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
|
||||||
|
// enabled. See the supported options in flags.go for further reference.
|
||||||
|
func MakeUnbindable(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "unbindable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
|
||||||
|
// option enabled. See the supported options in flags.go for further reference.
|
||||||
|
func MakeRUnbindable(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, "runbindable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureMountedAs(mountPoint, options string) error {
|
||||||
|
mounted, err := Mounted(mountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mounted {
|
||||||
|
if err := Mount(mountPoint, mountPoint, "none", "bind,rw"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mounted, err = Mounted(mountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ForceMount("", mountPoint, "none", options)
|
||||||
|
}
|
191
containerd/vendor/github.com/docker/docker/pkg/symlink/LICENSE.APACHE
generated
vendored
Normal file
191
containerd/vendor/github.com/docker/docker/pkg/symlink/LICENSE.APACHE
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-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
|
||||||
|
|
||||||
|
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.
|
27
containerd/vendor/github.com/docker/docker/pkg/symlink/LICENSE.BSD
generated
vendored
Normal file
27
containerd/vendor/github.com/docker/docker/pkg/symlink/LICENSE.BSD
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2014-2015 The Docker & Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
containerd/vendor/github.com/docker/docker/pkg/symlink/README.md
generated
vendored
Normal file
6
containerd/vendor/github.com/docker/docker/pkg/symlink/README.md
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Package symlink implements EvalSymlinksInScope which is an extension of filepath.EvalSymlinks,
|
||||||
|
as well as a Windows long-path aware version of filepath.EvalSymlinks
|
||||||
|
from the [Go standard library](https://golang.org/pkg/path/filepath).
|
||||||
|
|
||||||
|
The code from filepath.EvalSymlinks has been adapted in fs.go.
|
||||||
|
Please read the LICENSE.BSD file that governs fs.go and LICENSE.APACHE for fs_test.go.
|
143
containerd/vendor/github.com/docker/docker/pkg/symlink/fs.go
generated
vendored
Normal file
143
containerd/vendor/github.com/docker/docker/pkg/symlink/fs.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright 2012 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.BSD file.
|
||||||
|
|
||||||
|
// This code is a modified version of path/filepath/symlink.go from the Go standard library.
|
||||||
|
|
||||||
|
package symlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
|
||||||
|
// absolute path. This function handles paths in a platform-agnostic manner.
|
||||||
|
func FollowSymlinkInScope(path, root string) (string, error) {
|
||||||
|
path, err := filepath.Abs(filepath.FromSlash(path))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
root, err = filepath.Abs(filepath.FromSlash(root))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return evalSymlinksInScope(path, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return
|
||||||
|
// a result guaranteed to be contained within the scope `root`, at the time of the call.
|
||||||
|
// Symlinks in `root` are not evaluated and left as-is.
|
||||||
|
// Errors encountered while attempting to evaluate symlinks in path will be returned.
|
||||||
|
// Non-existing paths are valid and do not constitute an error.
|
||||||
|
// `path` has to contain `root` as a prefix, or else an error will be returned.
|
||||||
|
// Trying to break out from `root` does not constitute an error.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// If /foo/bar -> /outside,
|
||||||
|
// FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/oustide"
|
||||||
|
//
|
||||||
|
// IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks
|
||||||
|
// are created and not to create subsequently, additional symlinks that could potentially make a
|
||||||
|
// previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo")
|
||||||
|
// would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should
|
||||||
|
// no longer be considered safely contained in "/foo".
|
||||||
|
func evalSymlinksInScope(path, root string) (string, error) {
|
||||||
|
root = filepath.Clean(root)
|
||||||
|
if path == root {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(path, root) {
|
||||||
|
return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
|
||||||
|
}
|
||||||
|
const maxIter = 255
|
||||||
|
originalPath := path
|
||||||
|
// given root of "/a" and path of "/a/b/../../c" we want path to be "/b/../../c"
|
||||||
|
path = path[len(root):]
|
||||||
|
if root == string(filepath.Separator) {
|
||||||
|
path = string(filepath.Separator) + path
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(path, string(filepath.Separator)) {
|
||||||
|
return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
|
||||||
|
}
|
||||||
|
path = filepath.Clean(path)
|
||||||
|
// consume path by taking each frontmost path element,
|
||||||
|
// expanding it if it's a symlink, and appending it to b
|
||||||
|
var b bytes.Buffer
|
||||||
|
// b here will always be considered to be the "current absolute path inside
|
||||||
|
// root" when we append paths to it, we also append a slash and use
|
||||||
|
// filepath.Clean after the loop to trim the trailing slash
|
||||||
|
for n := 0; path != ""; n++ {
|
||||||
|
if n > maxIter {
|
||||||
|
return "", errors.New("evalSymlinksInScope: too many links in " + originalPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find next path component, p
|
||||||
|
i := strings.IndexRune(path, filepath.Separator)
|
||||||
|
var p string
|
||||||
|
if i == -1 {
|
||||||
|
p, path = path, ""
|
||||||
|
} else {
|
||||||
|
p, path = path[:i], path[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// this takes a b.String() like "b/../" and a p like "c" and turns it
|
||||||
|
// into "/b/../c" which then gets filepath.Cleaned into "/c" and then
|
||||||
|
// root gets prepended and we Clean again (to remove any trailing slash
|
||||||
|
// if the first Clean gave us just "/")
|
||||||
|
cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p)
|
||||||
|
if cleanP == string(filepath.Separator) {
|
||||||
|
// never Lstat "/" itself
|
||||||
|
b.Reset()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fullP := filepath.Clean(root + cleanP)
|
||||||
|
|
||||||
|
fi, err := os.Lstat(fullP)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// if p does not exist, accept it
|
||||||
|
b.WriteString(p)
|
||||||
|
b.WriteRune(filepath.Separator)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSymlink == 0 {
|
||||||
|
b.WriteString(p + string(filepath.Separator))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's a symlink, put it at the front of path
|
||||||
|
dest, err := os.Readlink(fullP)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if system.IsAbs(dest) {
|
||||||
|
b.Reset()
|
||||||
|
}
|
||||||
|
path = dest + string(filepath.Separator) + path
|
||||||
|
}
|
||||||
|
|
||||||
|
// see note above on "fullP := ..." for why this is double-cleaned and
|
||||||
|
// what's happening here
|
||||||
|
return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalSymlinks returns the path name after the evaluation of any symbolic
|
||||||
|
// links.
|
||||||
|
// If path is relative the result will be relative to the current directory,
|
||||||
|
// unless one of the components is an absolute symbolic link.
|
||||||
|
// This version has been updated to support long paths prepended with `\\?\`.
|
||||||
|
func EvalSymlinks(path string) (string, error) {
|
||||||
|
return evalSymlinks(path)
|
||||||
|
}
|
11
containerd/vendor/github.com/docker/docker/pkg/symlink/fs_unix.go
generated
vendored
Normal file
11
containerd/vendor/github.com/docker/docker/pkg/symlink/fs_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package symlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func evalSymlinks(path string) (string, error) {
|
||||||
|
return filepath.EvalSymlinks(path)
|
||||||
|
}
|
156
containerd/vendor/github.com/docker/docker/pkg/symlink/fs_windows.go
generated
vendored
Normal file
156
containerd/vendor/github.com/docker/docker/pkg/symlink/fs_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
package symlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/longpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func toShort(path string) (string, error) {
|
||||||
|
p, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := p // GetShortPathName says we can reuse buffer
|
||||||
|
n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n > uint32(len(b)) {
|
||||||
|
b = make([]uint16, n)
|
||||||
|
n, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syscall.UTF16ToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLong(path string) (string, error) {
|
||||||
|
p, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := p // GetLongPathName says we can reuse buffer
|
||||||
|
n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n > uint32(len(b)) {
|
||||||
|
b = make([]uint16, n)
|
||||||
|
n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b = b[:n]
|
||||||
|
return syscall.UTF16ToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalSymlinks(path string) (string, error) {
|
||||||
|
path, err := walkSymlinks(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := toShort(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
p, err = toLong(p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// syscall.GetLongPathName does not change the case of the drive letter,
|
||||||
|
// but the result of EvalSymlinks must be unique, so we have
|
||||||
|
// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
|
||||||
|
// Make drive letter upper case.
|
||||||
|
if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
|
||||||
|
p = string(p[0]+'A'-'a') + p[1:]
|
||||||
|
} else if len(p) >= 6 && p[5] == ':' && 'a' <= p[4] && p[4] <= 'z' {
|
||||||
|
p = p[:3] + string(p[4]+'A'-'a') + p[5:]
|
||||||
|
}
|
||||||
|
return filepath.Clean(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const utf8RuneSelf = 0x80
|
||||||
|
|
||||||
|
func walkSymlinks(path string) (string, error) {
|
||||||
|
const maxIter = 255
|
||||||
|
originalPath := path
|
||||||
|
// consume path by taking each frontmost path element,
|
||||||
|
// expanding it if it's a symlink, and appending it to b
|
||||||
|
var b bytes.Buffer
|
||||||
|
for n := 0; path != ""; n++ {
|
||||||
|
if n > maxIter {
|
||||||
|
return "", errors.New("EvalSymlinks: too many links in " + originalPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A path beginnging with `\\?\` represents the root, so automatically
|
||||||
|
// skip that part and begin processing the next segment.
|
||||||
|
if strings.HasPrefix(path, longpath.Prefix) {
|
||||||
|
b.WriteString(longpath.Prefix)
|
||||||
|
path = path[4:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// find next path component, p
|
||||||
|
var i = -1
|
||||||
|
for j, c := range path {
|
||||||
|
if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) {
|
||||||
|
i = j
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var p string
|
||||||
|
if i == -1 {
|
||||||
|
p, path = path, ""
|
||||||
|
} else {
|
||||||
|
p, path = path[:i], path[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == "" {
|
||||||
|
if b.Len() == 0 {
|
||||||
|
// must be absolute path
|
||||||
|
b.WriteRune(filepath.Separator)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the first segment after the long path prefix, accept the
|
||||||
|
// current segment as a volume root or UNC share and move on to the next.
|
||||||
|
if b.String() == longpath.Prefix {
|
||||||
|
b.WriteString(p)
|
||||||
|
b.WriteRune(filepath.Separator)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Lstat(b.String() + p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSymlink == 0 {
|
||||||
|
b.WriteString(p)
|
||||||
|
if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') {
|
||||||
|
b.WriteRune(filepath.Separator)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's a symlink, put it at the front of path
|
||||||
|
dest, err := os.Readlink(b.String() + p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if filepath.IsAbs(dest) || os.IsPathSeparator(dest[0]) {
|
||||||
|
b.Reset()
|
||||||
|
}
|
||||||
|
path = dest + string(filepath.Separator) + path
|
||||||
|
}
|
||||||
|
return filepath.Clean(b.String()), nil
|
||||||
|
}
|
31
containerd/vendor/github.com/docker/docker/pkg/system/chtimes.go
generated
vendored
Normal file
31
containerd/vendor/github.com/docker/docker/pkg/system/chtimes.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Chtimes changes the access time and modified time of a file at the given path
|
||||||
|
func Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||||
|
unixMinTime := time.Unix(0, 0)
|
||||||
|
// The max Unix time is 33 bits set
|
||||||
|
unixMaxTime := unixMinTime.Add((1<<33 - 1) * time.Second)
|
||||||
|
|
||||||
|
// If the modified time is prior to the Unix Epoch, or after the
|
||||||
|
// end of Unix Time, os.Chtimes has undefined behavior
|
||||||
|
// default to Unix Epoch in this case, just in case
|
||||||
|
|
||||||
|
if atime.Before(unixMinTime) || atime.After(unixMaxTime) {
|
||||||
|
atime = unixMinTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if mtime.Before(unixMinTime) || mtime.After(unixMaxTime) {
|
||||||
|
mtime = unixMinTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Chtimes(name, atime, mtime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
10
containerd/vendor/github.com/docker/docker/pkg/system/errors.go
generated
vendored
Normal file
10
containerd/vendor/github.com/docker/docker/pkg/system/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNotSupportedPlatform means the platform is not supported.
|
||||||
|
ErrNotSupportedPlatform = errors.New("platform and architecture is not supported")
|
||||||
|
)
|
83
containerd/vendor/github.com/docker/docker/pkg/system/events_windows.go
generated
vendored
Normal file
83
containerd/vendor/github.com/docker/docker/pkg/system/events_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
// This file implements syscalls for Win32 events which are not implemented
|
||||||
|
// in golang.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
procCreateEvent = modkernel32.NewProc("CreateEventW")
|
||||||
|
procOpenEvent = modkernel32.NewProc("OpenEventW")
|
||||||
|
procSetEvent = modkernel32.NewProc("SetEvent")
|
||||||
|
procResetEvent = modkernel32.NewProc("ResetEvent")
|
||||||
|
procPulseEvent = modkernel32.NewProc("PulseEvent")
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateEvent implements win32 CreateEventW func in golang. It will create an event object.
|
||||||
|
func CreateEvent(eventAttributes *syscall.SecurityAttributes, manualReset bool, initialState bool, name string) (handle syscall.Handle, err error) {
|
||||||
|
namep, _ := syscall.UTF16PtrFromString(name)
|
||||||
|
var _p1 uint32
|
||||||
|
if manualReset {
|
||||||
|
_p1 = 1
|
||||||
|
}
|
||||||
|
var _p2 uint32
|
||||||
|
if initialState {
|
||||||
|
_p2 = 1
|
||||||
|
}
|
||||||
|
r0, _, e1 := procCreateEvent.Call(uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(namep)))
|
||||||
|
use(unsafe.Pointer(namep))
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenEvent implements win32 OpenEventW func in golang. It opens an event object.
|
||||||
|
func OpenEvent(desiredAccess uint32, inheritHandle bool, name string) (handle syscall.Handle, err error) {
|
||||||
|
namep, _ := syscall.UTF16PtrFromString(name)
|
||||||
|
var _p1 uint32
|
||||||
|
if inheritHandle {
|
||||||
|
_p1 = 1
|
||||||
|
}
|
||||||
|
r0, _, e1 := procOpenEvent.Call(uintptr(desiredAccess), uintptr(_p1), uintptr(unsafe.Pointer(namep)))
|
||||||
|
use(unsafe.Pointer(namep))
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEvent implements win32 SetEvent func in golang.
|
||||||
|
func SetEvent(handle syscall.Handle) (err error) {
|
||||||
|
return setResetPulse(handle, procSetEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetEvent implements win32 ResetEvent func in golang.
|
||||||
|
func ResetEvent(handle syscall.Handle) (err error) {
|
||||||
|
return setResetPulse(handle, procResetEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PulseEvent implements win32 PulseEvent func in golang.
|
||||||
|
func PulseEvent(handle syscall.Handle) (err error) {
|
||||||
|
return setResetPulse(handle, procPulseEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setResetPulse(handle syscall.Handle, proc *syscall.LazyProc) (err error) {
|
||||||
|
r0, _, _ := proc.Call(uintptr(handle))
|
||||||
|
if r0 != 0 {
|
||||||
|
err = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var temp unsafe.Pointer
|
||||||
|
|
||||||
|
// use ensures a variable is kept alive without the GC freeing while still needed
|
||||||
|
func use(p unsafe.Pointer) {
|
||||||
|
temp = p
|
||||||
|
}
|
19
containerd/vendor/github.com/docker/docker/pkg/system/filesys.go
generated
vendored
Normal file
19
containerd/vendor/github.com/docker/docker/pkg/system/filesys.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MkdirAll creates a directory named path along with any necessary parents,
|
||||||
|
// with permission specified by attribute perm for all dir created.
|
||||||
|
func MkdirAll(path string, perm os.FileMode) error {
|
||||||
|
return os.MkdirAll(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAbs is a platform-specific wrapper for filepath.IsAbs.
|
||||||
|
func IsAbs(path string) bool {
|
||||||
|
return filepath.IsAbs(path)
|
||||||
|
}
|
82
containerd/vendor/github.com/docker/docker/pkg/system/filesys_windows.go
generated
vendored
Normal file
82
containerd/vendor/github.com/docker/docker/pkg/system/filesys_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MkdirAll implementation that is volume path aware for Windows.
|
||||||
|
func MkdirAll(path string, perm os.FileMode) error {
|
||||||
|
if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest of this method is copied from os.MkdirAll and should be kept
|
||||||
|
// as-is to ensure compatibility.
|
||||||
|
|
||||||
|
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
||||||
|
dir, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
if dir.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &os.PathError{
|
||||||
|
Op: "mkdir",
|
||||||
|
Path: path,
|
||||||
|
Err: syscall.ENOTDIR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path: make sure parent exists and then call Mkdir for path.
|
||||||
|
i := len(path)
|
||||||
|
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
|
j := i
|
||||||
|
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
|
||||||
|
if j > 1 {
|
||||||
|
// Create parent
|
||||||
|
err = MkdirAll(path[0:j-1], perm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent now exists; invoke Mkdir and use its result.
|
||||||
|
err = os.Mkdir(path, perm)
|
||||||
|
if err != nil {
|
||||||
|
// Handle arguments like "foo/." by
|
||||||
|
// double-checking that directory doesn't exist.
|
||||||
|
dir, err1 := os.Lstat(path)
|
||||||
|
if err1 == nil && dir.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows,
|
||||||
|
// golang filepath.IsAbs does not consider a path \windows\system32 as absolute
|
||||||
|
// as it doesn't start with a drive-letter/colon combination. However, in
|
||||||
|
// docker we need to verify things such as WORKDIR /windows/system32 in
|
||||||
|
// a Dockerfile (which gets translated to \windows\system32 when being processed
|
||||||
|
// by the daemon. This SHOULD be treated as absolute from a docker processing
|
||||||
|
// perspective.
|
||||||
|
func IsAbs(path string) bool {
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
if !strings.HasPrefix(path, string(os.PathSeparator)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
19
containerd/vendor/github.com/docker/docker/pkg/system/lstat.go
generated
vendored
Normal file
19
containerd/vendor/github.com/docker/docker/pkg/system/lstat.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lstat takes a path to a file and returns
|
||||||
|
// a system.StatT type pertaining to that file.
|
||||||
|
//
|
||||||
|
// Throws an error if the file does not exist
|
||||||
|
func Lstat(path string) (*StatT, error) {
|
||||||
|
s := &syscall.Stat_t{}
|
||||||
|
if err := syscall.Lstat(path, s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return fromStatT(s)
|
||||||
|
}
|
25
containerd/vendor/github.com/docker/docker/pkg/system/lstat_windows.go
generated
vendored
Normal file
25
containerd/vendor/github.com/docker/docker/pkg/system/lstat_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lstat calls os.Lstat to get a fileinfo interface back.
|
||||||
|
// This is then copied into our own locally defined structure.
|
||||||
|
// Note the Linux version uses fromStatT to do the copy back,
|
||||||
|
// but that not strictly necessary when already in an OS specific module.
|
||||||
|
func Lstat(path string) (*StatT, error) {
|
||||||
|
fi, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &StatT{
|
||||||
|
name: fi.Name(),
|
||||||
|
size: fi.Size(),
|
||||||
|
mode: fi.Mode(),
|
||||||
|
modTime: fi.ModTime(),
|
||||||
|
isDir: fi.IsDir()}, nil
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue