Update runtime-spec to v1.0.0.rc5

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
Mrunal Patel 2017-04-12 16:12:04 -07:00 committed by Mrunal Patel
parent b6c022caae
commit 24df2538db
166 changed files with 3544 additions and 15322 deletions

View File

@ -43,13 +43,12 @@ RUN mkdir -p /usr/src/criu \
&& rm -rf /usr/src/criu
# Install runc
# TODO: This should actually be v1.0.0-rc3 but we first need to switch to
# v1.0.0-rc5 runtime config generation.
ENV RUNC_COMMIT 31980a53ae7887b2c8f8715d13c3eb486c27b6cf
ENV RUNC_COMMIT v1.0.0-rc3
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git fetch origin --tags \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="seccomp selinux" \
&& cp runc /usr/local/bin/runc \

View File

@ -1,5 +1,5 @@
{
"memo": "1290be673a75036ce5bea81021073dd7041dc3f421446912b6b7ae0ed511fe93",
"memo": "0d3077faf280e4e13e18e56f085053d4ced593c2fcfcb09d7df1aea8f0bba403",
"projects": [
{
"name": "github.com/BurntSushi/toml",
@ -35,6 +35,14 @@
"."
]
},
{
"name": "github.com/blang/semver",
"version": "v3.5.0",
"revision": "b38d23b8782a487059e8fc8773e9a5b228a77cb6",
"packages": [
"."
]
},
{
"name": "github.com/containernetworking/cni",
"version": "v0.4.0",
@ -325,8 +333,8 @@
},
{
"name": "github.com/opencontainers/runtime-spec",
"branch": "master",
"revision": "bb6925ea99f0e366a3f7d1c975f6577475ca25f0",
"version": "v1.0.0-rc5",
"revision": "035da1dca3dfbb00d752eb58b0b158d6129f3776",
"packages": [
"specs-go"
]
@ -334,10 +342,11 @@
{
"name": "github.com/opencontainers/runtime-tools",
"branch": "master",
"revision": "2d92f6557e64d4f9a0e799a75fdf153cec13dffa",
"revision": "18a122b45a71765b09c6a451008a63687040b74a",
"packages": [
"generate",
"generate/seccomp"
"generate/seccomp",
"validate"
]
},
{

View File

@ -22,6 +22,9 @@
"branch": "master"
},
"github.com/opencontainers/runtime-spec": {
"version": "v1.0.0-rc5"
},
"github.com/opencontainers/runtime-tools": {
"branch": "master"
},
"github.com/opencontainers/selinux": {

View File

@ -365,7 +365,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
cpuQuota := resources.CpuQuota
if cpuQuota != 0 {
specgen.SetLinuxResourcesCPUQuota(uint64(cpuQuota))
specgen.SetLinuxResourcesCPUQuota(cpuQuota)
}
cpuShares := resources.CpuShares

View File

@ -75,7 +75,7 @@ func setupSeccomp(config *Seccomp, specgen *generate.Generator) error {
}
customspec := specgen.Spec()
customspec.Linux.Seccomp = &specs.Seccomp{}
customspec.Linux.Seccomp = &specs.LinuxSeccomp{}
// if config.Architectures == 0 then libseccomp will figure out the architecture to use
if len(config.Architectures) != 0 {
@ -99,7 +99,7 @@ func setupSeccomp(config *Seccomp, specgen *generate.Generator) error {
}
}
customspec.Linux.Seccomp.DefaultAction = specs.Action(config.DefaultAction)
customspec.Linux.Seccomp.DefaultAction = specs.LinuxSeccompAction(config.DefaultAction)
Loop:
// Loop through all syscall blocks and convert them to libcontainer format after filtering them
@ -111,7 +111,7 @@ Loop:
}
if len(call.Excludes.Caps) > 0 {
for _, c := range call.Excludes.Caps {
if stringutils.InSlice(customspec.Process.Capabilities, c) {
if stringutils.InSlice(customspec.Process.Capabilities.Permitted, c) {
continue Loop
}
}
@ -123,7 +123,7 @@ Loop:
}
if len(call.Includes.Caps) > 0 {
for _, c := range call.Includes.Caps {
if !stringutils.InSlice(customspec.Process.Capabilities, c) {
if !stringutils.InSlice(customspec.Process.Capabilities.Permitted, c) {
continue Loop
}
}
@ -145,19 +145,19 @@ Loop:
return nil
}
func createSpecsSyscall(name string, action Action, args []*Arg) specs.Syscall {
newCall := specs.Syscall{
Name: name,
Action: specs.Action(action),
func createSpecsSyscall(name string, action Action, args []*Arg) specs.LinuxSyscall {
newCall := specs.LinuxSyscall{
Names: []string{name},
Action: specs.LinuxSeccompAction(action),
}
// Loop through all the arguments of the syscall and convert them
for _, arg := range args {
newArg := specs.Arg{
newArg := specs.LinuxSeccompArg{
Index: arg.Index,
Value: arg.Value,
ValueTwo: arg.ValueTwo,
Op: specs.Operator(arg.Op),
Op: specs.LinuxSeccompOperator(arg.Op),
}
newCall.Args = append(newCall.Args, newArg)

1
vendor/github.com/blang/semver/.gx/lastpubver generated vendored Normal file
View File

@ -0,0 +1 @@
3.4.0: QmZTgGMg34JKEvF1hjr7wwYESvFhg9Khv2WFibDAi5dhno

View File

@ -42,4 +42,8 @@ func TestJSONUnmarshal(t *testing.T) {
if err := json.Unmarshal([]byte(badVersionString), &v); err == nil {
t.Fatal("expected JSON unmarshal error, got nil")
}
if err := json.Unmarshal([]byte("3.1"), &v); err == nil {
t.Fatal("expected JSON unmarshal error, got nil")
}
}

17
vendor/github.com/blang/semver/package.json generated vendored Normal file
View File

@ -0,0 +1,17 @@
{
"author": "blang",
"bugs": {
"URL": "https://github.com/blang/semver/issues",
"url": "https://github.com/blang/semver/issues"
},
"gx": {
"dvcsimport": "github.com/blang/semver"
},
"gxVersion": "0.10.0",
"language": "go",
"license": "MIT",
"name": "semver",
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
"version": "3.4.0"
}

View File

@ -2,10 +2,33 @@ package semver
import (
"fmt"
"strconv"
"strings"
"unicode"
)
type wildcardType int
const (
noneWildcard wildcardType = iota
majorWildcard wildcardType = 1
minorWildcard wildcardType = 2
patchWildcard wildcardType = 3
)
func wildcardTypefromInt(i int) wildcardType {
switch i {
case 1:
return majorWildcard
case 2:
return minorWildcard
case 3:
return patchWildcard
default:
return noneWildcard
}
}
type comparator func(Version, Version) bool
var (
@ -92,8 +115,12 @@ func ParseRange(s string) (Range, error) {
if err != nil {
return nil, err
}
expandedParts, err := expandWildcardVersion(orParts)
if err != nil {
return nil, err
}
var orFn Range
for _, p := range orParts {
for _, p := range expandedParts {
var andFn Range
for _, ap := range p {
opStr, vStr, err := splitComparatorVersion(ap)
@ -164,20 +191,39 @@ func buildVersionRange(opStr, vStr string) (*versionRange, error) {
}
// splitAndTrim splits a range string by spaces and cleans leading and trailing spaces
// inArray checks if a byte is contained in an array of bytes
func inArray(s byte, list []byte) bool {
for _, el := range list {
if el == s {
return true
}
}
return false
}
// splitAndTrim splits a range string by spaces and cleans whitespaces
func splitAndTrim(s string) (result []string) {
last := 0
var lastChar byte
excludeFromSplit := []byte{'>', '<', '='}
for i := 0; i < len(s); i++ {
if s[i] == ' ' {
if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
if last < i-1 {
result = append(result, s[last:i])
}
last = i + 1
} else if s[i] != ' ' {
lastChar = s[i]
}
}
if last < len(s)-1 {
result = append(result, s[last:])
}
for i, v := range result {
result[i] = strings.Replace(v, " ", "", -1)
}
// parts := strings.Split(s, " ")
// for _, x := range parts {
// if s := strings.TrimSpace(x); len(s) != 0 {
@ -188,7 +234,6 @@ func splitAndTrim(s string) (result []string) {
}
// splitComparatorVersion splits the comparator from the version.
// Spaces between the comparator and the version are not allowed.
// Input must be free of leading or trailing spaces.
func splitComparatorVersion(s string) (string, string, error) {
i := strings.IndexFunc(s, unicode.IsDigit)
@ -198,6 +243,144 @@ func splitComparatorVersion(s string) (string, string, error) {
return strings.TrimSpace(s[0:i]), s[i:], nil
}
// getWildcardType will return the type of wildcard that the
// passed version contains
func getWildcardType(vStr string) wildcardType {
parts := strings.Split(vStr, ".")
nparts := len(parts)
wildcard := parts[nparts-1]
possibleWildcardType := wildcardTypefromInt(nparts)
if wildcard == "x" {
return possibleWildcardType
}
return noneWildcard
}
// createVersionFromWildcard will convert a wildcard version
// into a regular version, replacing 'x's with '0's, handling
// special cases like '1.x.x' and '1.x'
func createVersionFromWildcard(vStr string) string {
// handle 1.x.x
vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
parts := strings.Split(vStr2, ".")
// handle 1.x
if len(parts) == 2 {
return vStr2 + ".0"
}
return vStr2
}
// incrementMajorVersion will increment the major version
// of the passed version
func incrementMajorVersion(vStr string) (string, error) {
parts := strings.Split(vStr, ".")
i, err := strconv.Atoi(parts[0])
if err != nil {
return "", err
}
parts[0] = strconv.Itoa(i + 1)
return strings.Join(parts, "."), nil
}
// incrementMajorVersion will increment the minor version
// of the passed version
func incrementMinorVersion(vStr string) (string, error) {
parts := strings.Split(vStr, ".")
i, err := strconv.Atoi(parts[1])
if err != nil {
return "", err
}
parts[1] = strconv.Itoa(i + 1)
return strings.Join(parts, "."), nil
}
// expandWildcardVersion will expand wildcards inside versions
// following these rules:
//
// * when dealing with patch wildcards:
// >= 1.2.x will become >= 1.2.0
// <= 1.2.x will become < 1.3.0
// > 1.2.x will become >= 1.3.0
// < 1.2.x will become < 1.2.0
// != 1.2.x will become < 1.2.0 >= 1.3.0
//
// * when dealing with minor wildcards:
// >= 1.x will become >= 1.0.0
// <= 1.x will become < 2.0.0
// > 1.x will become >= 2.0.0
// < 1.0 will become < 1.0.0
// != 1.x will become < 1.0.0 >= 2.0.0
//
// * when dealing with wildcards without
// version operator:
// 1.2.x will become >= 1.2.0 < 1.3.0
// 1.x will become >= 1.0.0 < 2.0.0
func expandWildcardVersion(parts [][]string) ([][]string, error) {
var expandedParts [][]string
for _, p := range parts {
var newParts []string
for _, ap := range p {
if strings.Index(ap, "x") != -1 {
opStr, vStr, err := splitComparatorVersion(ap)
if err != nil {
return nil, err
}
versionWildcardType := getWildcardType(vStr)
flatVersion := createVersionFromWildcard(vStr)
var resultOperator string
var shouldIncrementVersion bool
switch opStr {
case ">":
resultOperator = ">="
shouldIncrementVersion = true
case ">=":
resultOperator = ">="
case "<":
resultOperator = "<"
case "<=":
resultOperator = "<"
shouldIncrementVersion = true
case "", "=", "==":
newParts = append(newParts, ">="+flatVersion)
resultOperator = "<"
shouldIncrementVersion = true
case "!=", "!":
newParts = append(newParts, "<"+flatVersion)
resultOperator = ">="
shouldIncrementVersion = true
}
var resultVersion string
if shouldIncrementVersion {
switch versionWildcardType {
case patchWildcard:
resultVersion, _ = incrementMinorVersion(flatVersion)
case minorWildcard:
resultVersion, _ = incrementMajorVersion(flatVersion)
}
} else {
resultVersion = flatVersion
}
ap = resultOperator + resultVersion
}
newParts = append(newParts, ap)
}
expandedParts = append(expandedParts, newParts)
}
return expandedParts, nil
}
func parseComparator(s string) comparator {
switch s {
case "==":
@ -222,3 +405,12 @@ func parseComparator(s string) comparator {
return nil
}
// MustParseRange is like ParseRange but panics if the range cannot be parsed.
func MustParseRange(s string) Range {
r, err := ParseRange(s)
if err != nil {
panic(`semver: ParseRange(` + s + `): ` + err.Error())
}
return r
}

View File

@ -6,6 +6,11 @@ import (
"testing"
)
type wildcardTypeTest struct {
input string
wildcardType wildcardType
}
type comparatorTest struct {
input string
comparator func(comparator) bool
@ -75,7 +80,8 @@ func TestSplitAndTrim(t *testing.T) {
s []string
}{
{"1.2.3 1.2.3", []string{"1.2.3", "1.2.3"}},
{" 1.2.3 1.2.3 ", []string{"1.2.3", "1.2.3"}}, // Spaces
{" 1.2.3 1.2.3 ", []string{"1.2.3", "1.2.3"}}, // Spaces
{" >= 1.2.3 <= 1.2.3 ", []string{">=1.2.3", "<=1.2.3"}}, // Spaces between operator and version
{"1.2.3 || >=1.2.3 <1.2.3", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
{" 1.2.3 || >=1.2.3 <1.2.3 ", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
}
@ -191,6 +197,103 @@ func TestSplitORParts(t *testing.T) {
}
}
func TestGetWildcardType(t *testing.T) {
wildcardTypeTests := []wildcardTypeTest{
{"x", majorWildcard},
{"1.x", minorWildcard},
{"1.2.x", patchWildcard},
{"fo.o.b.ar", noneWildcard},
}
for _, tc := range wildcardTypeTests {
o := getWildcardType(tc.input)
if o != tc.wildcardType {
t.Errorf("Invalid for case: %q: Expected %q, got: %q", tc.input, tc.wildcardType, o)
}
}
}
func TestCreateVersionFromWildcard(t *testing.T) {
tests := []struct {
i string
s string
}{
{"1.2.x", "1.2.0"},
{"1.x", "1.0.0"},
}
for _, tc := range tests {
p := createVersionFromWildcard(tc.i)
if p != tc.s {
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
}
}
}
func TestIncrementMajorVersion(t *testing.T) {
tests := []struct {
i string
s string
}{
{"1.2.3", "2.2.3"},
{"1.2", "2.2"},
{"foo.bar", ""},
}
for _, tc := range tests {
p, _ := incrementMajorVersion(tc.i)
if p != tc.s {
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
}
}
}
func TestIncrementMinorVersion(t *testing.T) {
tests := []struct {
i string
s string
}{
{"1.2.3", "1.3.3"},
{"1.2", "1.3"},
{"foo.bar", ""},
}
for _, tc := range tests {
p, _ := incrementMinorVersion(tc.i)
if p != tc.s {
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
}
}
}
func TestExpandWildcardVersion(t *testing.T) {
tests := []struct {
i [][]string
o [][]string
}{
{[][]string{[]string{"foox"}}, nil},
{[][]string{[]string{">=1.2.x"}}, [][]string{[]string{">=1.2.0"}}},
{[][]string{[]string{"<=1.2.x"}}, [][]string{[]string{"<1.3.0"}}},
{[][]string{[]string{">1.2.x"}}, [][]string{[]string{">=1.3.0"}}},
{[][]string{[]string{"<1.2.x"}}, [][]string{[]string{"<1.2.0"}}},
{[][]string{[]string{"!=1.2.x"}}, [][]string{[]string{"<1.2.0", ">=1.3.0"}}},
{[][]string{[]string{">=1.x"}}, [][]string{[]string{">=1.0.0"}}},
{[][]string{[]string{"<=1.x"}}, [][]string{[]string{"<2.0.0"}}},
{[][]string{[]string{">1.x"}}, [][]string{[]string{">=2.0.0"}}},
{[][]string{[]string{"<1.x"}}, [][]string{[]string{"<1.0.0"}}},
{[][]string{[]string{"!=1.x"}}, [][]string{[]string{"<1.0.0", ">=2.0.0"}}},
{[][]string{[]string{"1.2.x"}}, [][]string{[]string{">=1.2.0", "<1.3.0"}}},
{[][]string{[]string{"1.x"}}, [][]string{[]string{">=1.0.0", "<2.0.0"}}},
}
for _, tc := range tests {
o, _ := expandWildcardVersion(tc.i)
if !reflect.DeepEqual(tc.o, o) {
t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o)
}
}
}
func TestVersionRangeToRange(t *testing.T) {
vr := versionRange{
v: MustParse("1.2.3"),
@ -310,7 +413,7 @@ func TestParseRange(t *testing.T) {
{"1.0", nil},
{"string", nil},
{"", nil},
{"fo.ob.ar.x", nil},
// AND Expressions
{">1.2.2 <1.2.4", []tv{
{"1.2.2", false},
@ -346,6 +449,18 @@ func TestParseRange(t *testing.T) {
{"1.2.3", false},
{"1.2.4", false},
}},
// Wildcard expressions
{">1.x", []tv{
{"0.1.9", false},
{"1.2.6", false},
{"1.9.0", false},
{"2.0.0", true},
}},
{">1.2.x", []tv{
{"1.1.9", false},
{"1.2.6", false},
{"1.3.0", true},
}},
// Combined Expressions
{">1.2.2 <1.2.4 || >=2.0.0", []tv{
{"1.2.2", false},
@ -354,6 +469,13 @@ func TestParseRange(t *testing.T) {
{"2.0.0", true},
{"2.0.1", true},
}},
{"1.x || >=2.0.x <2.2.x", []tv{
{"0.9.2", false},
{"1.2.2", true},
{"2.0.0", true},
{"2.1.8", true},
{"2.2.0", false},
}},
{">1.2.2 <1.2.4 || >=2.0.0 <3.0.0", []tv{
{"1.2.2", false},
{"1.2.3", true},
@ -381,6 +503,23 @@ func TestParseRange(t *testing.T) {
}
}
func TestMustParseRange(t *testing.T) {
testCase := ">1.2.2 <1.2.4 || >=2.0.0 <3.0.0"
r := MustParseRange(testCase)
if !r(MustParse("1.2.3")) {
t.Errorf("Unexpected range behavior on MustParseRange")
}
}
func TestMustParseRange_panic(t *testing.T) {
defer func() {
if recover() == nil {
t.Errorf("Should have panicked")
}
}()
_ = MustParseRange("invalid version")
}
func BenchmarkRangeParseSimple(b *testing.B) {
const VERSION = ">1.0.0"
b.ReportAllocs()

View File

@ -200,6 +200,29 @@ func Make(s string) (Version, error) {
return Parse(s)
}
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
// specs to be parsed by this library. It does so by normalizing versions before passing them to
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
// with only major and minor components specified
func ParseTolerant(s string) (Version, error) {
s = strings.TrimSpace(s)
s = strings.TrimPrefix(s, "v")
// Split into major.minor.(patch+pr+meta)
parts := strings.SplitN(s, ".", 3)
if len(parts) < 3 {
if strings.ContainsAny(parts[len(parts)-1], "+-") {
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
}
for len(parts) < 3 {
parts = append(parts, "0")
}
s = strings.Join(parts, ".")
}
return Parse(s)
}
// Parse parses version string and returns a validated Version or error
func Parse(s string) (Version, error) {
if len(s) == 0 {

View File

@ -30,6 +30,13 @@ var formatTests = []formatTest{
{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"},
}
var tolerantFormatTests = []formatTest{
{Version{1, 2, 3, nil, nil}, "v1.2.3"},
{Version{1, 2, 3, nil, nil}, " 1.2.3 "},
{Version{1, 2, 0, nil, nil}, "1.2"},
{Version{1, 0, 0, nil, nil}, "1"},
}
func TestStringer(t *testing.T) {
for _, test := range formatTests {
if res := test.v.String(); res != test.result {
@ -50,6 +57,18 @@ func TestParse(t *testing.T) {
}
}
func TestParseTolerant(t *testing.T) {
for _, test := range tolerantFormatTests {
if v, err := ParseTolerant(test.result); err != nil {
t.Errorf("Error parsing %q: %q", test.result, err)
} else if comp := v.Compare(test.v); comp != 0 {
t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
} else if err := v.Validate(); err != nil {
t.Errorf("Error validating parsed version %q: %q", test.v, err)
}
}
}
func TestMustParse(t *testing.T) {
_ = MustParse("32.2.1-alpha")
}
@ -184,6 +203,19 @@ func TestWrongFormat(t *testing.T) {
}
}
var wrongTolerantFormatTests = []wrongformatTest{
{nil, "1.0+abc"},
{nil, "1.0-rc.1"},
}
func TestWrongTolerantFormat(t *testing.T) {
for _, test := range wrongTolerantFormatTests {
if res, err := ParseTolerant(test.str); err == nil {
t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
}
}
}
func TestCompareHelper(t *testing.T) {
v := Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}
v1 := Version{1, 0, 0, nil, nil}
@ -319,6 +351,15 @@ func BenchmarkParseAverage(b *testing.B) {
}
}
func BenchmarkParseTolerantAverage(b *testing.B) {
l := len(tolerantFormatTests)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
ParseTolerant(tolerantFormatTests[n%l].result)
}
}
func BenchmarkStringSimple(b *testing.B) {
const VERSION = "0.0.1"
v, _ := Parse(VERSION)

View File

@ -1,4 +1,3 @@
output
schema/validate
code-of-conduct.md
version.md

View File

@ -3,6 +3,8 @@ approve_regex: ^LGTM
reject_regex: ^Rejected
reset_on_push: true
author_approval: ignored
signed_off_by:
required: true
reviewers:
teams:
- runtime-spec-maintainers

View File

@ -1,7 +1,8 @@
language: go
go:
- 1.6
- 1.5.3
- 1.7
- 1.6.3
- 1.5.4
sudo: required
@ -15,7 +16,9 @@ before_install:
install: true
script:
- env | grep TRAVIS_
- make .govet
- make .golint
- make .gitvalidation
- echo "${TRAVIS_COMMIT_RANGE} -> ${TRAVIS_COMMIT_RANGE/.../..} (travis-ci/travis-ci#4596)"
- TRAVIS_COMMIT_RANGE="${TRAVIS_COMMIT_RANGE/.../..}" make .gitvalidation
- make docs

View File

@ -1,5 +1,133 @@
OpenContainers Specifications
Changes with v1.0.0-rc4:
Additions:
* config-linux: Allow negative values for some resources (#648)
* config-linux: Lift no-tweaking namespace restriction (#649)
Removals and increased restrictions:
* config: Rlimit types must be unique (#607)
* config: Forbid empty-string keys in 'annotations' (#645, #654)
* config-linux: Require runtime errors for pre-existing devices
(#647)
* runtime: Only require 'pid' in the state for created/running
statuses (#664)
* schema: Add 'consoleSize' and update requirements (#646)
* schema: Remove string pointers (#656)
* schema/config-linux: Remove blockIODeviceThrottle and other
pointers (#545)
Breaking Go changes:
* specs-go/config: Remove string pointers (#653)
* specs-go/config: Make Spec.Hooks a pointer (#427)
* specs-go/config: Convert some resources from unsigned integers
to signed integers (#648)
Minor fixes and documentation:
* config: Explicitly list 'hooks' as optional and cite POSIX for
'env' and 'args' (#427)
* runtime: Replace "process is stopped" with "process exits"
(#465)
* schema/config-linux: Add missing kernelTCP (#655)
* schema/validate: Allow schema identifiers to contain a URL
scheme (#490)
* .travis: Fix git-validation commit ranges (#216)
* *: Add anchor tags to a number of spec locations (#612, #636,
#637, #638, #639, #640)
* *: Typo fixes and polishing (#643, #650, #652, #656, #660, #665)
Changes with v1.0.0-rc3:
Additions:
* config: Add support for Windows-based containers (#565, #573)
* config: Add process.consoleSize (#563)
* config: Explicitly allow unknown extensions and document
annotations key conventions (#510)
* config: Define mounts entries for Solaris (#588)
Removals and increased restrictions:
* config: Require absolute paths for mount destinations (#609)
* config-linux: Require absolute path for maskedPaths and
readonlyPaths (#587)
* config-linux: Only require /dev/console when process.terminal is
true. Also require /dev/console to be provided by a bind mount
(#518)
* runtime: Require runtimes to generate errors when the container
specified in config.json cannot be created (#559)
Breaking Go changes:
* specs-go/config: Aggressive namespacing (#567)
* specs-go/config: Remove pointers from LinuxHugepageLimit,
LinuxInterfacePriority, and LinuxPids properties (#586)
* specs-go/state: Rename version to ociVersion (#633)
LinuxInterfacePriority, and LinuxPids properties (#586)
Minor fixes and documentation:
* spec: Separate the spec from project scaffolding (#626)
* README: Define "unspecified", "undefined", and
"implementation-defined" (#575)
* config: Clarify absolue and relative values for root.path (#558)
* config: Clarify ociVersion covering the configuration <->
runtime API (#523)
* config-linux: Forbid duplicated namespaces with same `type`
(#597)
* glossary: Make objects explicitly unordered and forbid duplicate
names (#584)
* specs-go/config: Add platform tags to Rlimits and
NoNewPRivileges (#564)
* schema/defs-linux: Use int64 for major/minor types (#610)
* Makefile: Add support for Go 1.7 (#547)
* Makefile: Require Go >= 1.6 for golint (#589)
* Makefile: Use a POSIX-compatible test ('==' -> '=') (#542)
* implementations: Rename ocitools -> runtime-tools (#585)
* *: Typo fixes and polishing (#556, #566, #568, #569, #571, #572,
#574, #595, #596, #599, #600, #601, #603, #605, #608, #613, #617,
#619, #621, #622, #623, #624, #625, #627, #629)
Changes with v1.0.0-rc2:
Additions:
* config-linux: Add new architectures from libseccomp 2.3.0 (#505)
* schema: Add JSON Schema for state JSON and move schema.json to
config-schema.json and similar (#481, #498, #519)
Minor fixes and documentation:
* Add compliance language for platforms and architectures (#527)
* Remove "unconditionally compliant" language (#553)
* bundle: Remove distribution references (#487)
* runtime: Fix sub-bullet indentation (#495)
* config: Replace Arch fstab reference with mount(8) (#443)
* config: Synchronize comments between Markdown and Go (#525)
* config: Drop v0.x compatibility statement (#488)
* config-linux: RFC 2119 wording for cgroupsPath (#493)
* config-linux: Make linux.devices and linux.resources.devices
optional (#526)
* config-linux: Extend no-tweak requirement to runtime namespaces (#538)
* schema: Add hook.timeout (#544)
* schema: Add missing '"type": "object"' (#528)
* schema: Run 'make fmt' and remove duplicates (#546, #551)
* schema/config: Make 'hostname' optional (#491)
* schema/config-linux: Add linux.resources.devices (#550)
* specs-go/config: Add Solaris tags to User properties (#496)
* specs-go/config: Make Linux and Solaris omitempty again (#502)
* specs-go/config: Make KernelTCP and ClassID omitempty (#531)
* specs-go/config: Fix "specified" typo for ApparmorProfile (#503)
* Makefile: Remove code-of-conduct.md and version.md when clean (#541)
* implementations: Mention cc-oci-runtime (#539)
* Use filesystem instead of file system (#529)
* .pullapprove: Add DCO check via PullApprove
* GOVERNANCE: Add governance and release process docs (#521)
* README: Change meeting time from 10am to 2pm Pacific (#524)
* README: Update conference-call phone number (#512, #515)
Changes with v1.0.0-rc1:
Breaking changes:

View File

@ -0,0 +1,70 @@
# Project governance
The [OCI charter][charter] §5.b.viii tasks an OCI Project's maintainers (listed in the repository's MAINTAINERS file and sometimes referred to as "the TDC", [§5.e][charter]) with:
> Creating, maintaining and enforcing governance guidelines for the TDC, approved by the maintainers, and which shall be posted visibly for the TDC.
This section describes generic rules and procedures for fulfilling that mandate.
## Proposing a motion
A maintainer SHOULD propose a motion on the dev@opencontainers.org mailing list (except [security issues](#security-issues)) with another maintainer as a co-sponsor.
## Voting
Voting on a proposed motion SHOULD happen on the dev@opencontainers.org mailing list (except [security issues](#security-issues)) with maintainers posting LGTM or REJECT.
Maintainers MAY also explicitly not vote by posting ABSTAIN (which is useful to revert a previous vote).
Maintainers MAY post multiple times (e.g. as they revise their position based on feeback), but only their final post counts in the tally.
A proposed motion is adopted if two-thirds of votes cast, a quorum having voted, are in favor of the release.
Voting SHOULD remain open for a week to collect feedback from the wider community and allow the maintainers to digest the proposed motion.
Under exceptional conditions (e.g. non-major security fix releases) proposals which reach quorum with unanimous support MAY be adopted earlier.
A maintainer MAY choose to reply with REJECT.
A maintainer posting a REJECT MUST include a list of concerns or links to written documentation for those concerns (e.g. GitHub issues or mailing-list threads).
The maintainers SHOULD try to resolve the concerns and wait for the rejecting maintainer to change their opinion to LGTM.
However, a motion MAY be adopted with REJECTs, as outlined in the previous paragraphs.
## Quorum
A quorum is established when at least two-thirds of maintainers have voted.
For projects that are not specifications, a [motion to release](#release-approval) MAY be adopted if the tally is at least three LGTMs and no REJECTs, even if three votes does not meet the usual two-thirds quorum.
## Security issues
Motions with sensitive security implications MUST be proposed on the security@opencontainers.org mailing list instead of dev@opencontainers.org, but should otherwise follow the standard [proposal](#proposing-a-motion) process.
The security@opencontainers.org mailing list includes all members of the TOB.
The TOB will contact the project maintainers and provide a channel for discussing and voting on the motion, but voting will otherwise follow the standard [voting](#voting) and [quorum](#quorum) rules.
The TOB and project maintainers will work together to notify affected parties before making an adopted motion public.
## Amendments
The [project governance](#project-governance) rules and procedures MAY be amended or replaced using the procedures themselves.
The MAINTAINERS of this project governance document is the total set of MAINTAINERS from all Open Containers projects (runC, runtime-spec, and image-spec).
## Subject templates
Maintainers are busy and get lots of email.
To make project proposals recognizable, proposed motions SHOULD use the following subject templates.
### Proposing a motion
> [{project} VOTE]: {motion description} (closes {end of voting window})
For example:
> [runtime-spec VOTE]: Tag 0647920 as 1.0.0-rc (closes 2016-06-03 20:00 UTC)
### Tallying results
After voting closes, a maintainer SHOULD post a tally to the motion thread with a subject template like:
> [{project} {status}]: {motion description} (+{LGTMs} -{REJECTs} #{ABSTAINs})
Where `{status}` is either `adopted` or `rejected`.
For example:
> [runtime-spec adopted]: Tag 0647920 as 1.0.0-rc (+6 -0 #3)
[charter]: https://www.opencontainers.org/about/governance

View File

@ -1,8 +1,7 @@
EPOCH_TEST_COMMIT := 78e6667ae2d67aad100b28ee9580b41b7a24e667
OUTPUT_DIRNAME ?= output/
OUTPUT_DIRNAME ?= output
DOC_FILENAME ?= oci-runtime-spec
SHELL ?= $(shell command -v bash 2>/dev/null)
DOCKER ?= $(shell command -v docker 2>/dev/null)
PANDOC ?= $(shell command -v pandoc 2>/dev/null)
ifeq "$(strip $(PANDOC))" ''
@ -22,13 +21,8 @@ endif
# These docs are in an order that determines how they show up in the PDF/HTML docs.
DOC_FILES := \
version.md \
README.md \
code-of-conduct.md \
spec.md \
principles.md \
style.md \
ROADMAP.md \
implementations.md \
project.md \
bundle.md \
runtime.md \
runtime-linux.md \
@ -55,28 +49,23 @@ $(OUTPUT_DIRNAME)/$(DOC_FILENAME).html: $(DOC_FILES)
$(PANDOC) -f markdown_github -t html5 -o $(PANDOC_DST)$@ $(patsubst %,$(PANDOC_SRC)%,$(DOC_FILES))
endif
code-of-conduct.md:
curl -o $@ https://raw.githubusercontent.com/opencontainers/tob/d2f9d68c1332870e40693fe077d311e0742bc73d/code-of-conduct.md
version.md: ./specs-go/version.go
go run ./.tool/version-doc.go > $@
HOST_GOLANG_VERSION = $(shell go version | cut -d ' ' -f3 | cut -c 3-)
# this variable is used like a function. First arg is the minimum version, Second arg is the version to be checked.
ALLOWED_GO_VERSION = $(shell test '$(shell /bin/echo -e "$(1)\n$(2)" | sort -V | head -n1)' == '$(1)' && echo 'true')
ALLOWED_GO_VERSION = $(shell test '$(shell /bin/echo -e "$(1)\n$(2)" | sort -V | head -n1)' = '$(1)' && echo 'true')
.PHONY: test .govet .golint .gitvalidation
test: .govet .golint .gitvalidation
# `go get golang.org/x/tools/cmd/vet`
.govet:
@go tool | grep -qw vet || (echo "ERROR: 'go vet' not found. Consider 'make install.tools' target" && false)
go vet -x ./...
# `go get github.com/golang/lint/golint`
.golint:
ifeq ($(call ALLOWED_GO_VERSION,1.5,$(HOST_GOLANG_VERSION)),true)
ifeq ($(call ALLOWED_GO_VERSION,1.6,$(HOST_GOLANG_VERSION)),true)
@which golint > /dev/null 2>/dev/null || (echo "ERROR: golint not found. Consider 'make install.tools' target" && false)
golint ./...
endif
@ -85,7 +74,7 @@ endif
# When this is running in travis, it will only check the travis commit range
.gitvalidation:
@which git-validation > /dev/null 2>/dev/null || (echo "ERROR: git-validation not found. Consider 'make install.tools' target" && false)
ifeq ($(TRAVIS),true)
ifdef TRAVIS_COMMIT_RANGE
git-validation -q -run DCO,short-subject,dangling-whitespace
else
git-validation -v -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..HEAD
@ -93,25 +82,20 @@ endif
.PHONY: install.tools
install.tools: .install.golint .install.govet .install.gitvalidation
install.tools: .install.golint .install.gitvalidation
# golint does not even build for <go1.5
# golint does not even build for <go1.6
.install.golint:
ifeq ($(call ALLOWED_GO_VERSION,1.5,$(HOST_GOLANG_VERSION)),true)
go get github.com/golang/lint/golint
endif
# go vet is now included in >=go1.5, so no need to get it.
.install.govet:
ifeq ($(call ALLOWED_GO_VERSION,1.5,$(HOST_GOLANG_VERSION)),true)
go get golang.org/x/tools/cmd/vet
ifeq ($(call ALLOWED_GO_VERSION,1.6,$(HOST_GOLANG_VERSION)),true)
go get -u github.com/golang/lint/golint
endif
.install.gitvalidation:
go get github.com/vbatts/git-validation
go get -u github.com/vbatts/git-validation
.PHONY: clean
clean:
rm -rf $(OUTPUT_DIRNAME) *~
rm -f version.md

View File

@ -1,68 +1,53 @@
# Open Container Runtime Specification
# Open Container Initiative Runtime Specification
The [Open Container Initiative](http://www.opencontainers.org/) develops specifications for standards on Operating System process and application containers.
The [Open Container Initiative][oci] develops specifications for standards on Operating System process and application containers.
The specification can be found [here](spec.md).
Table of Contents
## Table of Contents
- [Introduction](README.md)
- [Code of Conduct](#code-of-conduct)
- [Container Principles](principles.md)
- [Style and Conventions](style.md)
- [Roadmap](ROADMAP.md)
- [Implementations](implementations.md)
- [project](project.md)
- [Filesystem Bundle](bundle.md)
- Runtime and Lifecycle
- [General Runtime and Lifecycle](runtime.md)
- [Linux-specific Runtime and Lifecycle](runtime-linux.md)
- Configuration
- [General Configuration](config.md)
- [Linux-specific Configuration](config-linux.md)
- [Solaris-specific Configuration](config-solaris.md)
- [Glossary](glossary.md)
Additional documentation about how this group operates:
In the specifications in the above table of contents, the keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119) (Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997).
- [Code of Conduct][code-of-conduct]
- [Style and Conventions](style.md)
- [Roadmap](ROADMAP.md)
- [Implementations](implementations.md)
- [Releases](RELEASES.md)
- [project](project.md)
- [charter][charter]
An implementation is not compliant if it fails to satisfy one or more of the MUST or REQUIRED requirements for the protocols it implements.
An implementation that satisfies all the MUST or REQUIRED and all the SHOULD requirements for its protocols is said to be "unconditionally compliant".
# Use Cases
## Use Cases
To provide context for users the following section gives example use cases for each part of the spec.
#### Application Bundle Builders
### Application Bundle Builders
Application bundle builders can create a [bundle](bundle.md) directory that includes all of the files required for launching an application as a container.
The bundle contains an OCI [configuration file](config.md) where the builder can specify host-independent details such as [which executable to launch](config.md#process-configuration) and host-specific settings such as [mount](config.md#mounts) locations, [hook](config.md#hooks) paths, Linux [namespaces](config-linux.md#namespaces) and [cgroups](config-linux.md#control-groups).
The bundle contains an OCI [configuration file](config.md) where the builder can specify host-independent details such as [which executable to launch](config.md#process) and host-specific settings such as [mount](config.md#mounts) locations, [hook](config.md#hooks) paths, Linux [namespaces](config-linux.md#namespaces) and [cgroups](config-linux.md#control-groups).
Because the configuration includes host-specific settings, application bundle directories copied between two hosts may require configuration adjustments.
#### Hook Developers
### Hook Developers
[Hook](config.md#hooks) developers can extend the functionality of an OCI-compliant runtime by hooking into a container's lifecycle with an external application.
Example use cases include sophisticated network configuration, volume garbage collection, etc.
#### Runtime Developers
### Runtime Developers
Runtime developers can build runtime implementations that run OCI-compliant bundles and container configuration, containing low-level OS and host specific details, on a particular platform.
# Releases
## Releases
There is a loose [Road Map](./ROADMAP.md).
During the `0.x` series of OCI releases we make no backwards compatibility guarantees and intend to break the schema during this series.
# Contributing
## Contributing
Development happens on GitHub for the spec.
Issues are used for bugs and actionable items and longer discussions can happen on the [mailing list](#mailing-list).
The specification and code is licensed under the Apache 2.0 license found in the [LICENSE](./LICENSE) file.
## Code of Conduct
Participation in the OpenContainers community is governed by [OpenContainer's Code of Conduct](https://github.com/opencontainers/tob/blob/d2f9d68c1332870e40693fe077d311e0742bc73d/code-of-conduct.md).
## Discuss your design
### Discuss your design
The project welcomes submissions, but please let everyone know what you are working on.
@ -73,27 +58,27 @@ It also guarantees that the design is sound before code is written; a GitHub pul
Typos and grammatical errors can go straight to a pull-request.
When in doubt, start on the [mailing-list](#mailing-list).
## Weekly Call
### Weekly Call
The contributors and maintainers of all OCI projects have a weekly meeting Wednesdays at 10:00 AM (USA Pacific.)
Everyone is welcome to participate via [UberConference web][UberConference] or audio-only: 415-968-0849 (no PIN needed.)
The contributors and maintainers of all OCI projects have a weekly meeting Wednesdays at 2:00 PM (USA Pacific).
Everyone is welcome to participate via [UberConference web][uberconference] or audio-only: 415-968-0849 (no PIN needed.)
An initial agenda will be posted to the [mailing list](#mailing-list) earlier in the week, and everyone is welcome to propose additional topics or suggest other agenda alterations there.
Minutes are posted to the [mailing list](#mailing-list) and minutes from past calls are archived to the [wiki](https://github.com/opencontainers/runtime-spec/wiki) for those who are unable to join the call.
Minutes are posted to the [mailing list](#mailing-list) and minutes from past calls are archived to the [wiki][runtime-wiki].
## Mailing List
### Mailing List
You can subscribe and join the mailing list on [Google Groups](https://groups.google.com/a/opencontainers.org/forum/#!forum/dev).
You can subscribe and join the mailing list on [Google Groups][dev-list].
## IRC
### IRC
OCI discussion happens on #opencontainers on Freenode ([logs][irc-logs]).
## Git commit
### Git commit
### Sign your work
#### Sign your work
The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch.
The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)):
The rules are pretty simple: if you can certify the below (from http://developercertificate.org):
```
Developer Certificate of Origin
@ -142,10 +127,10 @@ using your real name (sorry, no pseudonyms or anonymous contributions.)
You can add the sign off when creating the git commit via `git commit -s`.
### Commit Style
#### Commit Style
Simple house-keeping for clean git history.
Read more on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/) or the Discussion section of [`git-commit(1)`](http://git-scm.com/docs/git-commit).
Read more on [How to Write a Git Commit Message][how-to-git-commit] or the Discussion section of [git-commit(1)][git-commit.1].
1. Separate the subject from body with a blank line
2. Limit the subject line to 50 characters
@ -157,5 +142,14 @@ Read more on [How to Write a Git Commit Message](http://chris.beams.io/posts/git
* If there was important/useful/essential conversation or information, copy or include a reference
8. When possible, one keyword to scope the change in the subject (i.e. "README: ...", "runtime: ...")
[UberConference]: https://www.uberconference.com/opencontainers
[charter]: https://www.opencontainers.org/about/governance
[code-of-conduct]: https://github.com/opencontainers/tob/blob/master/code-of-conduct.md
[dev-list]: https://groups.google.com/a/opencontainers.org/forum/#!forum/dev
[how-to-git-commit]: http://chris.beams.io/posts/git-commit
[irc-logs]: http://ircbot.wl.linuxfoundation.org/eavesdrop/%23opencontainers/
[oci]: https://www.opencontainers.org
[runtime-wiki]: https://github.com/opencontainers/runtime-spec/wiki
[uberconference]: https://www.uberconference.com/opencontainers
[git-commit.1]: http://git-scm.com/docs/git-commit

View File

@ -0,0 +1,51 @@
# Releases
The release process hopes to encourage early, consistent consensus-building during project development.
The mechanisms used are regular community communication on the mailing list about progress, scheduled meetings for issue resolution and release triage, and regularly paced and communicated releases.
Releases are proposed and adopted or rejected using the usual [project governance](GOVERNANCE.md) rules and procedures.
An anti-pattern that we want to avoid is heavy development or discussions "late cycle" around major releases.
We want to build a community that is involved and communicates consistently through all releases instead of relying on "silent periods" as a judge of stability.
## Parallel releases
A single project MAY consider several motions to release in parallel.
However each motion to release after the initial 0.1.0 MUST be based on a previous release that has already landed.
For example, runtime-spec maintainers may propose a v1.0.0-rc2 on the 1st of the month and a v0.9.1 bugfix on the 2nd of the month.
They may not propose a v1.0.0-rc3 until the v1.0.0-rc2 is accepted (on the 7th if the vote initiated on the 1st passes).
## Specifications
The OCI maintains three categories of projects: specifications, applications, and conformance-testing tools.
However, specification releases have special restrictions in the [OCI charter][charter]:
* They are the target of backwards compatibility (§7.g), and
* They are subject to the OFWa patent grant (§8.d and e).
To avoid unfortunate side effects (onerous backwards compatibity requirements or Member resignations), the following additional procedures apply to specification releases:
### Planning a release
Every OCI specification project SHOULD hold meetings that involve maintainers reviewing pull requests, debating outstanding issues, and planning releases.
This meeting MUST be advertised on the project README and MAY happen on a phone call, video conference, or on IRC.
Maintainers MUST send updates to the dev@opencontainers.org with results of these meetings.
Before the specification reaches v1.0.0, the meetings SHOULD be weekly.
Once a specification has reached v1.0.0, the maintainers may alter the cadence, but a meeting MUST be held within four weeks of the previous meeting.
The release plans, corresponding milestones and estimated due dates MUST be published on GitHub (e.g. https://github.com/opencontainers/runtime-spec/milestones).
GitHub milestones and issues are only used for community organization and all releases MUST follow the [project governance](GOVERNANCE.md) rules and procedures.
### Timelines
Specifications have a variety of different timelines in their lifecycle.
* Pre-v1.0.0 specifications SHOULD release on a monthly cadence to garner feedback.
* Major specification releases MUST release at least three release candidates spaced a minimum of one week apart.
This means a major release like a v1.0.0 or v2.0.0 release will take 1 month at minimum: one week for rc1, one week for rc2, one week for rc3, and one week for the major release itself.
Maintainers SHOULD strive to make zero breaking changes during this cycle of release candidates and SHOULD restart the three-candidate count when a breaking change is introduced.
For example if a breaking change is introduced in v1.0.0-rc2 then the series would end with v1.0.0-rc4 and v1.0.0.
- Minor and patch releases SHOULD be made on an as-needed basis.
[charter]: https://www.opencontainers.org/about/governance

View File

@ -6,7 +6,7 @@ The items in the 1.0 roadmap can be broken down into smaller milestones that are
The topics below are broad and small working groups will be needed for each to define scope and requirements or if the feature is required at all for the OCI level.
Topics listed in the roadmap do not mean that they will be implemented or added but are areas that need discussion to see if they fit in to the goals of the OCI.
Listed topics may defer to the [project wiki](https://github.com/opencontainers/runtime-spec/wiki/RoadMap:) for collaboration.
Listed topics may defer to the [project wiki][runtime-wiki] for collaboration.
## 1.0
@ -18,14 +18,6 @@ Could be solved by lifecycle/ops and create/start split discussions
*Owner:* vishh & duglin
### Live Container Updates
Should we allow dynamic container updates to runtime options?
Proposal: make it an optional feature
*Owner:* hqhq (was vishh) robdolinms, bcorrie
### Version Schema
Decide on a robust versioning schema for the spec as it evolves.
@ -40,9 +32,9 @@ Ensure that the base configuration format is viable for various platforms.
Systems:
* Linux
* Solaris
* Windows
* Linux
*Owner:* robdolinms as lead coordinator
@ -53,3 +45,6 @@ Ensure that we have lifecycle hooks in the correct places with full coverage ove
Will probably go away with Vish's work on splitting create and start, and if we have exec.
*Owner:*
[runtime-wiki]: https://github.com/opencontainers/runtime-spec/wiki/RoadMap

View File

@ -1,22 +1,24 @@
# Filesystem Bundle
# <a name="filesystemBundle" />Filesystem Bundle
## Container Format
## <a name="containerFormat" />Container Format
This section defines a format for encoding a container as a *filesystem bundle* - a set of files organized in a certain way, and containing all the necessary data and metadata for any compliant runtime to perform all standard operations against it.
See also [OS X application bundles](http://en.wikipedia.org/wiki/Bundle_%28OS_X%29) for a similar use of the term *bundle*.
See also [MacOS application bundles][macos_bundle] for a similar use of the term *bundle*.
The definition of a bundle is only concerned with how a container, and its configuration data, are stored on a local file system so that it can be consumed by a compliant runtime.
The definition of a bundle is only concerned with how a container, and its configuration data, are stored on a local filesystem so that it can be consumed by a compliant runtime.
A Standard Container bundle contains all the information needed to load and run a container.
This MUST include the following artifacts:
1. `config.json` : contains configuration data.
1. <a name="containerFormat01" />`config.json`: contains configuration data.
This REQUIRED file MUST reside in the root of the bundle directory and MUST be named `config.json`.
See [`config.json`](config.md) for more details.
2. A directory representing the root filesystem of the container.
2. <a name="containerFormat02" />A directory representing the root filesystem of the container.
While the name of this REQUIRED directory may be arbitrary, users should consider using a conventional name, such as `rootfs`.
This directory MUST be referenced from within the `config.json` file.
While these artifacts MUST all be present in a single directory on the local filesystem, that directory itself is not part of the bundle.
In other words, a tar archive of a *bundle* will have these artifacts at the root of the archive, not nested within a top-level directory.
[macos_bundle]: https://en.wikipedia.org/wiki/Bundle_%28macOS%29

View File

@ -1,32 +1,32 @@
# Linux-specific Container Configuration
# <a name="linuxContainerConfiguration" />Linux Container Configuration
This document describes the schema for the [Linux-specific section](config.md#platform-specific-configuration) of the [container configuration](config.md).
The Linux container specification uses various kernel features like namespaces, cgroups, capabilities, LSM, and file system jails to fulfill the spec.
The Linux container specification uses various kernel features like namespaces, cgroups, capabilities, LSM, and filesystem jails to fulfill the spec.
## Default File Systems
## <a name="configLinuxDefaultFilesystems" />Default Filesystems
The Linux ABI includes both syscalls and several special file paths.
Applications expecting a Linux environment will very likely expect these files paths to be setup correctly.
Applications expecting a Linux environment will very likely expect these file paths to be setup correctly.
The following filesystems MUST be made available in each application's filesystem
The following filesystems SHOULD be made available in each container's filesystem:
| Path | Type |
| Path | Type |
| -------- | ------ |
| /proc | [procfs](https://www.kernel.org/doc/Documentation/filesystems/proc.txt) |
| /sys | [sysfs](https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt) |
| /dev/pts | [devpts](https://www.kernel.org/doc/Documentation/filesystems/devpts.txt) |
| /dev/shm | [tmpfs](https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt) |
| /proc | [procfs][procfs] |
| /sys | [sysfs][sysfs] |
| /dev/pts | [devpts][devpts] |
| /dev/shm | [tmpfs][tmpfs] |
## Namespaces
## <a name="configLinuxNamespaces" />Namespaces
A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource.
Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes.
For more information, see [the man page](http://man7.org/linux/man-pages/man7/namespaces.7.html).
For more information, see the [namespaces(7)][namespaces.7_2] man page.
Namespaces are specified as an array of entries inside the `namespaces` root field.
The following parameters can be specified to setup namespaces:
* **`type`** *(string, required)* - namespace type. The following namespaces types are supported:
* **`type`** *(string, REQUIRED)* - namespace type. The following namespace types are supported:
* **`pid`** processes inside the container will only be able to see other processes inside the same container.
* **`network`** the container will have its own network stack.
* **`mount`** the container will have an isolated mount table.
@ -35,10 +35,11 @@ The following parameters can be specified to setup namespaces:
* **`user`** the container will be able to remap user and group IDs from the host to local users and groups within the container.
* **`cgroup`** the container will have an isolated view of the cgroup hierarchy.
* **`path`** *(string, optional)* - path to namespace file in the [runtime mount namespace](glossary.md#runtime-namespace)
* **`path`** *(string, OPTIONAL)* - path to namespace file in the [runtime mount namespace](glossary.md#runtime-namespace)
If a path is specified, that particular file is used to join that type of namespace.
Also, when a path is specified, a runtime MUST assume that the setup for that particular namespace has already been done and error out if the config specifies anything else related to that namespace.
If a namespace type is not specified in the `namespaces` array, the container MUST inherit the [runtime namespace](glossary.md#runtime-namespace) of that type.
If a `namespaces` field contains duplicated namespaces with same `type`, the runtime MUST error out.
###### Example
@ -70,7 +71,19 @@ Also, when a path is specified, a runtime MUST assume that the setup for that pa
]
```
## User namespace mappings
## <a name="configLinuxUserNamespaceMappings" />User namespace mappings
**`uidMappings`** (array of objects, OPTIONAL) describes the user namespace uid mappings from the host to the container.
**`gidMappings`** (array of objects, OPTIONAL) describes the user namespace gid mappings from the host to the container.
Each entry has the following structure:
* **`hostID`** *(uint32, REQUIRED)* - is the starting uid/gid on the host to be mapped to *containerID*.
* **`containerID`** *(uint32, REQUIRED)* - is the starting uid/gid in the container.
* **`size`** *(uint32, REQUIRED)* - is the number of ids to be mapped.
The runtime SHOULD NOT modify the ownership of referenced filesystems to realize the mapping.
Note that the number of mapping entries MAY be limited by the [kernel][user-namespaces].
###### Example
@ -79,38 +92,36 @@ Also, when a path is specified, a runtime MUST assume that the setup for that pa
{
"hostID": 1000,
"containerID": 0,
"size": 10
"size": 32000
}
],
"gidMappings": [
{
"hostID": 1000,
"containerID": 0,
"size": 10
"size": 32000
}
]
```
uid/gid mappings describe the user namespace mappings from the host to the container.
The runtime SHOULD NOT modify the ownership of referenced filesystems to realize the mapping.
*hostID* is the starting uid/gid on the host to be mapped to *containerID* which is the starting uid/gid in the container and *size* refers to the number of ids to be mapped.
There is a limit of 5 mappings which is the Linux kernel hard limit.
## <a name="configLinuxDevices" />Devices
## Devices
`devices` is an array specifying the list of devices that MUST be available in the container.
**`devices`** (array of objects, OPTIONAL) lists devices that MUST be available in the container.
The runtime may supply them however it likes (with [mknod][mknod.2], by bind mounting from the runtime mount namespace, etc.).
The following parameters can be specified:
Each entry has the following structure:
* **`type`** *(string, required)* - type of device: `c`, `b`, `u` or `p`.
* **`type`** *(string, REQUIRED)* - type of device: `c`, `b`, `u` or `p`.
More info in [mknod(1)][mknod.1].
* **`path`** *(string, required)* - full path to device inside container.
* **`major, minor`** *(int64, required unless **`type`** is `p`)* - [major, minor numbers][devices] for the device.
* **`fileMode`** *(uint32, optional)* - file mode for the device.
* **`path`** *(string, REQUIRED)* - full path to device inside container.
If a [file][file.1] already exists at `path` that does not match the requested device, the runtime MUST generate an error.
* **`major, minor`** *(int64, REQUIRED unless `type` is `p`)* - [major, minor numbers][devices] for the device.
* **`fileMode`** *(uint32, OPTIONAL)* - file mode for the device.
You can also control access to devices [with cgroups](#device-whitelist).
* **`uid`** *(uint32, optional)* - id of device owner.
* **`gid`** *(uint32, optional)* - id of device group.
* **`uid`** *(uint32, OPTIONAL)* - id of device owner.
* **`gid`** *(uint32, OPTIONAL)* - id of device group.
The same `type`, `major` and `minor` SHOULD NOT be used for multiple devices.
###### Example
@ -137,7 +148,7 @@ The following parameters can be specified:
]
```
###### Default Devices
###### <a name="configLinuxDefaultDevices" />Default Devices
In addition to any devices configured with this setting, the runtime MUST also supply:
@ -147,49 +158,68 @@ In addition to any devices configured with this setting, the runtime MUST also s
* [`/dev/random`][random.4]
* [`/dev/urandom`][random.4]
* [`/dev/tty`][tty.4]
* [`/dev/console`][console.4]
* [`/dev/console`][console.4] is setup if terminal is enabled in the config by bind mounting the pseudoterminal slave to /dev/console.
* [`/dev/ptmx`][pts.4].
A [bind-mount or symlink of the container's `/dev/pts/ptmx`][devpts].
## Control groups
## <a name="configLinuxControlGroups" />Control groups
Also known as cgroups, they are used to restrict resource usage for a container and handle device access.
cgroups provide controls to restrict cpu, memory, IO, pids and network for the container.
cgroups provide controls (through controllers) to restrict cpu, memory, IO, pids and network for the container.
For more information, see the [kernel cgroups documentation][cgroup-v1].
The path to the cgroups can be specified in the Spec via `cgroupsPath`.
`cgroupsPath` is expected to be relative to the cgroups mount point.
If `cgroupsPath` is not specified, implementations can define the default cgroup path.
`cgroupsPath` can be used to either control the cgroup hierarchy for containers or to run a new process in an existing container.
If `cgroupsPath` is:
* ... an absolute path (starting with `/`), the runtime MUST take the path to be relative to the cgroup mount point.
* ... a relative path (not starting with `/`), the runtime MAY interpret the path relative to a runtime-determined location in the cgroup hierarchy.
* ... not specified, the runtime MAY define the default cgroup path.
Runtimes MAY consider certain `cgroupsPath` values to be invalid, and MUST generate an error if this is the case.
If a `cgroupsPath` value is specified, the runtime MUST consistently attach to the same place in the cgroup hierarchy given the same value of `cgroupsPath`.
Implementations of the Spec can choose to name cgroups in any manner.
The Spec does not include naming schema for cgroups.
The Spec does not support [split hierarchy][cgroup-v2].
The Spec does not support per-controller paths for the reasons discussed in the [cgroupv2 documentation][cgroup-v2].
The cgroups will be created if they don't exist.
###### Example
```json
"cgroupsPath": "/myRuntime/myContainer"
```
`cgroupsPath` can be used to either control the cgroups hierarchy for containers or to run a new process in an existing container.
You can configure a container's cgroups via the `resources` field of the Linux configuration.
Do not specify `resources` unless limits have to be updated.
For example, to run a new process in an existing container without updating limits, `resources` need not be specified.
#### Device whitelist
A runtime MUST at least use the minimum set of cgroup controllers required to fulfill the `resources` settings.
However, a runtime MAY attach the container process to additional cgroup controllers supported by the system.
`devices` is an array of entries to control the [device whitelist][cgroup-v1-devices].
###### Example
```json
"cgroupsPath": "/myRuntime/myContainer",
"resources": {
"memory": {
"limit": 100000,
"reservation": 200000
},
"devices": [
{
"allow": false,
"access": "rwm"
}
]
}
```
#### <a name="configLinuxDeviceWhitelist" />Device whitelist
**`devices`** (array of objects, OPTIONAL) configures the [device whitelist][cgroup-v1-devices].
The runtime MUST apply entries in the listed order.
The following parameters can be specified:
Each entry has the following structure:
* **`allow`** *(boolean, required)* - whether the entry is allowed or denied.
* **`type`** *(string, optional)* - type of device: `a` (all), `c` (char), or `b` (block).
* **`allow`** *(boolean, REQUIRED)* - whether the entry is allowed or denied.
* **`type`** *(string, OPTIONAL)* - type of device: `a` (all), `c` (char), or `b` (block).
`null` or unset values mean "all", mapping to `a`.
* **`major, minor`** *(int64, optional)* - [major, minor numbers][devices] for the device.
* **`major, minor`** *(int64, OPTIONAL)* - [major, minor numbers][devices] for the device.
`null` or unset values mean "all", mapping to [`*` in the filesystem API][cgroup-v1-devices].
* **`access`** *(string, optional)* - cgroup permissions for device.
* **`access`** *(string, OPTIONAL)* - cgroup permissions for device.
A composition of `r` (read), `w` (write), and `m` (mknod).
###### Example
@ -217,7 +247,7 @@ The following parameters can be specified:
]
```
#### Disable out-of-memory killer
#### <a name="configLinuxDisableOutOfMemoryKiller" />Disable out-of-memory killer
`disableOOMKiller` contains a boolean (`true` or `false`) that enables or disables the Out of Memory killer for a cgroup.
If enabled (`false`), tasks that attempt to consume more memory than they are allowed are immediately killed by the OOM killer.
@ -225,7 +255,7 @@ The OOM killer is enabled by default in every cgroup using the `memory` subsyste
To disable it, specify a value of `true`.
For more information, see [the memory cgroup man page][cgroup-v1-memory].
* **`disableOOMKiller`** *(bool, optional)* - enables or disables the OOM killer
* **`disableOOMKiller`** *(bool, OPTIONAL)* - enables or disables the OOM killer
###### Example
@ -233,14 +263,14 @@ For more information, see [the memory cgroup man page][cgroup-v1-memory].
"disableOOMKiller": false
```
#### Set oom_score_adj
#### <a name="configLinuxSetOomScoreAdj" />Set oom_score_adj
`oomScoreAdj` sets heuristic regarding how the process is evaluated by the kernel during memory pressure.
For more information, see [the proc filesystem documentation section 3.1](https://www.kernel.org/doc/Documentation/filesystems/proc.txt).
For more information, see [the proc filesystem documentation section 3.1][procfs].
This is a kernel/system level setting, where as `disableOOMKiller` is scoped for a memory cgroup.
For more information on how these two settings work together, see [the memory cgroup documentation section 10. OOM Contol][cgroup-v1-memory].
* **`oomScoreAdj`** *(int, optional)* - adjust the oom-killer score
* **`oomScoreAdj`** *(int, OPTIONAL)* - adjust the oom-killer score
###### Example
@ -248,24 +278,24 @@ For more information on how these two settings work together, see [the memory cg
"oomScoreAdj": 100
```
#### Memory
#### <a name="configLinuxMemory" />Memory
`memory` represents the cgroup subsystem `memory` and it's used to set limits on the container's memory usage.
**`memory`** (object, OPTIONAL) represents the cgroup subsystem `memory` and it's used to set limits on the container's memory usage.
For more information, see [the memory cgroup man page][cgroup-v1-memory].
The following parameters can be specified to setup the controller:
* **`limit`** *(uint64, optional)* - sets limit of memory usage in bytes
* **`limit`** *(uint64, OPTIONAL)* - sets limit of memory usage in bytes
* **`reservation`** *(uint64, optional)* - sets soft limit of memory usage in bytes
* **`reservation`** *(uint64, OPTIONAL)* - sets soft limit of memory usage in bytes
* **`swap`** *(uint64, optional)* - sets limit of memory+Swap usage
* **`swap`** *(uint64, OPTIONAL)* - sets limit of memory+Swap usage
* **`kernel`** *(uint64, optional)* - sets hard limit for kernel memory
* **`kernel`** *(uint64, OPTIONAL)* - sets hard limit for kernel memory
* **`kernelTCP`** *(uint64, optional)* - sets hard limit in bytes for kernel TCP buffer memory
* **`kernelTCP`** *(uint64, OPTIONAL)* - sets hard limit in bytes for kernel TCP buffer memory
* **`swappiness`** *(uint64, optional)* - sets swappiness parameter of vmscan (See sysctl's vm.swappiness)
* **`swappiness`** *(uint64, OPTIONAL)* - sets swappiness parameter of vmscan (See sysctl's vm.swappiness)
###### Example
@ -280,26 +310,26 @@ The following parameters can be specified to setup the controller:
}
```
#### CPU
#### <a name="configLinuxCPU" />CPU
`cpu` represents the cgroup subsystems `cpu` and `cpusets`.
**`cpu`** (object, OPTIONAL) represents the cgroup subsystems `cpu` and `cpusets`.
For more information, see [the cpusets cgroup man page][cgroup-v1-cpusets].
The following parameters can be specified to setup the controller:
* **`shares`** *(uint64, optional)* - specifies a relative share of CPU time available to the tasks in a cgroup
* **`shares`** *(uint64, OPTIONAL)* - specifies a relative share of CPU time available to the tasks in a cgroup
* **`quota`** *(uint64, optional)* - specifies the total amount of time in microseconds for which all tasks in a cgroup can run during one period (as defined by **`period`** below)
* **`quota`** *(int64, OPTIONAL)* - specifies the total amount of time in microseconds for which all tasks in a cgroup can run during one period (as defined by **`period`** below)
* **`period`** *(uint64, optional)* - specifies a period of time in microseconds for how regularly a cgroup's access to CPU resources should be reallocated (CFS scheduler only)
* **`period`** *(uint64, OPTIONAL)* - specifies a period of time in microseconds for how regularly a cgroup's access to CPU resources should be reallocated (CFS scheduler only)
* **`realtimeRuntime`** *(uint64, optional)* - specifies a period of time in microseconds for the longest continuous period in which the tasks in a cgroup have access to CPU resources
* **`realtimeRuntime`** *(int64, OPTIONAL)* - specifies a period of time in microseconds for the longest continuous period in which the tasks in a cgroup have access to CPU resources
* **`realtimePeriod`** *(uint64, optional)* - same as **`period`** but applies to realtime scheduler only
* **`realtimePeriod`** *(uint64, OPTIONAL)* - same as **`period`** but applies to realtime scheduler only
* **`cpus`** *(string, optional)* - list of CPUs the container will run in
* **`cpus`** *(string, OPTIONAL)* - list of CPUs the container will run in
* **`mems`** *(string, optional)* - list of Memory Nodes the container will run in
* **`mems`** *(string, OPTIONAL)* - list of Memory Nodes the container will run in
###### Example
@ -315,27 +345,28 @@ The following parameters can be specified to setup the controller:
}
```
#### Block IO Controller
#### <a name="configLinuxBlockIO" />Block IO
`blockIO` represents the cgroup subsystem `blkio` which implements the block io controller.
**`blockIO`** (object, OPTIONAL) represents the cgroup subsystem `blkio` which implements the block IO controller.
For more information, see [the kernel cgroups documentation about blkio][cgroup-v1-blkio].
The following parameters can be specified to setup the controller:
* **`blkioWeight`** *(uint16, optional)* - specifies per-cgroup weight. This is default weight of the group on all devices until and unless overridden by per-device rules. The range is from 10 to 1000.
* **`blkioWeight`** *(uint16, OPTIONAL)* - specifies per-cgroup weight. This is default weight of the group on all devices until and unless overridden by per-device rules. The range is from 10 to 1000.
* **`blkioLeafWeight`** *(uint16, optional)* - equivalents of `blkioWeight` for the purpose of deciding how much weight tasks in the given cgroup has while competing with the cgroup's child cgroups. The range is from 10 to 1000.
* **`blkioLeafWeight`** *(uint16, OPTIONAL)* - equivalents of `blkioWeight` for the purpose of deciding how much weight tasks in the given cgroup has while competing with the cgroup's child cgroups. The range is from 10 to 1000.
* **`blkioWeightDevice`** *(array, optional)* - specifies the list of devices which will be bandwidth rate limited. The following parameters can be specified per-device:
* **`major, minor`** *(int64, required)* - major, minor numbers for device. More info in `man mknod`.
* **`weight`** *(uint16, optional)* - bandwidth rate for the device, range is from 10 to 1000
* **`leafWeight`** *(uint16, optional)* - bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
* **`blkioWeightDevice`** *(array, OPTIONAL)* - specifies the list of devices which will be bandwidth rate limited. The following parameters can be specified per-device:
* **`major, minor`** *(int64, REQUIRED)* - major, minor numbers for device. More info in `man mknod`.
* **`weight`** *(uint16, OPTIONAL)* - bandwidth rate for the device, range is from 10 to 1000
* **`leafWeight`** *(uint16, OPTIONAL)* - bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
You must specify at least one of `weight` or `leafWeight` in a given entry, and can specify both.
* **`blkioThrottleReadBpsDevice`**, **`blkioThrottleWriteBpsDevice`**, **`blkioThrottleReadIOPSDevice`**, **`blkioThrottleWriteIOPSDevice`** *(array, optional)* - specify the list of devices which will be IO rate limited. The following parameters can be specified per-device:
* **`major, minor`** *(int64, required)* - major, minor numbers for device. More info in `man mknod`.
* **`rate`** *(uint64, required)* - IO rate limit for the device
* **`blkioThrottleReadBpsDevice`**, **`blkioThrottleWriteBpsDevice`**, **`blkioThrottleReadIOPSDevice`**, **`blkioThrottleWriteIOPSDevice`** *(array, OPTIONAL)* - specify the list of devices which will be IO rate limited.
The following parameters can be specified per-device:
* **`major, minor`** *(int64, REQUIRED)* - major, minor numbers for device. More info in `man mknod`.
* **`rate`** *(uint64, REQUIRED)* - IO rate limit for the device
###### Example
@ -373,17 +404,17 @@ The following parameters can be specified to setup the controller:
}
```
#### Huge page limits
#### <a name="configLinuxHugePageLimits" />Huge page limits
`hugepageLimits` represents the `hugetlb` controller which allows to limit the
**`hugepageLimits`** (array of objects, OPTIONAL) represents the `hugetlb` controller which allows to limit the
HugeTLB usage per control group and enforces the controller limit during page fault.
For more information, see the [kernel cgroups documentation about HugeTLB][cgroup-v1-hugetlb].
`hugepageLimits` is an array of entries, each having the following structure:
Each entry has the following structure:
* **`pageSize`** *(string, required)* - hugepage size
* **`pageSize`** *(string, REQUIRED)* - hugepage size
* **`limit`** *(uint64, required)* - limit in bytes of *hugepagesize* HugeTLB usage
* **`limit`** *(uint64, REQUIRED)* - limit in bytes of *hugepagesize* HugeTLB usage
###### Example
@ -391,24 +422,24 @@ For more information, see the [kernel cgroups documentation about HugeTLB][cgrou
"hugepageLimits": [
{
"pageSize": "2MB",
"limit": 9223372036854771712
"limit": 209715200
}
]
```
#### Network
#### <a name="configLinuxNetwork" />Network
`network` represents the cgroup subsystems `net_cls` and `net_prio`.
**`network`** (object, OPTIONAL) represents the cgroup subsystems `net_cls` and `net_prio`.
For more information, see [the net\_cls cgroup man page][cgroup-v1-net-cls] and [the net\_prio cgroup man page][cgroup-v1-net-prio].
The following parameters can be specified to setup these cgroup controllers:
The following parameters can be specified to setup the controller:
* **`classID`** *(uint32, optional)* - is the network class identifier the cgroup's network packets will be tagged with
* **`classID`** *(uint32, OPTIONAL)* - is the network class identifier the cgroup's network packets will be tagged with
* **`priorities`** *(array, optional)* - specifies a list of objects of the priorities assigned to traffic originating from
processes in the group and egressing the system on various interfaces. The following parameters can be specified per-priority:
* **`name`** *(string, required)* - interface name
* **`priority`** *(uint32, required)* - priority applied to the interface
* **`priorities`** *(array, OPTIONAL)* - specifies a list of objects of the priorities assigned to traffic originating from processes in the group and egressing the system on various interfaces.
The following parameters can be specified per-priority:
* **`name`** *(string, REQUIRED)* - interface name
* **`priority`** *(uint32, REQUIRED)* - priority applied to the interface
###### Example
@ -428,14 +459,14 @@ processes in the group and egressing the system on various interfaces. The follo
}
```
#### PIDs
#### <a name="configLinuxPIDS" />PIDs
`pids` represents the cgroup subsystem `pids`.
**`pids`** (object, OPTIONAL) represents the cgroup subsystem `pids`.
For more information, see [the pids cgroup man page][cgroup-v1-pids].
The following parameters can be specified to setup the controller:
* **`limit`** *(int64, required)* - specifies the maximum number of tasks in the cgroup
* **`limit`** *(int64, REQUIRED)* - specifies the maximum number of tasks in the cgroup
###### Example
@ -445,10 +476,10 @@ The following parameters can be specified to setup the controller:
}
```
## Sysctl
## <a name="configLinuxSysctl" />Sysctl
`sysctl` allows kernel parameters to be modified at runtime for the container.
For more information, see [the man page](http://man7.org/linux/man-pages/man8/sysctl.8.html)
**`sysctl`** (object, OPTIONAL) allows kernel parameters to be modified at runtime for the container.
For more information, see the [sysctl(8)][sysctl.8] man page.
###### Example
@ -459,13 +490,13 @@ For more information, see [the man page](http://man7.org/linux/man-pages/man8/sy
}
```
## seccomp
## <a name="configLinuxSeccomp" />Seccomp
Seccomp provides application sandboxing mechanism in the Linux kernel.
Seccomp configuration allows one to configure actions to take for matched syscalls and furthermore also allows matching on values passed as arguments to syscalls.
For more information about Seccomp, see [Seccomp kernel documentation](https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt)
The actions, architectures, and operators are strings that match the definitions in seccomp.h from [libseccomp](https://github.com/seccomp/libseccomp) and are translated to corresponding values.
A valid list of constants as of libseccomp v2.3.0 is shown below.
For more information about Seccomp, see [Seccomp][seccomp] kernel documentation.
The actions, architectures, and operators are strings that match the definitions in seccomp.h from [libseccomp][] and are translated to corresponding values.
A valid list of constants as of libseccomp v2.3.2 is shown below.
Architecture Constants
* `SCMP_ARCH_X86`
@ -484,6 +515,8 @@ Architecture Constants
* `SCMP_ARCH_PPC64LE`
* `SCMP_ARCH_S390`
* `SCMP_ARCH_S390X`
* `SCMP_ARCH_PARISC`
* `SCMP_ARCH_PARISC64`
Action Constants:
* `SCMP_ACT_KILL`
@ -507,22 +540,27 @@ Operator Constants:
"seccomp": {
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": [
"SCMP_ARCH_X86"
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"name": "getcwd",
"action": "SCMP_ACT_ERRNO"
"names": [
"getcwd",
"chmod"
],
"action": "SCMP_ACT_ERRNO",
"comment": "stop exploit x"
}
]
}
```
## Rootfs Mount Propagation
## <a name="configLinuxRootfsMountPropagation" />Rootfs Mount Propagation
`rootfsPropagation` sets the rootfs's mount propagation.
**`rootfsPropagation`** (string, OPTIONAL) sets the rootfs's mount propagation.
Its value is either slave, private, or shared.
[The kernel doc](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) has more information about mount propagation.
The [Shared Subtrees][sharedsubtree] article in the kernel documentation has more information about mount propagation.
###### Example
@ -530,9 +568,10 @@ Its value is either slave, private, or shared.
"rootfsPropagation": "slave",
```
## Masked Paths
## <a name="configLinuxMaskedPaths" />Masked Paths
`maskedPaths` will mask over the provided paths inside the container so that they cannot be read.
**`maskedPaths`** (array of strings, OPTIONAL) will mask over the provided paths inside the container so that they cannot be read.
The values MUST be absolute paths in the [container namespace][container-namespace2].
###### Example
@ -542,9 +581,10 @@ Its value is either slave, private, or shared.
]
```
## Readonly Paths
## <a name="configLinuxReadonlyPaths" />Readonly Paths
`readonlyPaths` will set the provided paths as readonly inside the container.
**`readonlyPaths`** (array of strings, OPTIONAL) will set the provided paths as readonly inside the container.
The values MUST be absolute paths in the [container namespace][container-namespace2].
###### Example
@ -554,9 +594,9 @@ Its value is either slave, private, or shared.
]
```
## Mount Label
## <a name"configLinuxMountLabel" />Mount Label
`mountLabel` will set the Selinux context for the mounts in the container.
**`mountLabel`** (string, OPTIONAL) will set the Selinux context for the mounts in the container.
###### Example
@ -564,6 +604,9 @@ Its value is either slave, private, or shared.
"mountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c715,c811"
```
[container-namespace2]: glossary.md#container_namespace
[cgroup-v1]: https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt
[cgroup-v1-blkio]: https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt
[cgroup-v1-cpusets]: https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt
@ -576,13 +619,23 @@ Its value is either slave, private, or shared.
[cgroup-v2]: https://www.kernel.org/doc/Documentation/cgroup-v2.txt
[devices]: https://www.kernel.org/doc/Documentation/devices.txt
[devpts]: https://www.kernel.org/doc/Documentation/filesystems/devpts.txt
[file]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_164
[libseccomp]: https://github.com/seccomp/libseccomp
[procfs]: https://www.kernel.org/doc/Documentation/filesystems/proc.txt
[seccomp]: https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt
[sharedsubtree]: https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
[sysfs]: https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt
[tmpfs]: https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt
[mknod.1]: http://man7.org/linux/man-pages/man1/mknod.1.html
[mknod.2]: http://man7.org/linux/man-pages/man2/mknod.2.html
[console.4]: http://man7.org/linux/man-pages/man4/console.4.html
[full.4]: http://man7.org/linux/man-pages/man4/full.4.html
[mknod.1]: http://man7.org/linux/man-pages/man1/mknod.1.html
[mknod.2]: http://man7.org/linux/man-pages/man2/mknod.2.html
[namespaces.7_2]: http://man7.org/linux/man-pages/man7/namespaces.7.html
[null.4]: http://man7.org/linux/man-pages/man4/null.4.html
[pts.4]: http://man7.org/linux/man-pages/man4/pts.4.html
[random.4]: http://man7.org/linux/man-pages/man4/random.4.html
[sysctl.8]: http://man7.org/linux/man-pages/man8/sysctl.8.html
[tty.4]: http://man7.org/linux/man-pages/man4/tty.4.html
[zero.4]: http://man7.org/linux/man-pages/man4/zero.4.html
[user-namespaces]: http://man7.org/linux/man-pages/man7/user_namespaces.7.html

View File

@ -1,49 +1,48 @@
# Solaris Application Container Configuration
# <a name="solarisApplicationContainerConfiguration" />Solaris Application Container Configuration
Solaris application containers can be configured using the following properties, all of the below properties have mappings to properties specified under zonecfg(1M) man page, except milestone.
The Solaris specification is entirely optional.
Solaris application containers can be configured using the following properties, all of the below properties have mappings to properties specified under [zonecfg(1M)][zonecfg.1m_2] man page, except milestone.
## milestone
## <a name="configSolarisMilestone" />milestone
The SMF(Service Management Facility) FMRI which should go to "online" state before we start the desired process within the container.
**`milestone`** *(string, optional)*
**`milestone`** *(string, OPTIONAL)*
### Example
```json
"milestone": "svc:/milestone/container:default"
```
## limitpriv
## <a name="configSolarisLimitpriv" />limitpriv
The maximum set of privileges any process in this container can obtain.
The property should consist of a comma-separated privilege set specification as described in priv_str_to_set(3C) man page for the respective release of Solaris.
The property should consist of a comma-separated privilege set specification as described in [priv_str_to_set(3C)][priv-str-to-set.3c] man page for the respective release of Solaris.
**`limitpriv`** *(string, optional)*
**`limitpriv`** *(string, OPTIONAL)*
### Example
```json
"limitpriv": "default"
```
## maxShmMemory
## <a name="configSolarisMaxShmMemory" />maxShmMemory
The maximum amount of shared memory allowed for this application container.
A scale (K, M, G, T) can be applied to the value for each of these numbers (for example, 1M is one megabyte).
Mapped to max-shm-memory in zonecfg(1M) man page.
Mapped to `max-shm-memory` in [zonecfg(1M)][zonecfg.1m] man page.
**`maxShmMemory`** *(string, optional)*
**`maxShmMemory`** *(string, OPTIONAL)*
### Example
```json
"maxShmMemory": "512m"
```
## cappedCPU
## <a name="configSolarisCappedCpu" />cappedCPU
Sets a limit on the amount of CPU time that can be used by a container.
The unit used translates to the percentage of a single CPU that can be used by all user threads in a container, expressed as a fraction (for example, .75) or a mixed number (whole number and fraction, for example, 1.25).
An ncpu value of 1 means 100% of a CPU, a value of 1.25 means 125%, .75 mean 75%, and so forth.
When projects within a capped container have their own caps, the minimum value takes precedence.
cappedCPU is mapped to capped-cpu in zonecfg(1M) man page.
cappedCPU is mapped to `capped-cpu` in [zonecfg(1M)][zonecfg.1m] man page.
* **`ncpus`** *(string, optional)*
* **`ncpus`** *(string, OPTIONAL)*
### Example
```json
@ -52,13 +51,13 @@ cappedCPU is mapped to capped-cpu in zonecfg(1M) man page.
}
```
## cappedMemory
## <a name="configSolarisCappedMemory" />cappedMemory
The physical and swap caps on the memory that can be used by this application container.
A scale (K, M, G, T) can be applied to the value for each of these numbers (for example, 1M is one megabyte).
cappedMemory is mapped to capped-memory in zonecfg(1M) man page.
cappedMemory is mapped to `capped-memory` in [zonecfg(1M)][zonecfg.1m] man page.
* **`physical`** *(string, optional)*
* **`swap`** *(string, optional)*
* **`physical`** *(string, OPTIONAL)*
* **`swap`** *(string, OPTIONAL)*
### Example
```json
@ -68,37 +67,37 @@ cappedMemory is mapped to capped-memory in zonecfg(1M) man page.
}
```
## Network
## <a name="configSolarisNetwork" />Network
### Automatic Network (anet)
### <a name="configSolarisAutomaticNetwork" />Automatic Network (anet)
anet is specified as an array that is used to setup networking for Solaris application containers.
The anet resource represents the automatic creation of a network resource for an application container.
The zones administration daemon, zoneadmd, is the primary process for managing the container's virtual platform.
One of the daemons is responsibilities is creation and teardown of the networks for the container.
For more information on the daemon check the zoneadmd(1M) man page.
One of the daemon's responsibilities is creation and teardown of the networks for the container.
For more information on the daemon see the [zoneadmd(1M)][zoneadmd.1m] man page.
When such a container is started, a temporary VNIC(Virtual NIC) is automatically created for the container.
The VNIC is deleted when the container is torn down.
The following properties can be used to setup automatic networks.
For additional information on properties check zonecfg(1M) man page for the respective release of Solaris.
For additional information on properties, check the [zonecfg(1M)][zonecfg.1m] man page for the respective release of Solaris.
* **`linkname`** *(string, optional)* Specify a name for the automatically created VNIC datalink.
* **`lowerLink`** *(string, optional)* Specify the link over which the VNIC will be created.
Mapped to lower-link in the zonecfg(1M) man page.
* **`allowedAddress`** *(string, optional)* The set of IP addresses that the container can use might be constrained by specifying the allowedAddress property.
* **`linkname`** *(string, OPTIONAL)* Specify a name for the automatically created VNIC datalink.
* **`lowerLink`** *(string, OPTIONAL)* Specify the link over which the VNIC will be created.
Mapped to `lower-link` in the [zonecfg(1M)][zonecfg.1m] man page.
* **`allowedAddress`** *(string, OPTIONAL)* The set of IP addresses that the container can use might be constrained by specifying the allowedAddress property.
If allowedAddress has not been specified, then they can use any IP address on the associated physical interface for the network resource.
Otherwise, when allowedAddress is specified, the container cannot use IP addresses that are not in the allowedAddress list for the physical address.
Mapped to allowed-address in the zonecfg(1M) man page.
* **`configureAllowedAddress`** *(string, optional)* If configureAllowedAddress is set to true, the addresses specified by allowedAddress are automatically configured on the interface each time the container starts.
Mapped to `allowed-address` in the [zonecfg(1M)][zonecfg.1m] man page.
* **`configureAllowedAddress`** *(string, OPTIONAL)* If configureAllowedAddress is set to true, the addresses specified by allowedAddress are automatically configured on the interface each time the container starts.
When it is set to false, the allowedAddress will not be configured on container start.
Mapped to configure-allowed-address in the zonecfg(1M) man page.
* **`defrouter`** *(string, optional)* The value for the optional default router.
* **`macAddress`** *(string, optional)* Set the VNIC's MAC addresses based on the specified value or keyword.
Mapped to `configure-allowed-address` in the [zonecfg(1M)][zonecfg.1m] man page.
* **`defrouter`** *(string, OPTIONAL)* The value for the OPTIONAL default router.
* **`macAddress`** *(string, OPTIONAL)* Set the VNIC`s MAC addresses based on the specified value or keyword.
If not a keyword, it is interpreted as a unicast MAC address.
For a list of the supported keywords please refer to the zonecfg(1M) man page of the respective Solaris release.
Mapped to mac-address in the zonecfg(1M) man page.
* **`linkProtection`** *(string, optional)* Enables one or more types of link protection using comma-separated values.
For a list of the supported keywords please refer to the [zonecfg(1M)][zonecfg.1m] man page of the respective Solaris release.
Mapped to `mac-address` in the [zonecfg(1M)][zonecfg.1m] man page.
* **`linkProtection`** *(string, OPTIONAL)* Enables one or more types of link protection using comma-separated values.
See the protection property in dladm(8) for supported values in respective release of Solaris.
Mapped to link-protection in the zonecfg(1M) man page.
Mapped to `link-protection` in the [zonecfg(1M)][zonecfg.1m] man page.
#### Example
```json
@ -114,3 +113,8 @@ Mapped to link-protection in the zonecfg(1M) man page.
}
]
```
[priv-str-to-set.3c]: http://docs.oracle.com/cd/E53394_01/html/E54766/priv-str-to-set-3c.html
[zoneadmd.1m]: http://docs.oracle.com/cd/E53394_01/html/E54764/zoneadmd-1m.html
[zonecfg.1m_2]: http://docs.oracle.com/cd/E53394_01/html/E54764/zonecfg-1m.html

View File

@ -0,0 +1,99 @@
# <a name="windowsSpecificContainerConfiguration" />Windows-specific Container Configuration
This document describes the schema for the [Windows-specific section](config.md#platform-specific-configuration) of the [container configuration](config.md).
The Windows container specification uses APIs provided by the Windows Host Compute Service (HCS) to fulfill the spec.
## <a name="configWindowsResources" />Resources
You can configure a container's resource limits via the OPTIONAL `resources` field of the Windows configuration.
### <a name="configWindowsMemory" />Memory
`memory` is an OPTIONAL configuration for the container's memory usage.
The following parameters can be specified:
* **`limit`** *(uint64, OPTIONAL)* - sets limit of memory usage in bytes.
* **`reservation`** *(uint64, OPTIONAL)* - sets the guaranteed minimum amount of memory for a container in bytes.
#### Example
```json
"windows": {
"resources": {
"memory": {
"limit": 2097152,
"reservation": 524288
}
}
}
```
### <a name="configWindowsCpu" />CPU
`cpu` is an OPTIONAL configuration for the container's CPU usage.
The following parameters can be specified:
* **`count`** *(uint64, OPTIONAL)* - specifies the number of CPUs available to the container.
* **`shares`** *(uint16, OPTIONAL)* - specifies the relative weight to other containers with CPU shares. The range is from 1 to 10000.
* **`percent`** *(uint, OPTIONAL)* - specifies the percentage of available CPUs usable by the container.
#### Example
```json
"windows": {
"resources": {
"cpu": {
"percent": 50
}
}
}
```
### <a name="configWindowsStorage" />Storage
`storage` is an OPTIONAL configuration for the container's storage usage.
The following parameters can be specified:
* **`iops`** *(uint64, OPTIONAL)* - specifies the maximum IO operations per second for the system drive of the container.
* **`bps`** *(uint64, OPTIONAL)* - specifies the maximum bytes per second for the system drive of the container.
* **`sandboxSize`** *(uint64, OPTIONAL)* - specifies the minimum size of the system drive in bytes.
#### Example
```json
"windows": {
"resources": {
"storage": {
"iops": 50
}
}
}
```
### <a name="configWindowsNetwork" />Network
`network` is an OPTIONAL configuration for the container's network usage.
The following parameters can be specified:
* **`egressBandwidth`** *(uint64, OPTIONAL)* - specified the maximum egress bandwidth in bytes per second for the container.
#### Example
```json
"windows": {
"resources": {
"network": {
"egressBandwidth": 1048577
}
}
}
```

View File

@ -1,18 +1,23 @@
# Container Configuration file
# <a name="containerConfigurationFile" />Container Configuration file
The container's top-level directory MUST contain a configuration file called `config.json`.
The canonical schema is defined in this document, but there is a JSON Schema in [`schema/config-schema.json`](schema/config-schema.json) and Go bindings in [`specs-go/config.go`](specs-go/config.go).
[Platform](spec.md#platforms)-specific configuration schema are defined in the [platform-specific documents](#platform-specific-configuration) linked below.
For properties that are only defined for some [platforms](spec.md#platforms), the Go property has a `platform` tag listing those protocols (e.g. `platform:"linux,solaris"`).
The configuration file contains metadata necessary to implement standard operations against the container.
This includes the process to run, environment variables to inject, sandboxing features to use, etc.
Below is a detailed description of each field defined in the configuration format.
Below is a detailed description of each field defined in the configuration format and valid values are specified.
Platform-specific fields are identified as such.
For all platform-specific configuration values, the scope defined below in the [Platform-specific configuration](#platform-specific-configuration) section applies.
## Specification version
* **`ociVersion`** (string, required) MUST be in [SemVer v2.0.0](http://semver.org/spec/v2.0.0.html) format and specifies the version of the OpenContainer specification with which the bundle complies.
The OpenContainer spec follows semantic versioning and retains forward and backward compatibility within major versions.
For example, if an implementation is compliant with version 1.0.1 of the spec, it is compatible with the complete 1.x series.
## <a name="configSpecificationVersion" />Specification version
* **`ociVersion`** (string, REQUIRED) MUST be in [SemVer v2.0.0][semver-v2.0.0] format and specifies the version of the Open Container Runtime Specification with which the bundle complies.
The Open Container Runtime Specification follows semantic versioning and retains forward and backward compatibility within major versions.
For example, if a configuration is compliant with version 1.1 of this specification, it is compatible with all runtimes that support any 1.1 or later release of this specification, but is not compatible with a runtime that supports 1.0 and not 1.1.
### Example
@ -20,12 +25,15 @@ For example, if an implementation is compliant with version 1.0.1 of the spec, i
"ociVersion": "0.1.0"
```
## Root Configuration
## <a name="configRoot" />Root
Each container has exactly one *root filesystem*, specified in the *root* object:
**`root`** (object, REQUIRED) specifies the container's root filesystem.
* **`path`** (string, required) Specifies the path to the root filesystem for the container. A directory MUST exist at the path declared by the field.
* **`readonly`** (bool, optional) If true then the root filesystem MUST be read-only inside the container. Defaults to false.
* **`path`** (string, REQUIRED) Specifies the path to the root filesystem for the container.
The path is either an absolute path or a relative path to the bundle.
On Linux, for example, with a bundle at `/to/bundle` and a root filesystem at `/to/bundle/rootfs`, the `path` value can be either `/to/bundle/rootfs` or `rootfs`.
A directory MUST exist at the path declared by the field.
* **`readonly`** (bool, OPTIONAL) If true then the root filesystem MUST be read-only inside the container, defaults to false.
### Example
@ -36,17 +44,29 @@ Each container has exactly one *root filesystem*, specified in the *root* object
}
```
## Mounts
## <a name="configMounts" />Mounts
You MAY add array of mount points inside container as `mounts`.
**`mounts`** (array, OPTIONAL) specifies additional mounts beyond [`root`](#root-configuration).
The runtime MUST mount entries in the listed order.
The parameters are similar to the ones in [the Linux mount system call](http://man7.org/linux/man-pages/man2/mount.2.html).
For Linux, the parameters are as documented in [mount(2)][mount.2] system call man page.
For Solaris, the mount entry corresponds to the 'fs' resource in the [zonecfg(1M)][zonecfg.1m] man page.
For Windows, see [mountvol][mountvol] and [SetVolumeMountPoint][set-volume-mountpoint] for details.
* **`destination`** (string, required) Destination of mount point: path inside container.
For the Windows operating system, one mount destination MUST NOT be nested within another mount. (Ex: c:\\foo and c:\\foo\\bar).
* **`type`** (string, required) Linux, *filesystemtype* argument supported by the kernel are listed in */proc/filesystems* (e.g., "minix", "ext2", "ext3", "jfs", "xfs", "reiserfs", "msdos", "proc", "nfs", "iso9660"). Windows: ntfs
* **`source`** (string, required) a device name, but can also be a directory name or a dummy. Windows, the volume name that is the target of the mount point. \\?\Volume\{GUID}\ (on Windows source is called target)
* **`options`** (list of strings, optional) in the fstab format [https://wiki.archlinux.org/index.php/Fstab](https://wiki.archlinux.org/index.php/Fstab).
* **`destination`** (string, REQUIRED) Destination of mount point: path inside container.
This value MUST be an absolute path.
* Windows: one mount destination MUST NOT be nested within another mount (e.g., c:\\foo and c:\\foo\\bar).
* Solaris: corresponds to "dir" of the fs resource in [zonecfg(1M)][zonecfg.1m].
* **`type`** (string, OPTIONAL) The filesystem type of the filesystem to be mounted.
* Linux: valid *filesystemtype* supported by the kernel as listed in */proc/filesystems* (e.g., "minix", "ext2", "ext3", "jfs", "xfs", "reiserfs", "msdos", "proc", "nfs", "iso9660").
* Windows: the type of file system on the volume, e.g. "ntfs".
* Solaris: corresponds to "type" of the fs resource in [zonecfg(1M)][zonecfg.1m].
* **`source`** (string, OPTIONAL) A device name, but can also be a directory name or a dummy.
* Windows: the volume name that is the target of the mount point, \\?\Volume\{GUID}\ (on Windows source is called target).
* Solaris: corresponds to "special" of the fs resource in [zonecfg(1M)][zonecfg.1m].
* **`options`** (list of strings, OPTIONAL) Mount options of the filesystem to be used.
* Linux: supported options are listed in the [mount(8)][mount.8] man page. Note both [filesystem-independent][mount.8-filesystem-independent] and [filesystem-specific][mount.8-filesystem-specific] options are listed.
* Solaris: corresponds to "options" of the fs resource in [zonecfg(1M)][zonecfg.1m].
### Example (Linux)
@ -80,51 +100,87 @@ For the Windows operating system, one mount destination MUST NOT be nested withi
]
```
See links for details about [mountvol](http://ss64.com/nt/mountvol.html) and [SetVolumeMountPoint](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365561(v=vs.85).aspx) in Windows.
### Example (Solaris)
```json
"mounts": [
{
"destination": "/opt/local",
"type": "lofs",
"source": "/usr/local",
"options": ["ro","nodevices"]
},
{
"destination": "/opt/sfw",
"type": "lofs",
"source": "/opt/sfw"
}
]
```
## Process configuration
## <a name="configProcess" />Process
* **`terminal`** (bool, optional) specifies whether you want a terminal attached to that process. Defaults to false.
* **`cwd`** (string, required) is the working directory that will be set for the executable. This value MUST be an absolute path.
* **`env`** (array of strings, optional) contains a list of variables that will be set in the process's environment prior to execution. Elements in the array are specified as Strings in the form "KEY=value". The left hand side MUST consist solely of letters, digits, and underscores `_` as outlined in [IEEE Std 1003.1-2001](http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html).
* **`args`** (array of strings, required) executable to launch and any flags as an array. The executable is the first element and MUST be available at the given path inside of the rootfs. If the executable path is not an absolute path then the search $PATH is interpreted to find the executable.
**`process`** (object, REQUIRED) specifies the container process.
For Linux-based systems the process structure supports the following process specific fields:
* **`terminal`** (bool, OPTIONAL) specifies whether a terminal is attached to that process, defaults to false.
As an example, if set to true on Linux a pseudoterminal pair is allocated for the container process and the pseudoterminal slave is duplicated on the container process's [standard streams][stdin.3].
* **`consoleSize`** (object, OPTIONAL) specifies the console size of the terminal if attached, containing the following properties:
* **`height`** (uint, REQUIRED)
* **`width`** (uint, REQUIRED)
* **`cwd`** (string, REQUIRED) is the working directory that will be set for the executable.
This value MUST be an absolute path.
* **`env`** (array of strings, OPTIONAL) with the same semantics as [IEEE Std 1003.1-2001's `environ`][ieee-1003.1-2001-xbd-c8.1].
* **`args`** (array of strings, REQUIRED) with similar semantics to [IEEE Std 1003.1-2001 `execvp`'s *argv*][ieee-1003.1-2001-xsh-exec].
This specification extends the IEEE standard in that at least one entry is REQUIRED, and that entry is used with the same semantics as `execvp`'s *file*.
* **`capabilities`** (object, OPTIONAL) is an object containing arrays that specifies the sets of capabilities for the process(es) inside the container. Valid values are platform-specific. For example, valid values for Linux are defined in the [capabilities(7)][capabilities.7] man page.
capabilities contains the following properties:
* **`effective`** (array of strings, OPTIONAL) - the `effective` field is an array of effective capabilities that are kept for the process.
* **`bounding`** (array of strings, OPTIONAL) - the `bounding` field is an array of bounding capabilities that are kept for the process.
* **`inheritable`** (array of strings, OPTIONAL) - the `inheritable` field is an array of inheritable capabilities that are kept for the process.
* **`permitted`** (array of strings, OPTIONAL) - the `permitted` field is an array of permitted capabilities that are kept for the process.
* **`ambient`** (array of strings, OPTIONAL) - the `ambient` field is an array of ambient capabilities that are kept for the process.
* **`rlimits`** (array of objects, OPTIONAL) allows setting resource limits for a process inside the container.
Each entry has the following structure:
* **`capabilities`** (array of strings, optional) capabilities is an array that specifies Linux capabilities that can be provided to the process inside the container.
Valid values are the strings for capabilities defined in [the man page](http://man7.org/linux/man-pages/man7/capabilities.7.html)
* **`rlimits`** (array of rlimits, optional) rlimits is an array of rlimits that allows setting resource limits for a process inside the container.
The kernel enforces the `soft` limit for a resource while the `hard` limit acts as a ceiling for that value that could be set by an unprivileged process.
Valid values for the 'type' field are the resources defined in [the man page](http://man7.org/linux/man-pages/man2/setrlimit.2.html).
* **`apparmorProfile`** (string, optional) apparmor profile specifies the name of the apparmor profile that will be used for the container.
For more information about Apparmor, see [Apparmor documentation](https://wiki.ubuntu.com/AppArmor)
* **`selinuxLabel`** (string, optional) SELinux process label specifies the label with which the processes in a container are run.
For more information about SELinux, see [Selinux documentation](http://selinuxproject.org/page/Main_Page)
* **`noNewPrivileges`** (bool, optional) setting `noNewPrivileges` to true prevents the processes in the container from gaining additional privileges.
[The kernel doc](https://www.kernel.org/doc/Documentation/prctl/no_new_privs.txt) has more information on how this is achieved using a prctl system call.
* **`type`** (string, REQUIRED) - the platform resource being limited, for example on Linux as defined in the [setrlimit(2)][setrlimit.2] man page.
* **`soft`** (uint64, REQUIRED) - the value of the limit enforced for the corresponding resource.
* **`hard`** (uint64, REQUIRED) - the ceiling for the soft limit that could be set by an unprivileged process. Only a privileged process (e.g. under Linux: one with the CAP_SYS_RESOURCE capability) can raise a hard limit.
### User
If `rlimits` contains duplicated entries with same `type`, the runtime MUST error out.
* **`noNewPrivileges`** (bool, OPTIONAL) setting `noNewPrivileges` to true prevents the processes in the container from gaining additional privileges.
As an example, the ['no_new_privs'][no-new-privs] article in the kernel documentation has information on how this is achieved using a prctl system call on Linux.
For Linux-based systems the process structure supports the following process specific fields.
* **`apparmorProfile`** (string, OPTIONAL) specifies the name of the AppArmor profile to be applied to processes in the container.
For more information about AppArmor, see [AppArmor documentation][apparmor].
* **`selinuxLabel`** (string, OPTIONAL) specifies the SELinux label to be applied to the processes in the container.
For more information about SELinux, see [SELinux documentation][selinux].
### <a name="configUser" />User
The user for the process is a platform-specific structure that allows specific control over which user the process runs as.
#### Linux and Solaris User
#### <a name="configLinuxAndSolarisUser" />Linux and Solaris User
For Linux and Solaris based systems the user structure has the following fields:
* **`uid`** (int, required) specifies the user ID in the [container namespace][container-namespace].
* **`gid`** (int, required) specifies the group ID in the [container namespace][container-namespace].
* **`additionalGids`** (array of ints, optional) specifies additional group IDs (in the [container namespace][container-namespace]) to be added to the process.
* **`uid`** (int, REQUIRED) specifies the user ID in the [container namespace](glossary.md#container-namespace).
* **`gid`** (int, REQUIRED) specifies the group ID in the [container namespace](glossary.md#container-namespace).
* **`additionalGids`** (array of ints, OPTIONAL) specifies additional group IDs (in the [container namespace](glossary.md#container-namespace) to be added to the process.
_Note: symbolic name for uid and gid, such as uname and gname respectively, are left to upper levels to derive (i.e. `/etc/passwd` parsing, NSS, etc)_
_Note: For Solaris, uid and gid specify the uid and gid of the process inside the container and need not be same as in the host._
### Example (Linux)
```json
"process": {
"terminal": true,
"consoleSize": {
"height": 25,
"width": 80
},
"user": {
"uid": 1,
"gid": 1,
@ -141,11 +197,30 @@ _Note: For Solaris, uid and gid specify the uid and gid of the process inside th
"apparmorProfile": "acme_secure_profile",
"selinuxLabel": "system_u:system_r:svirt_lxc_net_t:s0:c124,c675",
"noNewPrivileges": true,
"capabilities": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"permitted": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"inheritable": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
],
"ambient": [
"CAP_NET_BIND_SERVICE"
]
},
"rlimits": [
{
"type": "RLIMIT_NOFILE",
@ -160,6 +235,10 @@ _Note: For Solaris, uid and gid specify the uid and gid of the process inside th
```json
"process": {
"terminal": true,
"consoleSize": {
"height": 25,
"width": 80
},
"user": {
"uid": 1,
"gid": 1,
@ -172,14 +251,40 @@ _Note: For Solaris, uid and gid specify the uid and gid of the process inside th
"cwd": "/root",
"args": [
"/usr/bin/bash"
]
}
```
#### <a name="configWindowsUser" />Windows User
For Windows based systems the user structure has the following fields:
* **`username`** (string, OPTIONAL) specifies the user name for the process.
### Example (Windows)
```json
"process": {
"terminal": true,
"user": {
"username": "containeradministrator"
},
"env": [
"VARIABLE=1"
],
"cwd": "c:\\foo",
"args": [
"someapp.exe",
]
}
```
## Hostname
## <a name="configHostname" />Hostname
* **`hostname`** (string, optional) as it is accessible to processes running inside. On Linux, you can only set this if your bundle creates a new [UTS namespace][uts-namespace].
* **`hostname`** (string, OPTIONAL) specifies the container's hostname as seen by processes running inside the container.
On Linux, for example, this will change the hostname in the [container](glossary.md#container-namespace) [UTS namespace][uts-namespace.7].
Depending on your [namespace configuration](config-linux.md#namespaces), the container UTS namespace may be the [runtime UTS namespace](glossary.md#runtime-namespace).
### Example
@ -187,14 +292,16 @@ _Note: For Solaris, uid and gid specify the uid and gid of the process inside th
"hostname": "mrsdalloway"
```
## Platform
## <a name="configPlatform" />Platform
* **`os`** (string, required) specifies the operating system family this image targets.
The runtime MUST generate an error if it does not support the configured **`os`**.
**`platform`** (object, REQUIRED) specifies the configuration's target platform.
* **`os`** (string, REQUIRED) specifies the operating system family of the container configuration's specified [`root`](#root-configuration) file system bundle.
The runtime MUST generate an error if it does not support the specified **`os`**.
Bundles SHOULD use, and runtimes SHOULD understand, **`os`** entries listed in the Go Language document for [`$GOOS`][go-environment].
If an operating system is not included in the `$GOOS` documentation, it SHOULD be submitted to this specification for standardization.
* **`arch`** (string, required) specifies the instruction set for which the binaries in the image have been compiled.
The runtime MUST generate an error if it does not support the configured **`arch`**.
* **`arch`** (string, REQUIRED) specifies the instruction set for which the binaries in the specified [`root`](#root-configuration) file system bundle have been compiled.
The runtime MUST generate an error if it does not support the specified **`arch`**.
Values for **`arch`** SHOULD use, and runtimes SHOULD understand, **`arch`** entries listed in the Go Language document for [`$GOARCH`][go-environment].
If an architecture is not included in the `$GOARCH` documentation, it SHOULD be submitted to this specification for standardization.
@ -207,14 +314,18 @@ _Note: For Solaris, uid and gid specify the uid and gid of the process inside th
}
```
## Platform-specific configuration
## <a name="configPlatformSpecificConfiguration" />Platform-specific configuration
[**`platform.os`**](#platform) is used to lookup further platform-specific configuration.
[**`platform.os`**](#platform) is used to specify platform-specific configuration.
Runtime implementations MAY support any valid values for platform-specific fields as part of this configuration.
Implementations MUST error out when invalid values are encountered and MUST generate an error message and error out when encountering valid values it chooses to not support.
* **`linux`** (object, optional) [Linux-specific configuration](config-linux.md).
This SHOULD only be set if **`platform.os`** is `linux`.
* **`solaris`** (object, optional) [Solaris-specific configuration](config-solaris.md).
This SHOULD only be set if **`platform.os`** is `solaris`.
* **`linux`** (object, OPTIONAL) [Linux-specific configuration](config-linux.md).
This MAY be set if **`platform.os`** is `linux` and MUST NOT be set otherwise.
* **`windows`** (object, OPTIONAL) [Windows-specific configuration](config-windows.md).
This MAY be set if **`platform.os`** is `windows` and MUST NOT be set otherwise.
* **`solaris`** (object, OPTIONAL) [Solaris-specific configuration](config-solaris.md).
This MAY be set if **`platform.os`** is `solaris` and MUST NOT be set otherwise.
### Example (Linux)
@ -234,46 +345,46 @@ _Note: For Solaris, uid and gid specify the uid and gid of the process inside th
}
```
## Hooks
## <a name="configHooks" />Hooks
Lifecycle hooks allow custom events for different points in a container's runtime.
Presently there are `Prestart`, `Poststart` and `Poststop`.
Hooks allow for the configuration of custom actions related to the [lifecycle](runtime.md#lifecycle) of the container.
* [`Prestart`](#prestart) is a list of hooks to be run before the container process is executed
* [`Poststart`](#poststart) is a list of hooks to be run immediately after the container process is started
* [`Poststop`](#poststop) is a list of hooks to be run after the container process exits
* **`hooks`** (object, OPTIONAL) MAY contain any of the following properties:
* **`prestart`** (array, OPTIONAL) is an array of [pre-start hooks](#prestart).
Entries in the array contain the following properties:
* **`path`** (string, REQUIRED) with similar semantics to [IEEE Std 1003.1-2001 `execv`'s *path*][ieee-1003.1-2001-xsh-exec].
This specification extends the IEEE standard in that **`path`** MUST be absolute.
* **`args`** (array of strings, OPTIONAL) with the same semantics as [IEEE Std 1003.1-2001 `execv`'s *argv*][ieee-1003.1-2001-xsh-exec].
* **`env`** (array of strings, OPTIONAL) with the same semantics as [IEEE Std 1003.1-2001's `environ`][ieee-1003.1-2001-xbd-c8.1].
* **`timeout`** (int, OPTIONAL) is the number of seconds before aborting the hook.
* **`poststart`** (array, OPTIONAL) is an array of [post-start hooks](#poststart).
Entries in the array have the same schema as pre-start entries.
* **`poststop`** (array, OPTIONAL) is an array of [post-stop hooks](#poststop).
Entries in the array have the same schema as pre-start entries.
Hooks allow one to run code before/after various lifecycle events of the container.
Hooks allow users to specify programs to run before or after various lifecycle events.
Hooks MUST be called in the listed order.
The state of the container is passed to the hooks over stdin, so the hooks could get the information they need to do their work.
The [state](runtime.md#state) of the container MUST be passed to hooks over stdin so that they may do work appropriate to the current state of the container.
Hook paths are absolute and are executed from the host's filesystem in the [runtime namespace][runtime-namespace].
### <a name="configHooksPrestart" />Prestart
### Prestart
The pre-start hooks MUST be called after the [`start`](runtime.md#start) operation is called but [before the user-specified program command is executed](runtime.md#lifecycle).
On Linux, for example, they are called after the container namespaces are created, so they provide an opportunity to customize the container (e.g. the network namespace could be specified in this hook).
The pre-start hooks are called after the container process is spawned, but before the user supplied command is executed.
They are called after the container namespaces are created on Linux, so they provide an opportunity to customize the container.
In Linux, for e.g., the network namespace could be configured in this hook.
### <a name="configHooksPoststart" />Poststart
If a hook returns a non-zero exit code, then an error including the exit code and the stderr is returned to the caller and the container is torn down.
The post-start hooks MUST be called [after the user-specified process is executed](runtime#lifecycle) but before the [`start`](runtime.md#start) operation returns.
For example, this hook can notify the user that the container process is spawned.
### Poststart
### <a name="configHooksPoststop" />Poststop
The post-start hooks are called after the user process is started.
For example this hook can notify user that real process is spawned.
If a hook returns a non-zero exit code, then an error is logged and the remaining hooks are executed.
### Poststop
The post-stop hooks are called after the container process is stopped.
Cleanup or debugging could be performed in such a hook.
If a hook returns a non-zero exit code, then an error is logged and the remaining hooks are executed.
The post-stop hooks MUST be called [after the container is deleted](runtime#lifecycle) but before the [`delete`](runtime.md#delete) operation returns.
Cleanup or debugging functions are examples of such a hook.
### Example
```json
"hooks" : {
"hooks": {
"prestart": [
{
"path": "/usr/bin/fix-mounts",
@ -299,24 +410,33 @@ If a hook returns a non-zero exit code, then an error is logged and the remainin
}
```
`path` is required for a hook.
`args` and `env` are optional.
`timeout` is the number of seconds before aborting the hook.
The semantics are the same as `Path`, `Args` and `Env` in [golang Cmd](https://golang.org/pkg/os/exec/#Cmd).
## <a name="configAnnotations" />Annotations
## Annotations
This OPTIONAL property contains arbitrary metadata for the container.
**`annotations`** (object, OPTIONAL) contains arbitrary metadata for the container.
This information MAY be structured or unstructured.
Annotations are key-value maps.
Annotations MUST be a key-value map.
If there are no annotations then this property MAY either be absent or an empty map.
Keys MUST be strings.
Keys MUST be unique within this map.
Keys MUST NOT be an empty string.
Keys SHOULD be named using a reverse domain notation - e.g. `com.example.myKey`.
Keys using the `org.opencontainers` namespace are reserved and MUST NOT be used by subsequent specifications.
Implementations that are reading/processing this configuration file MUST NOT generate an error if they encounter an unknown annotation key.
Values MUST be strings.
Values MAY be an empty string.
```json
"annotations": {
"key1" : "value1",
"key2" : "value2"
"com.example.gpu-cores": "2"
}
```
## <a name="configExtensibility" />Extensibility
Implementations that are reading/processing this configuration file MUST NOT generate an error if they encounter an unknown property.
Instead they MUST ignore unknown properties.
## Configuration Schema Example
Here is a full example `config.json` for reference.
@ -346,11 +466,30 @@ Here is a full example `config.json` for reference.
"TERM=xterm"
],
"cwd": "/",
"capabilities": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"permitted": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"inheritable": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
],
"ambient": [
"CAP_NET_BIND_SERVICE"
]
},
"rlimits": [
{
"type": "RLIMIT_CORE",
@ -618,12 +757,17 @@ Here is a full example `config.json` for reference.
"seccomp": {
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": [
"SCMP_ARCH_X86"
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"name": "getcwd",
"action": "SCMP_ACT_ERRNO"
"names": [
"getcwd",
"chmod"
],
"action": "SCMP_ACT_ERRNO",
"comment": "stop exploit x"
}
]
},
@ -667,13 +811,29 @@ Here is a full example `config.json` for reference.
"mountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c715,c811"
},
"annotations": {
"key1": "value1",
"key2": "value2"
"com.example.key1": "value1",
"com.example.key2": "value2"
}
}
```
[container-namespace]: glossary.md#container-namespace
[apparmor]: https://wiki.ubuntu.com/AppArmor
[selinux]:http://selinuxproject.org/page/Main_Page
[no-new-privs]: https://www.kernel.org/doc/Documentation/prctl/no_new_privs.txt
[semver-v2.0.0]: http://semver.org/spec/v2.0.0.html
[go-environment]: https://golang.org/doc/install/source#environment
[runtime-namespace]: glossary.md#runtime-namespace
[uts-namespace]: http://man7.org/linux/man-pages/man7/namespaces.7.html
[ieee-1003.1-2001-xbd-c8.1]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html#tag_08_01
[ieee-1003.1-2001-xsh-exec]: http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
[mountvol]: http://ss64.com/nt/mountvol.html
[set-volume-mountpoint]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365561(v=vs.85).aspx
[capabilities.7]: http://man7.org/linux/man-pages/man7/capabilities.7.html
[mount.2]: http://man7.org/linux/man-pages/man2/mount.2.html
[mount.8]: http://man7.org/linux/man-pages/man8/mount.8.html
[mount.8-filesystem-independent]: http://man7.org/linux/man-pages/man8/mount.8.html#FILESYSTEM-INDEPENDENT_MOUNT%20OPTIONS
[mount.8-filesystem-specific]: http://man7.org/linux/man-pages/man8/mount.8.html#FILESYSTEM-SPECIFIC_MOUNT%20OPTIONS
[setrlimit.2]: http://man7.org/linux/man-pages/man2/setrlimit.2.html
[stdin.3]: http://man7.org/linux/man-pages/man3/stdin.3.html
[uts-namespace.7]: http://man7.org/linux/man-pages/man7/namespaces.7.html
[zonecfg.1m]: http://docs.oracle.com/cd/E53394_01/html/E54764/zonecfg-1m.html

View File

@ -1,36 +1,40 @@
# Glossary
# <a name="glossary" />Glossary
## Bundle
## <a name="glossaryBundle" />Bundle
A [directory structure](bundle.md) that is written ahead of time, distributed, and used to seed the runtime for creating a [container](#container) and launching a process within it.
## Configuration
## <a name="glossaryConfiguration" />Configuration
The [`config.json`](config.md) file in a [bundle](#bundle) which defines the intended [container](#container) and container process.
## Container
## <a name="glossaryContainer" />Container
An environment for executing processes with configurable isolation and resource limitations.
For example, namespaces, resource limits, and mounts are all part of the container environment.
## Container namespace
## <a name="glossaryContainerNamespace" />Container namespace
On Linux, a leaf in the [namespace][namespaces.7] hierarchy in which the [configured process](config.md#process-configuration) executes.
On Linux, a leaf in the [namespace][namespaces.7] hierarchy in which the [configured process](config.md#process) executes.
## JSON
## <a name="glossaryJson" />JSON
All configuration [JSON][] MUST be encoded in [UTF-8][].
JSON objects MUST NOT include duplicate names.
The order of entries in JSON objects is not significant.
## Runtime
## <a name="glossaryRuntime" />Runtime
An implementation of this specification.
It reads the [configuration files](#configuration) from a [bundle](#bundle), uses that information to create a [container](#container), launches a process inside the container, and performs other [lifecycle actions](runtime.md).
## Runtime namespace
## <a name="glossaryRuntimeNamespace" />Runtime namespace
On Linux, a leaf in the [namespace][namespaces.7] hierarchy from which the [runtime](#runtime) process is executed.
New container namespaces will be created as children of the runtime namespaces.
[JSON]: http://json.org/
[JSON]: https://tools.ietf.org/html/rfc7159
[UTF-8]: http://www.unicode.org/versions/Unicode8.0.0/ch03.pdf
[namespaces.7]: http://man7.org/linux/man-pages/man7/namespaces.7.html

View File

@ -1,18 +1,27 @@
# Implementations
# <a name="implementations" />Implementations
The following sections link to associated projects, some of which are maintained by the OCI and some of which are maintained by external organizations.
If you know of any associated projects that are not listed here, please file a pull request adding a link to that project.
## Runtime (Container)
## <a name"implementationsRuntimeContainer" />Runtime (Container)
* [opencontainers/runc](https://github.com/opencontainers/runc) - Reference implementation of OCI runtime
* [opencontainers/runc][runc] - Reference implementation of OCI runtime
## Runtime (Virtual Machine)
## <a name="implementationsRuntimeVirtualMachine" />Runtime (Virtual Machine)
* [hyperhq/runv](https://github.com/hyperhq/runv) - Hypervisor-based runtime for OCI
* [hyperhq/runv][runv] - Hypervisor-based runtime for OCI
* [01org/cc-oci-runtime][cc-oci] - Hypervisor-based OCI runtime for Intel® Architecture
## Testing & Tools
## <a name="implementationsTestingTools" />Testing & Tools
* [kunalkushwaha/octool](https://github.com/kunalkushwaha/octool) - A config linter and validator.
* [opencontainers/ocitools](https://github.com/opencontainers/ocitools) - A config generator and runtime/bundle testing framework.
* [huawei-openlab/oct](https://github.com/huawei-openlab/oct) - Open Container Testing framework for OCI configuration and runtime
* [kunalkushwaha/octool][octool] - A config linter and validator.
* [huawei-openlab/oct][oct] - Open Container Testing framework for OCI configuration and runtime
* [opencontainers/runtime-tools][runtime-tools] - A config generator and runtime/bundle testing framework.
[runc]: https://github.com/opencontainers/runc
[runv]: https://github.com/hyperhq/runv
[cc-oci]: https://github.com/01org/cc-oci-runtime
[octool]: https://github.com/kunalkushwaha/octool
[oct]: https://github.com/huawei-openlab/oct
[runtime-tools]: https://github.com/opencontainers/runtime-tools

View File

@ -1,4 +1,4 @@
# The 5 principles of Standard Containers
# <a name="the5PrinciplesOfStandardContainers" />The 5 principles of Standard Containers
Define a unit of software delivery called a Standard Container.
The goal of a Standard Container is to encapsulate a software component and all its dependencies in a format that is self-describing and portable, so that any compliant runtime can run it without extra dependencies, regardless of the underlying machine and the contents of the container.
@ -14,22 +14,22 @@ Shipping containers are a fundamental unit of delivery, they can be lifted, stac
Irrespective of their contents, by standardizing the container itself it allowed for a consistent, more streamlined and efficient set of processes to be defined.
For software Standard Containers offer similar functionality by being the fundamental, standardized, unit of delivery for a software package.
## 1. Standard operations
## <a name="standardOperations" />1. Standard operations
Standard Containers define a set of STANDARD OPERATIONS.
They can be created, started, and stopped using standard container tools; copied and snapshotted using standard filesystem tools; and downloaded and uploaded using standard network tools.
## 2. Content-agnostic
## <a name="contentAgnostic" />2. Content-agnostic
Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents.
They are started in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
## 3. Infrastructure-agnostic
## <a name="infrastructureAgnostic" />3. Infrastructure-agnostic
Standard Containers are INFRASTRUCTURE-AGNOSTIC: they can be run in any OCI supported infrastructure.
For example, a standard container can be bundled on a laptop, uploaded to cloud storage, downloaded, run and snapshotted by a build server at a fiber hotel in Virginia, uploaded to 10 staging servers in a home-made private cloud cluster, then sent to 30 production instances across 3 public cloud regions.
## 4. Designed for automation
## <a name="designedForAutomation" />4. Designed for automation
Standard Containers are DESIGNED FOR AUTOMATION: because they offer the same standard operations regardless of content and infrastructure, Standard Containers, are extremely well-suited for automation.
In fact, you could say automation is their secret weapon.
@ -39,7 +39,7 @@ Before Standard Containers, by the time a software component ran in production,
Builds failed, libraries conflicted, mirrors crashed, post-it notes were lost, logs were misplaced, cluster updates were half-broken.
The process was slow, inefficient and cost a fortune - and was entirely different depending on the language and infrastructure provider.
## 5. Industrial-grade delivery
## <a name="industrialGradeDelivery" />5. Industrial-grade delivery
Standard Containers make INDUSTRIAL-GRADE DELIVERY of software a reality.
Leveraging all of the properties listed above, Standard Containers are enabling large and small enterprises to streamline and automate their software delivery pipelines.

View File

@ -1,10 +1,12 @@
# Project docs
# <a name="projectDocs" />Project docs
## Release Process
## <a name="projectReleaseProcess" />Release Process
* Increment version in [`specs-go/version.go`](specs-go/version.go)
* `git commit` version increment
* `git tag` the prior commit (preferably signed tag)
* `make docs` to produce PDF and HTML copies of the spec
* Make a release on [github.com/opencontainers/runtime-spec](https://github.com/opencontainers/runtime-spec/releases) for the version. Attach the produced docs.
* Make a [release][releases] for the version. Attach the produced docs.
[releases]: https://github.com/opencontainers/runtime-spec/releases

View File

@ -1,14 +1,14 @@
# Linux Runtime
# <a name="linuxRuntime" />Linux Runtime
## File descriptors
## <a name="runtimeLinuxFileDescriptors" />File descriptors
By default, only the `stdin`, `stdout` and `stderr` file descriptors are kept open for the application by the runtime.
The runtime MAY pass additional file descriptors to the application to support features such as [socket activation](http://0pointer.de/blog/projects/socket-activated-containers.html).
The runtime MAY pass additional file descriptors to the application to support features such as [socket activation][socket-activated-containers].
Some of the file descriptors MAY be redirected to `/dev/null` even though they are open.
## Dev symbolic links
## <a name="runtimeLinuxDevSymbolicLinks" /> Dev symbolic links
After the container has `/proc` mounted, the following standard symlinks MUST be setup within `/dev/` for the io.
After the container has `/proc` mounted, the following standard symlinks MUST be setup within `/dev/` for the IO.
| Source | Destination |
| --------------- | ----------- |
@ -16,3 +16,6 @@ After the container has `/proc` mounted, the following standard symlinks MUST be
| /proc/self/fd/0 | /dev/stdin |
| /proc/self/fd/1 | /dev/stdout |
| /proc/self/fd/2 | /dev/stderr |
[socket-activated-containers]: http://0pointer.de/blog/projects/socket-activated-containers.html

View File

@ -1,31 +1,35 @@
# Runtime and Lifecycle
# <a name="runtimeAndLifecycle" />Runtime and Lifecycle
## Scope of a Container
## <a name="runtimeScopeContainer" />Scope of a Container
Barring access control concerns, the entity using a runtime to create a container MUST be able to use the operations defined in this specification against that same container.
Whether other entities using the same, or other, instance of the runtime can see that container is out of scope of this specification.
## State
## <a name="runtimeState" />State
The state of a container MUST include, at least, the following properties:
The state of a container includes the following properties:
* **`ociVersion`**: (string) is the OCI specification version used when creating the container.
* **`id`**: (string) is the container's ID.
* **`ociVersion`** (string, REQUIRED) is the OCI specification version used when creating the container.
* **`id`** (string, REQUIRED) is the container's ID.
This MUST be unique across all containers on this host.
There is no requirement that it be unique across hosts.
* **`status`**: (string) is the runtime state of the container.
* **`status`** (string, REQUIRED) is the runtime state of the container.
The value MAY be one of:
* `created` : the container has been created but the user-specified code has not yet been executed
* `running` : the container has been created and the user-specified code is running
* `stopped` : the container has been created and the user-specified code has been executed but is no longer running
Additional values MAY be defined by the runtime, however, they MUST be used to represent new runtime states not defined above.
* **`pid`**: (int) is the ID of the main process within the container, as seen by the host.
* **`bundlePath`**: (string) is the absolute path to the container's bundle directory.
* `creating`: the container is being created (step 2 in the [lifecycle](#lifecycle))
* `created`: the runtime has finished the [create operation](#create) (after step 2 in the [lifecycle](#lifecycle)), and the container process has neither exited nor executed the user-specified program
* `running`: the container process has executed the user-specified program but has not exited (after step 4 in the [lifecycle](#lifecycle))
* `stopped`: the container process has exited (step 5 in the [lifecycle](#lifecycle))
Additional values MAY be defined by the runtime, however, they MUST be used to represent new runtime states not defined above.
* **`pid`** (int, REQUIRED when `status` is `created` or `running`) is the ID of the container process, as seen by the host.
* **`bundle`** (string, REQUIRED) is the absolute path to the container's bundle directory.
This is provided so that consumers can find the container's configuration and root filesystem on the host.
* **`annotations`**: (map) contains the list of annotations associated with the container.
* **`annotations`** (map, OPTIONAL) contains the list of annotations associated with the container.
If no annotations were provided then this property MAY either be absent or an empty map.
The state MAY include additional properties.
When serialized in JSON, the format MUST adhere to the following pattern:
```json
@ -34,7 +38,7 @@ When serialized in JSON, the format MUST adhere to the following pattern:
"id": "oci-container1",
"status": "running",
"pid": 4422,
"bundlePath": "/containers/redis",
"bundle": "/containers/redis",
"annotations": {
"myKey": "myValue"
}
@ -43,50 +47,62 @@ When serialized in JSON, the format MUST adhere to the following pattern:
See [Query State](#query-state) for information on retrieving the state of a container.
## Lifecycle
## <a name="runtimeLifecycle" />Lifecycle
The lifecycle describes the timeline of events that happen from when a container is created to when it ceases to exist.
1. OCI compliant runtime's `create` command is invoked with a reference to the location of the bundle and a unique identifier.
1. OCI compliant runtime's [`create`](runtime.md#create) command is invoked with a reference to the location of the bundle and a unique identifier.
2. The container's runtime environment MUST be created according to the configuration in [`config.json`](config.md).
If the runtime is unable to create the environment specified in the [`config.json`](config.md), it MUST generate an error.
While the resources requested in the [`config.json`](config.md) MUST be created, the user-specified code (from [`process`](config.md#process-configuration) MUST NOT be run at this time.
Any updates to `config.json` after this step MUST NOT affect the container.
If the runtime is unable to create the environment specified in the [`config.json`](config.md), it MUST [generate an error](#errors).
While the resources requested in the [`config.json`](config.md) MUST be created, the user-specified program (from [`process`](config.md#process)) MUST NOT be run at this time.
Any updates to [`config.json`](config.md) after this step MUST NOT affect the container.
3. Once the container is created additional actions MAY be performed based on the features the runtime chooses to support.
However, some actions might only be available based on the current state of the container (e.g. only available while it is started).
4. Runtime's `start` command is invoked with the unique identifier of the container.
The runtime MUST run the user-specified code, as specified by [`process`](config.md#process-configuration).
5. The container's process is stopped.
This MAY happen due to them erroring out, exiting, crashing or the runtime's `kill` operation being invoked.
6. Runtime's `delete` command is invoked with the unique identifier of the container.
The container MUST be destroyed by undoing the steps performed during create phase (step 2).
4. Runtime's [`start`](runtime.md#start) command is invoked with the unique identifier of the container.
5. The [prestart hooks](config.md#prestart) MUST be invoked by the runtime.
If any prestart hook fails, the runtime MUST [generate an error](#errors), stop the container, and continue the lifecycle at step 10.
6. The runtime MUST run the user-specified program, as specified by [`process`](config.md#process).
7. The [poststart hooks](config.md#poststart) MUST be invoked by the runtime.
If any poststart hook fails, the runtime MUST [log a warning](#warnings), but the remaining hooks and lifecycle continue as if the hook had succeeded.
8. The container process exits.
This MAY happen due to erroring out, exiting, crashing or the runtime's [`kill`](runtime.md#kill) operation being invoked.
9. Runtime's [`delete`](runtime.md#delete) command is invoked with the unique identifier of the container.
10. The container MUST be destroyed by undoing the steps performed during create phase (step 2).
11. The [poststop hooks](config.md#poststop) MUST be invoked by the runtime.
If any poststop hook fails, the runtime MUST [log a warning](#warnings), but the remaining hooks and lifecycle continue as if the hook had succeeded.
## Errors
## <a name="runtimeErrors" />Errors
In cases where the specified operation generates an error, this specification does not mandate how, or even if, that error is returned or exposed to the user of an implementation.
Unless otherwise stated, generating an error MUST leave the state of the environment as if the operation were never attempted - modulo any possible trivial ancillary changes such as logging.
## Operations
## <a name="runtimeWarnings" />Warnings
In cases where the specified operation logs a warning, this specification does not mandate how, or even if, that warning is returned or exposed to the user of an implementation.
Unless otherwise stated, logging a warning does not change the flow of the operation; it MUST continue as if the warning had not been logged.
## <a name="runtimeOperations" />Operations
OCI compliant runtimes MUST support the following operations, unless the operation is not supported by the base operating system.
Note: these operations are not specifying any command-line APIs, and the paramenters are inputs for general operations.
Note: these operations are not specifying any command-line APIs, and the parameters are inputs for general operations.
### Query State
### <a name="runtimeQueryState" />Query State
`state <container-id>`
This operation MUST generate an error if it is not provided the ID of a container.
Attempting to query a container that does not exist MUST generate an error.
This operation MUST [generate an error](#errors) if it is not provided the ID of a container.
Attempting to query a container that does not exist MUST [generate an error](#errors).
This operation MUST return the state of a container as specified in the [State](#state) section.
### Create
### <a name="runtimeCreate" />Create
`create <container-id> <path-to-bundle>`
This operation MUST generate an error if it is not provided a path to the bundle and the container ID to associate with the container.
If the ID provided is not unique across all containers within the scope of the runtime, or is not valid in any other way, the implementation MUST generate an error and a new container MUST not be created.
This operation MUST [generate an error](#errors) if it is not provided a path to the bundle and the container ID to associate with the container.
If the ID provided is not unique across all containers within the scope of the runtime, or is not valid in any other way, the implementation MUST [generate an error](#errors) and a new container MUST NOT be created.
Using the data in [`config.json`](config.md), this operation MUST create a new container.
This means that all of the resources associated with the container MUST be created, however, the user-specified code MUST NOT be run at this time.
This means that all of the resources associated with the container MUST be created, however, the user-specified program MUST NOT be run at this time.
If the runtime cannot create the container as specified in [`config.json`](config.md), it MUST [generate an error](#errors) and a new container MUST NOT be created.
Upon successful completion of this operation the `status` property of this container MUST be `created`.
@ -95,36 +111,36 @@ Runtime callers who are interested in pre-create validation can run [bundle-vali
Any changes made to the [`config.json`](config.md) file after this operation will not have an effect on the container.
### Start
### <a name="runtimeStart" />Start
`start <container-id>`
This operation MUST generate an error if it is not provided the container ID.
Attempting to start a container that does not exist MUST generate an error.
Attempting to start an already started container MUST have no effect on the container and MUST generate an error.
This operation MUST run the user-specified code as specified by [`process`](config.md#process-configuration).
This operation MUST [generate an error](#errors) if it is not provided the container ID.
Attempting to start a container that does not exist MUST [generate an error](#errors).
Attempting to start an already started container MUST have no effect on the container and MUST [generate an error](#errors).
This operation MUST run the user-specified program as specified by [`process`](config.md#process).
Upon successful completion of this operation the `status` property of this container MUST be `running`.
### Kill
### <a name="runtimeKill" />Kill
`kill <container-id> <signal>`
This operation MUST generate an error if it is not provided the container ID.
Attempting to send a signal to a container that is not running MUST have no effect on the container and MUST generate an error.
This operation MUST [generate an error](#errors) if it is not provided the container ID.
Attempting to send a signal to a container that is not running MUST have no effect on the container and MUST [generate an error](#errors).
This operation MUST send the specified signal to the process in the container.
When the process in the container is stopped, irrespective of it being as a result of a `kill` operation or any other reason, the `status` property of this container MUST be `stopped`.
### Delete
### <a name="runtimeDelete" />Delete
`delete <container-id>`
This operation MUST generate an error if it is not provided the container ID.
Attempting to delete a container that does not exist MUST generate an error.
Attempting to delete a container whose process is still running MUST generate an error.
This operation MUST [generate an error](#errors) if it is not provided the container ID.
Attempting to delete a container that does not exist MUST [generate an error](#errors).
Attempting to delete a container whose process is still running MUST [generate an error](#errors).
Deleting a container MUST delete the resources that were created during the `create` step.
Note that resources associated with the container, but not created by this container, MUST NOT be deleted.
Once a container is deleted its ID MAY be used by a subsequent container.
## Hooks
## <a name="runtimeHooks" />Hooks
Many of the operations specified in this specification have "hooks" that allow for additional actions to be taken before or after each operation.
See [runtime configuration for hooks](./config.md#hooks) for more information.

View File

@ -9,6 +9,7 @@ The layout of the files is as follows:
* [config-schema.json](config-schema.json) - the primary entrypoint for the [configuration](../config.md) schema
* [config-linux.json](config-linux.json) - the [Linux-specific configuration sub-structure](../config-linux.md)
* [config-solaris.json](config-solaris.json) - the [Solaris-specific configuration sub-structure](../config-solaris.md)
* [config-windows.json](config-windows.json) - the [Windows-specific configuration sub-structure](../config-windows.md)
* [state-schema.json](state-schema.json) - the primary entrypoint for the [state JSON](../runtime.md#state) schema
* [defs.json](defs.json) - definitions for general types
* [defs-linux.json](defs-linux.json) - definitions for Linux-specific types
@ -38,3 +39,9 @@ Then use it like:
```bash
./validate config-schema.json <yourpath>/config.json
```
Or like:
```bash
./validate https://raw.githubusercontent.com/opencontainers/runtime-spec/v1.0.0-rc1/schema/schema.json <yourpath>/config.json
```

View File

@ -6,45 +6,24 @@
"properties": {
"devices": {
"id": "https://opencontainers.org/schema/bundle/linux/devices",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Device"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Device"
}
},
"uidMappings": {
"id": "https://opencontainers.org/schema/bundle/linux/uidMappings",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs.json#/definitions/IDMapping"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs.json#/definitions/IDMapping"
}
},
"gidMappings": {
"id": "https://opencontainers.org/schema/bundle/linux/gidMappings",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs.json#/definitions/IDMapping"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs.json#/definitions/IDMapping"
}
},
"namespaces": {
"id": "https://opencontainers.org/schema/bundle/linux/namespaces",
@ -61,6 +40,13 @@
"id": "https://opencontainers.org/schema/bundle/linux/resources",
"type": "object",
"properties": {
"devices": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/devices",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/DeviceCgroup"
}
},
"oomScoreAdj": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/oomScoreAdj",
"type": "integer",
@ -69,12 +55,16 @@
},
"pids": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/pids",
"type": "object",
"properties": {
"limit": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/pids/limit",
"$ref": "defs.json#/definitions/int64"
}
}
},
"required": [
"limit"
]
},
"blockIO": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO",
@ -82,112 +72,82 @@
"properties": {
"blkioWeight": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioWeight",
"$ref": "defs-linux.json#/definitions/blkioWeightPointer"
"$ref": "defs-linux.json#/definitions/blkioWeight"
},
"blkioLeafWeight": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioLeafWeight",
"$ref": "defs-linux.json#/definitions/blkioWeightPointer"
"$ref": "defs-linux.json#/definitions/blkioWeight"
},
"blkioThrottleReadBpsDevice": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleReadBpsDevice",
"oneOf": [
{
"type": "array",
"items": [
{
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer"
}
]
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottle"
}
},
"blkioThrottleWriteBpsDevice": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleWriteBpsDevice",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottle"
}
},
"blkioThrottleReadIopsDevice": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleReadIopsDevice",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottle"
}
},
"blkioThrottleWriteIopsDevice": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleWriteIopsDevice",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceThrottle"
}
},
"blkioWeightDevice": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioWeightDevice",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/blockIODeviceWeightPointer"
"$ref": "defs-linux.json#/definitions/blockIODeviceWeight"
}
}
}
},
"cpu": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu",
"type": "object",
"properties": {
"cpus": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/cpus",
"$ref": "defs.json#/definitions/stringPointer"
"type": "string"
},
"mems": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/mems",
"$ref": "defs.json#/definitions/stringPointer"
"type": "string"
},
"period": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/period",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
},
"quota": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/quota",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/int64"
},
"realtimePeriod": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/realtimePeriod",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
},
"realtimeRuntime": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/realtimeRuntime",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/int64"
},
"shares": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/shares",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
}
},
"type": "object"
}
},
"disableOOMKiller": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/disableOOMKiller",
@ -195,25 +155,22 @@
},
"hugepageLimits": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/hugepageLimits",
"oneOf": [
{
"type": "array",
"items": {
"type": "object",
"properties": {
"pageSize": {
"type": "string"
},
"limit": {
"$ref": "defs.json#/definitions/uint64"
}
}
"type": "array",
"items": {
"type": "object",
"properties": {
"pageSize": {
"type": "string"
},
"limit": {
"$ref": "defs.json#/definitions/uint64"
}
},
{
"type": "null"
}
]
"required": [
"pageSize",
"limit"
]
}
},
"memory": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/memory",
@ -221,23 +178,27 @@
"properties": {
"kernel": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/memory/kernel",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
},
"kernelTCP": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/memory/kernelTCP",
"$ref": "defs.json#/definitions/uint64"
},
"limit": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/memory/limit",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
},
"reservation": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/memory/reservation",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
},
"swap": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/memory/swap",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
},
"swappiness": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/memory/swappiness",
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
}
}
},
@ -251,31 +212,18 @@
},
"priorities": {
"id": "https://opencontainers.org/schema/bundle/linux/resources/network/priorities",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/NetworkInterfacePriority"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/NetworkInterfacePriority"
}
}
}
}
}
},
"cgroupsPath": {
"oneOf": [
{
"type": "null"
},
{
"type": "string"
}
]
"id": "https://opencontainers.org/schema/bundle/linux/cgroupsPath",
"type": "string"
},
"rootfsPropagation": {
"id": "https://opencontainers.org/schema/bundle/linux/rootfsPropagation",
@ -283,6 +231,7 @@
},
"seccomp": {
"id": "https://opencontainers.org/schema/bundle/linux/seccomp",
"type": "object",
"properties": {
"defaultAction": {
"id": "https://opencontainers.org/schema/bundle/linux/seccomp/defaultAction",
@ -290,17 +239,10 @@
},
"architectures": {
"id": "https://opencontainers.org/schema/bundle/linux/seccomp/architectures",
"oneOf": [
{
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/SeccompArch"
}
},
{
"type": "null"
}
]
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/SeccompArch"
}
},
"syscalls": {
"id": "https://opencontainers.org/schema/bundle/linux/seccomp/syscalls",
@ -309,19 +251,11 @@
"$ref": "defs-linux.json#/definitions/Syscall"
}
}
},
"type": "object"
}
},
"sysctl": {
"id": "https://opencontainers.org/schema/bundle/linux/sysctl",
"oneOf": [
{
"$ref": "defs.json#/definitions/mapStringString"
},
{
"type": "null"
}
]
"$ref": "defs.json#/definitions/mapStringString"
},
"maskedPaths": {
"id": "https://opencontainers.org/schema/bundle/linux/maskedPaths",

View File

@ -56,9 +56,12 @@
}
},
"root": {
"description": "The path to the root filesystem for the container.",
"description": "Configures the container's root filesystem.",
"id": "https://opencontainers.org/schema/bundle/root",
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"id": "https://opencontainers.org/schema/bundle/root/path",
@ -82,6 +85,24 @@
"id": "https://opencontainers.org/schema/bundle/process/args",
"$ref": "defs.json#/definitions/ArrayOfStrings"
},
"consoleSize": {
"id": "https://opencontainers.org/schema/bundle/process/consoleSize",
"type": "object",
"required": [
"height",
"width"
],
"properties": {
"height": {
"id": "https://opencontainers.org/schema/bundle/process/consoleSize/height",
"$ref": "defs.json#/definitions/uint64"
},
"width": {
"id": "https://opencontainers.org/schema/bundle/process/consoleSize/width",
"$ref": "defs.json#/definitions/uint64"
}
}
},
"cwd": {
"id": "https://opencontainers.org/schema/bundle/process/cwd",
"type": "string"
@ -114,9 +135,43 @@
},
"capabilities": {
"id": "https://opencontainers.org/schema/bundle/process/linux/capabilities",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Capability"
"type": "object",
"properties": {
"bounding": {
"id": "https://opencontainers.org/schema/bundle/process/linux/capabilities/bounding",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Capability"
}
},
"permitted": {
"id": "https://opencontainers.org/schema/bundle/process/linux/capabilities/permitted",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Capability"
}
},
"effective": {
"id": "https://opencontainers.org/schema/bundle/process/linux/capabilities/effective",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Capability"
}
},
"inheritable": {
"id": "https://opencontainers.org/schema/bundle/process/linux/capabilities/inheritable",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Capability"
}
},
"ambient": {
"id": "https://opencontainers.org/schema/bundle/process/linux/capabilities/ambient",
"type": "array",
"items": {
"$ref": "defs-linux.json#/definitions/Capability"
}
}
}
},
"apparmorProfile": {
@ -137,6 +192,11 @@
"items": {
"id": "https://opencontainers.org/schema/bundle/linux/rlimits/0",
"type": "object",
"required": [
"type",
"soft",
"hard"
],
"properties": {
"hard": {
"id": "https://opencontainers.org/schema/bundle/linux/rlimits/0/hard",
@ -161,14 +221,15 @@
},
"solaris": {
"$ref": "config-solaris.json#/solaris"
},
"windows": {
"$ref": "config-windows.json#/windows"
}
},
"required": [
"ociVersion",
"platform",
"process",
"root",
"mounts",
"hooks"
"root"
]
}

View File

@ -0,0 +1,75 @@
{
"windows": {
"description": "Windows platform-specific configurations",
"id": "https://opencontainers.org/schema/bundle/windows",
"type": "object",
"properties": {
"resources": {
"id": "https://opencontainers.org/schema/bundle/windows/resources",
"type": "object",
"properties": {
"memory": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/memory",
"type": "object",
"properties": {
"limit": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/memory/limit",
"$ref": "defs.json#/definitions/uint64"
},
"reservation": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/memory/reservation",
"$ref": "defs.json#/definitions/uint64"
}
}
},
"cpu": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/cpu",
"type": "object",
"properties": {
"count": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/cpu/count",
"$ref": "defs.json#/definitions/uint64"
},
"shares": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/cpu/shares",
"$ref": "defs-windows.json#/definitions/cpuShares"
},
"percent": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/cpu/percent",
"$ref": "defs.json#/definitions/percent"
}
}
},
"storage": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/storage",
"type": "object",
"properties": {
"iops": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/storage/iops",
"$ref": "defs.json#/definitions/uint64"
},
"bps": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/storage/bps",
"$ref": "defs.json#/definitions/uint64"
},
"sandboxSize": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/storage/sandboxSize",
"$ref": "defs.json#/definitions/uint64"
}
}
},
"network": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/network",
"type": "object",
"properties": {
"egressBandwidth": {
"id": "https://opencontainers.org/schema/bundle/windows/resources/network/egressBandwidth",
"$ref": "defs.json#/definitions/uint64"
}
}
}
}
}
}
}
}

View File

@ -18,7 +18,9 @@
"SCMP_ARCH_PPC64",
"SCMP_ARCH_PPC64LE",
"SCMP_ARCH_S390",
"SCMP_ARCH_S390X"
"SCMP_ARCH_S390X",
"SCMP_ARCH_PARISC",
"SCMP_ARCH_PARISC64"
]
},
"SeccompAction": {
@ -44,6 +46,7 @@
]
},
"SyscallArg": {
"type": "object",
"properties": {
"index": {
"$ref": "defs.json#/definitions/uint32"
@ -60,9 +63,12 @@
}
},
"Syscall": {
"type": "object",
"properties": {
"name": {
"type": "string"
"names": {
"type": [
"string"
]
},
"action": {
"$ref": "#/definitions/SeccompAction"
@ -76,17 +82,17 @@
}
},
"Capability": {
"description": "Linux process permissions",
"description": "Linux process capabilities",
"type": "string",
"pattern": "^CAP_([A-Z]|_)+$"
},
"Major": {
"description": "major device number",
"$ref": "defs.json#/definitions/uint16"
"$ref": "defs.json#/definitions/int64"
},
"Minor": {
"description": "minor device number",
"$ref": "defs.json#/definitions/uint16"
"$ref": "defs.json#/definitions/int64"
},
"FileMode": {
"description": "File permissions mode (typically an octal value)",
@ -94,22 +100,21 @@
"minimum": 0,
"maximum": 512
},
"FilePermissions": {
"type": "string"
},
"FileType": {
"description": "Type of a block or special character device",
"type": "string",
"pattern": "^[cbup]$"
},
"Device": {
"type": "object",
"required": [
"type",
"path"
],
"properties": {
"type": {
"$ref": "#/definitions/FileType"
},
"permissions": {
"$ref": "#/definitions/FilePermissions"
},
"path": {
"$ref": "defs.json#/definitions/FilePath"
},
@ -135,17 +140,8 @@
"minimum": 10,
"maximum": 1000
},
"blkioWeightPointer": {
"oneOf": [
{
"$ref": "#/definitions/blkioWeight"
},
{
"type": "null"
}
]
},
"blockIODevice": {
"type": "object",
"properties": {
"major": {
"$ref": "#/definitions/Major"
@ -166,52 +162,58 @@
"$ref": "#/definitions/blockIODevice"
},
{
"type": "object",
"properties": {
"weight": {
"$ref": "#/definitions/blkioWeightPointer"
"$ref": "#/definitions/blkioWeight"
},
"leafWeight": {
"$ref": "#/definitions/blkioWeightPointer"
"$ref": "#/definitions/blkioWeight"
}
}
}
]
},
"blockIODeviceWeightPointer": {
"oneOf": [
{
"$ref": "#/definitions/blockIODeviceWeight"
},
{
"type": "null"
}
]
},
"blockIODeviceThrottle": {
"allOf": [
{
"$ref": "#/definitions/blockIODevice"
},
{
"type": "object",
"properties": {
"rate": {
"$ref": "defs.json#/definitions/uint64Pointer"
"$ref": "defs.json#/definitions/uint64"
}
}
}
]
},
"blockIODeviceThrottlePointer": {
"oneOf": [
{
"$ref": "#/definitions/blockIODeviceThrottle"
"DeviceCgroup": {
"type": "object",
"properties": {
"allow": {
"type": "boolean"
},
{
"type": "null"
"type": {
"type": "string"
},
"major": {
"$ref": "#/definitions/Major"
},
"minor": {
"$ref": "#/definitions/Minor"
},
"access": {
"type": "string"
}
},
"required": [
"allow"
]
},
"NetworkInterfacePriority": {
"type": "object",
"properties": {
"name": {
"type": "string"
@ -219,7 +221,11 @@
"priority": {
"$ref": "defs.json#/definitions/uint32"
}
}
},
"required": [
"name",
"priority"
]
},
"NamespaceType": {
"type": "string",
@ -234,6 +240,7 @@
]
},
"NamespaceReference": {
"type": "object",
"properties": {
"type": {
"$ref": "#/definitions/NamespaceType"
@ -241,7 +248,10 @@
"path": {
"$ref": "defs.json#/definitions/FilePath"
}
}
},
"required": [
"type"
]
}
}
}

View File

@ -0,0 +1,10 @@
{
"definitions": {
"cpuShares": {
"description": "Relative weight to other containers with CPU Shares defined",
"type": "integer",
"minimum": 1,
"maximum": 10000
}
}
}

View File

@ -1,5 +1,5 @@
{
"description": "Definitions used throughout the OpenContainer Specification",
"description": "Definitions used throughout the Open Container Runtime Specification",
"definitions": {
"int8": {
"type": "integer",
@ -41,35 +41,10 @@
"minimum": 0,
"maximum": 18446744073709552000
},
"uint16Pointer": {
"oneOf": [
{
"$ref": "#/definitions/uint16"
},
{
"type": "null"
}
]
},
"uint64Pointer": {
"oneOf": [
{
"$ref": "#/definitions/uint64"
},
{
"type": "null"
}
]
},
"stringPointer": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
"percent": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"mapStringString": {
"type": "object",
@ -104,6 +79,7 @@
"$ref": "#/definitions/ArrayOfStrings"
},
"Hook": {
"type": "object",
"properties": {
"path": {
"$ref": "#/definitions/FilePath"
@ -113,8 +89,14 @@
},
"env": {
"$ref": "#/definitions/Env"
},
"timeout": {
"type": "integer"
}
}
},
"required": [
"path"
]
},
"ArrayOfHooks": {
"type": "array",
@ -123,6 +105,7 @@
}
},
"IDMapping": {
"type": "object",
"properties": {
"hostID": {
"$ref": "#/definitions/uint32"
@ -133,9 +116,15 @@
"size": {
"$ref": "#/definitions/uint32"
}
}
},
"required": [
"hostID",
"containerID",
"size"
]
},
"Mount": {
"type": "object",
"properties": {
"source": {
"$ref": "#/definitions/FilePath"
@ -151,9 +140,7 @@
}
},
"required": [
"destination",
"source",
"type"
"destination"
]
},
"ociVersion": {
@ -161,14 +148,7 @@
"type": "string"
},
"annotations": {
"oneOf": [
{
"$ref": "#/definitions/mapStringString"
},
{
"type": "null"
}
]
"$ref": "#/definitions/mapStringString"
}
}
}

View File

@ -17,6 +17,7 @@
"id": "https://opencontainers.org/schema/runtime/state/status",
"type": "string",
"enum": [
"creating",
"created",
"running",
"stopped"
@ -27,8 +28,8 @@
"type": "integer",
"minimum": 0
},
"bundlePath": {
"id": "https://opencontainers.org/schema/runtime/state/bundlePath",
"bundle": {
"id": "https://opencontainers.org/schema/runtime/state/bundle",
"type": "string"
},
"annotations": {
@ -40,6 +41,6 @@
"id",
"status",
"pid",
"bundlePath"
"bundle"
]
}

View File

@ -5,29 +5,58 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/xeipuuv/gojsonschema"
)
const usage = `Validate is used to check document with specified schema.
You can use validate in following ways:
1.specify document file as an argument
validate <schema.json> <document.json>
2.pass document content through a pipe
cat <document.json> | validate <schema.json>
3.input document content manually, ended with ctrl+d(or your self-defined EOF keys)
validate <schema.json>
[INPUT DOCUMENT CONTENT HERE]
`
func main() {
nargs := len(os.Args[1:])
if nargs == 0 || nargs > 2 {
fmt.Printf("ERROR: usage is: %s <schema.json> [<document.json>]\n", os.Args[0])
fmt.Printf("ERROR: invalid arguments number\n\n%s\n", usage)
os.Exit(1)
}
schemaPath, err := filepath.Abs(os.Args[1])
if err != nil {
fmt.Println(err)
if os.Args[1] == "help" ||
os.Args[1] == "--help" ||
os.Args[1] == "-h" {
fmt.Printf("%s\n", usage)
os.Exit(1)
}
schemaLoader := gojsonschema.NewReferenceLoader("file://" + schemaPath)
schemaPath := os.Args[1]
if !strings.Contains(schemaPath, "://") {
var err error
schemaPath, err = formatFilePath(schemaPath)
if err != nil {
fmt.Printf("ERROR: invalid schema-file path: %s\n", err)
os.Exit(1)
}
schemaPath = "file://" + schemaPath
}
schemaLoader := gojsonschema.NewReferenceLoader(schemaPath)
var documentLoader gojsonschema.JSONLoader
if nargs > 1 {
documentPath, err := filepath.Abs(os.Args[2])
documentPath, err := formatFilePath(os.Args[2])
if err != nil {
fmt.Println(err)
fmt.Printf("ERROR: invalid document-file path: %s\n", err)
os.Exit(1)
}
documentLoader = gojsonschema.NewReferenceLoader("file://" + documentPath)
@ -43,7 +72,8 @@ func main() {
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
if err != nil {
panic(err.Error())
fmt.Println(err)
os.Exit(1)
}
if result.Valid() {
@ -56,3 +86,15 @@ func main() {
os.Exit(1)
}
}
func formatFilePath(path string) (string, error) {
if _, err := os.Stat(path); err != nil {
return "", err
}
absPath, err := filepath.Abs(path)
if err != nil {
return "", err
}
return absPath, nil
}

46
vendor/github.com/opencontainers/runtime-spec/spec.md generated vendored Normal file
View File

@ -0,0 +1,46 @@
# <a name="openContainerInitiativeRuntimeSpecification" />Open Container Initiative Runtime Specification
The [Open Container Initiative][oci] develops specifications for standards on Operating System process and application containers.
# <a name="ociRuntimeSpecAbstract" />Abstract
The OCI Runtime Specification aims to specify the configuration, execution environment, and lifecycle a container.
A container's configuration is specified as the `config.json` for the supported platforms and details the fields that enable the creation of a container.
The execution environment is specified to ensure that applications running inside a container have a consistent environment between runtimes along with common actions defined for the container's lifecycle.
# <a name="ociRuntimeSpecPlatforms" />Platforms
Platforms defined by this specification are:
* `linux`: [runtime.md](runtime.md), [config.md](config.md), [config-linux.md](config-linux.md), and [runtime-linux.md](runtime-linux.md).
* `solaris`: [runtime.md](runtime.md), [config.md](config.md), and [config-solaris.md](config-solaris.md).
* `windows`: [runtime.md](runtime.md), [config.md](config.md), and [config-windows.md](config-windows.md).
# <a name="ociRuntimeSpecTOC" />Table of Contents
- [Introduction](spec.md)
- [Notational Conventions](#notational-conventions)
- [Container Principles](principles.md)
- [Filesystem Bundle](bundle.md)
- [Runtime and Lifecycle](runtime.md)
- [Linux-specific Runtime and Lifecycle](runtime-linux.md)
- [Configuration](config.md)
- [Linux-specific Configuration](config-linux.md)
- [Solaris-specific Configuration](config-solaris.md)
- [Windows-specific Configuration](config-windows.md)
- [Glossary](glossary.md)
# <a name="ociRuntimeSpecNotationalConventions" />Notational Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC 2119][rfc2119].
The key words "unspecified", "undefined", and "implementation-defined" are to be interpreted as described in the [rationale for the C99 standard][c99-unspecified].
An implementation is not compliant for a given CPU architecture if it fails to satisfy one or more of the MUST, REQUIRED, or SHALL requirements for the [platforms](#platforms) it implements.
An implementation is compliant for a given CPU architecture if it satisfies all the MUST, REQUIRED, and SHALL requirements for the [platforms](#platforms) it implements.
[c99-unspecified]: http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf#page=18
[oci]: http://www.opencontainers.org
[rfc2119]: http://tools.ietf.org/html/rfc2119

View File

@ -4,33 +4,37 @@ import "os"
// Spec is the base configuration for the container.
type Spec struct {
// Version is the version of the specification that is supported.
// Version of the Open Container Runtime Specification with which the bundle complies.
Version string `json:"ociVersion"`
// Platform is the host information for OS and Arch.
// Platform specifies the configuration's target platform.
Platform Platform `json:"platform"`
// Process is the container's main process.
// Process configures the container process.
Process Process `json:"process"`
// Root is the root information for the container's filesystem.
// Root configures the container's root filesystem.
Root Root `json:"root"`
// Hostname is the container's host name.
// Hostname configures the container's hostname.
Hostname string `json:"hostname,omitempty"`
// Mounts profile configuration for adding mounts to the container's filesystem.
// Mounts configures additional mounts (on top of Root).
Mounts []Mount `json:"mounts,omitempty"`
// Hooks are the commands run at various lifecycle events of the container.
Hooks Hooks `json:"hooks"`
// Annotations is an unstructured key value map that may be set by external tools to store and retrieve arbitrary metadata.
// Hooks configures callbacks for container lifecycle events.
Hooks *Hooks `json:"hooks,omitempty"`
// Annotations contains arbitrary metadata for the container.
Annotations map[string]string `json:"annotations,omitempty"`
// Linux is platform specific configuration for Linux based containers.
Linux *Linux `json:"linux,omitempty" platform:"linux"`
// Solaris is platform specific configuration for Solaris containers.
Solaris *Solaris `json:"solaris,omitempty" platform:"solaris"`
// Windows is platform specific configuration for Windows based containers, including Hyper-V containers.
Windows *Windows `json:"windows,omitempty" platform:"windows"`
}
// Process contains information to start a specific application inside the container.
type Process struct {
// Terminal creates an interactive terminal for the container.
Terminal bool `json:"terminal,omitempty"`
// ConsoleSize specifies the size of the console.
ConsoleSize Box `json:"consoleSize,omitempty"`
// User specifies user information for the process.
User User `json:"user"`
// Args specifies the binary and arguments for the application to execute.
@ -40,28 +44,51 @@ type Process struct {
// Cwd is the current working directory for the process and must be
// relative to the container's root.
Cwd string `json:"cwd"`
// Capabilities are Linux capabilities that are kept for the container.
Capabilities []string `json:"capabilities,omitempty" platform:"linux"`
// Capabilities are Linux capabilities that are kept for the process.
Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"`
// Rlimits specifies rlimit options to apply to the process.
Rlimits []Rlimit `json:"rlimits,omitempty"`
Rlimits []LinuxRlimit `json:"rlimits,omitempty" platform:"linux"`
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
NoNewPrivileges bool `json:"noNewPrivileges,omitempty"`
// ApparmorProfile specifies the apparmor profile for the container. (this field is platform dependent)
NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"`
// ApparmorProfile specifies the apparmor profile for the container.
ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"`
// SelinuxLabel specifies the selinux context that the container process is run as. (this field is platform dependent)
// SelinuxLabel specifies the selinux context that the container process is run as.
SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"`
}
// User specifies Linux/Solaris specific user and group information for the container's
// main process.
// LinuxCapabilities specifies the whitelist of capabilities that are kept for a process.
// http://man7.org/linux/man-pages/man7/capabilities.7.html
type LinuxCapabilities struct {
// Bounding is the set of capabilities checked by the kernel.
Bounding []string `json:"bounding,omitempty" platform:"linux"`
// Effective is the set of capabilities checked by the kernel.
Effective []string `json:"effective,omitempty" platform:"linux"`
// Inheritable is the capabilities preserved across execve.
Inheritable []string `json:"inheritable,omitempty" platform:"linux"`
// Permitted is the limiting superset for effective capabilities.
Permitted []string `json:"permitted,omitempty" platform:"linux"`
// Ambient is the ambient set of capabilities that are kept.
Ambient []string `json:"ambient,omitempty" platform:"linux"`
}
// Box specifies dimensions of a rectangle. Used for specifying the size of a console.
type Box struct {
// Height is the vertical dimension of a box.
Height uint `json:"height"`
// Width is the horizontal dimension of a box.
Width uint `json:"width"`
}
// User specifies specific user (and group) information for the container process.
type User struct {
// UID is the user id. (this field is platform dependent)
// UID is the user id.
UID uint32 `json:"uid" platform:"linux,solaris"`
// GID is the group id. (this field is platform dependent)
// GID is the group id.
GID uint32 `json:"gid" platform:"linux,solaris"`
// AdditionalGids are additional group ids set for the container's process. (this field is platform dependent)
// AdditionalGids are additional group ids set for the container's process.
AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"`
// Username is the user name.
Username string `json:"username,omitempty" platform:"windows"`
}
// Root contains information about the container's root filesystem on the host.
@ -86,10 +113,10 @@ type Mount struct {
// Destination is the path where the mount will be placed relative to the container's root. The path and child directories MUST exist, a runtime MUST NOT create directories automatically to a mount point.
Destination string `json:"destination"`
// Type specifies the mount kind.
Type string `json:"type"`
Type string `json:"type,omitempty"`
// Source specifies the source path of the mount. In the case of bind mounts on
// Linux based systems this would be the file on the host.
Source string `json:"source"`
Source string `json:"source,omitempty"`
// Options are fstab style mount options.
Options []string `json:"options,omitempty"`
}
@ -116,24 +143,24 @@ type Hooks struct {
// Linux contains platform specific configuration for Linux based containers.
type Linux struct {
// UIDMapping specifies user mappings for supporting user namespaces on Linux.
UIDMappings []IDMapping `json:"uidMappings,omitempty"`
UIDMappings []LinuxIDMapping `json:"uidMappings,omitempty"`
// GIDMapping specifies group mappings for supporting user namespaces on Linux.
GIDMappings []IDMapping `json:"gidMappings,omitempty"`
GIDMappings []LinuxIDMapping `json:"gidMappings,omitempty"`
// Sysctl are a set of key value pairs that are set for the container on start
Sysctl map[string]string `json:"sysctl,omitempty"`
// Resources contain cgroup information for handling resource constraints
// for the container
Resources *Resources `json:"resources,omitempty"`
Resources *LinuxResources `json:"resources,omitempty"`
// CgroupsPath specifies the path to cgroups that are created and/or joined by the container.
// The path is expected to be relative to the cgroups mountpoint.
// If resources are specified, the cgroups at CgroupsPath will be updated based on resources.
CgroupsPath *string `json:"cgroupsPath,omitempty"`
CgroupsPath string `json:"cgroupsPath,omitempty"`
// Namespaces contains the namespaces that are created and/or joined by the container
Namespaces []Namespace `json:"namespaces,omitempty"`
Namespaces []LinuxNamespace `json:"namespaces,omitempty"`
// Devices are a list of device nodes that are created for the container
Devices []Device `json:"devices,omitempty"`
Devices []LinuxDevice `json:"devices,omitempty"`
// Seccomp specifies the seccomp security settings for the container.
Seccomp *Seccomp `json:"seccomp,omitempty"`
Seccomp *LinuxSeccomp `json:"seccomp,omitempty"`
// RootfsPropagation is the rootfs mount propagation mode for the container.
RootfsPropagation string `json:"rootfsPropagation,omitempty"`
// MaskedPaths masks over the provided paths inside the container.
@ -144,21 +171,21 @@ type Linux struct {
MountLabel string `json:"mountLabel,omitempty"`
}
// Namespace is the configuration for a Linux namespace
type Namespace struct {
// LinuxNamespace is the configuration for a Linux namespace
type LinuxNamespace struct {
// Type is the type of Linux namespace
Type NamespaceType `json:"type"`
Type LinuxNamespaceType `json:"type"`
// Path is a path to an existing namespace persisted on disk that can be joined
// and is of the same type
Path string `json:"path,omitempty"`
}
// NamespaceType is one of the Linux namespaces
type NamespaceType string
// LinuxNamespaceType is one of the Linux namespaces
type LinuxNamespaceType string
const (
// PIDNamespace for isolating process IDs
PIDNamespace NamespaceType = "pid"
PIDNamespace LinuxNamespaceType = "pid"
// NetworkNamespace for isolating network devices, stacks, ports, etc
NetworkNamespace = "network"
// MountNamespace for isolating mount points
@ -173,18 +200,18 @@ const (
CgroupNamespace = "cgroup"
)
// IDMapping specifies UID/GID mappings
type IDMapping struct {
// HostID is the UID/GID of the host user or group
// LinuxIDMapping specifies UID/GID mappings
type LinuxIDMapping struct {
// HostID is the starting UID/GID on the host to be mapped to 'ContainerID'
HostID uint32 `json:"hostID"`
// ContainerID is the UID/GID of the container's user or group
// ContainerID is the starting UID/GID in the container
ContainerID uint32 `json:"containerID"`
// Size is the length of the range of IDs mapped between the two namespaces
// Size is the number of IDs to be mapped
Size uint32 `json:"size"`
}
// Rlimit type and restrictions
type Rlimit struct {
// LinuxRlimit type and restrictions
type LinuxRlimit struct {
// Type of the rlimit to set
Type string `json:"type"`
// Hard is the hard limit for the specified type
@ -193,66 +220,66 @@ type Rlimit struct {
Soft uint64 `json:"soft"`
}
// HugepageLimit structure corresponds to limiting kernel hugepages
type HugepageLimit struct {
// LinuxHugepageLimit structure corresponds to limiting kernel hugepages
type LinuxHugepageLimit struct {
// Pagesize is the hugepage size
Pagesize *string `json:"pageSize,omitempty"`
Pagesize string `json:"pageSize"`
// Limit is the limit of "hugepagesize" hugetlb usage
Limit *uint64 `json:"limit,omitempty"`
Limit uint64 `json:"limit"`
}
// InterfacePriority for network interfaces
type InterfacePriority struct {
// LinuxInterfacePriority for network interfaces
type LinuxInterfacePriority struct {
// Name is the name of the network interface
Name string `json:"name"`
// Priority for the interface
Priority uint32 `json:"priority"`
}
// blockIODevice holds major:minor format supported in blkio cgroup
type blockIODevice struct {
// linuxBlockIODevice holds major:minor format supported in blkio cgroup
type linuxBlockIODevice struct {
// Major is the device's major number.
Major int64 `json:"major"`
// Minor is the device's minor number.
Minor int64 `json:"minor"`
}
// WeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice
type WeightDevice struct {
blockIODevice
// LinuxWeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice
type LinuxWeightDevice struct {
linuxBlockIODevice
// Weight is the bandwidth rate for the device, range is from 10 to 1000
Weight *uint16 `json:"weight,omitempty"`
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
LeafWeight *uint16 `json:"leafWeight,omitempty"`
}
// ThrottleDevice struct holds a `major:minor rate_per_second` pair
type ThrottleDevice struct {
blockIODevice
// LinuxThrottleDevice struct holds a `major:minor rate_per_second` pair
type LinuxThrottleDevice struct {
linuxBlockIODevice
// Rate is the IO rate limit per cgroup per device
Rate *uint64 `json:"rate,omitempty"`
Rate uint64 `json:"rate"`
}
// BlockIO for Linux cgroup 'blkio' resource management
type BlockIO struct {
// LinuxBlockIO for Linux cgroup 'blkio' resource management
type LinuxBlockIO struct {
// Specifies per cgroup weight, range is from 10 to 1000
Weight *uint16 `json:"blkioWeight,omitempty"`
// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
LeafWeight *uint16 `json:"blkioLeafWeight,omitempty"`
// Weight per cgroup per device, can override BlkioWeight
WeightDevice []WeightDevice `json:"blkioWeightDevice,omitempty"`
WeightDevice []LinuxWeightDevice `json:"blkioWeightDevice,omitempty"`
// IO read rate limit per cgroup per device, bytes per second
ThrottleReadBpsDevice []ThrottleDevice `json:"blkioThrottleReadBpsDevice,omitempty"`
ThrottleReadBpsDevice []LinuxThrottleDevice `json:"blkioThrottleReadBpsDevice,omitempty"`
// IO write rate limit per cgroup per device, bytes per second
ThrottleWriteBpsDevice []ThrottleDevice `json:"blkioThrottleWriteBpsDevice,omitempty"`
ThrottleWriteBpsDevice []LinuxThrottleDevice `json:"blkioThrottleWriteBpsDevice,omitempty"`
// IO read rate limit per cgroup per device, IO per second
ThrottleReadIOPSDevice []ThrottleDevice `json:"blkioThrottleReadIOPSDevice,omitempty"`
ThrottleReadIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleReadIOPSDevice,omitempty"`
// IO write rate limit per cgroup per device, IO per second
ThrottleWriteIOPSDevice []ThrottleDevice `json:"blkioThrottleWriteIOPSDevice,omitempty"`
ThrottleWriteIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleWriteIOPSDevice,omitempty"`
}
// Memory for Linux cgroup 'memory' resource management
type Memory struct {
// LinuxMemory for Linux cgroup 'memory' resource management
type LinuxMemory struct {
// Memory limit (in bytes).
Limit *uint64 `json:"limit,omitempty"`
// Memory reservation or soft_limit (in bytes).
@ -262,67 +289,67 @@ type Memory struct {
// Kernel memory limit (in bytes).
Kernel *uint64 `json:"kernel,omitempty"`
// Kernel memory limit for tcp (in bytes)
KernelTCP *uint64 `json:"kernelTCP"`
KernelTCP *uint64 `json:"kernelTCP,omitempty"`
// How aggressive the kernel will swap memory pages. Range from 0 to 100.
Swappiness *uint64 `json:"swappiness,omitempty"`
}
// CPU for Linux cgroup 'cpu' resource management
type CPU struct {
// LinuxCPU for Linux cgroup 'cpu' resource management
type LinuxCPU struct {
// CPU shares (relative weight (ratio) vs. other cgroups with cpu shares).
Shares *uint64 `json:"shares,omitempty"`
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
Quota *uint64 `json:"quota,omitempty"`
Quota *int64 `json:"quota,omitempty"`
// CPU period to be used for hardcapping (in usecs).
Period *uint64 `json:"period,omitempty"`
// How much time realtime scheduling may use (in usecs).
RealtimeRuntime *uint64 `json:"realtimeRuntime,omitempty"`
RealtimeRuntime *int64 `json:"realtimeRuntime,omitempty"`
// CPU period to be used for realtime scheduling (in usecs).
RealtimePeriod *uint64 `json:"realtimePeriod,omitempty"`
// CPUs to use within the cpuset. Default is to use any CPU available.
Cpus *string `json:"cpus,omitempty"`
Cpus string `json:"cpus,omitempty"`
// List of memory nodes in the cpuset. Default is to use any available memory node.
Mems *string `json:"mems,omitempty"`
Mems string `json:"mems,omitempty"`
}
// Pids for Linux cgroup 'pids' resource management (Linux 4.3)
type Pids struct {
// LinuxPids for Linux cgroup 'pids' resource management (Linux 4.3)
type LinuxPids struct {
// Maximum number of PIDs. Default is "no limit".
Limit *int64 `json:"limit,omitempty"`
Limit int64 `json:"limit"`
}
// Network identification and priority configuration
type Network struct {
// LinuxNetwork identification and priority configuration
type LinuxNetwork struct {
// Set class identifier for container's network packets
ClassID *uint32 `json:"classID"`
ClassID *uint32 `json:"classID,omitempty"`
// Set priority of network traffic for container
Priorities []InterfacePriority `json:"priorities,omitempty"`
Priorities []LinuxInterfacePriority `json:"priorities,omitempty"`
}
// Resources has container runtime resource constraints
type Resources struct {
// Devices are a list of device rules for the whitelist controller
Devices []DeviceCgroup `json:"devices"`
// LinuxResources has container runtime resource constraints
type LinuxResources struct {
// Devices configures the device whitelist.
Devices []LinuxDeviceCgroup `json:"devices,omitempty"`
// DisableOOMKiller disables the OOM killer for out of memory conditions
DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"`
// Specify an oom_score_adj for the container.
OOMScoreAdj *int `json:"oomScoreAdj,omitempty"`
// Memory restriction configuration
Memory *Memory `json:"memory,omitempty"`
Memory *LinuxMemory `json:"memory,omitempty"`
// CPU resource restriction configuration
CPU *CPU `json:"cpu,omitempty"`
CPU *LinuxCPU `json:"cpu,omitempty"`
// Task resource restriction configuration.
Pids *Pids `json:"pids,omitempty"`
Pids *LinuxPids `json:"pids,omitempty"`
// BlockIO restriction configuration
BlockIO *BlockIO `json:"blockIO,omitempty"`
BlockIO *LinuxBlockIO `json:"blockIO,omitempty"`
// Hugetlb limit (in bytes)
HugepageLimits []HugepageLimit `json:"hugepageLimits,omitempty"`
HugepageLimits []LinuxHugepageLimit `json:"hugepageLimits,omitempty"`
// Network restriction configuration
Network *Network `json:"network,omitempty"`
Network *LinuxNetwork `json:"network,omitempty"`
}
// Device represents the mknod information for a Linux special device file
type Device struct {
// LinuxDevice represents the mknod information for a Linux special device file
type LinuxDevice struct {
// Path to the device.
Path string `json:"path"`
// Device type, block, char, etc.
@ -339,25 +366,18 @@ type Device struct {
GID *uint32 `json:"gid,omitempty"`
}
// DeviceCgroup represents a device rule for the whitelist controller
type DeviceCgroup struct {
// LinuxDeviceCgroup represents a device rule for the whitelist controller
type LinuxDeviceCgroup struct {
// Allow or deny
Allow bool `json:"allow"`
// Device type, block, char, etc.
Type *string `json:"type,omitempty"`
Type string `json:"type,omitempty"`
// Major is the device's major number.
Major *int64 `json:"major,omitempty"`
// Minor is the device's minor number.
Minor *int64 `json:"minor,omitempty"`
// Cgroup access permissions format, rwm.
Access *string `json:"access,omitempty"`
}
// Seccomp represents syscall restrictions
type Seccomp struct {
DefaultAction Action `json:"defaultAction"`
Architectures []Arch `json:"architectures"`
Syscalls []Syscall `json:"syscalls,omitempty"`
Access string `json:"access,omitempty"`
}
// Solaris contains platform specific configuration for Solaris application containers.
@ -369,26 +389,26 @@ type Solaris struct {
// The maximum amount of shared memory allowed for this container.
MaxShmMemory string `json:"maxShmMemory,omitempty"`
// Specification for automatic creation of network resources for this container.
Anet []Anet `json:"anet,omitempty"`
Anet []SolarisAnet `json:"anet,omitempty"`
// Set limit on the amount of CPU time that can be used by container.
CappedCPU *CappedCPU `json:"cappedCPU,omitempty"`
CappedCPU *SolarisCappedCPU `json:"cappedCPU,omitempty"`
// The physical and swap caps on the memory that can be used by this container.
CappedMemory *CappedMemory `json:"cappedMemory,omitempty"`
CappedMemory *SolarisCappedMemory `json:"cappedMemory,omitempty"`
}
// CappedCPU allows users to set limit on the amount of CPU time that can be used by container.
type CappedCPU struct {
// SolarisCappedCPU allows users to set limit on the amount of CPU time that can be used by container.
type SolarisCappedCPU struct {
Ncpus string `json:"ncpus,omitempty"`
}
// CappedMemory allows users to set the physical and swap caps on the memory that can be used by this container.
type CappedMemory struct {
// SolarisCappedMemory allows users to set the physical and swap caps on the memory that can be used by this container.
type SolarisCappedMemory struct {
Physical string `json:"physical,omitempty"`
Swap string `json:"swap,omitempty"`
}
// Anet provides the specification for automatic creation of network resources for this container.
type Anet struct {
// SolarisAnet provides the specification for automatic creation of network resources for this container.
type SolarisAnet struct {
// Specify a name for the automatically created VNIC datalink.
Linkname string `json:"linkname,omitempty"`
// Specify the link over which the VNIC will be created.
@ -405,6 +425,65 @@ type Anet struct {
Macaddress string `json:"macAddress,omitempty"`
}
// Windows defines the runtime configuration for Windows based containers, including Hyper-V containers.
type Windows struct {
// Resources contains information for handling resource constraints for the container.
Resources *WindowsResources `json:"resources,omitempty"`
}
// WindowsResources has container runtime resource constraints for containers running on Windows.
type WindowsResources struct {
// Memory restriction configuration.
Memory *WindowsMemoryResources `json:"memory,omitempty"`
// CPU resource restriction configuration.
CPU *WindowsCPUResources `json:"cpu,omitempty"`
// Storage restriction configuration.
Storage *WindowsStorageResources `json:"storage,omitempty"`
// Network restriction configuration.
Network *WindowsNetworkResources `json:"network,omitempty"`
}
// WindowsMemoryResources contains memory resource management settings.
type WindowsMemoryResources struct {
// Memory limit in bytes.
Limit *uint64 `json:"limit,omitempty"`
// Memory reservation in bytes.
Reservation *uint64 `json:"reservation,omitempty"`
}
// WindowsCPUResources contains CPU resource management settings.
type WindowsCPUResources struct {
// Number of CPUs available to the container.
Count *uint64 `json:"count,omitempty"`
// CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000.
Shares *uint16 `json:"shares,omitempty"`
// Percent of available CPUs usable by the container.
Percent *uint8 `json:"percent,omitempty"`
}
// WindowsStorageResources contains storage resource management settings.
type WindowsStorageResources struct {
// Specifies maximum Iops for the system drive.
Iops *uint64 `json:"iops,omitempty"`
// Specifies maximum bytes per second for the system drive.
Bps *uint64 `json:"bps,omitempty"`
// Sandbox size specifies the minimum size of the system drive in bytes.
SandboxSize *uint64 `json:"sandboxSize,omitempty"`
}
// WindowsNetworkResources contains network resource management settings.
type WindowsNetworkResources struct {
// EgressBandwidth is the maximum egress bandwidth in bytes per second.
EgressBandwidth *uint64 `json:"egressBandwidth,omitempty"`
}
// LinuxSeccomp represents syscall restrictions
type LinuxSeccomp struct {
DefaultAction LinuxSeccompAction `json:"defaultAction"`
Architectures []Arch `json:"architectures,omitempty"`
Syscalls []LinuxSyscall `json:"syscalls"`
}
// Arch used for additional architectures
type Arch string
@ -427,45 +506,48 @@ const (
ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE"
ArchS390 Arch = "SCMP_ARCH_S390"
ArchS390X Arch = "SCMP_ARCH_S390X"
ArchPARISC Arch = "SCMP_ARCH_PARISC"
ArchPARISC64 Arch = "SCMP_ARCH_PARISC64"
)
// Action taken upon Seccomp rule match
type Action string
// LinuxSeccompAction taken upon Seccomp rule match
type LinuxSeccompAction string
// Define actions for Seccomp rules
const (
ActKill Action = "SCMP_ACT_KILL"
ActTrap Action = "SCMP_ACT_TRAP"
ActErrno Action = "SCMP_ACT_ERRNO"
ActTrace Action = "SCMP_ACT_TRACE"
ActAllow Action = "SCMP_ACT_ALLOW"
ActKill LinuxSeccompAction = "SCMP_ACT_KILL"
ActTrap LinuxSeccompAction = "SCMP_ACT_TRAP"
ActErrno LinuxSeccompAction = "SCMP_ACT_ERRNO"
ActTrace LinuxSeccompAction = "SCMP_ACT_TRACE"
ActAllow LinuxSeccompAction = "SCMP_ACT_ALLOW"
)
// Operator used to match syscall arguments in Seccomp
type Operator string
// LinuxSeccompOperator used to match syscall arguments in Seccomp
type LinuxSeccompOperator string
// Define operators for syscall arguments in Seccomp
const (
OpNotEqual Operator = "SCMP_CMP_NE"
OpLessThan Operator = "SCMP_CMP_LT"
OpLessEqual Operator = "SCMP_CMP_LE"
OpEqualTo Operator = "SCMP_CMP_EQ"
OpGreaterEqual Operator = "SCMP_CMP_GE"
OpGreaterThan Operator = "SCMP_CMP_GT"
OpMaskedEqual Operator = "SCMP_CMP_MASKED_EQ"
OpNotEqual LinuxSeccompOperator = "SCMP_CMP_NE"
OpLessThan LinuxSeccompOperator = "SCMP_CMP_LT"
OpLessEqual LinuxSeccompOperator = "SCMP_CMP_LE"
OpEqualTo LinuxSeccompOperator = "SCMP_CMP_EQ"
OpGreaterEqual LinuxSeccompOperator = "SCMP_CMP_GE"
OpGreaterThan LinuxSeccompOperator = "SCMP_CMP_GT"
OpMaskedEqual LinuxSeccompOperator = "SCMP_CMP_MASKED_EQ"
)
// Arg used for matching specific syscall arguments in Seccomp
type Arg struct {
Index uint `json:"index"`
Value uint64 `json:"value"`
ValueTwo uint64 `json:"valueTwo"`
Op Operator `json:"op"`
// LinuxSeccompArg used for matching specific syscall arguments in Seccomp
type LinuxSeccompArg struct {
Index uint `json:"index"`
Value uint64 `json:"value"`
ValueTwo uint64 `json:"valueTwo"`
Op LinuxSeccompOperator `json:"op"`
}
// Syscall is used to match a syscall in Seccomp
type Syscall struct {
Name string `json:"name"`
Action Action `json:"action"`
Args []Arg `json:"args,omitempty"`
// LinuxSyscall is used to match a syscall in Seccomp
type LinuxSyscall struct {
Names []string `json:"names"`
Action LinuxSeccompAction `json:"action"`
Args []LinuxSeccompArg `json:"args"`
Comment string `json:"comment"`
}

View File

@ -3,15 +3,15 @@ package specs
// State holds information about the runtime state of the container.
type State struct {
// Version is the version of the specification that is supported.
Version string `json:"version"`
Version string `json:"ociVersion"`
// ID is the container ID
ID string `json:"id"`
// Status is the runtime state of the container.
// Status is the runtime status of the container.
Status string `json:"status"`
// Pid is the process id for the container's main process.
// Pid is the process ID for the container process.
Pid int `json:"pid"`
// BundlePath is the path to the container's bundle directory.
BundlePath string `json:"bundlePath"`
// Annotations are the annotations associated with the container.
Annotations map[string]string `json:"annotations"`
// Bundle is the path to the container's bundle directory.
Bundle string `json:"bundle"`
// Annotations are key values associated with the container.
Annotations map[string]string `json:"annotations,omitempty"`
}

View File

@ -11,7 +11,7 @@ const (
VersionPatch = 0
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = "-rc1-dev"
VersionDev = "-rc5"
)
// Version is the specification version that the package types support.

View File

@ -1,29 +1,45 @@
# Style and conventions
# <a name="styleAndConventions" />Style and conventions
## One sentence per line
## <a name="styleOneSentence" />One sentence per line
To keep consistency throughout the Markdown files in the Open Container spec all files should be formatted one sentence per line.
This fixes two things: it makes diffing easier with git and it resolves fights about line wrapping length.
For example, this paragraph will span three lines in the Markdown source.
## Traditionally hex settings should use JSON integers, not JSON strings
## <a name="styleHex" />Traditionally hex settings should use JSON integers, not JSON strings
For example, [`"classID": 1048577`][class-id] instead of `"classID": "0x100001"`.
The config JSON isn't enough of a UI to be worth jumping through string <-> integer hoops to support an 0x… form ([source][integer-over-hex]).
## Constant names should keep redundant prefixes
## <a name="styleConstantNames" />Constant names should keep redundant prefixes
For example, `CAP_KILL` instead of `KILL` in [**`linux.capabilities`**][capabilities].
The redundancy reduction from removing the namespacing prefix is not useful enough to be worth trimming the upstream identifier ([source][keep-prefix]).
## Optional settings should have pointer Go types
## <a name="styleOptionalSettings" />Optional settings should not have pointer Go types
So we have a consistent way to identify unset values ([source][optional-pointer]).
The exceptions are entries where the Go default for the type is a no-op in the spec, in which case `omitempty` is sufficient and no pointer is needed (sources [here][no-pointer-for-slices], [here][no-pointer-for-boolean], and [here][pointer-when-updates-require-changes]).
Because in many cases the Go default for the type is a no-op in the spec (sources [here][no-pointer-for-strings], [here][no-pointer-for-slices], and [here][no-pointer-for-boolean]).
The exceptions are entries where we need to distinguish between “not set” and “set to the Go default for that type” ([source][pointer-when-updates-require-changes]), and this decision should be made on a per-setting case.
## Links
Internal links should be [relative links][markdown-relative-links] when linking to content within the repository.
Internal links should be used inline.
External links should be collected at the bottom of a markdown file and used as referenced links.
See 'Referenced Links' in this [markdown quick reference][markdown-quick-reference].
The use of referenced links in the markdown body helps to keep files clean and organized.
This also facilitates updates of external link targets on a per-file basis.
Referenced links should be kept in two alphabetically sorted sets, a general reference section followed by a man page section.
To keep Pandoc happy, duplicate naming of links within pages listed in the Makefile's DOC_FILES variable should be avoided by appending an '_N' to the link tagname, where 'N' is some number not currently in use.
The organization and style of an existing reference section should be maintained unless it violates these style guidelines.
An exception to these rules is when a URL is needed contextually, for example when showing an explicit link to the reader.
## Examples
### Anchoring
### <a name="styleAnchoring" />Anchoring
For any given section that provides a notable example, it is ideal to have it denoted with [markdown headers][markdown-headers].
The level of header should be such that it is a subheader of the header it is an example of.
@ -47,7 +63,7 @@ To use Some Topic, ...
```
### Content
### <a name="styleContent" />Content
Where necessary, the values in the example can be empty or unset, but accommodate with comments regarding this intention.
@ -86,12 +102,32 @@ Following is a fully populated example (not necessarily for copy/paste use)
}
```
### Links
The following is an example of different types of links.
This is shown as a complete markdown file, where the referenced links are at the bottom.
```markdown
The specification repository's [glossary](glossary.md) is where readers can find definitions of commonly used terms.
Readers may click through to the [Open Containers namespace][open-containers] on [GitHub][github].
The URL for the Open Containers link above is: https://github.com/opencontainers
[github]: https://github.com
[open-containers]: https://github.com/opencontainers
```
[capabilities]: config-linux.md#capabilities
[class-id]: config-linux.md#network
[integer-over-hex]: https://github.com/opencontainers/runtime-spec/pull/267#discussion_r48360013
[integer-over-hex]: https://github.com/opencontainers/runtime-spec/pull/267#r48360013
[keep-prefix]: https://github.com/opencontainers/runtime-spec/pull/159#issuecomment-138728337
[no-pointer-for-boolean]: https://github.com/opencontainers/runtime-spec/pull/290#discussion_r50296396
[no-pointer-for-slices]: https://github.com/opencontainers/runtime-spec/pull/316/files#r50782982
[optional-pointer]: https://github.com/opencontainers/runtime-spec/pull/233#discussion_r47829711
[pointer-when-updates-require-changes]: https://github.com/opencontainers/runtime-spec/pull/317/files#r50932706
[no-pointer-for-boolean]: https://github.com/opencontainers/runtime-spec/pull/290#r50296396
[no-pointer-for-slices]: https://github.com/opencontainers/runtime-spec/pull/316#r50782982
[no-pointer-for-strings]: https://github.com/opencontainers/runtime-spec/pull/653#issue-200439192
[pointer-when-updates-require-changes]: https://github.com/opencontainers/runtime-spec/pull/317#r50932706
[markdown-headers]: https://help.github.com/articles/basic-writing-and-formatting-syntax/#headings
[markdown-quick-reference]: https://en.support.wordpress.com/markdown-quick-reference
[markdown-relative-links]: https://help.github.com/articles/basic-writing-and-formatting-syntax/#relative-links

View File

@ -1,46 +0,0 @@
{
"ImportPath": "github.com/opencontainers/runtime-tools",
"GoVersion": "go1.4",
"GodepVersion": "v75",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/Sirupsen/logrus",
"Comment": "v0.8.3-3-g07d998d",
"Rev": "07d998d174c4e2dc90e2f1989a20724220bca1ff"
},
{
"ImportPath": "github.com/blang/semver",
"Comment": "v3.1.0",
"Rev": "aea32c919a18e5ef4537bbd283ff29594b1b0165"
},
{
"ImportPath": "github.com/hashicorp/errwrap",
"Rev": "7554cd9344cec97297fa6649b055a8c98c2a1e55"
},
{
"ImportPath": "github.com/hashicorp/go-multierror",
"Rev": "ed905158d87462226a13fe39ddf685ea65f1c11f"
},
{
"ImportPath": "github.com/mndrix/tap-go",
"Rev": "67c9553625499b7e7ed4ac4f2d8bf1cb8f5ecf52"
},
{
"ImportPath": "github.com/opencontainers/runtime-spec/specs-go",
"Comment": "v1.0.0-rc1-31-gbb6925e",
"Rev": "bb6925ea99f0e366a3f7d1c975f6577475ca25f0"
},
{
"ImportPath": "github.com/syndtr/gocapability/capability",
"Rev": "2c00daeb6c3b45114c80ac44119e7b8801fdd852"
},
{
"ImportPath": "github.com/urfave/cli",
"Comment": "v1.19.1",
"Rev": "0bdeddeeb0f650497d603c4ad7b20cfe685682f6"
}
]
}

View File

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

View File

@ -1,2 +0,0 @@
/pkg
/bin

View File

@ -1,8 +0,0 @@
language: go
go:
- 1.2
- 1.3
- 1.4
- tip
install:
- go get -t ./...

View File

@ -1,29 +0,0 @@
# 0.8.3
logrus/core: fix entry log level (#208)
logrus/core: improve performance of text formatter by 40%
logrus/core: expose `LevelHooks` type
logrus/core: add support for DragonflyBSD and NetBSD
formatter/text: print structs more verbosely
# 0.8.2
logrus: fix more Fatal family functions
# 0.8.1
logrus: fix not exiting on `Fatalf` and `Fatalln`
# 0.8.0
logrus: defaults to stderr instead of stdout
hooks/sentry: add special field for `*http.Request`
formatter/text: ignore Windows for colors
# 0.7.3
formatter/\*: allow configuration of timestamp layout
# 0.7.2
formatter/text: Add configuration option for time format (#158)

View File

@ -1,21 +0,0 @@
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.

View File

@ -1,356 +0,0 @@
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus)&nbsp;[![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(&log.TextFormatter{})` when a TTY is not
attached, the output is compatible with the
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
```text
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
exit status 1
```
#### 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"
logrus_syslog "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. |
| [Sentry](https://github.com/Sirupsen/logrus/blob/master/hooks/sentry/sentry.go) | Send errors to the Sentry error logging and aggregation 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/) |
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
#### 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(&log.TextFormatter{})
}
}
```
This configuration is how `logrus` was intended to be used, but JSON in
production is mostly only useful if you do log aggregation with tools like
Splunk or Logstash.
#### Formatters
The built-in logging formatters are:
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
without colors.
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
field to `true`. To force no colored output even if there is a TTY set the
`DisableColors` field to `true`
* `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 transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
```go
w := logger.Writer()
defer w.Close()
srv := http.Server{
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog: log.New(w, "", 0),
}
```
Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is `info`.
#### 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

View File

@ -1,255 +0,0 @@
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),
Level: logger.Level,
}
}
// 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, Level: entry.Level}
}
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.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.Level >= InfoLevel {
entry.log(InfoLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Warn(args ...interface{}) {
if entry.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.Level >= ErrorLevel {
entry.log(ErrorLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Fatal(args ...interface{}) {
if entry.Level >= FatalLevel {
entry.log(FatalLevel, fmt.Sprint(args...))
}
os.Exit(1)
}
func (entry *Entry) Panic(args ...interface{}) {
if entry.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.Level >= DebugLevel {
entry.Debug(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Infof(format string, args ...interface{}) {
if entry.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.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.Level >= ErrorLevel {
entry.Error(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Fatalf(format string, args ...interface{}) {
if entry.Level >= FatalLevel {
entry.Fatal(fmt.Sprintf(format, args...))
}
os.Exit(1)
}
func (entry *Entry) Panicf(format string, args ...interface{}) {
if entry.Level >= PanicLevel {
entry.Panic(fmt.Sprintf(format, args...))
}
}
// Entry Println family functions
func (entry *Entry) Debugln(args ...interface{}) {
if entry.Level >= DebugLevel {
entry.Debug(entry.sprintlnn(args...))
}
}
func (entry *Entry) Infoln(args ...interface{}) {
if entry.Level >= InfoLevel {
entry.Info(entry.sprintlnn(args...))
}
}
func (entry *Entry) Println(args ...interface{}) {
entry.Infoln(args...)
}
func (entry *Entry) Warnln(args ...interface{}) {
if entry.Level >= WarnLevel {
entry.Warn(entry.sprintlnn(args...))
}
}
func (entry *Entry) Warningln(args ...interface{}) {
entry.Warnln(args...)
}
func (entry *Entry) Errorln(args ...interface{}) {
if entry.Level >= ErrorLevel {
entry.Error(entry.sprintlnn(args...))
}
}
func (entry *Entry) Fatalln(args ...interface{}) {
if entry.Level >= FatalLevel {
entry.Fatal(entry.sprintlnn(args...))
}
os.Exit(1)
}
func (entry *Entry) Panicln(args ...interface{}) {
if entry.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]
}

View File

@ -1,67 +0,0 @@
package logrus
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestEntryPanicln(t *testing.T) {
errBoom := fmt.Errorf("boom time")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicln("kaboom")
}
func TestEntryPanicf(t *testing.T) {
errBoom := fmt.Errorf("boom again")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom true", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
}
func TestEntryLogLevel(t *testing.T) {
out := &bytes.Buffer{}
logger := New()
logger.Out = out
logger.Level = DebugLevel
entry := NewEntry(logger)
assert.Equal(t, DebugLevel, entry.Level)
entry.Level = WarnLevel
entry.Info("it should not be displayed")
assert.Equal(t, "", out.String())
entry.Warn("it should be displayed")
assert.Contains(t, out.String(), "it should be displayed")
}

View File

@ -1,50 +0,0 @@
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!")
}

View File

@ -1,30 +0,0 @@
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!")
}

View File

@ -1,188 +0,0 @@
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...)
}

View File

@ -1,48 +0,0 @@
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"]
}
}

View File

@ -1,98 +0,0 @@
package logrus
import (
"fmt"
"testing"
"time"
)
// smallFields is a small size data set for benchmarking
var smallFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
}
// largeFields is a large size data set for benchmarking
var largeFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
"five": "six",
"seven": "eight",
"nine": "ten",
"eleven": "twelve",
"thirteen": "fourteen",
"fifteen": "sixteen",
"seventeen": "eighteen",
"nineteen": "twenty",
"a": "b",
"c": "d",
"e": "f",
"g": "h",
"i": "j",
"k": "l",
"m": "n",
"o": "p",
"q": "r",
"s": "t",
"u": "v",
"w": "x",
"y": "z",
"this": "will",
"make": "thirty",
"entries": "yeah",
}
var errorFields = Fields{
"foo": fmt.Errorf("bar"),
"baz": fmt.Errorf("qux"),
}
func BenchmarkErrorTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
}
func BenchmarkSmallTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
}
func BenchmarkLargeTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
}
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
}
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
}
func BenchmarkSmallJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, smallFields)
}
func BenchmarkLargeJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, largeFields)
}
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
entry := &Entry{
Time: time.Time{},
Level: InfoLevel,
Message: "message",
Data: fields,
}
var d []byte
var err error
for i := 0; i < b.N; i++ {
d, err = formatter.Format(entry)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(d)))
}
}

View File

@ -1,56 +0,0 @@
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
}

View File

@ -1,52 +0,0 @@
package logstash
import (
"bytes"
"encoding/json"
"github.com/Sirupsen/logrus"
"github.com/stretchr/testify/assert"
"testing"
)
func TestLogstashFormatter(t *testing.T) {
assert := assert.New(t)
lf := LogstashFormatter{Type: "abc"}
fields := logrus.Fields{
"message": "def",
"level": "ijk",
"type": "lmn",
"one": 1,
"pi": 3.14,
"bool": true,
}
entry := logrus.WithFields(fields)
entry.Message = "msg"
entry.Level = logrus.InfoLevel
b, _ := lf.Format(entry)
var data map[string]interface{}
dec := json.NewDecoder(bytes.NewReader(b))
dec.UseNumber()
dec.Decode(&data)
// base fields
assert.Equal(json.Number("1"), data["@version"])
assert.NotEmpty(data["@timestamp"])
assert.Equal("abc", data["type"])
assert.Equal("msg", data["message"])
assert.Equal("info", data["level"])
// substituted fields
assert.Equal("def", data["fields.message"])
assert.Equal("ijk", data["fields.level"])
assert.Equal("lmn", data["fields.type"])
// formats
assert.Equal(json.Number("1"), data["one"])
assert.Equal(json.Number("3.14"), data["pi"])
assert.Equal(true, data["bool"])
}

View File

@ -1,122 +0,0 @@
package logrus
import (
"testing"
"github.com/stretchr/testify/assert"
)
type TestHook struct {
Fired bool
}
func (hook *TestHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *TestHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookFires(t *testing.T) {
hook := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
assert.Equal(t, hook.Fired, false)
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}
type ModifyHook struct {
}
func (hook *ModifyHook) Fire(entry *Entry) error {
entry.Data["wow"] = "whale"
return nil
}
func (hook *ModifyHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookCanModifyEntry(t *testing.T) {
hook := new(ModifyHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
})
}
func TestCanFireMultipleHooks(t *testing.T) {
hook1 := new(ModifyHook)
hook2 := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook1)
log.Hooks.Add(hook2)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
assert.Equal(t, hook2.Fired, true)
})
}
type ErrorHook struct {
Fired bool
}
func (hook *ErrorHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *ErrorHook) Levels() []Level {
return []Level{
ErrorLevel,
}
}
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, false)
})
}
func TestErrorHookShouldFireOnError(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Error("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}

View File

@ -1,34 +0,0 @@
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
}

View File

@ -1,54 +0,0 @@
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,
}
}

View File

@ -1,133 +0,0 @@
package airbrake
import (
"encoding/xml"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/Sirupsen/logrus"
)
type notice struct {
Error NoticeError `xml:"error"`
}
type NoticeError struct {
Class string `xml:"class"`
Message string `xml:"message"`
}
type customErr struct {
msg string
}
func (e *customErr) Error() string {
return e.msg
}
const (
testAPIKey = "abcxyz"
testEnv = "development"
expectedClass = "*airbrake.customErr"
expectedMsg = "foo"
unintendedMsg = "Airbrake will not see this string"
)
var (
noticeError = make(chan NoticeError, 1)
)
// TestLogEntryMessageReceived checks if invoking Logrus' log.Error
// method causes an XML payload containing the log entry message is received
// by a HTTP server emulating an Airbrake-compatible endpoint.
func TestLogEntryMessageReceived(t *testing.T) {
log := logrus.New()
ts := startAirbrakeServer(t)
defer ts.Close()
hook := NewHook(ts.URL, testAPIKey, "production")
log.Hooks.Add(hook)
log.Error(expectedMsg)
select {
case received := <-noticeError:
if received.Message != expectedMsg {
t.Errorf("Unexpected message received: %s", received.Message)
}
case <-time.After(time.Second):
t.Error("Timed out; no notice received by Airbrake API")
}
}
// TestLogEntryMessageReceived confirms that, when passing an error type using
// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the
// error message returned by the Error() method on the error interface
// rather than the logrus.Entry.Message string.
func TestLogEntryWithErrorReceived(t *testing.T) {
log := logrus.New()
ts := startAirbrakeServer(t)
defer ts.Close()
hook := NewHook(ts.URL, testAPIKey, "production")
log.Hooks.Add(hook)
log.WithFields(logrus.Fields{
"error": &customErr{expectedMsg},
}).Error(unintendedMsg)
select {
case received := <-noticeError:
if received.Message != expectedMsg {
t.Errorf("Unexpected message received: %s", received.Message)
}
if received.Class != expectedClass {
t.Errorf("Unexpected error class: %s", received.Class)
}
case <-time.After(time.Second):
t.Error("Timed out; no notice received by Airbrake API")
}
}
// TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a
// non-error type using logrus.Fields, a HTTP server emulating an Airbrake
// endpoint receives the logrus.Entry.Message string.
//
// Only error types are supported when setting the 'error' field using
// logrus.WithFields().
func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) {
log := logrus.New()
ts := startAirbrakeServer(t)
defer ts.Close()
hook := NewHook(ts.URL, testAPIKey, "production")
log.Hooks.Add(hook)
log.WithFields(logrus.Fields{
"error": expectedMsg,
}).Error(unintendedMsg)
select {
case received := <-noticeError:
if received.Message != unintendedMsg {
t.Errorf("Unexpected message received: %s", received.Message)
}
case <-time.After(time.Second):
t.Error("Timed out; no notice received by Airbrake API")
}
}
func startAirbrakeServer(t *testing.T) *httptest.Server {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var notice notice
if err := xml.NewDecoder(r.Body).Decode(&notice); err != nil {
t.Error(err)
}
r.Body.Close()
noticeError <- notice.Error
}))
return ts
}

View File

@ -1,68 +0,0 @@
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,
}
}

View File

@ -1,64 +0,0 @@
package logrus_bugsnag
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/Sirupsen/logrus"
"github.com/bugsnag/bugsnag-go"
)
type notice struct {
Events []struct {
Exceptions []struct {
Message string `json:"message"`
} `json:"exceptions"`
} `json:"events"`
}
func TestNoticeReceived(t *testing.T) {
msg := make(chan string, 1)
expectedMsg := "foo"
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var notice notice
data, _ := ioutil.ReadAll(r.Body)
if err := json.Unmarshal(data, &notice); err != nil {
t.Error(err)
}
_ = r.Body.Close()
msg <- notice.Events[0].Exceptions[0].Message
}))
defer ts.Close()
hook := &bugsnagHook{}
bugsnag.Configure(bugsnag.Configuration{
Endpoint: ts.URL,
ReleaseStage: "production",
APIKey: "12345678901234567890123456789012",
Synchronous: true,
})
log := logrus.New()
log.Hooks.Add(hook)
log.WithFields(logrus.Fields{
"error": errors.New(expectedMsg),
}).Error("Bugsnag will not see this string")
select {
case received := <-msg:
if received != expectedMsg {
t.Errorf("Unexpected message received: %s", received)
}
case <-time.After(time.Second):
t.Error("Timed out; no notice received by Bugsnag API")
}
}

View File

@ -1,28 +0,0 @@
# 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)
}
}
```

View File

@ -1,55 +0,0 @@
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,
}
}

View File

@ -1,26 +0,0 @@
package logrus_papertrail
import (
"fmt"
"testing"
"github.com/Sirupsen/logrus"
"github.com/stvp/go-udp-testing"
)
func TestWritingToUDP(t *testing.T) {
port := 16661
udp.SetAddr(fmt.Sprintf(":%d", port))
hook, err := NewPapertrailHook("localhost", port, "test")
if err != nil {
t.Errorf("Unable to connect to local UDP server.")
}
log := logrus.New()
log.Hooks.Add(hook)
udp.ShouldReceive(t, "foo", func() {
log.Info("foo")
})
}

View File

@ -1,78 +0,0 @@
# 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)
}
}
```
If you wish to initialize a SentryHook with tags, you can use the `NewWithTagsSentryHook` constructor to provide default tags:
```go
tags := map[string]string{
"site": "example.com",
}
levels := []logrus.Level{
logrus.PanicLevel,
logrus.FatalLevel,
logrus.ErrorLevel,
}
hook, err := logrus_sentry.NewWithTagsSentryHook(YOUR_DSN, tags, levels)
```
## Special fields
Some logrus fields have a special meaning in this hook,
these are `server_name`, `logger` and `http_request`.
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.
- `http_request` is the in-coming request(*http.Request). The detailed request data are sent to Sentry.
## 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
```

View File

@ -1,131 +0,0 @@
package logrus_sentry
import (
"fmt"
"net/http"
"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
}
func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) {
var (
ok bool
v interface{}
req *http.Request
)
if v, ok = d[key]; !ok {
return nil, false
}
if req, ok = v.(*http.Request); !ok || req == nil {
return nil, false
}
delete(d, key)
return req, 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.New(DSN)
if err != nil {
return nil, err
}
return &SentryHook{100 * time.Millisecond, client, levels}, nil
}
// NewWithTagsSentryHook creates a hook with tags to be added to an instance
// of logger and initializes the raven client. This method sets the timeout to
// 100 milliseconds.
func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) {
client, err := raven.NewWithTags(DSN, tags)
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, server_name and http_request
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
}
if req, ok := getAndDelRequest(d, "http_request"); ok {
packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req))
}
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
}

View File

@ -1,132 +0,0 @@
package logrus_sentry
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"github.com/Sirupsen/logrus"
"github.com/getsentry/raven-go"
)
const (
message = "error message"
server_name = "testserver.internal"
logger_name = "test.logger"
)
func getTestLogger() *logrus.Logger {
l := logrus.New()
l.Out = ioutil.Discard
return l
}
func WithTestDSN(t *testing.T, tf func(string, <-chan *raven.Packet)) {
pch := make(chan *raven.Packet, 1)
s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
d := json.NewDecoder(req.Body)
p := &raven.Packet{}
err := d.Decode(p)
if err != nil {
t.Fatal(err.Error())
}
pch <- p
}))
defer s.Close()
fragments := strings.SplitN(s.URL, "://", 2)
dsn := fmt.Sprintf(
"%s://public:secret@%s/sentry/project-id",
fragments[0],
fragments[1],
)
tf(dsn, pch)
}
func TestSpecialFields(t *testing.T) {
WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
logger := getTestLogger()
hook, err := NewSentryHook(dsn, []logrus.Level{
logrus.ErrorLevel,
})
if err != nil {
t.Fatal(err.Error())
}
logger.Hooks.Add(hook)
req, _ := http.NewRequest("GET", "url", nil)
logger.WithFields(logrus.Fields{
"server_name": server_name,
"logger": logger_name,
"http_request": req,
}).Error(message)
packet := <-pch
if packet.Logger != logger_name {
t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger)
}
if packet.ServerName != server_name {
t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName)
}
})
}
func TestSentryHandler(t *testing.T) {
WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
logger := getTestLogger()
hook, err := NewSentryHook(dsn, []logrus.Level{
logrus.ErrorLevel,
})
if err != nil {
t.Fatal(err.Error())
}
logger.Hooks.Add(hook)
logger.Error(message)
packet := <-pch
if packet.Message != message {
t.Errorf("message should have been %s, was %s", message, packet.Message)
}
})
}
func TestSentryTags(t *testing.T) {
WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
logger := getTestLogger()
tags := map[string]string{
"site": "test",
}
levels := []logrus.Level{
logrus.ErrorLevel,
}
hook, err := NewWithTagsSentryHook(dsn, tags, levels)
if err != nil {
t.Fatal(err.Error())
}
logger.Hooks.Add(hook)
logger.Error(message)
packet := <-pch
expected := raven.Tags{
raven.Tag{
Key: "site",
Value: "test",
},
}
if !reflect.DeepEqual(packet.Tags, expected) {
t.Errorf("message should have been %s, was %s", message, packet.Message)
}
})
}

View File

@ -1,20 +0,0 @@
# 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)
}
}
```

View File

@ -1,59 +0,0 @@
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,
}
}

View File

@ -1,26 +0,0 @@
package logrus_syslog
import (
"github.com/Sirupsen/logrus"
"log/syslog"
"testing"
)
func TestLocalhostAddAndPrint(t *testing.T) {
log := logrus.New()
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
t.Errorf("Unable to connect to local syslog.")
}
log.Hooks.Add(hook)
for _, level := range hook.Levels() {
if len(log.Hooks[level]) != 1 {
t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
}
}
log.Info("Congratulations!")
}

View File

@ -1,41 +0,0 @@
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)
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = DefaultTimestampFormat
}
data["time"] = entry.Time.Format(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
}

View File

@ -1,120 +0,0 @@
package logrus
import (
"encoding/json"
"errors"
"testing"
)
func TestErrorNotLost(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["error"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["omg"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestFieldClashWithTime(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("time", "right now!"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.time"] != "right now!" {
t.Fatal("fields.time not set to original time field")
}
if entry["time"] != "0001-01-01T00:00:00Z" {
t.Fatal("time field not set to current time, was: ", entry["time"])
}
}
func TestFieldClashWithMsg(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("msg", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.msg"] != "something" {
t.Fatal("fields.msg not set to original msg field")
}
}
func TestFieldClashWithLevel(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.level"] != "something" {
t.Fatal("fields.level not set to original level field")
}
}
func TestJSONEntryEndsWithNewline(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
if b[len(b)-1] != '\n' {
t.Fatal("Expected JSON log entry to end with a newline")
}
}

View File

@ -1,206 +0,0 @@
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.Stderr,
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...)
}
os.Exit(1)
}
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...)
}
os.Exit(1)
}
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...)
}
os.Exit(1)
}
func (logger *Logger) Panicln(args ...interface{}) {
if logger.Level >= PanicLevel {
NewEntry(logger).Panicln(args...)
}
}

View File

@ -1,94 +0,0 @@
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{})
}

View File

@ -1,301 +0,0 @@
package logrus
import (
"bytes"
"encoding/json"
"strconv"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
log(logger)
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assertions(fields)
}
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
var buffer bytes.Buffer
logger := New()
logger.Out = &buffer
logger.Formatter = &TextFormatter{
DisableColors: true,
}
log(logger)
fields := make(map[string]string)
for _, kv := range strings.Split(buffer.String(), " ") {
if !strings.Contains(kv, "=") {
continue
}
kvArr := strings.Split(kv, "=")
key := strings.TrimSpace(kvArr[0])
val := kvArr[1]
if kvArr[1][0] == '"' {
var err error
val, err = strconv.Unquote(val)
assert.NoError(t, err)
}
fields[key] = val
}
assertions(fields)
}
func TestPrint(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestInfo(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestWarn(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Warn("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "warning")
})
}
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test test")
})
}
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test 10")
})
}
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test10")
})
}
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "testtest")
})
}
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
localLog := logger.WithFields(Fields{
"key1": "value1",
})
localLog.WithField("key2", "value2").Info("test")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assert.Equal(t, "value2", fields["key2"])
assert.Equal(t, "value1", fields["key1"])
buffer = bytes.Buffer{}
fields = Fields{}
localLog.Info("test")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
_, ok := fields["key2"]
assert.Equal(t, false, ok)
assert.Equal(t, "value1", fields["key1"])
}
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
})
}
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["fields.msg"], "hello")
})
}
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("time", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["fields.time"], "hello")
})
}
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("level", 1).Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["level"], "info")
assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
})
}
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
LogAndAssertText(t, func(log *Logger) {
ll := log.WithField("herp", "derp")
ll.Info("hello")
ll.Info("bye")
}, func(fields map[string]string) {
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
if _, ok := fields[fieldName]; ok {
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
}
}
})
}
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
llog := logger.WithField("context", "eating raw fish")
llog.Info("looks delicious")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded first message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "looks delicious")
assert.Equal(t, fields["context"], "eating raw fish")
buffer.Reset()
llog.Warn("omg it is!")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded second message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "omg it is!")
assert.Equal(t, fields["context"], "eating raw fish")
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
}
func TestConvertLevelToString(t *testing.T) {
assert.Equal(t, "debug", DebugLevel.String())
assert.Equal(t, "info", InfoLevel.String())
assert.Equal(t, "warning", WarnLevel.String())
assert.Equal(t, "error", ErrorLevel.String())
assert.Equal(t, "fatal", FatalLevel.String())
assert.Equal(t, "panic", PanicLevel.String())
}
func TestParseLevel(t *testing.T) {
l, err := ParseLevel("panic")
assert.Nil(t, err)
assert.Equal(t, PanicLevel, l)
l, err = ParseLevel("fatal")
assert.Nil(t, err)
assert.Equal(t, FatalLevel, l)
l, err = ParseLevel("error")
assert.Nil(t, err)
assert.Equal(t, ErrorLevel, l)
l, err = ParseLevel("warn")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("warning")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("info")
assert.Nil(t, err)
assert.Equal(t, InfoLevel, l)
l, err = ParseLevel("debug")
assert.Nil(t, err)
assert.Equal(t, DebugLevel, l)
l, err = ParseLevel("invalid")
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
}
func TestGetSetLevelRace(t *testing.T) {
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if i%2 == 0 {
SetLevel(InfoLevel)
} else {
GetLevel()
}
}(i)
}
wg.Wait()
}

View File

@ -1,9 +0,0 @@
// +build darwin freebsd openbsd netbsd dragonfly
package logrus
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA
type Termios syscall.Termios

View File

@ -1,12 +0,0 @@
// 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

View File

@ -1,21 +0,0 @@
// 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 netbsd dragonfly
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
}

View File

@ -1,27 +0,0 @@
// 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
}

View File

@ -1,158 +0,0 @@
package logrus
import (
"bytes"
"fmt"
"runtime"
"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)
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
isColored := (f.ForceColors || isColorTerminal) && !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 string, value interface{}) {
b.WriteString(key)
b.WriteByte('=')
switch value := value.(type) {
case string:
if needsQuoting(value) {
b.WriteString(value)
} else {
fmt.Fprintf(b, "%q", value)
}
case error:
errmsg := value.Error()
if needsQuoting(errmsg) {
b.WriteString(errmsg)
} else {
fmt.Fprintf(b, "%q", value)
}
default:
fmt.Fprint(b, value)
}
b.WriteByte(' ')
}

View File

@ -1,61 +0,0 @@
package logrus
import (
"bytes"
"errors"
"testing"
"time"
)
func TestQuoting(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
checkQuoting := func(q bool, value interface{}) {
b, _ := tf.Format(WithField("test", value))
idx := bytes.Index(b, ([]byte)("test="))
cont := bytes.Contains(b[idx+5:], []byte{'"'})
if cont != q {
if q {
t.Errorf("quoting expected for: %#v", value)
} else {
t.Errorf("quoting not expected for: %#v", value)
}
}
}
checkQuoting(false, "abcd")
checkQuoting(false, "v1.0")
checkQuoting(false, "1234567890")
checkQuoting(true, "/foobar")
checkQuoting(true, "x y")
checkQuoting(true, "x,y")
checkQuoting(false, errors.New("invalid"))
checkQuoting(true, errors.New("invalid argument"))
}
func TestTimestampFormat(t *testing.T) {
checkTimeStr := func(format string) {
customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
customStr, _ := customFormatter.Format(WithField("test", "test"))
timeStart := bytes.Index(customStr, ([]byte)("time="))
timeEnd := bytes.Index(customStr, ([]byte)("level="))
timeStr := customStr[timeStart+5 : timeEnd-1]
if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' {
timeStr = timeStr[1 : len(timeStr)-1]
}
if format == "" {
format = time.RFC3339
}
_, e := time.Parse(format, (string)(timeStr))
if e != nil {
t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
}
}
checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
checkTimeStr("Mon Jan _2 15:04:05 2006")
checkTimeStr("")
}
// TODO add tests for sorting etc., this requires a parser for the text
// formatter output.

View File

@ -1,31 +0,0 @@
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()
}

View File

@ -1,354 +0,0 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. “Contributor”
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. “Contributor Version”
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributors Contribution.
1.3. “Contribution”
means Covered Software of a particular Contributor.
1.4. “Covered Software”
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. “Incompatible With Secondary Licenses”
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of version
1.1 or earlier of the License, but not also under the terms of a
Secondary License.
1.6. “Executable Form”
means any form of the work other than Source Code Form.
1.7. “Larger Work”
means a work that combines Covered Software with other material, in a separate
file or files, that is not Covered Software.
1.8. “License”
means this document.
1.9. “Licensable”
means having the right to grant, to the maximum extent possible, whether at the
time of the initial grant or subsequently, any and all of the rights conveyed by
this License.
1.10. “Modifications”
means any of the following:
a. any file in Source Code Form that results from an addition to, deletion
from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. “Patent Claims” of a Contributor
means any patent claim(s), including without limitation, method, process,
and apparatus claims, in any patent Licensable by such Contributor that
would be infringed, but for the grant of the License, by the making,
using, selling, offering for sale, having made, import, or transfer of
either its Contributions or its Contributor Version.
1.12. “Secondary License”
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. “Source Code Form”
means the form of the work preferred for making modifications.
1.14. “You” (or “Your”)
means an individual or a legal entity exercising rights under this
License. For legal entities, “You” includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, “control” means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or as
part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its Contributions
or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution become
effective for each Contribution on the date the Contributor first distributes
such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under this
License. No additional rights or licenses will be implied from the distribution
or licensing of Covered Software under this License. Notwithstanding Section
2.1(b) above, no patent license is granted by a Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third partys
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of its
Contributions.
This License does not grant any rights in the trademarks, service marks, or
logos of any Contributor (except as may be necessary to comply with the
notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this License
(see Section 10.2) or under the terms of a Secondary License (if permitted
under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its Contributions
are its original creation(s) or it has sufficient rights to grant the
rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under applicable
copyright doctrines of fair use, fair dealing, or other equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under the
terms of this License. You must inform recipients that the Source Code Form
of the Covered Software is governed by the terms of this License, and how
they can obtain a copy of this License. You may not attempt to alter or
restrict the recipients rights in the Source Code Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this License,
or sublicense it under different terms, provided that the license for
the Executable Form does not attempt to limit or alter the recipients
rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for the
Covered Software. If the Larger Work is a combination of Covered Software
with a work governed by one or more Secondary Licenses, and the Covered
Software is not Incompatible With Secondary Licenses, this License permits
You to additionally distribute such Covered Software under the terms of
such Secondary License(s), so that the recipient of the Larger Work may, at
their option, further distribute the Covered Software under the terms of
either this License or such Secondary License(s).
3.4. Notices
You may not remove or alter the substance of any license notices (including
copyright notices, patent notices, disclaimers of warranty, or limitations
of liability) contained within the Source Code Form of the Covered
Software, except that You may alter any license notices to the extent
required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on behalf
of any Contributor. You must make it absolutely clear that any such
warranty, support, indemnity, or liability obligation is offered by You
alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute, judicial
order, or regulation then You must: (a) comply with the terms of this License
to the maximum extent possible; and (b) describe the limitations and the code
they affect. Such description must be placed in a text file included with all
distributions of the Covered Software under this License. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
if such Contributor fails to notify You of the non-compliance by some
reasonable means prior to 60 days after You have come back into compliance.
Moreover, Your grants from a particular Contributor are reinstated on an
ongoing basis if such Contributor notifies You of the non-compliance by
some reasonable means, this is the first time You have received notice of
non-compliance with this License from such Contributor, and You become
compliant prior to 30 days after Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions, counter-claims,
and cross-claims) alleging that a Contributor Version directly or
indirectly infringes any patent, then the rights granted to You by any and
all Contributors for the Covered Software under Section 2.1 of this License
shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an “as is” basis, without
warranty of any kind, either expressed, implied, or statutory, including,
without limitation, warranties that the Covered Software is free of defects,
merchantable, fit for a particular purpose or non-infringing. The entire
risk as to the quality and performance of the Covered Software is with You.
Should any Covered Software prove defective in any respect, You (not any
Contributor) assume the cost of any necessary servicing, repair, or
correction. This disclaimer of warranty constitutes an essential part of this
License. No use of any Covered Software is authorized under this License
except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from such
partys negligence to the extent applicable law prohibits such limitation.
Some jurisdictions do not allow the exclusion or limitation of incidental or
consequential damages, so this exclusion and limitation may not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts of
a jurisdiction where the defendant maintains its principal place of business
and such litigation shall be governed by laws of that jurisdiction, without
reference to its conflict-of-law provisions. Nothing in this Section shall
prevent a partys ability to bring cross-claims or counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. Any law or regulation which provides that the language of a
contract shall be construed against the drafter shall not be used to construe
this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version of
the License under which You originally received the Covered Software, or
under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a modified
version of this License if you rename the license and remove any
references to the name of the license steward (except to note that such
modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a relevant
directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - “Incompatible With Secondary Licenses” Notice
This Source Code Form is “Incompatible
With Secondary Licenses”, as defined by
the Mozilla Public License, v. 2.0.

View File

@ -1,89 +0,0 @@
# errwrap
`errwrap` is a package for Go that formalizes the pattern of wrapping errors
and checking if an error contains another error.
There is a common pattern in Go of taking a returned `error` value and
then wrapping it (such as with `fmt.Errorf`) before returning it. The problem
with this pattern is that you completely lose the original `error` structure.
Arguably the _correct_ approach is that you should make a custom structure
implementing the `error` interface, and have the original error as a field
on that structure, such [as this example](http://golang.org/pkg/os/#PathError).
This is a good approach, but you have to know the entire chain of possible
rewrapping that happens, when you might just care about one.
`errwrap` formalizes this pattern (it doesn't matter what approach you use
above) by giving a single interface for wrapping errors, checking if a specific
error is wrapped, and extracting that error.
## Installation and Docs
Install using `go get github.com/hashicorp/errwrap`.
Full documentation is available at
http://godoc.org/github.com/hashicorp/errwrap
## Usage
#### Basic Usage
Below is a very basic example of its usage:
```go
// A function that always returns an error, but wraps it, like a real
// function might.
func tryOpen() error {
_, err := os.Open("/i/dont/exist")
if err != nil {
return errwrap.Wrapf("Doesn't exist: {{err}}", err)
}
return nil
}
func main() {
err := tryOpen()
// We can use the Contains helpers to check if an error contains
// another error. It is safe to do this with a nil error, or with
// an error that doesn't even use the errwrap package.
if errwrap.Contains(err, ErrNotExist) {
// Do something
}
if errwrap.ContainsType(err, new(os.PathError)) {
// Do something
}
// Or we can use the associated `Get` functions to just extract
// a specific error. This would return nil if that specific error doesn't
// exist.
perr := errwrap.GetType(err, new(os.PathError))
}
```
#### Custom Types
If you're already making custom types that properly wrap errors, then
you can get all the functionality of `errwraps.Contains` and such by
implementing the `Wrapper` interface with just one function. Example:
```go
type AppError {
Code ErrorCode
Err error
}
func (e *AppError) WrappedErrors() []error {
return []error{e.Err}
}
```
Now this works:
```go
err := &AppError{Err: fmt.Errorf("an error")}
if errwrap.ContainsType(err, fmt.Errorf("")) {
// This will work!
}
```

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