mirror of https://github.com/docker/cli.git
Vendoring bump for containerd and licensing
Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>
This commit is contained in:
parent
3f7c6c8200
commit
11a312118f
17
vendor.conf
17
vendor.conf
|
@ -1,12 +1,16 @@
|
||||||
github.com/agl/ed25519 5312a61534124124185d41f09206b9fef1d88403
|
github.com/agl/ed25519 5312a61534124124185d41f09206b9fef1d88403
|
||||||
|
github.com/asaskevich/govalidator f9ffefc3facfbe0caee3fea233cbb6e8208f4541
|
||||||
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
|
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
|
||||||
github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceafb
|
github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceafb
|
||||||
github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761
|
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
|
||||||
github.com/containerd/containerd a88b6319614de846458750ff882723479ca7b1a1
|
github.com/containerd/containerd 0ffb948
|
||||||
github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371
|
github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371
|
||||||
|
github.com/containerd/fifo 3d5202a
|
||||||
|
github.com/containerd/typeurl f694355
|
||||||
github.com/coreos/etcd v3.3.9
|
github.com/coreos/etcd v3.3.9
|
||||||
github.com/cpuguy83/go-md2man v1.0.8
|
github.com/cpuguy83/go-md2man v1.0.8
|
||||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0
|
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0
|
||||||
|
github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af
|
||||||
github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5
|
github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5
|
||||||
github.com/docker/docker a7ff19d69a90dfe152abd146221c8b9b46a0903d
|
github.com/docker/docker a7ff19d69a90dfe152abd146221c8b9b46a0903d
|
||||||
github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962
|
github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962
|
||||||
|
@ -17,9 +21,12 @@ github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4
|
||||||
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
||||||
github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
|
github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
|
||||||
github.com/docker/go-units 47565b4f722fb6ceae66b95f853feed578a4a51c # v0.3.3
|
github.com/docker/go-units 47565b4f722fb6ceae66b95f853feed578a4a51c # v0.3.3
|
||||||
|
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||||
|
github.com/docker/licensing 369e530
|
||||||
github.com/docker/swarmkit edd5641391926a50bc5f7040e20b7efc05003c26
|
github.com/docker/swarmkit edd5641391926a50bc5f7040e20b7efc05003c26
|
||||||
github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff
|
github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff
|
||||||
github.com/ghodss/yaml 0ca9ea5df5451ffdf184b4428c902747c2c11cd7 # v1.0.0
|
github.com/ghodss/yaml 0ca9ea5df5451ffdf184b4428c902747c2c11cd7 # v1.0.0
|
||||||
|
github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef
|
||||||
github.com/gogo/protobuf v1.1.1
|
github.com/gogo/protobuf v1.1.1
|
||||||
github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998
|
github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998
|
||||||
github.com/golang/protobuf v1.1.0
|
github.com/golang/protobuf v1.1.0
|
||||||
|
@ -34,11 +41,13 @@ github.com/gregjones/httpcache 9cad4c3443a7200dd6400aef47183728de563a38
|
||||||
github.com/grpc-ecosystem/grpc-gateway 1a03ca3bad1e1ebadaedd3abb76bc58d4ac8143b
|
github.com/grpc-ecosystem/grpc-gateway 1a03ca3bad1e1ebadaedd3abb76bc58d4ac8143b
|
||||||
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
|
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
|
||||||
github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3
|
github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3
|
||||||
|
github.com/hashicorp/go-version 23480c0
|
||||||
github.com/imdario/mergo v0.3.6
|
github.com/imdario/mergo v0.3.6
|
||||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # v1.0
|
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # v1.0
|
||||||
github.com/json-iterator/go ab8a2e0c74be9d3be70b3184d9acc634935ded82 # 1.1.4
|
github.com/json-iterator/go ab8a2e0c74be9d3be70b3184d9acc634935ded82 # 1.1.4
|
||||||
github.com/mattn/go-shellwords v1.0.3
|
github.com/mattn/go-shellwords v1.0.3
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
||||||
|
github.com/Microsoft/hcsshim v0.6.11
|
||||||
github.com/Microsoft/go-winio v0.4.9
|
github.com/Microsoft/go-winio v0.4.9
|
||||||
github.com/miekg/pkcs11 287d9350987cc9334667882061e202e96cdfb4d0
|
github.com/miekg/pkcs11 287d9350987cc9334667882061e202e96cdfb4d0
|
||||||
github.com/mitchellh/mapstructure f15292f7a699fcc1a38a80977f80a046874ba8ac
|
github.com/mitchellh/mapstructure f15292f7a699fcc1a38a80977f80a046874ba8ac
|
||||||
|
@ -50,6 +59,7 @@ github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1
|
github.com/opencontainers/go-digest v1.0.0-rc1
|
||||||
github.com/opencontainers/image-spec v1.0.1
|
github.com/opencontainers/image-spec v1.0.1
|
||||||
github.com/opencontainers/runc ad0f5255060d36872be04de22f8731f38ef2d7b1
|
github.com/opencontainers/runc ad0f5255060d36872be04de22f8731f38ef2d7b1
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.1
|
||||||
github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
|
github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
|
||||||
github.com/peterbourgon/diskv 5f041e8faa004a95c88a202771f4cc3e991971e6 # v2.0.1
|
github.com/peterbourgon/diskv 5f041e8faa004a95c88a202771f4cc3e991971e6 # v2.0.1
|
||||||
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
||||||
|
@ -58,10 +68,12 @@ github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
||||||
github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8
|
github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8
|
||||||
github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
|
github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
|
||||||
github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
|
github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
|
||||||
|
github.com/satori/go.uuid d41af8bb6a7704f00bc3b7cba9355ae6a5a80048
|
||||||
github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77
|
github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77
|
||||||
github.com/sirupsen/logrus v1.0.6
|
github.com/sirupsen/logrus v1.0.6
|
||||||
github.com/spf13/cobra v0.0.3
|
github.com/spf13/cobra v0.0.3
|
||||||
github.com/spf13/pflag v1.0.1
|
github.com/spf13/pflag v1.0.1
|
||||||
|
github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
|
||||||
github.com/theupdateframework/notary v0.6.1
|
github.com/theupdateframework/notary v0.6.1
|
||||||
github.com/tonistiigi/fsutil b19464cd1b6a00773b4f2eb7acf9c30426f9df42
|
github.com/tonistiigi/fsutil b19464cd1b6a00773b4f2eb7acf9c30426f9df42
|
||||||
github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2
|
github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2
|
||||||
|
@ -85,4 +97,3 @@ k8s.io/client-go kubernetes-1.11.0
|
||||||
k8s.io/kube-openapi d8ea2fe547a448256204cfc68dfee7b26c720acb
|
k8s.io/kube-openapi d8ea2fe547a448256204cfc68dfee7b26c720acb
|
||||||
k8s.io/kubernetes v1.11.0
|
k8s.io/kubernetes v1.11.0
|
||||||
vbom.ml/util 256737ac55c46798123f754ab7d2c784e2c71783
|
vbom.ml/util 256737ac55c46798123f754ab7d2c784e2c71783
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,33 @@
|
||||||
|
# hcsshim
|
||||||
|
|
||||||
|
This package supports launching Windows Server containers from Go. It is
|
||||||
|
primarily used in the [Docker Engine](https://github.com/docker/docker) project,
|
||||||
|
but it can be freely used by other projects as well.
|
||||||
|
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
---------------
|
||||||
|
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||||
|
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||||
|
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||||
|
|
||||||
|
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
|
||||||
|
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
|
||||||
|
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
Security issues and bugs should be reported privately, via email, to the Microsoft Security
|
||||||
|
Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should
|
||||||
|
receive a response within 24 hours. If for some reason you do not, please follow up via
|
||||||
|
email to ensure we received your original message. Further information, including the
|
||||||
|
[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in
|
||||||
|
the [Security TechCenter](https://technet.microsoft.com/en-us/security/default).
|
||||||
|
|
||||||
|
-------------------------------------------
|
||||||
|
Copyright (c) 2018 Microsoft Corp. All rights reserved.
|
|
@ -0,0 +1,28 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// ActivateLayer will find the layer with the given id and mount it's filesystem.
|
||||||
|
// For a read/write layer, the mounted filesystem will appear as a volume on the
|
||||||
|
// host, while a read-only layer is generally expected to be a no-op.
|
||||||
|
// An activated layer must later be deactivated via DeactivateLayer.
|
||||||
|
func ActivateLayer(info DriverInfo, id string) error {
|
||||||
|
title := "hcsshim::ActivateLayer "
|
||||||
|
logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id)
|
||||||
|
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = activateLayer(&infop, id)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" - succeeded id=%s flavour=%d", id, info.Flavour)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
type baseLayerWriter struct {
|
||||||
|
root *os.File
|
||||||
|
f *os.File
|
||||||
|
bw *winio.BackupFileWriter
|
||||||
|
err error
|
||||||
|
hasUtilityVM bool
|
||||||
|
dirInfo []dirInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type dirInfo struct {
|
||||||
|
path string
|
||||||
|
fileInfo winio.FileBasicInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// reapplyDirectoryTimes reapplies directory modification, creation, etc. times
|
||||||
|
// after processing of the directory tree has completed. The times are expected
|
||||||
|
// to be ordered such that parent directories come before child directories.
|
||||||
|
func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error {
|
||||||
|
for i := range dis {
|
||||||
|
di := &dis[len(dis)-i-1] // reverse order: process child directories first
|
||||||
|
f, err := openRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_OPEN, _FILE_DIRECTORY_FILE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(f, &di.fileInfo)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) closeCurrentFile() error {
|
||||||
|
if w.f != nil {
|
||||||
|
err := w.bw.Close()
|
||||||
|
err2 := w.f.Close()
|
||||||
|
w.f = nil
|
||||||
|
w.bw = nil
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.err = err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = w.closeCurrentFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.ToSlash(name) == `UtilityVM/Files` {
|
||||||
|
w.hasUtilityVM = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var f *os.File
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
extraFlags := uint32(0)
|
||||||
|
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||||
|
extraFlags |= _FILE_DIRECTORY_FILE
|
||||||
|
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||||
|
w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
|
||||||
|
f, err = openRelative(name, w.root, mode, syscall.FILE_SHARE_READ, _FILE_CREATE, extraFlags)
|
||||||
|
if err != nil {
|
||||||
|
return makeError(err, "Failed to openRelative", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(f, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return makeError(err, "Failed to SetFileBasicInfo", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.f = f
|
||||||
|
w.bw = winio.NewBackupFileWriter(f, true)
|
||||||
|
f = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.err = err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = w.closeCurrentFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return linkRelative(target, w.root, name, w.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Remove(name string) error {
|
||||||
|
return errors.New("base layer cannot have tombstones")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Write(b []byte) (int, error) {
|
||||||
|
n, err := w.bw.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
w.err = err
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Close() error {
|
||||||
|
defer func() {
|
||||||
|
w.root.Close()
|
||||||
|
w.root = nil
|
||||||
|
}()
|
||||||
|
err := w.closeCurrentFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if w.err == nil {
|
||||||
|
// Restore the file times of all the directories, since they may have
|
||||||
|
// been modified by creating child directories.
|
||||||
|
err = reapplyDirectoryTimes(w.root, w.dirInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ProcessBaseLayer(w.root.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.hasUtilityVM {
|
||||||
|
err := ensureNotReparsePointRelative("UtilityVM", w.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w.err
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nextCallback uintptr
|
||||||
|
callbackMap = map[uintptr]*notifcationWatcherContext{}
|
||||||
|
callbackMapLock = sync.RWMutex{}
|
||||||
|
|
||||||
|
notificationWatcherCallback = syscall.NewCallback(notificationWatcher)
|
||||||
|
|
||||||
|
// Notifications for HCS_SYSTEM handles
|
||||||
|
hcsNotificationSystemExited hcsNotification = 0x00000001
|
||||||
|
hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002
|
||||||
|
hcsNotificationSystemStartCompleted hcsNotification = 0x00000003
|
||||||
|
hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004
|
||||||
|
hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005
|
||||||
|
|
||||||
|
// Notifications for HCS_PROCESS handles
|
||||||
|
hcsNotificationProcessExited hcsNotification = 0x00010000
|
||||||
|
|
||||||
|
// Common notifications
|
||||||
|
hcsNotificationInvalid hcsNotification = 0x00000000
|
||||||
|
hcsNotificationServiceDisconnect hcsNotification = 0x01000000
|
||||||
|
)
|
||||||
|
|
||||||
|
type hcsNotification uint32
|
||||||
|
type notificationChannel chan error
|
||||||
|
|
||||||
|
type notifcationWatcherContext struct {
|
||||||
|
channels notificationChannels
|
||||||
|
handle hcsCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
type notificationChannels map[hcsNotification]notificationChannel
|
||||||
|
|
||||||
|
func newChannels() notificationChannels {
|
||||||
|
channels := make(notificationChannels)
|
||||||
|
|
||||||
|
channels[hcsNotificationSystemExited] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemCreateCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemStartCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemPauseCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemResumeCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationProcessExited] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationServiceDisconnect] = make(notificationChannel, 1)
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
func closeChannels(channels notificationChannels) {
|
||||||
|
close(channels[hcsNotificationSystemExited])
|
||||||
|
close(channels[hcsNotificationSystemCreateCompleted])
|
||||||
|
close(channels[hcsNotificationSystemStartCompleted])
|
||||||
|
close(channels[hcsNotificationSystemPauseCompleted])
|
||||||
|
close(channels[hcsNotificationSystemResumeCompleted])
|
||||||
|
close(channels[hcsNotificationProcessExited])
|
||||||
|
close(channels[hcsNotificationServiceDisconnect])
|
||||||
|
}
|
||||||
|
|
||||||
|
func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr {
|
||||||
|
var result error
|
||||||
|
if int32(notificationStatus) < 0 {
|
||||||
|
result = syscall.Errno(win32FromHresult(notificationStatus))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
context := callbackMap[callbackNumber]
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
if context == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
context.channels[notificationType] <- result
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// This import is needed to make the library compile as CGO because HCSSHIM
|
||||||
|
// only works with CGO due to callbacks from HCS comming back from a C thread
|
||||||
|
// which is not supported without CGO. See https://github.com/golang/go/issues/10973
|
|
@ -0,0 +1,800 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultTimeout = time.Minute * 4
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}`
|
||||||
|
statisticsQuery = `{ "PropertyTypes" : ["Statistics"]}`
|
||||||
|
processListQuery = `{ "PropertyTypes" : ["ProcessList"]}`
|
||||||
|
mappedVirtualDiskQuery = `{ "PropertyTypes" : ["MappedVirtualDisk"]}`
|
||||||
|
)
|
||||||
|
|
||||||
|
type container struct {
|
||||||
|
handleLock sync.RWMutex
|
||||||
|
handle hcsSystem
|
||||||
|
id string
|
||||||
|
callbackNumber uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerProperties holds the properties for a container and the processes running in that container
|
||||||
|
type ContainerProperties struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
Name string
|
||||||
|
SystemType string
|
||||||
|
Owner string
|
||||||
|
SiloGUID string `json:"SiloGuid,omitempty"`
|
||||||
|
RuntimeID string `json:"RuntimeId,omitempty"`
|
||||||
|
IsRuntimeTemplate bool `json:",omitempty"`
|
||||||
|
RuntimeImagePath string `json:",omitempty"`
|
||||||
|
Stopped bool `json:",omitempty"`
|
||||||
|
ExitType string `json:",omitempty"`
|
||||||
|
AreUpdatesPending bool `json:",omitempty"`
|
||||||
|
ObRoot string `json:",omitempty"`
|
||||||
|
Statistics Statistics `json:",omitempty"`
|
||||||
|
ProcessList []ProcessListItem `json:",omitempty"`
|
||||||
|
MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryStats holds the memory statistics for a container
|
||||||
|
type MemoryStats struct {
|
||||||
|
UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"`
|
||||||
|
UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"`
|
||||||
|
UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessorStats holds the processor statistics for a container
|
||||||
|
type ProcessorStats struct {
|
||||||
|
TotalRuntime100ns uint64 `json:",omitempty"`
|
||||||
|
RuntimeUser100ns uint64 `json:",omitempty"`
|
||||||
|
RuntimeKernel100ns uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageStats holds the storage statistics for a container
|
||||||
|
type StorageStats struct {
|
||||||
|
ReadCountNormalized uint64 `json:",omitempty"`
|
||||||
|
ReadSizeBytes uint64 `json:",omitempty"`
|
||||||
|
WriteCountNormalized uint64 `json:",omitempty"`
|
||||||
|
WriteSizeBytes uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkStats holds the network statistics for a container
|
||||||
|
type NetworkStats struct {
|
||||||
|
BytesReceived uint64 `json:",omitempty"`
|
||||||
|
BytesSent uint64 `json:",omitempty"`
|
||||||
|
PacketsReceived uint64 `json:",omitempty"`
|
||||||
|
PacketsSent uint64 `json:",omitempty"`
|
||||||
|
DroppedPacketsIncoming uint64 `json:",omitempty"`
|
||||||
|
DroppedPacketsOutgoing uint64 `json:",omitempty"`
|
||||||
|
EndpointId string `json:",omitempty"`
|
||||||
|
InstanceId string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics is the structure returned by a statistics call on a container
|
||||||
|
type Statistics struct {
|
||||||
|
Timestamp time.Time `json:",omitempty"`
|
||||||
|
ContainerStartTime time.Time `json:",omitempty"`
|
||||||
|
Uptime100ns uint64 `json:",omitempty"`
|
||||||
|
Memory MemoryStats `json:",omitempty"`
|
||||||
|
Processor ProcessorStats `json:",omitempty"`
|
||||||
|
Storage StorageStats `json:",omitempty"`
|
||||||
|
Network []NetworkStats `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessList is the structure of an item returned by a ProcessList call on a container
|
||||||
|
type ProcessListItem struct {
|
||||||
|
CreateTimestamp time.Time `json:",omitempty"`
|
||||||
|
ImageName string `json:",omitempty"`
|
||||||
|
KernelTime100ns uint64 `json:",omitempty"`
|
||||||
|
MemoryCommitBytes uint64 `json:",omitempty"`
|
||||||
|
MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"`
|
||||||
|
MemoryWorkingSetSharedBytes uint64 `json:",omitempty"`
|
||||||
|
ProcessId uint32 `json:",omitempty"`
|
||||||
|
UserTime100ns uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container
|
||||||
|
type MappedVirtualDiskController struct {
|
||||||
|
MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of Request Support in ModifySystem
|
||||||
|
type RequestType string
|
||||||
|
|
||||||
|
// Type of Resource Support in ModifySystem
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
// RequestType const
|
||||||
|
const (
|
||||||
|
Add RequestType = "Add"
|
||||||
|
Remove RequestType = "Remove"
|
||||||
|
Network ResourceType = "Network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system
|
||||||
|
// Supported resource types are Network and Request Types are Add/Remove
|
||||||
|
type ResourceModificationRequestResponse struct {
|
||||||
|
Resource ResourceType `json:"ResourceType"`
|
||||||
|
Data interface{} `json:"Settings"`
|
||||||
|
Request RequestType `json:"RequestType,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// createContainerAdditionalJSON is read from the environment at initialisation
|
||||||
|
// time. It allows an environment variable to define additional JSON which
|
||||||
|
// is merged in the CreateContainer call to HCS.
|
||||||
|
var createContainerAdditionalJSON string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
createContainerAdditionalJSON = os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateContainer creates a new container with the given configuration but does not start it.
|
||||||
|
func CreateContainer(id string, c *ContainerConfig) (Container, error) {
|
||||||
|
return createContainerWithJSON(id, c, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateContainerWithJSON creates a new container with the given configuration but does not start it.
|
||||||
|
// It is identical to CreateContainer except that optional additional JSON can be merged before passing to HCS.
|
||||||
|
func CreateContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) {
|
||||||
|
return createContainerWithJSON(id, c, additionalJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) {
|
||||||
|
operation := "CreateContainer"
|
||||||
|
title := "HCSShim::" + operation
|
||||||
|
|
||||||
|
container := &container{
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
configurationb, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration := string(configurationb)
|
||||||
|
logrus.Debugf(title+" id=%s config=%s", id, configuration)
|
||||||
|
|
||||||
|
// Merge any additional JSON. Priority is given to what is passed in explicitly,
|
||||||
|
// falling back to what's set in the environment.
|
||||||
|
if additionalJSON == "" && createContainerAdditionalJSON != "" {
|
||||||
|
additionalJSON = createContainerAdditionalJSON
|
||||||
|
}
|
||||||
|
if additionalJSON != "" {
|
||||||
|
configurationMap := map[string]interface{}{}
|
||||||
|
if err := json.Unmarshal([]byte(configuration), &configurationMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal %s: %s", configuration, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalMap := map[string]interface{}{}
|
||||||
|
if err := json.Unmarshal([]byte(additionalJSON), &additionalMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal %s: %s", additionalJSON, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedMap := mergeMaps(additionalMap, configurationMap)
|
||||||
|
mergedJSON, err := json.Marshal(mergedMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal merged configuration map %+v: %s", mergedMap, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration = string(mergedJSON)
|
||||||
|
logrus.Debugf(title+" id=%s merged config=%s", id, configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
resultp *uint16
|
||||||
|
identity syscall.Handle
|
||||||
|
)
|
||||||
|
createError := hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp)
|
||||||
|
|
||||||
|
if createError == nil || IsPending(createError) {
|
||||||
|
if err := container.registerCallback(); err != nil {
|
||||||
|
// Terminate the container if it still exists. We're okay to ignore a failure here.
|
||||||
|
container.Terminate()
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrTimeout {
|
||||||
|
// Terminate the container if it still exists. We're okay to ignore a failure here.
|
||||||
|
container.Terminate()
|
||||||
|
}
|
||||||
|
return nil, makeContainerError(container, operation, configuration, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s handle=%d", id, container.handle)
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeMaps recursively merges map `fromMap` into map `ToMap`. Any pre-existing values
|
||||||
|
// in ToMap are overwritten. Values in fromMap are added to ToMap.
|
||||||
|
// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang
|
||||||
|
func mergeMaps(fromMap, ToMap interface{}) interface{} {
|
||||||
|
switch fromMap := fromMap.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
ToMap, ok := ToMap.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fromMap
|
||||||
|
}
|
||||||
|
for keyToMap, valueToMap := range ToMap {
|
||||||
|
if valueFromMap, ok := fromMap[keyToMap]; ok {
|
||||||
|
fromMap[keyToMap] = mergeMaps(valueFromMap, valueToMap)
|
||||||
|
} else {
|
||||||
|
fromMap[keyToMap] = valueToMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
// merge(nil, map[string]interface{...}) -> map[string]interface{...}
|
||||||
|
ToMap, ok := ToMap.(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
return ToMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fromMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenContainer opens an existing container by ID.
|
||||||
|
func OpenContainer(id string) (Container, error) {
|
||||||
|
operation := "OpenContainer"
|
||||||
|
title := "HCSShim::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", id)
|
||||||
|
|
||||||
|
container := &container{
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
handle hcsSystem
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
err := hcsOpenComputeSystem(id, &handle, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container.handle = handle
|
||||||
|
|
||||||
|
if err := container.registerCallback(); err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle)
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainers gets a list of the containers on the system that match the query
|
||||||
|
func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) {
|
||||||
|
operation := "GetContainers"
|
||||||
|
title := "HCSShim::" + operation
|
||||||
|
|
||||||
|
queryb, err := json.Marshal(q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
query := string(queryb)
|
||||||
|
logrus.Debugf(title+" query=%s", query)
|
||||||
|
|
||||||
|
var (
|
||||||
|
resultp *uint16
|
||||||
|
computeSystemsp *uint16
|
||||||
|
)
|
||||||
|
err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if computeSystemsp == nil {
|
||||||
|
return nil, ErrUnexpectedValue
|
||||||
|
}
|
||||||
|
computeSystemsRaw := convertAndFreeCoTaskMemBytes(computeSystemsp)
|
||||||
|
computeSystems := []ContainerProperties{}
|
||||||
|
if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return computeSystems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start synchronously starts the container.
|
||||||
|
func (container *container) Start() error {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "Start"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err := hcsStartComputeSystem(container.handle, "", &resultp)
|
||||||
|
err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemStartCompleted, &defaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown requests a container shutdown, if IsPending() on the error returned is true,
|
||||||
|
// it may not actually be shut down until Wait() succeeds.
|
||||||
|
func (container *container) Shutdown() error {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "Shutdown"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err := hcsShutdownComputeSystem(container.handle, "", &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate requests a container terminate, if IsPending() on the error returned is true,
|
||||||
|
// it may not actually be shut down until Wait() succeeds.
|
||||||
|
func (container *container) Terminate() error {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "Terminate"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err := hcsTerminateComputeSystem(container.handle, "", &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait synchronously waits for the container to shutdown or terminate.
|
||||||
|
func (container *container) Wait() error {
|
||||||
|
operation := "Wait"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, nil)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse.
|
||||||
|
// If the timeout expires, IsTimeout(err) == true
|
||||||
|
func (container *container) WaitTimeout(timeout time.Duration) error {
|
||||||
|
operation := "WaitTimeout"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, &timeout)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *container) properties(query string) (*ContainerProperties, error) {
|
||||||
|
var (
|
||||||
|
resultp *uint16
|
||||||
|
propertiesp *uint16
|
||||||
|
)
|
||||||
|
err := hcsGetComputeSystemProperties(container.handle, query, &propertiesp, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if propertiesp == nil {
|
||||||
|
return nil, ErrUnexpectedValue
|
||||||
|
}
|
||||||
|
propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
|
||||||
|
properties := &ContainerProperties{}
|
||||||
|
if err := json.Unmarshal(propertiesRaw, properties); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return properties, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPendingUpdates returns true if the container has updates pending to install
|
||||||
|
func (container *container) HasPendingUpdates() (bool, error) {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "HasPendingUpdates"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return false, makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties, err := container.properties(pendingUpdatesQuery)
|
||||||
|
if err != nil {
|
||||||
|
return false, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return properties.AreUpdatesPending, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics returns statistics for the container
|
||||||
|
func (container *container) Statistics() (Statistics, error) {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "Statistics"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return Statistics{}, makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties, err := container.properties(statisticsQuery)
|
||||||
|
if err != nil {
|
||||||
|
return Statistics{}, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return properties.Statistics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessList returns an array of ProcessListItems for the container
|
||||||
|
func (container *container) ProcessList() ([]ProcessListItem, error) {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "ProcessList"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties, err := container.properties(processListQuery)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return properties.ProcessList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MappedVirtualDisks returns a map of the controllers and the disks mapped
|
||||||
|
// to a container.
|
||||||
|
//
|
||||||
|
// Example of JSON returned by the query.
|
||||||
|
//{
|
||||||
|
// "Id":"1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3_svm",
|
||||||
|
// "SystemType":"Container",
|
||||||
|
// "RuntimeOsType":"Linux",
|
||||||
|
// "RuntimeId":"00000000-0000-0000-0000-000000000000",
|
||||||
|
// "State":"Running",
|
||||||
|
// "MappedVirtualDiskControllers":{
|
||||||
|
// "0":{
|
||||||
|
// "MappedVirtualDisks":{
|
||||||
|
// "2":{
|
||||||
|
// "HostPath":"C:\\lcow\\lcow\\scratch\\1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3.vhdx",
|
||||||
|
// "ContainerPath":"/mnt/gcs/LinuxServiceVM/scratch",
|
||||||
|
// "Lun":2,
|
||||||
|
// "CreateInUtilityVM":true
|
||||||
|
// },
|
||||||
|
// "3":{
|
||||||
|
// "HostPath":"C:\\lcow\\lcow\\1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3\\sandbox.vhdx",
|
||||||
|
// "Lun":3,
|
||||||
|
// "CreateInUtilityVM":true,
|
||||||
|
// "AttachOnly":true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "MappedVirtualDiskList"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties, err := container.properties(mappedVirtualDiskQuery)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return properties.MappedVirtualDiskControllers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause pauses the execution of the container. This feature is not enabled in TP5.
|
||||||
|
func (container *container) Pause() error {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "Pause"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err := hcsPauseComputeSystem(container.handle, "", &resultp)
|
||||||
|
err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemPauseCompleted, &defaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume resumes the execution of the container. This feature is not enabled in TP5.
|
||||||
|
func (container *container) Resume() error {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "Resume"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err := hcsResumeComputeSystem(container.handle, "", &resultp)
|
||||||
|
err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemResumeCompleted, &defaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProcess launches a new process within the container.
|
||||||
|
func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "CreateProcess"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
var (
|
||||||
|
processInfo hcsProcessInformation
|
||||||
|
processHandle hcsProcess
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are not emulating a console, ignore any console size passed to us
|
||||||
|
if !c.EmulateConsole {
|
||||||
|
c.ConsoleSize[0] = 0
|
||||||
|
c.ConsoleSize[1] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configurationb, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration := string(configurationb)
|
||||||
|
logrus.Debugf(title+" id=%s config=%s", container.id, configuration)
|
||||||
|
|
||||||
|
err = hcsCreateProcess(container.handle, configuration, &processInfo, &processHandle, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, configuration, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
process := &process{
|
||||||
|
handle: processHandle,
|
||||||
|
processID: int(processInfo.ProcessId),
|
||||||
|
container: container,
|
||||||
|
cachedPipes: &cachedPipes{
|
||||||
|
stdIn: processInfo.StdInput,
|
||||||
|
stdOut: processInfo.StdOutput,
|
||||||
|
stdErr: processInfo.StdError,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := process.registerCallback(); err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s processid=%d", container.id, process.processID)
|
||||||
|
return process, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenProcess gets an interface to an existing process within the container.
|
||||||
|
func (container *container) OpenProcess(pid int) (Process, error) {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "OpenProcess"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, processid=%d", container.id, pid)
|
||||||
|
var (
|
||||||
|
processHandle hcsProcess
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := hcsOpenProcess(container.handle, uint32(pid), &processHandle, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
process := &process{
|
||||||
|
handle: processHandle,
|
||||||
|
processID: pid,
|
||||||
|
container: container,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := process.registerCallback(); err != nil {
|
||||||
|
return nil, makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
|
||||||
|
return process, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the container but does not terminate or wait for it.
|
||||||
|
func (container *container) Close() error {
|
||||||
|
container.handleLock.Lock()
|
||||||
|
defer container.handleLock.Unlock()
|
||||||
|
operation := "Close"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", container.id)
|
||||||
|
|
||||||
|
// Don't double free this
|
||||||
|
if container.handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := container.unregisterCallback(); err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hcsCloseComputeSystem(container.handle); err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container.handle = 0
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *container) registerCallback() error {
|
||||||
|
context := ¬ifcationWatcherContext{
|
||||||
|
channels: newChannels(),
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackNumber := nextCallback
|
||||||
|
nextCallback++
|
||||||
|
callbackMap[callbackNumber] = context
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
var callbackHandle hcsCallback
|
||||||
|
err := hcsRegisterComputeSystemCallback(container.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
context.handle = callbackHandle
|
||||||
|
container.callbackNumber = callbackNumber
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *container) unregisterCallback() error {
|
||||||
|
callbackNumber := container.callbackNumber
|
||||||
|
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
context := callbackMap[callbackNumber]
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
if context == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
handle := context.handle
|
||||||
|
|
||||||
|
if handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hcsUnregisterComputeSystemCallback has its own syncronization
|
||||||
|
// to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
|
||||||
|
err := hcsUnregisterComputeSystemCallback(handle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
closeChannels(context.channels)
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackMap[callbackNumber] = nil
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
handle = 0
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifies the System by sending a request to HCS
|
||||||
|
func (container *container) Modify(config *ResourceModificationRequestResponse) error {
|
||||||
|
container.handleLock.RLock()
|
||||||
|
defer container.handleLock.RUnlock()
|
||||||
|
operation := "Modify"
|
||||||
|
title := "HCSShim::Container::" + operation
|
||||||
|
|
||||||
|
if container.handle == 0 {
|
||||||
|
return makeContainerError(container, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestJSON, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
requestString := string(requestJSON)
|
||||||
|
logrus.Debugf(title+" id=%s request=%s", container.id, requestString)
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err = hcsModifyComputeSystem(container.handle, requestString, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeContainerError(container, operation, "", err)
|
||||||
|
}
|
||||||
|
logrus.Debugf(title+" succeeded id=%s", container.id)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// CreateLayer creates a new, empty, read-only layer on the filesystem based on
|
||||||
|
// the parent layer provided.
|
||||||
|
func CreateLayer(info DriverInfo, id, parent string) error {
|
||||||
|
title := "hcsshim::CreateLayer "
|
||||||
|
logrus.Debugf(title+"Flavour %d ID %s parent %s", info.Flavour, id, parent)
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createLayer(&infop, id, parent)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "id=%s parent=%s flavour=%d", id, parent, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" - succeeded id=%s parent=%s flavour=%d", id, parent, info.Flavour)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// CreateSandboxLayer creates and populates new read-write layer for use by a container.
|
||||||
|
// This requires both the id of the direct parent layer, as well as the full list
|
||||||
|
// of paths to all parent layers up to the base (and including the direct parent
|
||||||
|
// whose id was provided).
|
||||||
|
func CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::CreateSandboxLayer "
|
||||||
|
logrus.Debugf(title+"layerId %s parentId %s", layerId, parentId)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createSandboxLayer(&infop, layerId, parentId, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "layerId=%s parentId=%s", layerId, parentId)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"- succeeded layerId=%s parentId=%s", layerId, parentId)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// DeactivateLayer will dismount a layer that was mounted via ActivateLayer.
|
||||||
|
func DeactivateLayer(info DriverInfo, id string) error {
|
||||||
|
title := "hcsshim::DeactivateLayer "
|
||||||
|
logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id)
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = deactivateLayer(&infop, id)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded flavour=%d id=%s", info.Flavour, id)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// DestroyLayer will remove the on-disk files representing the layer with the given
|
||||||
|
// id, including that layer's containing folder, if any.
|
||||||
|
func DestroyLayer(info DriverInfo, id string) error {
|
||||||
|
title := "hcsshim::DestroyLayer "
|
||||||
|
logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id)
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = destroyLayer(&infop, id)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded flavour=%d id=%s", info.Flavour, id)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,261 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists
|
||||||
|
ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e)
|
||||||
|
|
||||||
|
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||||
|
ErrElementNotFound = syscall.Errno(0x490)
|
||||||
|
|
||||||
|
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||||
|
ErrNotSupported = syscall.Errno(0x32)
|
||||||
|
|
||||||
|
// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
|
||||||
|
// decimal -2147024883 / hex 0x8007000d
|
||||||
|
ErrInvalidData = syscall.Errno(0xd)
|
||||||
|
|
||||||
|
// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
|
||||||
|
ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")
|
||||||
|
|
||||||
|
// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
|
||||||
|
ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed")
|
||||||
|
|
||||||
|
// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
|
||||||
|
ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type")
|
||||||
|
|
||||||
|
// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
|
||||||
|
ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")
|
||||||
|
|
||||||
|
// ErrTimeout is an error encountered when waiting on a notification times out
|
||||||
|
ErrTimeout = errors.New("hcsshim: timeout waiting for notification")
|
||||||
|
|
||||||
|
// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
|
||||||
|
// a different expected notification
|
||||||
|
ErrUnexpectedContainerExit = errors.New("unexpected container exit")
|
||||||
|
|
||||||
|
// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
|
||||||
|
// is lost while waiting for a notification
|
||||||
|
ErrUnexpectedProcessAbort = errors.New("lost communication with compute service")
|
||||||
|
|
||||||
|
// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
|
||||||
|
ErrUnexpectedValue = errors.New("unexpected value returned from hcs")
|
||||||
|
|
||||||
|
// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
|
||||||
|
ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110)
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
|
||||||
|
ErrVmcomputeOperationPending = syscall.Errno(0xC0370103)
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
|
||||||
|
ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105)
|
||||||
|
|
||||||
|
// ErrProcNotFound is an error encountered when the the process cannot be found
|
||||||
|
ErrProcNotFound = syscall.Errno(0x7f)
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
|
||||||
|
// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
|
||||||
|
ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5)
|
||||||
|
|
||||||
|
// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
|
||||||
|
ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d)
|
||||||
|
|
||||||
|
// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
|
||||||
|
ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b)
|
||||||
|
|
||||||
|
// ErrNotSupported is an error encountered when hcs doesn't support the request
|
||||||
|
ErrPlatformNotSupported = errors.New("unsupported platform request")
|
||||||
|
)
|
||||||
|
|
||||||
|
type EndpointNotFoundError struct {
|
||||||
|
EndpointName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e EndpointNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("Endpoint %s not found", e.EndpointName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkNotFoundError struct {
|
||||||
|
NetworkName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e NetworkNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("Network %s not found", e.NetworkName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessError is an error encountered in HCS during an operation on a Process object
|
||||||
|
type ProcessError struct {
|
||||||
|
Process *process
|
||||||
|
Operation string
|
||||||
|
ExtraInfo string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerError is an error encountered in HCS during an operation on a Container object
|
||||||
|
type ContainerError struct {
|
||||||
|
Container *container
|
||||||
|
Operation string
|
||||||
|
ExtraInfo string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ContainerError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Container == nil {
|
||||||
|
return "unexpected nil container for error: " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
s := "container " + e.Container.id
|
||||||
|
|
||||||
|
if e.Operation != "" {
|
||||||
|
s += " encountered an error during " + e.Operation
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Err.(type) {
|
||||||
|
case nil:
|
||||||
|
break
|
||||||
|
case syscall.Errno:
|
||||||
|
s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err))
|
||||||
|
default:
|
||||||
|
s += fmt.Sprintf(": %s", e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.ExtraInfo != "" {
|
||||||
|
s += " extra info: " + e.ExtraInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeContainerError(container *container, operation string, extraInfo string, err error) error {
|
||||||
|
// Don't double wrap errors
|
||||||
|
if _, ok := err.(*ContainerError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err}
|
||||||
|
return containerError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ProcessError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Process == nil {
|
||||||
|
return "Unexpected nil process for error: " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
s := fmt.Sprintf("process %d", e.Process.processID)
|
||||||
|
|
||||||
|
if e.Process.container != nil {
|
||||||
|
s += " in container " + e.Process.container.id
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Operation != "" {
|
||||||
|
s += " encountered an error during " + e.Operation
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Err.(type) {
|
||||||
|
case nil:
|
||||||
|
break
|
||||||
|
case syscall.Errno:
|
||||||
|
s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err))
|
||||||
|
default:
|
||||||
|
s += fmt.Sprintf(": %s", e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeProcessError(process *process, operation string, extraInfo string, err error) error {
|
||||||
|
// Don't double wrap errors
|
||||||
|
if _, ok := err.(*ProcessError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err}
|
||||||
|
return processError
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist checks if an error is caused by the Container or Process not existing.
|
||||||
|
// Note: Currently, ErrElementNotFound can mean that a Process has either
|
||||||
|
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
|
||||||
|
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
|
||||||
|
func IsNotExist(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
if _, ok := err.(EndpointNotFoundError); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := err.(NetworkNotFoundError); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return err == ErrComputeSystemDoesNotExist ||
|
||||||
|
err == ErrElementNotFound ||
|
||||||
|
err == ErrProcNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlreadyClosed checks if an error is caused by the Container or Process having been
|
||||||
|
// already closed by a call to the Close() method.
|
||||||
|
func IsAlreadyClosed(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrAlreadyClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPending returns a boolean indicating whether the error is that
|
||||||
|
// the requested operation is being completed in the background.
|
||||||
|
func IsPending(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrVmcomputeOperationPending
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTimeout returns a boolean indicating whether the error is caused by
|
||||||
|
// a timeout waiting for the operation to complete.
|
||||||
|
func IsTimeout(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlreadyStopped returns a boolean indicating whether the error is caused by
|
||||||
|
// a Container or Process being already stopped.
|
||||||
|
// Note: Currently, ErrElementNotFound can mean that a Process has either
|
||||||
|
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
|
||||||
|
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
|
||||||
|
func IsAlreadyStopped(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrVmcomputeAlreadyStopped ||
|
||||||
|
err == ErrElementNotFound ||
|
||||||
|
err == ErrProcNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotSupported returns a boolean indicating whether the error is caused by
|
||||||
|
// unsupported platform requests
|
||||||
|
// Note: Currently Unsupported platform requests can be mean either
|
||||||
|
// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
|
||||||
|
// is thrown from the Platform
|
||||||
|
func IsNotSupported(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
// If Platform doesn't recognize or support the request sent, below errors are seen
|
||||||
|
return err == ErrVmcomputeInvalidJSON ||
|
||||||
|
err == ErrInvalidData ||
|
||||||
|
err == ErrNotSupported ||
|
||||||
|
err == ErrVmcomputeUnknownMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInnerError(err error) error {
|
||||||
|
switch pe := err.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case *ContainerError:
|
||||||
|
err = pe.Err
|
||||||
|
case *ProcessError:
|
||||||
|
err = pe.Err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// ExpandSandboxSize expands the size of a layer to at least size bytes.
|
||||||
|
func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error {
|
||||||
|
title := "hcsshim::ExpandSandboxSize "
|
||||||
|
logrus.Debugf(title+"layerId=%s size=%d", layerId, size)
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expandSandboxSize(&infop, layerId, size)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "layerId=%s size=%d", layerId, size)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"- succeeded layerId=%s size=%d", layerId, size)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExportLayer will create a folder at exportFolderPath and fill that folder with
|
||||||
|
// the transport format version of the layer identified by layerId. This transport
|
||||||
|
// format includes any metadata required for later importing the layer (using
|
||||||
|
// ImportLayer), and requires the full list of parent layer paths in order to
|
||||||
|
// perform the export.
|
||||||
|
func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::ExportLayer "
|
||||||
|
logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerId, exportFolderPath)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = exportLayer(&infop, layerId, exportFolderPath, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerId, info.Flavour, exportFolderPath)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, exportFolderPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LayerReader interface {
|
||||||
|
Next() (string, int64, *winio.FileBasicInfo, error)
|
||||||
|
Read(b []byte) (int, error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterLayerReader provides an interface for extracting the contents of an on-disk layer.
|
||||||
|
type FilterLayerReader struct {
|
||||||
|
context uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next reads the next available file from a layer, ensuring that parent directories are always read
|
||||||
|
// before child files and directories.
|
||||||
|
//
|
||||||
|
// Next returns the file's relative path, size, and basic file metadata. Read() should be used to
|
||||||
|
// extract a Win32 backup stream with the remainder of the metadata and the data.
|
||||||
|
func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) {
|
||||||
|
var fileNamep *uint16
|
||||||
|
fileInfo := &winio.FileBasicInfo{}
|
||||||
|
var deleted uint32
|
||||||
|
var fileSize int64
|
||||||
|
err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted)
|
||||||
|
if err != nil {
|
||||||
|
if err == syscall.ERROR_NO_MORE_FILES {
|
||||||
|
err = io.EOF
|
||||||
|
} else {
|
||||||
|
err = makeError(err, "ExportLayerNext", "")
|
||||||
|
}
|
||||||
|
return "", 0, nil, err
|
||||||
|
}
|
||||||
|
fileName := convertAndFreeCoTaskMemString(fileNamep)
|
||||||
|
if deleted != 0 {
|
||||||
|
fileInfo = nil
|
||||||
|
}
|
||||||
|
if fileName[0] == '\\' {
|
||||||
|
fileName = fileName[1:]
|
||||||
|
}
|
||||||
|
return fileName, fileSize, fileInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the current file's Win32 backup stream.
|
||||||
|
func (r *FilterLayerReader) Read(b []byte) (int, error) {
|
||||||
|
var bytesRead uint32
|
||||||
|
err := exportLayerRead(r.context, b, &bytesRead)
|
||||||
|
if err != nil {
|
||||||
|
return 0, makeError(err, "ExportLayerRead", "")
|
||||||
|
}
|
||||||
|
if bytesRead == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return int(bytesRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees resources associated with the layer reader. It will return an
|
||||||
|
// error if there was an error while reading the layer or of the layer was not
|
||||||
|
// completely read.
|
||||||
|
func (r *FilterLayerReader) Close() (err error) {
|
||||||
|
if r.context != 0 {
|
||||||
|
err = exportLayerEnd(r.context)
|
||||||
|
if err != nil {
|
||||||
|
err = makeError(err, "ExportLayerEnd", "")
|
||||||
|
}
|
||||||
|
r.context = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer.
|
||||||
|
// The caller must have taken the SeBackupPrivilege privilege
|
||||||
|
// to call this and any methods on the resulting LayerReader.
|
||||||
|
func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) {
|
||||||
|
if procExportLayerBegin.Find() != nil {
|
||||||
|
// The new layer reader is not available on this Windows build. Fall back to the
|
||||||
|
// legacy export code path.
|
||||||
|
path, err := ioutil.TempDir("", "hcs")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ExportLayer(info, layerID, path, parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
os.RemoveAll(path)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &legacyLayerReaderWrapper{newLegacyLayerReader(path)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := &FilterLayerReader{}
|
||||||
|
err = exportLayerBegin(&infop, layerID, layers, &r.context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeError(err, "ExportLayerBegin", "")
|
||||||
|
}
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerReaderWrapper struct {
|
||||||
|
*legacyLayerReader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReaderWrapper) Close() error {
|
||||||
|
err := r.legacyLayerReader.Close()
|
||||||
|
os.RemoveAll(r.root)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetLayerMountPath will look for a mounted layer with the given id and return
|
||||||
|
// the path at which that layer can be accessed. This path may be a volume path
|
||||||
|
// if the layer is a mounted read-write layer, otherwise it is expected to be the
|
||||||
|
// folder path at which the layer is stored.
|
||||||
|
func GetLayerMountPath(info DriverInfo, id string) (string, error) {
|
||||||
|
title := "hcsshim::GetLayerMountPath "
|
||||||
|
logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id)
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mountPathLength uintptr
|
||||||
|
mountPathLength = 0
|
||||||
|
|
||||||
|
// Call the procedure itself.
|
||||||
|
logrus.Debugf("Calling proc (1)")
|
||||||
|
err = getLayerMountPath(&infop, id, &mountPathLength, nil)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "(first call) id=%s flavour=%d", id, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a mount path of the returned length.
|
||||||
|
if mountPathLength == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
mountPathp := make([]uint16, mountPathLength)
|
||||||
|
mountPathp[0] = 0
|
||||||
|
|
||||||
|
// Call the procedure again
|
||||||
|
logrus.Debugf("Calling proc (2)")
|
||||||
|
err = getLayerMountPath(&infop, id, &mountPathLength, &mountPathp[0])
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "(second call) id=%s flavour=%d", id, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
path := syscall.UTF16ToString(mountPathp[0:])
|
||||||
|
logrus.Debugf(title+"succeeded flavour=%d id=%s path=%s", info.Flavour, id, path)
|
||||||
|
return path, nil
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// GetSharedBaseImages will enumerate the images stored in the common central
|
||||||
|
// image store and return descriptive info about those images for the purpose
|
||||||
|
// of registering them with the graphdriver, graph, and tagstore.
|
||||||
|
func GetSharedBaseImages() (imageData string, err error) {
|
||||||
|
title := "hcsshim::GetSharedBaseImages "
|
||||||
|
|
||||||
|
logrus.Debugf("Calling proc")
|
||||||
|
var buffer *uint16
|
||||||
|
err = getBaseImages(&buffer)
|
||||||
|
if err != nil {
|
||||||
|
err = makeError(err, title, "")
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
imageData = convertAndFreeCoTaskMemString(buffer)
|
||||||
|
logrus.Debugf(title+" - succeeded output=%s", imageData)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GUID [16]byte
|
||||||
|
|
||||||
|
func NewGUID(source string) *GUID {
|
||||||
|
h := sha1.Sum([]byte(source))
|
||||||
|
var g GUID
|
||||||
|
copy(g[0:], h[0:16])
|
||||||
|
return &g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GUID) ToString() string {
|
||||||
|
return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:])
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
// Shim for the Host Compute Service (HCS) to manage Windows Server
|
||||||
|
// containers and Hyper-V containers.
|
||||||
|
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go safeopen.go
|
||||||
|
|
||||||
|
//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree
|
||||||
|
//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId
|
||||||
|
|
||||||
|
//sys activateLayer(info *driverInfo, id string) (hr error) = vmcompute.ActivateLayer?
|
||||||
|
//sys copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CopyLayer?
|
||||||
|
//sys createLayer(info *driverInfo, id string, parent string) (hr error) = vmcompute.CreateLayer?
|
||||||
|
//sys createSandboxLayer(info *driverInfo, id string, parent string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CreateSandboxLayer?
|
||||||
|
//sys expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) = vmcompute.ExpandSandboxSize?
|
||||||
|
//sys deactivateLayer(info *driverInfo, id string) (hr error) = vmcompute.DeactivateLayer?
|
||||||
|
//sys destroyLayer(info *driverInfo, id string) (hr error) = vmcompute.DestroyLayer?
|
||||||
|
//sys exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ExportLayer?
|
||||||
|
//sys getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) = vmcompute.GetLayerMountPath?
|
||||||
|
//sys getBaseImages(buffer **uint16) (hr error) = vmcompute.GetBaseImages?
|
||||||
|
//sys importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ImportLayer?
|
||||||
|
//sys layerExists(info *driverInfo, id string, exists *uint32) (hr error) = vmcompute.LayerExists?
|
||||||
|
//sys nameToGuid(name string, guid *GUID) (hr error) = vmcompute.NameToGuid?
|
||||||
|
//sys prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.PrepareLayer?
|
||||||
|
//sys unprepareLayer(info *driverInfo, id string) (hr error) = vmcompute.UnprepareLayer?
|
||||||
|
//sys processBaseImage(path string) (hr error) = vmcompute.ProcessBaseImage?
|
||||||
|
//sys processUtilityImage(path string) (hr error) = vmcompute.ProcessUtilityImage?
|
||||||
|
|
||||||
|
//sys importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ImportLayerBegin?
|
||||||
|
//sys importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) = vmcompute.ImportLayerNext?
|
||||||
|
//sys importLayerWrite(context uintptr, buffer []byte) (hr error) = vmcompute.ImportLayerWrite?
|
||||||
|
//sys importLayerEnd(context uintptr) (hr error) = vmcompute.ImportLayerEnd?
|
||||||
|
|
||||||
|
//sys exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ExportLayerBegin?
|
||||||
|
//sys exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) = vmcompute.ExportLayerNext?
|
||||||
|
//sys exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) = vmcompute.ExportLayerRead?
|
||||||
|
//sys exportLayerEnd(context uintptr) (hr error) = vmcompute.ExportLayerEnd?
|
||||||
|
|
||||||
|
//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems?
|
||||||
|
//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem?
|
||||||
|
//sys hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem?
|
||||||
|
//sys hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem?
|
||||||
|
//sys hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem?
|
||||||
|
//sys hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem?
|
||||||
|
//sys hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem?
|
||||||
|
//sys hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem?
|
||||||
|
//sys hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem?
|
||||||
|
//sys hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties?
|
||||||
|
//sys hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem?
|
||||||
|
//sys hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback?
|
||||||
|
//sys hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback?
|
||||||
|
|
||||||
|
//sys hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess?
|
||||||
|
//sys hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess?
|
||||||
|
//sys hcsCloseProcess(process hcsProcess) (hr error) = vmcompute.HcsCloseProcess?
|
||||||
|
//sys hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess?
|
||||||
|
//sys hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo?
|
||||||
|
//sys hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties?
|
||||||
|
//sys hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess?
|
||||||
|
//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties?
|
||||||
|
//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback?
|
||||||
|
//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback?
|
||||||
|
|
||||||
|
//sys hcsModifyServiceSettings(settings string, result **uint16) (hr error) = vmcompute.HcsModifyServiceSettings?
|
||||||
|
|
||||||
|
//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall?
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Specific user-visible exit codes
|
||||||
|
WaitErrExecFailed = 32767
|
||||||
|
|
||||||
|
ERROR_GEN_FAILURE = syscall.Errno(31)
|
||||||
|
ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115)
|
||||||
|
WSAEINVAL = syscall.Errno(10022)
|
||||||
|
|
||||||
|
// Timeout on wait calls
|
||||||
|
TimeoutInfinite = 0xFFFFFFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
type HcsError struct {
|
||||||
|
title string
|
||||||
|
rest string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type hcsSystem syscall.Handle
|
||||||
|
type hcsProcess syscall.Handle
|
||||||
|
type hcsCallback syscall.Handle
|
||||||
|
|
||||||
|
type hcsProcessInformation struct {
|
||||||
|
ProcessId uint32
|
||||||
|
Reserved uint32
|
||||||
|
StdInput syscall.Handle
|
||||||
|
StdOutput syscall.Handle
|
||||||
|
StdError syscall.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeError(err error, title, rest string) error {
|
||||||
|
// Pass through DLL errors directly since they do not originate from HCS.
|
||||||
|
if _, ok := err.(*syscall.DLLError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &HcsError{title, rest, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeErrorf(err error, title, format string, a ...interface{}) error {
|
||||||
|
return makeError(err, title, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func win32FromError(err error) uint32 {
|
||||||
|
if herr, ok := err.(*HcsError); ok {
|
||||||
|
return win32FromError(herr.Err)
|
||||||
|
}
|
||||||
|
if code, ok := err.(syscall.Errno); ok {
|
||||||
|
return uint32(code)
|
||||||
|
}
|
||||||
|
return uint32(ERROR_GEN_FAILURE)
|
||||||
|
}
|
||||||
|
|
||||||
|
func win32FromHresult(hr uintptr) uintptr {
|
||||||
|
if hr&0x1fff0000 == 0x00070000 {
|
||||||
|
return hr & 0xffff
|
||||||
|
}
|
||||||
|
return hr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HcsError) Error() string {
|
||||||
|
s := e.title
|
||||||
|
if len(s) > 0 && s[len(s)-1] != ' ' {
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err))
|
||||||
|
if e.rest != "" {
|
||||||
|
if e.rest[0] != ' ' {
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
s += e.rest
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertAndFreeCoTaskMemString(buffer *uint16) string {
|
||||||
|
str := syscall.UTF16ToString((*[1 << 30]uint16)(unsafe.Pointer(buffer))[:])
|
||||||
|
coTaskMemFree(unsafe.Pointer(buffer))
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertAndFreeCoTaskMemBytes(buffer *uint16) []byte {
|
||||||
|
return []byte(convertAndFreeCoTaskMemString(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
func processHcsResult(err error, resultp *uint16) error {
|
||||||
|
if resultp != nil {
|
||||||
|
result := convertAndFreeCoTaskMemString(resultp)
|
||||||
|
logrus.Debugf("Result: %s", result)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,323 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HNSEndpoint represents a network endpoint in HNS
|
||||||
|
type HNSEndpoint struct {
|
||||||
|
Id string `json:"ID,omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
VirtualNetwork string `json:",omitempty"`
|
||||||
|
VirtualNetworkName string `json:",omitempty"`
|
||||||
|
Policies []json.RawMessage `json:",omitempty"`
|
||||||
|
MacAddress string `json:",omitempty"`
|
||||||
|
IPAddress net.IP `json:",omitempty"`
|
||||||
|
DNSSuffix string `json:",omitempty"`
|
||||||
|
DNSServerList string `json:",omitempty"`
|
||||||
|
GatewayAddress string `json:",omitempty"`
|
||||||
|
EnableInternalDNS bool `json:",omitempty"`
|
||||||
|
DisableICC bool `json:",omitempty"`
|
||||||
|
PrefixLength uint8 `json:",omitempty"`
|
||||||
|
IsRemoteEndpoint bool `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//SystemType represents the type of the system on which actions are done
|
||||||
|
type SystemType string
|
||||||
|
|
||||||
|
// SystemType const
|
||||||
|
const (
|
||||||
|
ContainerType SystemType = "Container"
|
||||||
|
VirtualMachineType SystemType = "VirtualMachine"
|
||||||
|
HostType SystemType = "Host"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system
|
||||||
|
// Supported resource types are Network and Request Types are Add/Remove
|
||||||
|
type EndpointAttachDetachRequest struct {
|
||||||
|
ContainerID string `json:"ContainerId,omitempty"`
|
||||||
|
SystemType SystemType `json:"SystemType"`
|
||||||
|
CompartmentID uint16 `json:"CompartmentId,omitempty"`
|
||||||
|
VirtualNICName string `json:"VirtualNicName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointResquestResponse is object to get the endpoint request response
|
||||||
|
type EndpointResquestResponse struct {
|
||||||
|
Success bool
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSEndpointRequest makes a HNS call to modify/query a network endpoint
|
||||||
|
func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
|
||||||
|
endpoint := &HNSEndpoint{}
|
||||||
|
err := hnsCall(method, "/endpoints/"+path, request, &endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
|
||||||
|
func HNSListEndpointRequest() ([]HNSEndpoint, error) {
|
||||||
|
var endpoint []HNSEndpoint
|
||||||
|
err := hnsCall("GET", "/endpoints/", "", &endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
|
||||||
|
func HotAttachEndpoint(containerID string, endpointID string) error {
|
||||||
|
return modifyNetworkEndpoint(containerID, endpointID, Add)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
|
||||||
|
func HotDetachEndpoint(containerID string, endpointID string) error {
|
||||||
|
return modifyNetworkEndpoint(containerID, endpointID, Remove)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyContainer corresponding to the container id, by sending a request
|
||||||
|
func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
|
||||||
|
container, err := OpenContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
if IsNotExist(err) {
|
||||||
|
return ErrComputeSystemDoesNotExist
|
||||||
|
}
|
||||||
|
return getInnerError(err)
|
||||||
|
}
|
||||||
|
defer container.Close()
|
||||||
|
err = container.Modify(request)
|
||||||
|
if err != nil {
|
||||||
|
if IsNotSupported(err) {
|
||||||
|
return ErrPlatformNotSupported
|
||||||
|
}
|
||||||
|
return getInnerError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
|
||||||
|
requestMessage := &ResourceModificationRequestResponse{
|
||||||
|
Resource: Network,
|
||||||
|
Request: request,
|
||||||
|
Data: endpointID,
|
||||||
|
}
|
||||||
|
err := modifyContainer(containerID, requestMessage)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSEndpointByID get the Endpoint by ID
|
||||||
|
func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
|
||||||
|
return HNSEndpointRequest("GET", endpointID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSEndpointByName gets the endpoint filtered by Name
|
||||||
|
func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
|
||||||
|
hnsResponse, err := HNSListEndpointRequest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, hnsEndpoint := range hnsResponse {
|
||||||
|
if hnsEndpoint.Name == endpointName {
|
||||||
|
return &hnsEndpoint, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, EndpointNotFoundError{EndpointName: endpointName}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
|
||||||
|
func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) {
|
||||||
|
operation := "Create"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return HNSEndpointRequest("POST", "", string(jsonString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Endpoint by sending EndpointRequest to HNS
|
||||||
|
func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) {
|
||||||
|
operation := "Delete"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
return HNSEndpointRequest("DELETE", endpoint.Id, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Endpoint
|
||||||
|
func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) {
|
||||||
|
operation := "Update"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
jsonString, err := json.Marshal(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint)
|
||||||
|
|
||||||
|
return endpoint, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerHotAttach attaches an endpoint to a running container
|
||||||
|
func (endpoint *HNSEndpoint) ContainerHotAttach(containerID string) error {
|
||||||
|
operation := "ContainerHotAttach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
|
||||||
|
|
||||||
|
return modifyNetworkEndpoint(containerID, endpoint.Id, Add)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerHotDetach detaches an endpoint from a running container
|
||||||
|
func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error {
|
||||||
|
operation := "ContainerHotDetach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
|
||||||
|
|
||||||
|
return modifyNetworkEndpoint(containerID, endpoint.Id, Remove)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyACLPolicy applies a set of ACL Policies on the Endpoint
|
||||||
|
func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
|
||||||
|
operation := "ApplyACLPolicy"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
for _, policy := range policies {
|
||||||
|
if policy == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
jsonString, err := json.Marshal(policy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
endpoint.Policies = append(endpoint.Policies, jsonString)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := endpoint.Update()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAttach attaches an endpoint to container
|
||||||
|
func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error {
|
||||||
|
operation := "ContainerAttach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
ContainerID: containerID,
|
||||||
|
CompartmentID: compartmentID,
|
||||||
|
SystemType: ContainerType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerDetach detaches an endpoint from container
|
||||||
|
func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error {
|
||||||
|
operation := "ContainerDetach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
ContainerID: containerID,
|
||||||
|
SystemType: ContainerType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostAttach attaches a nic on the host
|
||||||
|
func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error {
|
||||||
|
operation := "HostAttach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
CompartmentID: compartmentID,
|
||||||
|
SystemType: HostType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostDetach detaches a nic on the host
|
||||||
|
func (endpoint *HNSEndpoint) HostDetach() error {
|
||||||
|
operation := "HostDetach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
SystemType: HostType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualMachineNICAttach attaches a endpoint to a virtual machine
|
||||||
|
func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error {
|
||||||
|
operation := "VirtualMachineNicAttach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
VirtualNICName: virtualMachineNICName,
|
||||||
|
SystemType: VirtualMachineType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualMachineNICDetach detaches a endpoint from a virtual machine
|
||||||
|
func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error {
|
||||||
|
operation := "VirtualMachineNicDetach"
|
||||||
|
title := "HCSShim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
SystemType: VirtualMachineType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hnsCall(method, path, request string, returnResponse interface{}) error {
|
||||||
|
var responseBuffer *uint16
|
||||||
|
logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request)
|
||||||
|
|
||||||
|
err := _hnsCall(method, path, request, &responseBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return makeError(err, "hnsCall ", "")
|
||||||
|
}
|
||||||
|
response := convertAndFreeCoTaskMemString(responseBuffer)
|
||||||
|
|
||||||
|
hnsresponse := &hnsResponse{}
|
||||||
|
if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hnsresponse.Success {
|
||||||
|
return fmt.Errorf("HNS failed with error : %s", hnsresponse.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hnsresponse.Output) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Network Response : %s", hnsresponse.Output)
|
||||||
|
err = json.Unmarshal(hnsresponse.Output, returnResponse)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Subnet is assoicated with a network and represents a list
|
||||||
|
// of subnets available to the network
|
||||||
|
type Subnet struct {
|
||||||
|
AddressPrefix string `json:",omitempty"`
|
||||||
|
GatewayAddress string `json:",omitempty"`
|
||||||
|
Policies []json.RawMessage `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MacPool is assoicated with a network and represents a list
|
||||||
|
// of macaddresses available to the network
|
||||||
|
type MacPool struct {
|
||||||
|
StartMacAddress string `json:",omitempty"`
|
||||||
|
EndMacAddress string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSNetwork represents a network in HNS
|
||||||
|
type HNSNetwork struct {
|
||||||
|
Id string `json:"ID,omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Type string `json:",omitempty"`
|
||||||
|
NetworkAdapterName string `json:",omitempty"`
|
||||||
|
SourceMac string `json:",omitempty"`
|
||||||
|
Policies []json.RawMessage `json:",omitempty"`
|
||||||
|
MacPools []MacPool `json:",omitempty"`
|
||||||
|
Subnets []Subnet `json:",omitempty"`
|
||||||
|
DNSSuffix string `json:",omitempty"`
|
||||||
|
DNSServerList string `json:",omitempty"`
|
||||||
|
DNSServerCompartment uint32 `json:",omitempty"`
|
||||||
|
ManagementIP string `json:",omitempty"`
|
||||||
|
AutomaticDNS bool `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hnsNetworkResponse struct {
|
||||||
|
Success bool
|
||||||
|
Error string
|
||||||
|
Output HNSNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
type hnsResponse struct {
|
||||||
|
Success bool
|
||||||
|
Error string
|
||||||
|
Output json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSNetworkRequest makes a call into HNS to update/query a single network
|
||||||
|
func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) {
|
||||||
|
var network HNSNetwork
|
||||||
|
err := hnsCall(method, "/networks/"+path, request, &network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &network, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListNetworkRequest makes a HNS call to query the list of available networks
|
||||||
|
func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) {
|
||||||
|
var network []HNSNetwork
|
||||||
|
err := hnsCall(method, "/networks/"+path, request, &network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return network, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSNetworkByID
|
||||||
|
func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) {
|
||||||
|
return HNSNetworkRequest("GET", networkID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSNetworkName filtered by Name
|
||||||
|
func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
|
||||||
|
hsnnetworks, err := HNSListNetworkRequest("GET", "", "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, hnsnetwork := range hsnnetworks {
|
||||||
|
if hnsnetwork.Name == networkName {
|
||||||
|
return &hnsnetwork, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, NetworkNotFoundError{NetworkName: networkName}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Network by sending NetworkRequest to HNS.
|
||||||
|
func (network *HNSNetwork) Create() (*HNSNetwork, error) {
|
||||||
|
operation := "Create"
|
||||||
|
title := "HCSShim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", network.Id)
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return HNSNetworkRequest("POST", "", string(jsonString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Network by sending NetworkRequest to HNS
|
||||||
|
func (network *HNSNetwork) Delete() (*HNSNetwork, error) {
|
||||||
|
operation := "Delete"
|
||||||
|
title := "HCSShim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", network.Id)
|
||||||
|
|
||||||
|
return HNSNetworkRequest("DELETE", network.Id, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an endpoint on the Network.
|
||||||
|
func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint {
|
||||||
|
return &HNSEndpoint{
|
||||||
|
VirtualNetwork: network.Id,
|
||||||
|
IPAddress: ipAddress,
|
||||||
|
MacAddress: string(macAddress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
|
||||||
|
operation := "CreateEndpoint"
|
||||||
|
title := "HCSShim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id)
|
||||||
|
|
||||||
|
endpoint.VirtualNetwork = network.Id
|
||||||
|
return endpoint.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
|
||||||
|
operation := "CreateRemoteEndpoint"
|
||||||
|
title := "HCSShim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", network.Id)
|
||||||
|
endpoint.IsRemoteEndpoint = true
|
||||||
|
return network.CreateEndpoint(endpoint)
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
// Type of Request Support in ModifySystem
|
||||||
|
type PolicyType string
|
||||||
|
|
||||||
|
// RequestType const
|
||||||
|
const (
|
||||||
|
Nat PolicyType = "NAT"
|
||||||
|
ACL PolicyType = "ACL"
|
||||||
|
PA PolicyType = "PA"
|
||||||
|
VLAN PolicyType = "VLAN"
|
||||||
|
VSID PolicyType = "VSID"
|
||||||
|
VNet PolicyType = "VNET"
|
||||||
|
L2Driver PolicyType = "L2Driver"
|
||||||
|
Isolation PolicyType = "Isolation"
|
||||||
|
QOS PolicyType = "QOS"
|
||||||
|
OutboundNat PolicyType = "OutBoundNAT"
|
||||||
|
ExternalLoadBalancer PolicyType = "ELB"
|
||||||
|
Route PolicyType = "ROUTE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NatPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
Protocol string
|
||||||
|
InternalPort uint16
|
||||||
|
ExternalPort uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type QosPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
MaximumOutgoingBandwidthInBytes uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsolationPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
VLAN uint
|
||||||
|
VSID uint
|
||||||
|
InDefaultIsolation bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type VlanPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
VLAN uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type VsidPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
VSID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
PA string `json:"PA"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutboundNatPolicy struct {
|
||||||
|
Policy
|
||||||
|
VIP string `json:"VIP,omitempty"`
|
||||||
|
Exceptions []string `json:"ExceptionList,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionType string
|
||||||
|
type DirectionType string
|
||||||
|
type RuleType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Allow ActionType = "Allow"
|
||||||
|
Block ActionType = "Block"
|
||||||
|
|
||||||
|
In DirectionType = "In"
|
||||||
|
Out DirectionType = "Out"
|
||||||
|
|
||||||
|
Host RuleType = "Host"
|
||||||
|
Switch RuleType = "Switch"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ACLPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
Protocol uint16
|
||||||
|
InternalPort uint16
|
||||||
|
Action ActionType
|
||||||
|
Direction DirectionType
|
||||||
|
LocalAddresses string
|
||||||
|
RemoteAddresses string
|
||||||
|
LocalPort uint16
|
||||||
|
RemotePort uint16
|
||||||
|
RuleType RuleType `json:"RuleType,omitempty"`
|
||||||
|
Priority uint16
|
||||||
|
ServiceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Policy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoutePolicy is a structure defining schema for Route based Policy
|
||||||
|
type RoutePolicy struct {
|
||||||
|
Policy
|
||||||
|
DestinationPrefix string `json:"DestinationPrefix,omitempty"`
|
||||||
|
NextHop string `json:"NextHop,omitempty"`
|
||||||
|
EncapEnabled bool `json:"NeedEncap,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy
|
||||||
|
type ELBPolicy struct {
|
||||||
|
LBPolicy
|
||||||
|
SourceVIP string `json:"SourceVIP,omitempty"`
|
||||||
|
VIPs []string `json:"VIPs,omitempty"`
|
||||||
|
ILB bool `json:"ILB,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LBPolicy is a structure defining schema for LoadBalancing based Policy
|
||||||
|
type LBPolicy struct {
|
||||||
|
Policy
|
||||||
|
Protocol uint16 `json:"Protocol,omitempty"`
|
||||||
|
InternalPort uint16
|
||||||
|
ExternalPort uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyList is a structure defining schema for Policy list request
|
||||||
|
type PolicyList struct {
|
||||||
|
ID string `json:"ID,omitempty"`
|
||||||
|
EndpointReferences []string `json:"References,omitempty"`
|
||||||
|
Policies []json.RawMessage `json:"Policies,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSPolicyListRequest makes a call into HNS to update/query a single network
|
||||||
|
func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) {
|
||||||
|
var policy PolicyList
|
||||||
|
err := hnsCall(method, "/policylists/"+path, request, &policy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListPolicyListRequest gets all the policy list
|
||||||
|
func HNSListPolicyListRequest() ([]PolicyList, error) {
|
||||||
|
var plist []PolicyList
|
||||||
|
err := hnsCall("GET", "/policylists/", "", &plist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return plist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyListRequest makes a HNS call to modify/query a network policy list
|
||||||
|
func PolicyListRequest(method, path, request string) (*PolicyList, error) {
|
||||||
|
policylist := &PolicyList{}
|
||||||
|
err := hnsCall(method, "/policylists/"+path, request, &policylist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return policylist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPolicyListByID get the policy list by ID
|
||||||
|
func GetPolicyListByID(policyListID string) (*PolicyList, error) {
|
||||||
|
return PolicyListRequest("GET", policyListID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create PolicyList by sending PolicyListRequest to HNS.
|
||||||
|
func (policylist *PolicyList) Create() (*PolicyList, error) {
|
||||||
|
operation := "Create"
|
||||||
|
title := "HCSShim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", policylist.ID)
|
||||||
|
jsonString, err := json.Marshal(policylist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return PolicyListRequest("POST", "", string(jsonString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes PolicyList
|
||||||
|
func (policylist *PolicyList) Delete() (*PolicyList, error) {
|
||||||
|
operation := "Delete"
|
||||||
|
title := "HCSShim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", policylist.ID)
|
||||||
|
|
||||||
|
return PolicyListRequest("DELETE", policylist.ID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEndpoint add an endpoint to a Policy List
|
||||||
|
func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
|
||||||
|
operation := "AddEndpoint"
|
||||||
|
title := "HCSShim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
|
||||||
|
|
||||||
|
_, err := policylist.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Endpoint to the Existing List
|
||||||
|
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
|
||||||
|
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEndpoint removes an endpoint from the Policy List
|
||||||
|
func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
|
||||||
|
operation := "RemoveEndpoint"
|
||||||
|
title := "HCSShim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
|
||||||
|
|
||||||
|
_, err := policylist.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
elementToRemove := "/endpoints/" + endpoint.Id
|
||||||
|
|
||||||
|
var references []string
|
||||||
|
|
||||||
|
for _, endpointReference := range policylist.EndpointReferences {
|
||||||
|
if endpointReference == elementToRemove {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
references = append(references, endpointReference)
|
||||||
|
}
|
||||||
|
policylist.EndpointReferences = references
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLoadBalancer policy list for the specified endpoints
|
||||||
|
func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) {
|
||||||
|
operation := "AddLoadBalancer"
|
||||||
|
title := "HCSShim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort)
|
||||||
|
|
||||||
|
policylist := &PolicyList{}
|
||||||
|
|
||||||
|
elbPolicy := &ELBPolicy{
|
||||||
|
SourceVIP: sourceVIP,
|
||||||
|
ILB: isILB,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vip) > 0 {
|
||||||
|
elbPolicy.VIPs = []string{vip}
|
||||||
|
}
|
||||||
|
elbPolicy.Type = ExternalLoadBalancer
|
||||||
|
elbPolicy.Protocol = protocol
|
||||||
|
elbPolicy.InternalPort = internalPort
|
||||||
|
elbPolicy.ExternalPort = externalPort
|
||||||
|
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(elbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
policylist.Policies = append(policylist.Policies, jsonString)
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoute adds route policy list for the specified endpoints
|
||||||
|
func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) {
|
||||||
|
operation := "AddRoute"
|
||||||
|
title := "HCSShim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix)
|
||||||
|
|
||||||
|
policylist := &PolicyList{}
|
||||||
|
|
||||||
|
rPolicy := &RoutePolicy{
|
||||||
|
DestinationPrefix: destinationPrefix,
|
||||||
|
NextHop: nextHop,
|
||||||
|
EncapEnabled: encapEnabled,
|
||||||
|
}
|
||||||
|
rPolicy.Type = Route
|
||||||
|
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(rPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
policylist.Policies = append(policylist.Policies, jsonString)
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImportLayer will take the contents of the folder at importFolderPath and import
|
||||||
|
// that into a layer with the id layerId. Note that in order to correctly populate
|
||||||
|
// the layer and interperet the transport format, all parent layers must already
|
||||||
|
// be present on the system at the paths provided in parentLayerPaths.
|
||||||
|
func ImportLayer(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::ImportLayer "
|
||||||
|
logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerID, importFolderPath)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = importLayer(&infop, layerID, importFolderPath, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerID, info.Flavour, importFolderPath)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerID, importFolderPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerWriter is an interface that supports writing a new container image layer.
|
||||||
|
type LayerWriter interface {
|
||||||
|
// Add adds a file to the layer with given metadata.
|
||||||
|
Add(name string, fileInfo *winio.FileBasicInfo) error
|
||||||
|
// AddLink adds a hard link to the layer. The target must already have been added.
|
||||||
|
AddLink(name string, target string) error
|
||||||
|
// Remove removes a file that was present in a parent layer from the layer.
|
||||||
|
Remove(name string) error
|
||||||
|
// Write writes data to the current file. The data must be in the format of a Win32
|
||||||
|
// backup stream.
|
||||||
|
Write(b []byte) (int, error)
|
||||||
|
// Close finishes the layer writing process and releases any resources.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterLayerWriter provides an interface to write the contents of a layer to the file system.
|
||||||
|
type FilterLayerWriter struct {
|
||||||
|
context uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a file or directory to the layer. The file's parent directory must have already been added.
|
||||||
|
//
|
||||||
|
// name contains the file's relative path. fileInfo contains file times and file attributes; the rest
|
||||||
|
// of the file metadata and the file data must be written as a Win32 backup stream to the Write() method.
|
||||||
|
// winio.BackupStreamWriter can be used to facilitate this.
|
||||||
|
func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
|
||||||
|
if name[0] != '\\' {
|
||||||
|
name = `\` + name
|
||||||
|
}
|
||||||
|
err := importLayerNext(w.context, name, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return makeError(err, "ImportLayerNext", "")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLink adds a hard link to the layer. The target of the link must have already been added.
|
||||||
|
func (w *FilterLayerWriter) AddLink(name string, target string) error {
|
||||||
|
return errors.New("hard links not yet supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a file from the layer. The file must have been present in the parent layer.
|
||||||
|
//
|
||||||
|
// name contains the file's relative path.
|
||||||
|
func (w *FilterLayerWriter) Remove(name string) error {
|
||||||
|
if name[0] != '\\' {
|
||||||
|
name = `\` + name
|
||||||
|
}
|
||||||
|
err := importLayerNext(w.context, name, nil)
|
||||||
|
if err != nil {
|
||||||
|
return makeError(err, "ImportLayerNext", "")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes more backup stream data to the current file.
|
||||||
|
func (w *FilterLayerWriter) Write(b []byte) (int, error) {
|
||||||
|
err := importLayerWrite(w.context, b)
|
||||||
|
if err != nil {
|
||||||
|
err = makeError(err, "ImportLayerWrite", "")
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close completes the layer write operation. The error must be checked to ensure that the
|
||||||
|
// operation was successful.
|
||||||
|
func (w *FilterLayerWriter) Close() (err error) {
|
||||||
|
if w.context != 0 {
|
||||||
|
err = importLayerEnd(w.context)
|
||||||
|
if err != nil {
|
||||||
|
err = makeError(err, "ImportLayerEnd", "")
|
||||||
|
}
|
||||||
|
w.context = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerWriterWrapper struct {
|
||||||
|
*legacyLayerWriter
|
||||||
|
info DriverInfo
|
||||||
|
layerID string
|
||||||
|
path string
|
||||||
|
parentLayerPaths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerWriterWrapper) Close() error {
|
||||||
|
defer os.RemoveAll(r.root.Name())
|
||||||
|
defer r.legacyLayerWriter.CloseRoots()
|
||||||
|
err := r.legacyLayerWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := r.info
|
||||||
|
info.HomeDir = ""
|
||||||
|
if err = ImportLayer(info, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, name := range r.Tombstones {
|
||||||
|
if err = removeRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add any hard links that were collected.
|
||||||
|
for _, lnk := range r.PendingLinks {
|
||||||
|
if err = removeRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = linkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prepare the utility VM for use if one is present in the layer.
|
||||||
|
if r.HasUtilityVM {
|
||||||
|
err := ensureNotReparsePointRelative("UtilityVM", r.destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLayerWriter returns a new layer writer for creating a layer on disk.
|
||||||
|
// The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges
|
||||||
|
// to call this and any methods on the resulting LayerWriter.
|
||||||
|
func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) {
|
||||||
|
if len(parentLayerPaths) == 0 {
|
||||||
|
// This is a base layer. It gets imported differently.
|
||||||
|
f, err := openRoot(filepath.Join(info.HomeDir, layerID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &baseLayerWriter{
|
||||||
|
root: f,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if procImportLayerBegin.Find() != nil {
|
||||||
|
// The new layer reader is not available on this Windows build. Fall back to the
|
||||||
|
// legacy export code path.
|
||||||
|
path, err := ioutil.TempDir("", "hcs")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
w, err := newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &legacyLayerWriterWrapper{
|
||||||
|
legacyLayerWriter: w,
|
||||||
|
info: info,
|
||||||
|
layerID: layerID,
|
||||||
|
path: path,
|
||||||
|
parentLayerPaths: parentLayerPaths,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &FilterLayerWriter{}
|
||||||
|
err = importLayerBegin(&infop, layerID, layers, &w.context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeError(err, "ImportLayerStart", "")
|
||||||
|
}
|
||||||
|
return w, nil
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessConfig is used as both the input of Container.CreateProcess
|
||||||
|
// and to convert the parameters to JSON for passing onto the HCS
|
||||||
|
type ProcessConfig struct {
|
||||||
|
ApplicationName string `json:",omitempty"`
|
||||||
|
CommandLine string `json:",omitempty"`
|
||||||
|
CommandArgs []string `json:",omitempty"` // Used by Linux Containers on Windows
|
||||||
|
User string `json:",omitempty"`
|
||||||
|
WorkingDirectory string `json:",omitempty"`
|
||||||
|
Environment map[string]string `json:",omitempty"`
|
||||||
|
EmulateConsole bool `json:",omitempty"`
|
||||||
|
CreateStdInPipe bool `json:",omitempty"`
|
||||||
|
CreateStdOutPipe bool `json:",omitempty"`
|
||||||
|
CreateStdErrPipe bool `json:",omitempty"`
|
||||||
|
ConsoleSize [2]uint `json:",omitempty"`
|
||||||
|
CreateInUtilityVm bool `json:",omitempty"` // Used by Linux Containers on Windows
|
||||||
|
OCISpecification *json.RawMessage `json:",omitempty"` // Used by Linux Containers on Windows
|
||||||
|
}
|
||||||
|
|
||||||
|
type Layer struct {
|
||||||
|
ID string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MappedDir struct {
|
||||||
|
HostPath string
|
||||||
|
ContainerPath string
|
||||||
|
ReadOnly bool
|
||||||
|
BandwidthMaximum uint64
|
||||||
|
IOPSMaximum uint64
|
||||||
|
CreateInUtilityVM bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type MappedPipe struct {
|
||||||
|
HostPath string
|
||||||
|
ContainerPipeName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HvRuntime struct {
|
||||||
|
ImagePath string `json:",omitempty"`
|
||||||
|
SkipTemplate bool `json:",omitempty"`
|
||||||
|
LinuxInitrdFile string `json:",omitempty"` // File under ImagePath on host containing an initrd image for starting a Linux utility VM
|
||||||
|
LinuxKernelFile string `json:",omitempty"` // File under ImagePath on host containing a kernel for starting a Linux utility VM
|
||||||
|
LinuxBootParameters string `json:",omitempty"` // Additional boot parameters for starting a Linux Utility VM in initrd mode
|
||||||
|
BootSource string `json:",omitempty"` // "Vhd" for Linux Utility VM booting from VHD
|
||||||
|
WritableBootSource bool `json:",omitempty"` // Linux Utility VM booting from VHD
|
||||||
|
}
|
||||||
|
|
||||||
|
type MappedVirtualDisk struct {
|
||||||
|
HostPath string `json:",omitempty"` // Path to VHD on the host
|
||||||
|
ContainerPath string // Platform-specific mount point path in the container
|
||||||
|
CreateInUtilityVM bool `json:",omitempty"`
|
||||||
|
ReadOnly bool `json:",omitempty"`
|
||||||
|
Cache string `json:",omitempty"` // "" (Unspecified); "Disabled"; "Enabled"; "Private"; "PrivateAllowSharing"
|
||||||
|
AttachOnly bool `json:",omitempty:`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerConfig is used as both the input of CreateContainer
|
||||||
|
// and to convert the parameters to JSON for passing onto the HCS
|
||||||
|
type ContainerConfig struct {
|
||||||
|
SystemType string // HCS requires this to be hard-coded to "Container"
|
||||||
|
Name string // Name of the container. We use the docker ID.
|
||||||
|
Owner string `json:",omitempty"` // The management platform that created this container
|
||||||
|
VolumePath string `json:",omitempty"` // Windows volume path for scratch space. Used by Windows Server Containers only. Format \\?\\Volume{GUID}
|
||||||
|
IgnoreFlushesDuringBoot bool `json:",omitempty"` // Optimization hint for container startup in Windows
|
||||||
|
LayerFolderPath string `json:",omitempty"` // Where the layer folders are located. Used by Windows Server Containers only. Format %root%\windowsfilter\containerID
|
||||||
|
Layers []Layer // List of storage layers. Required for Windows Server and Hyper-V Containers. Format ID=GUID;Path=%root%\windowsfilter\layerID
|
||||||
|
Credentials string `json:",omitempty"` // Credentials information
|
||||||
|
ProcessorCount uint32 `json:",omitempty"` // Number of processors to assign to the container.
|
||||||
|
ProcessorWeight uint64 `json:",omitempty"` // CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000. A value of 0 results in default shares.
|
||||||
|
ProcessorMaximum int64 `json:",omitempty"` // Specifies the portion of processor cycles that this container can use as a percentage times 100. Range is from 1 to 10000. A value of 0 results in no limit.
|
||||||
|
StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS
|
||||||
|
StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second
|
||||||
|
StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller
|
||||||
|
MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes
|
||||||
|
HostName string `json:",omitempty"` // Hostname
|
||||||
|
MappedDirectories []MappedDir `json:",omitempty"` // List of mapped directories (volumes/mounts)
|
||||||
|
MappedPipes []MappedPipe `json:",omitempty"` // List of mapped Windows named pipes
|
||||||
|
HvPartition bool // True if it a Hyper-V Container
|
||||||
|
NetworkSharedContainerName string `json:",omitempty"` // Name (ID) of the container that we will share the network stack with.
|
||||||
|
EndpointList []string `json:",omitempty"` // List of networking endpoints to be attached to container
|
||||||
|
HvRuntime *HvRuntime `json:",omitempty"` // Hyper-V container settings. Used by Hyper-V containers only. Format ImagePath=%root%\BaseLayerID\UtilityVM
|
||||||
|
Servicing bool `json:",omitempty"` // True if this container is for servicing
|
||||||
|
AllowUnqualifiedDNSQuery bool `json:",omitempty"` // True to allow unqualified DNS name resolution
|
||||||
|
DNSSearchList string `json:",omitempty"` // Comma seperated list of DNS suffixes to use for name resolution
|
||||||
|
ContainerType string `json:",omitempty"` // "Linux" for Linux containers on Windows. Omitted otherwise.
|
||||||
|
TerminateOnLastHandleClosed bool `json:",omitempty"` // Should HCS terminate the container once all handles have been closed
|
||||||
|
MappedVirtualDisks []MappedVirtualDisk `json:",omitempty"` // Array of virtual disks to mount at start
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComputeSystemQuery struct {
|
||||||
|
IDs []string `json:"Ids,omitempty"`
|
||||||
|
Types []string `json:",omitempty"`
|
||||||
|
Names []string `json:",omitempty"`
|
||||||
|
Owners []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container represents a created (but not necessarily running) container.
|
||||||
|
type Container interface {
|
||||||
|
// Start synchronously starts the container.
|
||||||
|
Start() error
|
||||||
|
|
||||||
|
// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
|
||||||
|
Shutdown() error
|
||||||
|
|
||||||
|
// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
|
||||||
|
Terminate() error
|
||||||
|
|
||||||
|
// Waits synchronously waits for the container to shutdown or terminate.
|
||||||
|
Wait() error
|
||||||
|
|
||||||
|
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
|
||||||
|
// returns false if timeout occurs.
|
||||||
|
WaitTimeout(time.Duration) error
|
||||||
|
|
||||||
|
// Pause pauses the execution of a container.
|
||||||
|
Pause() error
|
||||||
|
|
||||||
|
// Resume resumes the execution of a container.
|
||||||
|
Resume() error
|
||||||
|
|
||||||
|
// HasPendingUpdates returns true if the container has updates pending to install.
|
||||||
|
HasPendingUpdates() (bool, error)
|
||||||
|
|
||||||
|
// Statistics returns statistics for a container.
|
||||||
|
Statistics() (Statistics, error)
|
||||||
|
|
||||||
|
// ProcessList returns details for the processes in a container.
|
||||||
|
ProcessList() ([]ProcessListItem, error)
|
||||||
|
|
||||||
|
// MappedVirtualDisks returns virtual disks mapped to a utility VM, indexed by controller
|
||||||
|
MappedVirtualDisks() (map[int]MappedVirtualDiskController, error)
|
||||||
|
|
||||||
|
// CreateProcess launches a new process within the container.
|
||||||
|
CreateProcess(c *ProcessConfig) (Process, error)
|
||||||
|
|
||||||
|
// OpenProcess gets an interface to an existing process within the container.
|
||||||
|
OpenProcess(pid int) (Process, error)
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the container but does not terminate or wait for it.
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// Modify the System
|
||||||
|
Modify(config *ResourceModificationRequestResponse) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process represents a running or exited process.
|
||||||
|
type Process interface {
|
||||||
|
// Pid returns the process ID of the process within the container.
|
||||||
|
Pid() int
|
||||||
|
|
||||||
|
// Kill signals the process to terminate but does not wait for it to finish terminating.
|
||||||
|
Kill() error
|
||||||
|
|
||||||
|
// Wait waits for the process to exit.
|
||||||
|
Wait() error
|
||||||
|
|
||||||
|
// WaitTimeout waits for the process to exit or the duration to elapse. It returns
|
||||||
|
// false if timeout occurs.
|
||||||
|
WaitTimeout(time.Duration) error
|
||||||
|
|
||||||
|
// ExitCode returns the exit code of the process. The process must have
|
||||||
|
// already terminated.
|
||||||
|
ExitCode() (int, error)
|
||||||
|
|
||||||
|
// ResizeConsole resizes the console of the process.
|
||||||
|
ResizeConsole(width, height uint16) error
|
||||||
|
|
||||||
|
// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
|
||||||
|
// these pipes does not close the underlying pipes; it should be possible to
|
||||||
|
// call this multiple times to get multiple interfaces.
|
||||||
|
Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error)
|
||||||
|
|
||||||
|
// CloseStdin closes the write side of the stdin pipe so that the process is
|
||||||
|
// notified on the read side that there is no more data in stdin.
|
||||||
|
CloseStdin() error
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the process but does not kill
|
||||||
|
// or wait on it.
|
||||||
|
Close() error
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// LayerExists will return true if a layer with the given id exists and is known
|
||||||
|
// to the system.
|
||||||
|
func LayerExists(info DriverInfo, id string) (bool, error) {
|
||||||
|
title := "hcsshim::LayerExists "
|
||||||
|
logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id)
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the procedure itself.
|
||||||
|
var exists uint32
|
||||||
|
|
||||||
|
err = layerExists(&infop, id, &exists)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded flavour=%d id=%s exists=%d", info.Flavour, id, exists)
|
||||||
|
return exists != 0, nil
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
// This file contains utility functions to support storage (graph) related
|
||||||
|
// functionality.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* To pass into syscall, we need a struct matching the following:
|
||||||
|
enum GraphDriverType
|
||||||
|
{
|
||||||
|
DiffDriver,
|
||||||
|
FilterDriver
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DriverInfo {
|
||||||
|
GraphDriverType Flavour;
|
||||||
|
LPCWSTR HomeDir;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
type DriverInfo struct {
|
||||||
|
Flavour int
|
||||||
|
HomeDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
type driverInfo struct {
|
||||||
|
Flavour int
|
||||||
|
HomeDirp *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertDriverInfo(info DriverInfo) (driverInfo, error) {
|
||||||
|
homedirp, err := syscall.UTF16PtrFromString(info.HomeDir)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Failed conversion of home to pointer for driver info: %s", err.Error())
|
||||||
|
return driverInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return driverInfo{
|
||||||
|
Flavour: info.Flavour,
|
||||||
|
HomeDirp: homedirp,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To pass into syscall, we need a struct matching the following:
|
||||||
|
typedef struct _WC_LAYER_DESCRIPTOR {
|
||||||
|
|
||||||
|
//
|
||||||
|
// The ID of the layer
|
||||||
|
//
|
||||||
|
|
||||||
|
GUID LayerId;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Additional flags
|
||||||
|
//
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
ULONG Reserved : 31;
|
||||||
|
ULONG Dirty : 1; // Created from sandbox as a result of snapshot
|
||||||
|
};
|
||||||
|
ULONG Value;
|
||||||
|
} Flags;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Path to the layer root directory, null-terminated
|
||||||
|
//
|
||||||
|
|
||||||
|
PCWSTR Path;
|
||||||
|
|
||||||
|
} WC_LAYER_DESCRIPTOR, *PWC_LAYER_DESCRIPTOR;
|
||||||
|
*/
|
||||||
|
type WC_LAYER_DESCRIPTOR struct {
|
||||||
|
LayerId GUID
|
||||||
|
Flags uint32
|
||||||
|
Pathp *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func layerPathsToDescriptors(parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, error) {
|
||||||
|
// Array of descriptors that gets constructed.
|
||||||
|
var layers []WC_LAYER_DESCRIPTOR
|
||||||
|
|
||||||
|
for i := 0; i < len(parentLayerPaths); i++ {
|
||||||
|
// Create a layer descriptor, using the folder name
|
||||||
|
// as the source for a GUID LayerId
|
||||||
|
_, folderName := filepath.Split(parentLayerPaths[i])
|
||||||
|
g, err := NameToGuid(folderName)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Failed to convert name to guid %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := syscall.UTF16PtrFromString(parentLayerPaths[i])
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Failed conversion of parentLayerPath to pointer %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
layers = append(layers, WC_LAYER_DESCRIPTOR{
|
||||||
|
LayerId: g,
|
||||||
|
Flags: 0,
|
||||||
|
Pathp: p,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return layers, nil
|
||||||
|
}
|
|
@ -0,0 +1,827 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errorIterationCanceled = errors.New("")
|
||||||
|
|
||||||
|
var mutatedUtilityVMFiles = map[string]bool{
|
||||||
|
`EFI\Microsoft\Boot\BCD`: true,
|
||||||
|
`EFI\Microsoft\Boot\BCD.LOG`: true,
|
||||||
|
`EFI\Microsoft\Boot\BCD.LOG1`: true,
|
||||||
|
`EFI\Microsoft\Boot\BCD.LOG2`: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
filesPath = `Files`
|
||||||
|
hivesPath = `Hives`
|
||||||
|
utilityVMPath = `UtilityVM`
|
||||||
|
utilityVMFilesPath = `UtilityVM\Files`
|
||||||
|
)
|
||||||
|
|
||||||
|
func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) {
|
||||||
|
return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLongAbsPath(path string) (string, error) {
|
||||||
|
if strings.HasPrefix(path, `\\?\`) || strings.HasPrefix(path, `\\.\`) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
absPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
path = absPath
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(path, `\\`) {
|
||||||
|
return `\\?\UNC\` + path[2:], nil
|
||||||
|
}
|
||||||
|
return `\\?\` + path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasPathPrefix(p, prefix string) bool {
|
||||||
|
return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\'
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileEntry struct {
|
||||||
|
path string
|
||||||
|
fi os.FileInfo
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerReader struct {
|
||||||
|
root string
|
||||||
|
result chan *fileEntry
|
||||||
|
proceed chan bool
|
||||||
|
currentFile *os.File
|
||||||
|
backupReader *winio.BackupFileReader
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLegacyLayerReader returns a new LayerReader that can read the Windows
|
||||||
|
// container layer transport format from disk.
|
||||||
|
func newLegacyLayerReader(root string) *legacyLayerReader {
|
||||||
|
r := &legacyLayerReader{
|
||||||
|
root: root,
|
||||||
|
result: make(chan *fileEntry),
|
||||||
|
proceed: make(chan bool),
|
||||||
|
}
|
||||||
|
go r.walk()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTombstones(path string) (map[string]([]string), error) {
|
||||||
|
tf, err := os.Open(filepath.Join(path, "tombstones.txt"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tf.Close()
|
||||||
|
s := bufio.NewScanner(tf)
|
||||||
|
if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" {
|
||||||
|
return nil, errors.New("Invalid tombstones file")
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := make(map[string]([]string))
|
||||||
|
for s.Scan() {
|
||||||
|
t := filepath.Join(filesPath, s.Text()[1:]) // skip leading `\`
|
||||||
|
dir := filepath.Dir(t)
|
||||||
|
ts[dir] = append(ts[dir], t)
|
||||||
|
}
|
||||||
|
if err = s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) walkUntilCancelled() error {
|
||||||
|
root, err := makeLongAbsPath(r.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.root = root
|
||||||
|
ts, err := readTombstones(r.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048.
|
||||||
|
// Handle failure from what may be a golang bug in the conversion of
|
||||||
|
// UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
|
||||||
|
// which is called by filepath.Walk will fail when a filename contains
|
||||||
|
// unicode characters. Skip the recycle bin regardless which is goodness.
|
||||||
|
if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.result <- &fileEntry{path, info, nil}
|
||||||
|
if !<-r.proceed {
|
||||||
|
return errorIterationCanceled
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all the tombstones.
|
||||||
|
if info.IsDir() {
|
||||||
|
relPath, err := filepath.Rel(r.root, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dts, ok := ts[relPath]; ok {
|
||||||
|
for _, t := range dts {
|
||||||
|
r.result <- &fileEntry{filepath.Join(r.root, t), nil, nil}
|
||||||
|
if !<-r.proceed {
|
||||||
|
return errorIterationCanceled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err == errorIterationCanceled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) walk() {
|
||||||
|
defer close(r.result)
|
||||||
|
if !<-r.proceed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.walkUntilCancelled()
|
||||||
|
if err != nil {
|
||||||
|
for {
|
||||||
|
r.result <- &fileEntry{err: err}
|
||||||
|
if !<-r.proceed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) reset() {
|
||||||
|
if r.backupReader != nil {
|
||||||
|
r.backupReader.Close()
|
||||||
|
r.backupReader = nil
|
||||||
|
}
|
||||||
|
if r.currentFile != nil {
|
||||||
|
r.currentFile.Close()
|
||||||
|
r.currentFile = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findBackupStreamSize(r io.Reader) (int64, error) {
|
||||||
|
br := winio.NewBackupStreamReader(r)
|
||||||
|
for {
|
||||||
|
hdr, err := br.Next()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if hdr.Id == winio.BackupData {
|
||||||
|
return hdr.Size, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) {
|
||||||
|
r.reset()
|
||||||
|
r.proceed <- true
|
||||||
|
fe := <-r.result
|
||||||
|
if fe == nil {
|
||||||
|
err = errors.New("LegacyLayerReader closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fe.err != nil {
|
||||||
|
err = fe.err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err = filepath.Rel(r.root, fe.path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if fe.fi == nil {
|
||||||
|
// This is a tombstone. Return a nil fileInfo.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if fe.fi.IsDir() && hasPathPrefix(path, filesPath) {
|
||||||
|
fe.path += ".$wcidirs$"
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fileInfo, err = winio.GetFileBasicInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPathPrefix(path, filesPath) {
|
||||||
|
size = fe.fi.Size()
|
||||||
|
r.backupReader = winio.NewBackupFileReader(f, false)
|
||||||
|
if path == hivesPath || path == filesPath {
|
||||||
|
// The Hives directory has a non-deterministic file time because of the
|
||||||
|
// nature of the import process. Use the times from System_Delta.
|
||||||
|
var g *os.File
|
||||||
|
g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
attr := fileInfo.FileAttributes
|
||||||
|
fileInfo, err = winio.GetFileBasicInfo(g)
|
||||||
|
g.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileInfo.FileAttributes = attr
|
||||||
|
}
|
||||||
|
|
||||||
|
// The creation time and access time get reset for files outside of the Files path.
|
||||||
|
fileInfo.CreationTime = fileInfo.LastWriteTime
|
||||||
|
fileInfo.LastAccessTime = fileInfo.LastWriteTime
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The file attributes are written before the backup stream.
|
||||||
|
var attr uint32
|
||||||
|
err = binary.Read(f, binary.LittleEndian, &attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileInfo.FileAttributes = uintptr(attr)
|
||||||
|
beginning := int64(4)
|
||||||
|
|
||||||
|
// Find the accurate file size.
|
||||||
|
if !fe.fi.IsDir() {
|
||||||
|
size, err = findBackupStreamSize(f)
|
||||||
|
if err != nil {
|
||||||
|
err = &os.PathError{Op: "findBackupStreamSize", Path: fe.path, Err: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return back to the beginning of the backup stream.
|
||||||
|
_, err = f.Seek(beginning, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.currentFile = f
|
||||||
|
f = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Read(b []byte) (int, error) {
|
||||||
|
if r.backupReader == nil {
|
||||||
|
if r.currentFile == nil {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return r.currentFile.Read(b)
|
||||||
|
}
|
||||||
|
return r.backupReader.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
if r.backupReader == nil {
|
||||||
|
if r.currentFile == nil {
|
||||||
|
return 0, errors.New("no current file")
|
||||||
|
}
|
||||||
|
return r.currentFile.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
return 0, errors.New("seek not supported on this stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Close() error {
|
||||||
|
r.proceed <- false
|
||||||
|
<-r.result
|
||||||
|
r.reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pendingLink struct {
|
||||||
|
Path, Target string
|
||||||
|
TargetRoot *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type pendingDir struct {
|
||||||
|
Path string
|
||||||
|
Root *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerWriter struct {
|
||||||
|
root *os.File
|
||||||
|
destRoot *os.File
|
||||||
|
parentRoots []*os.File
|
||||||
|
currentFile *os.File
|
||||||
|
currentFileName string
|
||||||
|
currentFileRoot *os.File
|
||||||
|
backupWriter *winio.BackupFileWriter
|
||||||
|
Tombstones []string
|
||||||
|
HasUtilityVM bool
|
||||||
|
uvmDi []dirInfo
|
||||||
|
addedFiles map[string]bool
|
||||||
|
PendingLinks []pendingLink
|
||||||
|
pendingDirs []pendingDir
|
||||||
|
currentIsDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLegacyLayerWriter returns a LayerWriter that can write the contaler layer
|
||||||
|
// transport format to disk.
|
||||||
|
func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) {
|
||||||
|
w = &legacyLayerWriter{
|
||||||
|
addedFiles: make(map[string]bool),
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.CloseRoots()
|
||||||
|
w = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
w.root, err = openRoot(root)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.destRoot, err = openRoot(destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, r := range parentRoots {
|
||||||
|
f, err := openRoot(r)
|
||||||
|
if err != nil {
|
||||||
|
return w, err
|
||||||
|
}
|
||||||
|
w.parentRoots = append(w.parentRoots, f)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) CloseRoots() {
|
||||||
|
if w.root != nil {
|
||||||
|
w.root.Close()
|
||||||
|
w.root = nil
|
||||||
|
}
|
||||||
|
if w.destRoot != nil {
|
||||||
|
w.destRoot.Close()
|
||||||
|
w.destRoot = nil
|
||||||
|
}
|
||||||
|
for i := range w.parentRoots {
|
||||||
|
w.parentRoots[i].Close()
|
||||||
|
}
|
||||||
|
w.parentRoots = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) initUtilityVM() error {
|
||||||
|
if !w.HasUtilityVM {
|
||||||
|
err := mkdirRelative(utilityVMPath, w.destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Server 2016 does not support multiple layers for the utility VM, so
|
||||||
|
// clone the utility VM from the parent layer into this layer. Use hard
|
||||||
|
// links to avoid unnecessary copying, since most of the files are
|
||||||
|
// immutable.
|
||||||
|
err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
|
||||||
|
}
|
||||||
|
w.HasUtilityVM = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) reset() error {
|
||||||
|
if w.currentIsDir {
|
||||||
|
r := w.currentFile
|
||||||
|
br := winio.NewBackupStreamReader(r)
|
||||||
|
// Seek to the beginning of the backup stream, skipping the fileattrs
|
||||||
|
if _, err := r.Seek(4, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
bhdr, err := br.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
// end of backupstream data
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch bhdr.Id {
|
||||||
|
case winio.BackupReparseData:
|
||||||
|
// The current file is a `.$wcidirs$` metadata file that
|
||||||
|
// describes a directory reparse point. Delete the placeholder
|
||||||
|
// directory to prevent future files being added into the
|
||||||
|
// destination of the reparse point during the ImportLayer call
|
||||||
|
if err := removeRelative(w.currentFileName, w.currentFileRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot})
|
||||||
|
default:
|
||||||
|
// ignore all other stream types, as we only care about directory reparse points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.currentIsDir = false
|
||||||
|
}
|
||||||
|
if w.backupWriter != nil {
|
||||||
|
w.backupWriter.Close()
|
||||||
|
w.backupWriter = nil
|
||||||
|
}
|
||||||
|
if w.currentFile != nil {
|
||||||
|
w.currentFile.Close()
|
||||||
|
w.currentFile = nil
|
||||||
|
w.currentFileName = ""
|
||||||
|
w.currentFileRoot = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata
|
||||||
|
func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
|
||||||
|
src, err := openRelative(
|
||||||
|
subPath,
|
||||||
|
srcRoot,
|
||||||
|
syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY,
|
||||||
|
syscall.FILE_SHARE_READ,
|
||||||
|
_FILE_OPEN,
|
||||||
|
_FILE_OPEN_REPARSE_POINT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
srcr := winio.NewBackupFileReader(src, true)
|
||||||
|
defer srcr.Close()
|
||||||
|
|
||||||
|
fileInfo, err = winio.GetFileBasicInfo(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
extraFlags := uint32(0)
|
||||||
|
if isDir {
|
||||||
|
extraFlags |= _FILE_DIRECTORY_FILE
|
||||||
|
}
|
||||||
|
dest, err := openRelative(
|
||||||
|
subPath,
|
||||||
|
destRoot,
|
||||||
|
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
|
||||||
|
syscall.FILE_SHARE_READ,
|
||||||
|
_FILE_CREATE,
|
||||||
|
extraFlags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer dest.Close()
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(dest, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
destw := winio.NewBackupFileWriter(dest, true)
|
||||||
|
defer func() {
|
||||||
|
cerr := destw.Close()
|
||||||
|
if err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(destw, srcr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneTree clones a directory tree using hard links. It skips hard links for
|
||||||
|
// the file names in the provided map and just copies those files.
|
||||||
|
func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error {
|
||||||
|
var di []dirInfo
|
||||||
|
err := ensureNotReparsePointRelative(subPath, srcRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||||
|
// Directories, reparse points, and files that will be mutated during
|
||||||
|
// utility VM import must be copied. All other files can be hard linked.
|
||||||
|
isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
|
||||||
|
// In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink.
|
||||||
|
// See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc
|
||||||
|
// Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly
|
||||||
|
isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||||
|
|
||||||
|
if isDir || isReparsePoint || mutatedFiles[relPath] {
|
||||||
|
fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isDir && !isReparsePoint {
|
||||||
|
di = append(di, dirInfo{path: relPath, fileInfo: *fi})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = linkRelative(relPath, srcRoot, relPath, destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't recurse on reparse points in go1.8 and older. Filepath.Walk
|
||||||
|
// handles this in go1.9 and newer.
|
||||||
|
if isDir && isReparsePoint && shouldSkipDirectoryReparse {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return reapplyDirectoryTimes(destRoot, di)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
|
||||||
|
if err := w.reset(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == utilityVMPath {
|
||||||
|
return w.initUtilityVM()
|
||||||
|
}
|
||||||
|
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
if hasPathPrefix(name, utilityVMPath) {
|
||||||
|
if !w.HasUtilityVM {
|
||||||
|
return errors.New("missing UtilityVM directory")
|
||||||
|
}
|
||||||
|
if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
|
||||||
|
return errors.New("invalid UtilityVM layer")
|
||||||
|
}
|
||||||
|
createDisposition := uint32(_FILE_OPEN)
|
||||||
|
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||||
|
st, err := lstatRelative(name, w.destRoot)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if st != nil {
|
||||||
|
// Delete the existing file/directory if it is not the same type as this directory.
|
||||||
|
existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||||
|
if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
|
||||||
|
if err = removeAllRelative(name, w.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
st = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if st == nil {
|
||||||
|
if err = mkdirRelative(name, w.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||||
|
w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Overwrite any existing hard link.
|
||||||
|
err := removeRelative(name, w.destRoot)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createDisposition = _FILE_CREATE
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := openRelative(
|
||||||
|
name,
|
||||||
|
w.destRoot,
|
||||||
|
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
|
||||||
|
syscall.FILE_SHARE_READ,
|
||||||
|
createDisposition,
|
||||||
|
_FILE_OPEN_REPARSE_POINT,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
removeRelative(name, w.destRoot)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(f, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.backupWriter = winio.NewBackupFileWriter(f, true)
|
||||||
|
w.currentFile = f
|
||||||
|
w.currentFileName = name
|
||||||
|
w.currentFileRoot = w.destRoot
|
||||||
|
w.addedFiles[name] = true
|
||||||
|
f = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fname := name
|
||||||
|
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||||
|
err := mkdirRelative(name, w.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fname += ".$wcidirs$"
|
||||||
|
w.currentIsDir = true
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := openRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_CREATE, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
removeRelative(fname, w.root)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
strippedFi := *fileInfo
|
||||||
|
strippedFi.FileAttributes = 0
|
||||||
|
err = winio.SetFileBasicInfo(f, &strippedFi)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasPathPrefix(name, hivesPath) {
|
||||||
|
w.backupWriter = winio.NewBackupFileWriter(f, false)
|
||||||
|
} else {
|
||||||
|
// The file attributes are written before the stream.
|
||||||
|
err = binary.Write(f, binary.LittleEndian, uint32(fileInfo.FileAttributes))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.currentFile = f
|
||||||
|
w.currentFileName = name
|
||||||
|
w.currentFileRoot = w.root
|
||||||
|
w.addedFiles[name] = true
|
||||||
|
f = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
||||||
|
if err := w.reset(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
target = filepath.Clean(target)
|
||||||
|
var roots []*os.File
|
||||||
|
if hasPathPrefix(target, filesPath) {
|
||||||
|
// Look for cross-layer hard link targets in the parent layers, since
|
||||||
|
// nothing is in the destination path yet.
|
||||||
|
roots = w.parentRoots
|
||||||
|
} else if hasPathPrefix(target, utilityVMFilesPath) {
|
||||||
|
// Since the utility VM is fully cloned into the destination path
|
||||||
|
// already, look for cross-layer hard link targets directly in the
|
||||||
|
// destination path.
|
||||||
|
roots = []*os.File{w.destRoot}
|
||||||
|
}
|
||||||
|
|
||||||
|
if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
|
||||||
|
return errors.New("invalid hard link in layer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find to try the target of the link in a previously added file. If that
|
||||||
|
// fails, search in parent layers.
|
||||||
|
var selectedRoot *os.File
|
||||||
|
if _, ok := w.addedFiles[target]; ok {
|
||||||
|
selectedRoot = w.destRoot
|
||||||
|
} else {
|
||||||
|
for _, r := range roots {
|
||||||
|
if _, err := lstatRelative(target, r); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedRoot = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if selectedRoot == nil {
|
||||||
|
return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The link can't be written until after the ImportLayer call.
|
||||||
|
w.PendingLinks = append(w.PendingLinks, pendingLink{
|
||||||
|
Path: name,
|
||||||
|
Target: target,
|
||||||
|
TargetRoot: selectedRoot,
|
||||||
|
})
|
||||||
|
w.addedFiles[name] = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Remove(name string) error {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
if hasPathPrefix(name, filesPath) {
|
||||||
|
w.Tombstones = append(w.Tombstones, name)
|
||||||
|
} else if hasPathPrefix(name, utilityVMFilesPath) {
|
||||||
|
err := w.initUtilityVM()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Make sure the path exists; os.RemoveAll will not fail if the file is
|
||||||
|
// already gone, and this needs to be a fatal error for diagnostics
|
||||||
|
// purposes.
|
||||||
|
if _, err := lstatRelative(name, w.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = removeAllRelative(name, w.destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid tombstone %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Write(b []byte) (int, error) {
|
||||||
|
if w.backupWriter == nil {
|
||||||
|
if w.currentFile == nil {
|
||||||
|
return 0, errors.New("closed")
|
||||||
|
}
|
||||||
|
return w.currentFile.Write(b)
|
||||||
|
}
|
||||||
|
return w.backupWriter.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Close() error {
|
||||||
|
if err := w.reset(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := removeRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, pd := range w.pendingDirs {
|
||||||
|
err := mkdirRelative(pd.Path, pd.Root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if w.HasUtilityVM {
|
||||||
|
err := reapplyDirectoryTimes(w.destRoot, w.uvmDi)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !go1.9
|
||||||
|
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
// Due to a bug in go1.8 and before, directory reparse points need to be skipped
|
||||||
|
// during filepath.Walk. This is fixed in go1.9
|
||||||
|
var shouldSkipDirectoryReparse = true
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
// Due to a bug in go1.8 and before, directory reparse points need to be skipped
|
||||||
|
// during filepath.Walk. This is fixed in go1.9
|
||||||
|
var shouldSkipDirectoryReparse = false
|
|
@ -0,0 +1,20 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// NameToGuid converts the given string into a GUID using the algorithm in the
|
||||||
|
// Host Compute Service, ensuring GUIDs generated with the same string are common
|
||||||
|
// across all clients.
|
||||||
|
func NameToGuid(name string) (id GUID, err error) {
|
||||||
|
title := "hcsshim::NameToGuid "
|
||||||
|
logrus.Debugf(title+"Name %s", name)
|
||||||
|
|
||||||
|
err = nameToGuid(name, &id)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "name=%s", name)
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var prepareLayerLock sync.Mutex
|
||||||
|
|
||||||
|
// PrepareLayer finds a mounted read-write layer matching layerId and enables the
|
||||||
|
// the filesystem filter for use on that layer. This requires the paths to all
|
||||||
|
// parent layers, and is necessary in order to view or interact with the layer
|
||||||
|
// as an actual filesystem (reading and writing files, creating directories, etc).
|
||||||
|
// Disabling the filter must be done via UnprepareLayer.
|
||||||
|
func PrepareLayer(info DriverInfo, layerId string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::PrepareLayer "
|
||||||
|
logrus.Debugf(title+"flavour %d layerId %s", info.Flavour, layerId)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This lock is a temporary workaround for a Windows bug. Only allowing one
|
||||||
|
// call to prepareLayer at a time vastly reduces the chance of a timeout.
|
||||||
|
prepareLayerLock.Lock()
|
||||||
|
defer prepareLayerLock.Unlock()
|
||||||
|
err = prepareLayer(&infop, layerId, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "layerId=%s flavour=%d", layerId, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded flavour=%d layerId=%s", info.Flavour, layerId)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,384 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerError is an error encountered in HCS
|
||||||
|
type process struct {
|
||||||
|
handleLock sync.RWMutex
|
||||||
|
handle hcsProcess
|
||||||
|
processID int
|
||||||
|
container *container
|
||||||
|
cachedPipes *cachedPipes
|
||||||
|
callbackNumber uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedPipes struct {
|
||||||
|
stdIn syscall.Handle
|
||||||
|
stdOut syscall.Handle
|
||||||
|
stdErr syscall.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
type processModifyRequest struct {
|
||||||
|
Operation string
|
||||||
|
ConsoleSize *consoleSize `json:",omitempty"`
|
||||||
|
CloseHandle *closeHandle `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleSize struct {
|
||||||
|
Height uint16
|
||||||
|
Width uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type closeHandle struct {
|
||||||
|
Handle string
|
||||||
|
}
|
||||||
|
|
||||||
|
type processStatus struct {
|
||||||
|
ProcessID uint32
|
||||||
|
Exited bool
|
||||||
|
ExitCode uint32
|
||||||
|
LastWaitResult int32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
stdIn string = "StdIn"
|
||||||
|
stdOut string = "StdOut"
|
||||||
|
stdErr string = "StdErr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
modifyConsoleSize string = "ConsoleSize"
|
||||||
|
modifyCloseHandle string = "CloseHandle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pid returns the process ID of the process within the container.
|
||||||
|
func (process *process) Pid() int {
|
||||||
|
return process.processID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill signals the process to terminate but does not wait for it to finish terminating.
|
||||||
|
func (process *process) Kill() error {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "Kill"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return makeProcessError(process, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err := hcsTerminateProcess(process.handle, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the process to exit.
|
||||||
|
func (process *process) Wait() error {
|
||||||
|
operation := "Wait"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitTimeout waits for the process to exit or the duration to elapse. It returns
|
||||||
|
// false if timeout occurs.
|
||||||
|
func (process *process) WaitTimeout(timeout time.Duration) error {
|
||||||
|
operation := "WaitTimeout"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExitCode returns the exit code of the process. The process must have
|
||||||
|
// already terminated.
|
||||||
|
func (process *process) ExitCode() (int, error) {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "ExitCode"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return 0, makeProcessError(process, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties, err := process.properties()
|
||||||
|
if err != nil {
|
||||||
|
return 0, makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if properties.Exited == false {
|
||||||
|
return 0, makeProcessError(process, operation, "", ErrInvalidProcessState)
|
||||||
|
}
|
||||||
|
|
||||||
|
if properties.LastWaitResult != 0 {
|
||||||
|
return 0, makeProcessError(process, operation, "", syscall.Errno(properties.LastWaitResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d exitCode=%d", process.processID, properties.ExitCode)
|
||||||
|
return int(properties.ExitCode), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeConsole resizes the console of the process.
|
||||||
|
func (process *process) ResizeConsole(width, height uint16) error {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "ResizeConsole"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return makeProcessError(process, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequest := processModifyRequest{
|
||||||
|
Operation: modifyConsoleSize,
|
||||||
|
ConsoleSize: &consoleSize{
|
||||||
|
Height: height,
|
||||||
|
Width: width,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestb, err := json.Marshal(modifyRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestStr := string(modifyRequestb)
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (process *process) properties() (*processStatus, error) {
|
||||||
|
operation := "properties"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
var (
|
||||||
|
resultp *uint16
|
||||||
|
propertiesp *uint16
|
||||||
|
)
|
||||||
|
err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if propertiesp == nil {
|
||||||
|
return nil, ErrUnexpectedValue
|
||||||
|
}
|
||||||
|
propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
|
||||||
|
|
||||||
|
properties := &processStatus{}
|
||||||
|
if err := json.Unmarshal(propertiesRaw, properties); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d, properties=%s", process.processID, propertiesRaw)
|
||||||
|
return properties, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
|
||||||
|
// these pipes does not close the underlying pipes; it should be possible to
|
||||||
|
// call this multiple times to get multiple interfaces.
|
||||||
|
func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "Stdio"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return nil, nil, nil, makeProcessError(process, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdIn, stdOut, stdErr syscall.Handle
|
||||||
|
|
||||||
|
if process.cachedPipes == nil {
|
||||||
|
var (
|
||||||
|
processInfo hcsProcessInformation
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
err := hcsGetProcessInfo(process.handle, &processInfo, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError
|
||||||
|
} else {
|
||||||
|
// Use cached pipes
|
||||||
|
stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr
|
||||||
|
|
||||||
|
// Invalidate the cache
|
||||||
|
process.cachedPipes = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return pipes[0], pipes[1], pipes[2], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseStdin closes the write side of the stdin pipe so that the process is
|
||||||
|
// notified on the read side that there is no more data in stdin.
|
||||||
|
func (process *process) CloseStdin() error {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "CloseStdin"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return makeProcessError(process, operation, "", ErrAlreadyClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequest := processModifyRequest{
|
||||||
|
Operation: modifyCloseHandle,
|
||||||
|
CloseHandle: &closeHandle{
|
||||||
|
Handle: stdIn,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestb, err := json.Marshal(modifyRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestStr := string(modifyRequestb)
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the process but does not kill
|
||||||
|
// or wait on it.
|
||||||
|
func (process *process) Close() error {
|
||||||
|
process.handleLock.Lock()
|
||||||
|
defer process.handleLock.Unlock()
|
||||||
|
operation := "Close"
|
||||||
|
title := "HCSShim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
// Don't double free this
|
||||||
|
if process.handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := process.unregisterCallback(); err != nil {
|
||||||
|
return makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hcsCloseProcess(process.handle); err != nil {
|
||||||
|
return makeProcessError(process, operation, "", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
process.handle = 0
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (process *process) registerCallback() error {
|
||||||
|
context := ¬ifcationWatcherContext{
|
||||||
|
channels: newChannels(),
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackNumber := nextCallback
|
||||||
|
nextCallback++
|
||||||
|
callbackMap[callbackNumber] = context
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
var callbackHandle hcsCallback
|
||||||
|
err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
context.handle = callbackHandle
|
||||||
|
process.callbackNumber = callbackNumber
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (process *process) unregisterCallback() error {
|
||||||
|
callbackNumber := process.callbackNumber
|
||||||
|
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
context := callbackMap[callbackNumber]
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
if context == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
handle := context.handle
|
||||||
|
|
||||||
|
if handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hcsUnregisterProcessCallback has its own syncronization
|
||||||
|
// to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
|
||||||
|
err := hcsUnregisterProcessCallback(handle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
closeChannels(context.channels)
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackMap[callbackNumber] = nil
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
handle = 0
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// ProcessBaseLayer post-processes a base layer that has had its files extracted.
|
||||||
|
// The files should have been extracted to <path>\Files.
|
||||||
|
func ProcessBaseLayer(path string) error {
|
||||||
|
err := processBaseImage(path)
|
||||||
|
if err != nil {
|
||||||
|
return &os.PathError{Op: "ProcessBaseLayer", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessUtilityVMImage post-processes a utility VM image that has had its files extracted.
|
||||||
|
// The files should have been extracted to <path>\Files.
|
||||||
|
func ProcessUtilityVMImage(path string) error {
|
||||||
|
err := processUtilityImage(path)
|
||||||
|
if err != nil {
|
||||||
|
return &os.PathError{Op: "ProcessUtilityVMImage", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,427 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
winio "github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile
|
||||||
|
//sys ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile
|
||||||
|
//sys rtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||||
|
//sys localAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc
|
||||||
|
//sys localFree(ptr uintptr) = kernel32.LocalFree
|
||||||
|
|
||||||
|
type ioStatusBlock struct {
|
||||||
|
Status, Information uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type objectAttributes struct {
|
||||||
|
Length uintptr
|
||||||
|
RootDirectory uintptr
|
||||||
|
ObjectName uintptr
|
||||||
|
Attributes uintptr
|
||||||
|
SecurityDescriptor uintptr
|
||||||
|
SecurityQoS uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type unicodeString struct {
|
||||||
|
Length uint16
|
||||||
|
MaximumLength uint16
|
||||||
|
Buffer uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileLinkInformation struct {
|
||||||
|
ReplaceIfExists bool
|
||||||
|
RootDirectory uintptr
|
||||||
|
FileNameLength uint32
|
||||||
|
FileName [1]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileDispositionInformationEx struct {
|
||||||
|
Flags uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_FileLinkInformation = 11
|
||||||
|
_FileDispositionInformationEx = 64
|
||||||
|
|
||||||
|
_FILE_READ_ATTRIBUTES = 0x0080
|
||||||
|
_FILE_WRITE_ATTRIBUTES = 0x0100
|
||||||
|
_DELETE = 0x10000
|
||||||
|
|
||||||
|
_FILE_OPEN = 1
|
||||||
|
_FILE_CREATE = 2
|
||||||
|
|
||||||
|
_FILE_DIRECTORY_FILE = 0x00000001
|
||||||
|
_FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020
|
||||||
|
_FILE_DELETE_ON_CLOSE = 0x00001000
|
||||||
|
_FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000
|
||||||
|
_FILE_OPEN_REPARSE_POINT = 0x00200000
|
||||||
|
|
||||||
|
_FILE_DISPOSITION_DELETE = 0x00000001
|
||||||
|
|
||||||
|
_OBJ_DONT_REPARSE = 0x1000
|
||||||
|
|
||||||
|
_STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B
|
||||||
|
)
|
||||||
|
|
||||||
|
func openRoot(path string) (*os.File, error) {
|
||||||
|
longpath, err := makeLongAbsPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ntRelativePath(path string) ([]uint16, error) {
|
||||||
|
path = filepath.Clean(path)
|
||||||
|
if strings.Contains(":", path) {
|
||||||
|
// Since alternate data streams must follow the file they
|
||||||
|
// are attached to, finding one here (out of order) is invalid.
|
||||||
|
return nil, errors.New("path contains invalid character `:`")
|
||||||
|
}
|
||||||
|
fspath := filepath.FromSlash(path)
|
||||||
|
if len(fspath) > 0 && fspath[0] == '\\' {
|
||||||
|
return nil, errors.New("expected relative path")
|
||||||
|
}
|
||||||
|
|
||||||
|
path16 := utf16.Encode(([]rune)(fspath))
|
||||||
|
if len(path16) > 32767 {
|
||||||
|
return nil, syscall.ENAMETOOLONG
|
||||||
|
}
|
||||||
|
|
||||||
|
return path16, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// openRelativeInternal opens a relative path from the given root, failing if
|
||||||
|
// any of the intermediate path components are reparse points.
|
||||||
|
func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
|
||||||
|
var (
|
||||||
|
h uintptr
|
||||||
|
iosb ioStatusBlock
|
||||||
|
oa objectAttributes
|
||||||
|
)
|
||||||
|
|
||||||
|
path16, err := ntRelativePath(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if root == nil || root.Fd() == 0 {
|
||||||
|
return nil, errors.New("missing root directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
upathBuffer := localAlloc(0, int(unsafe.Sizeof(unicodeString{}))+len(path16)*2)
|
||||||
|
defer localFree(upathBuffer)
|
||||||
|
|
||||||
|
upath := (*unicodeString)(unsafe.Pointer(upathBuffer))
|
||||||
|
upath.Length = uint16(len(path16) * 2)
|
||||||
|
upath.MaximumLength = upath.Length
|
||||||
|
upath.Buffer = upathBuffer + unsafe.Sizeof(*upath)
|
||||||
|
copy((*[32768]uint16)(unsafe.Pointer(upath.Buffer))[:], path16)
|
||||||
|
|
||||||
|
oa.Length = unsafe.Sizeof(oa)
|
||||||
|
oa.ObjectName = upathBuffer
|
||||||
|
oa.RootDirectory = uintptr(root.Fd())
|
||||||
|
oa.Attributes = _OBJ_DONT_REPARSE
|
||||||
|
status := ntCreateFile(
|
||||||
|
&h,
|
||||||
|
accessMask|syscall.SYNCHRONIZE,
|
||||||
|
&oa,
|
||||||
|
&iosb,
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
|
shareFlags,
|
||||||
|
createDisposition,
|
||||||
|
_FILE_OPEN_FOR_BACKUP_INTENT|_FILE_SYNCHRONOUS_IO_NONALERT|flags,
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if status != 0 {
|
||||||
|
return nil, rtlNtStatusToDosError(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath, err := makeLongAbsPath(filepath.Join(root.Name(), path))
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(syscall.Handle(h))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.NewFile(h, fullPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// openRelative opens a relative path from the given root, failing if
|
||||||
|
// any of the intermediate path components are reparse points.
|
||||||
|
func openRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
|
||||||
|
f, err := openRelativeInternal(path, root, accessMask, shareFlags, createDisposition, flags)
|
||||||
|
if err != nil {
|
||||||
|
err = &os.PathError{Op: "open", Path: filepath.Join(root.Name(), path), Err: err}
|
||||||
|
}
|
||||||
|
return f, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// linkRelative creates a hard link from oldname to newname (relative to oldroot
|
||||||
|
// and newroot), failing if any of the intermediate path components are reparse
|
||||||
|
// points.
|
||||||
|
func linkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error {
|
||||||
|
// Open the old file.
|
||||||
|
oldf, err := openRelativeInternal(
|
||||||
|
oldname,
|
||||||
|
oldroot,
|
||||||
|
syscall.FILE_WRITE_ATTRIBUTES,
|
||||||
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||||
|
_FILE_OPEN,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return &os.LinkError{Op: "link", Old: filepath.Join(oldroot.Name(), oldname), New: filepath.Join(newroot.Name(), newname), Err: err}
|
||||||
|
}
|
||||||
|
defer oldf.Close()
|
||||||
|
|
||||||
|
// Open the parent of the new file.
|
||||||
|
var parent *os.File
|
||||||
|
parentPath := filepath.Dir(newname)
|
||||||
|
if parentPath != "." {
|
||||||
|
parent, err = openRelativeInternal(
|
||||||
|
parentPath,
|
||||||
|
newroot,
|
||||||
|
syscall.GENERIC_READ,
|
||||||
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||||
|
_FILE_OPEN,
|
||||||
|
_FILE_DIRECTORY_FILE)
|
||||||
|
if err != nil {
|
||||||
|
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err}
|
||||||
|
}
|
||||||
|
defer parent.Close()
|
||||||
|
|
||||||
|
fi, err := winio.GetFileBasicInfo(parent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
|
||||||
|
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: rtlNtStatusToDosError(_STATUS_REPARSE_POINT_ENCOUNTERED)}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
parent = newroot
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue an NT call to create the link. This will be safe because NT will
|
||||||
|
// not open any more directories to create the link, so it cannot walk any
|
||||||
|
// more reparse points.
|
||||||
|
newbase := filepath.Base(newname)
|
||||||
|
newbase16, err := ntRelativePath(newbase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
size := int(unsafe.Offsetof(fileLinkInformation{}.FileName)) + len(newbase16)*2
|
||||||
|
linkinfoBuffer := localAlloc(0, size)
|
||||||
|
defer localFree(linkinfoBuffer)
|
||||||
|
linkinfo := (*fileLinkInformation)(unsafe.Pointer(linkinfoBuffer))
|
||||||
|
linkinfo.RootDirectory = parent.Fd()
|
||||||
|
linkinfo.FileNameLength = uint32(len(newbase16) * 2)
|
||||||
|
copy((*[32768]uint16)(unsafe.Pointer(&linkinfo.FileName[0]))[:], newbase16)
|
||||||
|
|
||||||
|
var iosb ioStatusBlock
|
||||||
|
status := ntSetInformationFile(
|
||||||
|
oldf.Fd(),
|
||||||
|
&iosb,
|
||||||
|
linkinfoBuffer,
|
||||||
|
uint32(size),
|
||||||
|
_FileLinkInformation,
|
||||||
|
)
|
||||||
|
if status != 0 {
|
||||||
|
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: rtlNtStatusToDosError(status)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteOnClose marks a file to be deleted when the handle is closed.
|
||||||
|
func deleteOnClose(f *os.File) error {
|
||||||
|
disposition := fileDispositionInformationEx{Flags: _FILE_DISPOSITION_DELETE}
|
||||||
|
var iosb ioStatusBlock
|
||||||
|
status := ntSetInformationFile(
|
||||||
|
f.Fd(),
|
||||||
|
&iosb,
|
||||||
|
uintptr(unsafe.Pointer(&disposition)),
|
||||||
|
uint32(unsafe.Sizeof(disposition)),
|
||||||
|
_FileDispositionInformationEx,
|
||||||
|
)
|
||||||
|
if status != 0 {
|
||||||
|
return rtlNtStatusToDosError(status)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearReadOnly clears the readonly attribute on a file.
|
||||||
|
func clearReadOnly(f *os.File) error {
|
||||||
|
bi, err := winio.GetFileBasicInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sbi := winio.FileBasicInfo{
|
||||||
|
FileAttributes: bi.FileAttributes &^ syscall.FILE_ATTRIBUTE_READONLY,
|
||||||
|
}
|
||||||
|
if sbi.FileAttributes == 0 {
|
||||||
|
sbi.FileAttributes = syscall.FILE_ATTRIBUTE_NORMAL
|
||||||
|
}
|
||||||
|
return winio.SetFileBasicInfo(f, &sbi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeRelative removes a file or directory relative to a root, failing if any
|
||||||
|
// intermediate path components are reparse points.
|
||||||
|
func removeRelative(path string, root *os.File) error {
|
||||||
|
f, err := openRelativeInternal(
|
||||||
|
path,
|
||||||
|
root,
|
||||||
|
_FILE_READ_ATTRIBUTES|_FILE_WRITE_ATTRIBUTES|_DELETE,
|
||||||
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||||
|
_FILE_OPEN,
|
||||||
|
_FILE_OPEN_REPARSE_POINT)
|
||||||
|
if err == nil {
|
||||||
|
defer f.Close()
|
||||||
|
err = deleteOnClose(f)
|
||||||
|
if err == syscall.ERROR_ACCESS_DENIED {
|
||||||
|
// Maybe the file is marked readonly. Clear the bit and retry.
|
||||||
|
clearReadOnly(f)
|
||||||
|
err = deleteOnClose(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return &os.PathError{Op: "remove", Path: filepath.Join(root.Name(), path), Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeAllRelative removes a directory tree relative to a root, failing if any
|
||||||
|
// intermediate path components are reparse points.
|
||||||
|
func removeAllRelative(path string, root *os.File) error {
|
||||||
|
fi, err := lstatRelative(path, root)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fileAttributes := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||||
|
if fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 || fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||||
|
// If this is a reparse point, it can't have children. Simple remove will do.
|
||||||
|
err := removeRelative(path, root)
|
||||||
|
if err == nil || os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is necessary to use os.Open as Readdirnames does not work with
|
||||||
|
// openRelative. This is safe because the above lstatrelative fails
|
||||||
|
// if the target is outside the root, and we know this is not a
|
||||||
|
// symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check.
|
||||||
|
fd, err := os.Open(filepath.Join(root.Name(), path))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// Race. It was deleted between the Lstat and Open.
|
||||||
|
// Return nil per RemoveAll's docs.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove contents & return first error.
|
||||||
|
for {
|
||||||
|
names, err1 := fd.Readdirnames(100)
|
||||||
|
for _, name := range names {
|
||||||
|
err1 := removeAllRelative(path+string(os.PathSeparator)+name, root)
|
||||||
|
if err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err1 == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// If Readdirnames returned an error, use it.
|
||||||
|
if err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
if len(names) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
// Remove directory.
|
||||||
|
err1 := removeRelative(path, root)
|
||||||
|
if err1 == nil || os.IsNotExist(err1) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// mkdirRelative creates a directory relative to a root, failing if any
|
||||||
|
// intermediate path components are reparse points.
|
||||||
|
func mkdirRelative(path string, root *os.File) error {
|
||||||
|
f, err := openRelativeInternal(
|
||||||
|
path,
|
||||||
|
root,
|
||||||
|
0,
|
||||||
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||||
|
_FILE_CREATE,
|
||||||
|
_FILE_DIRECTORY_FILE)
|
||||||
|
if err == nil {
|
||||||
|
f.Close()
|
||||||
|
} else {
|
||||||
|
err = &os.PathError{Op: "mkdir", Path: filepath.Join(root.Name(), path), Err: err}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// lstatRelative performs a stat operation on a file relative to a root, failing
|
||||||
|
// if any intermediate path components are reparse points.
|
||||||
|
func lstatRelative(path string, root *os.File) (os.FileInfo, error) {
|
||||||
|
f, err := openRelativeInternal(
|
||||||
|
path,
|
||||||
|
root,
|
||||||
|
_FILE_READ_ATTRIBUTES,
|
||||||
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||||
|
_FILE_OPEN,
|
||||||
|
_FILE_OPEN_REPARSE_POINT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err}
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return f.Stat()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureNotReparsePointRelative validates that a given file (relative to a
|
||||||
|
// root) and all intermediate path components are not a reparse points.
|
||||||
|
func ensureNotReparsePointRelative(path string, root *os.File) error {
|
||||||
|
// Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT.
|
||||||
|
f, err := openRelative(
|
||||||
|
path,
|
||||||
|
root,
|
||||||
|
0,
|
||||||
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||||
|
_FILE_OPEN,
|
||||||
|
0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// UnprepareLayer disables the filesystem filter for the read-write layer with
|
||||||
|
// the given id.
|
||||||
|
func UnprepareLayer(info DriverInfo, layerId string) error {
|
||||||
|
title := "hcsshim::UnprepareLayer "
|
||||||
|
logrus.Debugf(title+"flavour %d layerId %s", info.Flavour, layerId)
|
||||||
|
|
||||||
|
// Convert info to API calling convention
|
||||||
|
infop, err := convertDriverInfo(info)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = unprepareLayer(&infop, layerId)
|
||||||
|
if err != nil {
|
||||||
|
err = makeErrorf(err, title, "layerId=%s flavour=%d", layerId, info.Flavour)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded flavour %d layerId=%s", info.Flavour, layerId)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
// makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles
|
||||||
|
// if there is an error.
|
||||||
|
func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) {
|
||||||
|
fs := make([]io.ReadWriteCloser, len(hs))
|
||||||
|
for i, h := range hs {
|
||||||
|
if h != syscall.Handle(0) {
|
||||||
|
if err == nil {
|
||||||
|
fs[i], err = winio.MakeOpenFile(h)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
for _, f := range fs {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return fs, nil
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
// IsTP4 returns whether the currently running Windows build is at least TP4.
|
||||||
|
func IsTP4() bool {
|
||||||
|
// HNSCall was not present in TP4
|
||||||
|
return procHNSCall.Find() != nil
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error {
|
||||||
|
err = processHcsResult(err, resultp)
|
||||||
|
if IsPending(err) {
|
||||||
|
return waitForNotification(callbackNumber, expectedNotification, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForNotification(callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error {
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
channels := callbackMap[callbackNumber].channels
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
expectedChannel := channels[expectedNotification]
|
||||||
|
if expectedChannel == nil {
|
||||||
|
logrus.Errorf("unknown notification type in waitForNotification %x", expectedNotification)
|
||||||
|
return ErrInvalidNotificationType
|
||||||
|
}
|
||||||
|
|
||||||
|
var c <-chan time.Time
|
||||||
|
if timeout != nil {
|
||||||
|
timer := time.NewTimer(*timeout)
|
||||||
|
c = timer.C
|
||||||
|
defer timer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err, ok := <-expectedChannel:
|
||||||
|
if !ok {
|
||||||
|
return ErrHandleClose
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
case err, ok := <-channels[hcsNotificationSystemExited]:
|
||||||
|
if !ok {
|
||||||
|
return ErrHandleClose
|
||||||
|
}
|
||||||
|
// If the expected notification is hcsNotificationSystemExited which of the two selects
|
||||||
|
// chosen is random. Return the raw error if hcsNotificationSystemExited is expected
|
||||||
|
if channels[hcsNotificationSystemExited] == expectedChannel {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ErrUnexpectedContainerExit
|
||||||
|
case _, ok := <-channels[hcsNotificationServiceDisconnect]:
|
||||||
|
if !ok {
|
||||||
|
return ErrHandleClose
|
||||||
|
}
|
||||||
|
// hcsNotificationServiceDisconnect should never be an expected notification
|
||||||
|
// it does not need the same handling as hcsNotificationSystemExited
|
||||||
|
return ErrUnexpectedProcessAbort
|
||||||
|
case <-c:
|
||||||
|
return ErrTimeout
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Alex Saskevich
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,496 @@
|
||||||
|
govalidator
|
||||||
|
===========
|
||||||
|
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
||||||
|
[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) [![Go Report Card](https://goreportcard.com/badge/github.com/asaskevich/govalidator)](https://goreportcard.com/report/github.com/asaskevich/govalidator) [![GoSearch](http://go-search.org/badge?id=github.com%2Fasaskevich%2Fgovalidator)](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) [![Backers on Open Collective](https://opencollective.com/govalidator/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/govalidator/sponsors/badge.svg)](#sponsors) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_shield)
|
||||||
|
|
||||||
|
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
|
||||||
|
|
||||||
|
#### Installation
|
||||||
|
Make sure that Go is installed on your computer.
|
||||||
|
Type the following command in your terminal:
|
||||||
|
|
||||||
|
go get github.com/asaskevich/govalidator
|
||||||
|
|
||||||
|
or you can get specified release of the package with `gopkg.in`:
|
||||||
|
|
||||||
|
go get gopkg.in/asaskevich/govalidator.v4
|
||||||
|
|
||||||
|
After it the package is ready to use.
|
||||||
|
|
||||||
|
|
||||||
|
#### Import package in your project
|
||||||
|
Add following line in your `*.go` file:
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
```
|
||||||
|
If you are unhappy to use long `govalidator`, you can do something like this:
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
valid "github.com/asaskevich/govalidator"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Activate behavior to require all fields have a validation tag by default
|
||||||
|
`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function.
|
||||||
|
|
||||||
|
`SetNilPtrAllowedByRequired` causes validation to pass when struct fields marked by `required` are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between `nil` and `zero value` state can use this. If disabled, both `nil` and `zero` values cause validation errors.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
govalidator.SetFieldsRequiredByDefault(true)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's some code to explain it:
|
||||||
|
```go
|
||||||
|
// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
||||||
|
type exampleStruct struct {
|
||||||
|
Name string ``
|
||||||
|
Email string `valid:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// this, however, will only fail when Email is empty or an invalid email address:
|
||||||
|
type exampleStruct2 struct {
|
||||||
|
Name string `valid:"-"`
|
||||||
|
Email string `valid:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
||||||
|
type exampleStruct2 struct {
|
||||||
|
Name string `valid:"-"`
|
||||||
|
Email string `valid:"email,optional"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123))
|
||||||
|
##### Custom validator function signature
|
||||||
|
A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible.
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
// old signature
|
||||||
|
func(i interface{}) bool
|
||||||
|
|
||||||
|
// new signature
|
||||||
|
func(i interface{}, o interface{}) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Adding a custom validator
|
||||||
|
This was changed to prevent data races when accessing custom validators.
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
// before
|
||||||
|
govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
|
||||||
|
// after
|
||||||
|
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||||
|
// ...
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### List of functions:
|
||||||
|
```go
|
||||||
|
func Abs(value float64) float64
|
||||||
|
func BlackList(str, chars string) string
|
||||||
|
func ByteLength(str string, params ...string) bool
|
||||||
|
func CamelCaseToUnderscore(str string) string
|
||||||
|
func Contains(str, substring string) bool
|
||||||
|
func Count(array []interface{}, iterator ConditionIterator) int
|
||||||
|
func Each(array []interface{}, iterator Iterator)
|
||||||
|
func ErrorByField(e error, field string) string
|
||||||
|
func ErrorsByField(e error) map[string]string
|
||||||
|
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
|
||||||
|
func Find(array []interface{}, iterator ConditionIterator) interface{}
|
||||||
|
func GetLine(s string, index int) (string, error)
|
||||||
|
func GetLines(s string) []string
|
||||||
|
func InRange(value, left, right float64) bool
|
||||||
|
func IsASCII(str string) bool
|
||||||
|
func IsAlpha(str string) bool
|
||||||
|
func IsAlphanumeric(str string) bool
|
||||||
|
func IsBase64(str string) bool
|
||||||
|
func IsByteLength(str string, min, max int) bool
|
||||||
|
func IsCIDR(str string) bool
|
||||||
|
func IsCreditCard(str string) bool
|
||||||
|
func IsDNSName(str string) bool
|
||||||
|
func IsDataURI(str string) bool
|
||||||
|
func IsDialString(str string) bool
|
||||||
|
func IsDivisibleBy(str, num string) bool
|
||||||
|
func IsEmail(str string) bool
|
||||||
|
func IsFilePath(str string) (bool, int)
|
||||||
|
func IsFloat(str string) bool
|
||||||
|
func IsFullWidth(str string) bool
|
||||||
|
func IsHalfWidth(str string) bool
|
||||||
|
func IsHexadecimal(str string) bool
|
||||||
|
func IsHexcolor(str string) bool
|
||||||
|
func IsHost(str string) bool
|
||||||
|
func IsIP(str string) bool
|
||||||
|
func IsIPv4(str string) bool
|
||||||
|
func IsIPv6(str string) bool
|
||||||
|
func IsISBN(str string, version int) bool
|
||||||
|
func IsISBN10(str string) bool
|
||||||
|
func IsISBN13(str string) bool
|
||||||
|
func IsISO3166Alpha2(str string) bool
|
||||||
|
func IsISO3166Alpha3(str string) bool
|
||||||
|
func IsISO693Alpha2(str string) bool
|
||||||
|
func IsISO693Alpha3b(str string) bool
|
||||||
|
func IsISO4217(str string) bool
|
||||||
|
func IsIn(str string, params ...string) bool
|
||||||
|
func IsInt(str string) bool
|
||||||
|
func IsJSON(str string) bool
|
||||||
|
func IsLatitude(str string) bool
|
||||||
|
func IsLongitude(str string) bool
|
||||||
|
func IsLowerCase(str string) bool
|
||||||
|
func IsMAC(str string) bool
|
||||||
|
func IsMongoID(str string) bool
|
||||||
|
func IsMultibyte(str string) bool
|
||||||
|
func IsNatural(value float64) bool
|
||||||
|
func IsNegative(value float64) bool
|
||||||
|
func IsNonNegative(value float64) bool
|
||||||
|
func IsNonPositive(value float64) bool
|
||||||
|
func IsNull(str string) bool
|
||||||
|
func IsNumeric(str string) bool
|
||||||
|
func IsPort(str string) bool
|
||||||
|
func IsPositive(value float64) bool
|
||||||
|
func IsPrintableASCII(str string) bool
|
||||||
|
func IsRFC3339(str string) bool
|
||||||
|
func IsRFC3339WithoutZone(str string) bool
|
||||||
|
func IsRGBcolor(str string) bool
|
||||||
|
func IsRequestURI(rawurl string) bool
|
||||||
|
func IsRequestURL(rawurl string) bool
|
||||||
|
func IsSSN(str string) bool
|
||||||
|
func IsSemver(str string) bool
|
||||||
|
func IsTime(str string, format string) bool
|
||||||
|
func IsURL(str string) bool
|
||||||
|
func IsUTFDigit(str string) bool
|
||||||
|
func IsUTFLetter(str string) bool
|
||||||
|
func IsUTFLetterNumeric(str string) bool
|
||||||
|
func IsUTFNumeric(str string) bool
|
||||||
|
func IsUUID(str string) bool
|
||||||
|
func IsUUIDv3(str string) bool
|
||||||
|
func IsUUIDv4(str string) bool
|
||||||
|
func IsUUIDv5(str string) bool
|
||||||
|
func IsUpperCase(str string) bool
|
||||||
|
func IsVariableWidth(str string) bool
|
||||||
|
func IsWhole(value float64) bool
|
||||||
|
func LeftTrim(str, chars string) string
|
||||||
|
func Map(array []interface{}, iterator ResultIterator) []interface{}
|
||||||
|
func Matches(str, pattern string) bool
|
||||||
|
func NormalizeEmail(str string) (string, error)
|
||||||
|
func PadBoth(str string, padStr string, padLen int) string
|
||||||
|
func PadLeft(str string, padStr string, padLen int) string
|
||||||
|
func PadRight(str string, padStr string, padLen int) string
|
||||||
|
func Range(str string, params ...string) bool
|
||||||
|
func RemoveTags(s string) string
|
||||||
|
func ReplacePattern(str, pattern, replace string) string
|
||||||
|
func Reverse(s string) string
|
||||||
|
func RightTrim(str, chars string) string
|
||||||
|
func RuneLength(str string, params ...string) bool
|
||||||
|
func SafeFileName(str string) string
|
||||||
|
func SetFieldsRequiredByDefault(value bool)
|
||||||
|
func Sign(value float64) float64
|
||||||
|
func StringLength(str string, params ...string) bool
|
||||||
|
func StringMatches(s string, params ...string) bool
|
||||||
|
func StripLow(str string, keepNewLines bool) string
|
||||||
|
func ToBoolean(str string) (bool, error)
|
||||||
|
func ToFloat(str string) (float64, error)
|
||||||
|
func ToInt(str string) (int64, error)
|
||||||
|
func ToJSON(obj interface{}) (string, error)
|
||||||
|
func ToString(obj interface{}) string
|
||||||
|
func Trim(str, chars string) string
|
||||||
|
func Truncate(str string, length int, ending string) string
|
||||||
|
func UnderscoreToCamelCase(s string) string
|
||||||
|
func ValidateStruct(s interface{}) (bool, error)
|
||||||
|
func WhiteList(str, chars string) string
|
||||||
|
type ConditionIterator
|
||||||
|
type CustomTypeValidator
|
||||||
|
type Error
|
||||||
|
func (e Error) Error() string
|
||||||
|
type Errors
|
||||||
|
func (es Errors) Error() string
|
||||||
|
func (es Errors) Errors() []error
|
||||||
|
type ISO3166Entry
|
||||||
|
type Iterator
|
||||||
|
type ParamValidator
|
||||||
|
type ResultIterator
|
||||||
|
type UnsupportedTypeError
|
||||||
|
func (e *UnsupportedTypeError) Error() string
|
||||||
|
type Validator
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
###### IsURL
|
||||||
|
```go
|
||||||
|
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
|
||||||
|
```
|
||||||
|
###### ToString
|
||||||
|
```go
|
||||||
|
type User struct {
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
}
|
||||||
|
|
||||||
|
str := govalidator.ToString(&User{"John", "Juan"})
|
||||||
|
println(str)
|
||||||
|
```
|
||||||
|
###### Each, Map, Filter, Count for slices
|
||||||
|
Each iterates over the slice/array and calls Iterator for every item
|
||||||
|
```go
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn govalidator.Iterator = func(value interface{}, index int) {
|
||||||
|
println(value.(int))
|
||||||
|
}
|
||||||
|
govalidator.Each(data, fn)
|
||||||
|
```
|
||||||
|
```go
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5}
|
||||||
|
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
|
||||||
|
return value.(int) * 3
|
||||||
|
}
|
||||||
|
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
||||||
|
```
|
||||||
|
```go
|
||||||
|
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
|
||||||
|
return value.(int)%2 == 0
|
||||||
|
}
|
||||||
|
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
||||||
|
_ = govalidator.Count(data, fn) // result = 5
|
||||||
|
```
|
||||||
|
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2)
|
||||||
|
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this:
|
||||||
|
```go
|
||||||
|
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||||
|
return str == "duck"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
For completely custom validators (interface-based), see below.
|
||||||
|
|
||||||
|
Here is a list of available validators for struct fields (validator - used function):
|
||||||
|
```go
|
||||||
|
"email": IsEmail,
|
||||||
|
"url": IsURL,
|
||||||
|
"dialstring": IsDialString,
|
||||||
|
"requrl": IsRequestURL,
|
||||||
|
"requri": IsRequestURI,
|
||||||
|
"alpha": IsAlpha,
|
||||||
|
"utfletter": IsUTFLetter,
|
||||||
|
"alphanum": IsAlphanumeric,
|
||||||
|
"utfletternum": IsUTFLetterNumeric,
|
||||||
|
"numeric": IsNumeric,
|
||||||
|
"utfnumeric": IsUTFNumeric,
|
||||||
|
"utfdigit": IsUTFDigit,
|
||||||
|
"hexadecimal": IsHexadecimal,
|
||||||
|
"hexcolor": IsHexcolor,
|
||||||
|
"rgbcolor": IsRGBcolor,
|
||||||
|
"lowercase": IsLowerCase,
|
||||||
|
"uppercase": IsUpperCase,
|
||||||
|
"int": IsInt,
|
||||||
|
"float": IsFloat,
|
||||||
|
"null": IsNull,
|
||||||
|
"uuid": IsUUID,
|
||||||
|
"uuidv3": IsUUIDv3,
|
||||||
|
"uuidv4": IsUUIDv4,
|
||||||
|
"uuidv5": IsUUIDv5,
|
||||||
|
"creditcard": IsCreditCard,
|
||||||
|
"isbn10": IsISBN10,
|
||||||
|
"isbn13": IsISBN13,
|
||||||
|
"json": IsJSON,
|
||||||
|
"multibyte": IsMultibyte,
|
||||||
|
"ascii": IsASCII,
|
||||||
|
"printableascii": IsPrintableASCII,
|
||||||
|
"fullwidth": IsFullWidth,
|
||||||
|
"halfwidth": IsHalfWidth,
|
||||||
|
"variablewidth": IsVariableWidth,
|
||||||
|
"base64": IsBase64,
|
||||||
|
"datauri": IsDataURI,
|
||||||
|
"ip": IsIP,
|
||||||
|
"port": IsPort,
|
||||||
|
"ipv4": IsIPv4,
|
||||||
|
"ipv6": IsIPv6,
|
||||||
|
"dns": IsDNSName,
|
||||||
|
"host": IsHost,
|
||||||
|
"mac": IsMAC,
|
||||||
|
"latitude": IsLatitude,
|
||||||
|
"longitude": IsLongitude,
|
||||||
|
"ssn": IsSSN,
|
||||||
|
"semver": IsSemver,
|
||||||
|
"rfc3339": IsRFC3339,
|
||||||
|
"rfc3339WithoutZone": IsRFC3339WithoutZone,
|
||||||
|
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||||
|
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||||
|
```
|
||||||
|
Validators with parameters
|
||||||
|
|
||||||
|
```go
|
||||||
|
"range(min|max)": Range,
|
||||||
|
"length(min|max)": ByteLength,
|
||||||
|
"runelength(min|max)": RuneLength,
|
||||||
|
"matches(pattern)": StringMatches,
|
||||||
|
"in(string1|string2|...|stringN)": IsIn,
|
||||||
|
```
|
||||||
|
|
||||||
|
And here is small example of usage:
|
||||||
|
```go
|
||||||
|
type Post struct {
|
||||||
|
Title string `valid:"alphanum,required"`
|
||||||
|
Message string `valid:"duck,ascii"`
|
||||||
|
AuthorIP string `valid:"ipv4"`
|
||||||
|
Date string `valid:"-"`
|
||||||
|
}
|
||||||
|
post := &Post{
|
||||||
|
Title: "My Example Post",
|
||||||
|
Message: "duck",
|
||||||
|
AuthorIP: "123.234.54.3",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add your own struct validation tags
|
||||||
|
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||||
|
return str == "duck"
|
||||||
|
})
|
||||||
|
|
||||||
|
result, err := govalidator.ValidateStruct(post)
|
||||||
|
if err != nil {
|
||||||
|
println("error: " + err.Error())
|
||||||
|
}
|
||||||
|
println(result)
|
||||||
|
```
|
||||||
|
###### WhiteList
|
||||||
|
```go
|
||||||
|
// Remove all characters from string ignoring characters between "a" and "z"
|
||||||
|
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
||||||
|
```
|
||||||
|
|
||||||
|
###### Custom validation functions
|
||||||
|
Custom validation using your own domain specific validators is also available - here's an example of how to use it:
|
||||||
|
```go
|
||||||
|
import "github.com/asaskevich/govalidator"
|
||||||
|
|
||||||
|
type CustomByteArray [6]byte // custom types are supported and can be validated
|
||||||
|
|
||||||
|
type StructWithCustomByteArray struct {
|
||||||
|
ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
|
||||||
|
Email string `valid:"email"`
|
||||||
|
CustomMinLength int `valid:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||||
|
switch v := context.(type) { // you can type switch on the context interface being validated
|
||||||
|
case StructWithCustomByteArray:
|
||||||
|
// you can check and validate against some other field in the context,
|
||||||
|
// return early or not validate against the context at all – your choice
|
||||||
|
case SomeOtherType:
|
||||||
|
// ...
|
||||||
|
default:
|
||||||
|
// expecting some other type? Throw/panic here or continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := i.(type) { // type switch on the struct field being validated
|
||||||
|
case CustomByteArray:
|
||||||
|
for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
|
||||||
|
if e != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}))
|
||||||
|
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||||
|
switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
|
||||||
|
case StructWithCustomByteArray:
|
||||||
|
return len(v.ID) >= v.CustomMinLength
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
###### Custom error messages
|
||||||
|
Custom error messages are supported via annotations by adding the `~` separator - here's an example of how to use it:
|
||||||
|
```go
|
||||||
|
type Ticket struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
FirstName string `json:"firstname" valid:"required~First name is blank"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Notes
|
||||||
|
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
|
||||||
|
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
|
||||||
|
|
||||||
|
#### Support
|
||||||
|
If you do have a contribution to the package, feel free to create a Pull Request or an Issue.
|
||||||
|
|
||||||
|
#### What to contribute
|
||||||
|
If you don't know what to do, there are some features and functions that need to be done
|
||||||
|
|
||||||
|
- [ ] Refactor code
|
||||||
|
- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check
|
||||||
|
- [ ] Create actual list of contributors and projects that currently using this package
|
||||||
|
- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues)
|
||||||
|
- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions)
|
||||||
|
- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new
|
||||||
|
- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc
|
||||||
|
- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224)
|
||||||
|
- [ ] Implement fuzzing testing
|
||||||
|
- [ ] Implement some struct/map/array utilities
|
||||||
|
- [ ] Implement map/array validation
|
||||||
|
- [ ] Implement benchmarking
|
||||||
|
- [ ] Implement batch of examples
|
||||||
|
- [ ] Look at forks for new features and fixes
|
||||||
|
|
||||||
|
#### Advice
|
||||||
|
Feel free to create what you want, but keep in mind when you implement new features:
|
||||||
|
- Code must be clear and readable, names of variables/constants clearly describes what they are doing
|
||||||
|
- Public functions must be documented and described in source file and added to README.md to the list of available functions
|
||||||
|
- There are must be unit-tests for any new functions and improvements
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
||||||
|
|
||||||
|
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
|
||||||
|
* [Daniel Lohse](https://github.com/annismckenzie)
|
||||||
|
* [Attila Oláh](https://github.com/attilaolah)
|
||||||
|
* [Daniel Korner](https://github.com/Dadie)
|
||||||
|
* [Steven Wilkin](https://github.com/stevenwilkin)
|
||||||
|
* [Deiwin Sarjas](https://github.com/deiwin)
|
||||||
|
* [Noah Shibley](https://github.com/slugmobile)
|
||||||
|
* [Nathan Davies](https://github.com/nathj07)
|
||||||
|
* [Matt Sanford](https://github.com/mzsanford)
|
||||||
|
* [Simon ccl1115](https://github.com/ccl1115)
|
||||||
|
|
||||||
|
<a href="graphs/contributors"><img src="https://opencollective.com/govalidator/contributors.svg?width=890" /></a>
|
||||||
|
|
||||||
|
|
||||||
|
### Backers
|
||||||
|
|
||||||
|
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/govalidator#backer)]
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/govalidator#backers" target="_blank"><img src="https://opencollective.com/govalidator/backers.svg?width=890"></a>
|
||||||
|
|
||||||
|
|
||||||
|
### Sponsors
|
||||||
|
|
||||||
|
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/govalidator#sponsor)]
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/0/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/0/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/1/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/1/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/2/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/2/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/3/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/3/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/4/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/4/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/5/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/5/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/6/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/6/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/7/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/7/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/8/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/8/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/govalidator/sponsor/9/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/9/avatar.svg"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_large)
|
|
@ -0,0 +1,58 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
// Iterator is the function that accepts element of slice/array and its index
|
||||||
|
type Iterator func(interface{}, int)
|
||||||
|
|
||||||
|
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
|
||||||
|
type ResultIterator func(interface{}, int) interface{}
|
||||||
|
|
||||||
|
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
|
||||||
|
type ConditionIterator func(interface{}, int) bool
|
||||||
|
|
||||||
|
// Each iterates over the slice and apply Iterator to every item
|
||||||
|
func Each(array []interface{}, iterator Iterator) {
|
||||||
|
for index, data := range array {
|
||||||
|
iterator(data, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
||||||
|
func Map(array []interface{}, iterator ResultIterator) []interface{} {
|
||||||
|
var result = make([]interface{}, len(array))
|
||||||
|
for index, data := range array {
|
||||||
|
result[index] = iterator(data, index)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
|
||||||
|
func Find(array []interface{}, iterator ConditionIterator) interface{} {
|
||||||
|
for index, data := range array {
|
||||||
|
if iterator(data, index) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
||||||
|
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
|
||||||
|
var result = make([]interface{}, 0)
|
||||||
|
for index, data := range array {
|
||||||
|
if iterator(data, index) {
|
||||||
|
result = append(result, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
|
||||||
|
func Count(array []interface{}, iterator ConditionIterator) int {
|
||||||
|
count := 0
|
||||||
|
for index, data := range array {
|
||||||
|
if iterator(data, index) {
|
||||||
|
count = count + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToString convert the input to a string.
|
||||||
|
func ToString(obj interface{}) string {
|
||||||
|
res := fmt.Sprintf("%v", obj)
|
||||||
|
return string(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJSON convert the input to a valid JSON string
|
||||||
|
func ToJSON(obj interface{}) (string, error) {
|
||||||
|
res, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
res = []byte("")
|
||||||
|
}
|
||||||
|
return string(res), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
|
||||||
|
func ToFloat(str string) (float64, error) {
|
||||||
|
res, err := strconv.ParseFloat(str, 64)
|
||||||
|
if err != nil {
|
||||||
|
res = 0.0
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer.
|
||||||
|
func ToInt(value interface{}) (res int64, err error) {
|
||||||
|
val := reflect.ValueOf(value)
|
||||||
|
|
||||||
|
switch value.(type) {
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
res = val.Int()
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
res = int64(val.Uint())
|
||||||
|
case string:
|
||||||
|
if IsInt(val.String()) {
|
||||||
|
res, err = strconv.ParseInt(val.String(), 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
res = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("math: square root of negative number %g", value)
|
||||||
|
res = 0
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("math: square root of negative number %g", value)
|
||||||
|
res = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBoolean convert the input string to a boolean.
|
||||||
|
func ToBoolean(str string) (bool, error) {
|
||||||
|
return strconv.ParseBool(str)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// Errors is an array of multiple errors and conforms to the error interface.
|
||||||
|
type Errors []error
|
||||||
|
|
||||||
|
// Errors returns itself.
|
||||||
|
func (es Errors) Errors() []error {
|
||||||
|
return es
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es Errors) Error() string {
|
||||||
|
var errs []string
|
||||||
|
for _, e := range es {
|
||||||
|
errs = append(errs, e.Error())
|
||||||
|
}
|
||||||
|
return strings.Join(errs, ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error encapsulates a name, an error and whether there's a custom error message or not.
|
||||||
|
type Error struct {
|
||||||
|
Name string
|
||||||
|
Err error
|
||||||
|
CustomErrorMessageExists bool
|
||||||
|
|
||||||
|
// Validator indicates the name of the validator that failed
|
||||||
|
Validator string
|
||||||
|
Path []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
if e.CustomErrorMessageExists {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
errName := e.Name
|
||||||
|
if len(e.Path) > 0 {
|
||||||
|
errName = strings.Join(append(e.Path, e.Name), ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
return errName + ": " + e.Err.Error()
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Abs returns absolute value of number
|
||||||
|
func Abs(value float64) float64 {
|
||||||
|
return math.Abs(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
|
||||||
|
func Sign(value float64) float64 {
|
||||||
|
if value > 0 {
|
||||||
|
return 1
|
||||||
|
} else if value < 0 {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNegative returns true if value < 0
|
||||||
|
func IsNegative(value float64) bool {
|
||||||
|
return value < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPositive returns true if value > 0
|
||||||
|
func IsPositive(value float64) bool {
|
||||||
|
return value > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonNegative returns true if value >= 0
|
||||||
|
func IsNonNegative(value float64) bool {
|
||||||
|
return value >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNonPositive returns true if value <= 0
|
||||||
|
func IsNonPositive(value float64) bool {
|
||||||
|
return value <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// InRange returns true if value lies between left and right border
|
||||||
|
func InRangeInt(value, left, right interface{}) bool {
|
||||||
|
value64, _ := ToInt(value)
|
||||||
|
left64, _ := ToInt(left)
|
||||||
|
right64, _ := ToInt(right)
|
||||||
|
if left64 > right64 {
|
||||||
|
left64, right64 = right64, left64
|
||||||
|
}
|
||||||
|
return value64 >= left64 && value64 <= right64
|
||||||
|
}
|
||||||
|
|
||||||
|
// InRange returns true if value lies between left and right border
|
||||||
|
func InRangeFloat32(value, left, right float32) bool {
|
||||||
|
if left > right {
|
||||||
|
left, right = right, left
|
||||||
|
}
|
||||||
|
return value >= left && value <= right
|
||||||
|
}
|
||||||
|
|
||||||
|
// InRange returns true if value lies between left and right border
|
||||||
|
func InRangeFloat64(value, left, right float64) bool {
|
||||||
|
if left > right {
|
||||||
|
left, right = right, left
|
||||||
|
}
|
||||||
|
return value >= left && value <= right
|
||||||
|
}
|
||||||
|
|
||||||
|
// InRange returns true if value lies between left and right border, generic type to handle int, float32 or float64, all types must the same type
|
||||||
|
func InRange(value interface{}, left interface{}, right interface{}) bool {
|
||||||
|
|
||||||
|
reflectValue := reflect.TypeOf(value).Kind()
|
||||||
|
reflectLeft := reflect.TypeOf(left).Kind()
|
||||||
|
reflectRight := reflect.TypeOf(right).Kind()
|
||||||
|
|
||||||
|
if reflectValue == reflect.Int && reflectLeft == reflect.Int && reflectRight == reflect.Int {
|
||||||
|
return InRangeInt(value.(int), left.(int), right.(int))
|
||||||
|
} else if reflectValue == reflect.Float32 && reflectLeft == reflect.Float32 && reflectRight == reflect.Float32 {
|
||||||
|
return InRangeFloat32(value.(float32), left.(float32), right.(float32))
|
||||||
|
} else if reflectValue == reflect.Float64 && reflectLeft == reflect.Float64 && reflectRight == reflect.Float64 {
|
||||||
|
return InRangeFloat64(value.(float64), left.(float64), right.(float64))
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWhole returns true if value is whole number
|
||||||
|
func IsWhole(value float64) bool {
|
||||||
|
return math.Remainder(value, 1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNatural returns true if value is natural number (positive and whole)
|
||||||
|
func IsNatural(value float64) bool {
|
||||||
|
return IsWhole(value) && IsPositive(value)
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
// Basic regular expressions for validating strings
|
||||||
|
const (
|
||||||
|
Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||||
|
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
|
||||||
|
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
|
||||||
|
ISBN13 string = "^(?:[0-9]{13})$"
|
||||||
|
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||||
|
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||||
|
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||||
|
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||||
|
Alpha string = "^[a-zA-Z]+$"
|
||||||
|
Alphanumeric string = "^[a-zA-Z0-9]+$"
|
||||||
|
Numeric string = "^[0-9]+$"
|
||||||
|
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
|
||||||
|
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
|
||||||
|
Hexadecimal string = "^[0-9a-fA-F]+$"
|
||||||
|
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
|
||||||
|
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
|
||||||
|
ASCII string = "^[\x00-\x7F]+$"
|
||||||
|
Multibyte string = "[^\x00-\x7F]"
|
||||||
|
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||||
|
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||||
|
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
||||||
|
PrintableASCII string = "^[\x20-\x7E]+$"
|
||||||
|
DataURI string = "^data:.+\\/(.+);base64$"
|
||||||
|
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||||
|
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||||
|
DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
|
||||||
|
IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
|
||||||
|
URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)`
|
||||||
|
URLUsername string = `(\S+(:\S*)?@)`
|
||||||
|
URLPath string = `((\/|\?|#)[^\s]*)`
|
||||||
|
URLPort string = `(:(\d{1,5}))`
|
||||||
|
URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))`
|
||||||
|
URLSubdomain string = `((www\.)|([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[a-zA-Z0-9]\.[a-zA-Z0-9]+))`
|
||||||
|
URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
|
||||||
|
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
|
||||||
|
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
||||||
|
UnixPath string = `^(/[^/\x00]*)+/?$`
|
||||||
|
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
|
||||||
|
tagName string = "valid"
|
||||||
|
hasLowerCase string = ".*[[:lower:]]"
|
||||||
|
hasUpperCase string = ".*[[:upper:]]"
|
||||||
|
hasWhitespace string = ".*[[:space:]]"
|
||||||
|
hasWhitespaceOnly string = "^[[:space:]]+$"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Used by IsFilePath func
|
||||||
|
const (
|
||||||
|
// Unknown is unresolved OS type
|
||||||
|
Unknown = iota
|
||||||
|
// Win is Windows type
|
||||||
|
Win
|
||||||
|
// Unix is *nix OS types
|
||||||
|
Unix
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
|
||||||
|
hostRegexp = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$")
|
||||||
|
userDotRegexp = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})")
|
||||||
|
rxEmail = regexp.MustCompile(Email)
|
||||||
|
rxCreditCard = regexp.MustCompile(CreditCard)
|
||||||
|
rxISBN10 = regexp.MustCompile(ISBN10)
|
||||||
|
rxISBN13 = regexp.MustCompile(ISBN13)
|
||||||
|
rxUUID3 = regexp.MustCompile(UUID3)
|
||||||
|
rxUUID4 = regexp.MustCompile(UUID4)
|
||||||
|
rxUUID5 = regexp.MustCompile(UUID5)
|
||||||
|
rxUUID = regexp.MustCompile(UUID)
|
||||||
|
rxAlpha = regexp.MustCompile(Alpha)
|
||||||
|
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
|
||||||
|
rxNumeric = regexp.MustCompile(Numeric)
|
||||||
|
rxInt = regexp.MustCompile(Int)
|
||||||
|
rxFloat = regexp.MustCompile(Float)
|
||||||
|
rxHexadecimal = regexp.MustCompile(Hexadecimal)
|
||||||
|
rxHexcolor = regexp.MustCompile(Hexcolor)
|
||||||
|
rxRGBcolor = regexp.MustCompile(RGBcolor)
|
||||||
|
rxASCII = regexp.MustCompile(ASCII)
|
||||||
|
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
|
||||||
|
rxMultibyte = regexp.MustCompile(Multibyte)
|
||||||
|
rxFullWidth = regexp.MustCompile(FullWidth)
|
||||||
|
rxHalfWidth = regexp.MustCompile(HalfWidth)
|
||||||
|
rxBase64 = regexp.MustCompile(Base64)
|
||||||
|
rxDataURI = regexp.MustCompile(DataURI)
|
||||||
|
rxLatitude = regexp.MustCompile(Latitude)
|
||||||
|
rxLongitude = regexp.MustCompile(Longitude)
|
||||||
|
rxDNSName = regexp.MustCompile(DNSName)
|
||||||
|
rxURL = regexp.MustCompile(URL)
|
||||||
|
rxSSN = regexp.MustCompile(SSN)
|
||||||
|
rxWinPath = regexp.MustCompile(WinPath)
|
||||||
|
rxUnixPath = regexp.MustCompile(UnixPath)
|
||||||
|
rxSemver = regexp.MustCompile(Semver)
|
||||||
|
rxHasLowerCase = regexp.MustCompile(hasLowerCase)
|
||||||
|
rxHasUpperCase = regexp.MustCompile(hasUpperCase)
|
||||||
|
rxHasWhitespace = regexp.MustCompile(hasWhitespace)
|
||||||
|
rxHasWhitespaceOnly = regexp.MustCompile(hasWhitespaceOnly)
|
||||||
|
)
|
|
@ -0,0 +1,636 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
||||||
|
type Validator func(str string) bool
|
||||||
|
|
||||||
|
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
||||||
|
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
|
||||||
|
type CustomTypeValidator func(i interface{}, o interface{}) bool
|
||||||
|
|
||||||
|
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
|
||||||
|
type ParamValidator func(str string, params ...string) bool
|
||||||
|
type tagOptionsMap map[string]tagOption
|
||||||
|
|
||||||
|
func (t tagOptionsMap) orderedKeys() []string {
|
||||||
|
var keys []string
|
||||||
|
for k := range t {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(keys, func(a, b int) bool {
|
||||||
|
return t[keys[a]].order < t[keys[b]].order
|
||||||
|
})
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagOption struct {
|
||||||
|
name string
|
||||||
|
customErrorMessage string
|
||||||
|
order int
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsupportedTypeError is a wrapper for reflect.Type
|
||||||
|
type UnsupportedTypeError struct {
|
||||||
|
Type reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||||
|
// It implements the methods to sort by string.
|
||||||
|
type stringValues []reflect.Value
|
||||||
|
|
||||||
|
// ParamTagMap is a map of functions accept variants parameters
|
||||||
|
var ParamTagMap = map[string]ParamValidator{
|
||||||
|
"length": ByteLength,
|
||||||
|
"range": Range,
|
||||||
|
"runelength": RuneLength,
|
||||||
|
"stringlength": StringLength,
|
||||||
|
"matches": StringMatches,
|
||||||
|
"in": isInRaw,
|
||||||
|
"rsapub": IsRsaPub,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamTagRegexMap maps param tags to their respective regexes.
|
||||||
|
var ParamTagRegexMap = map[string]*regexp.Regexp{
|
||||||
|
"range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
|
||||||
|
"in": regexp.MustCompile(`^in\((.*)\)`),
|
||||||
|
"matches": regexp.MustCompile(`^matches\((.+)\)$`),
|
||||||
|
"rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"),
|
||||||
|
}
|
||||||
|
|
||||||
|
type customTypeTagMap struct {
|
||||||
|
validators map[string]CustomTypeValidator
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
|
||||||
|
tm.RLock()
|
||||||
|
defer tm.RUnlock()
|
||||||
|
v, ok := tm.validators[name]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
|
||||||
|
tm.Lock()
|
||||||
|
defer tm.Unlock()
|
||||||
|
tm.validators[name] = ctv
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
||||||
|
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
||||||
|
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
||||||
|
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
|
||||||
|
|
||||||
|
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
||||||
|
var TagMap = map[string]Validator{
|
||||||
|
"email": IsEmail,
|
||||||
|
"url": IsURL,
|
||||||
|
"dialstring": IsDialString,
|
||||||
|
"requrl": IsRequestURL,
|
||||||
|
"requri": IsRequestURI,
|
||||||
|
"alpha": IsAlpha,
|
||||||
|
"utfletter": IsUTFLetter,
|
||||||
|
"alphanum": IsAlphanumeric,
|
||||||
|
"utfletternum": IsUTFLetterNumeric,
|
||||||
|
"numeric": IsNumeric,
|
||||||
|
"utfnumeric": IsUTFNumeric,
|
||||||
|
"utfdigit": IsUTFDigit,
|
||||||
|
"hexadecimal": IsHexadecimal,
|
||||||
|
"hexcolor": IsHexcolor,
|
||||||
|
"rgbcolor": IsRGBcolor,
|
||||||
|
"lowercase": IsLowerCase,
|
||||||
|
"uppercase": IsUpperCase,
|
||||||
|
"int": IsInt,
|
||||||
|
"float": IsFloat,
|
||||||
|
"null": IsNull,
|
||||||
|
"uuid": IsUUID,
|
||||||
|
"uuidv3": IsUUIDv3,
|
||||||
|
"uuidv4": IsUUIDv4,
|
||||||
|
"uuidv5": IsUUIDv5,
|
||||||
|
"creditcard": IsCreditCard,
|
||||||
|
"isbn10": IsISBN10,
|
||||||
|
"isbn13": IsISBN13,
|
||||||
|
"json": IsJSON,
|
||||||
|
"multibyte": IsMultibyte,
|
||||||
|
"ascii": IsASCII,
|
||||||
|
"printableascii": IsPrintableASCII,
|
||||||
|
"fullwidth": IsFullWidth,
|
||||||
|
"halfwidth": IsHalfWidth,
|
||||||
|
"variablewidth": IsVariableWidth,
|
||||||
|
"base64": IsBase64,
|
||||||
|
"datauri": IsDataURI,
|
||||||
|
"ip": IsIP,
|
||||||
|
"port": IsPort,
|
||||||
|
"ipv4": IsIPv4,
|
||||||
|
"ipv6": IsIPv6,
|
||||||
|
"dns": IsDNSName,
|
||||||
|
"host": IsHost,
|
||||||
|
"mac": IsMAC,
|
||||||
|
"latitude": IsLatitude,
|
||||||
|
"longitude": IsLongitude,
|
||||||
|
"ssn": IsSSN,
|
||||||
|
"semver": IsSemver,
|
||||||
|
"rfc3339": IsRFC3339,
|
||||||
|
"rfc3339WithoutZone": IsRFC3339WithoutZone,
|
||||||
|
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||||
|
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||||
|
"ISO4217": IsISO4217,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO3166Entry stores country codes
|
||||||
|
type ISO3166Entry struct {
|
||||||
|
EnglishShortName string
|
||||||
|
FrenchShortName string
|
||||||
|
Alpha2Code string
|
||||||
|
Alpha3Code string
|
||||||
|
Numeric string
|
||||||
|
}
|
||||||
|
|
||||||
|
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
|
||||||
|
var ISO3166List = []ISO3166Entry{
|
||||||
|
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
|
||||||
|
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
|
||||||
|
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
|
||||||
|
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
|
||||||
|
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
|
||||||
|
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
|
||||||
|
{"Angola", "Angola (l')", "AO", "AGO", "024"},
|
||||||
|
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
|
||||||
|
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
|
||||||
|
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
|
||||||
|
{"Australia", "Australie (l')", "AU", "AUS", "036"},
|
||||||
|
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
|
||||||
|
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
|
||||||
|
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
|
||||||
|
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
|
||||||
|
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
|
||||||
|
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
|
||||||
|
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
|
||||||
|
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
|
||||||
|
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
|
||||||
|
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
|
||||||
|
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
|
||||||
|
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
|
||||||
|
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
|
||||||
|
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
|
||||||
|
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
|
||||||
|
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
|
||||||
|
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
|
||||||
|
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
|
||||||
|
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
|
||||||
|
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
|
||||||
|
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
|
||||||
|
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
|
||||||
|
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
|
||||||
|
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
|
||||||
|
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
|
||||||
|
{"Canada", "Canada (le)", "CA", "CAN", "124"},
|
||||||
|
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
|
||||||
|
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
|
||||||
|
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
|
||||||
|
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
|
||||||
|
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
|
||||||
|
{"Chile", "Chili (le)", "CL", "CHL", "152"},
|
||||||
|
{"China", "Chine (la)", "CN", "CHN", "156"},
|
||||||
|
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
|
||||||
|
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
|
||||||
|
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
|
||||||
|
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
|
||||||
|
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
|
||||||
|
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
|
||||||
|
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
|
||||||
|
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
|
||||||
|
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
|
||||||
|
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
|
||||||
|
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
|
||||||
|
{"Cuba", "Cuba", "CU", "CUB", "192"},
|
||||||
|
{"Cyprus", "Chypre", "CY", "CYP", "196"},
|
||||||
|
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
|
||||||
|
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
|
||||||
|
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
|
||||||
|
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
|
||||||
|
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
|
||||||
|
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
|
||||||
|
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
|
||||||
|
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
|
||||||
|
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
|
||||||
|
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
|
||||||
|
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
|
||||||
|
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
|
||||||
|
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
|
||||||
|
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
|
||||||
|
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
|
||||||
|
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
|
||||||
|
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
|
||||||
|
{"France", "France (la)", "FR", "FRA", "250"},
|
||||||
|
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
|
||||||
|
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
|
||||||
|
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
|
||||||
|
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
|
||||||
|
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
|
||||||
|
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
|
||||||
|
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
|
||||||
|
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
|
||||||
|
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
|
||||||
|
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
|
||||||
|
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
|
||||||
|
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
|
||||||
|
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
|
||||||
|
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
|
||||||
|
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
|
||||||
|
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
|
||||||
|
{"Guam", "Guam", "GU", "GUM", "316"},
|
||||||
|
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
|
||||||
|
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
|
||||||
|
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
|
||||||
|
{"Haiti", "Haïti", "HT", "HTI", "332"},
|
||||||
|
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
|
||||||
|
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
|
||||||
|
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
|
||||||
|
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
|
||||||
|
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
|
||||||
|
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
|
||||||
|
{"India", "Inde (l')", "IN", "IND", "356"},
|
||||||
|
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
|
||||||
|
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
|
||||||
|
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
|
||||||
|
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
|
||||||
|
{"Israel", "Israël", "IL", "ISR", "376"},
|
||||||
|
{"Italy", "Italie (l')", "IT", "ITA", "380"},
|
||||||
|
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
|
||||||
|
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
|
||||||
|
{"Japan", "Japon (le)", "JP", "JPN", "392"},
|
||||||
|
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
|
||||||
|
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
|
||||||
|
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
|
||||||
|
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
|
||||||
|
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
|
||||||
|
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
|
||||||
|
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
|
||||||
|
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
|
||||||
|
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
|
||||||
|
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
|
||||||
|
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
|
||||||
|
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
|
||||||
|
{"Libya", "Libye (la)", "LY", "LBY", "434"},
|
||||||
|
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
|
||||||
|
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
|
||||||
|
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
|
||||||
|
{"Macao", "Macao", "MO", "MAC", "446"},
|
||||||
|
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
|
||||||
|
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
|
||||||
|
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
|
||||||
|
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
|
||||||
|
{"Mali", "Mali (le)", "ML", "MLI", "466"},
|
||||||
|
{"Malta", "Malte", "MT", "MLT", "470"},
|
||||||
|
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
|
||||||
|
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
|
||||||
|
{"Mauritius", "Maurice", "MU", "MUS", "480"},
|
||||||
|
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
|
||||||
|
{"Monaco", "Monaco", "MC", "MCO", "492"},
|
||||||
|
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
|
||||||
|
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
|
||||||
|
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
|
||||||
|
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
|
||||||
|
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
|
||||||
|
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
|
||||||
|
{"Oman", "Oman", "OM", "OMN", "512"},
|
||||||
|
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
|
||||||
|
{"Nauru", "Nauru", "NR", "NRU", "520"},
|
||||||
|
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
|
||||||
|
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
|
||||||
|
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
|
||||||
|
{"Aruba", "Aruba", "AW", "ABW", "533"},
|
||||||
|
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
|
||||||
|
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
|
||||||
|
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
|
||||||
|
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
|
||||||
|
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
|
||||||
|
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
|
||||||
|
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
|
||||||
|
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
|
||||||
|
{"Niue", "Niue", "NU", "NIU", "570"},
|
||||||
|
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
|
||||||
|
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
|
||||||
|
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
|
||||||
|
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
|
||||||
|
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
|
||||||
|
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
|
||||||
|
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
|
||||||
|
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
|
||||||
|
{"Panama", "Panama (le)", "PA", "PAN", "591"},
|
||||||
|
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
|
||||||
|
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
|
||||||
|
{"Peru", "Pérou (le)", "PE", "PER", "604"},
|
||||||
|
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
|
||||||
|
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
|
||||||
|
{"Poland", "Pologne (la)", "PL", "POL", "616"},
|
||||||
|
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
|
||||||
|
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
|
||||||
|
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
|
||||||
|
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
|
||||||
|
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
|
||||||
|
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
|
||||||
|
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
|
||||||
|
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
|
||||||
|
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
|
||||||
|
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
|
||||||
|
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
|
||||||
|
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
|
||||||
|
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
|
||||||
|
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
|
||||||
|
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
|
||||||
|
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
|
||||||
|
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
|
||||||
|
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
|
||||||
|
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
|
||||||
|
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
|
||||||
|
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
|
||||||
|
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
|
||||||
|
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
|
||||||
|
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
|
||||||
|
{"Singapore", "Singapour", "SG", "SGP", "702"},
|
||||||
|
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
|
||||||
|
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
|
||||||
|
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
|
||||||
|
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
|
||||||
|
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
|
||||||
|
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
|
||||||
|
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
|
||||||
|
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
|
||||||
|
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
|
||||||
|
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
|
||||||
|
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
|
||||||
|
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
|
||||||
|
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
|
||||||
|
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
|
||||||
|
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
|
||||||
|
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
|
||||||
|
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
|
||||||
|
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
|
||||||
|
{"Togo", "Togo (le)", "TG", "TGO", "768"},
|
||||||
|
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
|
||||||
|
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
|
||||||
|
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
|
||||||
|
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
|
||||||
|
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
|
||||||
|
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
|
||||||
|
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
|
||||||
|
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
|
||||||
|
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
|
||||||
|
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
|
||||||
|
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
|
||||||
|
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"},
|
||||||
|
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
|
||||||
|
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
|
||||||
|
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
|
||||||
|
{"Jersey", "Jersey", "JE", "JEY", "832"},
|
||||||
|
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
|
||||||
|
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
|
||||||
|
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
|
||||||
|
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
|
||||||
|
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
|
||||||
|
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
|
||||||
|
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
|
||||||
|
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
|
||||||
|
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
|
||||||
|
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
|
||||||
|
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
|
||||||
|
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO4217List is the list of ISO currency codes
|
||||||
|
var ISO4217List = []string{
|
||||||
|
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
|
||||||
|
"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
|
||||||
|
"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
|
||||||
|
"DJF", "DKK", "DOP", "DZD",
|
||||||
|
"EGP", "ERN", "ETB", "EUR",
|
||||||
|
"FJD", "FKP",
|
||||||
|
"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
|
||||||
|
"HKD", "HNL", "HRK", "HTG", "HUF",
|
||||||
|
"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
|
||||||
|
"JMD", "JOD", "JPY",
|
||||||
|
"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
|
||||||
|
"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
|
||||||
|
"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
|
||||||
|
"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
|
||||||
|
"OMR",
|
||||||
|
"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
|
||||||
|
"QAR",
|
||||||
|
"RON", "RSD", "RUB", "RWF",
|
||||||
|
"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL",
|
||||||
|
"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
|
||||||
|
"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS",
|
||||||
|
"VEF", "VND", "VUV",
|
||||||
|
"WST",
|
||||||
|
"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
|
||||||
|
"YER",
|
||||||
|
"ZAR", "ZMW", "ZWL",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO693Entry stores ISO language codes
|
||||||
|
type ISO693Entry struct {
|
||||||
|
Alpha3bCode string
|
||||||
|
Alpha2Code string
|
||||||
|
English string
|
||||||
|
}
|
||||||
|
|
||||||
|
//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
|
||||||
|
var ISO693List = []ISO693Entry{
|
||||||
|
{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
|
||||||
|
{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
|
||||||
|
{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
|
||||||
|
{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
|
||||||
|
{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
|
||||||
|
{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
|
||||||
|
{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
|
||||||
|
{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
|
||||||
|
{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
|
||||||
|
{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
|
||||||
|
{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
|
||||||
|
{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
|
||||||
|
{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
|
||||||
|
{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
|
||||||
|
{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
|
||||||
|
{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
|
||||||
|
{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
|
||||||
|
{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
|
||||||
|
{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
|
||||||
|
{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
|
||||||
|
{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
|
||||||
|
{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
|
||||||
|
{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
|
||||||
|
{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
|
||||||
|
{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
|
||||||
|
{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
|
||||||
|
{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
|
||||||
|
{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
|
||||||
|
{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
|
||||||
|
{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
|
||||||
|
{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
|
||||||
|
{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
|
||||||
|
{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
|
||||||
|
{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
|
||||||
|
{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
|
||||||
|
{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
|
||||||
|
{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
|
||||||
|
{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
|
||||||
|
{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
|
||||||
|
{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
|
||||||
|
{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
|
||||||
|
{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
|
||||||
|
{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
|
||||||
|
{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
|
||||||
|
{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
|
||||||
|
{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
|
||||||
|
{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
|
||||||
|
{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
|
||||||
|
{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
|
||||||
|
{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
|
||||||
|
{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
|
||||||
|
{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
|
||||||
|
{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
|
||||||
|
{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
|
||||||
|
{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
|
||||||
|
{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
|
||||||
|
{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
|
||||||
|
{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
|
||||||
|
{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
|
||||||
|
{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
|
||||||
|
{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
|
||||||
|
{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
|
||||||
|
{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
|
||||||
|
{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
|
||||||
|
{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
|
||||||
|
{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
|
||||||
|
{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
|
||||||
|
{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
|
||||||
|
{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
|
||||||
|
{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
|
||||||
|
{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
|
||||||
|
{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
|
||||||
|
{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
|
||||||
|
{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
|
||||||
|
{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
|
||||||
|
{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
|
||||||
|
{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
|
||||||
|
{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
|
||||||
|
{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
|
||||||
|
{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
|
||||||
|
{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
|
||||||
|
{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
|
||||||
|
{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
|
||||||
|
{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
|
||||||
|
{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
|
||||||
|
{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
|
||||||
|
{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
|
||||||
|
{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
|
||||||
|
{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
|
||||||
|
{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
|
||||||
|
{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
|
||||||
|
{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
|
||||||
|
{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
|
||||||
|
{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
|
||||||
|
{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
|
||||||
|
{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
|
||||||
|
{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
|
||||||
|
{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
|
||||||
|
{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
|
||||||
|
{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
|
||||||
|
{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
|
||||||
|
{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
|
||||||
|
{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
|
||||||
|
{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
|
||||||
|
{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
|
||||||
|
{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
|
||||||
|
{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
|
||||||
|
{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
|
||||||
|
{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
|
||||||
|
{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
|
||||||
|
{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
|
||||||
|
{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
|
||||||
|
{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
|
||||||
|
{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
|
||||||
|
{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
|
||||||
|
{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
|
||||||
|
{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
|
||||||
|
{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
|
||||||
|
{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
|
||||||
|
{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
|
||||||
|
{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
|
||||||
|
{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
|
||||||
|
{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
|
||||||
|
{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
|
||||||
|
{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
|
||||||
|
{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
|
||||||
|
{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
|
||||||
|
{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
|
||||||
|
{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
|
||||||
|
{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
|
||||||
|
{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
|
||||||
|
{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
|
||||||
|
{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
|
||||||
|
{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
|
||||||
|
{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
|
||||||
|
{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
|
||||||
|
{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
|
||||||
|
{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
|
||||||
|
{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
|
||||||
|
{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
|
||||||
|
{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
|
||||||
|
{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
|
||||||
|
{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
|
||||||
|
{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
|
||||||
|
{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
|
||||||
|
{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
|
||||||
|
{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
|
||||||
|
{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
|
||||||
|
{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
|
||||||
|
{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
|
||||||
|
{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
|
||||||
|
{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
|
||||||
|
{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
|
||||||
|
{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
|
||||||
|
{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
|
||||||
|
{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
|
||||||
|
{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
|
||||||
|
{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
|
||||||
|
{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
|
||||||
|
{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
|
||||||
|
{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
|
||||||
|
{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
|
||||||
|
{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
|
||||||
|
{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
|
||||||
|
{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
|
||||||
|
{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
|
||||||
|
{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
|
||||||
|
{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
|
||||||
|
{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
|
||||||
|
{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
|
||||||
|
{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
|
||||||
|
{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
|
||||||
|
{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
|
||||||
|
{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
|
||||||
|
{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
|
||||||
|
{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
|
||||||
|
{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
|
||||||
|
{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
|
||||||
|
{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
|
||||||
|
{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
|
||||||
|
{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
|
||||||
|
{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
|
||||||
|
{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
|
||||||
|
{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
package govalidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
"math"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Contains check if the string contains the substring.
|
||||||
|
func Contains(str, substring string) bool {
|
||||||
|
return strings.Contains(str, substring)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches check if string matches the pattern (pattern is regular expression)
|
||||||
|
// In case of error return false
|
||||||
|
func Matches(str, pattern string) bool {
|
||||||
|
match, _ := regexp.MatchString(pattern, str)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftTrim trim characters from the left-side of the input.
|
||||||
|
// If second argument is empty, it's will be remove leading spaces.
|
||||||
|
func LeftTrim(str, chars string) string {
|
||||||
|
if chars == "" {
|
||||||
|
return strings.TrimLeftFunc(str, unicode.IsSpace)
|
||||||
|
}
|
||||||
|
r, _ := regexp.Compile("^[" + chars + "]+")
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightTrim trim characters from the right-side of the input.
|
||||||
|
// If second argument is empty, it's will be remove spaces.
|
||||||
|
func RightTrim(str, chars string) string {
|
||||||
|
if chars == "" {
|
||||||
|
return strings.TrimRightFunc(str, unicode.IsSpace)
|
||||||
|
}
|
||||||
|
r, _ := regexp.Compile("[" + chars + "]+$")
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim trim characters from both sides of the input.
|
||||||
|
// If second argument is empty, it's will be remove spaces.
|
||||||
|
func Trim(str, chars string) string {
|
||||||
|
return LeftTrim(RightTrim(str, chars), chars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhiteList remove characters that do not appear in the whitelist.
|
||||||
|
func WhiteList(str, chars string) string {
|
||||||
|
pattern := "[^" + chars + "]+"
|
||||||
|
r, _ := regexp.Compile(pattern)
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlackList remove characters that appear in the blacklist.
|
||||||
|
func BlackList(str, chars string) string {
|
||||||
|
pattern := "[" + chars + "]+"
|
||||||
|
r, _ := regexp.Compile(pattern)
|
||||||
|
return r.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
|
||||||
|
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
|
||||||
|
func StripLow(str string, keepNewLines bool) string {
|
||||||
|
chars := ""
|
||||||
|
if keepNewLines {
|
||||||
|
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
|
||||||
|
} else {
|
||||||
|
chars = "\x00-\x1F\x7F"
|
||||||
|
}
|
||||||
|
return BlackList(str, chars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplacePattern replace regular expression pattern in string
|
||||||
|
func ReplacePattern(str, pattern, replace string) string {
|
||||||
|
r, _ := regexp.Compile(pattern)
|
||||||
|
return r.ReplaceAllString(str, replace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape replace <, >, & and " with HTML entities.
|
||||||
|
var Escape = html.EscapeString
|
||||||
|
|
||||||
|
func addSegment(inrune, segment []rune) []rune {
|
||||||
|
if len(segment) == 0 {
|
||||||
|
return inrune
|
||||||
|
}
|
||||||
|
if len(inrune) != 0 {
|
||||||
|
inrune = append(inrune, '_')
|
||||||
|
}
|
||||||
|
inrune = append(inrune, segment...)
|
||||||
|
return inrune
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
|
||||||
|
// Ex.: my_func => MyFunc
|
||||||
|
func UnderscoreToCamelCase(s string) string {
|
||||||
|
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
|
||||||
|
// Ex.: MyFunc => my_func
|
||||||
|
func CamelCaseToUnderscore(str string) string {
|
||||||
|
var output []rune
|
||||||
|
var segment []rune
|
||||||
|
for _, r := range str {
|
||||||
|
|
||||||
|
// not treat number as separate segment
|
||||||
|
if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) {
|
||||||
|
output = addSegment(output, segment)
|
||||||
|
segment = nil
|
||||||
|
}
|
||||||
|
segment = append(segment, unicode.ToLower(r))
|
||||||
|
}
|
||||||
|
output = addSegment(output, segment)
|
||||||
|
return string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse return reversed string
|
||||||
|
func Reverse(s string) string {
|
||||||
|
r := []rune(s)
|
||||||
|
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
r[i], r[j] = r[j], r[i]
|
||||||
|
}
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLines split string by "\n" and return array of lines
|
||||||
|
func GetLines(s string) []string {
|
||||||
|
return strings.Split(s, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLine return specified line of multiline string
|
||||||
|
func GetLine(s string, index int) (string, error) {
|
||||||
|
lines := GetLines(s)
|
||||||
|
if index < 0 || index >= len(lines) {
|
||||||
|
return "", errors.New("line index out of bounds")
|
||||||
|
}
|
||||||
|
return lines[index], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveTags remove all tags from HTML string
|
||||||
|
func RemoveTags(s string) string {
|
||||||
|
return ReplacePattern(s, "<[^>]*>", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeFileName return safe string that can be used in file names
|
||||||
|
func SafeFileName(str string) string {
|
||||||
|
name := strings.ToLower(str)
|
||||||
|
name = path.Clean(path.Base(name))
|
||||||
|
name = strings.Trim(name, " ")
|
||||||
|
separators, err := regexp.Compile(`[ &_=+:]`)
|
||||||
|
if err == nil {
|
||||||
|
name = separators.ReplaceAllString(name, "-")
|
||||||
|
}
|
||||||
|
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
|
||||||
|
if err == nil {
|
||||||
|
name = legal.ReplaceAllString(name, "")
|
||||||
|
}
|
||||||
|
for strings.Contains(name, "--") {
|
||||||
|
name = strings.Replace(name, "--", "-", -1)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeEmail canonicalize an email address.
|
||||||
|
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
|
||||||
|
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
|
||||||
|
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
|
||||||
|
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
|
||||||
|
// normalized to @gmail.com.
|
||||||
|
func NormalizeEmail(str string) (string, error) {
|
||||||
|
if !IsEmail(str) {
|
||||||
|
return "", fmt.Errorf("%s is not an email", str)
|
||||||
|
}
|
||||||
|
parts := strings.Split(str, "@")
|
||||||
|
parts[0] = strings.ToLower(parts[0])
|
||||||
|
parts[1] = strings.ToLower(parts[1])
|
||||||
|
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
|
||||||
|
parts[1] = "gmail.com"
|
||||||
|
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
|
||||||
|
}
|
||||||
|
return strings.Join(parts, "@"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate a string to the closest length without breaking words.
|
||||||
|
func Truncate(str string, length int, ending string) string {
|
||||||
|
var aftstr, befstr string
|
||||||
|
if len(str) > length {
|
||||||
|
words := strings.Fields(str)
|
||||||
|
before, present := 0, 0
|
||||||
|
for i := range words {
|
||||||
|
befstr = aftstr
|
||||||
|
before = present
|
||||||
|
aftstr = aftstr + words[i] + " "
|
||||||
|
present = len(aftstr)
|
||||||
|
if present > length && i != 0 {
|
||||||
|
if (length - before) < (present - length) {
|
||||||
|
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
|
||||||
|
}
|
||||||
|
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadLeft pad left side of string if size of string is less then indicated pad length
|
||||||
|
func PadLeft(str string, padStr string, padLen int) string {
|
||||||
|
return buildPadStr(str, padStr, padLen, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadRight pad right side of string if size of string is less then indicated pad length
|
||||||
|
func PadRight(str string, padStr string, padLen int) string {
|
||||||
|
return buildPadStr(str, padStr, padLen, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadBoth pad sides of string if size of string is less then indicated pad length
|
||||||
|
func PadBoth(str string, padStr string, padLen int) string {
|
||||||
|
return buildPadStr(str, padStr, padLen, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PadString either left, right or both sides, not the padding string can be unicode and more then one
|
||||||
|
// character
|
||||||
|
func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
|
||||||
|
|
||||||
|
// When padded length is less then the current string size
|
||||||
|
if padLen < utf8.RuneCountInString(str) {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
padLen -= utf8.RuneCountInString(str)
|
||||||
|
|
||||||
|
targetLen := padLen
|
||||||
|
|
||||||
|
targetLenLeft := targetLen
|
||||||
|
targetLenRight := targetLen
|
||||||
|
if padLeft && padRight {
|
||||||
|
targetLenLeft = padLen / 2
|
||||||
|
targetLenRight = padLen - targetLenLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
strToRepeatLen := utf8.RuneCountInString(padStr)
|
||||||
|
|
||||||
|
repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
|
||||||
|
repeatedString := strings.Repeat(padStr, repeatTimes)
|
||||||
|
|
||||||
|
leftSide := ""
|
||||||
|
if padLeft {
|
||||||
|
leftSide = repeatedString[0:targetLenLeft]
|
||||||
|
}
|
||||||
|
|
||||||
|
rightSide := ""
|
||||||
|
if padRight {
|
||||||
|
rightSide = repeatedString[0:targetLenRight]
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftSide + str + rightSide
|
||||||
|
}
|
||||||
|
|
||||||
|
// TruncatingErrorf removes extra args from fmt.Errorf if not formatted in the str object
|
||||||
|
func TruncatingErrorf(str string, args ...interface{}) error {
|
||||||
|
n := strings.Count(str, "%s")
|
||||||
|
return fmt.Errorf(str, args[:n]...)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -72,7 +72,7 @@ func NewEpoller() (*Epoller, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add creates an epoll console based on the provided console. The console will
|
// Add creates a epoll console based on the provided console. The console will
|
||||||
// be registered with EPOLLET (i.e. using edge-triggered notification) and its
|
// be registered with EPOLLET (i.e. using edge-triggered notification) and its
|
||||||
// file descriptor will be set to non-blocking mode. After this, user should use
|
// file descriptor will be set to non-blocking mode. After this, user should use
|
||||||
// the return console to perform I/O.
|
// the return console to perform I/O.
|
||||||
|
@ -134,7 +134,7 @@ func (e *Epoller) Wait() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseConsole unregisters the console's file descriptor from epoll interface
|
// Close unregister the console's file descriptor from epoll interface
|
||||||
func (e *Epoller) CloseConsole(fd int) error {
|
func (e *Epoller) CloseConsole(fd int) error {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
@ -149,12 +149,12 @@ func (e *Epoller) getConsole(sysfd int) *EpollConsole {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the epoll fd
|
// Close the epoll fd
|
||||||
func (e *Epoller) Close() error {
|
func (e *Epoller) Close() error {
|
||||||
return unix.Close(e.efd)
|
return unix.Close(e.efd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EpollConsole acts like a console but registers its file descriptor with an
|
// EpollConsole acts like a console but register its file descriptor with a
|
||||||
// epoll fd and uses epoll API to perform I/O.
|
// epoll fd and uses epoll API to perform I/O.
|
||||||
type EpollConsole struct {
|
type EpollConsole struct {
|
||||||
Console
|
Console
|
||||||
|
@ -167,7 +167,7 @@ type EpollConsole struct {
|
||||||
// Read reads up to len(p) bytes into p. It returns the number of bytes read
|
// Read reads up to len(p) bytes into p. It returns the number of bytes read
|
||||||
// (0 <= n <= len(p)) and any error encountered.
|
// (0 <= n <= len(p)) and any error encountered.
|
||||||
//
|
//
|
||||||
// If the console's read returns EAGAIN or EIO, we assume that it's a
|
// If the console's read returns EAGAIN or EIO, we assumes that its a
|
||||||
// temporary error because the other side went away and wait for the signal
|
// temporary error because the other side went away and wait for the signal
|
||||||
// generated by epoll event to continue.
|
// generated by epoll event to continue.
|
||||||
func (ec *EpollConsole) Read(p []byte) (n int, err error) {
|
func (ec *EpollConsole) Read(p []byte) (n int, err error) {
|
||||||
|
@ -207,7 +207,7 @@ func (ec *EpollConsole) Read(p []byte) (n int, err error) {
|
||||||
// written from p (0 <= n <= len(p)) and any error encountered that caused
|
// written from p (0 <= n <= len(p)) and any error encountered that caused
|
||||||
// the write to stop early.
|
// the write to stop early.
|
||||||
//
|
//
|
||||||
// If writes to the console returns EAGAIN or EIO, we assume that it's a
|
// If writes to the console returns EAGAIN or EIO, we assumes that its a
|
||||||
// temporary error because the other side went away and wait for the signal
|
// temporary error because the other side went away and wait for the signal
|
||||||
// generated by epoll event to continue.
|
// generated by epoll event to continue.
|
||||||
func (ec *EpollConsole) Write(p []byte) (n int, err error) {
|
func (ec *EpollConsole) Write(p []byte) (n int, err error) {
|
||||||
|
@ -224,7 +224,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) {
|
||||||
} else {
|
} else {
|
||||||
hangup = (err == unix.EAGAIN || err == unix.EIO)
|
hangup = (err == unix.EAGAIN || err == unix.EIO)
|
||||||
}
|
}
|
||||||
// if the other end disappears, assume this is temporary and wait for the
|
// if the other end disappear, assume this is temporary and wait for the
|
||||||
// signal to continue again.
|
// signal to continue again.
|
||||||
if hangup {
|
if hangup {
|
||||||
ec.writec.Wait()
|
ec.writec.Wait()
|
||||||
|
@ -242,7 +242,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown closes the file descriptor and signals call waiters for this fd.
|
// Close closed the file descriptor and signal call waiters for this fd.
|
||||||
// It accepts a callback which will be called with the console's fd. The
|
// It accepts a callback which will be called with the console's fd. The
|
||||||
// callback typically will be used to do further cleanup such as unregister the
|
// callback typically will be used to do further cleanup such as unregister the
|
||||||
// console's fd from the epoll interface.
|
// console's fd from the epoll interface.
|
||||||
|
@ -262,14 +262,10 @@ func (ec *EpollConsole) Shutdown(close func(int) error) error {
|
||||||
|
|
||||||
// signalRead signals that the console is readable.
|
// signalRead signals that the console is readable.
|
||||||
func (ec *EpollConsole) signalRead() {
|
func (ec *EpollConsole) signalRead() {
|
||||||
ec.readc.L.Lock()
|
|
||||||
ec.readc.Signal()
|
ec.readc.Signal()
|
||||||
ec.readc.L.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// signalWrite signals that the console is writable.
|
// signalWrite signals that the console is writable.
|
||||||
func (ec *EpollConsole) signalWrite() {
|
func (ec *EpollConsole) signalWrite() {
|
||||||
ec.writec.L.Lock()
|
|
||||||
ec.writec.Signal()
|
ec.writec.Signal()
|
||||||
ec.writec.L.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package console
|
package console
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -28,55 +29,90 @@ var (
|
||||||
ErrNotImplemented = errors.New("not implemented")
|
ErrNotImplemented = errors.New("not implemented")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *master) init() {
|
func (m *master) initStdios() {
|
||||||
m.h = windows.Handle(m.f.Fd())
|
m.in = windows.Handle(os.Stdin.Fd())
|
||||||
if err := windows.GetConsoleMode(m.h, &m.mode); err == nil {
|
if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
|
||||||
if m.f == os.Stdin {
|
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
|
||||||
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
|
if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
|
||||||
if err = windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
|
vtInputSupported = true
|
||||||
vtInputSupported = true
|
|
||||||
}
|
|
||||||
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
|
||||||
// remembers invalid bits on input handles.
|
|
||||||
windows.SetConsoleMode(m.h, m.mode)
|
|
||||||
} else if err := windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
|
||||||
m.mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
||||||
} else {
|
|
||||||
windows.SetConsoleMode(m.h, m.mode)
|
|
||||||
}
|
}
|
||||||
|
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
||||||
|
// remembers invalid bits on input handles.
|
||||||
|
windows.SetConsoleMode(m.in, m.inMode)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("failed to get console mode for stdin: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.out = windows.Handle(os.Stdout.Fd())
|
||||||
|
if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil {
|
||||||
|
if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
||||||
|
m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
} else {
|
||||||
|
windows.SetConsoleMode(m.out, m.outMode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("failed to get console mode for stdout: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.err = windows.Handle(os.Stderr.Fd())
|
||||||
|
if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil {
|
||||||
|
if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
||||||
|
m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
} else {
|
||||||
|
windows.SetConsoleMode(m.err, m.errMode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("failed to get console mode for stderr: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type master struct {
|
type master struct {
|
||||||
h windows.Handle
|
in windows.Handle
|
||||||
mode uint32
|
inMode uint32
|
||||||
f *os.File
|
|
||||||
|
out windows.Handle
|
||||||
|
outMode uint32
|
||||||
|
|
||||||
|
err windows.Handle
|
||||||
|
errMode uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) SetRaw() error {
|
func (m *master) SetRaw() error {
|
||||||
if m.f == os.Stdin {
|
if err := makeInputRaw(m.in, m.inMode); err != nil {
|
||||||
if err := makeInputRaw(m.h, m.mode); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set StdOut and StdErr to raw mode, we ignore failures since
|
|
||||||
// windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
|
|
||||||
// Windows.
|
|
||||||
windows.SetConsoleMode(m.h, m.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set StdOut and StdErr to raw mode, we ignore failures since
|
||||||
|
// windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
|
||||||
|
// Windows.
|
||||||
|
|
||||||
|
windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||||
|
|
||||||
|
windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Reset() error {
|
func (m *master) Reset() error {
|
||||||
if err := windows.SetConsoleMode(m.h, m.mode); err != nil {
|
for _, s := range []struct {
|
||||||
return errors.Wrap(err, "unable to restore console mode")
|
fd windows.Handle
|
||||||
|
mode uint32
|
||||||
|
}{
|
||||||
|
{m.in, m.inMode},
|
||||||
|
{m.out, m.outMode},
|
||||||
|
{m.err, m.errMode},
|
||||||
|
} {
|
||||||
|
if err := windows.SetConsoleMode(s.fd, s.mode); err != nil {
|
||||||
|
return errors.Wrap(err, "unable to restore console mode")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Size() (WinSize, error) {
|
func (m *master) Size() (WinSize, error) {
|
||||||
var info windows.ConsoleScreenBufferInfo
|
var info windows.ConsoleScreenBufferInfo
|
||||||
err := windows.GetConsoleScreenBufferInfo(m.h, &info)
|
err := windows.GetConsoleScreenBufferInfo(m.out, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return WinSize{}, errors.Wrap(err, "unable to get console info")
|
return WinSize{}, errors.Wrap(err, "unable to get console info")
|
||||||
}
|
}
|
||||||
|
@ -98,11 +134,11 @@ func (m *master) ResizeFrom(c Console) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) DisableEcho() error {
|
func (m *master) DisableEcho() error {
|
||||||
mode := m.mode &^ windows.ENABLE_ECHO_INPUT
|
mode := m.inMode &^ windows.ENABLE_ECHO_INPUT
|
||||||
mode |= windows.ENABLE_PROCESSED_INPUT
|
mode |= windows.ENABLE_PROCESSED_INPUT
|
||||||
mode |= windows.ENABLE_LINE_INPUT
|
mode |= windows.ENABLE_LINE_INPUT
|
||||||
|
|
||||||
if err := windows.SetConsoleMode(m.h, mode); err != nil {
|
if err := windows.SetConsoleMode(m.in, mode); err != nil {
|
||||||
return errors.Wrap(err, "unable to set console to disable echo")
|
return errors.Wrap(err, "unable to set console to disable echo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,15 +150,15 @@ func (m *master) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Read(b []byte) (int, error) {
|
func (m *master) Read(b []byte) (int, error) {
|
||||||
return m.f.Read(b)
|
panic("not implemented on windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Write(b []byte) (int, error) {
|
func (m *master) Write(b []byte) (int, error) {
|
||||||
return m.f.Write(b)
|
panic("not implemented on windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Fd() uintptr {
|
func (m *master) Fd() uintptr {
|
||||||
return uintptr(m.h)
|
return uintptr(m.in)
|
||||||
}
|
}
|
||||||
|
|
||||||
// on windows, console can only be made from os.Std{in,out,err}, hence there
|
// on windows, console can only be made from os.Std{in,out,err}, hence there
|
||||||
|
@ -174,7 +210,7 @@ func newMaster(f *os.File) (Console, error) {
|
||||||
if f != os.Stdin && f != os.Stdout && f != os.Stderr {
|
if f != os.Stdin && f != os.Stdout && f != os.Stderr {
|
||||||
return nil, errors.New("creating a console from a file is not supported on windows")
|
return nil, errors.New("creating a console from a file is not supported on windows")
|
||||||
}
|
}
|
||||||
m := &master{f: f}
|
m := &master{}
|
||||||
m.init()
|
m.initStdios()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ If you have [criu](https://criu.org/Main_Page) installed on your machine you can
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// checkpoint the task then push it to a registry
|
// checkpoint the task then push it to a registry
|
||||||
checkpoint, err := task.Checkpoint(context, containerd.WithExit)
|
checkpoint, err := task.Checkpoint(context)
|
||||||
|
|
||||||
err := client.Push(context, "myregistry/checkpoints/redis:master", checkpoint)
|
err := client.Push(context, "myregistry/checkpoints/redis:master", checkpoint)
|
||||||
|
|
||||||
|
@ -184,6 +184,28 @@ defer task.Delete(context)
|
||||||
err := task.Start(context)
|
err := task.Start(context)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Snapshot Plugins
|
||||||
|
|
||||||
|
In addition to the built-in Snapshot plugins in containerd, additional external
|
||||||
|
plugins can be configured using GRPC. An external plugin is made available using
|
||||||
|
the configured name and appears as a plugin alongside the built-in ones.
|
||||||
|
|
||||||
|
To add an external snapshot plugin, add the plugin to containerd's config file
|
||||||
|
(by default at `/etc/containerd/config.toml`). The string following
|
||||||
|
`proxy_plugin.` will be used as the name of the snapshotter and the address
|
||||||
|
should refer to a socket with a GRPC listener serving containerd's Snapshot
|
||||||
|
GRPC API. Remember to restart containerd for any configuration changes to take
|
||||||
|
effect.
|
||||||
|
|
||||||
|
```
|
||||||
|
[proxy_plugins]
|
||||||
|
[proxy_plugins.customsnapshot]
|
||||||
|
type = "snapshot"
|
||||||
|
address = "/var/run/mysnapshotter.sock"
|
||||||
|
```
|
||||||
|
|
||||||
|
See [PLUGINS.md](PLUGINS.md) for how to create plugins
|
||||||
|
|
||||||
### Releases and API Stability
|
### Releases and API Stability
|
||||||
|
|
||||||
Please see [RELEASES.md](RELEASES.md) for details on versioning and stability
|
Please see [RELEASES.md](RELEASES.md) for details on versioning and stability
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
This directory contains the GRPC API definitions for containerd.
|
||||||
|
|
||||||
|
All defined services and messages have been aggregated into `*.pb.txt`
|
||||||
|
descriptors files in this directory. Definitions present here are considered
|
||||||
|
frozen after the release.
|
||||||
|
|
||||||
|
At release time, the current `next.pb.txt` file will be moved into place to
|
||||||
|
freeze the API changes for the minor version. For example, when 1.0.0 is
|
||||||
|
released, `next.pb.txt` should be moved to `1.0.txt`. Notice that we leave off
|
||||||
|
the patch number, since the API will be completely locked down for a given
|
||||||
|
patch series.
|
||||||
|
|
||||||
|
We may find that by default, protobuf descriptors are too noisy to lock down
|
||||||
|
API changes. In that case, we may filter out certain fields in the descriptors,
|
||||||
|
possibly regenerating for old versions.
|
||||||
|
|
||||||
|
This process is similar to the [process used to ensure backwards compatibility
|
||||||
|
in Go](https://github.com/golang/go/tree/master/api).
|
2930
vendor/github.com/containerd/containerd/api/services/containers/v1/containers.pb.go
generated
vendored
Normal file
2930
vendor/github.com/containerd/containerd/api/services/containers/v1/containers.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
163
vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto
generated
vendored
Normal file
163
vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto
generated
vendored
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.containers.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/any.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/field_mask.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/containers/v1;containers";
|
||||||
|
|
||||||
|
// Containers provides metadata storage for containers used in the execution
|
||||||
|
// service.
|
||||||
|
//
|
||||||
|
// The objects here provide an state-independent view of containers for use in
|
||||||
|
// management and resource pinning. From that perspective, containers do not
|
||||||
|
// have a "state" but rather this is the set of resources that will be
|
||||||
|
// considered in use by the container.
|
||||||
|
//
|
||||||
|
// From the perspective of the execution service, these objects represent the
|
||||||
|
// base parameters for creating a container process.
|
||||||
|
//
|
||||||
|
// In general, when looking to add fields for this type, first ask yourself
|
||||||
|
// whether or not the function of the field has to do with runtime execution or
|
||||||
|
// is invariant of the runtime state of the container. If it has to do with
|
||||||
|
// runtime, or changes as the "container" is started and stops, it probably
|
||||||
|
// doesn't belong on this object.
|
||||||
|
service Containers {
|
||||||
|
rpc Get(GetContainerRequest) returns (GetContainerResponse);
|
||||||
|
rpc List(ListContainersRequest) returns (ListContainersResponse);
|
||||||
|
rpc ListStream(ListContainersRequest) returns (stream ListContainerMessage);
|
||||||
|
rpc Create(CreateContainerRequest) returns (CreateContainerResponse);
|
||||||
|
rpc Update(UpdateContainerRequest) returns (UpdateContainerResponse);
|
||||||
|
rpc Delete(DeleteContainerRequest) returns (google.protobuf.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message Container {
|
||||||
|
// ID is the user-specified identifier.
|
||||||
|
//
|
||||||
|
// This field may not be updated.
|
||||||
|
string id = 1;
|
||||||
|
|
||||||
|
// Labels provides an area to include arbitrary data on containers.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
//
|
||||||
|
// Note that to add a new value to this field, read the existing set and
|
||||||
|
// include the entire result in the update call.
|
||||||
|
map<string, string> labels = 2;
|
||||||
|
|
||||||
|
// Image contains the reference of the image used to build the
|
||||||
|
// specification and snapshots for running this container.
|
||||||
|
//
|
||||||
|
// If this field is updated, the spec and rootfs needed to updated, as well.
|
||||||
|
string image = 3;
|
||||||
|
|
||||||
|
message Runtime {
|
||||||
|
// Name is the name of the runtime.
|
||||||
|
string name = 1;
|
||||||
|
// Options specify additional runtime initialization options.
|
||||||
|
google.protobuf.Any options = 2;
|
||||||
|
}
|
||||||
|
// Runtime specifies which runtime to use for executing this container.
|
||||||
|
Runtime runtime = 4;
|
||||||
|
|
||||||
|
// Spec to be used when creating the container. This is runtime specific.
|
||||||
|
google.protobuf.Any spec = 5;
|
||||||
|
|
||||||
|
// Snapshotter specifies the snapshotter name used for rootfs
|
||||||
|
string snapshotter = 6;
|
||||||
|
|
||||||
|
// SnapshotKey specifies the snapshot key to use for the container's root
|
||||||
|
// filesystem. When starting a task from this container, a caller should
|
||||||
|
// look up the mounts from the snapshot service and include those on the
|
||||||
|
// task create request.
|
||||||
|
//
|
||||||
|
// Snapshots referenced in this field will not be garbage collected.
|
||||||
|
//
|
||||||
|
// This field is set to empty when the rootfs is not a snapshot.
|
||||||
|
//
|
||||||
|
// This field may be updated.
|
||||||
|
string snapshot_key = 7;
|
||||||
|
|
||||||
|
// CreatedAt is the time the container was first created.
|
||||||
|
google.protobuf.Timestamp created_at = 8 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdatedAt is the last time the container was mutated.
|
||||||
|
google.protobuf.Timestamp updated_at = 9 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Extensions allow clients to provide zero or more blobs that are directly
|
||||||
|
// associated with the container. One may provide protobuf, json, or other
|
||||||
|
// encoding formats. The primary use of this is to further decorate the
|
||||||
|
// container object with fields that may be specific to a client integration.
|
||||||
|
//
|
||||||
|
// The key portion of this map should identify a "name" for the extension
|
||||||
|
// that should be unique against other extensions. When updating extension
|
||||||
|
// data, one should only update the specified extension using field paths
|
||||||
|
// to select a specific map key.
|
||||||
|
map<string, google.protobuf.Any> extensions = 10 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetContainerRequest {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetContainerResponse {
|
||||||
|
Container container = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListContainersRequest {
|
||||||
|
// Filters contains one or more filters using the syntax defined in the
|
||||||
|
// containerd filter package.
|
||||||
|
//
|
||||||
|
// The returned result will be those that match any of the provided
|
||||||
|
// filters. Expanded, containers that match the following will be
|
||||||
|
// returned:
|
||||||
|
//
|
||||||
|
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||||
|
//
|
||||||
|
// If filters is zero-length or nil, all items will be returned.
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListContainersResponse {
|
||||||
|
repeated Container containers = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateContainerRequest {
|
||||||
|
Container container = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateContainerResponse {
|
||||||
|
Container container = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateContainerRequest updates the metadata on one or more container.
|
||||||
|
//
|
||||||
|
// The operation should follow semantics described in
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-mask,
|
||||||
|
// unless otherwise qualified.
|
||||||
|
message UpdateContainerRequest {
|
||||||
|
// Container provides the target values, as declared by the mask, for the update.
|
||||||
|
//
|
||||||
|
// The ID field must be set.
|
||||||
|
Container container = 1 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||||
|
// the operation applies to all fields.
|
||||||
|
google.protobuf.FieldMask update_mask = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateContainerResponse {
|
||||||
|
Container container = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteContainerRequest {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListContainerMessage {
|
||||||
|
Container container = 1;
|
||||||
|
}
|
4447
vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go
generated
vendored
Normal file
4447
vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
318
vendor/github.com/containerd/containerd/api/services/content/v1/content.proto
generated
vendored
Normal file
318
vendor/github.com/containerd/containerd/api/services/content/v1/content.proto
generated
vendored
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.content.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/field_mask.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/content/v1;content";
|
||||||
|
|
||||||
|
// Content provides access to a content addressable storage system.
|
||||||
|
service Content {
|
||||||
|
// Info returns information about a committed object.
|
||||||
|
//
|
||||||
|
// This call can be used for getting the size of content and checking for
|
||||||
|
// existence.
|
||||||
|
rpc Info(InfoRequest) returns (InfoResponse);
|
||||||
|
|
||||||
|
// Update updates content metadata.
|
||||||
|
//
|
||||||
|
// This call can be used to manage the mutable content labels. The
|
||||||
|
// immutable metadata such as digest, size, and committed at cannot
|
||||||
|
// be updated.
|
||||||
|
rpc Update(UpdateRequest) returns (UpdateResponse);
|
||||||
|
|
||||||
|
// List streams the entire set of content as Info objects and closes the
|
||||||
|
// stream.
|
||||||
|
//
|
||||||
|
// Typically, this will yield a large response, chunked into messages.
|
||||||
|
// Clients should make provisions to ensure they can handle the entire data
|
||||||
|
// set.
|
||||||
|
rpc List(ListContentRequest) returns (stream ListContentResponse);
|
||||||
|
|
||||||
|
// Delete will delete the referenced object.
|
||||||
|
rpc Delete(DeleteContentRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
// Read allows one to read an object based on the offset into the content.
|
||||||
|
//
|
||||||
|
// The requested data may be returned in one or more messages.
|
||||||
|
rpc Read(ReadContentRequest) returns (stream ReadContentResponse);
|
||||||
|
|
||||||
|
// Status returns the status for a single reference.
|
||||||
|
rpc Status(StatusRequest) returns (StatusResponse);
|
||||||
|
|
||||||
|
// ListStatuses returns the status of ongoing object ingestions, started via
|
||||||
|
// Write.
|
||||||
|
//
|
||||||
|
// Only those matching the regular expression will be provided in the
|
||||||
|
// response. If the provided regular expression is empty, all ingestions
|
||||||
|
// will be provided.
|
||||||
|
rpc ListStatuses(ListStatusesRequest) returns (ListStatusesResponse);
|
||||||
|
|
||||||
|
// Write begins or resumes writes to a resource identified by a unique ref.
|
||||||
|
// Only one active stream may exist at a time for each ref.
|
||||||
|
//
|
||||||
|
// Once a write stream has started, it may only write to a single ref, thus
|
||||||
|
// once a stream is started, the ref may be ommitted on subsequent writes.
|
||||||
|
//
|
||||||
|
// For any write transaction represented by a ref, only a single write may
|
||||||
|
// be made to a given offset. If overlapping writes occur, it is an error.
|
||||||
|
// Writes should be sequential and implementations may throw an error if
|
||||||
|
// this is required.
|
||||||
|
//
|
||||||
|
// If expected_digest is set and already part of the content store, the
|
||||||
|
// write will fail.
|
||||||
|
//
|
||||||
|
// When completed, the commit flag should be set to true. If expected size
|
||||||
|
// or digest is set, the content will be validated against those values.
|
||||||
|
rpc Write(stream WriteContentRequest) returns (stream WriteContentResponse);
|
||||||
|
|
||||||
|
// Abort cancels the ongoing write named in the request. Any resources
|
||||||
|
// associated with the write will be collected.
|
||||||
|
rpc Abort(AbortRequest) returns (google.protobuf.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message Info {
|
||||||
|
// Digest is the hash identity of the blob.
|
||||||
|
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Size is the total number of bytes in the blob.
|
||||||
|
int64 size = 2;
|
||||||
|
|
||||||
|
// CreatedAt provides the time at which the blob was committed.
|
||||||
|
google.protobuf.Timestamp created_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdatedAt provides the time the info was last updated.
|
||||||
|
google.protobuf.Timestamp updated_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Labels are arbitrary data on snapshots.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
map<string, string> labels = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InfoRequest {
|
||||||
|
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message InfoResponse {
|
||||||
|
Info info = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateRequest {
|
||||||
|
Info info = 1 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||||
|
// the operation applies to all fields.
|
||||||
|
//
|
||||||
|
// In info, Digest, Size, and CreatedAt are immutable,
|
||||||
|
// other field may be updated using this mask.
|
||||||
|
// If no mask is provided, all mutable field are updated.
|
||||||
|
google.protobuf.FieldMask update_mask = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateResponse {
|
||||||
|
Info info = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListContentRequest {
|
||||||
|
// Filters contains one or more filters using the syntax defined in the
|
||||||
|
// containerd filter package.
|
||||||
|
//
|
||||||
|
// The returned result will be those that match any of the provided
|
||||||
|
// filters. Expanded, containers that match the following will be
|
||||||
|
// returned:
|
||||||
|
//
|
||||||
|
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||||
|
//
|
||||||
|
// If filters is zero-length or nil, all items will be returned.
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListContentResponse {
|
||||||
|
repeated Info info = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteContentRequest {
|
||||||
|
// Digest specifies which content to delete.
|
||||||
|
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadContentRequest defines the fields that make up a request to read a portion of
|
||||||
|
// data from a stored object.
|
||||||
|
message ReadContentRequest {
|
||||||
|
// Digest is the hash identity to read.
|
||||||
|
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Offset specifies the number of bytes from the start at which to begin
|
||||||
|
// the read. If zero or less, the read will be from the start. This uses
|
||||||
|
// standard zero-indexed semantics.
|
||||||
|
int64 offset = 2;
|
||||||
|
|
||||||
|
// size is the total size of the read. If zero, the entire blob will be
|
||||||
|
// returned by the service.
|
||||||
|
int64 size = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadContentResponse carries byte data for a read request.
|
||||||
|
message ReadContentResponse {
|
||||||
|
int64 offset = 1; // offset of the returned data
|
||||||
|
bytes data = 2; // actual data
|
||||||
|
}
|
||||||
|
|
||||||
|
message Status {
|
||||||
|
google.protobuf.Timestamp started_at = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
google.protobuf.Timestamp updated_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
string ref = 3;
|
||||||
|
int64 offset = 4;
|
||||||
|
int64 total = 5;
|
||||||
|
string expected = 6 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message StatusRequest {
|
||||||
|
string ref = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatusResponse {
|
||||||
|
Status status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListStatusesRequest {
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListStatusesResponse {
|
||||||
|
repeated Status statuses = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAction defines the behavior of a WriteRequest.
|
||||||
|
enum WriteAction {
|
||||||
|
option (gogoproto.goproto_enum_prefix) = false;
|
||||||
|
option (gogoproto.enum_customname) = "WriteAction";
|
||||||
|
|
||||||
|
// WriteActionStat instructs the writer to return the current status while
|
||||||
|
// holding the lock on the write.
|
||||||
|
STAT = 0 [(gogoproto.enumvalue_customname) = "WriteActionStat"];
|
||||||
|
|
||||||
|
// WriteActionWrite sets the action for the write request to write data.
|
||||||
|
//
|
||||||
|
// Any data included will be written at the provided offset. The
|
||||||
|
// transaction will be left open for further writes.
|
||||||
|
//
|
||||||
|
// This is the default.
|
||||||
|
WRITE = 1 [(gogoproto.enumvalue_customname) = "WriteActionWrite"];
|
||||||
|
|
||||||
|
// WriteActionCommit will write any outstanding data in the message and
|
||||||
|
// commit the write, storing it under the digest.
|
||||||
|
//
|
||||||
|
// This can be used in a single message to send the data, verify it and
|
||||||
|
// commit it.
|
||||||
|
//
|
||||||
|
// This action will always terminate the write.
|
||||||
|
COMMIT = 2 [(gogoproto.enumvalue_customname) = "WriteActionCommit"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteContentRequest writes data to the request ref at offset.
|
||||||
|
message WriteContentRequest {
|
||||||
|
// Action sets the behavior of the write.
|
||||||
|
//
|
||||||
|
// When this is a write and the ref is not yet allocated, the ref will be
|
||||||
|
// allocated and the data will be written at offset.
|
||||||
|
//
|
||||||
|
// If the action is write and the ref is allocated, it will accept data to
|
||||||
|
// an offset that has not yet been written.
|
||||||
|
//
|
||||||
|
// If the action is write and there is no data, the current write status
|
||||||
|
// will be returned. This works differently from status because the stream
|
||||||
|
// holds a lock.
|
||||||
|
WriteAction action = 1;
|
||||||
|
|
||||||
|
// Ref identifies the pre-commit object to write to.
|
||||||
|
string ref = 2;
|
||||||
|
|
||||||
|
// Total can be set to have the service validate the total size of the
|
||||||
|
// committed content.
|
||||||
|
//
|
||||||
|
// The latest value before or with the commit action message will be use to
|
||||||
|
// validate the content. If the offset overflows total, the service may
|
||||||
|
// report an error. It is only required on one message for the write.
|
||||||
|
//
|
||||||
|
// If the value is zero or less, no validation of the final content will be
|
||||||
|
// performed.
|
||||||
|
int64 total = 3;
|
||||||
|
|
||||||
|
// Expected can be set to have the service validate the final content against
|
||||||
|
// the provided digest.
|
||||||
|
//
|
||||||
|
// If the digest is already present in the object store, an AlreadyExists
|
||||||
|
// error will be returned.
|
||||||
|
//
|
||||||
|
// Only the latest version will be used to check the content against the
|
||||||
|
// digest. It is only required to include it on a single message, before or
|
||||||
|
// with the commit action message.
|
||||||
|
string expected = 4 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Offset specifies the number of bytes from the start at which to begin
|
||||||
|
// the write. For most implementations, this means from the start of the
|
||||||
|
// file. This uses standard, zero-indexed semantics.
|
||||||
|
//
|
||||||
|
// If the action is write, the remote may remove all previously written
|
||||||
|
// data after the offset. Implementations may support arbitrary offsets but
|
||||||
|
// MUST support reseting this value to zero with a write. If an
|
||||||
|
// implementation does not support a write at a particular offset, an
|
||||||
|
// OutOfRange error must be returned.
|
||||||
|
int64 offset = 5;
|
||||||
|
|
||||||
|
// Data is the actual bytes to be written.
|
||||||
|
//
|
||||||
|
// If this is empty and the message is not a commit, a response will be
|
||||||
|
// returned with the current write state.
|
||||||
|
bytes data = 6;
|
||||||
|
|
||||||
|
// Labels are arbitrary data on snapshots.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
map<string, string> labels = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteContentResponse is returned on the culmination of a write call.
|
||||||
|
message WriteContentResponse {
|
||||||
|
// Action contains the action for the final message of the stream. A writer
|
||||||
|
// should confirm that they match the intended result.
|
||||||
|
WriteAction action = 1;
|
||||||
|
|
||||||
|
// StartedAt provides the time at which the write began.
|
||||||
|
//
|
||||||
|
// This must be set for stat and commit write actions. All other write
|
||||||
|
// actions may omit this.
|
||||||
|
google.protobuf.Timestamp started_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdatedAt provides the last time of a successful write.
|
||||||
|
//
|
||||||
|
// This must be set for stat and commit write actions. All other write
|
||||||
|
// actions may omit this.
|
||||||
|
google.protobuf.Timestamp updated_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Offset is the current committed size for the write.
|
||||||
|
int64 offset = 4;
|
||||||
|
|
||||||
|
// Total provides the current, expected total size of the write.
|
||||||
|
//
|
||||||
|
// We include this to provide consistency with the Status structure on the
|
||||||
|
// client writer.
|
||||||
|
//
|
||||||
|
// This is only valid on the Stat and Commit response.
|
||||||
|
int64 total = 5;
|
||||||
|
|
||||||
|
// Digest, if present, includes the digest up to the currently committed
|
||||||
|
// bytes. If action is commit, this field will be set. It is implementation
|
||||||
|
// defined if this is set for other actions.
|
||||||
|
string digest = 6 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message AbortRequest {
|
||||||
|
string ref = 1;
|
||||||
|
}
|
1250
vendor/github.com/containerd/containerd/api/services/diff/v1/diff.pb.go
generated
vendored
Normal file
1250
vendor/github.com/containerd/containerd/api/services/diff/v1/diff.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
62
vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto
generated
vendored
Normal file
62
vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.diff.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/diff/v1;diff";
|
||||||
|
|
||||||
|
// Diff service creates and applies diffs
|
||||||
|
service Diff {
|
||||||
|
// Apply applies the content associated with the provided digests onto
|
||||||
|
// the provided mounts. Archive content will be extracted and
|
||||||
|
// decompressed if necessary.
|
||||||
|
rpc Apply(ApplyRequest) returns (ApplyResponse);
|
||||||
|
|
||||||
|
// Diff creates a diff between the given mounts and uploads the result
|
||||||
|
// to the content store.
|
||||||
|
rpc Diff(DiffRequest) returns (DiffResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message ApplyRequest {
|
||||||
|
// Diff is the descriptor of the diff to be extracted
|
||||||
|
containerd.types.Descriptor diff = 1;
|
||||||
|
|
||||||
|
repeated containerd.types.Mount mounts = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ApplyResponse {
|
||||||
|
// Applied is the descriptor for the object which was applied.
|
||||||
|
// If the input was a compressed blob then the result will be
|
||||||
|
// the descriptor for the uncompressed blob.
|
||||||
|
containerd.types.Descriptor applied = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DiffRequest {
|
||||||
|
// Left are the mounts which represent the older copy
|
||||||
|
// in which is the base of the computed changes.
|
||||||
|
repeated containerd.types.Mount left = 1;
|
||||||
|
|
||||||
|
// Right are the mounts which represents the newer copy
|
||||||
|
// in which changes from the left were made into.
|
||||||
|
repeated containerd.types.Mount right = 2;
|
||||||
|
|
||||||
|
// MediaType is the media type descriptor for the created diff
|
||||||
|
// object
|
||||||
|
string media_type = 3;
|
||||||
|
|
||||||
|
// Ref identifies the pre-commit content store object. This
|
||||||
|
// reference can be used to get the status from the content store.
|
||||||
|
string ref = 4;
|
||||||
|
|
||||||
|
// Labels are the labels to apply to the generated content
|
||||||
|
// on content store commit.
|
||||||
|
map<string, string> labels = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DiffResponse {
|
||||||
|
// Diff is the descriptor of the diff which can be applied
|
||||||
|
containerd.types.Descriptor diff = 3;
|
||||||
|
}
|
18
vendor/github.com/containerd/containerd/api/services/events/v1/doc.go
generated
vendored
Normal file
18
vendor/github.com/containerd/containerd/api/services/events/v1/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package events defines the event pushing and subscription service.
|
||||||
|
package events
|
1182
vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go
generated
vendored
Normal file
1182
vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
56
vendor/github.com/containerd/containerd/api/services/events/v1/events.proto
generated
vendored
Normal file
56
vendor/github.com/containerd/containerd/api/services/events/v1/events.proto
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.events.v1;
|
||||||
|
|
||||||
|
import weak "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/any.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/events/v1;events";
|
||||||
|
|
||||||
|
service Events {
|
||||||
|
// Publish an event to a topic.
|
||||||
|
//
|
||||||
|
// The event will be packed into a timestamp envelope with the namespace
|
||||||
|
// introspected from the context. The envelope will then be dispatched.
|
||||||
|
rpc Publish(PublishRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
// Forward sends an event that has already been packaged into an envelope
|
||||||
|
// with a timestamp and namespace.
|
||||||
|
//
|
||||||
|
// This is useful if earlier timestamping is required or when fowarding on
|
||||||
|
// behalf of another component, namespace or publisher.
|
||||||
|
rpc Forward(ForwardRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
// Subscribe to a stream of events, possibly returning only that match any
|
||||||
|
// of the provided filters.
|
||||||
|
//
|
||||||
|
// Unlike many other methods in containerd, subscribers will get messages
|
||||||
|
// from all namespaces unless otherwise specified. If this is not desired,
|
||||||
|
// a filter can be provided in the format 'namespace==<namespace>' to
|
||||||
|
// restrict the received events.
|
||||||
|
rpc Subscribe(SubscribeRequest) returns (stream Envelope);
|
||||||
|
}
|
||||||
|
|
||||||
|
message PublishRequest {
|
||||||
|
string topic = 1;
|
||||||
|
google.protobuf.Any event = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ForwardRequest {
|
||||||
|
Envelope envelope = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequest {
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Envelope {
|
||||||
|
option (containerd.plugin.fieldpath) = true;
|
||||||
|
google.protobuf.Timestamp timestamp = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
string namespace = 2;
|
||||||
|
string topic = 3;
|
||||||
|
google.protobuf.Any event = 4;
|
||||||
|
}
|
17
vendor/github.com/containerd/containerd/api/services/images/v1/docs.go
generated
vendored
Normal file
17
vendor/github.com/containerd/containerd/api/services/images/v1/docs.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package images
|
2213
vendor/github.com/containerd/containerd/api/services/images/v1/images.pb.go
generated
vendored
Normal file
2213
vendor/github.com/containerd/containerd/api/services/images/v1/images.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
124
vendor/github.com/containerd/containerd/api/services/images/v1/images.proto
generated
vendored
Normal file
124
vendor/github.com/containerd/containerd/api/services/images/v1/images.proto
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.images.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/field_mask.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/images/v1;images";
|
||||||
|
|
||||||
|
// Images is a service that allows one to register images with containerd.
|
||||||
|
//
|
||||||
|
// In containerd, an image is merely the mapping of a name to a content root,
|
||||||
|
// described by a descriptor. The behavior and state of image is purely
|
||||||
|
// dictated by the type of the descriptor.
|
||||||
|
//
|
||||||
|
// From the perspective of this service, these references are mostly shallow,
|
||||||
|
// in that the existence of the required content won't be validated until
|
||||||
|
// required by consuming services.
|
||||||
|
//
|
||||||
|
// As such, this can really be considered a "metadata service".
|
||||||
|
service Images {
|
||||||
|
// Get returns an image by name.
|
||||||
|
rpc Get(GetImageRequest) returns (GetImageResponse);
|
||||||
|
|
||||||
|
// List returns a list of all images known to containerd.
|
||||||
|
rpc List(ListImagesRequest) returns (ListImagesResponse);
|
||||||
|
|
||||||
|
// Create an image record in the metadata store.
|
||||||
|
//
|
||||||
|
// The name of the image must be unique.
|
||||||
|
rpc Create(CreateImageRequest) returns (CreateImageResponse);
|
||||||
|
|
||||||
|
// Update assigns the name to a given target image based on the provided
|
||||||
|
// image.
|
||||||
|
rpc Update(UpdateImageRequest) returns (UpdateImageResponse);
|
||||||
|
|
||||||
|
// Delete deletes the image by name.
|
||||||
|
rpc Delete(DeleteImageRequest) returns (google.protobuf.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message Image {
|
||||||
|
// Name provides a unique name for the image.
|
||||||
|
//
|
||||||
|
// Containerd treats this as the primary identifier.
|
||||||
|
string name = 1;
|
||||||
|
|
||||||
|
// Labels provides free form labels for the image. These are runtime only
|
||||||
|
// and do not get inherited into the package image in any way.
|
||||||
|
//
|
||||||
|
// Labels may be updated using the field mask.
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
map<string, string> labels = 2;
|
||||||
|
|
||||||
|
// Target describes the content entry point of the image.
|
||||||
|
containerd.types.Descriptor target = 3 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// CreatedAt is the time the image was first created.
|
||||||
|
google.protobuf.Timestamp created_at = 7 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdatedAt is the last time the image was mutated.
|
||||||
|
google.protobuf.Timestamp updated_at = 8 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetImageRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetImageResponse {
|
||||||
|
Image image = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateImageRequest {
|
||||||
|
Image image = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateImageResponse {
|
||||||
|
Image image = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateImageRequest {
|
||||||
|
// Image provides a full or partial image for update.
|
||||||
|
//
|
||||||
|
// The name field must be set or an error will be returned.
|
||||||
|
Image image = 1 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||||
|
// the operation applies to all fields.
|
||||||
|
google.protobuf.FieldMask update_mask = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateImageResponse {
|
||||||
|
Image image = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListImagesRequest {
|
||||||
|
// Filters contains one or more filters using the syntax defined in the
|
||||||
|
// containerd filter package.
|
||||||
|
//
|
||||||
|
// The returned result will be those that match any of the provided
|
||||||
|
// filters. Expanded, images that match the following will be
|
||||||
|
// returned:
|
||||||
|
//
|
||||||
|
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||||
|
//
|
||||||
|
// If filters is zero-length or nil, all items will be returned.
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListImagesResponse {
|
||||||
|
repeated Image images = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteImageRequest {
|
||||||
|
string name = 1;
|
||||||
|
|
||||||
|
// Sync indicates that the delete and cleanup should be done
|
||||||
|
// synchronously before returning to the caller
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
bool sync = 2;
|
||||||
|
}
|
17
vendor/github.com/containerd/containerd/api/services/introspection/v1/doc.go
generated
vendored
Normal file
17
vendor/github.com/containerd/containerd/api/services/introspection/v1/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package introspection
|
1157
vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.pb.go
generated
vendored
Normal file
1157
vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
81
vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.proto
generated
vendored
Normal file
81
vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.proto
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.introspection.v1;
|
||||||
|
|
||||||
|
import "github.com/containerd/containerd/api/types/platform.proto";
|
||||||
|
import "google/rpc/status.proto";
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/introspection/v1;introspection";
|
||||||
|
|
||||||
|
service Introspection {
|
||||||
|
// Plugins returns a list of plugins in containerd.
|
||||||
|
//
|
||||||
|
// Clients can use this to detect features and capabilities when using
|
||||||
|
// containerd.
|
||||||
|
rpc Plugins(PluginsRequest) returns (PluginsResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message Plugin {
|
||||||
|
// Type defines the type of plugin.
|
||||||
|
//
|
||||||
|
// See package plugin for a list of possible values. Non core plugins may
|
||||||
|
// define their own values during registration.
|
||||||
|
string type = 1;
|
||||||
|
|
||||||
|
// ID identifies the plugin uniquely in the system.
|
||||||
|
string id = 2;
|
||||||
|
|
||||||
|
// Requires lists the plugin types required by this plugin.
|
||||||
|
repeated string requires = 3;
|
||||||
|
|
||||||
|
// Platforms enumerates the platforms this plugin will support.
|
||||||
|
//
|
||||||
|
// If values are provided here, the plugin will only be operable under the
|
||||||
|
// provided platforms.
|
||||||
|
//
|
||||||
|
// If this is empty, the plugin will work across all platforms.
|
||||||
|
//
|
||||||
|
// If the plugin prefers certain platforms over others, they should be
|
||||||
|
// listed from most to least preferred.
|
||||||
|
repeated types.Platform platforms = 4 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Exports allows plugins to provide values about state or configuration to
|
||||||
|
// interested parties.
|
||||||
|
//
|
||||||
|
// One example is exposing the configured path of a snapshotter plugin.
|
||||||
|
map<string, string> exports = 5;
|
||||||
|
|
||||||
|
// Capabilities allows plugins to communicate feature switches to allow
|
||||||
|
// clients to detect features that may not be on be default or may be
|
||||||
|
// different from version to version.
|
||||||
|
//
|
||||||
|
// Use this sparingly.
|
||||||
|
repeated string capabilities = 6;
|
||||||
|
|
||||||
|
// InitErr will be set if the plugin fails initialization.
|
||||||
|
//
|
||||||
|
// This means the plugin may have been registered but a non-terminal error
|
||||||
|
// was encountered during initialization.
|
||||||
|
//
|
||||||
|
// Plugins that have this value set cannot be used.
|
||||||
|
google.rpc.Status init_err = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PluginsRequest {
|
||||||
|
// Filters contains one or more filters using the syntax defined in the
|
||||||
|
// containerd filter package.
|
||||||
|
//
|
||||||
|
// The returned result will be those that match any of the provided
|
||||||
|
// filters. Expanded, plugins that match the following will be
|
||||||
|
// returned:
|
||||||
|
//
|
||||||
|
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||||
|
//
|
||||||
|
// If filters is zero-length or nil, all items will be returned.
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PluginsResponse {
|
||||||
|
repeated Plugin plugins = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
17
vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go
generated
vendored
Normal file
17
vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package leases
|
1597
vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go
generated
vendored
Normal file
1597
vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
64
vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto
generated
vendored
Normal file
64
vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.leases.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/leases/v1;leases";
|
||||||
|
|
||||||
|
// Leases service manages resources leases within the metadata store.
|
||||||
|
service Leases {
|
||||||
|
// Create creates a new lease for managing changes to metadata. A lease
|
||||||
|
// can be used to protect objects from being removed.
|
||||||
|
rpc Create(CreateRequest) returns (CreateResponse);
|
||||||
|
|
||||||
|
// Delete deletes the lease and makes any unreferenced objects created
|
||||||
|
// during the lease eligible for garbage collection if not referenced
|
||||||
|
// or retained by other resources during the lease.
|
||||||
|
rpc Delete(DeleteRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
// List lists all active leases, returning the full list of
|
||||||
|
// leases and optionally including the referenced resources.
|
||||||
|
rpc List(ListRequest) returns (ListResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lease is an object which retains resources while it exists.
|
||||||
|
message Lease {
|
||||||
|
string id = 1;
|
||||||
|
|
||||||
|
google.protobuf.Timestamp created_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
map<string, string> labels = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateRequest {
|
||||||
|
// ID is used to identity the lease, when the id is not set the service
|
||||||
|
// generates a random identifier for the lease.
|
||||||
|
string id = 1;
|
||||||
|
|
||||||
|
map<string, string> labels = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateResponse {
|
||||||
|
Lease lease = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteRequest {
|
||||||
|
string id = 1;
|
||||||
|
|
||||||
|
// Sync indicates that the delete and cleanup should be done
|
||||||
|
// synchronously before returning to the caller
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
bool sync = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListRequest {
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListResponse {
|
||||||
|
repeated Lease leases = 1;
|
||||||
|
}
|
1994
vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.pb.go
generated
vendored
Normal file
1994
vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
92
vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto
generated
vendored
Normal file
92
vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.namespaces.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/field_mask.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/namespaces/v1;namespaces";
|
||||||
|
|
||||||
|
// Namespaces provides the ability to manipulate containerd namespaces.
|
||||||
|
//
|
||||||
|
// All objects in the system are required to be a member of a namespace. If a
|
||||||
|
// namespace is deleted, all objects, including containers, images and
|
||||||
|
// snapshots, will be deleted, as well.
|
||||||
|
//
|
||||||
|
// Unless otherwise noted, operations in containerd apply only to the namespace
|
||||||
|
// supplied per request.
|
||||||
|
//
|
||||||
|
// I hope this goes without saying, but namespaces are themselves NOT
|
||||||
|
// namespaced.
|
||||||
|
service Namespaces {
|
||||||
|
rpc Get(GetNamespaceRequest) returns (GetNamespaceResponse);
|
||||||
|
rpc List(ListNamespacesRequest) returns (ListNamespacesResponse);
|
||||||
|
rpc Create(CreateNamespaceRequest) returns (CreateNamespaceResponse);
|
||||||
|
rpc Update(UpdateNamespaceRequest) returns (UpdateNamespaceResponse);
|
||||||
|
rpc Delete(DeleteNamespaceRequest) returns (google.protobuf.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message Namespace {
|
||||||
|
string name = 1;
|
||||||
|
|
||||||
|
// Labels provides an area to include arbitrary data on namespaces.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
//
|
||||||
|
// Note that to add a new value to this field, read the existing set and
|
||||||
|
// include the entire result in the update call.
|
||||||
|
map<string, string> labels = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetNamespaceRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetNamespaceResponse {
|
||||||
|
Namespace namespace = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListNamespacesRequest {
|
||||||
|
string filter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListNamespacesResponse {
|
||||||
|
repeated Namespace namespaces = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateNamespaceRequest {
|
||||||
|
Namespace namespace = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateNamespaceResponse {
|
||||||
|
Namespace namespace = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateNamespaceRequest updates the metadata for a namespace.
|
||||||
|
//
|
||||||
|
// The operation should follow semantics described in
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-mask,
|
||||||
|
// unless otherwise qualified.
|
||||||
|
message UpdateNamespaceRequest {
|
||||||
|
// Namespace provides the target value, as declared by the mask, for the update.
|
||||||
|
//
|
||||||
|
// The namespace field must be set.
|
||||||
|
Namespace namespace = 1 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||||
|
// the operation applies to all fields.
|
||||||
|
//
|
||||||
|
// For the most part, this applies only to selectively updating labels on
|
||||||
|
// the namespace. While field masks are typically limited to ascii alphas
|
||||||
|
// and digits, we just take everything after the "labels." as the map key.
|
||||||
|
google.protobuf.FieldMask update_mask = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateNamespaceResponse {
|
||||||
|
Namespace namespace = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteNamespaceRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
4263
vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.pb.go
generated
vendored
Normal file
4263
vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
150
vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto
generated
vendored
Normal file
150
vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.snapshots.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/field_mask.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/snapshots/v1;snapshots";
|
||||||
|
|
||||||
|
// Snapshot service manages snapshots
|
||||||
|
service Snapshots {
|
||||||
|
rpc Prepare(PrepareSnapshotRequest) returns (PrepareSnapshotResponse);
|
||||||
|
rpc View(ViewSnapshotRequest) returns (ViewSnapshotResponse);
|
||||||
|
rpc Mounts(MountsRequest) returns (MountsResponse);
|
||||||
|
rpc Commit(CommitSnapshotRequest) returns (google.protobuf.Empty);
|
||||||
|
rpc Remove(RemoveSnapshotRequest) returns (google.protobuf.Empty);
|
||||||
|
rpc Stat(StatSnapshotRequest) returns (StatSnapshotResponse);
|
||||||
|
rpc Update(UpdateSnapshotRequest) returns (UpdateSnapshotResponse);
|
||||||
|
rpc List(ListSnapshotsRequest) returns (stream ListSnapshotsResponse);
|
||||||
|
rpc Usage(UsageRequest) returns (UsageResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message PrepareSnapshotRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
string key = 2;
|
||||||
|
string parent = 3;
|
||||||
|
|
||||||
|
// Labels are arbitrary data on snapshots.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
map<string, string> labels = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PrepareSnapshotResponse {
|
||||||
|
repeated containerd.types.Mount mounts = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ViewSnapshotRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
string key = 2;
|
||||||
|
string parent = 3;
|
||||||
|
|
||||||
|
// Labels are arbitrary data on snapshots.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
map<string, string> labels = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ViewSnapshotResponse {
|
||||||
|
repeated containerd.types.Mount mounts = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MountsRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
string key = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MountsResponse {
|
||||||
|
repeated containerd.types.Mount mounts = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RemoveSnapshotRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
string key = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CommitSnapshotRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
string name = 2;
|
||||||
|
string key = 3;
|
||||||
|
|
||||||
|
// Labels are arbitrary data on snapshots.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
map<string, string> labels = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatSnapshotRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
string key = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Kind {
|
||||||
|
option (gogoproto.goproto_enum_prefix) = false;
|
||||||
|
option (gogoproto.enum_customname) = "Kind";
|
||||||
|
|
||||||
|
UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "KindUnknown"];
|
||||||
|
VIEW = 1 [(gogoproto.enumvalue_customname) = "KindView"];
|
||||||
|
ACTIVE = 2 [(gogoproto.enumvalue_customname) = "KindActive"];
|
||||||
|
COMMITTED = 3 [(gogoproto.enumvalue_customname) = "KindCommitted"];
|
||||||
|
}
|
||||||
|
|
||||||
|
message Info {
|
||||||
|
string name = 1;
|
||||||
|
string parent = 2;
|
||||||
|
Kind kind = 3;
|
||||||
|
|
||||||
|
// CreatedAt provides the time at which the snapshot was created.
|
||||||
|
google.protobuf.Timestamp created_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdatedAt provides the time the info was last updated.
|
||||||
|
google.protobuf.Timestamp updated_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// Labels are arbitrary data on snapshots.
|
||||||
|
//
|
||||||
|
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||||
|
map<string, string> labels = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatSnapshotResponse {
|
||||||
|
Info info = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateSnapshotRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
Info info = 2 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||||
|
// the operation applies to all fields.
|
||||||
|
//
|
||||||
|
// In info, Name, Parent, Kind, Created are immutable,
|
||||||
|
// other field may be updated using this mask.
|
||||||
|
// If no mask is provided, all mutable field are updated.
|
||||||
|
google.protobuf.FieldMask update_mask = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateSnapshotResponse {
|
||||||
|
Info info = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListSnapshotsRequest{
|
||||||
|
string snapshotter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListSnapshotsResponse {
|
||||||
|
repeated Info info = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message UsageRequest {
|
||||||
|
string snapshotter = 1;
|
||||||
|
string key = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UsageResponse {
|
||||||
|
int64 size = 1;
|
||||||
|
int64 inodes = 2;
|
||||||
|
}
|
5792
vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.pb.go
generated
vendored
Normal file
5792
vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
209
vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto
generated
vendored
Normal file
209
vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto
generated
vendored
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.tasks.v1;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/any.proto";
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/metrics.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||||
|
import "github.com/containerd/containerd/api/types/task/task.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/tasks/v1;tasks";
|
||||||
|
|
||||||
|
service Tasks {
|
||||||
|
// Create a task.
|
||||||
|
rpc Create(CreateTaskRequest) returns (CreateTaskResponse);
|
||||||
|
|
||||||
|
// Start a process.
|
||||||
|
rpc Start(StartRequest) returns (StartResponse);
|
||||||
|
|
||||||
|
// Delete a task and on disk state.
|
||||||
|
rpc Delete(DeleteTaskRequest) returns (DeleteResponse);
|
||||||
|
|
||||||
|
rpc DeleteProcess(DeleteProcessRequest) returns (DeleteResponse);
|
||||||
|
|
||||||
|
rpc Get(GetRequest) returns (GetResponse);
|
||||||
|
|
||||||
|
rpc List(ListTasksRequest) returns (ListTasksResponse);
|
||||||
|
|
||||||
|
// Kill a task or process.
|
||||||
|
rpc Kill(KillRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc Exec(ExecProcessRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc ResizePty(ResizePtyRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc CloseIO(CloseIORequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc Pause(PauseTaskRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc Resume(ResumeTaskRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc ListPids(ListPidsRequest) returns (ListPidsResponse);
|
||||||
|
|
||||||
|
rpc Checkpoint(CheckpointTaskRequest) returns (CheckpointTaskResponse);
|
||||||
|
|
||||||
|
rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc Metrics(MetricsRequest) returns (MetricsResponse);
|
||||||
|
|
||||||
|
rpc Wait(WaitRequest) returns (WaitResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateTaskRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
|
||||||
|
// RootFS provides the pre-chroot mounts to perform in the shim before
|
||||||
|
// executing the container task.
|
||||||
|
//
|
||||||
|
// These are for mounts that cannot be performed in the user namespace.
|
||||||
|
// Typically, these mounts should be resolved from snapshots specified on
|
||||||
|
// the container object.
|
||||||
|
repeated containerd.types.Mount rootfs = 3;
|
||||||
|
|
||||||
|
string stdin = 4;
|
||||||
|
string stdout = 5;
|
||||||
|
string stderr = 6;
|
||||||
|
bool terminal = 7;
|
||||||
|
|
||||||
|
containerd.types.Descriptor checkpoint = 8;
|
||||||
|
|
||||||
|
google.protobuf.Any options = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateTaskResponse {
|
||||||
|
string container_id = 1;
|
||||||
|
uint32 pid = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StartRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string exec_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StartResponse {
|
||||||
|
uint32 pid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteTaskRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteResponse {
|
||||||
|
string id = 1;
|
||||||
|
uint32 pid = 2;
|
||||||
|
uint32 exit_status = 3;
|
||||||
|
google.protobuf.Timestamp exited_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteProcessRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string exec_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string exec_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetResponse {
|
||||||
|
containerd.v1.types.Process process = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListTasksRequest {
|
||||||
|
string filter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListTasksResponse {
|
||||||
|
repeated containerd.v1.types.Process tasks = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message KillRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string exec_id = 2;
|
||||||
|
uint32 signal = 3;
|
||||||
|
bool all = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecProcessRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string stdin = 2;
|
||||||
|
string stdout = 3;
|
||||||
|
string stderr = 4;
|
||||||
|
bool terminal = 5;
|
||||||
|
// Spec for starting a process in the target container.
|
||||||
|
//
|
||||||
|
// For runc, this is a process spec, for example.
|
||||||
|
google.protobuf.Any spec = 6;
|
||||||
|
// id of the exec process
|
||||||
|
string exec_id = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecProcessResponse {
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResizePtyRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string exec_id = 2;
|
||||||
|
uint32 width = 3;
|
||||||
|
uint32 height = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CloseIORequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string exec_id = 2;
|
||||||
|
bool stdin = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PauseTaskRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResumeTaskRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListPidsRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListPidsResponse {
|
||||||
|
// Processes includes the process ID and additional process information
|
||||||
|
repeated containerd.v1.types.ProcessInfo processes = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CheckpointTaskRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string parent_checkpoint = 2 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
google.protobuf.Any options = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CheckpointTaskResponse {
|
||||||
|
repeated containerd.types.Descriptor descriptors = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateTaskRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
google.protobuf.Any resources = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MetricsRequest {
|
||||||
|
repeated string filters = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MetricsResponse {
|
||||||
|
repeated types.Metric metrics = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WaitRequest {
|
||||||
|
string container_id = 1;
|
||||||
|
string exec_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WaitResponse {
|
||||||
|
uint32 exit_status = 1;
|
||||||
|
google.protobuf.Timestamp exited_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
}
|
446
vendor/github.com/containerd/containerd/api/services/version/v1/version.pb.go
generated
vendored
Normal file
446
vendor/github.com/containerd/containerd/api/services/version/v1/version.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: github.com/containerd/containerd/api/services/version/v1/version.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package version is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
github.com/containerd/containerd/api/services/version/v1/version.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
VersionResponse
|
||||||
|
*/
|
||||||
|
package version
|
||||||
|
|
||||||
|
import proto "github.com/gogo/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
import google_protobuf "github.com/gogo/protobuf/types"
|
||||||
|
|
||||||
|
// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto"
|
||||||
|
|
||||||
|
import context "golang.org/x/net/context"
|
||||||
|
import grpc "google.golang.org/grpc"
|
||||||
|
|
||||||
|
import strings "strings"
|
||||||
|
import reflect "reflect"
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type VersionResponse struct {
|
||||||
|
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||||
|
Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *VersionResponse) Reset() { *m = VersionResponse{} }
|
||||||
|
func (*VersionResponse) ProtoMessage() {}
|
||||||
|
func (*VersionResponse) Descriptor() ([]byte, []int) { return fileDescriptorVersion, []int{0} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*VersionResponse)(nil), "containerd.services.version.v1.VersionResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// Client API for Version service
|
||||||
|
|
||||||
|
type VersionClient interface {
|
||||||
|
Version(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*VersionResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type versionClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVersionClient(cc *grpc.ClientConn) VersionClient {
|
||||||
|
return &versionClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *versionClient) Version(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*VersionResponse, error) {
|
||||||
|
out := new(VersionResponse)
|
||||||
|
err := grpc.Invoke(ctx, "/containerd.services.version.v1.Version/Version", in, out, c.cc, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server API for Version service
|
||||||
|
|
||||||
|
type VersionServer interface {
|
||||||
|
Version(context.Context, *google_protobuf.Empty) (*VersionResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterVersionServer(s *grpc.Server, srv VersionServer) {
|
||||||
|
s.RegisterService(&_Version_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Version_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(google_protobuf.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(VersionServer).Version(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/containerd.services.version.v1.Version/Version",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(VersionServer).Version(ctx, req.(*google_protobuf.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _Version_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "containerd.services.version.v1.Version",
|
||||||
|
HandlerType: (*VersionServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "Version",
|
||||||
|
Handler: _Version_Version_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "github.com/containerd/containerd/api/services/version/v1/version.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *VersionResponse) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *VersionResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Version) > 0 {
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintVersion(dAtA, i, uint64(len(m.Version)))
|
||||||
|
i += copy(dAtA[i:], m.Version)
|
||||||
|
}
|
||||||
|
if len(m.Revision) > 0 {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintVersion(dAtA, i, uint64(len(m.Revision)))
|
||||||
|
i += copy(dAtA[i:], m.Revision)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintVersion(dAtA []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *VersionResponse) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Version)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovVersion(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Revision)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovVersion(uint64(l))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovVersion(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozVersion(x uint64) (n int) {
|
||||||
|
return sovVersion(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (this *VersionResponse) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&VersionResponse{`,
|
||||||
|
`Version:` + fmt.Sprintf("%v", this.Version) + `,`,
|
||||||
|
`Revision:` + fmt.Sprintf("%v", this.Revision) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func valueToStringVersion(v interface{}) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("*%v", pv)
|
||||||
|
}
|
||||||
|
func (m *VersionResponse) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowVersion
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: VersionResponse: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: VersionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowVersion
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthVersion
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Version = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowVersion
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthVersion
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Revision = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipVersion(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthVersion
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipVersion(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowVersion
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowVersion
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowVersion
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthVersion
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowVersion
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipVersion(dAtA[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthVersion = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowVersion = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/containerd/containerd/api/services/version/v1/version.proto", fileDescriptorVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptorVersion = []byte{
|
||||||
|
// 243 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x4b, 0xcf, 0x2c, 0xc9,
|
||||||
|
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
|
||||||
|
0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x17, 0xa7, 0x16, 0x95, 0x65, 0x26, 0xa7, 0x16, 0xeb,
|
||||||
|
0x97, 0xa5, 0x16, 0x15, 0x67, 0xe6, 0xe7, 0xe9, 0x97, 0x19, 0xc2, 0x98, 0x7a, 0x05, 0x45, 0xf9,
|
||||||
|
0x25, 0xf9, 0x42, 0x72, 0x08, 0x1d, 0x7a, 0x30, 0xd5, 0x7a, 0x30, 0x25, 0x65, 0x86, 0x52, 0xd2,
|
||||||
|
0xe9, 0xf9, 0xf9, 0xe9, 0x39, 0xa9, 0xfa, 0x60, 0xd5, 0x49, 0xa5, 0x69, 0xfa, 0xa9, 0xb9, 0x05,
|
||||||
|
0x25, 0x95, 0x10, 0xcd, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11,
|
||||||
|
0x55, 0x72, 0xe7, 0xe2, 0x0f, 0x83, 0x18, 0x10, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x2a,
|
||||||
|
0x24, 0xc1, 0xc5, 0x0e, 0x35, 0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x15, 0x92,
|
||||||
|
0xe2, 0xe2, 0x28, 0x4a, 0x2d, 0xcb, 0x04, 0x4b, 0x31, 0x81, 0xa5, 0xe0, 0x7c, 0xa3, 0x58, 0x2e,
|
||||||
|
0x76, 0xa8, 0x41, 0x42, 0x41, 0x08, 0xa6, 0x98, 0x1e, 0xc4, 0x49, 0x7a, 0x30, 0x27, 0xe9, 0xb9,
|
||||||
|
0x82, 0x9c, 0x24, 0xa5, 0xaf, 0x87, 0xdf, 0x2b, 0x7a, 0x68, 0x8e, 0x72, 0x8a, 0x3a, 0xf1, 0x50,
|
||||||
|
0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78,
|
||||||
|
0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x94, 0x03, 0xb9, 0x81, 0x6b, 0x0d, 0x65, 0x46, 0x30,
|
||||||
|
0x26, 0xb1, 0x81, 0x9d, 0x67, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x95, 0x0d, 0x52, 0x23, 0xa9,
|
||||||
|
0x01, 0x00, 0x00,
|
||||||
|
}
|
18
vendor/github.com/containerd/containerd/api/services/version/v1/version.proto
generated
vendored
Normal file
18
vendor/github.com/containerd/containerd/api/services/version/v1/version.proto
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.services.version.v1;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
// TODO(stevvooe): Should version service actually be versioned?
|
||||||
|
option go_package = "github.com/containerd/containerd/api/services/version/v1;version";
|
||||||
|
|
||||||
|
service Version {
|
||||||
|
rpc Version(google.protobuf.Empty) returns (VersionResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message VersionResponse {
|
||||||
|
string version = 1;
|
||||||
|
string revision = 2;
|
||||||
|
}
|
410
vendor/github.com/containerd/containerd/api/types/descriptor.pb.go
generated
vendored
Normal file
410
vendor/github.com/containerd/containerd/api/types/descriptor.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: github.com/containerd/containerd/api/types/descriptor.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package types is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
github.com/containerd/containerd/api/types/descriptor.proto
|
||||||
|
github.com/containerd/containerd/api/types/metrics.proto
|
||||||
|
github.com/containerd/containerd/api/types/mount.proto
|
||||||
|
github.com/containerd/containerd/api/types/platform.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
Descriptor
|
||||||
|
Metric
|
||||||
|
Mount
|
||||||
|
Platform
|
||||||
|
*/
|
||||||
|
package types
|
||||||
|
|
||||||
|
import proto "github.com/gogo/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto"
|
||||||
|
|
||||||
|
import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest"
|
||||||
|
|
||||||
|
import strings "strings"
|
||||||
|
import reflect "reflect"
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
// Descriptor describes a blob in a content store.
|
||||||
|
//
|
||||||
|
// This descriptor can be used to reference content from an
|
||||||
|
// oci descriptor found in a manifest.
|
||||||
|
// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor
|
||||||
|
type Descriptor struct {
|
||||||
|
MediaType string `protobuf:"bytes,1,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"`
|
||||||
|
Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"`
|
||||||
|
Size_ int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Descriptor) Reset() { *m = Descriptor{} }
|
||||||
|
func (*Descriptor) ProtoMessage() {}
|
||||||
|
func (*Descriptor) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{0} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Descriptor)(nil), "containerd.types.Descriptor")
|
||||||
|
}
|
||||||
|
func (m *Descriptor) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Descriptor) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.MediaType) > 0 {
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintDescriptor(dAtA, i, uint64(len(m.MediaType)))
|
||||||
|
i += copy(dAtA[i:], m.MediaType)
|
||||||
|
}
|
||||||
|
if len(m.Digest) > 0 {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintDescriptor(dAtA, i, uint64(len(m.Digest)))
|
||||||
|
i += copy(dAtA[i:], m.Digest)
|
||||||
|
}
|
||||||
|
if m.Size_ != 0 {
|
||||||
|
dAtA[i] = 0x18
|
||||||
|
i++
|
||||||
|
i = encodeVarintDescriptor(dAtA, i, uint64(m.Size_))
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintDescriptor(dAtA []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *Descriptor) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.MediaType)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovDescriptor(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Digest)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovDescriptor(uint64(l))
|
||||||
|
}
|
||||||
|
if m.Size_ != 0 {
|
||||||
|
n += 1 + sovDescriptor(uint64(m.Size_))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovDescriptor(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozDescriptor(x uint64) (n int) {
|
||||||
|
return sovDescriptor(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (this *Descriptor) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&Descriptor{`,
|
||||||
|
`MediaType:` + fmt.Sprintf("%v", this.MediaType) + `,`,
|
||||||
|
`Digest:` + fmt.Sprintf("%v", this.Digest) + `,`,
|
||||||
|
`Size_:` + fmt.Sprintf("%v", this.Size_) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func valueToStringDescriptor(v interface{}) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("*%v", pv)
|
||||||
|
}
|
||||||
|
func (m *Descriptor) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Descriptor: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Descriptor: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field MediaType", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthDescriptor
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.MediaType = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthDescriptor
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Digest = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType)
|
||||||
|
}
|
||||||
|
m.Size_ = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Size_ |= (int64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipDescriptor(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthDescriptor
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipDescriptor(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthDescriptor
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowDescriptor
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipDescriptor(dAtA[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthDescriptor = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowDescriptor = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/containerd/containerd/api/types/descriptor.proto", fileDescriptorDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptorDescriptor = []byte{
|
||||||
|
// 234 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9,
|
||||||
|
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
|
||||||
|
0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xa7, 0xa4, 0x16,
|
||||||
|
0x27, 0x17, 0x65, 0x16, 0x94, 0xe4, 0x17, 0xe9, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20,
|
||||||
|
0x94, 0xe9, 0x81, 0x95, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88,
|
||||||
|
0x3a, 0xa5, 0x6e, 0x46, 0x2e, 0x2e, 0x17, 0xb8, 0x66, 0x21, 0x59, 0x2e, 0xae, 0xdc, 0xd4, 0x94,
|
||||||
|
0xcc, 0xc4, 0x78, 0x90, 0x1e, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x4e, 0xb0, 0x48, 0x48,
|
||||||
|
0x65, 0x41, 0xaa, 0x90, 0x17, 0x17, 0x5b, 0x4a, 0x66, 0x7a, 0x6a, 0x71, 0x89, 0x04, 0x13, 0x48,
|
||||||
|
0xca, 0xc9, 0xe8, 0xc4, 0x3d, 0x79, 0x86, 0x5b, 0xf7, 0xe4, 0xb5, 0x90, 0x9c, 0x9a, 0x5f, 0x90,
|
||||||
|
0x9a, 0x07, 0xb7, 0xbc, 0x58, 0x3f, 0x3d, 0x5f, 0x17, 0xa2, 0x45, 0xcf, 0x05, 0x4c, 0x05, 0x41,
|
||||||
|
0x4d, 0x10, 0x12, 0xe2, 0x62, 0x29, 0xce, 0xac, 0x4a, 0x95, 0x60, 0x56, 0x60, 0xd4, 0x60, 0x0e,
|
||||||
|
0x02, 0xb3, 0x9d, 0xbc, 0x4e, 0x3c, 0x94, 0x63, 0xb8, 0xf1, 0x50, 0x8e, 0xa1, 0xe1, 0x91, 0x1c,
|
||||||
|
0xe3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x18, 0x65, 0x40,
|
||||||
|
0x7c, 0x60, 0x58, 0x83, 0xc9, 0x08, 0x86, 0x24, 0x36, 0xb0, 0x17, 0x8d, 0x01, 0x01, 0x00, 0x00,
|
||||||
|
0xff, 0xff, 0xea, 0xac, 0x78, 0x9a, 0x49, 0x01, 0x00, 0x00,
|
||||||
|
}
|
18
vendor/github.com/containerd/containerd/api/types/descriptor.proto
generated
vendored
Normal file
18
vendor/github.com/containerd/containerd/api/types/descriptor.proto
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.types;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||||
|
|
||||||
|
// Descriptor describes a blob in a content store.
|
||||||
|
//
|
||||||
|
// This descriptor can be used to reference content from an
|
||||||
|
// oci descriptor found in a manifest.
|
||||||
|
// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor
|
||||||
|
message Descriptor {
|
||||||
|
string media_type = 1;
|
||||||
|
string digest = 2 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
|
||||||
|
int64 size = 3;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package types
|
|
@ -0,0 +1,412 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: github.com/containerd/containerd/api/types/metrics.proto
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import proto "github.com/gogo/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto"
|
||||||
|
import google_protobuf1 "github.com/gogo/protobuf/types"
|
||||||
|
import _ "github.com/gogo/protobuf/types"
|
||||||
|
|
||||||
|
import time "time"
|
||||||
|
|
||||||
|
import types1 "github.com/gogo/protobuf/types"
|
||||||
|
|
||||||
|
import strings "strings"
|
||||||
|
import reflect "reflect"
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
var _ = time.Kitchen
|
||||||
|
|
||||||
|
type Metric struct {
|
||||||
|
Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,stdtime" json:"timestamp"`
|
||||||
|
ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
Data *google_protobuf1.Any `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) Reset() { *m = Metric{} }
|
||||||
|
func (*Metric) ProtoMessage() {}
|
||||||
|
func (*Metric) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{0} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Metric)(nil), "containerd.types.Metric")
|
||||||
|
}
|
||||||
|
func (m *Metric) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metric) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(types1.SizeOfStdTime(m.Timestamp)))
|
||||||
|
n1, err := types1.StdTimeMarshalTo(m.Timestamp, dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n1
|
||||||
|
if len(m.ID) > 0 {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(len(m.ID)))
|
||||||
|
i += copy(dAtA[i:], m.ID)
|
||||||
|
}
|
||||||
|
if m.Data != nil {
|
||||||
|
dAtA[i] = 0x1a
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(m.Data.Size()))
|
||||||
|
n2, err := m.Data.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n2
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *Metric) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = types1.SizeOfStdTime(m.Timestamp)
|
||||||
|
n += 1 + l + sovMetrics(uint64(l))
|
||||||
|
l = len(m.ID)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovMetrics(uint64(l))
|
||||||
|
}
|
||||||
|
if m.Data != nil {
|
||||||
|
l = m.Data.Size()
|
||||||
|
n += 1 + l + sovMetrics(uint64(l))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovMetrics(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozMetrics(x uint64) (n int) {
|
||||||
|
return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (this *Metric) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&Metric{`,
|
||||||
|
`Timestamp:` + strings.Replace(strings.Replace(this.Timestamp.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`,
|
||||||
|
`ID:` + fmt.Sprintf("%v", this.ID) + `,`,
|
||||||
|
`Data:` + strings.Replace(fmt.Sprintf("%v", this.Data), "Any", "google_protobuf1.Any", 1) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func valueToStringMetrics(v interface{}) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("*%v", pv)
|
||||||
|
}
|
||||||
|
func (m *Metric) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Metric: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Metric: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if err := types1.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.ID = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Data == nil {
|
||||||
|
m.Data = &google_protobuf1.Any{}
|
||||||
|
}
|
||||||
|
if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipMetrics(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipMetrics(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipMetrics(dAtA[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/containerd/containerd/api/types/metrics.proto", fileDescriptorMetrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptorMetrics = []byte{
|
||||||
|
// 258 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x48, 0xcf, 0x2c, 0xc9,
|
||||||
|
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
|
||||||
|
0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xa6, 0x96,
|
||||||
|
0x14, 0x65, 0x26, 0x17, 0xeb, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0xd4, 0xe8, 0x81,
|
||||||
|
0xe5, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x94, 0x64,
|
||||||
|
0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x3e, 0x98, 0x97, 0x54, 0x9a, 0xa6, 0x9f, 0x98, 0x57, 0x09,
|
||||||
|
0x95, 0x92, 0x47, 0x97, 0x2a, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x80, 0x28, 0x50,
|
||||||
|
0xea, 0x63, 0xe4, 0x62, 0xf3, 0x05, 0xdb, 0x2a, 0xe4, 0xc4, 0xc5, 0x09, 0x97, 0x95, 0x60, 0x54,
|
||||||
|
0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd2, 0x83, 0xe8, 0xd7, 0x83, 0xe9, 0xd7, 0x0b, 0x81, 0xa9, 0x70,
|
||||||
|
0xe2, 0x38, 0x71, 0x4f, 0x9e, 0x61, 0xc2, 0x7d, 0x79, 0xc6, 0x20, 0x84, 0x36, 0x21, 0x31, 0x2e,
|
||||||
|
0xa6, 0xcc, 0x14, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0x3c,
|
||||||
|
0x5d, 0x82, 0x98, 0x32, 0x53, 0x84, 0x34, 0xb8, 0x58, 0x52, 0x12, 0x4b, 0x12, 0x25, 0x98, 0xc1,
|
||||||
|
0xc6, 0x8a, 0x60, 0x18, 0xeb, 0x98, 0x57, 0x19, 0x04, 0x56, 0xe1, 0xe4, 0x75, 0xe2, 0xa1, 0x1c,
|
||||||
|
0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48,
|
||||||
|
0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x28, 0x03, 0xe2, 0x03, 0xd2, 0x1a, 0x4c, 0x46, 0x30, 0x24,
|
||||||
|
0xb1, 0x81, 0x6d, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xde, 0x0d, 0x02, 0xfe, 0x85, 0x01,
|
||||||
|
0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.types;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/any.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||||
|
|
||||||
|
message Metric {
|
||||||
|
google.protobuf.Timestamp timestamp = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
string id = 2;
|
||||||
|
google.protobuf.Any data = 3;
|
||||||
|
}
|
|
@ -0,0 +1,456 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: github.com/containerd/containerd/api/types/mount.proto
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import proto "github.com/gogo/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto"
|
||||||
|
|
||||||
|
import strings "strings"
|
||||||
|
import reflect "reflect"
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// Mount describes mounts for a container.
|
||||||
|
//
|
||||||
|
// This type is the lingua franca of ContainerD. All services provide mounts
|
||||||
|
// to be used with the container at creation time.
|
||||||
|
//
|
||||||
|
// The Mount type follows the structure of the mount syscall, including a type,
|
||||||
|
// source, target and options.
|
||||||
|
type Mount struct {
|
||||||
|
// Type defines the nature of the mount.
|
||||||
|
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||||
|
// Source specifies the name of the mount. Depending on mount type, this
|
||||||
|
// may be a volume name or a host path, or even ignored.
|
||||||
|
Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"`
|
||||||
|
// Target path in container
|
||||||
|
Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"`
|
||||||
|
// Options specifies zero or more fstab style mount options.
|
||||||
|
Options []string `protobuf:"bytes,4,rep,name=options" json:"options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mount) Reset() { *m = Mount{} }
|
||||||
|
func (*Mount) ProtoMessage() {}
|
||||||
|
func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorMount, []int{0} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Mount)(nil), "containerd.types.Mount")
|
||||||
|
}
|
||||||
|
func (m *Mount) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mount) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Type) > 0 {
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintMount(dAtA, i, uint64(len(m.Type)))
|
||||||
|
i += copy(dAtA[i:], m.Type)
|
||||||
|
}
|
||||||
|
if len(m.Source) > 0 {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintMount(dAtA, i, uint64(len(m.Source)))
|
||||||
|
i += copy(dAtA[i:], m.Source)
|
||||||
|
}
|
||||||
|
if len(m.Target) > 0 {
|
||||||
|
dAtA[i] = 0x1a
|
||||||
|
i++
|
||||||
|
i = encodeVarintMount(dAtA, i, uint64(len(m.Target)))
|
||||||
|
i += copy(dAtA[i:], m.Target)
|
||||||
|
}
|
||||||
|
if len(m.Options) > 0 {
|
||||||
|
for _, s := range m.Options {
|
||||||
|
dAtA[i] = 0x22
|
||||||
|
i++
|
||||||
|
l = len(s)
|
||||||
|
for l >= 1<<7 {
|
||||||
|
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
|
||||||
|
l >>= 7
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
dAtA[i] = uint8(l)
|
||||||
|
i++
|
||||||
|
i += copy(dAtA[i:], s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintMount(dAtA []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *Mount) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Type)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovMount(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Source)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovMount(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Target)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovMount(uint64(l))
|
||||||
|
}
|
||||||
|
if len(m.Options) > 0 {
|
||||||
|
for _, s := range m.Options {
|
||||||
|
l = len(s)
|
||||||
|
n += 1 + l + sovMount(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovMount(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozMount(x uint64) (n int) {
|
||||||
|
return sovMount(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (this *Mount) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&Mount{`,
|
||||||
|
`Type:` + fmt.Sprintf("%v", this.Type) + `,`,
|
||||||
|
`Source:` + fmt.Sprintf("%v", this.Source) + `,`,
|
||||||
|
`Target:` + fmt.Sprintf("%v", this.Target) + `,`,
|
||||||
|
`Options:` + fmt.Sprintf("%v", this.Options) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func valueToStringMount(v interface{}) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("*%v", pv)
|
||||||
|
}
|
||||||
|
func (m *Mount) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Mount: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Mount: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthMount
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Type = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthMount
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Source = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthMount
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Target = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 4:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthMount
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Options = append(m.Options, string(dAtA[iNdEx:postIndex]))
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipMount(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthMount
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipMount(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthMount
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowMount
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipMount(dAtA[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthMount = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowMount = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/containerd/containerd/api/types/mount.proto", fileDescriptorMount)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptorMount = []byte{
|
||||||
|
// 202 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4b, 0xcf, 0x2c, 0xc9,
|
||||||
|
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
|
||||||
|
0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xe6, 0x97,
|
||||||
|
0xe6, 0x95, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x54, 0xe8, 0x81, 0x65, 0xa5,
|
||||||
|
0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x52, 0x2a, 0x17, 0xab,
|
||||||
|
0x2f, 0x48, 0x9b, 0x90, 0x10, 0x17, 0x0b, 0x48, 0x9d, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10,
|
||||||
|
0x98, 0x2d, 0x24, 0xc6, 0xc5, 0x56, 0x9c, 0x5f, 0x5a, 0x94, 0x9c, 0x2a, 0xc1, 0x04, 0x16, 0x85,
|
||||||
|
0xf2, 0x40, 0xe2, 0x25, 0x89, 0x45, 0xe9, 0xa9, 0x25, 0x12, 0xcc, 0x10, 0x71, 0x08, 0x4f, 0x48,
|
||||||
|
0x82, 0x8b, 0x3d, 0xbf, 0xa0, 0x24, 0x33, 0x3f, 0xaf, 0x58, 0x82, 0x45, 0x81, 0x59, 0x83, 0x33,
|
||||||
|
0x08, 0xc6, 0x75, 0xf2, 0x3a, 0xf1, 0x50, 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72,
|
||||||
|
0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x94, 0x01,
|
||||||
|
0xf1, 0x1e, 0xb4, 0x06, 0x93, 0x11, 0x0c, 0x49, 0x6c, 0x60, 0xb7, 0x1b, 0x03, 0x02, 0x00, 0x00,
|
||||||
|
0xff, 0xff, 0x82, 0x1c, 0x02, 0x18, 0x1d, 0x01, 0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.types;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||||
|
|
||||||
|
// Mount describes mounts for a container.
|
||||||
|
//
|
||||||
|
// This type is the lingua franca of ContainerD. All services provide mounts
|
||||||
|
// to be used with the container at creation time.
|
||||||
|
//
|
||||||
|
// The Mount type follows the structure of the mount syscall, including a type,
|
||||||
|
// source, target and options.
|
||||||
|
message Mount {
|
||||||
|
// Type defines the nature of the mount.
|
||||||
|
string type = 1;
|
||||||
|
|
||||||
|
// Source specifies the name of the mount. Depending on mount type, this
|
||||||
|
// may be a volume name or a host path, or even ignored.
|
||||||
|
string source = 2;
|
||||||
|
|
||||||
|
// Target path in container
|
||||||
|
string target = 3;
|
||||||
|
|
||||||
|
// Options specifies zero or more fstab style mount options.
|
||||||
|
repeated string options = 4;
|
||||||
|
}
|
|
@ -0,0 +1,394 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: github.com/containerd/containerd/api/types/platform.proto
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import proto "github.com/gogo/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto"
|
||||||
|
|
||||||
|
import strings "strings"
|
||||||
|
import reflect "reflect"
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// Platform follows the structure of the OCI platform specification, from
|
||||||
|
// descriptors.
|
||||||
|
type Platform struct {
|
||||||
|
OS string `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"`
|
||||||
|
Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"`
|
||||||
|
Variant string `protobuf:"bytes,3,opt,name=variant,proto3" json:"variant,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Platform) Reset() { *m = Platform{} }
|
||||||
|
func (*Platform) ProtoMessage() {}
|
||||||
|
func (*Platform) Descriptor() ([]byte, []int) { return fileDescriptorPlatform, []int{0} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Platform)(nil), "containerd.types.Platform")
|
||||||
|
}
|
||||||
|
func (m *Platform) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Platform) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.OS) > 0 {
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintPlatform(dAtA, i, uint64(len(m.OS)))
|
||||||
|
i += copy(dAtA[i:], m.OS)
|
||||||
|
}
|
||||||
|
if len(m.Architecture) > 0 {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintPlatform(dAtA, i, uint64(len(m.Architecture)))
|
||||||
|
i += copy(dAtA[i:], m.Architecture)
|
||||||
|
}
|
||||||
|
if len(m.Variant) > 0 {
|
||||||
|
dAtA[i] = 0x1a
|
||||||
|
i++
|
||||||
|
i = encodeVarintPlatform(dAtA, i, uint64(len(m.Variant)))
|
||||||
|
i += copy(dAtA[i:], m.Variant)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintPlatform(dAtA []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *Platform) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.OS)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovPlatform(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Architecture)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovPlatform(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Variant)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovPlatform(uint64(l))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovPlatform(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozPlatform(x uint64) (n int) {
|
||||||
|
return sovPlatform(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (this *Platform) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&Platform{`,
|
||||||
|
`OS:` + fmt.Sprintf("%v", this.OS) + `,`,
|
||||||
|
`Architecture:` + fmt.Sprintf("%v", this.Architecture) + `,`,
|
||||||
|
`Variant:` + fmt.Sprintf("%v", this.Variant) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func valueToStringPlatform(v interface{}) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("*%v", pv)
|
||||||
|
}
|
||||||
|
func (m *Platform) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Platform: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Platform: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field OS", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthPlatform
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.OS = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Architecture", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthPlatform
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Architecture = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Variant", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthPlatform
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Variant = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipPlatform(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthPlatform
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipPlatform(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthPlatform
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowPlatform
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipPlatform(dAtA[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthPlatform = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowPlatform = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/containerd/containerd/api/types/platform.proto", fileDescriptorPlatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptorPlatform = []byte{
|
||||||
|
// 205 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9,
|
||||||
|
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
|
||||||
|
0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0x17, 0xe4, 0x24,
|
||||||
|
0x96, 0xa4, 0xe5, 0x17, 0xe5, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x14, 0xe9,
|
||||||
|
0x81, 0x15, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88, 0x3a, 0xa5,
|
||||||
|
0x04, 0x2e, 0x8e, 0x00, 0xa8, 0x4e, 0x21, 0x31, 0x2e, 0xa6, 0xfc, 0x62, 0x09, 0x46, 0x05, 0x46,
|
||||||
|
0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0xfc, 0x83, 0x83, 0x98, 0xf2, 0x8b, 0x85, 0x94,
|
||||||
|
0xb8, 0x78, 0x12, 0x8b, 0x92, 0x33, 0x32, 0x4b, 0x52, 0x93, 0x4b, 0x4a, 0x8b, 0x52, 0x25, 0x98,
|
||||||
|
0x40, 0x2a, 0x82, 0x50, 0xc4, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x12, 0x8b, 0x32, 0x13, 0xf3, 0x4a,
|
||||||
|
0x24, 0x98, 0xc1, 0xd2, 0x30, 0xae, 0x93, 0xd7, 0x89, 0x87, 0x72, 0x0c, 0x37, 0x1e, 0xca, 0x31,
|
||||||
|
0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4,
|
||||||
|
0x18, 0xa3, 0x0c, 0x88, 0xf7, 0x9e, 0x35, 0x98, 0x8c, 0x60, 0x48, 0x62, 0x03, 0x3b, 0xdb, 0x18,
|
||||||
|
0x10, 0x00, 0x00, 0xff, 0xff, 0x05, 0xaa, 0xda, 0xa1, 0x1b, 0x01, 0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.types;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||||
|
|
||||||
|
// Platform follows the structure of the OCI platform specification, from
|
||||||
|
// descriptors.
|
||||||
|
message Platform {
|
||||||
|
string os = 1 [(gogoproto.customname) = "OS"];
|
||||||
|
string architecture = 2;
|
||||||
|
string variant = 3;
|
||||||
|
}
|
890
vendor/github.com/containerd/containerd/api/types/task/task.pb.go
generated
vendored
Normal file
890
vendor/github.com/containerd/containerd/api/types/task/task.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,890 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: github.com/containerd/containerd/api/types/task/task.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package task is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
github.com/containerd/containerd/api/types/task/task.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
Process
|
||||||
|
ProcessInfo
|
||||||
|
*/
|
||||||
|
package task
|
||||||
|
|
||||||
|
import proto "github.com/gogo/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto"
|
||||||
|
import _ "github.com/gogo/protobuf/types"
|
||||||
|
import google_protobuf2 "github.com/gogo/protobuf/types"
|
||||||
|
|
||||||
|
import time "time"
|
||||||
|
|
||||||
|
import types "github.com/gogo/protobuf/types"
|
||||||
|
|
||||||
|
import strings "strings"
|
||||||
|
import reflect "reflect"
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
var _ = time.Kitchen
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Status int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusUnknown Status = 0
|
||||||
|
StatusCreated Status = 1
|
||||||
|
StatusRunning Status = 2
|
||||||
|
StatusStopped Status = 3
|
||||||
|
StatusPaused Status = 4
|
||||||
|
StatusPausing Status = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
var Status_name = map[int32]string{
|
||||||
|
0: "UNKNOWN",
|
||||||
|
1: "CREATED",
|
||||||
|
2: "RUNNING",
|
||||||
|
3: "STOPPED",
|
||||||
|
4: "PAUSED",
|
||||||
|
5: "PAUSING",
|
||||||
|
}
|
||||||
|
var Status_value = map[string]int32{
|
||||||
|
"UNKNOWN": 0,
|
||||||
|
"CREATED": 1,
|
||||||
|
"RUNNING": 2,
|
||||||
|
"STOPPED": 3,
|
||||||
|
"PAUSED": 4,
|
||||||
|
"PAUSING": 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Status) String() string {
|
||||||
|
return proto.EnumName(Status_name, int32(x))
|
||||||
|
}
|
||||||
|
func (Status) EnumDescriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} }
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"`
|
||||||
|
ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"`
|
||||||
|
Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"`
|
||||||
|
Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"`
|
||||||
|
Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"`
|
||||||
|
Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"`
|
||||||
|
Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"`
|
||||||
|
ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"`
|
||||||
|
ExitedAt time.Time `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Process) Reset() { *m = Process{} }
|
||||||
|
func (*Process) ProtoMessage() {}
|
||||||
|
func (*Process) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} }
|
||||||
|
|
||||||
|
type ProcessInfo struct {
|
||||||
|
// PID is the process ID.
|
||||||
|
Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"`
|
||||||
|
// Info contains additional process information.
|
||||||
|
//
|
||||||
|
// Info varies by platform.
|
||||||
|
Info *google_protobuf2.Any `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ProcessInfo) Reset() { *m = ProcessInfo{} }
|
||||||
|
func (*ProcessInfo) ProtoMessage() {}
|
||||||
|
func (*ProcessInfo) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{1} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Process)(nil), "containerd.v1.types.Process")
|
||||||
|
proto.RegisterType((*ProcessInfo)(nil), "containerd.v1.types.ProcessInfo")
|
||||||
|
proto.RegisterEnum("containerd.v1.types.Status", Status_name, Status_value)
|
||||||
|
}
|
||||||
|
func (m *Process) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Process) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.ContainerID) > 0 {
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID)))
|
||||||
|
i += copy(dAtA[i:], m.ContainerID)
|
||||||
|
}
|
||||||
|
if len(m.ID) > 0 {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(len(m.ID)))
|
||||||
|
i += copy(dAtA[i:], m.ID)
|
||||||
|
}
|
||||||
|
if m.Pid != 0 {
|
||||||
|
dAtA[i] = 0x18
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(m.Pid))
|
||||||
|
}
|
||||||
|
if m.Status != 0 {
|
||||||
|
dAtA[i] = 0x20
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(m.Status))
|
||||||
|
}
|
||||||
|
if len(m.Stdin) > 0 {
|
||||||
|
dAtA[i] = 0x2a
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(len(m.Stdin)))
|
||||||
|
i += copy(dAtA[i:], m.Stdin)
|
||||||
|
}
|
||||||
|
if len(m.Stdout) > 0 {
|
||||||
|
dAtA[i] = 0x32
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(len(m.Stdout)))
|
||||||
|
i += copy(dAtA[i:], m.Stdout)
|
||||||
|
}
|
||||||
|
if len(m.Stderr) > 0 {
|
||||||
|
dAtA[i] = 0x3a
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(len(m.Stderr)))
|
||||||
|
i += copy(dAtA[i:], m.Stderr)
|
||||||
|
}
|
||||||
|
if m.Terminal {
|
||||||
|
dAtA[i] = 0x40
|
||||||
|
i++
|
||||||
|
if m.Terminal {
|
||||||
|
dAtA[i] = 1
|
||||||
|
} else {
|
||||||
|
dAtA[i] = 0
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if m.ExitStatus != 0 {
|
||||||
|
dAtA[i] = 0x48
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus))
|
||||||
|
}
|
||||||
|
dAtA[i] = 0x52
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt)))
|
||||||
|
n1, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n1
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ProcessInfo) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ProcessInfo) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.Pid != 0 {
|
||||||
|
dAtA[i] = 0x8
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(m.Pid))
|
||||||
|
}
|
||||||
|
if m.Info != nil {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintTask(dAtA, i, uint64(m.Info.Size()))
|
||||||
|
n2, err := m.Info.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n2
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintTask(dAtA []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *Process) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.ContainerID)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovTask(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.ID)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovTask(uint64(l))
|
||||||
|
}
|
||||||
|
if m.Pid != 0 {
|
||||||
|
n += 1 + sovTask(uint64(m.Pid))
|
||||||
|
}
|
||||||
|
if m.Status != 0 {
|
||||||
|
n += 1 + sovTask(uint64(m.Status))
|
||||||
|
}
|
||||||
|
l = len(m.Stdin)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovTask(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Stdout)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovTask(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Stderr)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovTask(uint64(l))
|
||||||
|
}
|
||||||
|
if m.Terminal {
|
||||||
|
n += 2
|
||||||
|
}
|
||||||
|
if m.ExitStatus != 0 {
|
||||||
|
n += 1 + sovTask(uint64(m.ExitStatus))
|
||||||
|
}
|
||||||
|
l = types.SizeOfStdTime(m.ExitedAt)
|
||||||
|
n += 1 + l + sovTask(uint64(l))
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ProcessInfo) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.Pid != 0 {
|
||||||
|
n += 1 + sovTask(uint64(m.Pid))
|
||||||
|
}
|
||||||
|
if m.Info != nil {
|
||||||
|
l = m.Info.Size()
|
||||||
|
n += 1 + l + sovTask(uint64(l))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovTask(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozTask(x uint64) (n int) {
|
||||||
|
return sovTask(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (this *Process) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&Process{`,
|
||||||
|
`ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`,
|
||||||
|
`ID:` + fmt.Sprintf("%v", this.ID) + `,`,
|
||||||
|
`Pid:` + fmt.Sprintf("%v", this.Pid) + `,`,
|
||||||
|
`Status:` + fmt.Sprintf("%v", this.Status) + `,`,
|
||||||
|
`Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`,
|
||||||
|
`Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`,
|
||||||
|
`Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`,
|
||||||
|
`Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`,
|
||||||
|
`ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`,
|
||||||
|
`ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf1.Timestamp", 1), `&`, ``, 1) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func (this *ProcessInfo) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&ProcessInfo{`,
|
||||||
|
`Pid:` + fmt.Sprintf("%v", this.Pid) + `,`,
|
||||||
|
`Info:` + strings.Replace(fmt.Sprintf("%v", this.Info), "Any", "google_protobuf2.Any", 1) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func valueToStringTask(v interface{}) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("*%v", pv)
|
||||||
|
}
|
||||||
|
func (m *Process) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Process: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Process: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.ContainerID = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.ID = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType)
|
||||||
|
}
|
||||||
|
m.Pid = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Pid |= (uint32(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
|
||||||
|
}
|
||||||
|
m.Status = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Status |= (Status(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Stdin = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 6:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Stdout = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 7:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Stderr = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 8:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType)
|
||||||
|
}
|
||||||
|
var v int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
v |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Terminal = bool(v != 0)
|
||||||
|
case 9:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType)
|
||||||
|
}
|
||||||
|
m.ExitStatus = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.ExitStatus |= (uint32(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 10:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipTask(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *ProcessInfo) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: ProcessInfo: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: ProcessInfo: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType)
|
||||||
|
}
|
||||||
|
m.Pid = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Pid |= (uint32(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Info == nil {
|
||||||
|
m.Info = &google_protobuf2.Any{}
|
||||||
|
}
|
||||||
|
if err := m.Info.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipTask(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipTask(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthTask
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowTask
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipTask(dAtA[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthTask = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowTask = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/containerd/containerd/api/types/task/task.proto", fileDescriptorTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptorTask = []byte{
|
||||||
|
// 545 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x3f, 0x6f, 0xd3, 0x40,
|
||||||
|
0x18, 0xc6, 0x7d, 0x6e, 0xeb, 0xa6, 0xe7, 0xb6, 0x18, 0x13, 0x55, 0xc6, 0x20, 0xdb, 0xea, 0x64,
|
||||||
|
0x31, 0xd8, 0x22, 0xdd, 0xd8, 0xf2, 0x4f, 0xc8, 0x42, 0x72, 0x23, 0x27, 0x11, 0x6c, 0x91, 0x13,
|
||||||
|
0x5f, 0xcc, 0xa9, 0xcd, 0x9d, 0x65, 0x9f, 0x81, 0x6c, 0x8c, 0xa8, 0x13, 0x5f, 0xa0, 0x13, 0x7c,
|
||||||
|
0x0a, 0x3e, 0x41, 0x46, 0x26, 0xc4, 0x14, 0xa8, 0x3f, 0x09, 0x3a, 0xdb, 0x49, 0x23, 0x60, 0x39,
|
||||||
|
0xbd, 0xef, 0xf3, 0x7b, 0xee, 0xbd, 0xf7, 0x1e, 0xf8, 0x22, 0xc6, 0xec, 0x6d, 0x3e, 0x75, 0x66,
|
||||||
|
0x74, 0xe1, 0xce, 0x28, 0x61, 0x21, 0x26, 0x28, 0x8d, 0x76, 0xcb, 0x30, 0xc1, 0x2e, 0x5b, 0x26,
|
||||||
|
0x28, 0x73, 0x59, 0x98, 0x5d, 0x95, 0x87, 0x93, 0xa4, 0x94, 0x51, 0xf5, 0xd1, 0xbd, 0xcb, 0x79,
|
||||||
|
0xf7, 0xdc, 0x29, 0x4d, 0x7a, 0x33, 0xa6, 0x31, 0x2d, 0xb9, 0xcb, 0xab, 0xca, 0xaa, 0x9b, 0x31,
|
||||||
|
0xa5, 0xf1, 0x35, 0x72, 0xcb, 0x6e, 0x9a, 0xcf, 0x5d, 0x86, 0x17, 0x28, 0x63, 0xe1, 0x22, 0xa9,
|
||||||
|
0x0d, 0x8f, 0xff, 0x36, 0x84, 0x64, 0x59, 0xa1, 0xf3, 0x42, 0x84, 0x87, 0x83, 0x94, 0xce, 0x50,
|
||||||
|
0x96, 0xa9, 0x2d, 0x78, 0xbc, 0x7d, 0x74, 0x82, 0x23, 0x0d, 0x58, 0xc0, 0x3e, 0xea, 0x3c, 0x28,
|
||||||
|
0xd6, 0xa6, 0xdc, 0xdd, 0xe8, 0x5e, 0x2f, 0x90, 0xb7, 0x26, 0x2f, 0x52, 0xcf, 0xa0, 0x88, 0x23,
|
||||||
|
0x4d, 0x2c, 0x9d, 0x52, 0xb1, 0x36, 0x45, 0xaf, 0x17, 0x88, 0x38, 0x52, 0x15, 0xb8, 0x97, 0xe0,
|
||||||
|
0x48, 0xdb, 0xb3, 0x80, 0x7d, 0x12, 0xf0, 0x52, 0xbd, 0x80, 0x52, 0xc6, 0x42, 0x96, 0x67, 0xda,
|
||||||
|
0xbe, 0x05, 0xec, 0xd3, 0xd6, 0x13, 0xe7, 0x3f, 0x3f, 0x74, 0x86, 0xa5, 0x25, 0xa8, 0xad, 0x6a,
|
||||||
|
0x13, 0x1e, 0x64, 0x2c, 0xc2, 0x44, 0x3b, 0xe0, 0x2f, 0x04, 0x55, 0xa3, 0x9e, 0xf1, 0x51, 0x11,
|
||||||
|
0xcd, 0x99, 0x26, 0x95, 0x72, 0xdd, 0xd5, 0x3a, 0x4a, 0x53, 0xed, 0x70, 0xab, 0xa3, 0x34, 0x55,
|
||||||
|
0x75, 0xd8, 0x60, 0x28, 0x5d, 0x60, 0x12, 0x5e, 0x6b, 0x0d, 0x0b, 0xd8, 0x8d, 0x60, 0xdb, 0xab,
|
||||||
|
0x26, 0x94, 0xd1, 0x07, 0xcc, 0x26, 0xf5, 0x6e, 0x47, 0xe5, 0xc2, 0x90, 0x4b, 0xd5, 0x2a, 0x6a,
|
||||||
|
0x1b, 0x1e, 0xf1, 0x0e, 0x45, 0x93, 0x90, 0x69, 0xd0, 0x02, 0xb6, 0xdc, 0xd2, 0x9d, 0x2a, 0x50,
|
||||||
|
0x67, 0x13, 0xa8, 0x33, 0xda, 0x24, 0xde, 0x69, 0xac, 0xd6, 0xa6, 0xf0, 0xf9, 0x97, 0x09, 0x82,
|
||||||
|
0x46, 0x75, 0xad, 0xcd, 0xce, 0x3d, 0x28, 0xd7, 0x19, 0x7b, 0x64, 0x4e, 0x37, 0xd9, 0x80, 0xfb,
|
||||||
|
0x6c, 0x6c, 0xb8, 0x8f, 0xc9, 0x9c, 0x96, 0x39, 0xca, 0xad, 0xe6, 0x3f, 0xe3, 0xdb, 0x64, 0x19,
|
||||||
|
0x94, 0x8e, 0x67, 0x3f, 0x00, 0x94, 0xea, 0xc5, 0x0c, 0x78, 0x38, 0xf6, 0x5f, 0xf9, 0x97, 0xaf,
|
||||||
|
0x7d, 0x45, 0xd0, 0x1f, 0xde, 0xdc, 0x5a, 0x27, 0x15, 0x18, 0x93, 0x2b, 0x42, 0xdf, 0x13, 0xce,
|
||||||
|
0xbb, 0x41, 0xbf, 0x3d, 0xea, 0xf7, 0x14, 0xb0, 0xcb, 0xbb, 0x29, 0x0a, 0x19, 0x8a, 0x38, 0x0f,
|
||||||
|
0xc6, 0xbe, 0xef, 0xf9, 0x2f, 0x15, 0x71, 0x97, 0x07, 0x39, 0x21, 0x98, 0xc4, 0x9c, 0x0f, 0x47,
|
||||||
|
0x97, 0x83, 0x41, 0xbf, 0xa7, 0xec, 0xed, 0xf2, 0x21, 0xa3, 0x49, 0x82, 0x22, 0xf5, 0x29, 0x94,
|
||||||
|
0x06, 0xed, 0xf1, 0xb0, 0xdf, 0x53, 0xf6, 0x75, 0xe5, 0xe6, 0xd6, 0x3a, 0xae, 0xf0, 0x20, 0xcc,
|
||||||
|
0xb3, 0x6a, 0x3a, 0xa7, 0x7c, 0xfa, 0xc1, 0xee, 0x6d, 0x8e, 0x31, 0x89, 0xf5, 0xd3, 0x4f, 0x5f,
|
||||||
|
0x0c, 0xe1, 0xdb, 0x57, 0xa3, 0xfe, 0x4d, 0x47, 0x5b, 0xdd, 0x19, 0xc2, 0xcf, 0x3b, 0x43, 0xf8,
|
||||||
|
0x58, 0x18, 0x60, 0x55, 0x18, 0xe0, 0x7b, 0x61, 0x80, 0xdf, 0x85, 0x01, 0xde, 0x08, 0x53, 0xa9,
|
||||||
|
0x0c, 0xe2, 0xe2, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x32, 0xd2, 0x86, 0x50, 0x03, 0x00,
|
||||||
|
0x00,
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package containerd.v1.types;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/any.proto";
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
option (gogoproto.goproto_enum_prefix) = false;
|
||||||
|
option (gogoproto.enum_customname) = "Status";
|
||||||
|
|
||||||
|
UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "StatusUnknown"];
|
||||||
|
CREATED = 1 [(gogoproto.enumvalue_customname) = "StatusCreated"];
|
||||||
|
RUNNING = 2 [(gogoproto.enumvalue_customname) = "StatusRunning"];
|
||||||
|
STOPPED = 3 [(gogoproto.enumvalue_customname) = "StatusStopped"];
|
||||||
|
PAUSED = 4 [(gogoproto.enumvalue_customname) = "StatusPaused"];
|
||||||
|
PAUSING = 5 [(gogoproto.enumvalue_customname) = "StatusPausing"];
|
||||||
|
}
|
||||||
|
|
||||||
|
message Process {
|
||||||
|
string container_id = 1;
|
||||||
|
string id = 2;
|
||||||
|
uint32 pid = 3;
|
||||||
|
Status status = 4;
|
||||||
|
string stdin = 5;
|
||||||
|
string stdout = 6;
|
||||||
|
string stderr = 7;
|
||||||
|
bool terminal = 8;
|
||||||
|
uint32 exit_status = 9;
|
||||||
|
google.protobuf.Timestamp exited_at = 10 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message ProcessInfo {
|
||||||
|
// PID is the process ID.
|
||||||
|
uint32 pid = 1;
|
||||||
|
// Info contains additional process information.
|
||||||
|
//
|
||||||
|
// Info varies by platform.
|
||||||
|
google.protobuf.Any info = 2;
|
||||||
|
}
|
153
vendor/github.com/containerd/containerd/archive/compression/compression.go
generated
vendored
Normal file
153
vendor/github.com/containerd/containerd/archive/compression/compression.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package compression
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Compression is the state represents if compressed or not.
|
||||||
|
Compression int
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Uncompressed represents the uncompressed.
|
||||||
|
Uncompressed Compression = iota
|
||||||
|
// Gzip is gzip compression algorithm.
|
||||||
|
Gzip
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bufioReader32KPool = &sync.Pool{
|
||||||
|
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecompressReadCloser include the stream after decompress and the compress method detected.
|
||||||
|
type DecompressReadCloser interface {
|
||||||
|
io.ReadCloser
|
||||||
|
// GetCompression returns the compress method which is used before decompressing
|
||||||
|
GetCompression() Compression
|
||||||
|
}
|
||||||
|
|
||||||
|
type readCloserWrapper struct {
|
||||||
|
io.Reader
|
||||||
|
compression Compression
|
||||||
|
closer func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readCloserWrapper) Close() error {
|
||||||
|
if r.closer != nil {
|
||||||
|
return r.closer()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readCloserWrapper) GetCompression() Compression {
|
||||||
|
return r.compression
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeCloserWrapper struct {
|
||||||
|
io.Writer
|
||||||
|
closer func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *writeCloserWrapper) Close() error {
|
||||||
|
if w.closer != nil {
|
||||||
|
w.closer()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectCompression detects the compression algorithm of the source.
|
||||||
|
func DetectCompression(source []byte) Compression {
|
||||||
|
for compression, m := range map[Compression][]byte{
|
||||||
|
Gzip: {0x1F, 0x8B, 0x08},
|
||||||
|
} {
|
||||||
|
if len(source) < len(m) {
|
||||||
|
// Len too short
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if bytes.Equal(m, source[:len(m)]) {
|
||||||
|
return compression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Uncompressed
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
|
||||||
|
func DecompressStream(archive io.Reader) (DecompressReadCloser, error) {
|
||||||
|
buf := bufioReader32KPool.Get().(*bufio.Reader)
|
||||||
|
buf.Reset(archive)
|
||||||
|
bs, err := buf.Peek(10)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
// Note: we'll ignore any io.EOF error because there are some odd
|
||||||
|
// cases where the layer.tar file will be empty (zero bytes) and
|
||||||
|
// that results in an io.EOF from the Peek() call. So, in those
|
||||||
|
// cases we'll just treat it as a non-compressed stream and
|
||||||
|
// that means just create an empty layer.
|
||||||
|
// See Issue docker/docker#18170
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
closer := func() error {
|
||||||
|
buf.Reset(nil)
|
||||||
|
bufioReader32KPool.Put(buf)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch compression := DetectCompression(bs); compression {
|
||||||
|
case Uncompressed:
|
||||||
|
readBufWrapper := &readCloserWrapper{buf, compression, closer}
|
||||||
|
return readBufWrapper, nil
|
||||||
|
case Gzip:
|
||||||
|
gzReader, err := gzip.NewReader(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
readBufWrapper := &readCloserWrapper{gzReader, compression, closer}
|
||||||
|
return readBufWrapper, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompressStream compresseses the dest with specified compression algorithm.
|
||||||
|
func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) {
|
||||||
|
switch compression {
|
||||||
|
case Uncompressed:
|
||||||
|
return &writeCloserWrapper{dest, nil}, nil
|
||||||
|
case Gzip:
|
||||||
|
return gzip.NewWriter(dest), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension returns the extension of a file that uses the specified compression algorithm.
|
||||||
|
func (compression *Compression) Extension() string {
|
||||||
|
switch *compression {
|
||||||
|
case Gzip:
|
||||||
|
return "gz"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"archive/tar"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Forked from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go
|
||||||
|
// as archive/tar doesn't support CreationTime, but does handle PAX time parsing,
|
||||||
|
// and there's no need to re-invent the wheel.
|
||||||
|
|
||||||
|
// parsePAXTime takes a string of the form %d.%d as described in the PAX
|
||||||
|
// specification. Note that this implementation allows for negative timestamps,
|
||||||
|
// which is allowed for by the PAX specification, but not always portable.
|
||||||
|
func parsePAXTime(s string) (time.Time, error) {
|
||||||
|
const maxNanoSecondDigits = 9
|
||||||
|
|
||||||
|
// Split string into seconds and sub-seconds parts.
|
||||||
|
ss, sn := s, ""
|
||||||
|
if pos := strings.IndexByte(s, '.'); pos >= 0 {
|
||||||
|
ss, sn = s[:pos], s[pos+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the seconds.
|
||||||
|
secs, err := strconv.ParseInt(ss, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, tar.ErrHeader
|
||||||
|
}
|
||||||
|
if len(sn) == 0 {
|
||||||
|
return time.Unix(secs, 0), nil // No sub-second values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the nanoseconds.
|
||||||
|
if strings.Trim(sn, "0123456789") != "" {
|
||||||
|
return time.Time{}, tar.ErrHeader
|
||||||
|
}
|
||||||
|
if len(sn) < maxNanoSecondDigits {
|
||||||
|
sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
|
||||||
|
} else {
|
||||||
|
sn = sn[:maxNanoSecondDigits] // Right truncate
|
||||||
|
}
|
||||||
|
nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
|
||||||
|
if len(ss) > 0 && ss[0] == '-' {
|
||||||
|
return time.Unix(secs, -nsecs), nil // Negative correction
|
||||||
|
}
|
||||||
|
return time.Unix(secs, nsecs), nil
|
||||||
|
}
|
|
@ -0,0 +1,686 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/continuity/fs"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
buffer := make([]byte, 32*1024)
|
||||||
|
return &buffer
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var errInvalidArchive = errors.New("invalid archive")
|
||||||
|
|
||||||
|
// Diff returns a tar stream of the computed filesystem
|
||||||
|
// difference between the provided directories.
|
||||||
|
//
|
||||||
|
// Produces a tar using OCI style file markers for deletions. Deleted
|
||||||
|
// files will be prepended with the prefix ".wh.". This style is
|
||||||
|
// based off AUFS whiteouts.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md
|
||||||
|
func Diff(ctx context.Context, a, b string) io.ReadCloser {
|
||||||
|
r, w := io.Pipe()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := WriteDiff(ctx, w, a, b)
|
||||||
|
if err = w.CloseWithError(err); err != nil {
|
||||||
|
log.G(ctx).WithError(err).Debugf("closing tar pipe failed")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteDiff writes a tar stream of the computed difference between the
|
||||||
|
// provided directories.
|
||||||
|
//
|
||||||
|
// Produces a tar using OCI style file markers for deletions. Deleted
|
||||||
|
// files will be prepended with the prefix ".wh.". This style is
|
||||||
|
// based off AUFS whiteouts.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md
|
||||||
|
func WriteDiff(ctx context.Context, w io.Writer, a, b string) error {
|
||||||
|
cw := newChangeWriter(w, b)
|
||||||
|
err := fs.Changes(ctx, a, b, cw.HandleChange)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create diff tar stream")
|
||||||
|
}
|
||||||
|
return cw.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// whiteoutPrefix prefix means file is a whiteout. If this is followed by a
|
||||||
|
// filename this means that file has been removed from the base layer.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#whiteouts
|
||||||
|
whiteoutPrefix = ".wh."
|
||||||
|
|
||||||
|
// whiteoutMetaPrefix prefix means whiteout has a special meaning and is not
|
||||||
|
// for removing an actual file. Normally these files are excluded from exported
|
||||||
|
// archives.
|
||||||
|
whiteoutMetaPrefix = whiteoutPrefix + whiteoutPrefix
|
||||||
|
|
||||||
|
// whiteoutLinkDir is a directory AUFS uses for storing hardlink links to other
|
||||||
|
// layers. Normally these should not go into exported archives and all changed
|
||||||
|
// hardlinks should be copied to the top layer.
|
||||||
|
whiteoutLinkDir = whiteoutMetaPrefix + "plnk"
|
||||||
|
|
||||||
|
// whiteoutOpaqueDir file means directory has been made opaque - meaning
|
||||||
|
// readdir calls to this directory do not follow to lower layers.
|
||||||
|
whiteoutOpaqueDir = whiteoutMetaPrefix + ".opq"
|
||||||
|
|
||||||
|
paxSchilyXattr = "SCHILY.xattrs."
|
||||||
|
)
|
||||||
|
|
||||||
|
// Apply applies a tar stream of an OCI style diff tar.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
||||||
|
func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int64, error) {
|
||||||
|
root = filepath.Clean(root)
|
||||||
|
|
||||||
|
var options ApplyOptions
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(&options); err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed to apply option")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if options.Filter == nil {
|
||||||
|
options.Filter = all
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply(ctx, root, tar.NewReader(r), options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyNaive applies a tar stream of an OCI style diff tar.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
||||||
|
func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) {
|
||||||
|
var (
|
||||||
|
dirs []*tar.Header
|
||||||
|
|
||||||
|
// Used for handling opaque directory markers which
|
||||||
|
// may occur out of order
|
||||||
|
unpackedPaths = make(map[string]struct{})
|
||||||
|
|
||||||
|
// Used for aufs plink directory
|
||||||
|
aufsTempdir = ""
|
||||||
|
aufsHardlinks = make(map[string]*tar.Header)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Iterate through the files in the archive.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return 0, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
// end of tar archive
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
size += hdr.Size
|
||||||
|
|
||||||
|
// Normalize name, for safety and for a simple is-root check
|
||||||
|
hdr.Name = filepath.Clean(hdr.Name)
|
||||||
|
|
||||||
|
accept, err := options.Filter(hdr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !accept {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if skipFile(hdr) {
|
||||||
|
log.G(ctx).Warnf("file %q ignored: archive may not be supported on system", hdr.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split name and resolve symlinks for root directory.
|
||||||
|
ppath, base := filepath.Split(hdr.Name)
|
||||||
|
ppath, err = fs.RootPath(root, ppath)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed to get root path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join to root before joining to parent path to ensure relative links are
|
||||||
|
// already resolved based on the root before adding to parent.
|
||||||
|
path := filepath.Join(ppath, filepath.Join("/", base))
|
||||||
|
if path == root {
|
||||||
|
log.G(ctx).Debugf("file %q ignored: resolved to root", hdr.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If file is not directly under root, ensure parent directory
|
||||||
|
// exists or is created.
|
||||||
|
if ppath != root {
|
||||||
|
parentPath := ppath
|
||||||
|
if base == "" {
|
||||||
|
parentPath = filepath.Dir(path)
|
||||||
|
}
|
||||||
|
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||||
|
err = mkdirAll(parentPath, 0700)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip AUFS metadata dirs
|
||||||
|
if strings.HasPrefix(hdr.Name, whiteoutMetaPrefix) {
|
||||||
|
// Regular files inside /.wh..wh.plnk can be used as hardlink targets
|
||||||
|
// We don't want this directory, but we need the files in them so that
|
||||||
|
// such hardlinks can be resolved.
|
||||||
|
if strings.HasPrefix(hdr.Name, whiteoutLinkDir) && hdr.Typeflag == tar.TypeReg {
|
||||||
|
basename := filepath.Base(hdr.Name)
|
||||||
|
aufsHardlinks[basename] = hdr
|
||||||
|
if aufsTempdir == "" {
|
||||||
|
if aufsTempdir, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "dockerplnk"); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(aufsTempdir)
|
||||||
|
}
|
||||||
|
p, err := fs.RootPath(aufsTempdir, basename)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := createTarFile(ctx, p, root, hdr, tr); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdr.Name != whiteoutOpaqueDir {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(base, whiteoutPrefix) {
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
if base == whiteoutOpaqueDir {
|
||||||
|
_, err := os.Lstat(dir)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = nil // parent was deleted
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if path == dir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, exists := unpackedPaths[path]; !exists {
|
||||||
|
err := os.RemoveAll(path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
originalBase := base[len(whiteoutPrefix):]
|
||||||
|
originalPath := filepath.Join(dir, originalBase)
|
||||||
|
|
||||||
|
// Ensure originalPath is under dir
|
||||||
|
if dir[len(dir)-1] != filepath.Separator {
|
||||||
|
dir += string(filepath.Separator)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(originalPath, dir) {
|
||||||
|
return 0, errors.Wrapf(errInvalidArchive, "invalid whiteout name: %v", base)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.RemoveAll(originalPath); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// If path exits we almost always just want to remove and replace it.
|
||||||
|
// The only exception is when it is a directory *and* the file from
|
||||||
|
// the layer is also a directory. Then we want to merge them (i.e.
|
||||||
|
// just apply the metadata from the layer).
|
||||||
|
if fi, err := os.Lstat(path); err == nil {
|
||||||
|
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
|
||||||
|
if err := os.RemoveAll(path); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcData := io.Reader(tr)
|
||||||
|
srcHdr := hdr
|
||||||
|
|
||||||
|
// Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so
|
||||||
|
// we manually retarget these into the temporary files we extracted them into
|
||||||
|
if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), whiteoutLinkDir) {
|
||||||
|
linkBasename := filepath.Base(hdr.Linkname)
|
||||||
|
srcHdr = aufsHardlinks[linkBasename]
|
||||||
|
if srcHdr == nil {
|
||||||
|
return 0, fmt.Errorf("Invalid aufs hardlink")
|
||||||
|
}
|
||||||
|
p, err := fs.RootPath(aufsTempdir, linkBasename)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
tmpFile, err := os.Open(p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer tmpFile.Close()
|
||||||
|
srcData = tmpFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := createTarFile(ctx, path, root, srcHdr, srcData); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directory mtimes must be handled at the end to avoid further
|
||||||
|
// file creation in them to modify the directory mtime
|
||||||
|
if hdr.Typeflag == tar.TypeDir {
|
||||||
|
dirs = append(dirs, hdr)
|
||||||
|
}
|
||||||
|
unpackedPaths[path] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hdr := range dirs {
|
||||||
|
path, err := fs.RootPath(root, hdr.Name)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := chtimes(path, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header, reader io.Reader) error {
|
||||||
|
// hdr.Mode is in linux format, which we can use for syscalls,
|
||||||
|
// but for os.Foo() calls we need the mode converted to os.FileMode,
|
||||||
|
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
|
||||||
|
hdrInfo := hdr.FileInfo()
|
||||||
|
|
||||||
|
switch hdr.Typeflag {
|
||||||
|
case tar.TypeDir:
|
||||||
|
// Create directory unless it exists as a directory already.
|
||||||
|
// In that case we just want to merge the two
|
||||||
|
if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
|
||||||
|
if err := mkdir(path, hdrInfo.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case tar.TypeReg, tar.TypeRegA:
|
||||||
|
file, err := openFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, hdrInfo.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = copyBuffered(ctx, file, reader)
|
||||||
|
if err1 := file.Close(); err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case tar.TypeBlock, tar.TypeChar:
|
||||||
|
// Handle this is an OS-specific way
|
||||||
|
if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case tar.TypeFifo:
|
||||||
|
// Handle this is an OS-specific way
|
||||||
|
if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case tar.TypeLink:
|
||||||
|
targetPath, err := hardlinkRootPath(extractDir, hdr.Linkname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Link(targetPath, path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case tar.TypeSymlink:
|
||||||
|
if err := os.Symlink(hdr.Linkname, path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case tar.TypeXGlobalHeader:
|
||||||
|
log.G(ctx).Debug("PAX Global Extended Headers found and ignored")
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.Errorf("unhandled tar header type %d\n", hdr.Typeflag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lchown is not supported on Windows.
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range hdr.PAXRecords {
|
||||||
|
if strings.HasPrefix(key, paxSchilyXattr) {
|
||||||
|
key = key[len(paxSchilyXattr):]
|
||||||
|
if err := setxattr(path, key, value); err != nil {
|
||||||
|
if errors.Cause(err) == syscall.ENOTSUP {
|
||||||
|
log.G(ctx).WithError(err).Warnf("ignored xattr %s in archive", key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no LChmod, so ignore mode for symlink. Also, this
|
||||||
|
// must happen after chown, as that can modify the file mode
|
||||||
|
if err := handleLChmod(hdr, path, hdrInfo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return chtimes(path, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime))
|
||||||
|
}
|
||||||
|
|
||||||
|
type changeWriter struct {
|
||||||
|
tw *tar.Writer
|
||||||
|
source string
|
||||||
|
whiteoutT time.Time
|
||||||
|
inodeSrc map[uint64]string
|
||||||
|
inodeRefs map[uint64][]string
|
||||||
|
addedDirs map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChangeWriter(w io.Writer, source string) *changeWriter {
|
||||||
|
return &changeWriter{
|
||||||
|
tw: tar.NewWriter(w),
|
||||||
|
source: source,
|
||||||
|
whiteoutT: time.Now(),
|
||||||
|
inodeSrc: map[uint64]string{},
|
||||||
|
inodeRefs: map[uint64][]string{},
|
||||||
|
addedDirs: map[string]struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *changeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if k == fs.ChangeKindDelete {
|
||||||
|
whiteOutDir := filepath.Dir(p)
|
||||||
|
whiteOutBase := filepath.Base(p)
|
||||||
|
whiteOut := filepath.Join(whiteOutDir, whiteoutPrefix+whiteOutBase)
|
||||||
|
hdr := &tar.Header{
|
||||||
|
Typeflag: tar.TypeReg,
|
||||||
|
Name: whiteOut[1:],
|
||||||
|
Size: 0,
|
||||||
|
ModTime: cw.whiteoutT,
|
||||||
|
AccessTime: cw.whiteoutT,
|
||||||
|
ChangeTime: cw.whiteoutT,
|
||||||
|
}
|
||||||
|
if err := cw.includeParents(hdr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cw.tw.WriteHeader(hdr); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write whiteout header")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var (
|
||||||
|
link string
|
||||||
|
err error
|
||||||
|
source = filepath.Join(cw.source, p)
|
||||||
|
)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case f.Mode()&os.ModeSocket != 0:
|
||||||
|
return nil // ignore sockets
|
||||||
|
case f.Mode()&os.ModeSymlink != 0:
|
||||||
|
if link, err = os.Readlink(source); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr, err := tar.FileInfoHeader(f, link)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
||||||
|
|
||||||
|
name := p
|
||||||
|
if strings.HasPrefix(name, string(filepath.Separator)) {
|
||||||
|
name, err = filepath.Rel(string(filepath.Separator), name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to make path relative")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name, err = tarName(name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "cannot canonicalize path")
|
||||||
|
}
|
||||||
|
// suffix with '/' for directories
|
||||||
|
if f.IsDir() && !strings.HasSuffix(name, "/") {
|
||||||
|
name += "/"
|
||||||
|
}
|
||||||
|
hdr.Name = name
|
||||||
|
|
||||||
|
if err := setHeaderForSpecialDevice(hdr, name, f); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to set device headers")
|
||||||
|
}
|
||||||
|
|
||||||
|
// additionalLinks stores file names which must be linked to
|
||||||
|
// this file when this file is added
|
||||||
|
var additionalLinks []string
|
||||||
|
inode, isHardlink := fs.GetLinkInfo(f)
|
||||||
|
if isHardlink {
|
||||||
|
// If the inode has a source, always link to it
|
||||||
|
if source, ok := cw.inodeSrc[inode]; ok {
|
||||||
|
hdr.Typeflag = tar.TypeLink
|
||||||
|
hdr.Linkname = source
|
||||||
|
hdr.Size = 0
|
||||||
|
} else {
|
||||||
|
if k == fs.ChangeKindUnmodified {
|
||||||
|
cw.inodeRefs[inode] = append(cw.inodeRefs[inode], name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cw.inodeSrc[inode] = name
|
||||||
|
additionalLinks = cw.inodeRefs[inode]
|
||||||
|
delete(cw.inodeRefs, inode)
|
||||||
|
}
|
||||||
|
} else if k == fs.ChangeKindUnmodified {
|
||||||
|
// Nothing to write to diff
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if capability, err := getxattr(source, "security.capability"); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get capabilities xattr")
|
||||||
|
} else if capability != nil {
|
||||||
|
if hdr.PAXRecords == nil {
|
||||||
|
hdr.PAXRecords = map[string]string{}
|
||||||
|
}
|
||||||
|
hdr.PAXRecords[paxSchilyXattr+"security.capability"] = string(capability)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cw.includeParents(hdr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cw.tw.WriteHeader(hdr); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write file header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
|
||||||
|
file, err := open(source)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to open path: %v", source)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
n, err := copyBuffered(context.TODO(), cw.tw, file)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to copy")
|
||||||
|
}
|
||||||
|
if n != hdr.Size {
|
||||||
|
return errors.New("short write copying file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if additionalLinks != nil {
|
||||||
|
source = hdr.Name
|
||||||
|
for _, extra := range additionalLinks {
|
||||||
|
hdr.Name = extra
|
||||||
|
hdr.Typeflag = tar.TypeLink
|
||||||
|
hdr.Linkname = source
|
||||||
|
hdr.Size = 0
|
||||||
|
|
||||||
|
if err := cw.includeParents(hdr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cw.tw.WriteHeader(hdr); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write file header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *changeWriter) Close() error {
|
||||||
|
if err := cw.tw.Close(); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to close tar writer")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *changeWriter) includeParents(hdr *tar.Header) error {
|
||||||
|
name := strings.TrimRight(hdr.Name, "/")
|
||||||
|
fname := filepath.Join(cw.source, name)
|
||||||
|
parent := filepath.Dir(name)
|
||||||
|
pname := filepath.Join(cw.source, parent)
|
||||||
|
|
||||||
|
// Do not include root directory as parent
|
||||||
|
if fname != cw.source && pname != cw.source {
|
||||||
|
_, ok := cw.addedDirs[parent]
|
||||||
|
if !ok {
|
||||||
|
cw.addedDirs[parent] = struct{}{}
|
||||||
|
fi, err := os.Stat(pname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cw.HandleChange(fs.ChangeKindModify, parent, fi, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Typeflag == tar.TypeDir {
|
||||||
|
cw.addedDirs[name] = struct{}{}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written int64, err error) {
|
||||||
|
buf := bufPool.Get().(*[]byte)
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = ctx.Err()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
nr, er := src.Read(*buf)
|
||||||
|
if nr > 0 {
|
||||||
|
nw, ew := dst.Write((*buf)[0:nr])
|
||||||
|
if nw > 0 {
|
||||||
|
written += int64(nw)
|
||||||
|
}
|
||||||
|
if ew != nil {
|
||||||
|
err = ew
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if nr != nw {
|
||||||
|
err = io.ErrShortWrite
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if er != nil {
|
||||||
|
if er != io.EOF {
|
||||||
|
err = er
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return written, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// hardlinkRootPath returns target linkname, evaluating and bounding any
|
||||||
|
// symlink to the parent directory.
|
||||||
|
//
|
||||||
|
// NOTE: Allow hardlink to the softlink, not the real one. For example,
|
||||||
|
//
|
||||||
|
// touch /tmp/zzz
|
||||||
|
// ln -s /tmp/zzz /tmp/xxx
|
||||||
|
// ln /tmp/xxx /tmp/yyy
|
||||||
|
//
|
||||||
|
// /tmp/yyy should be softlink which be same of /tmp/xxx, not /tmp/zzz.
|
||||||
|
func hardlinkRootPath(root, linkname string) (string, error) {
|
||||||
|
ppath, base := filepath.Split(linkname)
|
||||||
|
ppath, err := fs.RootPath(root, ppath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPath := filepath.Join(ppath, base)
|
||||||
|
if !strings.HasPrefix(targetPath, root) {
|
||||||
|
targetPath = root
|
||||||
|
}
|
||||||
|
return targetPath, nil
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import "archive/tar"
|
||||||
|
|
||||||
|
// ApplyOpt allows setting mutable archive apply properties on creation
|
||||||
|
type ApplyOpt func(options *ApplyOptions) error
|
||||||
|
|
||||||
|
// Filter specific files from the archive
|
||||||
|
type Filter func(*tar.Header) (bool, error)
|
||||||
|
|
||||||
|
// all allows all files
|
||||||
|
func all(_ *tar.Header) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFilter uses the filter to select which files are to be extracted.
|
||||||
|
func WithFilter(f Filter) ApplyOpt {
|
||||||
|
return func(options *ApplyOptions) error {
|
||||||
|
options.Filter = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
// ApplyOptions provides additional options for an Apply operation
|
||||||
|
type ApplyOptions struct {
|
||||||
|
Filter Filter // Filter tar headers
|
||||||
|
}
|
45
vendor/github.com/containerd/containerd/archive/tar_opts_windows.go
generated
vendored
Normal file
45
vendor/github.com/containerd/containerd/archive/tar_opts_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
// ApplyOptions provides additional options for an Apply operation
|
||||||
|
type ApplyOptions struct {
|
||||||
|
ParentLayerPaths []string // Parent layer paths used for Windows layer apply
|
||||||
|
IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer
|
||||||
|
Filter Filter // Filter tar headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithParentLayers adds parent layers to the apply process this is required
|
||||||
|
// for all Windows layers except the base layer.
|
||||||
|
func WithParentLayers(parentPaths []string) ApplyOpt {
|
||||||
|
return func(options *ApplyOptions) error {
|
||||||
|
options.ParentLayerPaths = parentPaths
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsWindowsContainerLayer indicates that the tar stream to apply is that of
|
||||||
|
// a Windows Container Layer. The caller must be holding SeBackupPrivilege and
|
||||||
|
// SeRestorePrivilege.
|
||||||
|
func AsWindowsContainerLayer() ApplyOpt {
|
||||||
|
return func(options *ApplyOptions) error {
|
||||||
|
options.IsWindowsContainerLayer = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containerd/continuity/sysx"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tarName(p string) (string, error) {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||||
|
return perm
|
||||||
|
}
|
||||||
|
|
||||||
|
func setHeaderForSpecialDevice(hdr *tar.Header, name string, fi os.FileInfo) error {
|
||||||
|
s, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unsupported stat type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rdev is int32 on darwin/bsd, int64 on linux/solaris
|
||||||
|
rdev := uint64(s.Rdev) // nolint: unconvert
|
||||||
|
|
||||||
|
// Currently go does not fill in the major/minors
|
||||||
|
if s.Mode&syscall.S_IFBLK != 0 ||
|
||||||
|
s.Mode&syscall.S_IFCHR != 0 {
|
||||||
|
hdr.Devmajor = int64(unix.Major(rdev))
|
||||||
|
hdr.Devminor = int64(unix.Minor(rdev))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func open(p string) (*os.File, error) {
|
||||||
|
return os.Open(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func openFile(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||||
|
f, err := os.OpenFile(name, flag, perm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Call chmod to avoid permission mask
|
||||||
|
if err := os.Chmod(name, perm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkdirAll(path string, perm os.FileMode) error {
|
||||||
|
return os.MkdirAll(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkdir(path string, perm os.FileMode) error {
|
||||||
|
if err := os.Mkdir(path, perm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Only final created directory gets explicit permission
|
||||||
|
// call to avoid permission mask
|
||||||
|
return os.Chmod(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
inUserNS bool
|
||||||
|
nsOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func setInUserNS() {
|
||||||
|
inUserNS = system.RunningInUserNS()
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipFile(hdr *tar.Header) bool {
|
||||||
|
switch hdr.Typeflag {
|
||||||
|
case tar.TypeBlock, tar.TypeChar:
|
||||||
|
// cannot create a device if running in user namespace
|
||||||
|
nsOnce.Do(setInUserNS)
|
||||||
|
return inUserNS
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
||||||
|
// createTarFile to handle the following types of header: Block; Char; Fifo.
|
||||||
|
// This function must not be called for Block and Char when running in userns.
|
||||||
|
// (skipFile() should return true for them.)
|
||||||
|
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
||||||
|
mode := uint32(hdr.Mode & 07777)
|
||||||
|
switch hdr.Typeflag {
|
||||||
|
case tar.TypeBlock:
|
||||||
|
mode |= unix.S_IFBLK
|
||||||
|
case tar.TypeChar:
|
||||||
|
mode |= unix.S_IFCHR
|
||||||
|
case tar.TypeFifo:
|
||||||
|
mode |= unix.S_IFIFO
|
||||||
|
}
|
||||||
|
|
||||||
|
return unix.Mknod(path, mode, int(unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||||
|
if hdr.Typeflag == tar.TypeLink {
|
||||||
|
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
||||||
|
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if hdr.Typeflag != tar.TypeSymlink {
|
||||||
|
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getxattr(path, attr string) ([]byte, error) {
|
||||||
|
b, err := sysx.LGetxattr(path, attr)
|
||||||
|
if err == unix.ENOTSUP || err == sysx.ENODATA {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setxattr(path, key, value string) error {
|
||||||
|
return sysx.LSetxattr(path, key, []byte(value), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply applies a tar stream of an OCI style diff tar.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
||||||
|
func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) {
|
||||||
|
return applyNaive(ctx, root, tr, options)
|
||||||
|
}
|
|
@ -0,0 +1,449 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/hcsshim"
|
||||||
|
"github.com/containerd/containerd/sys"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MSWINDOWS pax vendor extensions
|
||||||
|
hdrMSWindowsPrefix = "MSWINDOWS."
|
||||||
|
|
||||||
|
hdrFileAttributes = hdrMSWindowsPrefix + "fileattr"
|
||||||
|
hdrSecurityDescriptor = hdrMSWindowsPrefix + "sd"
|
||||||
|
hdrRawSecurityDescriptor = hdrMSWindowsPrefix + "rawsd"
|
||||||
|
hdrMountPoint = hdrMSWindowsPrefix + "mountpoint"
|
||||||
|
hdrEaPrefix = hdrMSWindowsPrefix + "xattr."
|
||||||
|
|
||||||
|
// LIBARCHIVE pax vendor extensions
|
||||||
|
hdrLibArchivePrefix = "LIBARCHIVE."
|
||||||
|
|
||||||
|
hdrCreateTime = hdrLibArchivePrefix + "creationtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// mutatedFiles is a list of files that are mutated by the import process
|
||||||
|
// and must be backed up and restored.
|
||||||
|
mutatedFiles = map[string]string{
|
||||||
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak",
|
||||||
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak",
|
||||||
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak",
|
||||||
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// tarName returns platform-specific filepath
|
||||||
|
// to canonical posix-style path for tar archival. p is relative
|
||||||
|
// path.
|
||||||
|
func tarName(p string) (string, error) {
|
||||||
|
// windows: convert windows style relative path with backslashes
|
||||||
|
// into forward slashes. Since windows does not allow '/' or '\'
|
||||||
|
// in file names, it is mostly safe to replace however we must
|
||||||
|
// check just in case
|
||||||
|
if strings.Contains(p, "/") {
|
||||||
|
return "", fmt.Errorf("Windows path contains forward slash: %s", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Replace(p, string(os.PathSeparator), "/", -1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// chmodTarEntry is used to adjust the file permissions used in tar header based
|
||||||
|
// on the platform the archival is done.
|
||||||
|
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||||
|
perm &= 0755
|
||||||
|
// Add the x bit: make everything +x from windows
|
||||||
|
perm |= 0111
|
||||||
|
|
||||||
|
return perm
|
||||||
|
}
|
||||||
|
|
||||||
|
func setHeaderForSpecialDevice(*tar.Header, string, os.FileInfo) error {
|
||||||
|
// do nothing. no notion of Rdev, Inode, Nlink in stat on Windows
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func open(p string) (*os.File, error) {
|
||||||
|
// We use sys.OpenSequential to ensure we use sequential file
|
||||||
|
// access on Windows to avoid depleting the standby list.
|
||||||
|
return sys.OpenSequential(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func openFile(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||||
|
// Source is regular file. We use sys.OpenFileSequential to use sequential
|
||||||
|
// file access to avoid depleting the standby list on Windows.
|
||||||
|
return sys.OpenFileSequential(name, flag, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkdirAll(path string, perm os.FileMode) error {
|
||||||
|
return sys.MkdirAll(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkdir(path string, perm os.FileMode) error {
|
||||||
|
return os.Mkdir(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipFile(hdr *tar.Header) bool {
|
||||||
|
// Windows does not support filenames with colons in them. Ignore
|
||||||
|
// these files. This is not a problem though (although it might
|
||||||
|
// appear that it is). Let's suppose a client is running docker pull.
|
||||||
|
// The daemon it points to is Windows. Would it make sense for the
|
||||||
|
// client to be doing a docker pull Ubuntu for example (which has files
|
||||||
|
// with colons in the name under /usr/share/man/man3)? No, absolutely
|
||||||
|
// not as it would really only make sense that they were pulling a
|
||||||
|
// Windows image. However, for development, it is necessary to be able
|
||||||
|
// to pull Linux images which are in the repository.
|
||||||
|
//
|
||||||
|
// TODO Windows. Once the registry is aware of what images are Windows-
|
||||||
|
// specific or Linux-specific, this warning should be changed to an error
|
||||||
|
// to cater for the situation where someone does manage to upload a Linux
|
||||||
|
// image but have it tagged as Windows inadvertently.
|
||||||
|
if strings.Contains(hdr.Name, ":") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
||||||
|
// createTarFile to handle the following types of header: Block; Char; Fifo
|
||||||
|
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getxattr(path, attr string) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setxattr(path, key, value string) error {
|
||||||
|
// Return not support error, do not wrap underlying not supported
|
||||||
|
// since xattrs should not exist in windows diff archives
|
||||||
|
return errors.New("xattrs not supported on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply applies a tar stream of an OCI style diff tar of a Windows layer.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
||||||
|
func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) {
|
||||||
|
if options.IsWindowsContainerLayer {
|
||||||
|
return applyWindowsLayer(ctx, root, tr, options)
|
||||||
|
}
|
||||||
|
return applyNaive(ctx, root, tr, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyWindowsLayer applies a tar stream of an OCI style diff tar of a Windows layer.
|
||||||
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
||||||
|
func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) {
|
||||||
|
home, id := filepath.Split(root)
|
||||||
|
info := hcsshim.DriverInfo{
|
||||||
|
HomeDir: home,
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := hcsshim.NewLayerWriter(info, id, options.ParentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err2 := w.Close(); err2 != nil {
|
||||||
|
// This error should not be discarded as a failure here
|
||||||
|
// could result in an invalid layer on disk
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := bufio.NewWriter(nil)
|
||||||
|
hdr, nextErr := tr.Next()
|
||||||
|
// Iterate through the files in the archive.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return 0, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextErr == io.EOF {
|
||||||
|
// end of tar archive
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if nextErr != nil {
|
||||||
|
return 0, nextErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: path is used instead of filepath to prevent OS specific handling
|
||||||
|
// of the tar path
|
||||||
|
base := path.Base(hdr.Name)
|
||||||
|
if strings.HasPrefix(base, whiteoutPrefix) {
|
||||||
|
dir := path.Dir(hdr.Name)
|
||||||
|
originalBase := base[len(whiteoutPrefix):]
|
||||||
|
originalPath := path.Join(dir, originalBase)
|
||||||
|
if err := w.Remove(filepath.FromSlash(originalPath)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
hdr, nextErr = tr.Next()
|
||||||
|
} else if hdr.Typeflag == tar.TypeLink {
|
||||||
|
err := w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
hdr, nextErr = tr.Next()
|
||||||
|
} else {
|
||||||
|
name, fileSize, fileInfo, err := fileInfoFromHeader(hdr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := w.Add(filepath.FromSlash(name), fileInfo); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
size += fileSize
|
||||||
|
hdr, nextErr = tarToBackupStreamWithMutatedFiles(buf, w, tr, hdr, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileInfoFromHeader retrieves basic Win32 file information from a tar header, using the additional metadata written by
|
||||||
|
// WriteTarFileFromBackupStream.
|
||||||
|
func fileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) {
|
||||||
|
name = hdr.Name
|
||||||
|
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
||||||
|
size = hdr.Size
|
||||||
|
}
|
||||||
|
fileInfo = &winio.FileBasicInfo{
|
||||||
|
LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()),
|
||||||
|
LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
|
||||||
|
ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()),
|
||||||
|
|
||||||
|
// Default CreationTime to ModTime, updated below if MSWINDOWS.createtime exists
|
||||||
|
CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
|
||||||
|
}
|
||||||
|
if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok {
|
||||||
|
attr, err := strconv.ParseUint(attrStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, nil, err
|
||||||
|
}
|
||||||
|
fileInfo.FileAttributes = uintptr(attr)
|
||||||
|
} else {
|
||||||
|
if hdr.Typeflag == tar.TypeDir {
|
||||||
|
fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if createStr, ok := hdr.PAXRecords[hdrCreateTime]; ok {
|
||||||
|
createTime, err := parsePAXTime(createStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, nil, err
|
||||||
|
}
|
||||||
|
fileInfo.CreationTime = syscall.NsecToFiletime(createTime.UnixNano())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// tarToBackupStreamWithMutatedFiles reads data from a tar stream and
|
||||||
|
// writes it to a backup stream, and also saves any files that will be mutated
|
||||||
|
// by the import layer process to a backup location.
|
||||||
|
func tarToBackupStreamWithMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) {
|
||||||
|
var (
|
||||||
|
bcdBackup *os.File
|
||||||
|
bcdBackupWriter *winio.BackupFileWriter
|
||||||
|
)
|
||||||
|
if backupPath, ok := mutatedFiles[hdr.Name]; ok {
|
||||||
|
bcdBackup, err = os.Create(filepath.Join(root, backupPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
cerr := bcdBackup.Close()
|
||||||
|
if err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false)
|
||||||
|
defer func() {
|
||||||
|
cerr := bcdBackupWriter.Close()
|
||||||
|
if err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf.Reset(io.MultiWriter(w, bcdBackupWriter))
|
||||||
|
} else {
|
||||||
|
buf.Reset(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
ferr := buf.Flush()
|
||||||
|
if err == nil {
|
||||||
|
err = ferr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return writeBackupStreamFromTarFile(buf, t, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple
|
||||||
|
// tar file entries in order to collect all the alternate data streams for the file, it returns the next
|
||||||
|
// tar file that was not processed, or io.EOF is there are no more.
|
||||||
|
func writeBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) {
|
||||||
|
bw := winio.NewBackupStreamWriter(w)
|
||||||
|
var sd []byte
|
||||||
|
var err error
|
||||||
|
// Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written
|
||||||
|
// by this library will have raw binary for the security descriptor.
|
||||||
|
if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok {
|
||||||
|
sd, err = winio.SddlToSecurityDescriptor(sddl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok {
|
||||||
|
sd, err = base64.StdEncoding.DecodeString(sdraw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(sd) != 0 {
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupSecurity,
|
||||||
|
Size: int64(len(sd)),
|
||||||
|
}
|
||||||
|
err := bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = bw.Write(sd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var eas []winio.ExtendedAttribute
|
||||||
|
for k, v := range hdr.PAXRecords {
|
||||||
|
if !strings.HasPrefix(k, hdrEaPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := base64.StdEncoding.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
eas = append(eas, winio.ExtendedAttribute{
|
||||||
|
Name: k[len(hdrEaPrefix):],
|
||||||
|
Value: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(eas) != 0 {
|
||||||
|
eadata, err := winio.EncodeExtendedAttributes(eas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupEaData,
|
||||||
|
Size: int64(len(eadata)),
|
||||||
|
}
|
||||||
|
err = bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = bw.Write(eadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Typeflag == tar.TypeSymlink {
|
||||||
|
_, isMountPoint := hdr.PAXRecords[hdrMountPoint]
|
||||||
|
rp := winio.ReparsePoint{
|
||||||
|
Target: filepath.FromSlash(hdr.Linkname),
|
||||||
|
IsMountPoint: isMountPoint,
|
||||||
|
}
|
||||||
|
reparse := winio.EncodeReparsePoint(&rp)
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupReparseData,
|
||||||
|
Size: int64(len(reparse)),
|
||||||
|
}
|
||||||
|
err := bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = bw.Write(reparse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bufPool.Get().(*[]byte)
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
|
||||||
|
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupData,
|
||||||
|
Size: hdr.Size,
|
||||||
|
}
|
||||||
|
err := bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = io.CopyBuffer(bw, t, *buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy all the alternate data streams and return the next non-ADS header.
|
||||||
|
for {
|
||||||
|
ahdr, err := t.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
|
||||||
|
return ahdr, nil
|
||||||
|
}
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupAlternateData,
|
||||||
|
Size: ahdr.Size,
|
||||||
|
Name: ahdr.Name[len(hdr.Name):] + ":$DATA",
|
||||||
|
}
|
||||||
|
err = bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = io.CopyBuffer(bw, t, *buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue