fix: Update vendor folder base on vendor.conf
and removed unused `engine-api` dependency Signed-off-by: Vincent Demeester <vdemeest@redhat.com>
This commit is contained in:
parent
bd5818eda8
commit
8cac1d50b0
|
|
@ -2,7 +2,6 @@ github.com/BurntSushi/toml master
|
||||||
github.com/Microsoft/go-winio 307e919c663683a9000576fdc855acaf9534c165
|
github.com/Microsoft/go-winio 307e919c663683a9000576fdc855acaf9534c165
|
||||||
github.com/Microsoft/hcsshim a8d9cc56cbce765a7eebdf4792e6ceceeff3edb8
|
github.com/Microsoft/hcsshim a8d9cc56cbce765a7eebdf4792e6ceceeff3edb8
|
||||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
||||||
github.com/docker/engine-api 4290f40c056686fcaa5c9caf02eac1dde9315adf
|
|
||||||
github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
||||||
github.com/mattn/go-shellwords 753a2322a99f87c0eff284980e77f53041555bc6
|
github.com/mattn/go-shellwords 753a2322a99f87c0eff284980e77f53041555bc6
|
||||||
github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062
|
github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The MIT License (MIT)
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
copies of this license document, and changing it is allowed as long
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
as the name is changed.
|
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:
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The above copyright notice and this permission notice shall be included in
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
||||||
|
|
@ -775,7 +775,7 @@ func lexDatetime(lx *lexer) stateFn {
|
||||||
return lexDatetime
|
return lexDatetime
|
||||||
}
|
}
|
||||||
switch r {
|
switch r {
|
||||||
case '-', 'T', ':', '.', 'Z':
|
case '-', 'T', ':', '.', 'Z', '+':
|
||||||
return lexDatetime
|
return lexDatetime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
# go-winio
|
||||||
|
|
||||||
|
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||||
|
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||||
|
for using named pipes as a net transport.
|
||||||
|
|
||||||
|
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
|
||||||
|
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
|
||||||
|
newer operating systems. This is similar to the implementation of network sockets in Go's net
|
||||||
|
package.
|
||||||
|
|
||||||
|
Please see the LICENSE file for licensing information.
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of
|
||||||
|
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
||||||
|
see the [Code of Conduct
|
||||||
|
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
||||||
|
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
||||||
|
questions or comments.
|
||||||
|
|
||||||
|
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
||||||
|
for another named pipe implementation.
|
||||||
|
|
@ -1,350 +0,0 @@
|
||||||
/*Package filters provides tools for encoding a mapping of keys to a set of
|
|
||||||
multiple values.
|
|
||||||
*/
|
|
||||||
package filters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Args stores a mapping of keys to a set of multiple values.
|
|
||||||
type Args struct {
|
|
||||||
fields map[string]map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyValuePair are used to initialize a new Args
|
|
||||||
type KeyValuePair struct {
|
|
||||||
Key string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arg creates a new KeyValuePair for initializing Args
|
|
||||||
func Arg(key, value string) KeyValuePair {
|
|
||||||
return KeyValuePair{Key: key, Value: value}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewArgs returns a new Args populated with the initial args
|
|
||||||
func NewArgs(initialArgs ...KeyValuePair) Args {
|
|
||||||
args := Args{fields: map[string]map[string]bool{}}
|
|
||||||
for _, arg := range initialArgs {
|
|
||||||
args.Add(arg.Key, arg.Value)
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFlag parses a key=value string and adds it to an Args.
|
|
||||||
//
|
|
||||||
// Deprecated: Use Args.Add()
|
|
||||||
func ParseFlag(arg string, prev Args) (Args, error) {
|
|
||||||
filters := prev
|
|
||||||
if len(arg) == 0 {
|
|
||||||
return filters, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(arg, "=") {
|
|
||||||
return filters, ErrBadFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
f := strings.SplitN(arg, "=", 2)
|
|
||||||
|
|
||||||
name := strings.ToLower(strings.TrimSpace(f[0]))
|
|
||||||
value := strings.TrimSpace(f[1])
|
|
||||||
|
|
||||||
filters.Add(name, value)
|
|
||||||
|
|
||||||
return filters, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBadFormat is an error returned when a filter is not in the form key=value
|
|
||||||
//
|
|
||||||
// Deprecated: this error will be removed in a future version
|
|
||||||
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
|
|
||||||
|
|
||||||
// ToParam encodes the Args as args JSON encoded string
|
|
||||||
//
|
|
||||||
// Deprecated: use ToJSON
|
|
||||||
func ToParam(a Args) (string, error) {
|
|
||||||
return ToJSON(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON returns a JSON byte representation of the Args
|
|
||||||
func (args Args) MarshalJSON() ([]byte, error) {
|
|
||||||
if len(args.fields) == 0 {
|
|
||||||
return []byte{}, nil
|
|
||||||
}
|
|
||||||
return json.Marshal(args.fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToJSON returns the Args as a JSON encoded string
|
|
||||||
func ToJSON(a Args) (string, error) {
|
|
||||||
if a.Len() == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
buf, err := json.Marshal(a)
|
|
||||||
return string(buf), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22
|
|
||||||
// then the encoded format will use an older legacy format where the values are a
|
|
||||||
// list of strings, instead of a set.
|
|
||||||
//
|
|
||||||
// Deprecated: Use ToJSON
|
|
||||||
func ToParamWithVersion(version string, a Args) (string, error) {
|
|
||||||
if a.Len() == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if version != "" && versions.LessThan(version, "1.22") {
|
|
||||||
buf, err := json.Marshal(convertArgsToSlice(a.fields))
|
|
||||||
return string(buf), err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ToJSON(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromParam decodes a JSON encoded string into Args
|
|
||||||
//
|
|
||||||
// Deprecated: use FromJSON
|
|
||||||
func FromParam(p string) (Args, error) {
|
|
||||||
return FromJSON(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromJSON decodes a JSON encoded string into Args
|
|
||||||
func FromJSON(p string) (Args, error) {
|
|
||||||
args := NewArgs()
|
|
||||||
|
|
||||||
if p == "" {
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
raw := []byte(p)
|
|
||||||
err := json.Unmarshal(raw, &args)
|
|
||||||
if err == nil {
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to parsing arguments in the legacy slice format
|
|
||||||
deprecated := map[string][]string{}
|
|
||||||
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
|
|
||||||
return args, err
|
|
||||||
}
|
|
||||||
|
|
||||||
args.fields = deprecatedArgs(deprecated)
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON populates the Args from JSON encode bytes
|
|
||||||
func (args Args) UnmarshalJSON(raw []byte) error {
|
|
||||||
if len(raw) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return json.Unmarshal(raw, &args.fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the list of values associated with the key
|
|
||||||
func (args Args) Get(key string) []string {
|
|
||||||
values := args.fields[key]
|
|
||||||
if values == nil {
|
|
||||||
return make([]string, 0)
|
|
||||||
}
|
|
||||||
slice := make([]string, 0, len(values))
|
|
||||||
for key := range values {
|
|
||||||
slice = append(slice, key)
|
|
||||||
}
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a new value to the set of values
|
|
||||||
func (args Args) Add(key, value string) {
|
|
||||||
if _, ok := args.fields[key]; ok {
|
|
||||||
args.fields[key][value] = true
|
|
||||||
} else {
|
|
||||||
args.fields[key] = map[string]bool{value: true}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Del removes a value from the set
|
|
||||||
func (args Args) Del(key, value string) {
|
|
||||||
if _, ok := args.fields[key]; ok {
|
|
||||||
delete(args.fields[key], value)
|
|
||||||
if len(args.fields[key]) == 0 {
|
|
||||||
delete(args.fields, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of keys in the mapping
|
|
||||||
func (args Args) Len() int {
|
|
||||||
return len(args.fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchKVList returns true if all the pairs in sources exist as key=value
|
|
||||||
// pairs in the mapping at key, or if there are no values at key.
|
|
||||||
func (args Args) MatchKVList(key string, sources map[string]string) bool {
|
|
||||||
fieldValues := args.fields[key]
|
|
||||||
|
|
||||||
//do not filter if there is no filter set or cannot determine filter
|
|
||||||
if len(fieldValues) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sources) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for value := range fieldValues {
|
|
||||||
testKV := strings.SplitN(value, "=", 2)
|
|
||||||
|
|
||||||
v, ok := sources[testKV[0]]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(testKV) == 2 && testKV[1] != v {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match returns true if any of the values at key match the source string
|
|
||||||
func (args Args) Match(field, source string) bool {
|
|
||||||
if args.ExactMatch(field, source) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldValues := args.fields[field]
|
|
||||||
for name2match := range fieldValues {
|
|
||||||
match, err := regexp.MatchString(name2match, source)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if match {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExactMatch returns true if the source matches exactly one of the values.
|
|
||||||
func (args Args) ExactMatch(key, source string) bool {
|
|
||||||
fieldValues, ok := args.fields[key]
|
|
||||||
//do not filter if there is no filter set or cannot determine filter
|
|
||||||
if !ok || len(fieldValues) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to match full name value to avoid O(N) regular expression matching
|
|
||||||
return fieldValues[source]
|
|
||||||
}
|
|
||||||
|
|
||||||
// UniqueExactMatch returns true if there is only one value and the source
|
|
||||||
// matches exactly the value.
|
|
||||||
func (args Args) UniqueExactMatch(key, source string) bool {
|
|
||||||
fieldValues := args.fields[key]
|
|
||||||
//do not filter if there is no filter set or cannot determine filter
|
|
||||||
if len(fieldValues) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if len(args.fields[key]) != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to match full name value to avoid O(N) regular expression matching
|
|
||||||
return fieldValues[source]
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuzzyMatch returns true if the source matches exactly one value, or the
|
|
||||||
// source has one of the values as a prefix.
|
|
||||||
func (args Args) FuzzyMatch(key, source string) bool {
|
|
||||||
if args.ExactMatch(key, source) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldValues := args.fields[key]
|
|
||||||
for prefix := range fieldValues {
|
|
||||||
if strings.HasPrefix(source, prefix) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include returns true if the key exists in the mapping
|
|
||||||
//
|
|
||||||
// Deprecated: use Contains
|
|
||||||
func (args Args) Include(field string) bool {
|
|
||||||
_, ok := args.fields[field]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains returns true if the key exists in the mapping
|
|
||||||
func (args Args) Contains(field string) bool {
|
|
||||||
_, ok := args.fields[field]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
type invalidFilter string
|
|
||||||
|
|
||||||
func (e invalidFilter) Error() string {
|
|
||||||
return "Invalid filter '" + string(e) + "'"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (invalidFilter) InvalidParameter() {}
|
|
||||||
|
|
||||||
// Validate compared the set of accepted keys against the keys in the mapping.
|
|
||||||
// An error is returned if any mapping keys are not in the accepted set.
|
|
||||||
func (args Args) Validate(accepted map[string]bool) error {
|
|
||||||
for name := range args.fields {
|
|
||||||
if !accepted[name] {
|
|
||||||
return invalidFilter(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalkValues iterates over the list of values for a key in the mapping and calls
|
|
||||||
// op() for each value. If op returns an error the iteration stops and the
|
|
||||||
// error is returned.
|
|
||||||
func (args Args) WalkValues(field string, op func(value string) error) error {
|
|
||||||
if _, ok := args.fields[field]; !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for v := range args.fields[field] {
|
|
||||||
if err := op(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
|
|
||||||
m := map[string]map[string]bool{}
|
|
||||||
for k, v := range d {
|
|
||||||
values := map[string]bool{}
|
|
||||||
for _, vv := range v {
|
|
||||||
values[vv] = true
|
|
||||||
}
|
|
||||||
m[k] = values
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertArgsToSlice(f map[string]map[string]bool) map[string][]string {
|
|
||||||
m := map[string][]string{}
|
|
||||||
for k, v := range f {
|
|
||||||
values := []string{}
|
|
||||||
for kk := range v {
|
|
||||||
if v[kk] {
|
|
||||||
values = append(values, kk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m[k] = values
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
package versions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// compare compares two version strings
|
|
||||||
// returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
|
|
||||||
func compare(v1, v2 string) int {
|
|
||||||
var (
|
|
||||||
currTab = strings.Split(v1, ".")
|
|
||||||
otherTab = strings.Split(v2, ".")
|
|
||||||
)
|
|
||||||
|
|
||||||
max := len(currTab)
|
|
||||||
if len(otherTab) > max {
|
|
||||||
max = len(otherTab)
|
|
||||||
}
|
|
||||||
for i := 0; i < max; i++ {
|
|
||||||
var currInt, otherInt int
|
|
||||||
|
|
||||||
if len(currTab) > i {
|
|
||||||
currInt, _ = strconv.Atoi(currTab[i])
|
|
||||||
}
|
|
||||||
if len(otherTab) > i {
|
|
||||||
otherInt, _ = strconv.Atoi(otherTab[i])
|
|
||||||
}
|
|
||||||
if currInt > otherInt {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if otherInt > currInt {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// LessThan checks if a version is less than another
|
|
||||||
func LessThan(v, other string) bool {
|
|
||||||
return compare(v, other) == -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// LessThanOrEqualTo checks if a version is less than or equal to another
|
|
||||||
func LessThanOrEqualTo(v, other string) bool {
|
|
||||||
return compare(v, other) <= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GreaterThan checks if a version is greater than another
|
|
||||||
func GreaterThan(v, other string) bool {
|
|
||||||
return compare(v, other) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GreaterThanOrEqualTo checks if a version is greater than or equal to another
|
|
||||||
func GreaterThanOrEqualTo(v, other string) bool {
|
|
||||||
return compare(v, other) >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal checks if a version is equal to another
|
|
||||||
func Equal(v, other string) bool {
|
|
||||||
return compare(v, other) == 0
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
[](https://godoc.org/github.com/docker/go-units)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
go-units is a library to transform human friendly measurements into machine friendly values.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation.
|
||||||
|
|
||||||
|
## Copyright and license
|
||||||
|
|
||||||
|
Copyright © 2015 Docker, Inc.
|
||||||
|
|
||||||
|
go-units is licensed under the Apache License, Version 2.0.
|
||||||
|
See [LICENSE](LICENSE) for the full text of the license.
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
# go-shellwords
|
||||||
|
|
||||||
|
[](https://coveralls.io/r/mattn/go-shellwords?branch=master)
|
||||||
|
[](https://travis-ci.org/mattn/go-shellwords)
|
||||||
|
|
||||||
|
Parse line as shell words.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
args, err := shellwords.Parse("./foo --bar=baz")
|
||||||
|
// args should be ["./foo", "--bar=baz"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
os.Setenv("FOO", "bar")
|
||||||
|
p := shellwords.NewParser()
|
||||||
|
p.ParseEnv = true
|
||||||
|
args, err := p.Parse("./foo $FOO")
|
||||||
|
// args should be ["./foo", "bar"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
p := shellwords.NewParser()
|
||||||
|
p.ParseBacktick = true
|
||||||
|
args, err := p.Parse("./foo `echo $SHELL`")
|
||||||
|
// args should be ["./foo", "/bin/bash"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
shellwords.ParseBacktick = true
|
||||||
|
p := shellwords.NewParser()
|
||||||
|
args, err := p.Parse("./foo `echo $SHELL`")
|
||||||
|
// args should be ["./foo", "/bin/bash"]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Thanks
|
||||||
|
|
||||||
|
This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine).
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
under the MIT License: http://mattn.mit-license.org/2014
|
||||||
|
|
||||||
|
# Author
|
||||||
|
|
||||||
|
Yasuhiro Matsumoto (a.k.a mattn)
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Go Wrapper for ZFS #
|
||||||
|
|
||||||
|
Simple wrappers for ZFS command line tools.
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/mistifyio/go-zfs)
|
||||||
|
|
||||||
|
## Requirements ##
|
||||||
|
|
||||||
|
You need a working ZFS setup. To use on Ubuntu 14.04, setup ZFS:
|
||||||
|
|
||||||
|
sudo apt-get install python-software-properties
|
||||||
|
sudo apt-add-repository ppa:zfs-native/stable
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install ubuntu-zfs libzfs-dev
|
||||||
|
|
||||||
|
Developed using Go 1.3, but currently there isn't anything 1.3 specific. Don't use Ubuntu packages for Go, use http://golang.org/doc/install
|
||||||
|
|
||||||
|
Generally you need root privileges to use anything zfs related.
|
||||||
|
|
||||||
|
## Status ##
|
||||||
|
|
||||||
|
This has been only been tested on Ubuntu 14.04
|
||||||
|
|
||||||
|
In the future, we hope to work directly with libzfs.
|
||||||
|
|
||||||
|
# Hacking #
|
||||||
|
|
||||||
|
The tests have decent examples for most functions.
|
||||||
|
|
||||||
|
```go
|
||||||
|
//assuming a zpool named test
|
||||||
|
//error handling ommitted
|
||||||
|
|
||||||
|
|
||||||
|
f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
|
||||||
|
ok(t, err)
|
||||||
|
|
||||||
|
s, err := f.Snapshot("test", nil)
|
||||||
|
ok(t, err)
|
||||||
|
|
||||||
|
// snapshot is named "test/snapshot-test@test"
|
||||||
|
|
||||||
|
c, err := s.Clone("test/clone-test", nil)
|
||||||
|
|
||||||
|
err := c.Destroy()
|
||||||
|
err := s.Destroy()
|
||||||
|
err := f.Destroy()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Contributing #
|
||||||
|
|
||||||
|
See the [contributing guidelines](./CONTRIBUTING.md)
|
||||||
|
|
||||||
|
|
@ -176,7 +176,7 @@
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2015-2016 Docker, Inc.
|
Copyright 2016 Docker, Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
@ -0,0 +1,425 @@
|
||||||
|
Attribution-ShareAlike 4.0 International
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||||
|
does not provide legal services or legal advice. Distribution of
|
||||||
|
Creative Commons public licenses does not create a lawyer-client or
|
||||||
|
other relationship. Creative Commons makes its licenses and related
|
||||||
|
information available on an "as-is" basis. Creative Commons gives no
|
||||||
|
warranties regarding its licenses, any material licensed under their
|
||||||
|
terms and conditions, or any related information. Creative Commons
|
||||||
|
disclaims all liability for damages resulting from their use to the
|
||||||
|
fullest extent possible.
|
||||||
|
|
||||||
|
Using Creative Commons Public Licenses
|
||||||
|
|
||||||
|
Creative Commons public licenses provide a standard set of terms and
|
||||||
|
conditions that creators and other rights holders may use to share
|
||||||
|
original works of authorship and other material subject to copyright
|
||||||
|
and certain other rights specified in the public license below. The
|
||||||
|
following considerations are for informational purposes only, are not
|
||||||
|
exhaustive, and do not form part of our licenses.
|
||||||
|
|
||||||
|
Considerations for licensors: Our public licenses are
|
||||||
|
intended for use by those authorized to give the public
|
||||||
|
permission to use material in ways otherwise restricted by
|
||||||
|
copyright and certain other rights. Our licenses are
|
||||||
|
irrevocable. Licensors should read and understand the terms
|
||||||
|
and conditions of the license they choose before applying it.
|
||||||
|
Licensors should also secure all rights necessary before
|
||||||
|
applying our licenses so that the public can reuse the
|
||||||
|
material as expected. Licensors should clearly mark any
|
||||||
|
material not subject to the license. This includes other CC-
|
||||||
|
licensed material, or material used under an exception or
|
||||||
|
limitation to copyright. More considerations for licensors:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensors
|
||||||
|
|
||||||
|
Considerations for the public: By using one of our public
|
||||||
|
licenses, a licensor grants the public permission to use the
|
||||||
|
licensed material under specified terms and conditions. If
|
||||||
|
the licensor's permission is not necessary for any reason--for
|
||||||
|
example, because of any applicable exception or limitation to
|
||||||
|
copyright--then that use is not regulated by the license. Our
|
||||||
|
licenses grant only permissions under copyright and certain
|
||||||
|
other rights that a licensor has authority to grant. Use of
|
||||||
|
the licensed material may still be restricted for other
|
||||||
|
reasons, including because others have copyright or other
|
||||||
|
rights in the material. A licensor may make special requests,
|
||||||
|
such as asking that all changes be marked or described.
|
||||||
|
Although not required by our licenses, you are encouraged to
|
||||||
|
respect those requests where reasonable. More_considerations
|
||||||
|
for the public:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensees
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Attribution-ShareAlike 4.0 International Public
|
||||||
|
License
|
||||||
|
|
||||||
|
By exercising the Licensed Rights (defined below), You accept and agree
|
||||||
|
to be bound by the terms and conditions of this Creative Commons
|
||||||
|
Attribution-ShareAlike 4.0 International Public License ("Public
|
||||||
|
License"). To the extent this Public License may be interpreted as a
|
||||||
|
contract, You are granted the Licensed Rights in consideration of Your
|
||||||
|
acceptance of these terms and conditions, and the Licensor grants You
|
||||||
|
such rights in consideration of benefits the Licensor receives from
|
||||||
|
making the Licensed Material available under these terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
|
||||||
|
Section 1 -- Definitions.
|
||||||
|
|
||||||
|
a. Adapted Material means material subject to Copyright and Similar
|
||||||
|
Rights that is derived from or based upon the Licensed Material
|
||||||
|
and in which the Licensed Material is translated, altered,
|
||||||
|
arranged, transformed, or otherwise modified in a manner requiring
|
||||||
|
permission under the Copyright and Similar Rights held by the
|
||||||
|
Licensor. For purposes of this Public License, where the Licensed
|
||||||
|
Material is a musical work, performance, or sound recording,
|
||||||
|
Adapted Material is always produced where the Licensed Material is
|
||||||
|
synched in timed relation with a moving image.
|
||||||
|
|
||||||
|
b. Adapter's License means the license You apply to Your Copyright
|
||||||
|
and Similar Rights in Your contributions to Adapted Material in
|
||||||
|
accordance with the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
c. BY-SA Compatible License means a license listed at
|
||||||
|
creativecommons.org/compatiblelicenses, approved by Creative
|
||||||
|
Commons as essentially the equivalent of this Public License.
|
||||||
|
|
||||||
|
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||||
|
closely related to copyright including, without limitation,
|
||||||
|
performance, broadcast, sound recording, and Sui Generis Database
|
||||||
|
Rights, without regard to how the rights are labeled or
|
||||||
|
categorized. For purposes of this Public License, the rights
|
||||||
|
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||||
|
Rights.
|
||||||
|
|
||||||
|
e. Effective Technological Measures means those measures that, in the
|
||||||
|
absence of proper authority, may not be circumvented under laws
|
||||||
|
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||||
|
Treaty adopted on December 20, 1996, and/or similar international
|
||||||
|
agreements.
|
||||||
|
|
||||||
|
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||||
|
any other exception or limitation to Copyright and Similar Rights
|
||||||
|
that applies to Your use of the Licensed Material.
|
||||||
|
|
||||||
|
g. License Elements means the license attributes listed in the name
|
||||||
|
of a Creative Commons Public License. The License Elements of this
|
||||||
|
Public License are Attribution and ShareAlike.
|
||||||
|
|
||||||
|
h. Licensed Material means the artistic or literary work, database,
|
||||||
|
or other material to which the Licensor applied this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
i. Licensed Rights means the rights granted to You subject to the
|
||||||
|
terms and conditions of this Public License, which are limited to
|
||||||
|
all Copyright and Similar Rights that apply to Your use of the
|
||||||
|
Licensed Material and that the Licensor has authority to license.
|
||||||
|
|
||||||
|
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||||
|
under this Public License.
|
||||||
|
|
||||||
|
k. Share means to provide material to the public by any means or
|
||||||
|
process that requires permission under the Licensed Rights, such
|
||||||
|
as reproduction, public display, public performance, distribution,
|
||||||
|
dissemination, communication, or importation, and to make material
|
||||||
|
available to the public including in ways that members of the
|
||||||
|
public may access the material from a place and at a time
|
||||||
|
individually chosen by them.
|
||||||
|
|
||||||
|
l. Sui Generis Database Rights means rights other than copyright
|
||||||
|
resulting from Directive 96/9/EC of the European Parliament and of
|
||||||
|
the Council of 11 March 1996 on the legal protection of databases,
|
||||||
|
as amended and/or succeeded, as well as other essentially
|
||||||
|
equivalent rights anywhere in the world.
|
||||||
|
|
||||||
|
m. You means the individual or entity exercising the Licensed Rights
|
||||||
|
under this Public License. Your has a corresponding meaning.
|
||||||
|
|
||||||
|
|
||||||
|
Section 2 -- Scope.
|
||||||
|
|
||||||
|
a. License grant.
|
||||||
|
|
||||||
|
1. Subject to the terms and conditions of this Public License,
|
||||||
|
the Licensor hereby grants You a worldwide, royalty-free,
|
||||||
|
non-sublicensable, non-exclusive, irrevocable license to
|
||||||
|
exercise the Licensed Rights in the Licensed Material to:
|
||||||
|
|
||||||
|
a. reproduce and Share the Licensed Material, in whole or
|
||||||
|
in part; and
|
||||||
|
|
||||||
|
b. produce, reproduce, and Share Adapted Material.
|
||||||
|
|
||||||
|
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||||
|
Exceptions and Limitations apply to Your use, this Public
|
||||||
|
License does not apply, and You do not need to comply with
|
||||||
|
its terms and conditions.
|
||||||
|
|
||||||
|
3. Term. The term of this Public License is specified in Section
|
||||||
|
6(a).
|
||||||
|
|
||||||
|
4. Media and formats; technical modifications allowed. The
|
||||||
|
Licensor authorizes You to exercise the Licensed Rights in
|
||||||
|
all media and formats whether now known or hereafter created,
|
||||||
|
and to make technical modifications necessary to do so. The
|
||||||
|
Licensor waives and/or agrees not to assert any right or
|
||||||
|
authority to forbid You from making technical modifications
|
||||||
|
necessary to exercise the Licensed Rights, including
|
||||||
|
technical modifications necessary to circumvent Effective
|
||||||
|
Technological Measures. For purposes of this Public License,
|
||||||
|
simply making modifications authorized by this Section 2(a)
|
||||||
|
(4) never produces Adapted Material.
|
||||||
|
|
||||||
|
5. Downstream recipients.
|
||||||
|
|
||||||
|
a. Offer from the Licensor -- Licensed Material. Every
|
||||||
|
recipient of the Licensed Material automatically
|
||||||
|
receives an offer from the Licensor to exercise the
|
||||||
|
Licensed Rights under the terms and conditions of this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
b. Additional offer from the Licensor -- Adapted Material.
|
||||||
|
Every recipient of Adapted Material from You
|
||||||
|
automatically receives an offer from the Licensor to
|
||||||
|
exercise the Licensed Rights in the Adapted Material
|
||||||
|
under the conditions of the Adapter's License You apply.
|
||||||
|
|
||||||
|
c. No downstream restrictions. You may not offer or impose
|
||||||
|
any additional or different terms or conditions on, or
|
||||||
|
apply any Effective Technological Measures to, the
|
||||||
|
Licensed Material if doing so restricts exercise of the
|
||||||
|
Licensed Rights by any recipient of the Licensed
|
||||||
|
Material.
|
||||||
|
|
||||||
|
6. No endorsement. Nothing in this Public License constitutes or
|
||||||
|
may be construed as permission to assert or imply that You
|
||||||
|
are, or that Your use of the Licensed Material is, connected
|
||||||
|
with, or sponsored, endorsed, or granted official status by,
|
||||||
|
the Licensor or others designated to receive attribution as
|
||||||
|
provided in Section 3(a)(1)(A)(i).
|
||||||
|
|
||||||
|
b. Other rights.
|
||||||
|
|
||||||
|
1. Moral rights, such as the right of integrity, are not
|
||||||
|
licensed under this Public License, nor are publicity,
|
||||||
|
privacy, and/or other similar personality rights; however, to
|
||||||
|
the extent possible, the Licensor waives and/or agrees not to
|
||||||
|
assert any such rights held by the Licensor to the limited
|
||||||
|
extent necessary to allow You to exercise the Licensed
|
||||||
|
Rights, but not otherwise.
|
||||||
|
|
||||||
|
2. Patent and trademark rights are not licensed under this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
3. To the extent possible, the Licensor waives any right to
|
||||||
|
collect royalties from You for the exercise of the Licensed
|
||||||
|
Rights, whether directly or through a collecting society
|
||||||
|
under any voluntary or waivable statutory or compulsory
|
||||||
|
licensing scheme. In all other cases the Licensor expressly
|
||||||
|
reserves any right to collect such royalties.
|
||||||
|
|
||||||
|
|
||||||
|
Section 3 -- License Conditions.
|
||||||
|
|
||||||
|
Your exercise of the Licensed Rights is expressly made subject to the
|
||||||
|
following conditions.
|
||||||
|
|
||||||
|
a. Attribution.
|
||||||
|
|
||||||
|
1. If You Share the Licensed Material (including in modified
|
||||||
|
form), You must:
|
||||||
|
|
||||||
|
a. retain the following if it is supplied by the Licensor
|
||||||
|
with the Licensed Material:
|
||||||
|
|
||||||
|
i. identification of the creator(s) of the Licensed
|
||||||
|
Material and any others designated to receive
|
||||||
|
attribution, in any reasonable manner requested by
|
||||||
|
the Licensor (including by pseudonym if
|
||||||
|
designated);
|
||||||
|
|
||||||
|
ii. a copyright notice;
|
||||||
|
|
||||||
|
iii. a notice that refers to this Public License;
|
||||||
|
|
||||||
|
iv. a notice that refers to the disclaimer of
|
||||||
|
warranties;
|
||||||
|
|
||||||
|
v. a URI or hyperlink to the Licensed Material to the
|
||||||
|
extent reasonably practicable;
|
||||||
|
|
||||||
|
b. indicate if You modified the Licensed Material and
|
||||||
|
retain an indication of any previous modifications; and
|
||||||
|
|
||||||
|
c. indicate the Licensed Material is licensed under this
|
||||||
|
Public License, and include the text of, or the URI or
|
||||||
|
hyperlink to, this Public License.
|
||||||
|
|
||||||
|
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||||
|
reasonable manner based on the medium, means, and context in
|
||||||
|
which You Share the Licensed Material. For example, it may be
|
||||||
|
reasonable to satisfy the conditions by providing a URI or
|
||||||
|
hyperlink to a resource that includes the required
|
||||||
|
information.
|
||||||
|
|
||||||
|
3. If requested by the Licensor, You must remove any of the
|
||||||
|
information required by Section 3(a)(1)(A) to the extent
|
||||||
|
reasonably practicable.
|
||||||
|
|
||||||
|
b. ShareAlike.
|
||||||
|
|
||||||
|
In addition to the conditions in Section 3(a), if You Share
|
||||||
|
Adapted Material You produce, the following conditions also apply.
|
||||||
|
|
||||||
|
1. The Adapter's License You apply must be a Creative Commons
|
||||||
|
license with the same License Elements, this version or
|
||||||
|
later, or a BY-SA Compatible License.
|
||||||
|
|
||||||
|
2. You must include the text of, or the URI or hyperlink to, the
|
||||||
|
Adapter's License You apply. You may satisfy this condition
|
||||||
|
in any reasonable manner based on the medium, means, and
|
||||||
|
context in which You Share Adapted Material.
|
||||||
|
|
||||||
|
3. You may not offer or impose any additional or different terms
|
||||||
|
or conditions on, or apply any Effective Technological
|
||||||
|
Measures to, Adapted Material that restrict exercise of the
|
||||||
|
rights granted under the Adapter's License You apply.
|
||||||
|
|
||||||
|
|
||||||
|
Section 4 -- Sui Generis Database Rights.
|
||||||
|
|
||||||
|
Where the Licensed Rights include Sui Generis Database Rights that
|
||||||
|
apply to Your use of the Licensed Material:
|
||||||
|
|
||||||
|
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||||
|
to extract, reuse, reproduce, and Share all or a substantial
|
||||||
|
portion of the contents of the database;
|
||||||
|
|
||||||
|
b. if You include all or a substantial portion of the database
|
||||||
|
contents in a database in which You have Sui Generis Database
|
||||||
|
Rights, then the database in which You have Sui Generis Database
|
||||||
|
Rights (but not its individual contents) is Adapted Material,
|
||||||
|
|
||||||
|
including for purposes of Section 3(b); and
|
||||||
|
c. You must comply with the conditions in Section 3(a) if You Share
|
||||||
|
all or a substantial portion of the contents of the database.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 4 supplements and does not
|
||||||
|
replace Your obligations under this Public License where the Licensed
|
||||||
|
Rights include other Copyright and Similar Rights.
|
||||||
|
|
||||||
|
|
||||||
|
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||||
|
|
||||||
|
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||||
|
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||||
|
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||||
|
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||||
|
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||||
|
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||||
|
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||||
|
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||||
|
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||||
|
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||||
|
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||||
|
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||||
|
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||||
|
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||||
|
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||||
|
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
c. The disclaimer of warranties and limitation of liability provided
|
||||||
|
above shall be interpreted in a manner that, to the extent
|
||||||
|
possible, most closely approximates an absolute disclaimer and
|
||||||
|
waiver of all liability.
|
||||||
|
|
||||||
|
|
||||||
|
Section 6 -- Term and Termination.
|
||||||
|
|
||||||
|
a. This Public License applies for the term of the Copyright and
|
||||||
|
Similar Rights licensed here. However, if You fail to comply with
|
||||||
|
this Public License, then Your rights under this Public License
|
||||||
|
terminate automatically.
|
||||||
|
|
||||||
|
b. Where Your right to use the Licensed Material has terminated under
|
||||||
|
Section 6(a), it reinstates:
|
||||||
|
|
||||||
|
1. automatically as of the date the violation is cured, provided
|
||||||
|
it is cured within 30 days of Your discovery of the
|
||||||
|
violation; or
|
||||||
|
|
||||||
|
2. upon express reinstatement by the Licensor.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||||
|
right the Licensor may have to seek remedies for Your violations
|
||||||
|
of this Public License.
|
||||||
|
|
||||||
|
c. For the avoidance of doubt, the Licensor may also offer the
|
||||||
|
Licensed Material under separate terms or conditions or stop
|
||||||
|
distributing the Licensed Material at any time; however, doing so
|
||||||
|
will not terminate this Public License.
|
||||||
|
|
||||||
|
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 7 -- Other Terms and Conditions.
|
||||||
|
|
||||||
|
a. The Licensor shall not be bound by any additional or different
|
||||||
|
terms or conditions communicated by You unless expressly agreed.
|
||||||
|
|
||||||
|
b. Any arrangements, understandings, or agreements regarding the
|
||||||
|
Licensed Material not stated herein are separate from and
|
||||||
|
independent of the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 8 -- Interpretation.
|
||||||
|
|
||||||
|
a. For the avoidance of doubt, this Public License does not, and
|
||||||
|
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||||
|
conditions on any use of the Licensed Material that could lawfully
|
||||||
|
be made without permission under this Public License.
|
||||||
|
|
||||||
|
b. To the extent possible, if any provision of this Public License is
|
||||||
|
deemed unenforceable, it shall be automatically reformed to the
|
||||||
|
minimum extent necessary to make it enforceable. If the provision
|
||||||
|
cannot be reformed, it shall be severed from this Public License
|
||||||
|
without affecting the enforceability of the remaining terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
c. No term or condition of this Public License will be waived and no
|
||||||
|
failure to comply consented to unless expressly agreed to by the
|
||||||
|
Licensor.
|
||||||
|
|
||||||
|
d. Nothing in this Public License constitutes or may be interpreted
|
||||||
|
as a limitation upon, or waiver of, any privileges and immunities
|
||||||
|
that apply to the Licensor or You, including from the legal
|
||||||
|
processes of any jurisdiction or authority.
|
||||||
|
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons is not a party to its public licenses.
|
||||||
|
Notwithstanding, Creative Commons may elect to apply one of its public
|
||||||
|
licenses to material it publishes and in those instances will be
|
||||||
|
considered the "Licensor." Except for the limited purpose of indicating
|
||||||
|
that material is shared under a Creative Commons public license or as
|
||||||
|
otherwise permitted by the Creative Commons policies published at
|
||||||
|
creativecommons.org/policies, Creative Commons does not authorize the
|
||||||
|
use of the trademark "Creative Commons" or any other trademark or logo
|
||||||
|
of Creative Commons without its prior written consent including,
|
||||||
|
without limitation, in connection with any unauthorized modifications
|
||||||
|
to any of its public licenses or any other arrangements,
|
||||||
|
understandings, or agreements concerning use of licensed material. For
|
||||||
|
the avoidance of doubt, this paragraph does not form part of the public
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
Creative Commons may be contacted at creativecommons.org.
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
# go-digest
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/opencontainers/go-digest) [](https://goreportcard.com/report/github.com/opencontainers/go-digest) [](https://travis-ci.org/opencontainers/go-digest)
|
||||||
|
|
||||||
|
Common digest package used across the container ecosystem.
|
||||||
|
|
||||||
|
Please see the [godoc](https://godoc.org/github.com/opencontainers/go-digest) for more information.
|
||||||
|
|
||||||
|
# What is a digest?
|
||||||
|
|
||||||
|
A digest is just a hash.
|
||||||
|
|
||||||
|
The most common use case for a digest is to create a content
|
||||||
|
identifier for use in [Content Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage)
|
||||||
|
systems:
|
||||||
|
|
||||||
|
```go
|
||||||
|
id := digest.FromBytes([]byte("my content"))
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above, the id can be used to uniquely identify
|
||||||
|
the byte slice "my content". This allows two disparate applications
|
||||||
|
to agree on a verifiable identifier without having to trust one
|
||||||
|
another.
|
||||||
|
|
||||||
|
An identifying digest can be verified, as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if id != digest.FromBytes([]byte("my content")) {
|
||||||
|
return errors.New("the content has changed!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A `Verifier` type can be used to handle cases where an `io.Reader`
|
||||||
|
makes more sense:
|
||||||
|
|
||||||
|
```go
|
||||||
|
rd := getContent()
|
||||||
|
verifier := id.Verifier()
|
||||||
|
io.Copy(verifier, rd)
|
||||||
|
|
||||||
|
if !verifier.Verified() {
|
||||||
|
return errors.New("the content has changed!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using [Merkle DAGs](https://en.wikipedia.org/wiki/Merkle_tree), this
|
||||||
|
can power a rich, safe, content distribution system.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
While the [godoc](https://godoc.org/github.com/opencontainers/go-digest) is
|
||||||
|
considered the best resource, a few important items need to be called
|
||||||
|
out when using this package.
|
||||||
|
|
||||||
|
1. Make sure to import the hash implementations into your application
|
||||||
|
or the package will panic. You should have something like the
|
||||||
|
following in the main (or other entrypoint) of your application:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
_ "crypto/sha256"
|
||||||
|
_ "crypto/sha512"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
This may seem inconvenient but it allows you replace the hash
|
||||||
|
implementations with others, such as https://github.com/stevvooe/resumable.
|
||||||
|
|
||||||
|
2. Even though `digest.Digest` may be assemable as a string, _always_
|
||||||
|
verify your input with `digest.Parse` or use `Digest.Validate`
|
||||||
|
when accepting untrusted input. While there are measures to
|
||||||
|
avoid common problems, this will ensure you have valid digests
|
||||||
|
in the rest of your application.
|
||||||
|
|
||||||
|
# Stability
|
||||||
|
|
||||||
|
The Go API, at this stage, is considered stable, unless otherwise noted.
|
||||||
|
|
||||||
|
As always, before using a package export, read the [godoc](https://godoc.org/github.com/opencontainers/go-digest).
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
This package is considered fairly complete. It has been in production
|
||||||
|
in thousands (millions?) of deployments and is fairly battle-hardened.
|
||||||
|
New additions will be met with skepticism. If you think there is a
|
||||||
|
missing feature, please file a bug clearly describing the problem and
|
||||||
|
the alternatives you tried before submitting a PR.
|
||||||
|
|
||||||
|
# Reporting security issues
|
||||||
|
|
||||||
|
Please DO NOT file a public issue, instead send your report privately to
|
||||||
|
security@opencontainers.org.
|
||||||
|
|
||||||
|
The maintainers take security seriously. If you discover a security issue,
|
||||||
|
please bring it to their attention right away!
|
||||||
|
|
||||||
|
If you are reporting a security issue, do not create an issue or file a pull
|
||||||
|
request on GitHub. Instead, disclose the issue responsibly by sending an email
|
||||||
|
to security@opencontainers.org (which is inhabited only by the maintainers of
|
||||||
|
the various OCI projects).
|
||||||
|
|
||||||
|
# Copyright and license
|
||||||
|
|
||||||
|
Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code is released under the [Apache 2.0 license](LICENSE). This `README.md` file and the [`CONTRIBUTING.md`](CONTRIBUTING.md) file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file [`LICENSE.docs`](LICENSE.docs). You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/.
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
# runc
|
||||||
|
|
||||||
|
[](https://travis-ci.org/opencontainers/runc)
|
||||||
|
[](https://goreportcard.com/report/github.com/opencontainers/runc)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
`runc` is a CLI tool for spawning and running containers according to the OCI specification.
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
`runc` depends on and tracks the [runtime-spec](https://github.com/opencontainers/runtime-spec) repository.
|
||||||
|
We will try to make sure that `runc` and the OCI specification major versions stay in lockstep.
|
||||||
|
This means that `runc` 1.0.0 should implement the 1.0 version of the specification.
|
||||||
|
|
||||||
|
You can find official releases of `runc` on the [release](https://github.com/opencontainers/runc/releases) page.
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
If you wish to report a security issue, please disclose the issue responsibly
|
||||||
|
to security@opencontainers.org.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
`runc` currently supports the Linux platform with various architecture support.
|
||||||
|
It must be built with Go version 1.6 or higher in order for some features to function properly.
|
||||||
|
|
||||||
|
In order to enable seccomp support you will need to install `libseccomp` on your platform.
|
||||||
|
> e.g. `libseccomp-devel` for CentOS, or `libseccomp-dev` for Ubuntu
|
||||||
|
|
||||||
|
Otherwise, if you do not want to build `runc` with seccomp support you can add `BUILDTAGS=""` when running make.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create a 'github.com/opencontainers' in your GOPATH/src
|
||||||
|
cd github.com/opencontainers
|
||||||
|
git clone https://github.com/opencontainers/runc
|
||||||
|
cd runc
|
||||||
|
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
`runc` will be installed to `/usr/local/sbin/runc` on your system.
|
||||||
|
|
||||||
|
#### Build Tags
|
||||||
|
|
||||||
|
`runc` supports optional build tags for compiling support of various features.
|
||||||
|
To add build tags to the make option the `BUILDTAGS` variable must be set.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make BUILDTAGS='seccomp apparmor'
|
||||||
|
```
|
||||||
|
|
||||||
|
| Build Tag | Feature | Dependency |
|
||||||
|
|-----------|------------------------------------|-------------|
|
||||||
|
| seccomp | Syscall filtering | libseccomp |
|
||||||
|
| selinux | selinux process and mount labeling | <none> |
|
||||||
|
| apparmor | apparmor profile support | libapparmor |
|
||||||
|
| ambient | ambient capability support | kernel 4.3 |
|
||||||
|
|
||||||
|
|
||||||
|
### Running the test suite
|
||||||
|
|
||||||
|
`runc` currently supports running its test suite via Docker.
|
||||||
|
To run the suite just type `make test`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
There are additional make targets for running the tests outside of a container but this is not recommended as the tests are written with the expectation that they can write and remove anywhere.
|
||||||
|
|
||||||
|
You can run a specific test case by setting the `TESTFLAGS` variable.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# make test TESTFLAGS="-run=SomeTestFunction"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using runc
|
||||||
|
|
||||||
|
### Creating an OCI Bundle
|
||||||
|
|
||||||
|
In order to use runc you must have your container in the format of an OCI bundle.
|
||||||
|
If you have Docker installed you can use its `export` method to acquire a root filesystem from an existing Docker container.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create the top most bundle directory
|
||||||
|
mkdir /mycontainer
|
||||||
|
cd /mycontainer
|
||||||
|
|
||||||
|
# create the rootfs directory
|
||||||
|
mkdir rootfs
|
||||||
|
|
||||||
|
# export busybox via Docker into the rootfs directory
|
||||||
|
docker export $(docker create busybox) | tar -C rootfs -xvf -
|
||||||
|
```
|
||||||
|
|
||||||
|
After a root filesystem is populated you just generate a spec in the format of a `config.json` file inside your bundle.
|
||||||
|
`runc` provides a `spec` command to generate a base template spec that you are then able to edit.
|
||||||
|
To find features and documentation for fields in the spec please refer to the [specs](https://github.com/opencontainers/runtime-spec) repository.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
runc spec
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Containers
|
||||||
|
|
||||||
|
Assuming you have an OCI bundle from the previous step you can execute the container in two different ways.
|
||||||
|
|
||||||
|
The first way is to use the convenience command `run` that will handle creating, starting, and deleting the container after it exits.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mycontainer
|
||||||
|
|
||||||
|
runc run mycontainerid
|
||||||
|
```
|
||||||
|
|
||||||
|
If you used the unmodified `runc spec` template this should give you a `sh` session inside the container.
|
||||||
|
|
||||||
|
The second way to start a container is using the specs lifecycle operations.
|
||||||
|
This gives you more power over how the container is created and managed while it is running.
|
||||||
|
This will also launch the container in the background so you will have to edit the `config.json` to remove the `terminal` setting for the simple examples here.
|
||||||
|
Your process field in the `config.json` should look like this below with `"terminal": false` and `"args": ["sleep", "5"]`.
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
"process": {
|
||||||
|
"terminal": false,
|
||||||
|
"user": {
|
||||||
|
"uid": 0,
|
||||||
|
"gid": 0
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"sleep", "5"
|
||||||
|
],
|
||||||
|
"env": [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm"
|
||||||
|
],
|
||||||
|
"cwd": "/",
|
||||||
|
"capabilities": [
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_NET_BIND_SERVICE"
|
||||||
|
],
|
||||||
|
"rlimits": [
|
||||||
|
{
|
||||||
|
"type": "RLIMIT_NOFILE",
|
||||||
|
"hard": 1024,
|
||||||
|
"soft": 1024
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noNewPrivileges": true
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can go though the lifecycle operations in your shell.
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mycontainer
|
||||||
|
|
||||||
|
runc create mycontainerid
|
||||||
|
|
||||||
|
# view the container is created and in the "created" state
|
||||||
|
runc list
|
||||||
|
|
||||||
|
# start the process inside the container
|
||||||
|
runc start mycontainerid
|
||||||
|
|
||||||
|
# after 5 seconds view that the container has exited and is now in the stopped state
|
||||||
|
runc list
|
||||||
|
|
||||||
|
# now delete the container
|
||||||
|
runc delete mycontainerid
|
||||||
|
```
|
||||||
|
|
||||||
|
This adds more complexity but allows higher level systems to manage runc and provides points in the containers creation to setup various settings after the container has created and/or before it is deleted.
|
||||||
|
This is commonly used to setup the container's network stack after `create` but before `start` where the user's defined process will be running.
|
||||||
|
|
||||||
|
#### Supervisors
|
||||||
|
|
||||||
|
`runc` can be used with process supervisors and init systems to ensure that containers are restarted when they exit.
|
||||||
|
An example systemd unit file looks something like this.
|
||||||
|
|
||||||
|
```systemd
|
||||||
|
[Unit]
|
||||||
|
Description=Start My Container
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
ExecStart=/usr/local/sbin/runc run -d --pid-file /run/mycontainerid.pid mycontainerid
|
||||||
|
ExecStopPost=/usr/local/sbin/runc delete mycontainerid
|
||||||
|
WorkingDirectory=/mycontainer
|
||||||
|
PIDFile=/run/mycontainerid.pid
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
Libcontainer provides a native Go implementation for creating containers
|
||||||
|
with namespaces, cgroups, capabilities, and filesystem access controls.
|
||||||
|
It allows you to manage the lifecycle of the container performing additional operations
|
||||||
|
after the container is created.
|
||||||
|
|
||||||
|
|
||||||
|
#### Container
|
||||||
|
A container is a self contained execution environment that shares the kernel of the
|
||||||
|
host system and which is (optionally) isolated from other containers in the system.
|
||||||
|
|
||||||
|
#### Using libcontainer
|
||||||
|
|
||||||
|
Because containers are spawned in a two step process you will need a binary that
|
||||||
|
will be executed as the init process for the container. In libcontainer, we use
|
||||||
|
the current binary (/proc/self/exe) to be executed as the init process, and use
|
||||||
|
arg "init", we call the first step process "bootstrap", so you always need a "init"
|
||||||
|
function as the entry of "bootstrap".
|
||||||
|
|
||||||
|
In addition to the go init function the early stage bootstrap is handled by importing
|
||||||
|
[nsenter](https://github.com/opencontainers/runc/blob/master/libcontainer/nsenter/README.md).
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if len(os.Args) > 1 && os.Args[1] == "init" {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
runtime.LockOSThread()
|
||||||
|
factory, _ := libcontainer.New("")
|
||||||
|
if err := factory.StartInitialization(); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
panic("--this line should have never been executed, congratulations--")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then to create a container you first have to initialize an instance of a factory
|
||||||
|
that will handle the creation and initialization for a container.
|
||||||
|
|
||||||
|
```go
|
||||||
|
factory, err := libcontainer.New("/var/lib/container", libcontainer.Cgroupfs, libcontainer.InitArgs(os.Args[0], "init"))
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have an instance of the factory created we can create a configuration
|
||||||
|
struct describing how the container is to be created. A sample would look similar to this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
||||||
|
config := &configs.Config{
|
||||||
|
Rootfs: "/your/path/to/rootfs",
|
||||||
|
Capabilities: []string{
|
||||||
|
"CAP_CHOWN",
|
||||||
|
"CAP_DAC_OVERRIDE",
|
||||||
|
"CAP_FSETID",
|
||||||
|
"CAP_FOWNER",
|
||||||
|
"CAP_MKNOD",
|
||||||
|
"CAP_NET_RAW",
|
||||||
|
"CAP_SETGID",
|
||||||
|
"CAP_SETUID",
|
||||||
|
"CAP_SETFCAP",
|
||||||
|
"CAP_SETPCAP",
|
||||||
|
"CAP_NET_BIND_SERVICE",
|
||||||
|
"CAP_SYS_CHROOT",
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
Namespaces: configs.Namespaces([]configs.Namespace{
|
||||||
|
{Type: configs.NEWNS},
|
||||||
|
{Type: configs.NEWUTS},
|
||||||
|
{Type: configs.NEWIPC},
|
||||||
|
{Type: configs.NEWPID},
|
||||||
|
{Type: configs.NEWUSER},
|
||||||
|
{Type: configs.NEWNET},
|
||||||
|
}),
|
||||||
|
Cgroups: &configs.Cgroup{
|
||||||
|
Name: "test-container",
|
||||||
|
Parent: "system",
|
||||||
|
Resources: &configs.Resources{
|
||||||
|
MemorySwappiness: nil,
|
||||||
|
AllowAllDevices: nil,
|
||||||
|
AllowedDevices: configs.DefaultAllowedDevices,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaskPaths: []string{
|
||||||
|
"/proc/kcore",
|
||||||
|
"/sys/firmware",
|
||||||
|
},
|
||||||
|
ReadonlyPaths: []string{
|
||||||
|
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||||
|
},
|
||||||
|
Devices: configs.DefaultAutoCreatedDevices,
|
||||||
|
Hostname: "testing",
|
||||||
|
Mounts: []*configs.Mount{
|
||||||
|
{
|
||||||
|
Source: "proc",
|
||||||
|
Destination: "/proc",
|
||||||
|
Device: "proc",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "tmpfs",
|
||||||
|
Destination: "/dev",
|
||||||
|
Device: "tmpfs",
|
||||||
|
Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME,
|
||||||
|
Data: "mode=755",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "devpts",
|
||||||
|
Destination: "/dev/pts",
|
||||||
|
Device: "devpts",
|
||||||
|
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
|
||||||
|
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Device: "tmpfs",
|
||||||
|
Source: "shm",
|
||||||
|
Destination: "/dev/shm",
|
||||||
|
Data: "mode=1777,size=65536k",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "mqueue",
|
||||||
|
Destination: "/dev/mqueue",
|
||||||
|
Device: "mqueue",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "sysfs",
|
||||||
|
Destination: "/sys",
|
||||||
|
Device: "sysfs",
|
||||||
|
Flags: defaultMountFlags | syscall.MS_RDONLY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UidMappings: []configs.IDMap{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 1000,
|
||||||
|
Size: 65536,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GidMappings: []configs.IDMap{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 1000,
|
||||||
|
Size: 65536,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Networks: []*configs.Network{
|
||||||
|
{
|
||||||
|
Type: "loopback",
|
||||||
|
Address: "127.0.0.1/0",
|
||||||
|
Gateway: "localhost",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Rlimits: []configs.Rlimit{
|
||||||
|
{
|
||||||
|
Type: syscall.RLIMIT_NOFILE,
|
||||||
|
Hard: uint64(1025),
|
||||||
|
Soft: uint64(1025),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have the configuration populated you can create a container:
|
||||||
|
|
||||||
|
```go
|
||||||
|
container, err := factory.Create("container-id", config)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To spawn bash as the initial process inside the container and have the
|
||||||
|
processes pid returned in order to wait, signal, or kill the process:
|
||||||
|
|
||||||
|
```go
|
||||||
|
process := &libcontainer.Process{
|
||||||
|
Args: []string{"/bin/bash"},
|
||||||
|
Env: []string{"PATH=/bin"},
|
||||||
|
User: "daemon",
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := container.Run(process)
|
||||||
|
if err != nil {
|
||||||
|
container.Destroy()
|
||||||
|
logrus.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the process to finish.
|
||||||
|
_, err := process.Wait()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroy the container.
|
||||||
|
container.Destroy()
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional ways to interact with a running container are:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// return all the pids for all processes running inside the container.
|
||||||
|
processes, err := container.Processes()
|
||||||
|
|
||||||
|
// get detailed cpu, memory, io, and network statistics for the container and
|
||||||
|
// it's processes.
|
||||||
|
stats, err := container.Stats()
|
||||||
|
|
||||||
|
// pause all processes inside the container.
|
||||||
|
container.Pause()
|
||||||
|
|
||||||
|
// resume all paused processes.
|
||||||
|
container.Resume()
|
||||||
|
|
||||||
|
// send signal to container's init process.
|
||||||
|
container.Signal(signal)
|
||||||
|
|
||||||
|
// update container resource constraints.
|
||||||
|
container.Set(config)
|
||||||
|
|
||||||
|
// get current status of the container.
|
||||||
|
status, err := container.Status()
|
||||||
|
|
||||||
|
// get current container's state information.
|
||||||
|
state, err := container.State()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Checkpoint & Restore
|
||||||
|
|
||||||
|
libcontainer now integrates [CRIU](http://criu.org/) for checkpointing and restoring containers.
|
||||||
|
This let's you save the state of a process running inside a container to disk, and then restore
|
||||||
|
that state into a new process, on the same machine or on another machine.
|
||||||
|
|
||||||
|
`criu` version 1.5.2 or higher is required to use checkpoint and restore.
|
||||||
|
If you don't already have `criu` installed, you can build it from source, following the
|
||||||
|
[online instructions](http://criu.org/Installation). `criu` is also installed in the docker image
|
||||||
|
generated when building libcontainer with docker.
|
||||||
|
|
||||||
|
|
||||||
|
## Copyright and license
|
||||||
|
|
||||||
|
Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license.
|
||||||
|
Docs released under Creative commons.
|
||||||
|
|
||||||
44
vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md
generated
vendored
Normal file
44
vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
## nsenter
|
||||||
|
|
||||||
|
The `nsenter` package registers a special init constructor that is called before
|
||||||
|
the Go runtime has a chance to boot. This provides us the ability to `setns` on
|
||||||
|
existing namespaces and avoid the issues that the Go runtime has with multiple
|
||||||
|
threads. This constructor will be called if this package is registered,
|
||||||
|
imported, in your go application.
|
||||||
|
|
||||||
|
The `nsenter` package will `import "C"` and it uses [cgo](https://golang.org/cmd/cgo/)
|
||||||
|
package. In cgo, if the import of "C" is immediately preceded by a comment, that comment,
|
||||||
|
called the preamble, is used as a header when compiling the C parts of the package.
|
||||||
|
So every time we import package `nsenter`, the C code function `nsexec()` would be
|
||||||
|
called. And package `nsenter` is now only imported in `main_unix.go`, so every time
|
||||||
|
before we call `cmd.Start` on linux, that C code would run.
|
||||||
|
|
||||||
|
Because `nsexec()` must be run before the Go runtime in order to use the
|
||||||
|
Linux kernel namespace, you must `import` this library into a package if
|
||||||
|
you plan to use `libcontainer` directly. Otherwise Go will not execute
|
||||||
|
the `nsexec()` constructor, which means that the re-exec will not cause
|
||||||
|
the namespaces to be joined. You can import it like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import _ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||||
|
```
|
||||||
|
|
||||||
|
`nsexec()` will first get the file descriptor number for the init pipe
|
||||||
|
from the environment variable `_LIBCONTAINER_INITPIPE` (which was opened
|
||||||
|
by the parent and kept open across the fork-exec of the `nsexec()` init
|
||||||
|
process). The init pipe is used to read bootstrap data (namespace paths,
|
||||||
|
clone flags, uid and gid mappings, and the console path) from the parent
|
||||||
|
process. `nsexec()` will then call `setns(2)` to join the namespaces
|
||||||
|
provided in the bootstrap data (if available), `clone(2)` a child process
|
||||||
|
with the provided clone flags, update the user and group ID mappings, do
|
||||||
|
some further miscellaneous setup steps, and then send the PID of the
|
||||||
|
child process to the parent of the `nsexec()` "caller". Finally,
|
||||||
|
the parent `nsexec()` will exit and the child `nsexec()` process will
|
||||||
|
return to allow the Go runtime take over.
|
||||||
|
|
||||||
|
NOTE: We do both `setns(2)` and `clone(2)` even if we don't have any
|
||||||
|
CLONE_NEW* clone flags because we must fork a new process in order to
|
||||||
|
enter the PID namespace.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
This project was automatically exported from code.google.com/p/go-uuid
|
||||||
|
|
||||||
|
# uuid 
|
||||||
|
The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services.
|
||||||
|
|
||||||
|
###### Install
|
||||||
|
`go get github.com/pborman/uuid`
|
||||||
|
|
||||||
|
###### Documentation
|
||||||
|
[](http://godoc.org/github.com/pborman/uuid)
|
||||||
|
|
||||||
|
Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here:
|
||||||
|
http://godoc.org/github.com/pborman/uuid
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors)
|
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors) [](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||||
|
|
||||||
Package errors provides simple error handling primitives.
|
Package errors provides simple error handling primitives.
|
||||||
|
|
||||||
|
|
@ -47,6 +47,6 @@ We welcome pull requests, bug fixes and issue reports. With that said, the bar f
|
||||||
|
|
||||||
Before proposing a change, please discuss your change by raising an issue.
|
Before proposing a change, please discuss your change by raising an issue.
|
||||||
|
|
||||||
## Licence
|
## License
|
||||||
|
|
||||||
BSD-2-Clause
|
BSD-2-Clause
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// which applied recursively up the call stack results in error reports
|
// which when applied recursively up the call stack results in error reports
|
||||||
// without context or debugging information. The errors package allows
|
// without context or debugging information. The errors package allows
|
||||||
// programmers to add context to the failure path in their code in a way
|
// programmers to add context to the failure path in their code in a way
|
||||||
// that does not destroy the original value of the error.
|
// that does not destroy the original value of the error.
|
||||||
|
|
@ -15,16 +15,17 @@
|
||||||
//
|
//
|
||||||
// The errors.Wrap function returns a new error that adds context to the
|
// The errors.Wrap function returns a new error that adds context to the
|
||||||
// original error by recording a stack trace at the point Wrap is called,
|
// original error by recording a stack trace at the point Wrap is called,
|
||||||
// and the supplied message. For example
|
// together with the supplied message. For example
|
||||||
//
|
//
|
||||||
// _, err := ioutil.ReadAll(r)
|
// _, err := ioutil.ReadAll(r)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// return errors.Wrap(err, "read failed")
|
// return errors.Wrap(err, "read failed")
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// If additional control is required the errors.WithStack and errors.WithMessage
|
// If additional control is required, the errors.WithStack and
|
||||||
// functions destructure errors.Wrap into its component operations of annotating
|
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||||
// an error with a stack trace and an a message, respectively.
|
// operations: annotating an error with a stack trace and with a message,
|
||||||
|
// respectively.
|
||||||
//
|
//
|
||||||
// Retrieving the cause of an error
|
// Retrieving the cause of an error
|
||||||
//
|
//
|
||||||
|
|
@ -38,7 +39,7 @@
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||||
// the topmost error which does not implement causer, which is assumed to be
|
// the topmost error that does not implement causer, which is assumed to be
|
||||||
// the original cause. For example:
|
// the original cause. For example:
|
||||||
//
|
//
|
||||||
// switch err := errors.Cause(err).(type) {
|
// switch err := errors.Cause(err).(type) {
|
||||||
|
|
@ -48,16 +49,16 @@
|
||||||
// // unknown error
|
// // unknown error
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// causer interface is not exported by this package, but is considered a part
|
// Although the causer interface is not exported by this package, it is
|
||||||
// of stable public API.
|
// considered a part of its stable public interface.
|
||||||
//
|
//
|
||||||
// Formatted printing of errors
|
// Formatted printing of errors
|
||||||
//
|
//
|
||||||
// All error values returned from this package implement fmt.Formatter and can
|
// All error values returned from this package implement fmt.Formatter and can
|
||||||
// be formatted by the fmt package. The following verbs are supported
|
// be formatted by the fmt package. The following verbs are supported:
|
||||||
//
|
//
|
||||||
// %s print the error. If the error has a Cause it will be
|
// %s print the error. If the error has a Cause it will be
|
||||||
// printed recursively
|
// printed recursively.
|
||||||
// %v see %s
|
// %v see %s
|
||||||
// %+v extended format. Each Frame of the error's StackTrace will
|
// %+v extended format. Each Frame of the error's StackTrace will
|
||||||
// be printed in detail.
|
// be printed in detail.
|
||||||
|
|
@ -65,13 +66,13 @@
|
||||||
// Retrieving the stack trace of an error or wrapper
|
// Retrieving the stack trace of an error or wrapper
|
||||||
//
|
//
|
||||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||||
// invoked. This information can be retrieved with the following interface.
|
// invoked. This information can be retrieved with the following interface:
|
||||||
//
|
//
|
||||||
// type stackTracer interface {
|
// type stackTracer interface {
|
||||||
// StackTrace() errors.StackTrace
|
// StackTrace() errors.StackTrace
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Where errors.StackTrace is defined as
|
// The returned errors.StackTrace type is defined as
|
||||||
//
|
//
|
||||||
// type StackTrace []Frame
|
// type StackTrace []Frame
|
||||||
//
|
//
|
||||||
|
|
@ -85,8 +86,8 @@
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// stackTracer interface is not exported by this package, but is considered a part
|
// Although the stackTracer interface is not exported by this package, it is
|
||||||
// of stable public API.
|
// considered a part of its stable public interface.
|
||||||
//
|
//
|
||||||
// See the documentation for Frame.Format for more details.
|
// See the documentation for Frame.Format for more details.
|
||||||
package errors
|
package errors
|
||||||
|
|
@ -192,7 +193,7 @@ func Wrap(err error, message string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapf returns an error annotating err with a stack trace
|
// Wrapf returns an error annotating err with a stack trace
|
||||||
// at the point Wrapf is call, and the format specifier.
|
// at the point Wrapf is called, and the format specifier.
|
||||||
// If err is nil, Wrapf returns nil.
|
// If err is nil, Wrapf returns nil.
|
||||||
func Wrapf(err error, format string, args ...interface{}) error {
|
func Wrapf(err error, format string, args ...interface{}) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -220,6 +221,18 @@ func WithMessage(err error, message string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithMessagef annotates err with the format specifier.
|
||||||
|
// If err is nil, WithMessagef returns nil.
|
||||||
|
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &withMessage{
|
||||||
|
cause: err,
|
||||||
|
msg: fmt.Sprintf(format, args...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type withMessage struct {
|
type withMessage struct {
|
||||||
cause error
|
cause error
|
||||||
msg string
|
msg string
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,8 @@ func (f Frame) line() int {
|
||||||
//
|
//
|
||||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||||
//
|
//
|
||||||
// %+s path of source file relative to the compile time GOPATH
|
// %+s function name and path of source file relative to the compile time
|
||||||
|
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||||
// %+v equivalent to %+s:%d
|
// %+v equivalent to %+s:%d
|
||||||
func (f Frame) Format(s fmt.State, verb rune) {
|
func (f Frame) Format(s fmt.State, verb rune) {
|
||||||
switch verb {
|
switch verb {
|
||||||
|
|
@ -144,43 +145,3 @@ func funcname(name string) string {
|
||||||
i = strings.Index(name, ".")
|
i = strings.Index(name, ".")
|
||||||
return name[i+1:]
|
return name[i+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimGOPATH(name, file string) string {
|
|
||||||
// Here we want to get the source file path relative to the compile time
|
|
||||||
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
|
||||||
// GOPATH at runtime, but we can infer the number of path segments in the
|
|
||||||
// GOPATH. We note that fn.Name() returns the function name qualified by
|
|
||||||
// the import path, which does not include the GOPATH. Thus we can trim
|
|
||||||
// segments from the beginning of the file path until the number of path
|
|
||||||
// separators remaining is one more than the number of path separators in
|
|
||||||
// the function name. For example, given:
|
|
||||||
//
|
|
||||||
// GOPATH /home/user
|
|
||||||
// file /home/user/src/pkg/sub/file.go
|
|
||||||
// fn.Name() pkg/sub.Type.Method
|
|
||||||
//
|
|
||||||
// We want to produce:
|
|
||||||
//
|
|
||||||
// pkg/sub/file.go
|
|
||||||
//
|
|
||||||
// From this we can easily see that fn.Name() has one less path separator
|
|
||||||
// than our desired output. We count separators from the end of the file
|
|
||||||
// path until it finds two more than in the function name and then move
|
|
||||||
// one character forward to preserve the initial path segment without a
|
|
||||||
// leading separator.
|
|
||||||
const sep = "/"
|
|
||||||
goal := strings.Count(name, sep) + 2
|
|
||||||
i := len(file)
|
|
||||||
for n := 0; n < goal; n++ {
|
|
||||||
i = strings.LastIndex(file[:i], sep)
|
|
||||||
if i == -1 {
|
|
||||||
// not enough separators found, set i so that the slice expression
|
|
||||||
// below leaves file unmodified
|
|
||||||
i = -len(sep)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// get back to 0 or trim the leading separator
|
|
||||||
file = file[i+len(sep):]
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -103,12 +103,12 @@ func newPid(pid int) (c Capabilities, err error) {
|
||||||
case linuxCapVer1:
|
case linuxCapVer1:
|
||||||
p := new(capsV1)
|
p := new(capsV1)
|
||||||
p.hdr.version = capVers
|
p.hdr.version = capVers
|
||||||
p.hdr.pid = pid
|
p.hdr.pid = int32(pid)
|
||||||
c = p
|
c = p
|
||||||
case linuxCapVer2, linuxCapVer3:
|
case linuxCapVer2, linuxCapVer3:
|
||||||
p := new(capsV3)
|
p := new(capsV3)
|
||||||
p.hdr.version = capVers
|
p.hdr.version = capVers
|
||||||
p.hdr.pid = pid
|
p.hdr.pid = int32(pid)
|
||||||
c = p
|
c = p
|
||||||
default:
|
default:
|
||||||
err = errUnknownVers
|
err = errUnknownVers
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
type capHeader struct {
|
type capHeader struct {
|
||||||
version uint32
|
version uint32
|
||||||
pid int
|
pid int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type capData struct {
|
type capData struct {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
# go-patricia #
|
||||||
|
|
||||||
|
**Documentation**: [GoDoc](http://godoc.org/github.com/tchap/go-patricia/patricia)<br />
|
||||||
|
**Build Status**: [](https://drone.io/github.com/tchap/go-patricia/latest)<br />
|
||||||
|
**Test Coverage**: [](https://coveralls.io/r/tchap/go-patricia)
|
||||||
|
|
||||||
|
## About ##
|
||||||
|
|
||||||
|
A generic patricia trie (also called radix tree) implemented in Go (Golang).
|
||||||
|
|
||||||
|
The patricia trie as implemented in this library enables fast visiting of items
|
||||||
|
in some particular ways:
|
||||||
|
|
||||||
|
1. visit all items saved in the tree,
|
||||||
|
2. visit all items matching particular prefix (visit subtree), or
|
||||||
|
3. given a string, visit all items matching some prefix of that string.
|
||||||
|
|
||||||
|
`[]byte` type is used for keys, `interface{}` for values.
|
||||||
|
|
||||||
|
`Trie` is not thread safe. Synchronize the access yourself.
|
||||||
|
|
||||||
|
### State of the Project ###
|
||||||
|
|
||||||
|
Apparently some people are using this, so the API should not change often.
|
||||||
|
Any ideas on how to make the library better are still welcome.
|
||||||
|
|
||||||
|
More (unit) testing would be cool as well...
|
||||||
|
|
||||||
|
## Usage ##
|
||||||
|
|
||||||
|
Import the package from GitHub first.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/tchap/go-patricia/patricia"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can as well use gopkg.in thingie:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "gopkg.in/tchap/go-patricia.v2/patricia"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can start having fun.
|
||||||
|
|
||||||
|
```go
|
||||||
|
printItem := func(prefix patricia.Prefix, item patricia.Item) error {
|
||||||
|
fmt.Printf("%q: %v\n", prefix, item)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new default trie (using the default parameter values).
|
||||||
|
trie := NewTrie()
|
||||||
|
|
||||||
|
// Create a new custom trie.
|
||||||
|
trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10))
|
||||||
|
|
||||||
|
// Insert some items.
|
||||||
|
trie.Insert(Prefix("Pepa Novak"), 1)
|
||||||
|
trie.Insert(Prefix("Pepa Sindelar"), 2)
|
||||||
|
trie.Insert(Prefix("Karel Macha"), 3)
|
||||||
|
trie.Insert(Prefix("Karel Hynek Macha"), 4)
|
||||||
|
|
||||||
|
// Just check if some things are present in the tree.
|
||||||
|
key := Prefix("Pepa Novak")
|
||||||
|
fmt.Printf("%q present? %v\n", key, trie.Match(key))
|
||||||
|
// "Pepa Novak" present? true
|
||||||
|
key = Prefix("Karel")
|
||||||
|
fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
|
||||||
|
// Anybody called "Karel" here? true
|
||||||
|
|
||||||
|
// Walk the tree in alphabetical order.
|
||||||
|
trie.Visit(printItem)
|
||||||
|
// "Karel Hynek Macha": 4
|
||||||
|
// "Karel Macha": 3
|
||||||
|
// "Pepa Novak": 1
|
||||||
|
// "Pepa Sindelar": 2
|
||||||
|
|
||||||
|
// Walk a subtree.
|
||||||
|
trie.VisitSubtree(Prefix("Pepa"), printItem)
|
||||||
|
// "Pepa Novak": 1
|
||||||
|
// "Pepa Sindelar": 2
|
||||||
|
|
||||||
|
// Modify an item, then fetch it from the tree.
|
||||||
|
trie.Set(Prefix("Karel Hynek Macha"), 10)
|
||||||
|
key = Prefix("Karel Hynek Macha")
|
||||||
|
fmt.Printf("%q: %v\n", key, trie.Get(key))
|
||||||
|
// "Karel Hynek Macha": 10
|
||||||
|
|
||||||
|
// Walk prefixes.
|
||||||
|
prefix := Prefix("Karel Hynek Macha je kouzelnik")
|
||||||
|
trie.VisitPrefixes(prefix, printItem)
|
||||||
|
// "Karel Hynek Macha": 10
|
||||||
|
|
||||||
|
// Delete some items.
|
||||||
|
trie.Delete(Prefix("Pepa Novak"))
|
||||||
|
trie.Delete(Prefix("Karel Macha"))
|
||||||
|
|
||||||
|
// Walk again.
|
||||||
|
trie.Visit(printItem)
|
||||||
|
// "Karel Hynek Macha": 10
|
||||||
|
// "Pepa Sindelar": 2
|
||||||
|
|
||||||
|
// Delete a subtree.
|
||||||
|
trie.DeleteSubtree(Prefix("Pepa"))
|
||||||
|
|
||||||
|
// Print what is left.
|
||||||
|
trie.Visit(printItem)
|
||||||
|
// "Karel Hynek Macha": 10
|
||||||
|
```
|
||||||
|
|
||||||
|
## License ##
|
||||||
|
|
||||||
|
MIT, check the `LICENSE` file.
|
||||||
|
|
||||||
|
[](https://www.gittip.com/tchap/
|
||||||
|
"Gittip Badge")
|
||||||
|
|
||||||
|
[](https://bitdeli.com/free
|
||||||
|
"Bitdeli Badge")
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
# tar-split
|
||||||
|
|
||||||
|
[](https://travis-ci.org/vbatts/tar-split)
|
||||||
|
[](https://goreportcard.com/report/github.com/vbatts/tar-split)
|
||||||
|
|
||||||
|
Pristinely disassembling a tar archive, and stashing needed raw bytes and offsets to reassemble a validating original archive.
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
Code API for libraries provided by `tar-split`:
|
||||||
|
|
||||||
|
* https://godoc.org/github.com/vbatts/tar-split/tar/asm
|
||||||
|
* https://godoc.org/github.com/vbatts/tar-split/tar/storage
|
||||||
|
* https://godoc.org/github.com/vbatts/tar-split/archive/tar
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
The command line utilitiy is installable via:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/vbatts/tar-split/cmd/tar-split
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
For cli usage, see its [README.md](cmd/tar-split/README.md).
|
||||||
|
For the library see the [docs](#docs)
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
### Basic disassembly and assembly
|
||||||
|
|
||||||
|
This demonstrates the `tar-split` command and how to assemble a tar archive from the `tar-data.json.gz`
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
[youtube video of basic command demo](https://youtu.be/vh5wyjIOBtc)
|
||||||
|
|
||||||
|
### Docker layer preservation
|
||||||
|
|
||||||
|
This demonstrates the tar-split integration for docker-1.8. Providing consistent tar archives for the image layer content.
|
||||||
|
|
||||||
|

|
||||||
|
[youtube vide of docker layer checksums](https://youtu.be/tV_Dia8E8xw)
|
||||||
|
|
||||||
|
## Caveat
|
||||||
|
|
||||||
|
Eventually this should detect TARs that this is not possible with.
|
||||||
|
|
||||||
|
For example stored sparse files that have "holes" in them, will be read as a
|
||||||
|
contiguous file, though the archive contents may be recorded in sparse format.
|
||||||
|
Therefore when adding the file payload to a reassembled tar, to achieve
|
||||||
|
identical output, the file payload would need be precisely re-sparsified. This
|
||||||
|
is not something I seek to fix immediately, but would rather have an alert that
|
||||||
|
precise reassembly is not possible.
|
||||||
|
(see more http://www.gnu.org/software/tar/manual/html_node/Sparse-Formats.html)
|
||||||
|
|
||||||
|
|
||||||
|
Other caveat, while tar archives support having multiple file entries for the
|
||||||
|
same path, we will not support this feature. If there are more than one entries
|
||||||
|
with the same path, expect an err (like `ErrDuplicatePath`) or a resulting tar
|
||||||
|
stream that does not validate your original checksum/signature.
|
||||||
|
|
||||||
|
## Contract
|
||||||
|
|
||||||
|
Do not break the API of stdlib `archive/tar` in our fork (ideally find an upstream mergeable solution).
|
||||||
|
|
||||||
|
## Std Version
|
||||||
|
|
||||||
|
The version of golang stdlib `archive/tar` is from go1.6
|
||||||
|
It is minimally extended to expose the raw bytes of the TAR, rather than just the marshalled headers and file stream.
|
||||||
|
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
See the [design](concept/DESIGN.md).
|
||||||
|
|
||||||
|
## Stored Metadata
|
||||||
|
|
||||||
|
Since the raw bytes of the headers and padding are stored, you may be wondering
|
||||||
|
what the size implications are. The headers are at least 512 bytes per
|
||||||
|
file (sometimes more), at least 1024 null bytes on the end, and then various
|
||||||
|
padding. This makes for a constant linear growth in the stored metadata, with a
|
||||||
|
naive storage implementation.
|
||||||
|
|
||||||
|
First we'll get an archive to work with. For repeatability, we'll make an
|
||||||
|
archive from what you've just cloned:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git archive --format=tar -o tar-split.tar HEAD .
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go get github.com/vbatts/tar-split/cmd/tar-split
|
||||||
|
$ tar-split checksize ./tar-split.tar
|
||||||
|
inspecting "tar-split.tar" (size 210k)
|
||||||
|
-- number of files: 50
|
||||||
|
-- size of metadata uncompressed: 53k
|
||||||
|
-- size of gzip compressed metadata: 3k
|
||||||
|
```
|
||||||
|
|
||||||
|
So assuming you've managed the extraction of the archive yourself, for reuse of
|
||||||
|
the file payloads from a relative path, then the only additional storage
|
||||||
|
implications are as little as 3kb.
|
||||||
|
|
||||||
|
But let's look at a larger archive, with many files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ls -sh ./d.tar
|
||||||
|
1.4G ./d.tar
|
||||||
|
$ tar-split checksize ~/d.tar
|
||||||
|
inspecting "/home/vbatts/d.tar" (size 1420749k)
|
||||||
|
-- number of files: 38718
|
||||||
|
-- size of metadata uncompressed: 43261k
|
||||||
|
-- size of gzip compressed metadata: 2251k
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, an archive with 38,718 files has a compressed footprint of about 2mb.
|
||||||
|
|
||||||
|
Rolling the null bytes on the end of the archive, we will assume a
|
||||||
|
bytes-per-file rate for the storage implications.
|
||||||
|
|
||||||
|
| uncompressed | compressed |
|
||||||
|
| :----------: | :--------: |
|
||||||
|
| ~ 1kb per/file | 0.06kb per/file |
|
||||||
|
|
||||||
|
|
||||||
|
## What's Next?
|
||||||
|
|
||||||
|
* More implementations of storage Packer and Unpacker
|
||||||
|
* More implementations of FileGetter and FilePutter
|
||||||
|
* would be interesting to have an assembler stream that implements `io.Seeker`
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
See [LICENSE](LICENSE)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
asm
|
||||||
|
===
|
||||||
|
|
||||||
|
This library for assembly and disassembly of tar archives, facilitated by
|
||||||
|
`github.com/vbatts/tar-split/tar/storage`.
|
||||||
|
|
||||||
|
|
||||||
|
Concerns
|
||||||
|
--------
|
||||||
|
|
||||||
|
For completely safe assembly/disassembly, there will need to be a Content
|
||||||
|
Addressable Storage (CAS) directory, that maps to a checksum in the
|
||||||
|
`storage.Entity` of `storage.FileType`.
|
||||||
|
|
||||||
|
This is due to the fact that tar archives _can_ allow multiple records for the
|
||||||
|
same path, but the last one effectively wins. Even if the prior records had a
|
||||||
|
different payload.
|
||||||
|
|
||||||
|
In this way, when assembling an archive from relative paths, if the archive has
|
||||||
|
multiple entries for the same path, then all payloads read in from a relative
|
||||||
|
path would be identical.
|
||||||
|
|
||||||
|
|
||||||
|
Thoughts
|
||||||
|
--------
|
||||||
|
|
||||||
|
Have a look-aside directory or storage. This way when a clobbering record is
|
||||||
|
encountered from the tar stream, then the payload of the prior/existing file is
|
||||||
|
stored to the CAS. This way the clobbering record's file payload can be
|
||||||
|
extracted, but we'll have preserved the payload needed to reassemble a precise
|
||||||
|
tar archive.
|
||||||
|
|
||||||
|
clobbered/path/to/file.[0-N]
|
||||||
|
|
||||||
|
*alternatively*
|
||||||
|
|
||||||
|
We could just _not_ support tar streams that have clobbering file paths.
|
||||||
|
Appending records to the archive is not incredibly common, and doesn't happen
|
||||||
|
by default for most implementations. Not supporting them wouldn't be a
|
||||||
|
security concern either, as if it did occur, we would reassemble an archive
|
||||||
|
that doesn't validate signature/checksum, so it shouldn't be trusted anyway.
|
||||||
|
|
||||||
|
Otherwise, this will allow us to defer support for appended files as a FUTURE FEATURE.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// event represents auto-reset, initially non-signaled Windows event.
|
||||||
|
// It is used to communicate between go and asm parts of this package.
|
||||||
|
type event struct {
|
||||||
|
h windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEvent() (*event, error) {
|
||||||
|
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &event{h: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Close() error {
|
||||||
|
return windows.CloseHandle(e.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Set() error {
|
||||||
|
return windows.SetEvent(e.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Wait() error {
|
||||||
|
s, err := windows.WaitForSingleObject(e.h, windows.INFINITE)
|
||||||
|
switch s {
|
||||||
|
case windows.WAIT_OBJECT_0:
|
||||||
|
break
|
||||||
|
case windows.WAIT_FAILED:
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return errors.New("unexpected result from WaitForSingleObject")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build !go1.3
|
||||||
|
|
||||||
|
// copied from pkg/runtime
|
||||||
|
typedef unsigned int uint32;
|
||||||
|
typedef unsigned long long int uint64;
|
||||||
|
#ifdef _64BIT
|
||||||
|
typedef uint64 uintptr;
|
||||||
|
#else
|
||||||
|
typedef uint32 uintptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// from sys_386.s or sys_amd64.s
|
||||||
|
void ·servicemain(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
·getServiceMain(uintptr *r)
|
||||||
|
{
|
||||||
|
*r = (uintptr)·servicemain;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2014 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
|
||||||
|
// +build !go1.3
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
// from go12.c
|
||||||
|
func getServiceMain(r *uintptr)
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2014 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
|
||||||
|
// +build go1.3
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
|
||||||
|
|
||||||
|
// Should be a built-in for unsafe.Pointer?
|
||||||
|
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(p) + x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcPC returns the entry PC of the function f.
|
||||||
|
// It assumes that f is a func value. Otherwise the behavior is undefined.
|
||||||
|
func funcPC(f interface{}) uintptr {
|
||||||
|
return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
// from sys_386.s and sys_amd64.s
|
||||||
|
func servicectlhandler(ctl uint32) uintptr
|
||||||
|
func servicemain(argc uint32, argv **uint16)
|
||||||
|
|
||||||
|
func getServiceMain(r *uintptr) {
|
||||||
|
*r = funcPC(servicemain)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func allocSid(subAuth0 uint32) (*windows.SID, error) {
|
||||||
|
var sid *windows.SID
|
||||||
|
err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY,
|
||||||
|
1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAnInteractiveSession determines if calling process is running interactively.
|
||||||
|
// It queries the process token for membership in the Interactive group.
|
||||||
|
// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
|
||||||
|
func IsAnInteractiveSession() (bool, error) {
|
||||||
|
interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer windows.FreeSid(interSid)
|
||||||
|
|
||||||
|
serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer windows.FreeSid(serviceSid)
|
||||||
|
|
||||||
|
t, err := windows.OpenCurrentProcessToken()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer t.Close()
|
||||||
|
|
||||||
|
gs, err := t.GetTokenGroups()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
p := unsafe.Pointer(&gs.Groups[0])
|
||||||
|
groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount]
|
||||||
|
for _, g := range groups {
|
||||||
|
if windows.EqualSid(g.Sid, interSid) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if windows.EqualSid(g.Sid, serviceSid) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,363 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package svc provides everything required to build Windows service.
|
||||||
|
//
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State describes service execution state (Stopped, Running and so on).
|
||||||
|
type State uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Stopped = State(windows.SERVICE_STOPPED)
|
||||||
|
StartPending = State(windows.SERVICE_START_PENDING)
|
||||||
|
StopPending = State(windows.SERVICE_STOP_PENDING)
|
||||||
|
Running = State(windows.SERVICE_RUNNING)
|
||||||
|
ContinuePending = State(windows.SERVICE_CONTINUE_PENDING)
|
||||||
|
PausePending = State(windows.SERVICE_PAUSE_PENDING)
|
||||||
|
Paused = State(windows.SERVICE_PAUSED)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cmd represents service state change request. It is sent to a service
|
||||||
|
// by the service manager, and should be actioned upon by the service.
|
||||||
|
type Cmd uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Stop = Cmd(windows.SERVICE_CONTROL_STOP)
|
||||||
|
Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
|
||||||
|
Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
|
||||||
|
Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
|
||||||
|
Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
|
||||||
|
ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
|
||||||
|
NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
|
||||||
|
NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
|
||||||
|
NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
|
||||||
|
NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
|
||||||
|
DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
|
||||||
|
HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
|
||||||
|
PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
|
||||||
|
SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Accepted is used to describe commands accepted by the service.
|
||||||
|
// Note that Interrogate is always accepted.
|
||||||
|
type Accepted uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP)
|
||||||
|
AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN)
|
||||||
|
AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE)
|
||||||
|
AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)
|
||||||
|
AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE)
|
||||||
|
AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
|
||||||
|
AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT)
|
||||||
|
AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Status combines State and Accepted commands to fully describe running service.
|
||||||
|
type Status struct {
|
||||||
|
State State
|
||||||
|
Accepts Accepted
|
||||||
|
CheckPoint uint32 // used to report progress during a lengthy operation
|
||||||
|
WaitHint uint32 // estimated time required for a pending operation, in milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeRequest is sent to the service Handler to request service status change.
|
||||||
|
type ChangeRequest struct {
|
||||||
|
Cmd Cmd
|
||||||
|
EventType uint32
|
||||||
|
EventData uintptr
|
||||||
|
CurrentStatus Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is the interface that must be implemented to build Windows service.
|
||||||
|
type Handler interface {
|
||||||
|
|
||||||
|
// Execute will be called by the package code at the start of
|
||||||
|
// the service, and the service will exit once Execute completes.
|
||||||
|
// Inside Execute you must read service change requests from r and
|
||||||
|
// act accordingly. You must keep service control manager up to date
|
||||||
|
// about state of your service by writing into s as required.
|
||||||
|
// args contains service name followed by argument strings passed
|
||||||
|
// to the service.
|
||||||
|
// You can provide service exit code in exitCode return parameter,
|
||||||
|
// with 0 being "no error". You can also indicate if exit code,
|
||||||
|
// if any, is service specific or not by using svcSpecificEC
|
||||||
|
// parameter.
|
||||||
|
Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These are used by asm code.
|
||||||
|
goWaitsH uintptr
|
||||||
|
cWaitsH uintptr
|
||||||
|
ssHandle uintptr
|
||||||
|
sName *uint16
|
||||||
|
sArgc uintptr
|
||||||
|
sArgv **uint16
|
||||||
|
ctlHandlerExProc uintptr
|
||||||
|
cSetEvent uintptr
|
||||||
|
cWaitForSingleObject uintptr
|
||||||
|
cRegisterServiceCtrlHandlerExW uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
k := syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
cSetEvent = k.MustFindProc("SetEvent").Addr()
|
||||||
|
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
|
||||||
|
a := syscall.MustLoadDLL("advapi32.dll")
|
||||||
|
cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HandlerEx prototype also has a context pointer but since we don't use
|
||||||
|
// it at start-up time we don't have to pass it over either.
|
||||||
|
type ctlEvent struct {
|
||||||
|
cmd Cmd
|
||||||
|
eventType uint32
|
||||||
|
eventData uintptr
|
||||||
|
errno uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// service provides access to windows service api.
|
||||||
|
type service struct {
|
||||||
|
name string
|
||||||
|
h windows.Handle
|
||||||
|
cWaits *event
|
||||||
|
goWaits *event
|
||||||
|
c chan ctlEvent
|
||||||
|
handler Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func newService(name string, handler Handler) (*service, error) {
|
||||||
|
var s service
|
||||||
|
var err error
|
||||||
|
s.name = name
|
||||||
|
s.c = make(chan ctlEvent)
|
||||||
|
s.handler = handler
|
||||||
|
s.cWaits, err = newEvent()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.goWaits, err = newEvent()
|
||||||
|
if err != nil {
|
||||||
|
s.cWaits.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) close() error {
|
||||||
|
s.cWaits.Close()
|
||||||
|
s.goWaits.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type exitCode struct {
|
||||||
|
isSvcSpecific bool
|
||||||
|
errno uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) updateStatus(status *Status, ec *exitCode) error {
|
||||||
|
if s.h == 0 {
|
||||||
|
return errors.New("updateStatus with no service status handle")
|
||||||
|
}
|
||||||
|
var t windows.SERVICE_STATUS
|
||||||
|
t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
|
||||||
|
t.CurrentState = uint32(status.State)
|
||||||
|
if status.Accepts&AcceptStop != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptShutdown != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptPauseAndContinue != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptParamChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptNetBindChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptHardwareProfileChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptPowerEvent != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptSessionChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE
|
||||||
|
}
|
||||||
|
if ec.errno == 0 {
|
||||||
|
t.Win32ExitCode = windows.NO_ERROR
|
||||||
|
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||||
|
} else if ec.isSvcSpecific {
|
||||||
|
t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR)
|
||||||
|
t.ServiceSpecificExitCode = ec.errno
|
||||||
|
} else {
|
||||||
|
t.Win32ExitCode = ec.errno
|
||||||
|
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||||
|
}
|
||||||
|
t.CheckPoint = status.CheckPoint
|
||||||
|
t.WaitHint = status.WaitHint
|
||||||
|
return windows.SetServiceStatus(s.h, &t)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
|
||||||
|
sysErrNewThreadInCallback
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *service) run() {
|
||||||
|
s.goWaits.Wait()
|
||||||
|
s.h = windows.Handle(ssHandle)
|
||||||
|
argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
|
||||||
|
args := make([]string, len(argv))
|
||||||
|
for i, a := range argv {
|
||||||
|
args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdsToHandler := make(chan ChangeRequest)
|
||||||
|
changesFromHandler := make(chan Status)
|
||||||
|
exitFromHandler := make(chan exitCode)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
|
||||||
|
exitFromHandler <- exitCode{ss, errno}
|
||||||
|
}()
|
||||||
|
|
||||||
|
status := Status{State: Stopped}
|
||||||
|
ec := exitCode{isSvcSpecific: true, errno: 0}
|
||||||
|
var outch chan ChangeRequest
|
||||||
|
inch := s.c
|
||||||
|
var cmd Cmd
|
||||||
|
var evtype uint32
|
||||||
|
var evdata uintptr
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case r := <-inch:
|
||||||
|
if r.errno != 0 {
|
||||||
|
ec.errno = r.errno
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
inch = nil
|
||||||
|
outch = cmdsToHandler
|
||||||
|
cmd = r.cmd
|
||||||
|
evtype = r.eventType
|
||||||
|
evdata = r.eventData
|
||||||
|
case outch <- ChangeRequest{cmd, evtype, evdata, status}:
|
||||||
|
inch = s.c
|
||||||
|
outch = nil
|
||||||
|
case c := <-changesFromHandler:
|
||||||
|
err := s.updateStatus(&c, &ec)
|
||||||
|
if err != nil {
|
||||||
|
// best suitable error number
|
||||||
|
ec.errno = sysErrSetServiceStatusFailed
|
||||||
|
if err2, ok := err.(syscall.Errno); ok {
|
||||||
|
ec.errno = uint32(err2)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
status = c
|
||||||
|
case ec = <-exitFromHandler:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.updateStatus(&Status{State: Stopped}, &ec)
|
||||||
|
s.cWaits.Set()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCallback(fn interface{}) (cb uintptr, err error) {
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cb = 0
|
||||||
|
switch v := r.(type) {
|
||||||
|
case string:
|
||||||
|
err = errors.New(v)
|
||||||
|
case error:
|
||||||
|
err = v
|
||||||
|
default:
|
||||||
|
err = errors.New("unexpected panic in syscall.NewCallback")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return syscall.NewCallback(fn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUG(brainman): There is no mechanism to run multiple services
|
||||||
|
// inside one single executable. Perhaps, it can be overcome by
|
||||||
|
// using RegisterServiceCtrlHandlerEx Windows api.
|
||||||
|
|
||||||
|
// Run executes service name by calling appropriate handler function.
|
||||||
|
func Run(name string, handler Handler) error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
tid := windows.GetCurrentThreadId()
|
||||||
|
|
||||||
|
s, err := newService(name, handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
|
||||||
|
// We assume that this callback function is running on
|
||||||
|
// the same thread as Run. Nowhere in MS documentation
|
||||||
|
// I could find statement to guarantee that. So putting
|
||||||
|
// check here to verify, otherwise things will go bad
|
||||||
|
// quickly, if ignored.
|
||||||
|
i := windows.GetCurrentThreadId()
|
||||||
|
if i != tid {
|
||||||
|
e.errno = sysErrNewThreadInCallback
|
||||||
|
}
|
||||||
|
s.c <- e
|
||||||
|
// Always return NO_ERROR (0) for now.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var svcmain uintptr
|
||||||
|
getServiceMain(&svcmain)
|
||||||
|
t := []windows.SERVICE_TABLE_ENTRY{
|
||||||
|
{syscall.StringToUTF16Ptr(s.name), svcmain},
|
||||||
|
{nil, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
goWaitsH = uintptr(s.goWaits.h)
|
||||||
|
cWaitsH = uintptr(s.cWaits.h)
|
||||||
|
sName = t[0].ServiceName
|
||||||
|
ctlHandlerExProc, err = newCallback(ctlHandler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.run()
|
||||||
|
|
||||||
|
err = windows.StartServiceCtrlDispatcher(&t[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusHandle returns service status handle. It is safe to call this function
|
||||||
|
// from inside the Handler.Execute because then it is guaranteed to be set.
|
||||||
|
// This code will have to change once multiple services are possible per process.
|
||||||
|
func StatusHandle() windows.Handle {
|
||||||
|
return windows.Handle(ssHandle)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// func servicemain(argc uint32, argv **uint16)
|
||||||
|
TEXT ·servicemain(SB),7,$0
|
||||||
|
MOVL argc+0(FP), AX
|
||||||
|
MOVL AX, ·sArgc(SB)
|
||||||
|
MOVL argv+4(FP), AX
|
||||||
|
MOVL AX, ·sArgv(SB)
|
||||||
|
|
||||||
|
PUSHL BP
|
||||||
|
PUSHL BX
|
||||||
|
PUSHL SI
|
||||||
|
PUSHL DI
|
||||||
|
|
||||||
|
SUBL $12, SP
|
||||||
|
|
||||||
|
MOVL ·sName(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL $·servicectlhandler(SB), AX
|
||||||
|
MOVL AX, 4(SP)
|
||||||
|
MOVL $0, 8(SP)
|
||||||
|
MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
CMPL AX, $0
|
||||||
|
JE exit
|
||||||
|
MOVL AX, ·ssHandle(SB)
|
||||||
|
|
||||||
|
MOVL ·goWaitsH(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL ·cSetEvent(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
|
||||||
|
MOVL ·cWaitsH(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL $-1, AX
|
||||||
|
MOVL AX, 4(SP)
|
||||||
|
MOVL ·cWaitForSingleObject(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ADDL $12, SP
|
||||||
|
|
||||||
|
POPL DI
|
||||||
|
POPL SI
|
||||||
|
POPL BX
|
||||||
|
POPL BP
|
||||||
|
|
||||||
|
MOVL 0(SP), CX
|
||||||
|
ADDL $12, SP
|
||||||
|
JMP CX
|
||||||
|
|
||||||
|
// I do not know why, but this seems to be the only way to call
|
||||||
|
// ctlHandlerProc on Windows 7.
|
||||||
|
|
||||||
|
// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
TEXT ·servicectlhandler(SB),7,$0
|
||||||
|
MOVL ·ctlHandlerExProc(SB), CX
|
||||||
|
JMP CX
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// func servicemain(argc uint32, argv **uint16)
|
||||||
|
TEXT ·servicemain(SB),7,$0
|
||||||
|
MOVL CX, ·sArgc(SB)
|
||||||
|
MOVL DX, ·sArgv(SB)
|
||||||
|
|
||||||
|
SUBQ $32, SP // stack for the first 4 syscall params
|
||||||
|
|
||||||
|
MOVQ ·sName(SB), CX
|
||||||
|
MOVQ $·servicectlhandler(SB), DX
|
||||||
|
// BUG(pastarmovj): Figure out a way to pass in context in R8.
|
||||||
|
MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||||
|
CALL AX
|
||||||
|
CMPQ AX, $0
|
||||||
|
JE exit
|
||||||
|
MOVQ AX, ·ssHandle(SB)
|
||||||
|
|
||||||
|
MOVQ ·goWaitsH(SB), CX
|
||||||
|
MOVQ ·cSetEvent(SB), AX
|
||||||
|
CALL AX
|
||||||
|
|
||||||
|
MOVQ ·cWaitsH(SB), CX
|
||||||
|
MOVQ $4294967295, DX
|
||||||
|
MOVQ ·cWaitForSingleObject(SB), AX
|
||||||
|
CALL AX
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ADDQ $32, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
// I do not know why, but this seems to be the only way to call
|
||||||
|
// ctlHandlerProc on Windows 7.
|
||||||
|
|
||||||
|
// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
TEXT ·servicectlhandler(SB),7,$0
|
||||||
|
MOVQ ·ctlHandlerExProc(SB), AX
|
||||||
|
JMP AX
|
||||||
Loading…
Reference in New Issue