mirror of https://github.com/k3d-io/k3d
[Enhancement] docs: add automatic command tree generation via cobra (#562)
parent
f90b0e9220
commit
f2af514929
@ -0,0 +1,15 @@ |
||||
# docgen |
||||
|
||||
Only used to generate the command tree for <https://k3d.io/usage/commands>. |
||||
|
||||
The code will output files in [`../docs/usage/commands/`](../docs/usage/commands/) |
||||
|
||||
## Run |
||||
|
||||
```bash |
||||
# ensure that you're in the docgen dir, as the relative path to the docs/ dir is hardcoded |
||||
cd docgen |
||||
|
||||
# run |
||||
go run ./main.go |
||||
``` |
@ -0,0 +1,34 @@ |
||||
module github.com/rancher/k3d/docgen |
||||
|
||||
go 1.16 |
||||
|
||||
require ( |
||||
github.com/Microsoft/go-winio v0.4.17 // indirect |
||||
github.com/containerd/cgroups v0.0.0-20210414074453-680c246289fb // indirect |
||||
github.com/containerd/containerd v1.5.0-rc.1 // indirect |
||||
github.com/containerd/continuity v0.0.0-20210315143101-93e15499afd5 // indirect |
||||
github.com/docker/cli v20.10.6+incompatible // indirect |
||||
github.com/docker/docker v20.10.6+incompatible // indirect |
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect |
||||
github.com/google/gofuzz v1.2.0 // indirect |
||||
github.com/imdario/mergo v0.3.12 // indirect |
||||
github.com/magiconair/properties v1.8.5 // indirect |
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect |
||||
github.com/pelletier/go-toml v1.9.0 // indirect |
||||
github.com/rancher/k3d/v4 v4.4.1 |
||||
github.com/spf13/afero v1.6.0 // indirect |
||||
github.com/spf13/cobra v1.1.3 |
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 // indirect |
||||
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78 // indirect |
||||
golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0 // indirect |
||||
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 // indirect |
||||
google.golang.org/appengine v1.6.7 // indirect |
||||
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3 // indirect |
||||
google.golang.org/grpc v1.37.0 // indirect |
||||
gopkg.in/ini.v1 v1.62.0 // indirect |
||||
k8s.io/client-go v0.21.0 // indirect |
||||
k8s.io/utils v0.0.0-20210305010621-2afb4311ab10 // indirect |
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.1 // indirect |
||||
) |
||||
|
||||
replace github.com/rancher/k3d/v4 => /home/thklein/git/github.com/rancher/k3d |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"log" |
||||
|
||||
"github.com/rancher/k3d/v4/cmd" |
||||
"github.com/spf13/cobra/doc" |
||||
) |
||||
|
||||
func main() { |
||||
k3d := cmd.GetRootCmd() |
||||
k3d.DisableAutoGenTag = true |
||||
|
||||
if err := doc.GenMarkdownTree(k3d, "../docs/usage/commands"); err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
} |
@ -0,0 +1 @@ |
||||
*.exe |
@ -0,0 +1 @@ |
||||
* @microsoft/containerplat |
@ -0,0 +1,22 @@ |
||||
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,22 @@ |
||||
# go-winio |
||||
|
||||
This repository contains utilities for efficiently performing Win32 IO operations in |
||||
Go. Currently, this is focused on accessing named pipes and other file handles, and |
||||
for using named pipes as a net transport. |
||||
|
||||
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go |
||||
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and |
||||
newer operating systems. This is similar to the implementation of network sockets in Go's net |
||||
package. |
||||
|
||||
Please see the LICENSE file for licensing information. |
||||
|
||||
This project has adopted the [Microsoft Open Source Code of |
||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information |
||||
see the [Code of Conduct |
||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact |
||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional |
||||
questions or comments. |
||||
|
||||
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe |
||||
for another named pipe implementation. |
@ -0,0 +1,280 @@ |
||||
// +build windows
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"encoding/binary" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"io/ioutil" |
||||
"os" |
||||
"runtime" |
||||
"syscall" |
||||
"unicode/utf16" |
||||
) |
||||
|
||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||
|
||||
const ( |
||||
BackupData = uint32(iota + 1) |
||||
BackupEaData |
||||
BackupSecurity |
||||
BackupAlternateData |
||||
BackupLink |
||||
BackupPropertyData |
||||
BackupObjectId |
||||
BackupReparseData |
||||
BackupSparseBlock |
||||
BackupTxfsData |
||||
) |
||||
|
||||
const ( |
||||
StreamSparseAttributes = uint32(8) |
||||
) |
||||
|
||||
const ( |
||||
WRITE_DAC = 0x40000 |
||||
WRITE_OWNER = 0x80000 |
||||
ACCESS_SYSTEM_SECURITY = 0x1000000 |
||||
) |
||||
|
||||
// BackupHeader represents a backup stream of a file.
|
||||
type BackupHeader struct { |
||||
Id uint32 // The backup stream ID
|
||||
Attributes uint32 // Stream attributes
|
||||
Size int64 // The size of the stream in bytes
|
||||
Name string // The name of the stream (for BackupAlternateData only).
|
||||
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||
} |
||||
|
||||
type win32StreamId struct { |
||||
StreamId uint32 |
||||
Attributes uint32 |
||||
Size uint64 |
||||
NameSize uint32 |
||||
} |
||||
|
||||
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||
// of BackupHeader values.
|
||||
type BackupStreamReader struct { |
||||
r io.Reader |
||||
bytesLeft int64 |
||||
} |
||||
|
||||
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||
func NewBackupStreamReader(r io.Reader) *BackupStreamReader { |
||||
return &BackupStreamReader{r, 0} |
||||
} |
||||
|
||||
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
||||
// it was not completely read.
|
||||
func (r *BackupStreamReader) Next() (*BackupHeader, error) { |
||||
if r.bytesLeft > 0 { |
||||
if s, ok := r.r.(io.Seeker); ok { |
||||
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
||||
// before trying the actual seek.
|
||||
if _, err := s.Seek(0, io.SeekCurrent); err == nil { |
||||
if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil { |
||||
return nil, err |
||||
} |
||||
r.bytesLeft = 0 |
||||
} |
||||
} |
||||
if _, err := io.Copy(ioutil.Discard, r); err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
var wsi win32StreamId |
||||
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { |
||||
return nil, err |
||||
} |
||||
hdr := &BackupHeader{ |
||||
Id: wsi.StreamId, |
||||
Attributes: wsi.Attributes, |
||||
Size: int64(wsi.Size), |
||||
} |
||||
if wsi.NameSize != 0 { |
||||
name := make([]uint16, int(wsi.NameSize/2)) |
||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil { |
||||
return nil, err |
||||
} |
||||
hdr.Name = syscall.UTF16ToString(name) |
||||
} |
||||
if wsi.StreamId == BackupSparseBlock { |
||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { |
||||
return nil, err |
||||
} |
||||
hdr.Size -= 8 |
||||
} |
||||
r.bytesLeft = hdr.Size |
||||
return hdr, nil |
||||
} |
||||
|
||||
// Read reads from the current backup stream.
|
||||
func (r *BackupStreamReader) Read(b []byte) (int, error) { |
||||
if r.bytesLeft == 0 { |
||||
return 0, io.EOF |
||||
} |
||||
if int64(len(b)) > r.bytesLeft { |
||||
b = b[:r.bytesLeft] |
||||
} |
||||
n, err := r.r.Read(b) |
||||
r.bytesLeft -= int64(n) |
||||
if err == io.EOF { |
||||
err = io.ErrUnexpectedEOF |
||||
} else if r.bytesLeft == 0 && err == nil { |
||||
err = io.EOF |
||||
} |
||||
return n, err |
||||
} |
||||
|
||||
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||
type BackupStreamWriter struct { |
||||
w io.Writer |
||||
bytesLeft int64 |
||||
} |
||||
|
||||
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter { |
||||
return &BackupStreamWriter{w, 0} |
||||
} |
||||
|
||||
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { |
||||
if w.bytesLeft != 0 { |
||||
return fmt.Errorf("missing %d bytes", w.bytesLeft) |
||||
} |
||||
name := utf16.Encode([]rune(hdr.Name)) |
||||
wsi := win32StreamId{ |
||||
StreamId: hdr.Id, |
||||
Attributes: hdr.Attributes, |
||||
Size: uint64(hdr.Size), |
||||
NameSize: uint32(len(name) * 2), |
||||
} |
||||
if hdr.Id == BackupSparseBlock { |
||||
// Include space for the int64 block offset
|
||||
wsi.Size += 8 |
||||
} |
||||
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil { |
||||
return err |
||||
} |
||||
if len(name) != 0 { |
||||
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
if hdr.Id == BackupSparseBlock { |
||||
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
w.bytesLeft = hdr.Size |
||||
return nil |
||||
} |
||||
|
||||
// Write writes to the current backup stream.
|
||||
func (w *BackupStreamWriter) Write(b []byte) (int, error) { |
||||
if w.bytesLeft < int64(len(b)) { |
||||
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft) |
||||
} |
||||
n, err := w.w.Write(b) |
||||
w.bytesLeft -= int64(n) |
||||
return n, err |
||||
} |
||||
|
||||
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||
type BackupFileReader struct { |
||||
f *os.File |
||||
includeSecurity bool |
||||
ctx uintptr |
||||
} |
||||
|
||||
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||
// Read will attempt to read the security descriptor of the file.
|
||||
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader { |
||||
r := &BackupFileReader{f, includeSecurity, 0} |
||||
return r |
||||
} |
||||
|
||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||
func (r *BackupFileReader) Read(b []byte) (int, error) { |
||||
var bytesRead uint32 |
||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) |
||||
if err != nil { |
||||
return 0, &os.PathError{"BackupRead", r.f.Name(), err} |
||||
} |
||||
runtime.KeepAlive(r.f) |
||||
if bytesRead == 0 { |
||||
return 0, io.EOF |
||||
} |
||||
return int(bytesRead), nil |
||||
} |
||||
|
||||
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||
// the underlying file.
|
||||
func (r *BackupFileReader) Close() error { |
||||
if r.ctx != 0 { |
||||
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) |
||||
runtime.KeepAlive(r.f) |
||||
r.ctx = 0 |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||
type BackupFileWriter struct { |
||||
f *os.File |
||||
includeSecurity bool |
||||
ctx uintptr |
||||
} |
||||
|
||||
// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||
// Write() will attempt to restore the security descriptor from the stream.
|
||||
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { |
||||
w := &BackupFileWriter{f, includeSecurity, 0} |
||||
return w |
||||
} |
||||
|
||||
// Write restores a portion of the file using the provided backup stream.
|
||||
func (w *BackupFileWriter) Write(b []byte) (int, error) { |
||||
var bytesWritten uint32 |
||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) |
||||
if err != nil { |
||||
return 0, &os.PathError{"BackupWrite", w.f.Name(), err} |
||||
} |
||||
runtime.KeepAlive(w.f) |
||||
if int(bytesWritten) != len(b) { |
||||
return int(bytesWritten), errors.New("not all bytes could be written") |
||||
} |
||||
return len(b), nil |
||||
} |
||||
|
||||
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||
// close the underlying file.
|
||||
func (w *BackupFileWriter) Close() error { |
||||
if w.ctx != 0 { |
||||
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) |
||||
runtime.KeepAlive(w.f) |
||||
w.ctx = 0 |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||
// or restore privileges have been acquired.
|
||||
//
|
||||
// If the file opened was a directory, it cannot be used with Readdir().
|
||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) { |
||||
winPath, err := syscall.UTF16FromString(path) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) |
||||
if err != nil { |
||||
err = &os.PathError{Op: "open", Path: path, Err: err} |
||||
return nil, err |
||||
} |
||||
return os.NewFile(uintptr(h), path), nil |
||||
} |
@ -0,0 +1,137 @@ |
||||
package winio |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/binary" |
||||
"errors" |
||||
) |
||||
|
||||
type fileFullEaInformation struct { |
||||
NextEntryOffset uint32 |
||||
Flags uint8 |
||||
NameLength uint8 |
||||
ValueLength uint16 |
||||
} |
||||
|
||||
var ( |
||||
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{}) |
||||
|
||||
errInvalidEaBuffer = errors.New("invalid extended attribute buffer") |
||||
errEaNameTooLarge = errors.New("extended attribute name too large") |
||||
errEaValueTooLarge = errors.New("extended attribute value too large") |
||||
) |
||||
|
||||
// ExtendedAttribute represents a single Windows EA.
|
||||
type ExtendedAttribute struct { |
||||
Name string |
||||
Value []byte |
||||
Flags uint8 |
||||
} |
||||
|
||||
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { |
||||
var info fileFullEaInformation |
||||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) |
||||
if err != nil { |
||||
err = errInvalidEaBuffer |
||||
return |
||||
} |
||||
|
||||
nameOffset := fileFullEaInformationSize |
||||
nameLen := int(info.NameLength) |
||||
valueOffset := nameOffset + int(info.NameLength) + 1 |
||||
valueLen := int(info.ValueLength) |
||||
nextOffset := int(info.NextEntryOffset) |
||||
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { |
||||
err = errInvalidEaBuffer |
||||
return |
||||
} |
||||
|
||||
ea.Name = string(b[nameOffset : nameOffset+nameLen]) |
||||
ea.Value = b[valueOffset : valueOffset+valueLen] |
||||
ea.Flags = info.Flags |
||||
if info.NextEntryOffset != 0 { |
||||
nb = b[info.NextEntryOffset:] |
||||
} |
||||
return |
||||
} |
||||
|
||||
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
||||
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { |
||||
for len(b) != 0 { |
||||
ea, nb, err := parseEa(b) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
eas = append(eas, ea) |
||||
b = nb |
||||
} |
||||
return |
||||
} |
||||
|
||||
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { |
||||
if int(uint8(len(ea.Name))) != len(ea.Name) { |
||||
return errEaNameTooLarge |
||||
} |
||||
if int(uint16(len(ea.Value))) != len(ea.Value) { |
||||
return errEaValueTooLarge |
||||
} |
||||
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value)) |
||||
withPadding := (entrySize + 3) &^ 3 |
||||
nextOffset := uint32(0) |
||||
if !last { |
||||
nextOffset = withPadding |
||||
} |
||||
info := fileFullEaInformation{ |
||||
NextEntryOffset: nextOffset, |
||||
Flags: ea.Flags, |
||||
NameLength: uint8(len(ea.Name)), |
||||
ValueLength: uint16(len(ea.Value)), |
||||
} |
||||
|
||||
err := binary.Write(buf, binary.LittleEndian, &info) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
_, err = buf.Write([]byte(ea.Name)) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
err = buf.WriteByte(0) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
_, err = buf.Write(ea.Value) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize]) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
||||
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
||||
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) { |
||||
var buf bytes.Buffer |
||||
for i := range eas { |
||||
last := false |
||||
if i == len(eas)-1 { |
||||
last = true |
||||
} |
||||
|
||||
err := writeEa(&buf, &eas[i], last) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
return buf.Bytes(), nil |
||||
} |
@ -0,0 +1,323 @@ |
||||
// +build windows
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"errors" |
||||
"io" |
||||
"runtime" |
||||
"sync" |
||||
"sync/atomic" |
||||
"syscall" |
||||
"time" |
||||
) |
||||
|
||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||
|
||||
type atomicBool int32 |
||||
|
||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } |
||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } |
||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } |
||||
func (b *atomicBool) swap(new bool) bool { |
||||
var newInt int32 |
||||
if new { |
||||
newInt = 1 |
||||
} |
||||
return atomic.SwapInt32((*int32)(b), newInt) == 1 |
||||
} |
||||
|
||||
const ( |
||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 |
||||
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 |
||||
) |
||||
|
||||
var ( |
||||
ErrFileClosed = errors.New("file has already been closed") |
||||
ErrTimeout = &timeoutError{} |
||||
) |
||||
|
||||
type timeoutError struct{} |
||||
|
||||
func (e *timeoutError) Error() string { return "i/o timeout" } |
||||
func (e *timeoutError) Timeout() bool { return true } |
||||
func (e *timeoutError) Temporary() bool { return true } |
||||
|
||||
type timeoutChan chan struct{} |
||||
|
||||
var ioInitOnce sync.Once |
||||
var ioCompletionPort syscall.Handle |
||||
|
||||
// ioResult contains the result of an asynchronous IO operation
|
||||
type ioResult struct { |
||||
bytes uint32 |
||||
err error |
||||
} |
||||
|
||||
// ioOperation represents an outstanding asynchronous Win32 IO
|
||||
type ioOperation struct { |
||||
o syscall.Overlapped |
||||
ch chan ioResult |
||||
} |
||||
|
||||
func initIo() { |
||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
ioCompletionPort = h |
||||
go ioCompletionProcessor(h) |
||||
} |
||||
|
||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||
type win32File struct { |
||||
handle syscall.Handle |
||||
wg sync.WaitGroup |
||||
wgLock sync.RWMutex |
||||
closing atomicBool |
||||
socket bool |
||||
readDeadline deadlineHandler |
||||
writeDeadline deadlineHandler |
||||
} |
||||
|
||||
type deadlineHandler struct { |
||||
setLock sync.Mutex |
||||
channel timeoutChan |
||||
channelLock sync.RWMutex |
||||
timer *time.Timer |
||||
timedout atomicBool |
||||
} |
||||
|
||||
// makeWin32File makes a new win32File from an existing file handle
|
||||
func makeWin32File(h syscall.Handle) (*win32File, error) { |
||||
f := &win32File{handle: h} |
||||
ioInitOnce.Do(initIo) |
||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
f.readDeadline.channel = make(timeoutChan) |
||||
f.writeDeadline.channel = make(timeoutChan) |
||||
return f, nil |
||||
} |
||||
|
||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { |
||||
// If we return the result of makeWin32File directly, it can result in an
|
||||
// interface-wrapped nil, rather than a nil interface value.
|
||||
f, err := makeWin32File(h) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return f, nil |
||||
} |
||||
|
||||
// closeHandle closes the resources associated with a Win32 handle
|
||||
func (f *win32File) closeHandle() { |
||||
f.wgLock.Lock() |
||||
// Atomically set that we are closing, releasing the resources only once.
|
||||
if !f.closing.swap(true) { |
||||
f.wgLock.Unlock() |
||||
// cancel all IO and wait for it to complete
|
||||
cancelIoEx(f.handle, nil) |
||||
f.wg.Wait() |
||||
// at this point, no new IO can start
|
||||
syscall.Close(f.handle) |
||||
f.handle = 0 |
||||
} else { |
||||
f.wgLock.Unlock() |
||||
} |
||||
} |
||||
|
||||
// Close closes a win32File.
|
||||
func (f *win32File) Close() error { |
||||
f.closeHandle() |
||||
return nil |
||||
} |
||||
|
||||
// prepareIo prepares for a new IO operation.
|
||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||
func (f *win32File) prepareIo() (*ioOperation, error) { |
||||
f.wgLock.RLock() |
||||
if f.closing.isSet() { |
||||
f.wgLock.RUnlock() |
||||
return nil, ErrFileClosed |
||||
} |
||||
f.wg.Add(1) |
||||
f.wgLock.RUnlock() |
||||
c := &ioOperation{} |
||||
c.ch = make(chan ioResult) |
||||
return c, nil |
||||
} |
||||
|
||||
// ioCompletionProcessor processes completed async IOs forever
|
||||
func ioCompletionProcessor(h syscall.Handle) { |
||||
for { |
||||
var bytes uint32 |
||||
var key uintptr |
||||
var op *ioOperation |
||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE) |
||||
if op == nil { |
||||
panic(err) |
||||
} |
||||
op.ch <- ioResult{bytes, err} |
||||
} |
||||
} |
||||
|
||||
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
||||
// the operation has actually completed.
|
||||
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { |
||||
if err != syscall.ERROR_IO_PENDING { |
||||
return int(bytes), err |
||||
} |
||||
|
||||
if f.closing.isSet() { |
||||
cancelIoEx(f.handle, &c.o) |
||||
} |
||||
|
||||
var timeout timeoutChan |
||||
if d != nil { |
||||
d.channelLock.Lock() |
||||
timeout = d.channel |
||||
d.channelLock.Unlock() |
||||
} |
||||
|
||||
var r ioResult |
||||
select { |
||||
case r = <-c.ch: |
||||
err = r.err |
||||
if err == syscall.ERROR_OPERATION_ABORTED { |
||||
if f.closing.isSet() { |
||||
err = ErrFileClosed |
||||
} |
||||
} else if err != nil && f.socket { |
||||
// err is from Win32. Query the overlapped structure to get the winsock error.
|
||||
var bytes, flags uint32 |
||||
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) |
||||
} |
||||
case <-timeout: |
||||
cancelIoEx(f.handle, &c.o) |
||||
r = <-c.ch |
||||
err = r.err |
||||
if err == syscall.ERROR_OPERATION_ABORTED { |
||||
err = ErrTimeout |
||||
} |
||||
} |
||||
|
||||
// runtime.KeepAlive is needed, as c is passed via native
|
||||
// code to ioCompletionProcessor, c must remain alive
|
||||
// until the channel read is complete.
|
||||
runtime.KeepAlive(c) |
||||
return int(r.bytes), err |
||||
} |
||||
|
||||
// Read reads from a file handle.
|
||||
func (f *win32File) Read(b []byte) (int, error) { |
||||
c, err := f.prepareIo() |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
defer f.wg.Done() |
||||
|
||||
if f.readDeadline.timedout.isSet() { |
||||
return 0, ErrTimeout |
||||
} |
||||
|
||||
var bytes uint32 |
||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o) |
||||
n, err := f.asyncIo(c, &f.readDeadline, bytes, err) |
||||
runtime.KeepAlive(b) |
||||
|
||||
// Handle EOF conditions.
|
||||
if err == nil && n == 0 && len(b) != 0 { |
||||
return 0, io.EOF |
||||
} else if err == syscall.ERROR_BROKEN_PIPE { |
||||
return 0, io.EOF |
||||
} else { |
||||
return n, err |
||||
} |
||||
} |
||||
|
||||
// Write writes to a file handle.
|
||||
func (f *win32File) Write(b []byte) (int, error) { |
||||
c, err := f.prepareIo() |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
defer f.wg.Done() |
||||
|
||||
if f.writeDeadline.timedout.isSet() { |
||||
return 0, ErrTimeout |
||||
} |
||||
|
||||
var bytes uint32 |
||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o) |
||||
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) |
||||
runtime.KeepAlive(b) |
||||
return n, err |
||||
} |
||||
|
||||
func (f *win32File) SetReadDeadline(deadline time.Time) error { |
||||
return f.readDeadline.set(deadline) |
||||
} |
||||
|
||||
func (f *win32File) SetWriteDeadline(deadline time.Time) error { |
||||
return f.writeDeadline.set(deadline) |
||||
} |
||||
|
||||
func (f *win32File) Flush() error { |
||||
return syscall.FlushFileBuffers(f.handle) |
||||
} |
||||
|
||||
func (f *win32File) Fd() uintptr { |
||||
return uintptr(f.handle) |
||||
} |
||||
|
||||
func (d *deadlineHandler) set(deadline time.Time) error { |
||||
d.setLock.Lock() |
||||
defer d.setLock.Unlock() |
||||
|
||||
if d.timer != nil { |
||||
if !d.timer.Stop() { |
||||
<-d.channel |
||||
} |
||||
d.timer = nil |
||||
} |
||||
d.timedout.setFalse() |
||||
|
||||
select { |
||||
case <-d.channel: |
||||
d.channelLock.Lock() |
||||
d.channel = make(chan struct{}) |
||||
d.channelLock.Unlock() |
||||
default: |
||||
} |
||||
|
||||
if deadline.IsZero() { |
||||
return nil |
||||
} |
||||
|
||||
timeoutIO := func() { |
||||
d.timedout.setTrue() |
||||
close(d.channel) |
||||
} |
||||
|
||||
now := time.Now() |
||||
duration := deadline.Sub(now) |
||||
if deadline.After(now) { |
||||
// Deadline is in the future, set a timer to wait
|
||||
d.timer = time.AfterFunc(duration, timeoutIO) |
||||
} else { |
||||
// Deadline is in the past. Cancel all pending IO now.
|
||||
timeoutIO() |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,73 @@ |
||||
// +build windows
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"os" |
||||
"runtime" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
// FileBasicInfo contains file access time and file attributes information.
|
||||
type FileBasicInfo struct { |
||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime |
||||
FileAttributes uint32 |
||||
pad uint32 // padding
|
||||
} |
||||
|
||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { |
||||
bi := &FileBasicInfo{} |
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { |
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} |
||||
} |
||||
runtime.KeepAlive(f) |
||||
return bi, nil |
||||
} |
||||
|
||||
// SetFileBasicInfo sets times and attributes for a file.
|
||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { |
||||
if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { |
||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} |
||||
} |
||||
runtime.KeepAlive(f) |
||||
return nil |
||||
} |
||||
|
||||
// FileStandardInfo contains extended information for the file.
|
||||
// FILE_STANDARD_INFO in WinBase.h
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
|
||||
type FileStandardInfo struct { |
||||
AllocationSize, EndOfFile int64 |
||||
NumberOfLinks uint32 |
||||
DeletePending, Directory bool |
||||
} |
||||
|
||||
// GetFileStandardInfo retrieves ended information for the file.
|
||||
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { |
||||
si := &FileStandardInfo{} |
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil { |
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} |
||||
} |
||||
runtime.KeepAlive(f) |
||||
return si, nil |
||||
} |
||||
|
||||
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||
// unique on a system.
|
||||
type FileIDInfo struct { |
||||
VolumeSerialNumber uint64 |
||||
FileID [16]byte |
||||
} |
||||
|
||||
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||
func GetFileID(f *os.File) (*FileIDInfo, error) { |
||||
fileID := &FileIDInfo{} |
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { |
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} |
||||
} |
||||
runtime.KeepAlive(f) |
||||
return fileID, nil |
||||
} |
@ -0,0 +1,9 @@ |
||||
module github.com/Microsoft/go-winio |
||||
|
||||
go 1.12 |
||||
|
||||
require ( |
||||
github.com/pkg/errors v0.9.1 |
||||
github.com/sirupsen/logrus v1.7.0 |
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c |
||||
) |
@ -0,0 +1,14 @@ |
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= |
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= |
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= |
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= |
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= |
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= |
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
@ -0,0 +1,307 @@ |
||||
// +build windows
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io" |
||||
"net" |
||||
"os" |
||||
"syscall" |
||||
"time" |
||||
"unsafe" |
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid" |
||||
) |
||||
|
||||
//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
|
||||
|
||||
const ( |
||||
afHvSock = 34 // AF_HYPERV
|
||||
|
||||
socketError = ^uintptr(0) |
||||
) |
||||
|
||||
// An HvsockAddr is an address for a AF_HYPERV socket.
|
||||
type HvsockAddr struct { |
||||
VMID guid.GUID |
||||
ServiceID guid.GUID |
||||
} |
||||
|
||||
type rawHvsockAddr struct { |
||||
Family uint16 |
||||
_ uint16 |
||||
VMID guid.GUID |
||||
ServiceID guid.GUID |
||||
} |
||||
|
||||
// Network returns the address's network name, "hvsock".
|
||||
func (addr *HvsockAddr) Network() string { |
||||
return "hvsock" |
||||
} |
||||
|
||||
func (addr *HvsockAddr) String() string { |
||||
return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID) |
||||
} |
||||
|
||||
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
|
||||
func VsockServiceID(port uint32) guid.GUID { |
||||
g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3") |
||||
g.Data1 = port |
||||
return g |
||||
} |
||||
|
||||
func (addr *HvsockAddr) raw() rawHvsockAddr { |
||||
return rawHvsockAddr{ |
||||
Family: afHvSock, |
||||
VMID: addr.VMID, |
||||
ServiceID: addr.ServiceID, |
||||
} |
||||
} |
||||
|
||||
func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) { |
||||
addr.VMID = raw.VMID |
||||
addr.ServiceID = raw.ServiceID |
||||
} |
||||
|
||||
// HvsockListener is a socket listener for the AF_HYPERV address family.
|
||||
type HvsockListener struct { |
||||
sock *win32File |
||||
addr HvsockAddr |
||||
} |
||||
|
||||
// HvsockConn is a connected socket of the AF_HYPERV address family.
|
||||
type HvsockConn struct { |
||||
sock *win32File |
||||
local, remote HvsockAddr |
||||
} |
||||
|
||||
func newHvSocket() (*win32File, error) { |
||||
fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1) |
||||
if err != nil { |
||||
return nil, os.NewSyscallError("socket", err) |
||||
} |
||||
f, err := makeWin32File(fd) |
||||
if err != nil { |
||||
syscall.Close(fd) |
||||
return nil, err |
||||
} |
||||
f.socket = true |
||||
return f, nil |
||||
} |
||||
|
||||
// ListenHvsock listens for connections on the specified hvsock address.
|
||||
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) { |
||||
l := &HvsockListener{addr: *addr} |
||||
sock, err := newHvSocket() |
||||
if err != nil { |
||||
return nil, l.opErr("listen", err) |
||||
} |
||||
sa := addr.raw() |
||||
err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa))) |
||||
if err != nil { |
||||
return nil, l.opErr("listen", os.NewSyscallError("socket", err)) |
||||
} |
||||
err = syscall.Listen(sock.handle, 16) |
||||
if err != nil { |
||||
return nil, l.opErr("listen", os.NewSyscallError("listen", err)) |
||||
} |
||||
return &HvsockListener{sock: sock, addr: *addr}, nil |
||||
} |
||||
|
||||
func (l *HvsockListener) opErr(op string, err error) error { |
||||
return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err} |
||||
} |
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *HvsockListener) Addr() net.Addr { |
||||
return &l.addr |
||||
} |
||||
|
||||
// Accept waits for the next connection and returns it.
|
||||
func (l *HvsockListener) Accept() (_ net.Conn, err error) { |
||||
sock, err := newHvSocket() |
||||
if err != nil { |
||||
return nil, l.opErr("accept", err) |
||||
} |
||||
defer func() { |
||||
if sock != nil { |
||||
sock.Close() |
||||
} |
||||
}() |
||||
c, err := l.sock.prepareIo() |
||||
if err != nil { |
||||
return nil, l.opErr("accept", err) |
||||
} |
||||
defer l.sock.wg.Done() |
||||
|
||||
// AcceptEx, per documentation, requires an extra 16 bytes per address.
|
||||
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{})) |
||||
var addrbuf [addrlen * 2]byte |
||||
|
||||
var bytes uint32 |
||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o) |
||||
_, err = l.sock.asyncIo(c, nil, bytes, err) |
||||
if err != nil { |
||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err)) |
||||
} |
||||
conn := &HvsockConn{ |
||||
sock: sock, |
||||
} |
||||
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0]))) |
||||
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen]))) |
||||
sock = nil |
||||
return conn, nil |
||||
} |
||||
|
||||
// Close closes the listener, causing any pending Accept calls to fail.
|
||||
func (l *HvsockListener) Close() error { |
||||
return l.sock.Close() |
||||
} |
||||
|
||||
/* Need to finish ConnectEx handling |
||||
func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) { |
||||
sock, err := newHvSocket() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer func() { |
||||
if sock != nil { |
||||
sock.Close() |
||||
} |
||||
}() |
||||
c, err := sock.prepareIo() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer sock.wg.Done() |
||||
var bytes uint32 |
||||
err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o) |
||||
_, err = sock.asyncIo(ctx, c, nil, bytes, err) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
conn := &HvsockConn{ |
||||
sock: sock, |
||||
remote: *addr, |
||||
} |
||||
sock = nil |
||||
return conn, nil |
||||
} |
||||
*/ |
||||
|
||||
func (conn *HvsockConn) opErr(op string, err error) error { |
||||
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err} |
||||
} |
||||
|
||||
func (conn *HvsockConn) Read(b []byte) (int, error) { |
||||
c, err := conn.sock.prepareIo() |
||||
if err != nil { |
||||
return 0, conn.opErr("read", err) |
||||
} |
||||
defer conn.sock.wg.Done() |
||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} |
||||
var flags, bytes uint32 |
||||
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil) |
||||
n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err) |
||||
if err != nil { |
||||
if _, ok := err.(syscall.Errno); ok { |
||||
err = os.NewSyscallError("wsarecv", err) |
||||
} |
||||
return 0, conn.opErr("read", err) |
||||
} else if n == 0 { |
||||
err = io.EOF |
||||
} |
||||
return n, err |
||||
} |
||||
|
||||
func (conn *HvsockConn) Write(b []byte) (int, error) { |
||||
t := 0 |
||||
for len(b) != 0 { |
||||
n, err := conn.write(b) |
||||
if err != nil { |
||||
return t + n, err |
||||
} |
||||
t += n |
||||
b = b[n:] |
||||
} |
||||
return t, nil |
||||
} |
||||
|
||||
func (conn *HvsockConn) write(b []byte) (int, error) { |
||||
c, err := conn.sock.prepareIo() |
||||
if err != nil { |
||||
return 0, conn.opErr("write", err) |
||||
} |
||||
defer conn.sock.wg.Done() |
||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} |
||||
var bytes uint32 |
||||
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil) |
||||
n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err) |
||||
if err != nil { |
||||
if _, ok := err.(syscall.Errno); ok { |
||||
err = os.NewSyscallError("wsasend", err) |
||||
} |
||||
return 0, conn.opErr("write", err) |
||||
} |
||||
return n, err |
||||
} |
||||
|
||||
// Close closes the socket connection, failing any pending read or write calls.
|
||||
func (conn *HvsockConn) Close() error { |
||||
return conn.sock.Close() |
||||
} |
||||
|
||||
func (conn *HvsockConn) shutdown(how int) error { |
||||
err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD) |
||||
if err != nil { |
||||
return os.NewSyscallError("shutdown", err) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// CloseRead shuts down the read end of the socket.
|
||||
func (conn *HvsockConn) CloseRead() error { |
||||
err := conn.shutdown(syscall.SHUT_RD) |
||||
if err != nil { |
||||
return conn.opErr("close", err) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
|
||||
// no more data will be written.
|
||||
func (conn *HvsockConn) CloseWrite() error { |
||||
err := conn.shutdown(syscall.SHUT_WR) |
||||
if err != nil { |
||||
return conn.opErr("close", err) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// LocalAddr returns the local address of the connection.
|
||||
func (conn *HvsockConn) LocalAddr() net.Addr { |
||||
return &conn.local |
||||
} |
||||
|
||||
// RemoteAddr returns the remote address of the connection.
|
||||
func (conn *HvsockConn) RemoteAddr() net.Addr { |
||||
return &conn.remote |
||||
} |
||||
|
||||
// SetDeadline implements the net.Conn SetDeadline method.
|
||||
func (conn *HvsockConn) SetDeadline(t time.Time) error { |
||||
conn.SetReadDeadline(t) |
||||
conn.SetWriteDeadline(t) |
||||
return nil |
||||
} |
||||
|
||||
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
||||
func (conn *HvsockConn) SetReadDeadline(t time.Time) error { |
||||
return conn.sock.SetReadDeadline(t) |
||||
} |
||||
|
||||
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
||||
func (conn *HvsockConn) SetWriteDeadline(t time.Time) error { |
||||
return conn.sock.SetWriteDeadline(t) |
||||
} |
@ -0,0 +1,517 @@ |
||||
// +build windows
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"net" |
||||
"os" |
||||
"runtime" |
||||
"syscall" |
||||
"time" |
||||
"unsafe" |
||||
) |
||||
|
||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
||||
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
|
||||
//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
|
||||
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
|
||||
|
||||
type ioStatusBlock struct { |
||||
Status, Information uintptr |
||||
} |
||||
|
||||
type objectAttributes struct { |
||||
Length uintptr |
||||
RootDirectory uintptr |
||||
ObjectName *unicodeString |
||||
Attributes uintptr |
||||
SecurityDescriptor *securityDescriptor |
||||
SecurityQoS uintptr |
||||
} |
||||
|
||||
type unicodeString struct { |
||||
Length uint16 |
||||
MaximumLength uint16 |
||||
Buffer uintptr |
||||
} |
||||
|
||||
type securityDescriptor struct { |
||||
Revision byte |
||||
Sbz1 byte |
||||
Control uint16 |
||||
Owner uintptr |
||||
Group uintptr |
||||
Sacl uintptr |
||||
Dacl uintptr |
||||
} |
||||
|
||||
type ntstatus int32 |
||||
|
||||
func (status ntstatus) Err() error { |
||||
if status >= 0 { |
||||
return nil |
||||
} |
||||
return rtlNtStatusToDosError(status) |
||||
} |
||||
|
||||
const ( |
||||
cERROR_PIPE_BUSY = syscall.Errno(231) |
||||
cERROR_NO_DATA = syscall.Errno(232) |
||||
cERROR_PIPE_CONNECTED = syscall.Errno(535) |
||||
cERROR_SEM_TIMEOUT = syscall.Errno(121) |
||||
|
||||
cSECURITY_SQOS_PRESENT = 0x100000 |
||||
cSECURITY_ANONYMOUS = 0 |
||||
|
||||
cPIPE_TYPE_MESSAGE = 4 |
||||
|
||||
cPIPE_READMODE_MESSAGE = 2 |
||||
|
||||
cFILE_OPEN = 1 |
||||
cFILE_CREATE = 2 |
||||
|
||||
cFILE_PIPE_MESSAGE_TYPE = 1 |
||||
cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2 |
||||
|
||||
cSE_DACL_PRESENT = 4 |
||||
) |
||||
|
||||
var ( |
||||
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||
// This error should match net.errClosing since docker takes a dependency on its text.
|
||||
ErrPipeListenerClosed = errors.New("use of closed network connection") |
||||
|
||||
errPipeWriteClosed = errors.New("pipe has been closed for write") |
||||
) |
||||
|
||||
type win32Pipe struct { |
||||
*win32File |
||||
path string |
||||
} |
||||
|
||||
type win32MessageBytePipe struct { |
||||
win32Pipe |
||||
writeClosed bool |
||||
readEOF bool |
||||
} |
||||
|
||||
type pipeAddress string |
||||
|
||||
func (f *win32Pipe) LocalAddr() net.Addr { |
||||
return pipeAddress(f.path) |
||||
} |
||||
|
||||
func (f *win32Pipe) RemoteAddr() net.Addr { |
||||
return pipeAddress(f.path) |
||||
} |
||||
|
||||
func (f *win32Pipe) SetDeadline(t time.Time) error { |
||||
f.SetReadDeadline(t) |
||||
f.SetWriteDeadline(t) |
||||
return nil |
||||
} |
||||
|
||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||
func (f *win32MessageBytePipe) CloseWrite() error { |
||||
if f.writeClosed { |
||||
return errPipeWriteClosed |
||||
} |
||||
err := f.win32File.Flush() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
_, err = f.win32File.Write(nil) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
f.writeClosed = true |
||||
return nil |
||||
} |
||||
|
||||
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
||||
// they are used to implement CloseWrite().
|
||||
func (f *win32MessageBytePipe) Write(b []byte) (int, error) { |
||||
if f.writeClosed { |
||||
return 0, errPipeWriteClosed |
||||
} |
||||
if len(b) == 0 { |
||||
return 0, nil |
||||
} |
||||
return f.win32File.Write(b) |
||||
} |
||||
|
||||
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
||||
// mode pipe will return io.EOF, as will all subsequent reads.
|
||||
func (f *win32MessageBytePipe) Read(b []byte) (int, error) { |
||||
if f.readEOF { |
||||
return 0, io.EOF |
||||
} |
||||
n, err := f.win32File.Read(b) |
||||
if err == io.EOF { |
||||
// If this was the result of a zero-byte read, then
|
||||
// it is possible that the read was due to a zero-size
|
||||
// message. Since we are simulating CloseWrite with a
|
||||
// zero-byte message, ensure that all future Read() calls
|
||||
// also return EOF.
|
||||
f.readEOF = true |
||||
} else if err == syscall.ERROR_MORE_DATA { |
||||
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||
// and the message still has more bytes. Treat this as a success, since
|
||||
// this package presents all named pipes as byte streams.
|
||||
err = nil |
||||
} |
||||
return n, err |
||||
} |
||||
|
||||
func (s pipeAddress) Network() string { |
||||
return "pipe" |
||||
} |
||||
|
||||
func (s pipeAddress) String() string { |
||||
return string(s) |
||||
} |
||||
|
||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
||||
func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) { |
||||
for { |
||||
|
||||
select { |
||||
case <-ctx.Done(): |
||||
return syscall.Handle(0), ctx.Err() |
||||
default: |
||||
h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) |
||||
if err == nil { |
||||
return h, nil |
||||
} |
||||
if err != cERROR_PIPE_BUSY { |
||||
return h, &os.PathError{Err: err, Op: "open", Path: *path} |
||||
} |
||||
// Wait 10 msec and try again. This is a rather simplistic
|
||||
// view, as we always try each 10 milliseconds.
|
||||
time.Sleep(10 * time.Millisecond) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// DialPipe connects to a named pipe by path, timing out if the connection
|
||||
// takes longer than the specified duration. If timeout is nil, then we use
|
||||
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
|
||||
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { |
||||
var absTimeout time.Time |
||||
if timeout != nil { |
||||
absTimeout = time.Now().Add(*timeout) |
||||
} else { |
||||
absTimeout = time.Now().Add(2 * time.Second) |
||||
} |
||||
ctx, _ := context.WithDeadline(context.Background(), absTimeout) |
||||
conn, err := DialPipeContext(ctx, path) |
||||
if err == context.DeadlineExceeded { |
||||
return nil, ErrTimeout |
||||
} |
||||
return conn, err |
||||
} |
||||
|
||||
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
||||
// cancellation or timeout.
|
||||
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) { |
||||
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE) |
||||
} |
||||
|
||||
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
||||
// cancellation or timeout.
|
||||
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) { |
||||
var err error |
||||
var h syscall.Handle |
||||
h, err = tryDialPipe(ctx, &path, access) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
var flags uint32 |
||||
err = getNamedPipeInfo(h, &flags, nil, nil, nil) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
f, err := makeWin32File(h) |
||||
if err != nil { |
||||
syscall.Close(h) |
||||
return nil, err |
||||
} |
||||
|
||||
// If the pipe is in message mode, return a message byte pipe, which
|
||||
// supports CloseWrite().
|
||||
if flags&cPIPE_TYPE_MESSAGE != 0 { |
||||
return &win32MessageBytePipe{ |
||||
win32Pipe: win32Pipe{win32File: f, path: path}, |
||||
}, nil |
||||
} |
||||
return &win32Pipe{win32File: f, path: path}, nil |
||||
} |
||||
|
||||
type acceptResponse struct { |
||||
f *win32File |
||||
err error |
||||
} |
||||
|
||||
type win32PipeListener struct { |
||||
firstHandle syscall.Handle |
||||
path string |
||||
config PipeConfig |
||||
acceptCh chan (chan acceptResponse) |
||||
closeCh chan int |
||||
doneCh chan int |
||||
} |
||||
|
||||
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) { |
||||
path16, err := syscall.UTF16FromString(path) |
||||
if err != nil { |
||||
return 0, &os.PathError{Op: "open", Path: path, Err: err} |
||||
} |
||||
|
||||
var oa objectAttributes |
||||
oa.Length = unsafe.Sizeof(oa) |
||||
|
||||
var ntPath unicodeString |
||||
if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil { |
||||
return 0, &os.PathError{Op: "open", Path: path, Err: err} |
||||
} |
||||
defer localFree(ntPath.Buffer) |
||||
oa.ObjectName = &ntPath |
||||
|
||||
// The security descriptor is only needed for the first pipe.
|
||||
if first { |
||||
if sd != nil { |
||||
len := uint32(len(sd)) |
||||
sdb := localAlloc(0, len) |
||||
defer localFree(sdb) |
||||
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) |
||||
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) |
||||
} else { |
||||
// Construct the default named pipe security descriptor.
|
||||
var dacl uintptr |
||||
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { |
||||
return 0, fmt.Errorf("getting default named pipe ACL: %s", err) |
||||
} |
||||
defer localFree(dacl) |
||||
|
||||
sdb := &securityDescriptor{ |
||||
Revision: 1, |
||||
Control: cSE_DACL_PRESENT, |
||||
Dacl: dacl, |
||||
} |
||||
oa.SecurityDescriptor = sdb |
||||
} |
||||
} |
||||
|
||||
typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS) |
||||
if c.MessageMode { |
||||
typ |= cFILE_PIPE_MESSAGE_TYPE |
||||
} |
||||
|
||||
disposition := uint32(cFILE_OPEN) |
||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE) |
||||
if first { |
||||
disposition = cFILE_CREATE |
||||
// By not asking for read or write access, the named pipe file system
|
||||
// will put this pipe into an initially disconnected state, blocking
|
||||
// client connections until the next call with first == false.
|
||||
access = syscall.SYNCHRONIZE |
||||
} |
||||
|
||||
timeout := int64(-50 * 10000) // 50ms
|
||||
|
||||
var ( |
||||
h syscall.Handle |
||||
iosb ioStatusBlock |
||||
) |
||||
err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err() |
||||
if err != nil { |
||||
return 0, &os.PathError{Op: "open", Path: path, Err: err} |
||||
} |
||||
|
||||
runtime.KeepAlive(ntPath) |
||||
return h, nil |
||||
} |
||||
|
||||
func (l *win32PipeListener) makeServerPipe() (*win32File, error) { |
||||
h, err := makeServerPipeHandle(l.path, nil, &l.config, false) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
f, err := makeWin32File(h) |
||||
if err != nil { |
||||
syscall.Close(h) |
||||
return nil, err |
||||
} |
||||
return f, nil |
||||
} |
||||
|
||||
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { |
||||
p, err := l.makeServerPipe() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
// Wait for the client to connect.
|
||||
ch := make(chan error) |
||||
go func(p *win32File) { |
||||
ch <- connectPipe(p) |
||||
}(p) |
||||
|
||||
select { |
||||
case err = <-ch: |
||||
if err != nil { |
||||
p.Close() |
||||
p = nil |
||||
} |
||||
case <-l.closeCh: |
||||
// Abort the connect request by closing the handle.
|
||||
p.Close() |
||||
p = nil |
||||
err = <-ch |
||||
if err == nil || err == ErrFileClosed { |
||||
err = ErrPipeListenerClosed |
||||
} |
||||
} |
||||
return p, err |
||||
} |
||||
|
||||
func (l *win32PipeListener) listenerRoutine() { |
||||
closed := false |
||||
for !closed { |
||||
select { |
||||
case <-l.closeCh: |
||||
closed = true |
||||
case responseCh := <-l.acceptCh: |
||||
var ( |
||||
p *win32File |
||||
err error |
||||
) |
||||
for { |
||||
p, err = l.makeConnectedServerPipe() |
||||
// If the connection was immediately closed by the client, try
|
||||
// again.
|
||||
if err != cERROR_NO_DATA { |
||||
break |
||||
} |
||||
} |
||||
responseCh <- acceptResponse{p, err} |
||||
closed = err == ErrPipeListenerClosed |
||||
} |
||||
} |
||||
syscall.Close(l.firstHandle) |
||||
l.firstHandle = 0 |
||||
// Notify Close() and Accept() callers that the handle has been closed.
|
||||
close(l.doneCh) |
||||
} |
||||
|
||||
// PipeConfig contain configuration for the pipe listener.
|
||||
type PipeConfig struct { |
||||
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
||||
SecurityDescriptor string |
||||
|
||||
// MessageMode determines whether the pipe is in byte or message mode. In either
|
||||
// case the pipe is read in byte mode by default. The only practical difference in
|
||||
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
||||
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
||||
// transferred to the reader (and returned as io.EOF in this implementation)
|
||||
// when the pipe is in message mode.
|
||||
MessageMode bool |
||||
|
||||
// InputBufferSize specifies the size of the input buffer, in bytes.
|
||||
InputBufferSize int32 |
||||
|
||||
// OutputBufferSize specifies the size of the output buffer, in bytes.
|
||||
OutputBufferSize int32 |
||||
} |
||||
|
||||
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
||||
// The pipe must not already exist.
|
||||
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { |
||||
var ( |
||||
sd []byte |
||||
err error |
||||
) |
||||
if c == nil { |
||||
c = &PipeConfig{} |
||||
} |
||||
if c.SecurityDescriptor != "" { |
||||
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
h, err := makeServerPipeHandle(path, sd, c, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
l := &win32PipeListener{ |
||||
firstHandle: h, |
||||
path: path, |
||||
config: *c, |
||||
acceptCh: make(chan (chan acceptResponse)), |
||||
closeCh: make(chan int), |
||||
doneCh: make(chan int), |
||||
} |
||||
go l.listenerRoutine() |
||||
return l, nil |
||||
} |
||||
|
||||
func connectPipe(p *win32File) error { |
||||
c, err := p.prepareIo() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer p.wg.Done() |
||||
|
||||
err = connectNamedPipe(p.handle, &c.o) |
||||
_, err = p.asyncIo(c, nil, 0, err) |
||||
if err != nil && err != cERROR_PIPE_CONNECTED { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (l *win32PipeListener) Accept() (net.Conn, error) { |
||||
ch := make(chan acceptResponse) |
||||
select { |
||||
case l.acceptCh <- ch: |
||||
response := <-ch |
||||
err := response.err |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if l.config.MessageMode { |
||||
return &win32MessageBytePipe{ |
||||
win32Pipe: win32Pipe{win32File: response.f, path: l.path}, |
||||
}, nil |
||||
} |
||||
return &win32Pipe{win32File: response.f, path: l.path}, nil |
||||
case <-l.doneCh: |
||||
return nil, ErrPipeListenerClosed |
||||
} |
||||
} |
||||
|
||||
func (l *win32PipeListener) Close() error { |
||||
select { |
||||
case l.closeCh <- 1: |
||||
<-l.doneCh |
||||
case <-l.doneCh: |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (l *win32PipeListener) Addr() net.Addr { |
||||
return pipeAddress(l.path) |
||||
} |
@ -0,0 +1,237 @@ |
||||
// +build windows
|
||||
|
||||
// Package guid provides a GUID type. The backing structure for a GUID is
|
||||
// identical to that used by the golang.org/x/sys/windows GUID type.
|
||||
// There are two main binary encodings used for a GUID, the big-endian encoding,
|
||||
// and the Windows (mixed-endian) encoding. See here for details:
|
||||
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
|
||||
package guid |
||||
|
||||
import ( |
||||
"crypto/rand" |
||||
"crypto/sha1" |
||||
"encoding" |
||||
"encoding/binary" |
||||
"fmt" |
||||
"strconv" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
// Variant specifies which GUID variant (or "type") of the GUID. It determines
|
||||
// how the entirety of the rest of the GUID is interpreted.
|
||||
type Variant uint8 |
||||
|
||||
// The variants specified by RFC 4122.
|
||||
const ( |
||||
// VariantUnknown specifies a GUID variant which does not conform to one of
|
||||
// the variant encodings specified in RFC 4122.
|
||||
VariantUnknown Variant = iota |
||||
VariantNCS |
||||
VariantRFC4122 |
||||
VariantMicrosoft |
||||
VariantFuture |
||||
) |
||||
|
||||
// Version specifies how the bits in the GUID were generated. For instance, a
|
||||
// version 4 GUID is randomly generated, and a version 5 is generated from the
|
||||
// hash of an input string.
|
||||
type Version uint8 |
||||
|
||||
var _ = (encoding.TextMarshaler)(GUID{}) |
||||
var _ = (encoding.TextUnmarshaler)(&GUID{}) |
||||
|
||||
// GUID represents a GUID/UUID. It has the same structure as
|
||||
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
|
||||
// that type. It is defined as its own type so that stringification and
|
||||
// marshaling can be supported. The representation matches that used by native
|
||||
// Windows code.
|
||||
type GUID windows.GUID |
||||
|
||||
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
|
||||
func NewV4() (GUID, error) { |
||||
var b [16]byte |
||||
if _, err := rand.Read(b[:]); err != nil { |
||||
return GUID{}, err |
||||
} |
||||
|
||||
g := FromArray(b) |
||||
g.setVersion(4) // Version 4 means randomly generated.
|
||||
g.setVariant(VariantRFC4122) |
||||
|
||||
return g, nil |
||||
} |
||||
|
||||
// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
|
||||
// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
|
||||
// and the sample code treats it as a series of bytes, so we do the same here.
|
||||
//
|
||||
// Some implementations, such as those found on Windows, treat the name as a
|
||||
// big-endian UTF16 stream of bytes. If that is desired, the string can be
|
||||
// encoded as such before being passed to this function.
|
||||
func NewV5(namespace GUID, name []byte) (GUID, error) { |
||||
b := sha1.New() |
||||
namespaceBytes := namespace.ToArray() |
||||
b.Write(namespaceBytes[:]) |
||||
b.Write(name) |
||||
|
||||
a := [16]byte{} |
||||
copy(a[:], b.Sum(nil)) |
||||
|
||||
g := FromArray(a) |
||||
g.setVersion(5) // Version 5 means generated from a string.
|
||||
g.setVariant(VariantRFC4122) |
||||
|
||||
return g, nil |
||||
} |
||||
|
||||
func fromArray(b [16]byte, order binary.ByteOrder) GUID { |
||||
var g GUID |
||||
g.Data1 = order.Uint32(b[0:4]) |
||||
g.Data2 = order.Uint16(b[4:6]) |
||||
g.Data3 = order.Uint16(b[6:8]) |
||||
copy(g.Data4[:], b[8:16]) |
||||
return g |
||||
} |
||||
|
||||
func (g GUID) toArray(order binary.ByteOrder) [16]byte { |
||||
b := [16]byte{} |
||||
order.PutUint32(b[0:4], g.Data1) |
||||
order.PutUint16(b[4:6], g.Data2) |
||||
order.PutUint16(b[6:8], g.Data3) |
||||
copy(b[8:16], g.Data4[:]) |
||||
return b |
||||
} |
||||
|
||||
// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
|
||||
func FromArray(b [16]byte) GUID { |
||||
return fromArray(b, binary.BigEndian) |
||||
} |
||||
|
||||
// ToArray returns an array of 16 bytes representing the GUID in big-endian
|
||||
// encoding.
|
||||
func (g GUID) ToArray() [16]byte { |
||||
return g.toArray(binary.BigEndian) |
||||
} |
||||
|
||||
// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
|
||||
func FromWindowsArray(b [16]byte) GUID { |
||||
return fromArray(b, binary.LittleEndian) |
||||
} |
||||
|
||||
// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
|
||||
// encoding.
|
||||
func (g GUID) ToWindowsArray() [16]byte { |
||||
return g.toArray(binary.LittleEndian) |
||||
} |
||||
|
||||
func (g GUID) String() string { |
||||
return fmt.Sprintf( |
||||
"%08x-%04x-%04x-%04x-%012x", |
||||
g.Data1, |
||||
g.Data2, |
||||
g.Data3, |
||||
g.Data4[:2], |
||||
g.Data4[2:]) |
||||
} |
||||
|
||||
// FromString parses a string containing a GUID and returns the GUID. The only
|
||||
// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
||||
// format.
|
||||
func FromString(s string) (GUID, error) { |
||||
if len(s) != 36 { |
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s) |
||||
} |
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { |
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s) |
||||
} |
||||
|
||||
var g GUID |
||||
|
||||
data1, err := strconv.ParseUint(s[0:8], 16, 32) |
||||
if err != nil { |
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s) |
||||
} |
||||
g.Data1 = uint32(data1) |
||||
|
||||
data2, err := strconv.ParseUint(s[9:13], 16, 16) |
||||
if err != nil { |
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s) |
||||
} |
||||
g.Data2 = uint16(data2) |
||||
|
||||
data3, err := strconv.ParseUint(s[14:18], 16, 16) |
||||
if err != nil { |
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s) |
||||
} |
||||
g.Data3 = uint16(data3) |
||||
|
||||
for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} { |
||||
v, err := strconv.ParseUint(s[x:x+2], 16, 8) |
||||
if err != nil { |
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s) |
||||
} |
||||
g.Data4[i] = uint8(v) |
||||
} |
||||
|
||||
return g, nil |
||||
} |
||||
|
||||
func (g *GUID) setVariant(v Variant) { |
||||
d := g.Data4[0] |
||||
switch v { |
||||
case VariantNCS: |
||||
d = (d & 0x7f) |
||||
case VariantRFC4122: |
||||
d = (d & 0x3f) | 0x80 |
||||
case VariantMicrosoft: |
||||
d = (d & 0x1f) | 0xc0 |
||||
case VariantFuture: |
||||
d = (d & 0x0f) | 0xe0 |
||||
case VariantUnknown: |
||||
fallthrough |
||||
default: |
||||
panic(fmt.Sprintf("invalid variant: %d", v)) |
||||
} |
||||
g.Data4[0] = d |
||||
} |
||||
|
||||
// Variant returns the GUID variant, as defined in RFC 4122.
|
||||
func (g GUID) Variant() Variant { |
||||
b := g.Data4[0] |
||||
if b&0x80 == 0 { |
||||
return VariantNCS |
||||
} else if b&0xc0 == 0x80 { |
||||
return VariantRFC4122 |
||||
} else if b&0xe0 == 0xc0 { |
||||
return VariantMicrosoft |
||||
} else if b&0xe0 == 0xe0 { |
||||
return VariantFuture |
||||
} |
||||
return VariantUnknown |
||||
} |
||||
|
||||
func (g *GUID) setVersion(v Version) { |
||||
g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12) |
||||
} |
||||
|
||||
// Version returns the GUID version, as defined in RFC 4122.
|
||||
func (g GUID) Version() Version { |
||||
return Version((g.Data3 & 0xF000) >> 12) |
||||
} |
||||
|
||||
// MarshalText returns the textual representation of the GUID.
|
||||
func (g GUID) MarshalText() ([]byte, error) { |
||||
return []byte(g.String()), nil |
||||
} |
||||
|
||||
// UnmarshalText takes the textual representation of a GUID, and unmarhals it
|
||||
// into this GUID.
|
||||
func (g *GUID) UnmarshalText(text []byte) error { |
||||
g2, err := FromString(string(text)) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
*g = g2 |
||||
return nil |
||||
} |
161
docgen/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go
generated
vendored
161
docgen/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go
generated
vendored
@ -0,0 +1,161 @@ |
||||
// +build windows
|
||||
|
||||
package security |
||||
|
||||
import ( |
||||
"os" |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"github.com/pkg/errors" |
||||
) |
||||
|
||||
type ( |
||||
accessMask uint32 |
||||
accessMode uint32 |
||||
desiredAccess uint32 |
||||
inheritMode uint32 |
||||
objectType uint32 |
||||
shareMode uint32 |
||||
securityInformation uint32 |
||||
trusteeForm uint32 |
||||
trusteeType uint32 |
||||
|
||||
explicitAccess struct { |
||||
accessPermissions accessMask |
||||
accessMode accessMode |
||||
inheritance inheritMode |
||||
trustee trustee |
||||
} |
||||
|
||||
trustee struct { |
||||
multipleTrustee *trustee |
||||
multipleTrusteeOperation int32 |
||||
trusteeForm trusteeForm |
||||
trusteeType trusteeType |
||||
name uintptr |
||||
} |
||||
) |
||||
|
||||
const ( |
||||
accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ
|
||||
|
||||
accessModeGrant accessMode = 1 |
||||
|
||||
desiredAccessReadControl desiredAccess = 0x20000 |
||||
desiredAccessWriteDac desiredAccess = 0x40000 |
||||
|
||||
gvmga = "GrantVmGroupAccess:" |
||||
|
||||
inheritModeNoInheritance inheritMode = 0x0 |
||||
inheritModeSubContainersAndObjectsInherit inheritMode = 0x3 |
||||
|
||||
objectTypeFileObject objectType = 0x1 |
||||
|
||||
securityInformationDACL securityInformation = 0x4 |
||||
|
||||
shareModeRead shareMode = 0x1 |
||||
shareModeWrite shareMode = 0x2 |
||||
|
||||
sidVmGroup = "S-1-5-83-0" |
||||
|
||||
trusteeFormIsSid trusteeForm = 0 |
||||
|
||||
trusteeTypeWellKnownGroup trusteeType = 5 |
||||
) |
||||
|
||||
// GrantVMGroupAccess sets the DACL for a specified file or directory to
|
||||
// include Grant ACE entries for the VM Group SID. This is a golang re-
|
||||
// implementation of the same function in vmcompute, just not exported in
|
||||
// RS5. Which kind of sucks. Sucks a lot :/
|
||||
func GrantVmGroupAccess(name string) error { |
||||
// Stat (to determine if `name` is a directory).
|
||||
s, err := os.Stat(name) |
||||
if err != nil { |
||||
return errors.Wrapf(err, "%s os.Stat %s", gvmga, name) |
||||
} |
||||
|
||||
// Get a handle to the file/directory. Must defer Close on success.
|
||||
fd, err := createFile(name, s.IsDir()) |
||||
if err != nil { |
||||
return err // Already wrapped
|
||||
} |
||||
defer syscall.CloseHandle(fd) |
||||
|
||||
// Get the current DACL and Security Descriptor. Must defer LocalFree on success.
|
||||
ot := objectTypeFileObject |
||||
si := securityInformationDACL |
||||
sd := uintptr(0) |
||||
origDACL := uintptr(0) |
||||
if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil { |
||||
return errors.Wrapf(err, "%s GetSecurityInfo %s", gvmga, name) |
||||
} |
||||
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) |
||||
|
||||
// Generate a new DACL which is the current DACL with the required ACEs added.
|
||||
// Must defer LocalFree on success.
|
||||
newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL) |
||||
if err != nil { |
||||
return err // Already wrapped
|
||||
} |
||||
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) |
||||
|
||||
// And finally use SetSecurityInfo to apply the updated DACL.
|
||||
if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil { |
||||
return errors.Wrapf(err, "%s SetSecurityInfo %s", gvmga, name) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// createFile is a helper function to call [Nt]CreateFile to get a handle to
|
||||
// the file or directory.
|
||||
func createFile(name string, isDir bool) (syscall.Handle, error) { |
||||
namep := syscall.StringToUTF16(name) |
||||
da := uint32(desiredAccessReadControl | desiredAccessWriteDac) |
||||
sm := uint32(shareModeRead | shareModeWrite) |
||||
fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL) |
||||
if isDir { |
||||
fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS) |
||||
} |
||||
fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0) |
||||
if err != nil { |
||||
return 0, errors.Wrapf(err, "%s syscall.CreateFile %s", gvmga, name) |
||||
} |
||||
return fd, nil |
||||
} |
||||
|
||||
// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added.
|
||||
// The caller is responsible for LocalFree of the returned DACL on success.
|
||||
func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) { |
||||
// Generate pointers to the SIDs based on the string SIDs
|
||||
sid, err := syscall.StringToSid(sidVmGroup) |
||||
if err != nil { |
||||
return 0, errors.Wrapf(err, "%s syscall.StringToSid %s %s", gvmga, name, sidVmGroup) |
||||
} |
||||
|
||||
inheritance := inheritModeNoInheritance |
||||
if isDir { |
||||
inheritance = inheritModeSubContainersAndObjectsInherit |
||||
} |
||||
|
||||
eaArray := []explicitAccess{ |
||||
explicitAccess{ |
||||
accessPermissions: accessMaskDesiredPermission, |
||||
accessMode: accessModeGrant, |
||||
inheritance: inheritance, |
||||
trustee: trustee{ |
||||
trusteeForm: trusteeFormIsSid, |
||||
trusteeType: trusteeTypeWellKnownGroup, |
||||
name: uintptr(unsafe.Pointer(sid)), |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
modifiedDACL := uintptr(0) |
||||
if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil { |
||||
return 0, errors.Wrapf(err, "%s SetEntriesInAcl %s", gvmga, name) |
||||
} |
||||
|
||||
return modifiedDACL, nil |
||||
} |
@ -0,0 +1,7 @@ |
||||
package security |
||||
|
||||
//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
//sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo
|
||||
//sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) [failretval!=0] = advapi32.SetSecurityInfo
|
||||
//sys setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) [failretval!=0] = advapi32.SetEntriesInAclW
|
@ -0,0 +1,70 @@ |
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package security |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
var _ unsafe.Pointer |
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const ( |
||||
errnoERROR_IO_PENDING = 997 |
||||
) |
||||
|
||||
var ( |
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
||||
errERROR_EINVAL error = syscall.EINVAL |
||||
) |
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error { |
||||
switch e { |
||||
case 0: |
||||
return errERROR_EINVAL |
||||
case errnoERROR_IO_PENDING: |
||||
return errERROR_IO_PENDING |
||||
} |
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e |
||||
} |
||||
|
||||
var ( |
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") |
||||
|
||||
procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") |
||||
procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") |
||||
procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo") |
||||
) |
||||
|
||||
func getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) { |
||||
r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(ppsidOwner)), uintptr(unsafe.Pointer(ppsidGroup)), uintptr(unsafe.Pointer(ppDacl)), uintptr(unsafe.Pointer(ppSacl)), uintptr(unsafe.Pointer(ppSecurityDescriptor)), 0) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(count), uintptr(pListOfEEs), uintptr(oldAcl), uintptr(unsafe.Pointer(newAcl)), 0, 0) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) { |
||||
r1, _, e1 := syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(psidOwner), uintptr(psidGroup), uintptr(pDacl), uintptr(pSacl), 0, 0) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,202 @@ |
||||
// +build windows
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/binary" |
||||
"fmt" |
||||
"runtime" |
||||
"sync" |
||||
"syscall" |
||||
"unicode/utf16" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||
|
||||
const ( |
||||
SE_PRIVILEGE_ENABLED = 2 |
||||
|
||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 |
||||
|
||||
SeBackupPrivilege = "SeBackupPrivilege" |
||||
SeRestorePrivilege = "SeRestorePrivilege" |
||||
) |
||||
|
||||
const ( |
||||
securityAnonymous = iota |
||||
securityIdentification |
||||
securityImpersonation |
||||
securityDelegation |
||||
) |
||||
|
||||
var ( |
||||
privNames = make(map[string]uint64) |
||||
privNameMutex sync.Mutex |
||||
) |
||||
|
||||
// PrivilegeError represents an error enabling privileges.
|
||||
type PrivilegeError struct { |
||||
privileges []uint64 |
||||
} |
||||
|
||||
func (e *PrivilegeError) Error() string { |
||||
s := "" |
||||
if len(e.privileges) > 1 { |
||||
s = "Could not enable privileges " |
||||
} else { |
||||
s = "Could not enable privilege " |
||||
} |
||||
for i, p := range e.privileges { |
||||
if i != 0 { |
||||
s += ", " |
||||
} |
||||
s += `"` |
||||
s += getPrivilegeName(p) |
||||
s += `"` |
||||
} |
||||
return s |
||||
} |
||||
|
||||
// RunWithPrivilege enables a single privilege for a function call.
|
||||
func RunWithPrivilege(name string, fn func() error) error { |
||||
return RunWithPrivileges([]string{name}, fn) |
||||
} |
||||
|
||||
// RunWithPrivileges enables privileges for a function call.
|
||||
func RunWithPrivileges(names []string, fn func() error) error { |
||||
privileges, err := mapPrivileges(names) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
runtime.LockOSThread() |
||||
defer runtime.UnlockOSThread() |
||||
token, err := newThreadToken() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer releaseThreadToken(token) |
||||
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return fn() |
||||
} |
||||
|
||||
func mapPrivileges(names []string) ([]uint64, error) { |
||||
var privileges []uint64 |
||||
privNameMutex.Lock() |
||||
defer privNameMutex.Unlock() |
||||
for _, name := range names { |
||||
p, ok := privNames[name] |
||||
if !ok { |
||||
err := lookupPrivilegeValue("", name, &p) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
privNames[name] = p |
||||
} |
||||
privileges = append(privileges, p) |
||||
} |
||||
return privileges, nil |
||||
} |
||||
|
||||
// EnableProcessPrivileges enables privileges globally for the process.
|
||||
func EnableProcessPrivileges(names []string) error { |
||||
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED) |
||||
} |
||||
|
||||
// DisableProcessPrivileges disables privileges globally for the process.
|
||||
func DisableProcessPrivileges(names []string) error { |
||||
return enableDisableProcessPrivilege(names, 0) |
||||
} |
||||
|
||||
func enableDisableProcessPrivilege(names []string, action uint32) error { |
||||
privileges, err := mapPrivileges(names) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
p, _ := windows.GetCurrentProcess() |
||||
var token windows.Token |
||||
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
defer token.Close() |
||||
return adjustPrivileges(token, privileges, action) |
||||
} |
||||
|
||||
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { |
||||
var b bytes.Buffer |
||||
binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) |
||||
for _, p := range privileges { |
||||
binary.Write(&b, binary.LittleEndian, p) |
||||
binary.Write(&b, binary.LittleEndian, action) |
||||
} |
||||
prevState := make([]byte, b.Len()) |
||||
reqSize := uint32(0) |
||||
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize) |
||||
if !success { |
||||
return err |
||||
} |
||||
if err == ERROR_NOT_ALL_ASSIGNED { |
||||
return &PrivilegeError{privileges} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func getPrivilegeName(luid uint64) string { |
||||
var nameBuffer [256]uint16 |
||||
bufSize := uint32(len(nameBuffer)) |
||||
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize) |
||||
if err != nil { |
||||
return fmt.Sprintf("<unknown privilege %d>", luid) |
||||
} |
||||
|
||||
var displayNameBuffer [256]uint16 |
||||
displayBufSize := uint32(len(displayNameBuffer)) |
||||
var langID uint32 |
||||
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID) |
||||
if err != nil { |
||||
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize]))) |
||||
} |
||||
|
||||
return string(utf16.Decode(displayNameBuffer[:displayBufSize])) |
||||
} |
||||
|
||||
func newThreadToken() (windows.Token, error) { |
||||
err := impersonateSelf(securityImpersonation) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
|
||||
var token windows.Token |
||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token) |
||||
if err != nil { |
||||
rerr := revertToSelf() |
||||
if rerr != nil { |
||||
panic(rerr) |
||||
} |
||||
return 0, err |
||||
} |
||||
return token, nil |
||||
} |
||||
|
||||
func releaseThreadToken(h windows.Token) { |
||||
err := revertToSelf() |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
h.Close() |
||||
} |
@ -0,0 +1,128 @@ |
||||
package winio |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/binary" |
||||
"fmt" |
||||
"strings" |
||||
"unicode/utf16" |
||||
"unsafe" |
||||
) |
||||
|
||||
const ( |
||||
reparseTagMountPoint = 0xA0000003 |
||||
reparseTagSymlink = 0xA000000C |
||||
) |
||||
|
||||
type reparseDataBuffer struct { |
||||
ReparseTag uint32 |
||||
ReparseDataLength uint16 |
||||
Reserved uint16 |
||||
SubstituteNameOffset uint16 |
||||
SubstituteNameLength uint16 |
||||
PrintNameOffset uint16 |
||||
PrintNameLength uint16 |
||||
} |
||||
|
||||
// ReparsePoint describes a Win32 symlink or mount point.
|
||||
type ReparsePoint struct { |
||||
Target string |
||||
IsMountPoint bool |
||||
} |
||||
|
||||
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
||||
// mount point reparse point.
|
||||
type UnsupportedReparsePointError struct { |
||||
Tag uint32 |
||||
} |
||||
|
||||
func (e *UnsupportedReparsePointError) Error() string { |
||||
return fmt.Sprintf("unsupported reparse point %x", e.Tag) |
||||
} |
||||
|
||||
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
||||
// or a mount point.
|
||||
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) { |
||||
tag := binary.LittleEndian.Uint32(b[0:4]) |
||||
return DecodeReparsePointData(tag, b[8:]) |
||||
} |
||||
|
||||
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) { |
||||
isMountPoint := false |
||||
switch tag { |
||||
case reparseTagMountPoint: |
||||
isMountPoint = true |
||||
case reparseTagSymlink: |
||||
default: |
||||
return nil, &UnsupportedReparsePointError{tag} |
||||
} |
||||
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6]) |
||||
if !isMountPoint { |
||||
nameOffset += 4 |
||||
} |
||||
nameLength := binary.LittleEndian.Uint16(b[6:8]) |
||||
name := make([]uint16, nameLength/2) |
||||
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil |
||||
} |
||||
|
||||
func isDriveLetter(c byte) bool { |
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') |
||||
} |
||||
|
||||
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
||||
// mount point.
|
||||
func EncodeReparsePoint(rp *ReparsePoint) []byte { |
||||
// Generate an NT path and determine if this is a relative path.
|
||||
var ntTarget string |
||||
relative := false |
||||
if strings.HasPrefix(rp.Target, `\\?\`) { |
||||
ntTarget = `\??\` + rp.Target[4:] |
||||
} else if strings.HasPrefix(rp.Target, `\\`) { |
||||
ntTarget = `\??\UNC\` + rp.Target[2:] |
||||
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' { |
||||
ntTarget = `\??\` + rp.Target |
||||
} else { |
||||
ntTarget = rp.Target |
||||
relative = true |
||||
} |
||||
|
||||
// The paths must be NUL-terminated even though they are counted strings.
|
||||
target16 := utf16.Encode([]rune(rp.Target + "\x00")) |
||||
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00")) |
||||
|
||||
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8 |
||||
size += len(ntTarget16)*2 + len(target16)*2 |
||||
|
||||
tag := uint32(reparseTagMountPoint) |
||||
if !rp.IsMountPoint { |
||||
tag = reparseTagSymlink |
||||
size += 4 // Add room for symlink flags
|
||||
} |
||||
|
||||
data := reparseDataBuffer{ |
||||
ReparseTag: tag, |
||||
ReparseDataLength: uint16(size), |
||||
SubstituteNameOffset: 0, |
||||
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2), |
||||
PrintNameOffset: uint16(len(ntTarget16) * 2), |
||||
PrintNameLength: uint16((len(target16) - 1) * 2), |
||||
} |
||||
|
||||
var b bytes.Buffer |
||||
binary.Write(&b, binary.LittleEndian, &data) |
||||
if !rp.IsMountPoint { |
||||
flags := uint32(0) |
||||
if relative { |
||||
flags |= 1 |
||||
} |
||||
binary.Write(&b, binary.LittleEndian, flags) |
||||
} |
||||
|
||||
binary.Write(&b, binary.LittleEndian, ntTarget16) |
||||
binary.Write(&b, binary.LittleEndian, target16) |
||||
return b.Bytes() |
||||
} |
@ -0,0 +1,98 @@ |
||||
// +build windows
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
) |
||||
|
||||
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
||||
//sys localFree(mem uintptr) = LocalFree
|
||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
||||
|
||||
const ( |
||||
cERROR_NONE_MAPPED = syscall.Errno(1332) |
||||
) |
||||
|
||||
type AccountLookupError struct { |
||||
Name string |
||||
Err error |
||||
} |
||||
|
||||
func (e *AccountLookupError) Error() string { |
||||
if e.Name == "" { |
||||
return "lookup account: empty account name specified" |
||||
} |
||||
var s string |
||||
switch e.Err { |
||||
case cERROR_NONE_MAPPED: |
||||
s = "not found" |
||||
default: |
||||
s = e.Err.Error() |
||||
} |
||||
return "lookup account " + e.Name + ": " + s |
||||
} |
||||
|
||||
type SddlConversionError struct { |
||||
Sddl string |
||||
Err error |
||||
} |
||||
|
||||
func (e *SddlConversionError) Error() string { |
||||
return "convert " + e.Sddl + ": " + e.Err.Error() |
||||
} |
||||
|
||||
// LookupSidByName looks up the SID of an account by name
|
||||
func LookupSidByName(name string) (sid string, err error) { |
||||
if name == "" { |
||||
return "", &AccountLookupError{name, cERROR_NONE_MAPPED} |
||||
} |
||||
|
||||
var sidSize, sidNameUse, refDomainSize uint32 |
||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) |
||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { |
||||
return "", &AccountLookupError{name, err} |
||||
} |
||||
sidBuffer := make([]byte, sidSize) |
||||
refDomainBuffer := make([]uint16, refDomainSize) |
||||
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) |
||||
if err != nil { |
||||
return "", &AccountLookupError{name, err} |
||||
} |
||||
var strBuffer *uint16 |
||||
err = convertSidToStringSid(&sidBuffer[0], &strBuffer) |
||||
if err != nil { |
||||
return "", &AccountLookupError{name, err} |
||||
} |
||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:]) |
||||
localFree(uintptr(unsafe.Pointer(strBuffer))) |
||||
return sid, nil |
||||
} |
||||
|
||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) { |
||||
var sdBuffer uintptr |
||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) |
||||
if err != nil { |
||||
return nil, &SddlConversionError{sddl, err} |
||||
} |
||||
defer localFree(sdBuffer) |
||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) |
||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) |
||||
return sd, nil |
||||
} |
||||
|
||||
func SecurityDescriptorToSddl(sd []byte) (string, error) { |
||||
var sddl *uint16 |
||||
// The returned string length seems to including an aribtrary number of terminating NULs.
|
||||
// Don't use it.
|
||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
defer localFree(uintptr(unsafe.Pointer(sddl))) |
||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil |
||||
} |
@ -0,0 +1,3 @@ |
||||
package winio |
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
|
@ -0,0 +1,323 @@ |
||||
// +build windows
|
||||
|
||||
package vhd |
||||
|
||||
import ( |
||||
"fmt" |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid" |
||||
"github.com/pkg/errors" |
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
//go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go
|
||||
|
||||
//sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.CreateVirtualDisk
|
||||
//sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.OpenVirtualDisk
|
||||
//sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (err error) [failretval != 0] = virtdisk.AttachVirtualDisk
|
||||
//sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (err error) [failretval != 0] = virtdisk.DetachVirtualDisk
|
||||
//sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (err error) [failretval != 0] = virtdisk.GetVirtualDiskPhysicalPath
|
||||
|
||||
type ( |
||||
CreateVirtualDiskFlag uint32 |
||||
VirtualDiskFlag uint32 |
||||
AttachVirtualDiskFlag uint32 |
||||
DetachVirtualDiskFlag uint32 |
||||
VirtualDiskAccessMask uint32 |
||||
) |
||||
|
||||
type VirtualStorageType struct { |
||||
DeviceID uint32 |
||||
VendorID guid.GUID |
||||
} |
||||
|
||||
type CreateVersion2 struct { |
||||
UniqueID guid.GUID |
||||
MaximumSize uint64 |
||||
BlockSizeInBytes uint32 |
||||
SectorSizeInBytes uint32 |
||||
PhysicalSectorSizeInByte uint32 |
||||
ParentPath *uint16 // string
|
||||
SourcePath *uint16 // string
|
||||
OpenFlags uint32 |
||||
ParentVirtualStorageType VirtualStorageType |
||||
SourceVirtualStorageType VirtualStorageType |
||||
ResiliencyGUID guid.GUID |
||||
} |
||||
|
||||
type CreateVirtualDiskParameters struct { |
||||
Version uint32 // Must always be set to 2
|
||||
Version2 CreateVersion2 |
||||
} |
||||
|
||||
type OpenVersion2 struct { |
||||
GetInfoOnly bool |
||||
ReadOnly bool |
||||
ResiliencyGUID guid.GUID |
||||
} |
||||
|
||||
type OpenVirtualDiskParameters struct { |
||||
Version uint32 // Must always be set to 2
|
||||
Version2 OpenVersion2 |
||||
} |
||||
|
||||
type AttachVersion2 struct { |
||||
RestrictedOffset uint64 |
||||
RestrictedLength uint64 |
||||
} |
||||
|
||||
type AttachVirtualDiskParameters struct { |
||||
Version uint32 // Must always be set to 2
|
||||
Version2 AttachVersion2 |
||||
} |
||||
|
||||
const ( |
||||
VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3 |
||||
|
||||
// Access Mask for opening a VHD
|
||||
VirtualDiskAccessNone VirtualDiskAccessMask = 0x00000000 |
||||
VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000 |
||||
VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000 |
||||
VirtualDiskAccessDetach VirtualDiskAccessMask = 0x00040000 |
||||
VirtualDiskAccessGetInfo VirtualDiskAccessMask = 0x00080000 |
||||
VirtualDiskAccessCreate VirtualDiskAccessMask = 0x00100000 |
||||
VirtualDiskAccessMetaOps VirtualDiskAccessMask = 0x00200000 |
||||
VirtualDiskAccessRead VirtualDiskAccessMask = 0x000d0000 |
||||
VirtualDiskAccessAll VirtualDiskAccessMask = 0x003f0000 |
||||
VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000 |
||||
|
||||
// Flags for creating a VHD
|
||||
CreateVirtualDiskFlagNone CreateVirtualDiskFlag = 0x0 |
||||
CreateVirtualDiskFlagFullPhysicalAllocation CreateVirtualDiskFlag = 0x1 |
||||
CreateVirtualDiskFlagPreventWritesToSourceDisk CreateVirtualDiskFlag = 0x2 |
||||
CreateVirtualDiskFlagDoNotCopyMetadataFromParent CreateVirtualDiskFlag = 0x4 |
||||
CreateVirtualDiskFlagCreateBackingStorage CreateVirtualDiskFlag = 0x8 |
||||
CreateVirtualDiskFlagUseChangeTrackingSourceLimit CreateVirtualDiskFlag = 0x10 |
||||
CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20 |
||||
CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40 |
||||
CreateVirtualDiskFlagSparseFile CreateVirtualDiskFlag = 0x80 |
||||
CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100 |
||||
CreateVirtualDiskFlagSupportCompressedVolumes CreateVirtualDiskFlag = 0x200 |
||||
|
||||
// Flags for opening a VHD
|
||||
OpenVirtualDiskFlagNone VirtualDiskFlag = 0x00000000 |
||||
OpenVirtualDiskFlagNoParents VirtualDiskFlag = 0x00000001 |
||||
OpenVirtualDiskFlagBlankFile VirtualDiskFlag = 0x00000002 |
||||
OpenVirtualDiskFlagBootDrive VirtualDiskFlag = 0x00000004 |
||||
OpenVirtualDiskFlagCachedIO VirtualDiskFlag = 0x00000008 |
||||
OpenVirtualDiskFlagCustomDiffChain VirtualDiskFlag = 0x00000010 |
||||
OpenVirtualDiskFlagParentCachedIO VirtualDiskFlag = 0x00000020 |
||||
OpenVirtualDiskFlagVhdsetFileOnly VirtualDiskFlag = 0x00000040 |
||||
OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x00000080 |
||||
OpenVirtualDiskFlagNoWriteHardening VirtualDiskFlag = 0x00000100 |
||||
OpenVirtualDiskFlagSupportCompressedVolumes VirtualDiskFlag = 0x00000200 |
||||
|
||||
// Flags for attaching a VHD
|
||||
AttachVirtualDiskFlagNone AttachVirtualDiskFlag = 0x00000000 |
||||
AttachVirtualDiskFlagReadOnly AttachVirtualDiskFlag = 0x00000001 |
||||
AttachVirtualDiskFlagNoDriveLetter AttachVirtualDiskFlag = 0x00000002 |
||||
AttachVirtualDiskFlagPermanentLifetime AttachVirtualDiskFlag = 0x00000004 |
||||
AttachVirtualDiskFlagNoLocalHost AttachVirtualDiskFlag = 0x00000008 |
||||
AttachVirtualDiskFlagNoSecurityDescriptor AttachVirtualDiskFlag = 0x00000010 |
||||
AttachVirtualDiskFlagBypassDefaultEncryptionPolicy AttachVirtualDiskFlag = 0x00000020 |
||||
AttachVirtualDiskFlagNonPnp AttachVirtualDiskFlag = 0x00000040 |
||||
AttachVirtualDiskFlagRestrictedRange AttachVirtualDiskFlag = 0x00000080 |
||||
AttachVirtualDiskFlagSinglePartition AttachVirtualDiskFlag = 0x00000100 |
||||
AttachVirtualDiskFlagRegisterVolume AttachVirtualDiskFlag = 0x00000200 |
||||
|
||||
// Flags for detaching a VHD
|
||||
DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0 |
||||
) |
||||
|
||||
// CreateVhdx is a helper function to create a simple vhdx file at the given path using
|
||||
// default values.
|
||||
func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error { |
||||
params := CreateVirtualDiskParameters{ |
||||
Version: 2, |
||||
Version2: CreateVersion2{ |
||||
MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024, |
||||
BlockSizeInBytes: blockSizeInMb * 1024 * 1024, |
||||
}, |
||||
} |
||||
|
||||
handle, err := CreateVirtualDisk(path, VirtualDiskAccessNone, CreateVirtualDiskFlagNone, ¶ms) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
if err := syscall.CloseHandle(handle); err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// DetachVirtualDisk detaches a virtual hard disk by handle.
|
||||
func DetachVirtualDisk(handle syscall.Handle) (err error) { |
||||
if err := detachVirtualDisk(handle, 0, 0); err != nil { |
||||
return errors.Wrap(err, "failed to detach virtual disk") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// DetachVhd detaches a vhd found at `path`.
|
||||
func DetachVhd(path string) error { |
||||
handle, err := OpenVirtualDisk( |
||||
path, |
||||
VirtualDiskAccessNone, |
||||
OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator, |
||||
) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer syscall.CloseHandle(handle) |
||||
return DetachVirtualDisk(handle) |
||||
} |
||||
|
||||
// AttachVirtualDisk attaches a virtual hard disk for use.
|
||||
func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtualDiskFlag, parameters *AttachVirtualDiskParameters) (err error) { |
||||
// Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5.
|
||||
if err := attachVirtualDisk( |
||||
handle, |
||||
nil, |
||||
uint32(attachVirtualDiskFlag), |
||||
0, |
||||
parameters, |
||||
nil, |
||||
); err != nil { |
||||
return errors.Wrap(err, "failed to attach virtual disk") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2
|
||||
// of the ATTACH_VIRTUAL_DISK_PARAMETERS.
|
||||
func AttachVhd(path string) (err error) { |
||||
handle, err := OpenVirtualDisk( |
||||
path, |
||||
VirtualDiskAccessNone, |
||||
OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator, |
||||
) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
defer syscall.CloseHandle(handle) |
||||
params := AttachVirtualDiskParameters{Version: 2} |
||||
if err := AttachVirtualDisk( |
||||
handle, |
||||
AttachVirtualDiskFlagNone, |
||||
¶ms, |
||||
); err != nil { |
||||
return errors.Wrap(err, "failed to attach virtual disk") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
|
||||
func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag) (syscall.Handle, error) { |
||||
parameters := OpenVirtualDiskParameters{Version: 2} |
||||
handle, err := OpenVirtualDiskWithParameters( |
||||
vhdPath, |
||||
virtualDiskAccessMask, |
||||
openVirtualDiskFlags, |
||||
¶meters, |
||||
) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
return handle, nil |
||||
} |
||||
|
||||
// OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters.
|
||||
func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag, parameters *OpenVirtualDiskParameters) (syscall.Handle, error) { |
||||
var ( |
||||
handle syscall.Handle |
||||
defaultType VirtualStorageType |
||||
) |
||||
if parameters.Version != 2 { |
||||
return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version) |
||||
} |
||||
if err := openVirtualDisk( |
||||
&defaultType, |
||||
vhdPath, |
||||
uint32(virtualDiskAccessMask), |
||||
uint32(openVirtualDiskFlags), |
||||
parameters, |
||||
&handle, |
||||
); err != nil { |
||||
return 0, errors.Wrap(err, "failed to open virtual disk") |
||||
} |
||||
return handle, nil |
||||
} |
||||
|
||||
// CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk.
|
||||
func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, createVirtualDiskFlags CreateVirtualDiskFlag, parameters *CreateVirtualDiskParameters) (syscall.Handle, error) { |
||||
var ( |
||||
handle syscall.Handle |
||||
defaultType VirtualStorageType |
||||
) |
||||
if parameters.Version != 2 { |
||||
return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version) |
||||
} |
||||
|
||||
if err := createVirtualDisk( |
||||
&defaultType, |
||||
path, |
||||
uint32(virtualDiskAccessMask), |
||||
nil, |
||||
uint32(createVirtualDiskFlags), |
||||
0, |
||||
parameters, |
||||
nil, |
||||
&handle, |
||||
); err != nil { |
||||
return handle, errors.Wrap(err, "failed to create virtual disk") |
||||
} |
||||
return handle, nil |
||||
} |
||||
|
||||
// GetVirtualDiskPhysicalPath takes a handle to a virtual hard disk and returns the physical
|
||||
// path of the disk on the machine. This path is in the form \\.\PhysicalDriveX where X is an integer
|
||||
// that represents the particular enumeration of the physical disk on the caller's system.
|
||||
func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) { |
||||
var ( |
||||
diskPathSizeInBytes uint32 = 256 * 2 // max path length 256 wide chars
|
||||
diskPhysicalPathBuf [256]uint16 |
||||
) |
||||
if err := getVirtualDiskPhysicalPath( |
||||
handle, |
||||
&diskPathSizeInBytes, |
||||
&diskPhysicalPathBuf[0], |
||||
); err != nil { |
||||
return "", errors.Wrap(err, "failed to get disk physical path") |
||||
} |
||||
return windows.UTF16ToString(diskPhysicalPathBuf[:]), nil |
||||
} |
||||
|
||||
// CreateDiffVhd is a helper function to create a differencing virtual disk.
|
||||
func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error { |
||||
// Setting `ParentPath` is how to signal to create a differencing disk.
|
||||
createParams := &CreateVirtualDiskParameters{ |
||||
Version: 2, |
||||
Version2: CreateVersion2{ |
||||
ParentPath: windows.StringToUTF16Ptr(baseVhdPath), |
||||
BlockSizeInBytes: blockSizeInMB * 1024 * 1024, |
||||
OpenFlags: uint32(OpenVirtualDiskFlagCachedIO), |
||||
}, |
||||
} |
||||
|
||||
vhdHandle, err := CreateVirtualDisk( |
||||
diffVhdPath, |
||||
VirtualDiskAccessNone, |
||||
CreateVirtualDiskFlagNone, |
||||
createParams, |
||||
) |
||||
if err != nil { |
||||
return fmt.Errorf("failed to create differencing vhd: %s", err) |
||||
} |
||||
if err := syscall.CloseHandle(vhdHandle); err != nil { |
||||
return fmt.Errorf("failed to close differencing vhd handle: %s", err) |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,106 @@ |
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package vhd |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
var _ unsafe.Pointer |
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const ( |
||||
errnoERROR_IO_PENDING = 997 |
||||
) |
||||
|
||||
var ( |
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
||||
errERROR_EINVAL error = syscall.EINVAL |
||||
) |
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error { |
||||
switch e { |
||||
case 0: |
||||
return errERROR_EINVAL |
||||
case errnoERROR_IO_PENDING: |
||||
return errERROR_IO_PENDING |
||||
} |
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e |
||||
} |
||||
|
||||
var ( |
||||
modvirtdisk = windows.NewLazySystemDLL("virtdisk.dll") |
||||
|
||||
procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk") |
||||
procCreateVirtualDisk = modvirtdisk.NewProc("CreateVirtualDisk") |
||||
procDetachVirtualDisk = modvirtdisk.NewProc("DetachVirtualDisk") |
||||
procGetVirtualDiskPhysicalPath = modvirtdisk.NewProc("GetVirtualDiskPhysicalPath") |
||||
procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk") |
||||
) |
||||
|
||||
func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(attachVirtualDiskFlag), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped))) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, createVirtualDiskFlags, providerSpecificFlags, parameters, overlapped, handle) |
||||
} |
||||
|
||||
func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (err error) { |
||||
r1, _, e1 := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(createVirtualDiskFlags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(handle))) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(detachVirtualDiskFlags), uintptr(providerSpecificFlags)) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procGetVirtualDiskPhysicalPath.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer))) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(path) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, openVirtualDiskFlags, parameters, handle) |
||||
} |
||||
|
||||
func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle))) |
||||
if r1 != 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,427 @@ |
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package winio |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
var _ unsafe.Pointer |
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const ( |
||||
errnoERROR_IO_PENDING = 997 |
||||
) |
||||
|
||||
var ( |
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
||||
errERROR_EINVAL error = syscall.EINVAL |
||||
) |
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error { |
||||
switch e { |
||||
case 0: |
||||
return errERROR_EINVAL |
||||
case errnoERROR_IO_PENDING: |
||||
return errERROR_IO_PENDING |
||||
} |
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e |
||||
} |
||||
|
||||
var ( |
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") |
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll") |
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll") |
||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") |
||||
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") |
||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") |
||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") |
||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") |
||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") |
||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") |
||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") |
||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") |
||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") |
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") |
||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") |
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf") |
||||
procBackupRead = modkernel32.NewProc("BackupRead") |
||||
procBackupWrite = modkernel32.NewProc("BackupWrite") |
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx") |
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") |
||||
procCreateFileW = modkernel32.NewProc("CreateFileW") |
||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") |
||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") |
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") |
||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") |
||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") |
||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") |
||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc") |
||||
procLocalFree = modkernel32.NewProc("LocalFree") |
||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") |
||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") |
||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl") |
||||
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U") |
||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") |
||||
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") |
||||
procbind = modws2_32.NewProc("bind") |
||||
) |
||||
|
||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { |
||||
var _p0 uint32 |
||||
if releaseAll { |
||||
_p0 = 1 |
||||
} |
||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) |
||||
success = r0 != 0 |
||||
if true { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(str) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) |
||||
} |
||||
|
||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) { |
||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) |
||||
len = uint32(r0) |
||||
return |
||||
} |
||||
|
||||
func impersonateSelf(level uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(accountName) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) |
||||
} |
||||
|
||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(systemName) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) |
||||
} |
||||
|
||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(systemName) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _lookupPrivilegeName(_p0, luid, buffer, size) |
||||
} |
||||
|
||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(systemName) |
||||
if err != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, err = syscall.UTF16PtrFromString(name) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _lookupPrivilegeValue(_p0, _p1, luid) |
||||
} |
||||
|
||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { |
||||
var _p0 uint32 |
||||
if openAsSelf { |
||||
_p0 = 1 |
||||
} |
||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func revertToSelf() (err error) { |
||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { |
||||
var _p0 *byte |
||||
if len(b) > 0 { |
||||
_p0 = &b[0] |
||||
} |
||||
var _p1 uint32 |
||||
if abort { |
||||
_p1 = 1 |
||||
} |
||||
var _p2 uint32 |
||||
if processSecurity { |
||||
_p2 = 1 |
||||
} |
||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { |
||||
var _p0 *byte |
||||
if len(b) > 0 { |
||||
_p0 = &b[0] |
||||
} |
||||
var _p1 uint32 |
||||
if abort { |
||||
_p1 = 1 |
||||
} |
||||
var _p2 uint32 |
||||
if processSecurity { |
||||
_p2 = 1 |
||||
} |
||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(name) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) |
||||
} |
||||
|
||||
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { |
||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) |
||||
handle = syscall.Handle(r0) |
||||
if handle == syscall.InvalidHandle { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { |
||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) |
||||
newport = syscall.Handle(r0) |
||||
if newport == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { |
||||
var _p0 *uint16 |
||||
_p0, err = syscall.UTF16PtrFromString(name) |
||||
if err != nil { |
||||
return |
||||
} |
||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) |
||||
} |
||||
|
||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { |
||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) |
||||
handle = syscall.Handle(r0) |
||||
if handle == syscall.InvalidHandle { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func getCurrentThread() (h syscall.Handle) { |
||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) |
||||
h = syscall.Handle(r0) |
||||
return |
||||
} |
||||
|
||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { |
||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { |
||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) |
||||
ptr = uintptr(r0) |
||||
return |
||||
} |
||||
|
||||
func localFree(mem uintptr) { |
||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) |
||||
return |
||||
} |
||||
|
||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) { |
||||
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) |
||||
status = ntstatus(r0) |
||||
return |
||||
} |
||||
|
||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) { |
||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0) |
||||
status = ntstatus(r0) |
||||
return |
||||
} |
||||
|
||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) { |
||||
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0) |
||||
status = ntstatus(r0) |
||||
return |
||||
} |
||||
|
||||
func rtlNtStatusToDosError(status ntstatus) (winerr error) { |
||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) |
||||
if r0 != 0 { |
||||
winerr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) { |
||||
var _p0 uint32 |
||||
if wait { |
||||
_p0 = 1 |
||||
} |
||||
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0) |
||||
if r1 == 0 { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { |
||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) |
||||
if r1 == socketError { |
||||
err = errnoErr(e1) |
||||
} |
||||
return |
||||
} |
@ -0,0 +1 @@ |
||||
* text=auto eol=lf |
@ -0,0 +1,3 @@ |
||||
*.exe |
||||
.idea |
||||
.vscode |
@ -0,0 +1,3 @@ |
||||
* @microsoft/containerplat |
||||
|
||||
/hcn/* @nagiesek |
@ -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,44 @@ |
||||
version = "unstable" |
||||
generator = "gogoctrd" |
||||
plugins = ["grpc", "fieldpath"] |
||||
|
||||
# Control protoc include paths. Below are usually some good defaults, but feel |
||||
# free to try it without them if it works for your project. |
||||
[includes] |
||||
# Include paths that will be added before all others. Typically, you want to |
||||
# treat the root of the project as an include, but this may not be necessary. |
||||
before = ["./protobuf"] |
||||
|
||||
# Paths that should be treated as include roots in relation to the vendor |
||||
# directory. These will be calculated with the vendor directory nearest the |
||||
# target package. |
||||
packages = ["github.com/gogo/protobuf"] |
||||
|
||||
# Paths that will be added untouched to the end of the includes. We use |
||||
# `/usr/local/include` to pickup the common install location of protobuf. |
||||
# This is the default. |
||||
after = ["/usr/local/include"] |
||||
|
||||
# This section maps protobuf imports to Go packages. These will become |
||||
# `-M` directives in the call to the go protobuf generator. |
||||
[packages] |
||||
"gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto" |
||||
"google/protobuf/any.proto" = "github.com/gogo/protobuf/types" |
||||
"google/protobuf/empty.proto" = "github.com/gogo/protobuf/types" |
||||
"google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" |
||||
"google/protobuf/field_mask.proto" = "github.com/gogo/protobuf/types" |
||||
"google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types" |
||||
"google/protobuf/duration.proto" = "github.com/gogo/protobuf/types" |
||||
"github/containerd/cgroups/stats/v1/metrics.proto" = "github.com/containerd/cgroups/stats/v1" |
||||
|
||||
[[overrides]] |
||||
prefixes = ["github.com/Microsoft/hcsshim/internal/shimdiag"] |
||||
plugins = ["ttrpc"] |
||||
|
||||
[[overrides]] |
||||
prefixes = ["github.com/Microsoft/hcsshim/internal/computeagent"] |
||||
plugins = ["ttrpc"] |
||||
|
||||
[[overrides]] |
||||
prefixes = ["github.com/Microsoft/hcsshim/internal/ncproxyttrpc"] |
||||
plugins = ["ttrpc"] |
@ -0,0 +1,46 @@ |
||||
# hcsshim |
||||
|
||||
[![Build status](https://github.com/microsoft/hcsshim/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/microsoft/hcsshim/actions?query=branch%3Amaster) |
||||
|
||||
This package contains the Golang interface for using the Windows [Host Compute Service](https://techcommunity.microsoft.com/t5/containers/introducing-the-host-compute-service-hcs/ba-p/382332) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS). |
||||
|
||||
It is primarily used in the [Moby Project](https://github.com/moby/moby), 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. |
||||
|
||||
We also ask that contributors [sign their commits](https://git-scm.com/docs/git-commit) using `git commit -s` or `git commit --signoff` to certify they either authored the work themselves or otherwise have permission to use it in this project. |
||||
|
||||
|
||||
## Code of Conduct |
||||
|
||||
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. |
||||
|
||||
## Dependencies |
||||
|
||||
This project requires Golang 1.9 or newer to build. |
||||
|
||||
For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements). |
||||
|
||||
## 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). |
||||
|
||||
For additional details, see [Report a Computer Security Vulnerability](https://technet.microsoft.com/en-us/security/ff852094.aspx) on Technet |
||||
|
||||
--------------- |
||||
Copyright (c) 2018 Microsoft Corp. All rights reserved. |
@ -0,0 +1,38 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// AttachLayerStorageFilter sets up the layer storage filter on a writable
|
||||
// container layer.
|
||||
//
|
||||
// `layerPath` is a path to a directory the writable layer is mounted. If the
|
||||
// path does not end in a `\` the platform will append it automatically.
|
||||
//
|
||||
// `layerData` is the parent read-only layer data.
|
||||
func AttachLayerStorageFilter(ctx context.Context, layerPath string, layerData LayerData) (err error) { |
||||
title := "hcsshim.AttachLayerStorageFilter" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("layerPath", layerPath), |
||||
) |
||||
|
||||
bytes, err := json.Marshal(layerData) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
err = hcsAttachLayerStorageFilter(layerPath, string(bytes)) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to attach layer storage filter") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,26 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// DestroyLayer deletes a container layer.
|
||||
//
|
||||
// `layerPath` is a path to a directory containing the layer to export.
|
||||
func DestroyLayer(ctx context.Context, layerPath string) (err error) { |
||||
title := "hcsshim.DestroyLayer" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("layerPath", layerPath)) |
||||
|
||||
err = hcsDestroyLayer(layerPath) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to destroy layer") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,26 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// DetachLayerStorageFilter detaches the layer storage filter on a writable container layer.
|
||||
//
|
||||
// `layerPath` is a path to a directory containing the layer to export.
|
||||
func DetachLayerStorageFilter(ctx context.Context, layerPath string) (err error) { |
||||
title := "hcsshim.DetachLayerStorageFilter" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("layerPath", layerPath)) |
||||
|
||||
err = hcsDetachLayerStorageFilter(layerPath) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to detach layer storage filter") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,46 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// ExportLayer exports a container layer.
|
||||
//
|
||||
// `layerPath` is a path to a directory containing the layer to export.
|
||||
//
|
||||
// `exportFolderPath` is a pre-existing folder to export the layer to.
|
||||
//
|
||||
// `layerData` is the parent layer data.
|
||||
//
|
||||
// `options` are the export options applied to the exported layer.
|
||||
func ExportLayer(ctx context.Context, layerPath, exportFolderPath string, layerData LayerData, options ExportLayerOptions) (err error) { |
||||
title := "hcsshim.ExportLayer" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("layerPath", layerPath), |
||||
trace.StringAttribute("exportFolderPath", exportFolderPath), |
||||
) |
||||
|
||||
ldbytes, err := json.Marshal(layerData) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
obytes, err := json.Marshal(options) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
err = hcsExportLayer(layerPath, exportFolderPath, string(ldbytes), string(obytes)) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to export layer") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,26 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
// FormatWritableLayerVhd formats a virtual disk for use as a writable container layer.
|
||||
//
|
||||
// If the VHD is not mounted it will be temporarily mounted.
|
||||
func FormatWritableLayerVhd(ctx context.Context, vhdHandle windows.Handle) (err error) { |
||||
title := "hcsshim.FormatWritableLayerVhd" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
|
||||
err = hcsFormatWritableLayerVhd(vhdHandle) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to format writable layer vhd") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,193 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
"os" |
||||
"path/filepath" |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/go-winio/pkg/security" |
||||
"github.com/Microsoft/go-winio/vhd" |
||||
"github.com/pkg/errors" |
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
const defaultVHDXBlockSizeInMB = 1 |
||||
|
||||
// SetupContainerBaseLayer is a helper to setup a containers scratch. It
|
||||
// will create and format the vhdx's inside and the size is configurable with the sizeInGB
|
||||
// parameter.
|
||||
//
|
||||
// `layerPath` is the path to the base container layer on disk.
|
||||
//
|
||||
// `baseVhdPath` is the path to where the base vhdx for the base layer should be created.
|
||||
//
|
||||
// `diffVhdPath` is the path where the differencing disk for the base layer should be created.
|
||||
//
|
||||
// `sizeInGB` is the size in gigabytes to make the base vhdx.
|
||||
func SetupContainerBaseLayer(ctx context.Context, layerPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { |
||||
var ( |
||||
hivesPath = filepath.Join(layerPath, "Hives") |
||||
layoutPath = filepath.Join(layerPath, "Layout") |
||||
) |
||||
|
||||
// We need to remove the hives directory and layout file as `SetupBaseOSLayer` fails if these files
|
||||
// already exist. `SetupBaseOSLayer` will create these files internally. We also remove the base and
|
||||
// differencing disks if they exist in case we're asking for a different size.
|
||||
if _, err := os.Stat(hivesPath); err == nil { |
||||
if err := os.RemoveAll(hivesPath); err != nil { |
||||
return errors.Wrap(err, "failed to remove prexisting hives directory") |
||||
} |
||||
} |
||||
if _, err := os.Stat(layoutPath); err == nil { |
||||
if err := os.RemoveAll(layoutPath); err != nil { |
||||
return errors.Wrap(err, "failed to remove prexisting layout file") |
||||
} |
||||
} |
||||
|
||||
if _, err := os.Stat(baseVhdPath); err == nil { |
||||
if err := os.RemoveAll(baseVhdPath); err != nil { |
||||
return errors.Wrap(err, "failed to remove base vhdx path") |
||||
} |
||||
} |
||||
if _, err := os.Stat(diffVhdPath); err == nil { |
||||
if err := os.RemoveAll(diffVhdPath); err != nil { |
||||
return errors.Wrap(err, "failed to remove differencing vhdx") |
||||
} |
||||
} |
||||
|
||||
createParams := &vhd.CreateVirtualDiskParameters{ |
||||
Version: 2, |
||||
Version2: vhd.CreateVersion2{ |
||||
MaximumSize: sizeInGB * 1024 * 1024 * 1024, |
||||
BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, |
||||
}, |
||||
} |
||||
handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to create vhdx") |
||||
} |
||||
|
||||
defer func() { |
||||
if err != nil { |
||||
_ = syscall.CloseHandle(handle) |
||||
os.RemoveAll(baseVhdPath) |
||||
os.RemoveAll(diffVhdPath) |
||||
} |
||||
}() |
||||
|
||||
if err = FormatWritableLayerVhd(ctx, windows.Handle(handle)); err != nil { |
||||
return err |
||||
} |
||||
// Base vhd handle must be closed before calling SetupBaseLayer in case of Container layer
|
||||
if err = syscall.CloseHandle(handle); err != nil { |
||||
return errors.Wrap(err, "failed to close vhdx handle") |
||||
} |
||||
|
||||
options := OsLayerOptions{ |
||||
Type: OsLayerTypeContainer, |
||||
} |
||||
|
||||
// SetupBaseOSLayer expects an empty vhd handle for a container layer and will
|
||||
// error out otherwise.
|
||||
if err = SetupBaseOSLayer(ctx, layerPath, 0, options); err != nil { |
||||
return err |
||||
} |
||||
// Create the differencing disk that will be what's copied for the final rw layer
|
||||
// for a container.
|
||||
if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { |
||||
return errors.Wrap(err, "failed to create differencing disk") |
||||
} |
||||
|
||||
if err = security.GrantVmGroupAccess(baseVhdPath); err != nil { |
||||
return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) |
||||
} |
||||
if err = security.GrantVmGroupAccess(diffVhdPath); err != nil { |
||||
return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// SetupUtilityVMBaseLayer is a helper to setup a UVMs scratch space. It will create and format
|
||||
// the vhdx inside and the size is configurable by the sizeInGB parameter.
|
||||
//
|
||||
// `uvmPath` is the path to the UtilityVM filesystem.
|
||||
//
|
||||
// `baseVhdPath` is the path to where the base vhdx for the UVM should be created.
|
||||
//
|
||||
// `diffVhdPath` is the path where the differencing disk for the UVM should be created.
|
||||
//
|
||||
// `sizeInGB` specifies the size in gigabytes to make the base vhdx.
|
||||
func SetupUtilityVMBaseLayer(ctx context.Context, uvmPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { |
||||
// Remove the base and differencing disks if they exist in case we're asking for a different size.
|
||||
if _, err := os.Stat(baseVhdPath); err == nil { |
||||
if err := os.RemoveAll(baseVhdPath); err != nil { |
||||
return errors.Wrap(err, "failed to remove base vhdx") |
||||
} |
||||
} |
||||
if _, err := os.Stat(diffVhdPath); err == nil { |
||||
if err := os.RemoveAll(diffVhdPath); err != nil { |
||||
return errors.Wrap(err, "failed to remove differencing vhdx") |
||||
} |
||||
} |
||||
|
||||
// Just create the vhdx for utilityVM layer, no need to format it.
|
||||
createParams := &vhd.CreateVirtualDiskParameters{ |
||||
Version: 2, |
||||
Version2: vhd.CreateVersion2{ |
||||
MaximumSize: sizeInGB * 1024 * 1024 * 1024, |
||||
BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, |
||||
}, |
||||
} |
||||
handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to create vhdx") |
||||
} |
||||
|
||||
defer func() { |
||||
if err != nil { |
||||
_ = syscall.CloseHandle(handle) |
||||
os.RemoveAll(baseVhdPath) |
||||
os.RemoveAll(diffVhdPath) |
||||
} |
||||
}() |
||||
|
||||
// If it is a UtilityVM layer then the base vhdx must be attached when calling
|
||||
// `SetupBaseOSLayer`
|
||||
attachParams := &vhd.AttachVirtualDiskParameters{ |
||||
Version: 2, |
||||
} |
||||
if err := vhd.AttachVirtualDisk(handle, vhd.AttachVirtualDiskFlagNone, attachParams); err != nil { |
||||
return errors.Wrapf(err, "failed to attach virtual disk") |
||||
} |
||||
|
||||
options := OsLayerOptions{ |
||||
Type: OsLayerTypeVM, |
||||
} |
||||
if err := SetupBaseOSLayer(ctx, uvmPath, windows.Handle(handle), options); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Detach and close the handle after setting up the layer as we don't need the handle
|
||||
// for anything else and we no longer need to be attached either.
|
||||
if err = vhd.DetachVirtualDisk(handle); err != nil { |
||||
return errors.Wrap(err, "failed to detach vhdx") |
||||
} |
||||
if err = syscall.CloseHandle(handle); err != nil { |
||||
return errors.Wrap(err, "failed to close vhdx handle") |
||||
} |
||||
|
||||
// Create the differencing disk that will be what's copied for the final rw layer
|
||||
// for a container.
|
||||
if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { |
||||
return errors.Wrap(err, "failed to create differencing disk") |
||||
} |
||||
|
||||
if err := security.GrantVmGroupAccess(baseVhdPath); err != nil { |
||||
return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) |
||||
} |
||||
if err := security.GrantVmGroupAccess(diffVhdPath); err != nil { |
||||
return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,41 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// ImportLayer imports a container layer.
|
||||
//
|
||||
// `layerPath` is a path to a directory to import the layer to. If the directory
|
||||
// does not exist it will be automatically created.
|
||||
//
|
||||
// `sourceFolderpath` is a pre-existing folder that contains the layer to
|
||||
// import.
|
||||
//
|
||||
// `layerData` is the parent layer data.
|
||||
func ImportLayer(ctx context.Context, layerPath, sourceFolderPath string, layerData LayerData) (err error) { |
||||
title := "hcsshim.ImportLayer" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("layerPath", layerPath), |
||||
trace.StringAttribute("sourceFolderPath", sourceFolderPath), |
||||
) |
||||
|
||||
bytes, err := json.Marshal(layerData) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
err = hcsImportLayer(layerPath, sourceFolderPath, string(bytes)) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to import layer") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,38 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// InitializeWritableLayer initializes a writable layer for a container.
|
||||
//
|
||||
// `layerPath` is a path to a directory the layer is mounted. If the
|
||||
// path does not end in a `\` the platform will append it automatically.
|
||||
//
|
||||
// `layerData` is the parent read-only layer data.
|
||||
func InitializeWritableLayer(ctx context.Context, layerPath string, layerData LayerData) (err error) { |
||||
title := "hcsshim.InitializeWritableLayer" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("layerPath", layerPath), |
||||
) |
||||
|
||||
bytes, err := json.Marshal(layerData) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
// Options are not used in the platform as of RS5
|
||||
err = hcsInitializeWritableLayer(layerPath, string(bytes), "") |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to intitialize container layer") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,27 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/interop" |
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
// GetLayerVhdMountPath returns the volume path for a virtual disk of a writable container layer.
|
||||
func GetLayerVhdMountPath(ctx context.Context, vhdHandle windows.Handle) (path string, err error) { |
||||
title := "hcsshim.GetLayerVhdMountPath" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
|
||||
var mountPath *uint16 |
||||
err = hcsGetLayerVhdMountPath(vhdHandle, &mountPath) |
||||
if err != nil { |
||||
return "", errors.Wrap(err, "failed to get vhd mount path") |
||||
} |
||||
path = interop.ConvertAndFreeCoTaskMemString(mountPath) |
||||
return path, nil |
||||
} |
@ -0,0 +1,74 @@ |
||||
package computestorage |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/Microsoft/hcsshim/osversion" |
||||
"github.com/pkg/errors" |
||||
"go.opencensus.io/trace" |
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
// SetupBaseOSLayer sets up a layer that contains a base OS for a container.
|
||||
//
|
||||
// `layerPath` is a path to a directory containing the layer.
|
||||
//
|
||||
// `vhdHandle` is an empty file handle of `options.Type == OsLayerTypeContainer`
|
||||
// or else it is a file handle to the 'SystemTemplateBase.vhdx' if `options.Type
|
||||
// == OsLayerTypeVm`.
|
||||
//
|
||||
// `options` are the options applied while processing the layer.
|
||||
func SetupBaseOSLayer(ctx context.Context, layerPath string, vhdHandle windows.Handle, options OsLayerOptions) (err error) { |
||||
title := "hcsshim.SetupBaseOSLayer" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("layerPath", layerPath), |
||||
) |
||||
|
||||
bytes, err := json.Marshal(options) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
err = hcsSetupBaseOSLayer(layerPath, vhdHandle, string(bytes)) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to setup base OS layer") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// SetupBaseOSVolume sets up a volume that contains a base OS for a container.
|
||||
//
|
||||
// `layerPath` is a path to a directory containing the layer.
|
||||
//
|
||||
// `volumePath` is the path to the volume to be used for setup.
|
||||
//
|
||||
// `options` are the options applied while processing the layer.
|
||||
func SetupBaseOSVolume(ctx context.Context, layerPath, volumePath string, options OsLayerOptions) (err error) { |
||||
if osversion.Get().Build < 19645 { |
||||
return errors.New("SetupBaseOSVolume is not present on builds older than 19645") |
||||
} |
||||
title := "hcsshim.SetupBaseOSVolume" |
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("layerPath", layerPath), |
||||
trace.StringAttribute("volumePath", volumePath), |
||||
) |
||||
|
||||
bytes, err := json.Marshal(options) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
err = hcsSetupBaseOSVolume(layerPath, volumePath, string(bytes)) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to setup base OS layer") |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,50 @@ |
||||
// Package computestorage is a wrapper around the HCS storage APIs. These are new storage APIs introduced
|
||||
// separate from the original graphdriver calls intended to give more freedom around creating
|
||||
// and managing container layers and scratch spaces.
|
||||
package computestorage |
||||
|
||||
import ( |
||||
hcsschema "github.com/Microsoft/hcsshim/internal/schema2" |
||||
) |
||||
|
||||
//go:generate go run ../mksyscall_windows.go -output zsyscall_windows.go storage.go
|
||||
|
||||
//sys hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) = computestorage.HcsImportLayer?
|
||||
//sys hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) = computestorage.HcsExportLayer?
|
||||
//sys hcsDestroyLayer(layerPath string) (hr error) = computestorage.HcsDestoryLayer?
|
||||
//sys hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) = computestorage.HcsSetupBaseOSLayer?
|
||||
//sys hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) = computestorage.HcsInitializeWritableLayer?
|
||||
//sys hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) = computestorage.HcsAttachLayerStorageFilter?
|
||||
//sys hcsDetachLayerStorageFilter(layerPath string) (hr error) = computestorage.HcsDetachLayerStorageFilter?
|
||||
//sys hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) = computestorage.HcsFormatWritableLayerVhd?
|
||||
//sys hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) = computestorage.HcsGetLayerVhdMountPath?
|
||||
//sys hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) = computestorage.HcsSetupBaseOSVolume?
|
||||
|
||||
// LayerData is the data used to describe parent layer information.
|
||||
type LayerData struct { |
||||
SchemaVersion hcsschema.Version `json:"SchemaVersion,omitempty"` |
||||
Layers []hcsschema.Layer `json:"Layers,omitempty"` |
||||
} |
||||
|
||||
// ExportLayerOptions are the set of options that are used with the `computestorage.HcsExportLayer` syscall.
|
||||
type ExportLayerOptions struct { |
||||
IsWritableLayer bool `json:"IsWritableLayer,omitempty"` |
||||
} |
||||
|
||||
// OsLayerType is the type of layer being operated on.
|
||||
type OsLayerType string |
||||
|
||||
const ( |
||||
// OsLayerTypeContainer is a container layer.
|
||||
OsLayerTypeContainer OsLayerType = "Container" |
||||
// OsLayerTypeVM is a virtual machine layer.
|
||||
OsLayerTypeVM OsLayerType = "Vm" |
||||
) |
||||
|
||||
// OsLayerOptions are the set of options that are used with the `SetupBaseOSLayer` and
|
||||
// `SetupBaseOSVolume` calls.
|
||||
type OsLayerOptions struct { |
||||
Type OsLayerType `json:"Type,omitempty"` |
||||
DisableCiCacheOptimization bool `json:"DisableCiCacheOptimization,omitempty"` |
||||
SkipUpdateBcdForBoot bool `json:"SkipUpdateBcdForBoot,omitempty"` |
||||
} |
319
docgen/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go
generated
vendored
319
docgen/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go
generated
vendored
@ -0,0 +1,319 @@ |
||||
// Code generated mksyscall_windows.exe DO NOT EDIT
|
||||
|
||||
package computestorage |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
var _ unsafe.Pointer |
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const ( |
||||
errnoERROR_IO_PENDING = 997 |
||||
) |
||||
|
||||
var ( |
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
||||
) |
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error { |
||||
switch e { |
||||
case 0: |
||||
return nil |
||||
case errnoERROR_IO_PENDING: |
||||
return errERROR_IO_PENDING |
||||
} |
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e |
||||
} |
||||
|
||||
var ( |
||||
modcomputestorage = windows.NewLazySystemDLL("computestorage.dll") |
||||
|
||||
procHcsImportLayer = modcomputestorage.NewProc("HcsImportLayer") |
||||
procHcsExportLayer = modcomputestorage.NewProc("HcsExportLayer") |
||||
procHcsDestoryLayer = modcomputestorage.NewProc("HcsDestoryLayer") |
||||
procHcsSetupBaseOSLayer = modcomputestorage.NewProc("HcsSetupBaseOSLayer") |
||||
procHcsInitializeWritableLayer = modcomputestorage.NewProc("HcsInitializeWritableLayer") |
||||
procHcsAttachLayerStorageFilter = modcomputestorage.NewProc("HcsAttachLayerStorageFilter") |
||||
procHcsDetachLayerStorageFilter = modcomputestorage.NewProc("HcsDetachLayerStorageFilter") |
||||
procHcsFormatWritableLayerVhd = modcomputestorage.NewProc("HcsFormatWritableLayerVhd") |
||||
procHcsGetLayerVhdMountPath = modcomputestorage.NewProc("HcsGetLayerVhdMountPath") |
||||
procHcsSetupBaseOSVolume = modcomputestorage.NewProc("HcsSetupBaseOSVolume") |
||||
) |
||||
|
||||
func hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, hr = syscall.UTF16PtrFromString(sourceFolderPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p2 *uint16 |
||||
_p2, hr = syscall.UTF16PtrFromString(layerData) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsImportLayer(_p0, _p1, _p2) |
||||
} |
||||
|
||||
func _hcsImportLayer(layerPath *uint16, sourceFolderPath *uint16, layerData *uint16) (hr error) { |
||||
if hr = procHcsImportLayer.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsImportLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(sourceFolderPath)), uintptr(unsafe.Pointer(layerData))) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, hr = syscall.UTF16PtrFromString(exportFolderPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p2 *uint16 |
||||
_p2, hr = syscall.UTF16PtrFromString(layerData) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p3 *uint16 |
||||
_p3, hr = syscall.UTF16PtrFromString(options) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsExportLayer(_p0, _p1, _p2, _p3) |
||||
} |
||||
|
||||
func _hcsExportLayer(layerPath *uint16, exportFolderPath *uint16, layerData *uint16, options *uint16) (hr error) { |
||||
if hr = procHcsExportLayer.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall6(procHcsExportLayer.Addr(), 4, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(exportFolderPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options)), 0, 0) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsDestroyLayer(layerPath string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsDestroyLayer(_p0) |
||||
} |
||||
|
||||
func _hcsDestroyLayer(layerPath *uint16) (hr error) { |
||||
if hr = procHcsDestoryLayer.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsDestoryLayer.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, hr = syscall.UTF16PtrFromString(options) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsSetupBaseOSLayer(_p0, handle, _p1) |
||||
} |
||||
|
||||
func _hcsSetupBaseOSLayer(layerPath *uint16, handle windows.Handle, options *uint16) (hr error) { |
||||
if hr = procHcsSetupBaseOSLayer.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsSetupBaseOSLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(handle), uintptr(unsafe.Pointer(options))) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(writableLayerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, hr = syscall.UTF16PtrFromString(layerData) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p2 *uint16 |
||||
_p2, hr = syscall.UTF16PtrFromString(options) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsInitializeWritableLayer(_p0, _p1, _p2) |
||||
} |
||||
|
||||
func _hcsInitializeWritableLayer(writableLayerPath *uint16, layerData *uint16, options *uint16) (hr error) { |
||||
if hr = procHcsInitializeWritableLayer.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsInitializeWritableLayer.Addr(), 3, uintptr(unsafe.Pointer(writableLayerPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options))) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, hr = syscall.UTF16PtrFromString(layerData) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsAttachLayerStorageFilter(_p0, _p1) |
||||
} |
||||
|
||||
func _hcsAttachLayerStorageFilter(layerPath *uint16, layerData *uint16) (hr error) { |
||||
if hr = procHcsAttachLayerStorageFilter.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsAttachLayerStorageFilter.Addr(), 2, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(layerData)), 0) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsDetachLayerStorageFilter(layerPath string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsDetachLayerStorageFilter(_p0) |
||||
} |
||||
|
||||
func _hcsDetachLayerStorageFilter(layerPath *uint16) (hr error) { |
||||
if hr = procHcsDetachLayerStorageFilter.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsDetachLayerStorageFilter.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) { |
||||
if hr = procHcsFormatWritableLayerVhd.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsFormatWritableLayerVhd.Addr(), 1, uintptr(handle), 0, 0) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) { |
||||
if hr = procHcsGetLayerVhdMountPath.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsGetLayerVhdMountPath.Addr(), 2, uintptr(vhdHandle), uintptr(unsafe.Pointer(mountPath)), 0) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, hr = syscall.UTF16PtrFromString(volumePath) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p2 *uint16 |
||||
_p2, hr = syscall.UTF16PtrFromString(options) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return _hcsSetupBaseOSVolume(_p0, _p1, _p2) |
||||
} |
||||
|
||||
func _hcsSetupBaseOSVolume(layerPath *uint16, volumePath *uint16, options *uint16) (hr error) { |
||||
if hr = procHcsSetupBaseOSVolume.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall(procHcsSetupBaseOSVolume.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(volumePath)), uintptr(unsafe.Pointer(options))) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,223 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"os" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/hcs" |
||||
"github.com/Microsoft/hcsshim/internal/mergemaps" |
||||
"github.com/Microsoft/hcsshim/internal/schema1" |
||||
) |
||||
|
||||
// ContainerProperties holds the properties for a container and the processes running in that container
|
||||
type ContainerProperties = schema1.ContainerProperties |
||||
|
||||
// MemoryStats holds the memory statistics for a container
|
||||
type MemoryStats = schema1.MemoryStats |
||||
|
||||
// ProcessorStats holds the processor statistics for a container
|
||||
type ProcessorStats = schema1.ProcessorStats |
||||
|
||||
// StorageStats holds the storage statistics for a container
|
||||
type StorageStats = schema1.StorageStats |
||||
|
||||
// NetworkStats holds the network statistics for a container
|
||||
type NetworkStats = schema1.NetworkStats |
||||
|
||||
// Statistics is the structure returned by a statistics call on a container
|
||||
type Statistics = schema1.Statistics |
||||
|
||||
// ProcessList is the structure of an item returned by a ProcessList call on a container
|
||||
type ProcessListItem = schema1.ProcessListItem |
||||
|
||||
// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container
|
||||
type MappedVirtualDiskController = schema1.MappedVirtualDiskController |
||||
|
||||
// Type of Request Support in ModifySystem
|
||||
type RequestType = schema1.RequestType |
||||
|
||||
// Type of Resource Support in ModifySystem
|
||||
type ResourceType = schema1.ResourceType |
||||
|
||||
// RequestType const
|
||||
const ( |
||||
Add = schema1.Add |
||||
Remove = schema1.Remove |
||||
Network = schema1.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 = schema1.ResourceModificationRequestResponse |
||||
|
||||
type container struct { |
||||
system *hcs.System |
||||
waitOnce sync.Once |
||||
waitErr error |
||||
waitCh chan struct{} |
||||
} |
||||
|
||||
// createComputeSystemAdditionalJSON is read from the environment at initialisation
|
||||
// time. It allows an environment variable to define additional JSON which
|
||||
// is merged in the CreateComputeSystem call to HCS.
|
||||
var createContainerAdditionalJSON []byte |
||||
|
||||
func init() { |
||||
createContainerAdditionalJSON = ([]byte)(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) { |
||||
fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err) |
||||
} |
||||
|
||||
system, err := hcs.CreateComputeSystem(context.Background(), id, fullConfig) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &container{system: system}, err |
||||
} |
||||
|
||||
// OpenContainer opens an existing container by ID.
|
||||
func OpenContainer(id string) (Container, error) { |
||||
system, err := hcs.OpenComputeSystem(context.Background(), id) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &container{system: system}, err |
||||
} |
||||
|
||||
// GetContainers gets a list of the containers on the system that match the query
|
||||
func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) { |
||||
return hcs.GetComputeSystems(context.Background(), q) |
||||
} |
||||
|
||||
// Start synchronously starts the container.
|
||||
func (container *container) Start() error { |
||||
return convertSystemError(container.system.Start(context.Background()), container) |
||||
} |
||||
|
||||
// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
|
||||
func (container *container) Shutdown() error { |
||||
err := container.system.Shutdown(context.Background()) |
||||
if err != nil { |
||||
return convertSystemError(err, container) |
||||
} |
||||
return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Shutdown"} |
||||
} |
||||
|
||||
// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
|
||||
func (container *container) Terminate() error { |
||||
err := container.system.Terminate(context.Background()) |
||||
if err != nil { |
||||
return convertSystemError(err, container) |
||||
} |
||||
return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Terminate"} |
||||
} |
||||
|
||||
// Waits synchronously waits for the container to shutdown or terminate.
|
||||
func (container *container) Wait() error { |
||||
err := container.system.Wait() |
||||
if err == nil { |
||||
err = container.system.ExitError() |
||||
} |
||||
return convertSystemError(err, container) |
||||
} |
||||
|
||||
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
|
||||
// returns false if timeout occurs.
|
||||
func (container *container) WaitTimeout(timeout time.Duration) error { |
||||
container.waitOnce.Do(func() { |
||||
container.waitCh = make(chan struct{}) |
||||
go func() { |
||||
container.waitErr = container.Wait() |
||||
close(container.waitCh) |
||||
}() |
||||
}) |
||||
t := time.NewTimer(timeout) |
||||
defer t.Stop() |
||||
select { |
||||
case <-t.C: |
||||
return &ContainerError{Container: container, Err: ErrTimeout, Operation: "hcsshim::ComputeSystem::Wait"} |
||||
case <-container.waitCh: |
||||
return container.waitErr |
||||
} |
||||
} |
||||
|
||||
// Pause pauses the execution of a container.
|
||||
func (container *container) Pause() error { |
||||
return convertSystemError(container.system.Pause(context.Background()), container) |
||||
} |
||||
|
||||
// Resume resumes the execution of a container.
|
||||
func (container *container) Resume() error { |
||||
return convertSystemError(container.system.Resume(context.Background()), container) |
||||
} |
||||
|
||||
// HasPendingUpdates returns true if the container has updates pending to install
|
||||
func (container *container) HasPendingUpdates() (bool, error) { |
||||
return false, nil |
||||
} |
||||
|
||||
// Statistics returns statistics for the container. This is a legacy v1 call
|
||||
func (container *container) Statistics() (Statistics, error) { |
||||
properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeStatistics) |
||||
if err != nil { |
||||
return Statistics{}, convertSystemError(err, container) |
||||
} |
||||
|
||||
return properties.Statistics, nil |
||||
} |
||||
|
||||
// ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call
|
||||
func (container *container) ProcessList() ([]ProcessListItem, error) { |
||||
properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeProcessList) |
||||
if err != nil { |
||||
return nil, convertSystemError(err, container) |
||||
} |
||||
|
||||
return properties.ProcessList, nil |
||||
} |
||||
|
||||
// This is a legacy v1 call
|
||||
func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) { |
||||
properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeMappedVirtualDisk) |
||||
if err != nil { |
||||
return nil, convertSystemError(err, container) |
||||
} |
||||
|
||||
return properties.MappedVirtualDiskControllers, nil |
||||
} |
||||
|
||||
// CreateProcess launches a new process within the container.
|
||||
func (container *container) CreateProcess(c *ProcessConfig) (Process, error) { |
||||
p, err := container.system.CreateProcess(context.Background(), c) |
||||
if err != nil { |
||||
return nil, convertSystemError(err, container) |
||||
} |
||||
return &process{p: p.(*hcs.Process)}, nil |
||||
} |
||||
|
||||
// OpenProcess gets an interface to an existing process within the container.
|
||||
func (container *container) OpenProcess(pid int) (Process, error) { |
||||
p, err := container.system.OpenProcess(context.Background(), pid) |
||||
if err != nil { |
||||
return nil, convertSystemError(err, container) |
||||
} |
||||
return &process{p: p}, nil |
||||
} |
||||
|
||||
// Close cleans up any state associated with the container but does not terminate or wait for it.
|
||||
func (container *container) Close() error { |
||||
return convertSystemError(container.system.Close(), container) |
||||
} |
||||
|
||||
// Modify the System
|
||||
func (container *container) Modify(config *ResourceModificationRequestResponse) error { |
||||
return convertSystemError(container.system.Modify(context.Background(), config), container) |
||||
} |
@ -0,0 +1,245 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"fmt" |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/hns" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/hcs" |
||||
"github.com/Microsoft/hcsshim/internal/hcserror" |
||||
) |
||||
|
||||
var ( |
||||
// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists = hcs.exist
|
||||
ErrComputeSystemDoesNotExist = hcs.ErrComputeSystemDoesNotExist |
||||
|
||||
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||
ErrElementNotFound = hcs.ErrElementNotFound |
||||
|
||||
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||
ErrNotSupported = hcs.ErrNotSupported |
||||
|
||||
// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
|
||||
// decimal -2147024883 / hex 0x8007000d
|
||||
ErrInvalidData = hcs.ErrInvalidData |
||||
|
||||
// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
|
||||
ErrHandleClose = hcs.ErrHandleClose |
||||
|
||||
// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
|
||||
ErrAlreadyClosed = hcs.ErrAlreadyClosed |
||||
|
||||
// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
|
||||
ErrInvalidNotificationType = hcs.ErrInvalidNotificationType |
||||
|
||||
// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
|
||||
ErrInvalidProcessState = hcs.ErrInvalidProcessState |
||||
|
||||
// ErrTimeout is an error encountered when waiting on a notification times out
|
||||
ErrTimeout = hcs.ErrTimeout |
||||
|
||||
// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
|
||||
// a different expected notification
|
||||
ErrUnexpectedContainerExit = hcs.ErrUnexpectedContainerExit |
||||
|
||||
// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
|
||||
// is lost while waiting for a notification
|
||||
ErrUnexpectedProcessAbort = hcs.ErrUnexpectedProcessAbort |
||||
|
||||
// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
|
||||
ErrUnexpectedValue = hcs.ErrUnexpectedValue |
||||
|
||||
// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
|
||||
ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped |
||||
|
||||
// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
|
||||
ErrVmcomputeOperationPending = hcs.ErrVmcomputeOperationPending |
||||
|
||||
// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
|
||||
ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState |
||||
|
||||
// ErrProcNotFound is an error encountered when the the process cannot be found
|
||||
ErrProcNotFound = hcs.ErrProcNotFound |
||||
|
||||
// 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 = hcs.ErrVmcomputeOperationAccessIsDenied |
||||
|
||||
// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
|
||||
ErrVmcomputeInvalidJSON = hcs.ErrVmcomputeInvalidJSON |
||||
|
||||
// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
|
||||
ErrVmcomputeUnknownMessage = hcs.ErrVmcomputeUnknownMessage |
||||
|
||||
// ErrNotSupported is an error encountered when hcs doesn't support the request
|
||||
ErrPlatformNotSupported = hcs.ErrPlatformNotSupported |
||||
) |
||||
|
||||
type EndpointNotFoundError = hns.EndpointNotFoundError |
||||
type NetworkNotFoundError = hns.NetworkNotFoundError |
||||
|
||||
// ProcessError is an error encountered in HCS during an operation on a Process object
|
||||
type ProcessError struct { |
||||
Process *process |
||||
Operation string |
||||
Err error |
||||
Events []hcs.ErrorEvent |
||||
} |
||||
|
||||
// ContainerError is an error encountered in HCS during an operation on a Container object
|
||||
type ContainerError struct { |
||||
Container *container |
||||
Operation string |
||||
Err error |
||||
Events []hcs.ErrorEvent |
||||
} |
||||
|
||||
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.system.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, hcserror.Win32FromError(e.Err)) |
||||
default: |
||||
s += fmt.Sprintf(": %s", e.Err.Error()) |
||||
} |
||||
|
||||
for _, ev := range e.Events { |
||||
s += "\n" + ev.String() |
||||
} |
||||
|
||||
return s |
||||
} |
||||
|
||||
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 in container %s", e.Process.p.Pid(), e.Process.p.SystemID()) |
||||
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, hcserror.Win32FromError(e.Err)) |
||||
default: |
||||
s += fmt.Sprintf(": %s", e.Err.Error()) |
||||
} |
||||
|
||||
for _, ev := range e.Events { |
||||
s += "\n" + ev.String() |
||||
} |
||||
|
||||
return s |
||||
} |
||||
|
||||
// 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 { |
||||
if _, ok := err.(EndpointNotFoundError); ok { |
||||
return true |
||||
} |
||||
if _, ok := err.(NetworkNotFoundError); ok { |
||||
return true |
||||
} |
||||
return hcs.IsNotExist(getInnerError(err)) |
||||
} |
||||
|
||||
// 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 { |
||||
return hcs.IsAlreadyClosed(getInnerError(err)) |
||||
} |
||||
|
||||
// IsPending returns a boolean indicating whether the error is that
|
||||
// the requested operation is being completed in the background.
|
||||
func IsPending(err error) bool { |
||||
return hcs.IsPending(getInnerError(err)) |
||||
} |
||||
|
||||
// IsTimeout returns a boolean indicating whether the error is caused by
|
||||
// a timeout waiting for the operation to complete.
|
||||
func IsTimeout(err error) bool { |
||||
return hcs.IsTimeout(getInnerError(err)) |
||||
} |
||||
|
||||
// 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 { |
||||
return hcs.IsAlreadyStopped(getInnerError(err)) |
||||
} |
||||
|
||||
// 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 { |
||||
return hcs.IsNotSupported(getInnerError(err)) |
||||
} |
||||
|
||||
// IsOperationInvalidState returns true when err is caused by
|
||||
// `ErrVmcomputeOperationInvalidState`.
|
||||
func IsOperationInvalidState(err error) bool { |
||||
return hcs.IsOperationInvalidState(getInnerError(err)) |
||||
} |
||||
|
||||
// IsAccessIsDenied returns true when err is caused by
|
||||
// `ErrVmcomputeOperationAccessIsDenied`.
|
||||
func IsAccessIsDenied(err error) bool { |
||||
return hcs.IsAccessIsDenied(getInnerError(err)) |
||||
} |
||||
|
||||
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 |
||||
} |
||||
|
||||
func convertSystemError(err error, c *container) error { |
||||
if serr, ok := err.(*hcs.SystemError); ok { |
||||
return &ContainerError{Container: c, Operation: serr.Op, Err: serr.Err, Events: serr.Events} |
||||
} |
||||
return err |
||||
} |
||||
|
||||
func convertProcessError(err error, p *process) error { |
||||
if perr, ok := err.(*hcs.ProcessError); ok { |
||||
return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events} |
||||
} |
||||
return err |
||||
} |
@ -0,0 +1,12 @@ |
||||
# Requirements so far: |
||||
# dockerd running |
||||
# - image microsoft/nanoserver (matching host base image) docker load -i c:\baseimages\nanoserver.tar |
||||
# - image alpine (linux) docker pull --platform=linux alpine |
||||
|
||||
|
||||
# TODO: Add this a parameter for debugging. ie "functional-tests -debug=$true" |
||||
#$env:HCSSHIM_FUNCTIONAL_TESTS_DEBUG="yes please" |
||||
|
||||
#pushd uvm |
||||
go test -v -tags "functional uvmcreate uvmscratch uvmscsi uvmvpmem uvmvsmb uvmp9" ./... |
||||
#popd |
@ -0,0 +1,22 @@ |
||||
module github.com/Microsoft/hcsshim |
||||
|
||||
go 1.13 |
||||
|
||||
require ( |
||||
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 |
||||
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 |
||||
github.com/containerd/console v1.0.1 |
||||
github.com/containerd/containerd v1.5.0-beta.4 |
||||
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0 |
||||
github.com/containerd/ttrpc v1.0.2 |
||||
github.com/containerd/typeurl v1.0.1 |
||||
github.com/gogo/protobuf v1.3.2 |
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d |
||||
github.com/pkg/errors v0.9.1 |
||||
github.com/sirupsen/logrus v1.7.0 |
||||
github.com/urfave/cli v1.22.2 |
||||
go.opencensus.io v0.22.3 |
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a |
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 |
||||
google.golang.org/grpc v1.33.2 |
||||
) |
@ -0,0 +1,851 @@ |
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= |
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= |
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= |
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= |
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= |
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= |
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= |
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= |
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= |
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= |
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= |
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= |
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= |
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= |
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= |
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= |
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= |
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= |
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= |
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= |
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= |
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= |
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= |
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= |
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= |
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= |
||||
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= |
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= |
||||
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= |
||||
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= |
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= |
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= |
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= |
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= |
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= |
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= |
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= |
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= |
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= |
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= |
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= |
||||
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= |
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= |
||||
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 h1:mw6pDQqv38/WGF1cO/jF5t/jyAJ2yi7CmtFLLO5tGFI= |
||||
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= |
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= |
||||
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= |
||||
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= |
||||
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= |
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= |
||||
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= |
||||
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= |
||||
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= |
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= |
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= |
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= |
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= |
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= |
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= |
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= |
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= |
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= |
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= |
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= |
||||
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= |
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= |
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= |
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= |
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= |
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= |
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= |
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= |
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= |
||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= |
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= |
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= |
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= |
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= |
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= |
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= |
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= |
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= |
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= |
||||
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= |
||||
github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= |
||||
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= |
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= |
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= |
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= |
||||
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= |
||||
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= |
||||
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= |
||||
github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= |
||||
github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= |
||||
github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= |
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= |
||||
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= |
||||
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= |
||||
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= |
||||
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 h1:hkGVFjz+plgr5UfxZUTPFbUFIF/Km6/s+RVRIRHLrrY= |
||||
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= |
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= |
||||
github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= |
||||
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= |
||||
github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc= |
||||
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= |
||||
github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= |
||||
github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= |
||||
github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= |
||||
github.com/containerd/containerd v1.5.0-beta.4 h1:zjz4MOAOFgdBlwid2nNUlJ3YLpVi/97L36lfMYJex60= |
||||
github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= |
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= |
||||
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= |
||||
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= |
||||
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= |
||||
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= |
||||
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg= |
||||
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= |
||||
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= |
||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= |
||||
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= |
||||
github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= |
||||
github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d h1:u6sWqdNGAy7+O8qG/r1dqdnZE7IdEjteK3WGuvbfreo= |
||||
github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= |
||||
github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= |
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= |
||||
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= |
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= |
||||
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0 h1:e+50zk22gvHLJKe8+d+xSMyA88PPQk/XfWuUw1BdnPA= |
||||
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= |
||||
github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= |
||||
github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= |
||||
github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= |
||||
github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= |
||||
github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= |
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= |
||||
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= |
||||
github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= |
||||
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= |
||||
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U= |
||||
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= |
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= |
||||
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= |
||||
github.com/containerd/typeurl v1.0.1 h1:PvuK4E3D5S5q6IqsPDCy928FhP0LUIGcmZ/Yhgp5Djw= |
||||
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= |
||||
github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= |
||||
github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= |
||||
github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= |
||||
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= |
||||
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= |
||||
github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= |
||||
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= |
||||
github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= |
||||
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= |
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= |
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= |
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= |
||||
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= |
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= |
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= |
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= |
||||
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= |
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= |
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= |
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= |
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= |
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= |
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= |
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= |
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= |
||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= |
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= |
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= |
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= |
||||
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= |
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= |
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= |
||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= |
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= |
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= |
||||
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= |
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= |
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= |
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= |
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= |
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= |
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= |
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= |
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= |
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= |
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= |
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= |
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= |
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= |
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= |
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= |
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= |
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= |
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= |
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= |
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= |
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= |
||||
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= |
||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= |
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= |
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= |
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= |
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= |
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= |
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= |
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= |
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= |
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= |
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= |
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= |
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= |
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= |
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= |
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= |
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= |
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= |
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= |
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= |
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= |
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= |
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= |
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= |
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= |
||||
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= |
||||
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= |
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= |
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= |
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= |
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= |
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= |
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= |
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= |
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= |
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= |
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= |
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= |
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= |
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= |
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= |
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= |
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= |
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= |
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= |
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= |
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= |
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= |
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= |
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= |
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= |
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= |
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= |
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= |
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= |
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= |
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= |
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= |
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= |
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= |
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= |
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= |
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= |
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= |
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= |
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= |
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= |
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= |
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= |
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= |
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= |
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= |
||||
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= |
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= |
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= |
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= |
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= |
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= |
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= |
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= |
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= |
||||
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= |
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= |
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= |
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= |
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= |
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= |
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= |
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= |
||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= |
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= |
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= |
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= |
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= |
||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= |
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= |
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= |
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= |
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= |
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= |
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= |
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= |
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= |
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= |
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= |
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= |
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= |
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= |
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= |
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= |
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= |
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= |
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= |
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= |
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= |
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= |
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= |
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= |
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= |
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= |
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= |
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= |
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= |
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= |
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= |
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= |
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= |
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= |
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= |
||||
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= |
||||
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= |
||||
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= |
||||
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= |
||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= |
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= |
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= |
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= |
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= |
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= |
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= |
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= |
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= |
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= |
||||
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= |
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= |
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= |
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= |
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= |
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= |
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= |
||||
github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= |
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= |
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= |
||||
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= |
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= |
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= |
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= |
||||
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= |
||||
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= |
||||
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= |
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= |
||||
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= |
||||
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= |
||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= |
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d h1:pNa8metDkwZjb9g4T8s+krQ+HRgZAkqnXml+wNir/+s= |
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= |
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= |
||||
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= |
||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= |
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= |
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= |
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= |
||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= |
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= |
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= |
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= |
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= |
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= |
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= |
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= |
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= |
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= |
||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= |
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= |
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= |
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= |
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= |
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= |
||||
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= |
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= |
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= |
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= |
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= |
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= |
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= |
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= |
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= |
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= |
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= |
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= |
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= |
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= |
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= |
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= |
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= |
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= |
||||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= |
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= |
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= |
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= |
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= |
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= |
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= |
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= |
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= |
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= |
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= |
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= |
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= |
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= |
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= |
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= |
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= |
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= |
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= |
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= |
||||
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= |
||||
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= |
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= |
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= |
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= |
||||
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= |
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= |
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= |
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= |
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= |
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= |
||||
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= |
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= |
||||
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= |
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= |
||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= |
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= |
||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= |
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= |
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= |
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= |
||||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= |
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= |
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= |
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= |
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= |
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= |
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= |
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= |
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= |
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= |
||||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= |
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= |
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= |
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= |
||||
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= |
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= |
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= |
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= |
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= |
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= |
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= |
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= |
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= |
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= |
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= |
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= |
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= |
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= |
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= |
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= |
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= |
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= |
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= |
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= |
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= |
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= |
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= |
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= |
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= |
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= |
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= |
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= |
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= |
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= |
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= |
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= |
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= |
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= |
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= |
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= |
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= |
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= |
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= |
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= |
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= |
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= |
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= |
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= |
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= |
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= |
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= |
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= |
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= |
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= |
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= |
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= |
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= |
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= |
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= |
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= |
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= |
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= |
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= |
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= |
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= |
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= |
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= |
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= |
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= |
||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= |
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= |
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= |
||||
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= |
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= |
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= |
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= |
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
||||
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= |
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= |
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= |
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= |
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= |
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= |
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= |
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= |
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= |
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= |
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= |
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= |
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= |
||||
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= |
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= |
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= |
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= |
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= |
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= |
||||
google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= |
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= |
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= |
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= |
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= |
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= |
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= |
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= |
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= |
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= |
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= |
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= |
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= |
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= |
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= |
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= |
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= |
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= |
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= |
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= |
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= |
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= |
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= |
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= |
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= |
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= |
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= |
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= |
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= |
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= |
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= |
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= |
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= |
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= |
||||
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= |
||||
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= |
||||
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= |
||||
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= |
||||
k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= |
||||
k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= |
||||
k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= |
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= |
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= |
||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= |
||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= |
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= |
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= |
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= |
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= |
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= |
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= |
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= |
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= |
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= |
@ -0,0 +1,28 @@ |
||||
// Shim for the Host Compute Service (HCS) to manage Windows Server
|
||||
// containers and Hyper-V containers.
|
||||
|
||||
package hcsshim |
||||
|
||||
import ( |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/hcserror" |
||||
) |
||||
|
||||
//go:generate go run mksyscall_windows.go -output zsyscall_windows.go hcsshim.go
|
||||
|
||||
//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId
|
||||
|
||||
const ( |
||||
// Specific user-visible exit codes
|
||||
WaitErrExecFailed = 32767 |
||||
|
||||
ERROR_GEN_FAILURE = hcserror.ERROR_GEN_FAILURE |
||||
ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115) |
||||
WSAEINVAL = syscall.Errno(10022) |
||||
|
||||
// Timeout on wait calls
|
||||
TimeoutInfinite = 0xFFFFFFFF |
||||
) |
||||
|
||||
type HcsError = hcserror.HcsError |
@ -0,0 +1,110 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"github.com/Microsoft/hcsshim/internal/hns" |
||||
) |
||||
|
||||
// HNSEndpoint represents a network endpoint in HNS
|
||||
type HNSEndpoint = hns.HNSEndpoint |
||||
|
||||
// Namespace represents a Compartment.
|
||||
type Namespace = hns.Namespace |
||||
|
||||
//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 = hns.EndpointAttachDetachRequest |
||||
|
||||
// EndpointResquestResponse is object to get the endpoint request response
|
||||
type EndpointResquestResponse = hns.EndpointResquestResponse |
||||
|
||||
// HNSEndpointRequest makes a HNS call to modify/query a network endpoint
|
||||
func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { |
||||
return hns.HNSEndpointRequest(method, path, request) |
||||
} |
||||
|
||||
// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
|
||||
func HNSListEndpointRequest() ([]HNSEndpoint, error) { |
||||
return hns.HNSListEndpointRequest() |
||||
} |
||||
|
||||
// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
|
||||
func HotAttachEndpoint(containerID string, endpointID string) error { |
||||
endpoint, err := GetHNSEndpointByID(endpointID) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
isAttached, err := endpoint.IsAttached(containerID) |
||||
if isAttached { |
||||
return err |
||||
} |
||||
return modifyNetworkEndpoint(containerID, endpointID, Add) |
||||
} |
||||
|
||||
// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
|
||||
func HotDetachEndpoint(containerID string, endpointID string) error { |
||||
endpoint, err := GetHNSEndpointByID(endpointID) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
isAttached, err := endpoint.IsAttached(containerID) |
||||
if !isAttached { |
||||
return err |
||||
} |
||||
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 hns.GetHNSEndpointByID(endpointID) |
||||
} |
||||
|
||||
// GetHNSEndpointByName gets the endpoint filtered by Name
|
||||
func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { |
||||
return hns.GetHNSEndpointByName(endpointName) |
||||
} |
@ -0,0 +1,16 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"github.com/Microsoft/hcsshim/internal/hns" |
||||
) |
||||
|
||||
type HNSGlobals = hns.HNSGlobals |
||||
type HNSVersion = hns.HNSVersion |
||||
|
||||
var ( |
||||
HNSVersion1803 = hns.HNSVersion1803 |
||||
) |
||||
|
||||
func GetHNSGlobals() (*HNSGlobals, error) { |
||||
return hns.GetHNSGlobals() |
||||
} |
@ -0,0 +1,36 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"github.com/Microsoft/hcsshim/internal/hns" |
||||
) |
||||
|
||||
// Subnet is assoicated with a network and represents a list
|
||||
// of subnets available to the network
|
||||
type Subnet = hns.Subnet |
||||
|
||||
// MacPool is assoicated with a network and represents a list
|
||||
// of macaddresses available to the network
|
||||
type MacPool = hns.MacPool |
||||
|
||||
// HNSNetwork represents a network in HNS
|
||||
type HNSNetwork = hns.HNSNetwork |
||||
|
||||
// HNSNetworkRequest makes a call into HNS to update/query a single network
|
||||
func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { |
||||
return hns.HNSNetworkRequest(method, path, request) |
||||
} |
||||
|
||||
// HNSListNetworkRequest makes a HNS call to query the list of available networks
|
||||
func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { |
||||
return hns.HNSListNetworkRequest(method, path, request) |
||||
} |
||||
|
||||
// GetHNSNetworkByID
|
||||
func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { |
||||
return hns.GetHNSNetworkByID(networkID) |
||||
} |
||||
|
||||
// GetHNSNetworkName filtered by Name
|
||||
func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { |
||||
return hns.GetHNSNetworkByName(networkName) |
||||
} |
@ -0,0 +1,60 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"github.com/Microsoft/hcsshim/internal/hns" |
||||
) |
||||
|
||||
// Type of Request Support in ModifySystem
|
||||
type PolicyType = hns.PolicyType |
||||
|
||||
// RequestType const
|
||||
const ( |
||||
Nat = hns.Nat |
||||
ACL = hns.ACL |
||||
PA = hns.PA |
||||
VLAN = hns.VLAN |
||||
VSID = hns.VSID |
||||
VNet = hns.VNet |
||||
L2Driver = hns.L2Driver |
||||
Isolation = hns.Isolation |
||||
QOS = hns.QOS |
||||
OutboundNat = hns.OutboundNat |
||||
ExternalLoadBalancer = hns.ExternalLoadBalancer |
||||
Route = hns.Route |
||||
Proxy = hns.Proxy |
||||
) |
||||
|
||||
type ProxyPolicy = hns.ProxyPolicy |
||||
|
||||
type NatPolicy = hns.NatPolicy |
||||
|
||||
type QosPolicy = hns.QosPolicy |
||||
|
||||
type IsolationPolicy = hns.IsolationPolicy |
||||
|
||||
type VlanPolicy = hns.VlanPolicy |
||||
|
||||
type VsidPolicy = hns.VsidPolicy |
||||
|
||||
type PaPolicy = hns.PaPolicy |
||||
|
||||
type OutboundNatPolicy = hns.OutboundNatPolicy |
||||
|
||||
type ActionType = hns.ActionType |
||||
type DirectionType = hns.DirectionType |
||||
type RuleType = hns.RuleType |
||||
|
||||
const ( |
||||
Allow = hns.Allow |
||||
Block = hns.Block |
||||
|
||||
In = hns.In |
||||
Out = hns.Out |
||||
|
||||
Host = hns.Host |
||||
Switch = hns.Switch |
||||
) |
||||
|
||||
type ACLPolicy = hns.ACLPolicy |
||||
|
||||
type Policy = hns.Policy |
@ -0,0 +1,47 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"github.com/Microsoft/hcsshim/internal/hns" |
||||
) |
||||
|
||||
// RoutePolicy is a structure defining schema for Route based Policy
|
||||
type RoutePolicy = hns.RoutePolicy |
||||
|
||||
// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy
|
||||
type ELBPolicy = hns.ELBPolicy |
||||
|
||||
// LBPolicy is a structure defining schema for LoadBalancing based Policy
|
||||
type LBPolicy = hns.LBPolicy |
||||
|
||||
// PolicyList is a structure defining schema for Policy list request
|
||||
type PolicyList = hns.PolicyList |
||||
|
||||
// HNSPolicyListRequest makes a call into HNS to update/query a single network
|
||||
func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { |
||||
return hns.HNSPolicyListRequest(method, path, request) |
||||
} |
||||
|
||||
// HNSListPolicyListRequest gets all the policy list
|
||||
func HNSListPolicyListRequest() ([]PolicyList, error) { |
||||
return hns.HNSListPolicyListRequest() |
||||
} |
||||
|
||||
// PolicyListRequest makes a HNS call to modify/query a network policy list
|
||||
func PolicyListRequest(method, path, request string) (*PolicyList, error) { |
||||
return hns.PolicyListRequest(method, path, request) |
||||
} |
||||
|
||||
// GetPolicyListByID get the policy list by ID
|
||||
func GetPolicyListByID(policyListID string) (*PolicyList, error) { |
||||
return hns.GetPolicyListByID(policyListID) |
||||
} |
||||
|
||||
// AddLoadBalancer policy list for the specified endpoints
|
||||
func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { |
||||
return hns.AddLoadBalancer(endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) |
||||
} |
||||
|
||||
// AddRoute adds route policy list for the specified endpoints
|
||||
func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { |
||||
return hns.AddRoute(endpoints, destinationPrefix, nextHop, encapEnabled) |
||||
} |
@ -0,0 +1,13 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"github.com/Microsoft/hcsshim/internal/hns" |
||||
) |
||||
|
||||
type HNSSupportedFeatures = hns.HNSSupportedFeatures |
||||
|
||||
type HNSAclFeatures = hns.HNSAclFeatures |
||||
|
||||
func GetHNSSupportedFeatures() HNSSupportedFeatures { |
||||
return hns.GetHNSSupportedFeatures() |
||||
} |
@ -0,0 +1,114 @@ |
||||
package hcsshim |
||||
|
||||
import ( |
||||
"io" |
||||
"time" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/schema1" |
||||
) |
||||
|
||||
// ProcessConfig is used as both the input of Container.CreateProcess
|
||||
// and to convert the parameters to JSON for passing onto the HCS
|
||||
type ProcessConfig = schema1.ProcessConfig |
||||
|
||||
type Layer = schema1.Layer |
||||
type MappedDir = schema1.MappedDir |
||||
type MappedPipe = schema1.MappedPipe |
||||
type HvRuntime = schema1.HvRuntime |
||||
type MappedVirtualDisk = schema1.MappedVirtualDisk |
||||
|
||||
// AssignedDevice represents a device that has been directly assigned to a container
|
||||
//
|
||||
// NOTE: Support added in RS5
|
||||
type AssignedDevice = schema1.AssignedDevice |
||||
|
||||
// ContainerConfig is used as both the input of CreateContainer
|
||||
// and to convert the parameters to JSON for passing onto the HCS
|
||||
type ContainerConfig = schema1.ContainerConfig |
||||
|
||||
type ComputeSystemQuery = schema1.ComputeSystemQuery |
||||
|
||||
// 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,85 @@ |
||||
package cow |
||||
|
||||
import ( |
||||
"context" |
||||
"io" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/schema1" |
||||
hcsschema "github.com/Microsoft/hcsshim/internal/schema2" |
||||
) |
||||
|
||||
// Process is the interface for an OS process running in a container or utility VM.
|
||||
type Process interface { |
||||
// Close releases resources associated with the process and closes the
|
||||
// writer and readers returned by Stdio. Depending on the implementation,
|
||||
// this may also terminate the process.
|
||||
Close() error |
||||
// CloseStdin causes the process's stdin handle to receive EOF/EPIPE/whatever
|
||||
// is appropriate to indicate that no more data is available.
|
||||
CloseStdin(ctx context.Context) error |
||||
// Pid returns the process ID.
|
||||
Pid() int |
||||
// Stdio returns the stdio streams for a process. These may be nil if a stream
|
||||
// was not requested during CreateProcess.
|
||||
Stdio() (_ io.Writer, _ io.Reader, _ io.Reader) |
||||
// ResizeConsole resizes the virtual terminal associated with the process.
|
||||
ResizeConsole(ctx context.Context, width, height uint16) error |
||||
// Kill sends a SIGKILL or equivalent signal to the process and returns whether
|
||||
// the signal was delivered. It does not wait for the process to terminate.
|
||||
Kill(ctx context.Context) (bool, error) |
||||
// Signal sends a signal to the process and returns whether the signal was
|
||||
// delivered. The input is OS specific (either
|
||||
// guestrequest.SignalProcessOptionsWCOW or
|
||||
// guestrequest.SignalProcessOptionsLCOW). It does not wait for the process
|
||||
// to terminate.
|
||||
Signal(ctx context.Context, options interface{}) (bool, error) |
||||
// Wait waits for the process to complete, or for a connection to the process to be
|
||||
// terminated by some error condition (including calling Close).
|
||||
Wait() error |
||||
// ExitCode returns the exit code of the process. Returns an error if the process is
|
||||
// not running.
|
||||
ExitCode() (int, error) |
||||
} |
||||
|
||||
// ProcessHost is the interface for creating processes.
|
||||
type ProcessHost interface { |
||||
// CreateProcess creates a process. The configuration is host specific
|
||||
// (either hcsschema.ProcessParameters or lcow.ProcessParameters).
|
||||
CreateProcess(ctx context.Context, config interface{}) (Process, error) |
||||
// OS returns the host's operating system, "linux" or "windows".
|
||||
OS() string |
||||
// IsOCI specifies whether this is an OCI-compliant process host. If true,
|
||||
// then the configuration passed to CreateProcess should have an OCI process
|
||||
// spec (or nil if this is the initial process in an OCI container).
|
||||
// Otherwise, it should have the HCS-specific process parameters.
|
||||
IsOCI() bool |
||||
} |
||||
|
||||
// Container is the interface for container objects, either running on the host or
|
||||
// in a utility VM.
|
||||
type Container interface { |
||||
ProcessHost |
||||
// Close releases the resources associated with the container. Depending on
|
||||
// the implementation, this may also terminate the container.
|
||||
Close() error |
||||
// ID returns the container ID.
|
||||
ID() string |
||||
// Properties returns the requested container properties targeting a V1 schema container.
|
||||
Properties(ctx context.Context, types ...schema1.PropertyType) (*schema1.ContainerProperties, error) |
||||
// PropertiesV2 returns the requested container properties targeting a V2 schema container.
|
||||
PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (*hcsschema.Properties, error) |
||||
// Start starts a container.
|
||||
Start(ctx context.Context) error |
||||
// Shutdown sends a shutdown request to the container (but does not wait for
|
||||
// the shutdown to complete).
|
||||
Shutdown(ctx context.Context) error |
||||
// Terminate sends a terminate request to the container (but does not wait
|
||||
// for the terminate to complete).
|
||||
Terminate(ctx context.Context) error |
||||
// Wait waits for the container to terminate, or for the connection to the
|
||||
// container to be terminated by some error condition (including calling
|
||||
// Close).
|
||||
Wait() error |
||||
// Modify sends a request to modify container resources
|
||||
Modify(ctx context.Context, config interface{}) error |
||||
} |
@ -0,0 +1,161 @@ |
||||
package hcs |
||||
|
||||
import ( |
||||
"fmt" |
||||
"sync" |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/interop" |
||||
"github.com/Microsoft/hcsshim/internal/logfields" |
||||
"github.com/Microsoft/hcsshim/internal/vmcompute" |
||||
"github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
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 |
||||
hcsNotificationSystemCrashReport hcsNotification = 0x00000006 |
||||
hcsNotificationSystemSiloJobCreated hcsNotification = 0x00000007 |
||||
hcsNotificationSystemSaveCompleted hcsNotification = 0x00000008 |
||||
hcsNotificationSystemRdpEnhancedModeStateChanged hcsNotification = 0x00000009 |
||||
hcsNotificationSystemShutdownFailed hcsNotification = 0x0000000A |
||||
hcsNotificationSystemGetPropertiesCompleted hcsNotification = 0x0000000B |
||||
hcsNotificationSystemModifyCompleted hcsNotification = 0x0000000C |
||||
hcsNotificationSystemCrashInitiated hcsNotification = 0x0000000D |
||||
hcsNotificationSystemGuestConnectionClosed hcsNotification = 0x0000000E |
||||
|
||||
// Notifications for HCS_PROCESS handles
|
||||
hcsNotificationProcessExited hcsNotification = 0x00010000 |
||||
|
||||
// Common notifications
|
||||
hcsNotificationInvalid hcsNotification = 0x00000000 |
||||
hcsNotificationServiceDisconnect hcsNotification = 0x01000000 |
||||
) |
||||
|
||||
type hcsNotification uint32 |
||||
|
||||
func (hn hcsNotification) String() string { |
||||
switch hn { |
||||
case hcsNotificationSystemExited: |
||||
return "SystemExited" |
||||
case hcsNotificationSystemCreateCompleted: |
||||
return "SystemCreateCompleted" |
||||
case hcsNotificationSystemStartCompleted: |
||||
return "SystemStartCompleted" |
||||
case hcsNotificationSystemPauseCompleted: |
||||
return "SystemPauseCompleted" |
||||
case hcsNotificationSystemResumeCompleted: |
||||
return "SystemResumeCompleted" |
||||
case hcsNotificationSystemCrashReport: |
||||
return "SystemCrashReport" |
||||
case hcsNotificationSystemSiloJobCreated: |
||||
return "SystemSiloJobCreated" |
||||
case hcsNotificationSystemSaveCompleted: |
||||
return "SystemSaveCompleted" |
||||
case hcsNotificationSystemRdpEnhancedModeStateChanged: |
||||
return "SystemRdpEnhancedModeStateChanged" |
||||
case hcsNotificationSystemShutdownFailed: |
||||
return "SystemShutdownFailed" |
||||
case hcsNotificationSystemGetPropertiesCompleted: |
||||
return "SystemGetPropertiesCompleted" |
||||
case hcsNotificationSystemModifyCompleted: |
||||
return "SystemModifyCompleted" |
||||
case hcsNotificationSystemCrashInitiated: |
||||
return "SystemCrashInitiated" |
||||
case hcsNotificationSystemGuestConnectionClosed: |
||||
return "SystemGuestConnectionClosed" |
||||
case hcsNotificationProcessExited: |
||||
return "ProcessExited" |
||||
case hcsNotificationInvalid: |
||||
return "Invalid" |
||||
case hcsNotificationServiceDisconnect: |
||||
return "ServiceDisconnect" |
||||
default: |
||||
return fmt.Sprintf("Unknown: %d", hn) |
||||
} |
||||
} |
||||
|
||||
type notificationChannel chan error |
||||
|
||||
type notifcationWatcherContext struct { |
||||
channels notificationChannels |
||||
handle vmcompute.HcsCallback |
||||
|
||||
systemID string |
||||
processID int |
||||
} |
||||
|
||||
type notificationChannels map[hcsNotification]notificationChannel |
||||
|
||||
func newSystemChannels() notificationChannels { |
||||
channels := make(notificationChannels) |
||||
for _, notif := range []hcsNotification{ |
||||
hcsNotificationServiceDisconnect, |
||||
hcsNotificationSystemExited, |
||||
hcsNotificationSystemCreateCompleted, |
||||
hcsNotificationSystemStartCompleted, |
||||
hcsNotificationSystemPauseCompleted, |
||||
hcsNotificationSystemResumeCompleted, |
||||
hcsNotificationSystemSaveCompleted, |
||||
} { |
||||
channels[notif] = make(notificationChannel, 1) |
||||
} |
||||
return channels |
||||
} |
||||
|
||||
func newProcessChannels() notificationChannels { |
||||
channels := make(notificationChannels) |
||||
for _, notif := range []hcsNotification{ |
||||
hcsNotificationServiceDisconnect, |
||||
hcsNotificationProcessExited, |
||||
} { |
||||
channels[notif] = make(notificationChannel, 1) |
||||
} |
||||
return channels |
||||
} |
||||
|
||||
func closeChannels(channels notificationChannels) { |
||||
for _, c := range channels { |
||||
close(c) |
||||
} |
||||
} |
||||
|
||||
func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr { |
||||
var result error |
||||
if int32(notificationStatus) < 0 { |
||||
result = interop.Win32FromHresult(notificationStatus) |
||||
} |
||||
|
||||
callbackMapLock.RLock() |
||||
context := callbackMap[callbackNumber] |
||||
callbackMapLock.RUnlock() |
||||
|
||||
if context == nil { |
||||
return 0 |
||||
} |
||||
|
||||
log := logrus.WithFields(logrus.Fields{ |
||||
"notification-type": notificationType.String(), |
||||
"system-id": context.systemID, |
||||
}) |
||||
if context.processID != 0 { |
||||
log.Data[logfields.ProcessID] = context.processID |
||||
} |
||||
log.Debug("HCS notification") |
||||
|
||||
if channel, ok := context.channels[notificationType]; ok { |
||||
channel <- result |
||||
} |
||||
|
||||
return 0 |
||||
} |
@ -0,0 +1,329 @@ |
||||
package hcs |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"net" |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/log" |
||||
) |
||||
|
||||
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) |
||||
|
||||
// ErrVmcomputeUnexpectedExit is an error encountered when the compute system terminates unexpectedly
|
||||
ErrVmcomputeUnexpectedExit = syscall.Errno(0xC0370106) |
||||
|
||||
// ErrNotSupported is an error encountered when hcs doesn't support the request
|
||||
ErrPlatformNotSupported = errors.New("unsupported platform request") |
||||
) |
||||
|
||||
type ErrorEvent struct { |
||||
Message string `json:"Message,omitempty"` // Fully formated error message
|
||||
StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form
|
||||
Provider string `json:"Provider,omitempty"` |
||||
EventID uint16 `json:"EventId,omitempty"` |
||||
Flags uint32 `json:"Flags,omitempty"` |
||||
Source string `json:"Source,omitempty"` |
||||
//Data []EventData `json:"Data,omitempty"` // Omit this as HCS doesn't encode this well. It's more confusing to include. It is however logged in debug mode (see processHcsResult function)
|
||||
} |
||||
|
||||
type hcsResult struct { |
||||
Error int32 |
||||
ErrorMessage string |
||||
ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"` |
||||
} |
||||
|
||||
func (ev *ErrorEvent) String() string { |
||||
evs := "[Event Detail: " + ev.Message |
||||
if ev.StackTrace != "" { |
||||
evs += " Stack Trace: " + ev.StackTrace |
||||
} |
||||
if ev.Provider != "" { |
||||
evs += " Provider: " + ev.Provider |
||||
} |
||||
if ev.EventID != 0 { |
||||
evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID) |
||||
} |
||||
if ev.Flags != 0 { |
||||
evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags) |
||||
} |
||||
if ev.Source != "" { |
||||
evs += " Source: " + ev.Source |
||||
} |
||||
evs += "]" |
||||
return evs |
||||
} |
||||
|
||||
func processHcsResult(ctx context.Context, resultJSON string) []ErrorEvent { |
||||
if resultJSON != "" { |
||||
result := &hcsResult{} |
||||
if err := json.Unmarshal([]byte(resultJSON), result); err != nil { |
||||
log.G(ctx).WithError(err).Warning("Could not unmarshal HCS result") |
||||
return nil |
||||
} |
||||
return result.ErrorEvents |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
type HcsError struct { |
||||
Op string |
||||
Err error |
||||
Events []ErrorEvent |
||||
} |
||||
|
||||
var _ net.Error = &HcsError{} |
||||
|
||||
func (e *HcsError) Error() string { |
||||
s := e.Op + ": " + e.Err.Error() |
||||
for _, ev := range e.Events { |
||||
s += "\n" + ev.String() |
||||
} |
||||
return s |
||||
} |
||||
|
||||
func (e *HcsError) Temporary() bool { |
||||
err, ok := e.Err.(net.Error) |
||||
return ok && err.Temporary() |
||||
} |
||||
|
||||
func (e *HcsError) Timeout() bool { |
||||
err, ok := e.Err.(net.Error) |
||||
return ok && err.Timeout() |
||||
} |
||||
|
||||
// ProcessError is an error encountered in HCS during an operation on a Process object
|
||||
type ProcessError struct { |
||||
SystemID string |
||||
Pid int |
||||
Op string |
||||
Err error |
||||
Events []ErrorEvent |
||||
} |
||||
|
||||
var _ net.Error = &ProcessError{} |
||||
|
||||
// SystemError is an error encountered in HCS during an operation on a Container object
|
||||
type SystemError struct { |
||||
ID string |
||||
Op string |
||||
Err error |
||||
Events []ErrorEvent |
||||
} |
||||
|
||||
var _ net.Error = &SystemError{} |
||||
|
||||
func (e *SystemError) Error() string { |
||||
s := e.Op + " " + e.ID + ": " + e.Err.Error() |
||||
for _, ev := range e.Events { |
||||
s += "\n" + ev.String() |
||||
} |
||||
return s |
||||
} |
||||
|
||||
func (e *SystemError) Temporary() bool { |
||||
err, ok := e.Err.(net.Error) |
||||
return ok && err.Temporary() |
||||
} |
||||
|
||||
func (e *SystemError) Timeout() bool { |
||||
err, ok := e.Err.(net.Error) |
||||
return ok && err.Timeout() |
||||
} |
||||
|
||||
func makeSystemError(system *System, op string, err error, events []ErrorEvent) error { |
||||
// Don't double wrap errors
|
||||
if _, ok := err.(*SystemError); ok { |
||||
return err |
||||
} |
||||
return &SystemError{ |
||||
ID: system.ID(), |
||||
Op: op, |
||||
Err: err, |
||||
Events: events, |
||||
} |
||||
} |
||||
|
||||
func (e *ProcessError) Error() string { |
||||
s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error()) |
||||
for _, ev := range e.Events { |
||||
s += "\n" + ev.String() |
||||
} |
||||
return s |
||||
} |
||||
|
||||
func (e *ProcessError) Temporary() bool { |
||||
err, ok := e.Err.(net.Error) |
||||
return ok && err.Temporary() |
||||
} |
||||
|
||||
func (e *ProcessError) Timeout() bool { |
||||
err, ok := e.Err.(net.Error) |
||||
return ok && err.Timeout() |
||||
} |
||||
|
||||
func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error { |
||||
// Don't double wrap errors
|
||||
if _, ok := err.(*ProcessError); ok { |
||||
return err |
||||
} |
||||
return &ProcessError{ |
||||
Pid: process.Pid(), |
||||
SystemID: process.SystemID(), |
||||
Op: op, |
||||
Err: err, |
||||
Events: events, |
||||
} |
||||
} |
||||
|
||||
// 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) |
||||
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 { |
||||
if err, ok := err.(net.Error); ok && err.Timeout() { |
||||
return true |
||||
} |
||||
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 |
||||
} |
||||
|
||||
// IsOperationInvalidState returns true when err is caused by
|
||||
// `ErrVmcomputeOperationInvalidState`.
|
||||
func IsOperationInvalidState(err error) bool { |
||||
err = getInnerError(err) |
||||
return err == ErrVmcomputeOperationInvalidState |
||||
} |
||||
|
||||
// IsAccessIsDenied returns true when err is caused by
|
||||
// `ErrVmcomputeOperationAccessIsDenied`.
|
||||
func IsAccessIsDenied(err error) bool { |
||||
err = getInnerError(err) |
||||
return err == ErrVmcomputeOperationAccessIsDenied |
||||
} |
||||
|
||||
func getInnerError(err error) error { |
||||
switch pe := err.(type) { |
||||
case nil: |
||||
return nil |
||||
case *HcsError: |
||||
err = pe.Err |
||||
case *SystemError: |
||||
err = pe.Err |
||||
case *ProcessError: |
||||
err = pe.Err |
||||
} |
||||
return err |
||||
} |
@ -0,0 +1,472 @@ |
||||
package hcs |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"io" |
||||
"sync" |
||||
"syscall" |
||||
"time" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/log" |
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/Microsoft/hcsshim/internal/vmcompute" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// ContainerError is an error encountered in HCS
|
||||
type Process struct { |
||||
handleLock sync.RWMutex |
||||
handle vmcompute.HcsProcess |
||||
processID int |
||||
system *System |
||||
hasCachedStdio bool |
||||
stdioLock sync.Mutex |
||||
stdin io.WriteCloser |
||||
stdout io.ReadCloser |
||||
stderr io.ReadCloser |
||||
callbackNumber uintptr |
||||
|
||||
closedWaitOnce sync.Once |
||||
waitBlock chan struct{} |
||||
exitCode int |
||||
waitError error |
||||
} |
||||
|
||||
func newProcess(process vmcompute.HcsProcess, processID int, computeSystem *System) *Process { |
||||
return &Process{ |
||||
handle: process, |
||||
processID: processID, |
||||
system: computeSystem, |
||||
waitBlock: make(chan struct{}), |
||||
} |
||||
} |
||||
|
||||
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" |
||||
|
||||
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 |
||||
} |
||||
|
||||
// SystemID returns the ID of the process's compute system.
|
||||
func (process *Process) SystemID() string { |
||||
return process.system.ID() |
||||
} |
||||
|
||||
func (process *Process) processSignalResult(ctx context.Context, err error) (bool, error) { |
||||
switch err { |
||||
case nil: |
||||
return true, nil |
||||
case ErrVmcomputeOperationInvalidState, ErrComputeSystemDoesNotExist, ErrElementNotFound: |
||||
select { |
||||
case <-process.waitBlock: |
||||
// The process exit notification has already arrived.
|
||||
default: |
||||
// The process should be gone, but we have not received the notification.
|
||||
// After a second, force unblock the process wait to work around a possible
|
||||
// deadlock in the HCS.
|
||||
go func() { |
||||
time.Sleep(time.Second) |
||||
process.closedWaitOnce.Do(func() { |
||||
log.G(ctx).WithError(err).Warn("force unblocking process waits") |
||||
process.exitCode = -1 |
||||
process.waitError = err |
||||
close(process.waitBlock) |
||||
}) |
||||
}() |
||||
} |
||||
return false, nil |
||||
default: |
||||
return false, err |
||||
} |
||||
} |
||||
|
||||
// Signal signals the process with `options`.
|
||||
//
|
||||
// For LCOW `guestrequest.SignalProcessOptionsLCOW`.
|
||||
//
|
||||
// For WCOW `guestrequest.SignalProcessOptionsWCOW`.
|
||||
func (process *Process) Signal(ctx context.Context, options interface{}) (bool, error) { |
||||
process.handleLock.RLock() |
||||
defer process.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::Process::Signal" |
||||
|
||||
if process.handle == 0 { |
||||
return false, makeProcessError(process, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
optionsb, err := json.Marshal(options) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsSignalProcess(ctx, process.handle, string(optionsb)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
delivered, err := process.processSignalResult(ctx, err) |
||||
if err != nil { |
||||
err = makeProcessError(process, operation, err, events) |
||||
} |
||||
return delivered, err |
||||
} |
||||
|
||||
// Kill signals the process to terminate but does not wait for it to finish terminating.
|
||||
func (process *Process) Kill(ctx context.Context) (bool, error) { |
||||
process.handleLock.RLock() |
||||
defer process.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::Process::Kill" |
||||
|
||||
if process.handle == 0 { |
||||
return false, makeProcessError(process, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsTerminateProcess(ctx, process.handle) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
delivered, err := process.processSignalResult(ctx, err) |
||||
if err != nil { |
||||
err = makeProcessError(process, operation, err, events) |
||||
} |
||||
return delivered, err |
||||
} |
||||
|
||||
// waitBackground waits for the process exit notification. Once received sets
|
||||
// `process.waitError` (if any) and unblocks all `Wait` calls.
|
||||
//
|
||||
// This MUST be called exactly once per `process.handle` but `Wait` is safe to
|
||||
// call multiple times.
|
||||
func (process *Process) waitBackground() { |
||||
operation := "hcsshim::Process::waitBackground" |
||||
ctx, span := trace.StartSpan(context.Background(), operation) |
||||
defer span.End() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("cid", process.SystemID()), |
||||
trace.Int64Attribute("pid", int64(process.processID))) |
||||
|
||||
var ( |
||||
err error |
||||
exitCode = -1 |
||||
propertiesJSON string |
||||
resultJSON string |
||||
) |
||||
|
||||
err = waitForNotification(ctx, process.callbackNumber, hcsNotificationProcessExited, nil) |
||||
if err != nil { |
||||
err = makeProcessError(process, operation, err, nil) |
||||
log.G(ctx).WithError(err).Error("failed wait") |
||||
} else { |
||||
process.handleLock.RLock() |
||||
defer process.handleLock.RUnlock() |
||||
|
||||
// Make sure we didnt race with Close() here
|
||||
if process.handle != 0 { |
||||
propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
err = makeProcessError(process, operation, err, events) //nolint:ineffassign
|
||||
} else { |
||||
properties := &processStatus{} |
||||
err = json.Unmarshal([]byte(propertiesJSON), properties) |
||||
if err != nil { |
||||
err = makeProcessError(process, operation, err, nil) //nolint:ineffassign
|
||||
} else { |
||||
if properties.LastWaitResult != 0 { |
||||
log.G(ctx).WithField("wait-result", properties.LastWaitResult).Warning("non-zero last wait result") |
||||
} else { |
||||
exitCode = int(properties.ExitCode) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
log.G(ctx).WithField("exitCode", exitCode).Debug("process exited") |
||||
|
||||
process.closedWaitOnce.Do(func() { |
||||
process.exitCode = exitCode |
||||
process.waitError = err |
||||
close(process.waitBlock) |
||||
}) |
||||
oc.SetSpanStatus(span, err) |
||||
} |
||||
|
||||
// Wait waits for the process to exit. If the process has already exited returns
|
||||
// the pervious error (if any).
|
||||
func (process *Process) Wait() error { |
||||
<-process.waitBlock |
||||
return process.waitError |
||||
} |
||||
|
||||
// ResizeConsole resizes the console of the process.
|
||||
func (process *Process) ResizeConsole(ctx context.Context, width, height uint16) error { |
||||
process.handleLock.RLock() |
||||
defer process.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::Process::ResizeConsole" |
||||
|
||||
if process.handle == 0 { |
||||
return makeProcessError(process, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
modifyRequest := processModifyRequest{ |
||||
Operation: modifyConsoleSize, |
||||
ConsoleSize: &consoleSize{ |
||||
Height: height, |
||||
Width: width, |
||||
}, |
||||
} |
||||
|
||||
modifyRequestb, err := json.Marshal(modifyRequest) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return makeProcessError(process, operation, err, events) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// ExitCode returns the exit code of the process. The process must have
|
||||
// already terminated.
|
||||
func (process *Process) ExitCode() (int, error) { |
||||
select { |
||||
case <-process.waitBlock: |
||||
if process.waitError != nil { |
||||
return -1, process.waitError |
||||
} |
||||
return process.exitCode, nil |
||||
default: |
||||
return -1, makeProcessError(process, "hcsshim::Process::ExitCode", ErrInvalidProcessState, nil) |
||||
} |
||||
} |
||||
|
||||
// StdioLegacy returns the stdin, stdout, and stderr pipes, respectively. Closing
|
||||
// these pipes does not close the underlying pipes. Once returned, these pipes
|
||||
// are the responsibility of the caller to close.
|
||||
func (process *Process) StdioLegacy() (_ io.WriteCloser, _ io.ReadCloser, _ io.ReadCloser, err error) { |
||||
operation := "hcsshim::Process::StdioLegacy" |
||||
ctx, span := trace.StartSpan(context.Background(), operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("cid", process.SystemID()), |
||||
trace.Int64Attribute("pid", int64(process.processID))) |
||||
|
||||
process.handleLock.RLock() |
||||
defer process.handleLock.RUnlock() |
||||
|
||||
if process.handle == 0 { |
||||
return nil, nil, nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
process.stdioLock.Lock() |
||||
defer process.stdioLock.Unlock() |
||||
if process.hasCachedStdio { |
||||
stdin, stdout, stderr := process.stdin, process.stdout, process.stderr |
||||
process.stdin, process.stdout, process.stderr = nil, nil, nil |
||||
process.hasCachedStdio = false |
||||
return stdin, stdout, stderr, nil |
||||
} |
||||
|
||||
processInfo, resultJSON, err := vmcompute.HcsGetProcessInfo(ctx, process.handle) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, nil, nil, makeProcessError(process, operation, err, events) |
||||
} |
||||
|
||||
pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError}) |
||||
if err != nil { |
||||
return nil, nil, nil, makeProcessError(process, operation, err, nil) |
||||
} |
||||
|
||||
return pipes[0], pipes[1], pipes[2], nil |
||||
} |
||||
|
||||
// Stdio returns the stdin, stdout, and stderr pipes, respectively.
|
||||
// To close them, close the process handle.
|
||||
func (process *Process) Stdio() (stdin io.Writer, stdout, stderr io.Reader) { |
||||
process.stdioLock.Lock() |
||||
defer process.stdioLock.Unlock() |
||||
return process.stdin, process.stdout, process.stderr |
||||
} |
||||
|
||||
// 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(ctx context.Context) error { |
||||
process.handleLock.RLock() |
||||
defer process.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::Process::CloseStdin" |
||||
|
||||
if process.handle == 0 { |
||||
return makeProcessError(process, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
modifyRequest := processModifyRequest{ |
||||
Operation: modifyCloseHandle, |
||||
CloseHandle: &closeHandle{ |
||||
Handle: stdIn, |
||||
}, |
||||
} |
||||
|
||||
modifyRequestb, err := json.Marshal(modifyRequest) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return makeProcessError(process, operation, err, events) |
||||
} |
||||
|
||||
process.stdioLock.Lock() |
||||
if process.stdin != nil { |
||||
process.stdin.Close() |
||||
process.stdin = nil |
||||
} |
||||
process.stdioLock.Unlock() |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Close cleans up any state associated with the process but does not kill
|
||||
// or wait on it.
|
||||
func (process *Process) Close() (err error) { |
||||
operation := "hcsshim::Process::Close" |
||||
ctx, span := trace.StartSpan(context.Background(), operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes( |
||||
trace.StringAttribute("cid", process.SystemID()), |
||||
trace.Int64Attribute("pid", int64(process.processID))) |
||||
|
||||
process.handleLock.Lock() |
||||
defer process.handleLock.Unlock() |
||||
|
||||
// Don't double free this
|
||||
if process.handle == 0 { |
||||
return nil |
||||
} |
||||
|
||||
process.stdioLock.Lock() |
||||
if process.stdin != nil { |
||||
process.stdin.Close() |
||||
process.stdin = nil |
||||
} |
||||
if process.stdout != nil { |
||||
process.stdout.Close() |
||||
process.stdout = nil |
||||
} |
||||
if process.stderr != nil { |
||||
process.stderr.Close() |
||||
process.stderr = nil |
||||
} |
||||
process.stdioLock.Unlock() |
||||
|
||||
if err = process.unregisterCallback(ctx); err != nil { |
||||
return makeProcessError(process, operation, err, nil) |
||||
} |
||||
|
||||
if err = vmcompute.HcsCloseProcess(ctx, process.handle); err != nil { |
||||
return makeProcessError(process, operation, err, nil) |
||||
} |
||||
|
||||
process.handle = 0 |
||||
process.closedWaitOnce.Do(func() { |
||||
process.exitCode = -1 |
||||
process.waitError = ErrAlreadyClosed |
||||
close(process.waitBlock) |
||||
}) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (process *Process) registerCallback(ctx context.Context) error { |
||||
callbackContext := ¬ifcationWatcherContext{ |
||||
channels: newProcessChannels(), |
||||
systemID: process.SystemID(), |
||||
processID: process.processID, |
||||
} |
||||
|
||||
callbackMapLock.Lock() |
||||
callbackNumber := nextCallback |
||||
nextCallback++ |
||||
callbackMap[callbackNumber] = callbackContext |
||||
callbackMapLock.Unlock() |
||||
|
||||
callbackHandle, err := vmcompute.HcsRegisterProcessCallback(ctx, process.handle, notificationWatcherCallback, callbackNumber) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
callbackContext.handle = callbackHandle |
||||
process.callbackNumber = callbackNumber |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (process *Process) unregisterCallback(ctx context.Context) error { |
||||
callbackNumber := process.callbackNumber |
||||
|
||||
callbackMapLock.RLock() |
||||
callbackContext := callbackMap[callbackNumber] |
||||
callbackMapLock.RUnlock() |
||||
|
||||
if callbackContext == nil { |
||||
return nil |
||||
} |
||||
|
||||
handle := callbackContext.handle |
||||
|
||||
if handle == 0 { |
||||
return nil |
||||
} |
||||
|
||||
// vmcompute.HcsUnregisterProcessCallback has its own synchronization to
|
||||
// wait for all callbacks to complete. We must NOT hold the callbackMapLock.
|
||||
err := vmcompute.HcsUnregisterProcessCallback(ctx, handle) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
closeChannels(callbackContext.channels) |
||||
|
||||
callbackMapLock.Lock() |
||||
delete(callbackMap, callbackNumber) |
||||
callbackMapLock.Unlock() |
||||
|
||||
handle = 0 //nolint:ineffassign
|
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,49 @@ |
||||
package hcs |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
|
||||
hcsschema "github.com/Microsoft/hcsshim/internal/schema2" |
||||
"github.com/Microsoft/hcsshim/internal/vmcompute" |
||||
) |
||||
|
||||
// GetServiceProperties returns properties of the host compute service.
|
||||
func GetServiceProperties(ctx context.Context, q hcsschema.PropertyQuery) (*hcsschema.ServiceProperties, error) { |
||||
operation := "hcsshim::GetServiceProperties" |
||||
|
||||
queryb, err := json.Marshal(q) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
propertiesJSON, resultJSON, err := vmcompute.HcsGetServiceProperties(ctx, string(queryb)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, &HcsError{Op: operation, Err: err, Events: events} |
||||
} |
||||
|
||||
if propertiesJSON == "" { |
||||
return nil, ErrUnexpectedValue |
||||
} |
||||
properties := &hcsschema.ServiceProperties{} |
||||
if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { |
||||
return nil, err |
||||
} |
||||
return properties, nil |
||||
} |
||||
|
||||
// ModifyServiceSettings modifies settings of the host compute service.
|
||||
func ModifyServiceSettings(ctx context.Context, settings hcsschema.ModificationRequest) error { |
||||
operation := "hcsshim::ModifyServiceSettings" |
||||
|
||||
settingsJSON, err := json.Marshal(settings) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
resultJSON, err := vmcompute.HcsModifyServiceSettings(ctx, string(settingsJSON)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return &HcsError{Op: operation, Err: err, Events: events} |
||||
} |
||||
return nil |
||||
} |
@ -0,0 +1,637 @@ |
||||
package hcs |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"errors" |
||||
"strings" |
||||
"sync" |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/cow" |
||||
"github.com/Microsoft/hcsshim/internal/log" |
||||
"github.com/Microsoft/hcsshim/internal/oc" |
||||
"github.com/Microsoft/hcsshim/internal/schema1" |
||||
hcsschema "github.com/Microsoft/hcsshim/internal/schema2" |
||||
"github.com/Microsoft/hcsshim/internal/timeout" |
||||
"github.com/Microsoft/hcsshim/internal/vmcompute" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
type System struct { |
||||
handleLock sync.RWMutex |
||||
handle vmcompute.HcsSystem |
||||
id string |
||||
callbackNumber uintptr |
||||
|
||||
closedWaitOnce sync.Once |
||||
waitBlock chan struct{} |
||||
waitError error |
||||
exitError error |
||||
os, typ string |
||||
} |
||||
|
||||
func newSystem(id string) *System { |
||||
return &System{ |
||||
id: id, |
||||
waitBlock: make(chan struct{}), |
||||
} |
||||
} |
||||
|
||||
// CreateComputeSystem creates a new compute system with the given configuration but does not start it.
|
||||
func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface interface{}) (_ *System, err error) { |
||||
operation := "hcsshim::CreateComputeSystem" |
||||
|
||||
// hcsCreateComputeSystemContext is an async operation. Start the outer span
|
||||
// here to measure the full create time.
|
||||
ctx, span := trace.StartSpan(ctx, operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("cid", id)) |
||||
|
||||
computeSystem := newSystem(id) |
||||
|
||||
hcsDocumentB, err := json.Marshal(hcsDocumentInterface) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
hcsDocument := string(hcsDocumentB) |
||||
|
||||
var ( |
||||
identity syscall.Handle |
||||
resultJSON string |
||||
createError error |
||||
) |
||||
computeSystem.handle, resultJSON, createError = vmcompute.HcsCreateComputeSystem(ctx, id, hcsDocument, identity) |
||||
if createError == nil || IsPending(createError) { |
||||
defer func() { |
||||
if err != nil { |
||||
computeSystem.Close() |
||||
} |
||||
}() |
||||
if err = computeSystem.registerCallback(ctx); err != nil { |
||||
// Terminate the compute system if it still exists. We're okay to
|
||||
// ignore a failure here.
|
||||
_ = computeSystem.Terminate(ctx) |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
} |
||||
|
||||
events, err := processAsyncHcsResult(ctx, createError, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate) |
||||
if err != nil { |
||||
if err == ErrTimeout { |
||||
// Terminate the compute system if it still exists. We're okay to
|
||||
// ignore a failure here.
|
||||
_ = computeSystem.Terminate(ctx) |
||||
} |
||||
return nil, makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
go computeSystem.waitBackground() |
||||
if err = computeSystem.getCachedProperties(ctx); err != nil { |
||||
return nil, err |
||||
} |
||||
return computeSystem, nil |
||||
} |
||||
|
||||
// OpenComputeSystem opens an existing compute system by ID.
|
||||
func OpenComputeSystem(ctx context.Context, id string) (*System, error) { |
||||
operation := "hcsshim::OpenComputeSystem" |
||||
|
||||
computeSystem := newSystem(id) |
||||
handle, resultJSON, err := vmcompute.HcsOpenComputeSystem(ctx, id) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
computeSystem.handle = handle |
||||
defer func() { |
||||
if err != nil { |
||||
computeSystem.Close() |
||||
} |
||||
}() |
||||
if err = computeSystem.registerCallback(ctx); err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
go computeSystem.waitBackground() |
||||
if err = computeSystem.getCachedProperties(ctx); err != nil { |
||||
return nil, err |
||||
} |
||||
return computeSystem, nil |
||||
} |
||||
|
||||
func (computeSystem *System) getCachedProperties(ctx context.Context) error { |
||||
props, err := computeSystem.Properties(ctx) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
computeSystem.typ = strings.ToLower(props.SystemType) |
||||
computeSystem.os = strings.ToLower(props.RuntimeOSType) |
||||
if computeSystem.os == "" && computeSystem.typ == "container" { |
||||
// Pre-RS5 HCS did not return the OS, but it only supported containers
|
||||
// that ran Windows.
|
||||
computeSystem.os = "windows" |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// OS returns the operating system of the compute system, "linux" or "windows".
|
||||
func (computeSystem *System) OS() string { |
||||
return computeSystem.os |
||||
} |
||||
|
||||
// IsOCI returns whether processes in the compute system should be created via
|
||||
// OCI.
|
||||
func (computeSystem *System) IsOCI() bool { |
||||
return computeSystem.os == "linux" && computeSystem.typ == "container" |
||||
} |
||||
|
||||
// GetComputeSystems gets a list of the compute systems on the system that match the query
|
||||
func GetComputeSystems(ctx context.Context, q schema1.ComputeSystemQuery) ([]schema1.ContainerProperties, error) { |
||||
operation := "hcsshim::GetComputeSystems" |
||||
|
||||
queryb, err := json.Marshal(q) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
computeSystemsJSON, resultJSON, err := vmcompute.HcsEnumerateComputeSystems(ctx, string(queryb)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, &HcsError{Op: operation, Err: err, Events: events} |
||||
} |
||||
|
||||
if computeSystemsJSON == "" { |
||||
return nil, ErrUnexpectedValue |
||||
} |
||||
computeSystems := []schema1.ContainerProperties{} |
||||
if err = json.Unmarshal([]byte(computeSystemsJSON), &computeSystems); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return computeSystems, nil |
||||
} |
||||
|
||||
// Start synchronously starts the computeSystem.
|
||||
func (computeSystem *System) Start(ctx context.Context) (err error) { |
||||
operation := "hcsshim::System::Start" |
||||
|
||||
// hcsStartComputeSystemContext is an async operation. Start the outer span
|
||||
// here to measure the full start time.
|
||||
ctx, span := trace.StartSpan(ctx, operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) |
||||
|
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsStartComputeSystem(ctx, computeSystem.handle, "") |
||||
events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart) |
||||
if err != nil { |
||||
return makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// ID returns the compute system's identifier.
|
||||
func (computeSystem *System) ID() string { |
||||
return computeSystem.id |
||||
} |
||||
|
||||
// Shutdown requests a compute system shutdown.
|
||||
func (computeSystem *System) Shutdown(ctx context.Context) error { |
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::System::Shutdown" |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return nil |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "") |
||||
events := processHcsResult(ctx, resultJSON) |
||||
switch err { |
||||
case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: |
||||
default: |
||||
return makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// Terminate requests a compute system terminate.
|
||||
func (computeSystem *System) Terminate(ctx context.Context) error { |
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::System::Terminate" |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return nil |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "") |
||||
events := processHcsResult(ctx, resultJSON) |
||||
switch err { |
||||
case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: |
||||
default: |
||||
return makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// waitBackground waits for the compute system exit notification. Once received
|
||||
// sets `computeSystem.waitError` (if any) and unblocks all `Wait` calls.
|
||||
//
|
||||
// This MUST be called exactly once per `computeSystem.handle` but `Wait` is
|
||||
// safe to call multiple times.
|
||||
func (computeSystem *System) waitBackground() { |
||||
operation := "hcsshim::System::waitBackground" |
||||
ctx, span := trace.StartSpan(context.Background(), operation) |
||||
defer span.End() |
||||
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) |
||||
|
||||
err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil) |
||||
switch err { |
||||
case nil: |
||||
log.G(ctx).Debug("system exited") |
||||
case ErrVmcomputeUnexpectedExit: |
||||
log.G(ctx).Debug("unexpected system exit") |
||||
computeSystem.exitError = makeSystemError(computeSystem, operation, err, nil) |
||||
err = nil |
||||
default: |
||||
err = makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
computeSystem.closedWaitOnce.Do(func() { |
||||
computeSystem.waitError = err |
||||
close(computeSystem.waitBlock) |
||||
}) |
||||
oc.SetSpanStatus(span, err) |
||||
} |
||||
|
||||
// Wait synchronously waits for the compute system to shutdown or terminate. If
|
||||
// the compute system has already exited returns the previous error (if any).
|
||||
func (computeSystem *System) Wait() error { |
||||
<-computeSystem.waitBlock |
||||
return computeSystem.waitError |
||||
} |
||||
|
||||
// ExitError returns an error describing the reason the compute system terminated.
|
||||
func (computeSystem *System) ExitError() error { |
||||
select { |
||||
case <-computeSystem.waitBlock: |
||||
if computeSystem.waitError != nil { |
||||
return computeSystem.waitError |
||||
} |
||||
return computeSystem.exitError |
||||
default: |
||||
return errors.New("container not exited") |
||||
} |
||||
} |
||||
|
||||
// Properties returns the requested container properties targeting a V1 schema container.
|
||||
func (computeSystem *System) Properties(ctx context.Context, types ...schema1.PropertyType) (*schema1.ContainerProperties, error) { |
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::System::Properties" |
||||
|
||||
queryBytes, err := json.Marshal(schema1.PropertyQuery{PropertyTypes: types}) |
||||
if err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
|
||||
propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
if propertiesJSON == "" { |
||||
return nil, ErrUnexpectedValue |
||||
} |
||||
properties := &schema1.ContainerProperties{} |
||||
if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
|
||||
return properties, nil |
||||
} |
||||
|
||||
// PropertiesV2 returns the requested container properties targeting a V2 schema container.
|
||||
func (computeSystem *System) PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (*hcsschema.Properties, error) { |
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::System::PropertiesV2" |
||||
|
||||
queryBytes, err := json.Marshal(hcsschema.PropertyQuery{PropertyTypes: types}) |
||||
if err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
|
||||
propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
if propertiesJSON == "" { |
||||
return nil, ErrUnexpectedValue |
||||
} |
||||
properties := &hcsschema.Properties{} |
||||
if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
|
||||
return properties, nil |
||||
} |
||||
|
||||
// Pause pauses the execution of the computeSystem. This feature is not enabled in TP5.
|
||||
func (computeSystem *System) Pause(ctx context.Context) (err error) { |
||||
operation := "hcsshim::System::Pause" |
||||
|
||||
// hcsPauseComputeSystemContext is an async peration. Start the outer span
|
||||
// here to measure the full pause time.
|
||||
ctx, span := trace.StartSpan(ctx, operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) |
||||
|
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsPauseComputeSystem(ctx, computeSystem.handle, "") |
||||
events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause) |
||||
if err != nil { |
||||
return makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Resume resumes the execution of the computeSystem. This feature is not enabled in TP5.
|
||||
func (computeSystem *System) Resume(ctx context.Context) (err error) { |
||||
operation := "hcsshim::System::Resume" |
||||
|
||||
// hcsResumeComputeSystemContext is an async operation. Start the outer span
|
||||
// here to measure the full restore time.
|
||||
ctx, span := trace.StartSpan(ctx, operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) |
||||
|
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
resultJSON, err := vmcompute.HcsResumeComputeSystem(ctx, computeSystem.handle, "") |
||||
events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume) |
||||
if err != nil { |
||||
return makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Save the compute system
|
||||
func (computeSystem *System) Save(ctx context.Context, options interface{}) (err error) { |
||||
operation := "hcsshim::System::Save" |
||||
|
||||
// hcsSaveComputeSystemContext is an async peration. Start the outer span
|
||||
// here to measure the full save time.
|
||||
ctx, span := trace.StartSpan(ctx, operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) |
||||
|
||||
saveOptions, err := json.Marshal(options) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
result, err := vmcompute.HcsSaveComputeSystem(ctx, computeSystem.handle, string(saveOptions)) |
||||
events, err := processAsyncHcsResult(ctx, err, result, computeSystem.callbackNumber, hcsNotificationSystemSaveCompleted, &timeout.SystemSave) |
||||
if err != nil { |
||||
return makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (computeSystem *System) createProcess(ctx context.Context, operation string, c interface{}) (*Process, *vmcompute.HcsProcessInformation, error) { |
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return nil, nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
configurationb, err := json.Marshal(c) |
||||
if err != nil { |
||||
return nil, nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
|
||||
configuration := string(configurationb) |
||||
processInfo, processHandle, resultJSON, err := vmcompute.HcsCreateProcess(ctx, computeSystem.handle, configuration) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, nil, makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
log.G(ctx).WithField("pid", processInfo.ProcessId).Debug("created process pid") |
||||
return newProcess(processHandle, int(processInfo.ProcessId), computeSystem), &processInfo, nil |
||||
} |
||||
|
||||
// CreateProcess launches a new process within the computeSystem.
|
||||
func (computeSystem *System) CreateProcess(ctx context.Context, c interface{}) (cow.Process, error) { |
||||
operation := "hcsshim::System::CreateProcess" |
||||
process, processInfo, err := computeSystem.createProcess(ctx, operation, c) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer func() { |
||||
if err != nil { |
||||
process.Close() |
||||
} |
||||
}() |
||||
|
||||
pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError}) |
||||
if err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
process.stdin = pipes[0] |
||||
process.stdout = pipes[1] |
||||
process.stderr = pipes[2] |
||||
process.hasCachedStdio = true |
||||
|
||||
if err = process.registerCallback(ctx); err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
go process.waitBackground() |
||||
|
||||
return process, nil |
||||
} |
||||
|
||||
// OpenProcess gets an interface to an existing process within the computeSystem.
|
||||
func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process, error) { |
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::System::OpenProcess" |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
processHandle, resultJSON, err := vmcompute.HcsOpenProcess(ctx, computeSystem.handle, uint32(pid)) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
process := newProcess(processHandle, pid, computeSystem) |
||||
if err = process.registerCallback(ctx); err != nil { |
||||
return nil, makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
go process.waitBackground() |
||||
|
||||
return process, nil |
||||
} |
||||
|
||||
// Close cleans up any state associated with the compute system but does not terminate or wait for it.
|
||||
func (computeSystem *System) Close() (err error) { |
||||
operation := "hcsshim::System::Close" |
||||
ctx, span := trace.StartSpan(context.Background(), operation) |
||||
defer span.End() |
||||
defer func() { oc.SetSpanStatus(span, err) }() |
||||
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) |
||||
|
||||
computeSystem.handleLock.Lock() |
||||
defer computeSystem.handleLock.Unlock() |
||||
|
||||
// Don't double free this
|
||||
if computeSystem.handle == 0 { |
||||
return nil |
||||
} |
||||
|
||||
if err = computeSystem.unregisterCallback(ctx); err != nil { |
||||
return makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
|
||||
err = vmcompute.HcsCloseComputeSystem(ctx, computeSystem.handle) |
||||
if err != nil { |
||||
return makeSystemError(computeSystem, operation, err, nil) |
||||
} |
||||
|
||||
computeSystem.handle = 0 |
||||
computeSystem.closedWaitOnce.Do(func() { |
||||
computeSystem.waitError = ErrAlreadyClosed |
||||
close(computeSystem.waitBlock) |
||||
}) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (computeSystem *System) registerCallback(ctx context.Context) error { |
||||
callbackContext := ¬ifcationWatcherContext{ |
||||
channels: newSystemChannels(), |
||||
systemID: computeSystem.id, |
||||
} |
||||
|
||||
callbackMapLock.Lock() |
||||
callbackNumber := nextCallback |
||||
nextCallback++ |
||||
callbackMap[callbackNumber] = callbackContext |
||||
callbackMapLock.Unlock() |
||||
|
||||
callbackHandle, err := vmcompute.HcsRegisterComputeSystemCallback(ctx, computeSystem.handle, notificationWatcherCallback, callbackNumber) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
callbackContext.handle = callbackHandle |
||||
computeSystem.callbackNumber = callbackNumber |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (computeSystem *System) unregisterCallback(ctx context.Context) error { |
||||
callbackNumber := computeSystem.callbackNumber |
||||
|
||||
callbackMapLock.RLock() |
||||
callbackContext := callbackMap[callbackNumber] |
||||
callbackMapLock.RUnlock() |
||||
|
||||
if callbackContext == nil { |
||||
return nil |
||||
} |
||||
|
||||
handle := callbackContext.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 := vmcompute.HcsUnregisterComputeSystemCallback(ctx, handle) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
closeChannels(callbackContext.channels) |
||||
|
||||
callbackMapLock.Lock() |
||||
delete(callbackMap, callbackNumber) |
||||
callbackMapLock.Unlock() |
||||
|
||||
handle = 0 //nolint:ineffassign
|
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Modify the System by sending a request to HCS
|
||||
func (computeSystem *System) Modify(ctx context.Context, config interface{}) error { |
||||
computeSystem.handleLock.RLock() |
||||
defer computeSystem.handleLock.RUnlock() |
||||
|
||||
operation := "hcsshim::System::Modify" |
||||
|
||||
if computeSystem.handle == 0 { |
||||
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) |
||||
} |
||||
|
||||
requestBytes, err := json.Marshal(config) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
requestJSON := string(requestBytes) |
||||
resultJSON, err := vmcompute.HcsModifyComputeSystem(ctx, computeSystem.handle, requestJSON) |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if err != nil { |
||||
return makeSystemError(computeSystem, operation, err, events) |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,62 @@ |
||||
package hcs |
||||
|
||||
import ( |
||||
"context" |
||||
"io" |
||||
"syscall" |
||||
|
||||
"github.com/Microsoft/go-winio" |
||||
diskutil "github.com/Microsoft/go-winio/vhd" |
||||
"github.com/Microsoft/hcsshim/computestorage" |
||||
"github.com/pkg/errors" |
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// CreateNTFSVHD creates a VHD formatted with NTFS of size `sizeGB` at the given `vhdPath`.
|
||||
func CreateNTFSVHD(ctx context.Context, vhdPath string, sizeGB uint32) (err error) { |
||||
if err := diskutil.CreateVhdx(vhdPath, sizeGB, 1); err != nil { |
||||
return errors.Wrap(err, "failed to create VHD") |
||||
} |
||||
|
||||
vhd, err := diskutil.OpenVirtualDisk(vhdPath, diskutil.VirtualDiskAccessNone, diskutil.OpenVirtualDiskFlagNone) |
||||
if err != nil { |
||||
return errors.Wrap(err, "failed to open VHD") |
||||
} |
||||
defer func() { |
||||
err2 := windows.CloseHandle(windows.Handle(vhd)) |
||||
if err == nil { |
||||
err = errors.Wrap(err2, "failed to close VHD") |
||||
} |
||||
}() |
||||
|
||||
if err := computestorage.FormatWritableLayerVhd(ctx, windows.Handle(vhd)); err != nil { |
||||
return errors.Wrap(err, "failed to format VHD") |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,68 @@ |
||||
package hcs |
||||
|
||||
import ( |
||||
"context" |
||||
"time" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/log" |
||||
) |
||||
|
||||
func processAsyncHcsResult(ctx context.Context, err error, resultJSON string, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) ([]ErrorEvent, error) { |
||||
events := processHcsResult(ctx, resultJSON) |
||||
if IsPending(err) { |
||||
return nil, waitForNotification(ctx, callbackNumber, expectedNotification, timeout) |
||||
} |
||||
|
||||
return events, err |
||||
} |
||||
|
||||
func waitForNotification(ctx context.Context, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { |
||||
callbackMapLock.RLock() |
||||
if _, ok := callbackMap[callbackNumber]; !ok { |
||||
callbackMapLock.RUnlock() |
||||
log.G(ctx).WithField("callbackNumber", callbackNumber).Error("failed to waitForNotification: callbackNumber does not exist in callbackMap") |
||||
return ErrHandleClose |
||||
} |
||||
channels := callbackMap[callbackNumber].channels |
||||
callbackMapLock.RUnlock() |
||||
|
||||
expectedChannel := channels[expectedNotification] |
||||
if expectedChannel == nil { |
||||
log.G(ctx).WithField("type", expectedNotification).Error("unknown notification type in waitForNotification") |
||||
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 |
||||
} |
||||
} |
@ -0,0 +1,47 @@ |
||||
package hcserror |
||||
|
||||
import ( |
||||
"fmt" |
||||
"syscall" |
||||
) |
||||
|
||||
const ERROR_GEN_FAILURE = syscall.Errno(31) |
||||
|
||||
type HcsError struct { |
||||
title string |
||||
rest string |
||||
Err error |
||||
} |
||||
|
||||
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 New(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 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) |
||||
} |
@ -0,0 +1,23 @@ |
||||
package hns |
||||
|
||||
import "fmt" |
||||
|
||||
//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hns.go
|
||||
|
||||
//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall?
|
||||
|
||||
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) |
||||
} |
@ -0,0 +1,308 @@ |
||||
package hns |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"net" |
||||
"strings" |
||||
|
||||
"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"` |
||||
IPv6Address net.IP `json:",omitempty"` |
||||
DNSSuffix string `json:",omitempty"` |
||||
DNSServerList string `json:",omitempty"` |
||||
GatewayAddress string `json:",omitempty"` |
||||
GatewayAddressV6 string `json:",omitempty"` |
||||
EnableInternalDNS bool `json:",omitempty"` |
||||
DisableICC bool `json:",omitempty"` |
||||
PrefixLength uint8 `json:",omitempty"` |
||||
IPv6PrefixLength uint8 `json:",omitempty"` |
||||
IsRemoteEndpoint bool `json:",omitempty"` |
||||
EnableLowMetric bool `json:",omitempty"` |
||||
Namespace *Namespace `json:",omitempty"` |
||||
EncapOverhead uint16 `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 |
||||
} |
||||
|
||||
// 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} |
||||
} |
||||
|
||||
type endpointAttachInfo struct { |
||||
SharedContainers json.RawMessage `json:",omitempty"` |
||||
} |
||||
|
||||
func (endpoint *HNSEndpoint) IsAttached(vID string) (bool, error) { |
||||
attachInfo := endpointAttachInfo{} |
||||
err := hnsCall("GET", "/endpoints/"+endpoint.Id, "", &attachInfo) |
||||
|
||||
// Return false allows us to just return the err
|
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
|
||||
if strings.Contains(strings.ToLower(string(attachInfo.SharedContainers)), strings.ToLower(vID)) { |
||||
return true, nil |
||||
} |
||||
|
||||
return false, nil |
||||
|
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// ApplyProxyPolicy applies a set of Proxy Policies on the Endpoint
|
||||
func (endpoint *HNSEndpoint) ApplyProxyPolicy(policies ...*ProxyPolicy) error { |
||||
operation := "ApplyProxyPolicy" |
||||
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,49 @@ |
||||
package hns |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/hcserror" |
||||
"github.com/Microsoft/hcsshim/internal/interop" |
||||
"github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
func hnsCallRawResponse(method, path, request string) (*hnsResponse, error) { |
||||
var responseBuffer *uint16 |
||||
logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request) |
||||
|
||||
err := _hnsCall(method, path, request, &responseBuffer) |
||||
if err != nil { |
||||
return nil, hcserror.New(err, "hnsCall ", "") |
||||
} |
||||
response := interop.ConvertAndFreeCoTaskMemString(responseBuffer) |
||||
|
||||
hnsresponse := &hnsResponse{} |
||||
if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil { |
||||
return nil, err |
||||
} |
||||
return hnsresponse, nil |
||||
} |
||||
|
||||
func hnsCall(method, path, request string, returnResponse interface{}) error { |
||||
hnsresponse, err := hnsCallRawResponse(method, path, request) |
||||
if err != nil { |
||||
return fmt.Errorf("failed during hnsCallRawResponse: %v", 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,28 @@ |
||||
package hns |
||||
|
||||
type HNSGlobals struct { |
||||
Version HNSVersion `json:"Version"` |
||||
} |
||||
|
||||
type HNSVersion struct { |
||||
Major int `json:"Major"` |
||||
Minor int `json:"Minor"` |
||||
} |
||||
|
||||
var ( |
||||
HNSVersion1803 = HNSVersion{Major: 7, Minor: 2} |
||||
) |
||||
|
||||
func GetHNSGlobals() (*HNSGlobals, error) { |
||||
var version HNSVersion |
||||
err := hnsCall("GET", "/globals/version", "", &version) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
globals := &HNSGlobals{ |
||||
Version: version, |
||||
} |
||||
|
||||
return globals, nil |
||||
} |
@ -0,0 +1,141 @@ |
||||
package hns |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"github.com/sirupsen/logrus" |
||||
"net" |
||||
) |
||||
|
||||
// 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 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) |
||||
|
||||
for _, subnet := range network.Subnets { |
||||
if (subnet.AddressPrefix != "") && (subnet.GatewayAddress == "") { |
||||
return nil, errors.New("network create error, subnet has address prefix but no gateway specified") |
||||
} |
||||
} |
||||
|
||||
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,109 @@ |
||||
package hns |
||||
|
||||
// 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" |
||||
Proxy PolicyType = "PROXY" |
||||
) |
||||
|
||||
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"` |
||||
Destinations []string `json:",omitempty"` |
||||
} |
||||
|
||||
type ProxyPolicy struct { |
||||
Type PolicyType `json:"Type"` |
||||
IP string `json:",omitempty"` |
||||
Port string `json:",omitempty"` |
||||
ExceptionList []string `json:",omitempty"` |
||||
Destination string `json:",omitempty"` |
||||
OutboundNat bool `json:",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"` |
||||
Id string `json:"Id,omitempty"` |
||||
Protocol uint16 |
||||
Protocols string `json:"Protocols,omitempty"` |
||||
InternalPort uint16 |
||||
Action ActionType |
||||
Direction DirectionType |
||||
LocalAddresses string |
||||
RemoteAddresses string |
||||
LocalPorts string `json:"LocalPorts,omitempty"` |
||||
LocalPort uint16 |
||||
RemotePorts string `json:"RemotePorts,omitempty"` |
||||
RemotePort uint16 |
||||
RuleType RuleType `json:"RuleType,omitempty"` |
||||
Priority uint16 |
||||
ServiceName string |
||||
} |
||||
|
||||
type Policy struct { |
||||
Type PolicyType `json:"Type"` |
||||
} |
@ -0,0 +1,201 @@ |
||||
package hns |
||||
|
||||
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"` |
||||
DSR bool `json:"IsDSR,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,49 @@ |
||||
package hns |
||||
|
||||
import ( |
||||
"github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
type HNSSupportedFeatures struct { |
||||
Acl HNSAclFeatures `json:"ACL"` |
||||
} |
||||
|
||||
type HNSAclFeatures struct { |
||||
AclAddressLists bool `json:"AclAddressLists"` |
||||
AclNoHostRulePriority bool `json:"AclHostRulePriority"` |
||||
AclPortRanges bool `json:"AclPortRanges"` |
||||
AclRuleId bool `json:"AclRuleId"` |
||||
} |
||||
|
||||
func GetHNSSupportedFeatures() HNSSupportedFeatures { |
||||
var hnsFeatures HNSSupportedFeatures |
||||
|
||||
globals, err := GetHNSGlobals() |
||||
if err != nil { |
||||
// Expected on pre-1803 builds, all features will be false/unsupported
|
||||
logrus.Debugf("Unable to obtain HNS globals: %s", err) |
||||
return hnsFeatures |
||||
} |
||||
|
||||
hnsFeatures.Acl = HNSAclFeatures{ |
||||
AclAddressLists: isHNSFeatureSupported(globals.Version, HNSVersion1803), |
||||
AclNoHostRulePriority: isHNSFeatureSupported(globals.Version, HNSVersion1803), |
||||
AclPortRanges: isHNSFeatureSupported(globals.Version, HNSVersion1803), |
||||
AclRuleId: isHNSFeatureSupported(globals.Version, HNSVersion1803), |
||||
} |
||||
|
||||
return hnsFeatures |
||||
} |
||||
|
||||
func isHNSFeatureSupported(currentVersion HNSVersion, minVersionSupported HNSVersion) bool { |
||||
if currentVersion.Major < minVersionSupported.Major { |
||||
return false |
||||
} |
||||
if currentVersion.Major > minVersionSupported.Major { |
||||
return true |
||||
} |
||||
if currentVersion.Minor < minVersionSupported.Minor { |
||||
return false |
||||
} |
||||
return true |
||||
} |
@ -0,0 +1,111 @@ |
||||
package hns |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"os" |
||||
"path" |
||||
"strings" |
||||
) |
||||
|
||||
type namespaceRequest struct { |
||||
IsDefault bool `json:",omitempty"` |
||||
} |
||||
|
||||
type namespaceEndpointRequest struct { |
||||
ID string `json:"Id"` |
||||
} |
||||
|
||||
type NamespaceResource struct { |
||||
Type string |
||||
Data json.RawMessage |
||||
} |
||||
|
||||
type namespaceResourceRequest struct { |
||||
Type string |
||||
Data interface{} |
||||
} |
||||
|
||||
type Namespace struct { |
||||
ID string |
||||
IsDefault bool `json:",omitempty"` |
||||
ResourceList []NamespaceResource `json:",omitempty"` |
||||
CompartmentId uint32 `json:",omitempty"` |
||||
} |
||||
|
||||
func issueNamespaceRequest(id *string, method, subpath string, request interface{}) (*Namespace, error) { |
||||
var err error |
||||
hnspath := "/namespaces/" |
||||
if id != nil { |
||||
hnspath = path.Join(hnspath, *id) |
||||
} |
||||
if subpath != "" { |
||||
hnspath = path.Join(hnspath, subpath) |
||||
} |
||||
var reqJSON []byte |
||||
if request != nil { |
||||
if reqJSON, err = json.Marshal(request); err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
var ns Namespace |
||||
err = hnsCall(method, hnspath, string(reqJSON), &ns) |
||||
if err != nil { |
||||
if strings.Contains(err.Error(), "Element not found.") { |
||||
return nil, os.ErrNotExist |
||||
} |
||||
return nil, fmt.Errorf("%s %s: %s", method, hnspath, err) |
||||
} |
||||
return &ns, err |
||||
} |
||||
|
||||
func CreateNamespace() (string, error) { |
||||
req := namespaceRequest{} |
||||
ns, err := issueNamespaceRequest(nil, "POST", "", &req) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
return ns.ID, nil |
||||
} |
||||
|
||||
func RemoveNamespace(id string) error { |
||||
_, err := issueNamespaceRequest(&id, "DELETE", "", nil) |
||||
return err |
||||
} |
||||
|
||||
func GetNamespaceEndpoints(id string) ([]string, error) { |
||||
ns, err := issueNamespaceRequest(&id, "GET", "", nil) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
var endpoints []string |
||||
for _, rsrc := range ns.ResourceList { |
||||
if rsrc.Type == "Endpoint" { |
||||
var endpoint namespaceEndpointRequest |
||||
err = json.Unmarshal(rsrc.Data, &endpoint) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("unmarshal endpoint: %s", err) |
||||
} |
||||
endpoints = append(endpoints, endpoint.ID) |
||||
} |
||||
} |
||||
return endpoints, nil |
||||
} |
||||
|
||||
func AddNamespaceEndpoint(id string, endpointID string) error { |
||||
resource := namespaceResourceRequest{ |
||||
Type: "Endpoint", |
||||
Data: namespaceEndpointRequest{endpointID}, |
||||
} |
||||
_, err := issueNamespaceRequest(&id, "POST", "addresource", &resource) |
||||
return err |
||||
} |
||||
|
||||
func RemoveNamespaceEndpoint(id string, endpointID string) error { |
||||
resource := namespaceResourceRequest{ |
||||
Type: "Endpoint", |
||||
Data: namespaceEndpointRequest{endpointID}, |
||||
} |
||||
_, err := issueNamespaceRequest(&id, "POST", "removeresource", &resource) |
||||
return err |
||||
} |
@ -0,0 +1,76 @@ |
||||
// Code generated mksyscall_windows.exe DO NOT EDIT
|
||||
|
||||
package hns |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
var _ unsafe.Pointer |
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const ( |
||||
errnoERROR_IO_PENDING = 997 |
||||
) |
||||
|
||||
var ( |
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
||||
) |
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error { |
||||
switch e { |
||||
case 0: |
||||
return nil |
||||
case errnoERROR_IO_PENDING: |
||||
return errERROR_IO_PENDING |
||||
} |
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e |
||||
} |
||||
|
||||
var ( |
||||
modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") |
||||
|
||||
procHNSCall = modvmcompute.NewProc("HNSCall") |
||||
) |
||||
|
||||
func _hnsCall(method string, path string, object string, response **uint16) (hr error) { |
||||
var _p0 *uint16 |
||||
_p0, hr = syscall.UTF16PtrFromString(method) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p1 *uint16 |
||||
_p1, hr = syscall.UTF16PtrFromString(path) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
var _p2 *uint16 |
||||
_p2, hr = syscall.UTF16PtrFromString(object) |
||||
if hr != nil { |
||||
return |
||||
} |
||||
return __hnsCall(_p0, _p1, _p2, response) |
||||
} |
||||
|
||||
func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) (hr error) { |
||||
if hr = procHNSCall.Find(); hr != nil { |
||||
return |
||||
} |
||||
r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0) |
||||
if int32(r0) < 0 { |
||||
if r0&0x1fff0000 == 0x00070000 { |
||||
r0 &= 0xffff |
||||
} |
||||
hr = syscall.Errno(r0) |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,23 @@ |
||||
package interop |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
) |
||||
|
||||
//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go interop.go
|
||||
|
||||
//sys coTaskMemFree(buffer unsafe.Pointer) = api_ms_win_core_com_l1_1_0.CoTaskMemFree
|
||||
|
||||
func ConvertAndFreeCoTaskMemString(buffer *uint16) string { |
||||
str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:]) |
||||
coTaskMemFree(unsafe.Pointer(buffer)) |
||||
return str |
||||
} |
||||
|
||||
func Win32FromHresult(hr uintptr) syscall.Errno { |
||||
if hr&0x1fff0000 == 0x00070000 { |
||||
return syscall.Errno(hr & 0xffff) |
||||
} |
||||
return syscall.Errno(hr) |
||||
} |
48
docgen/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go
generated
vendored
48
docgen/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go
generated
vendored
@ -0,0 +1,48 @@ |
||||
// Code generated mksyscall_windows.exe DO NOT EDIT
|
||||
|
||||
package interop |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
var _ unsafe.Pointer |
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const ( |
||||
errnoERROR_IO_PENDING = 997 |
||||
) |
||||
|
||||
var ( |
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
||||
) |
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error { |
||||
switch e { |
||||
case 0: |
||||
return nil |
||||
case errnoERROR_IO_PENDING: |
||||
return errERROR_IO_PENDING |
||||
} |
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e |
||||
} |
||||
|
||||
var ( |
||||
modapi_ms_win_core_com_l1_1_0 = windows.NewLazySystemDLL("api-ms-win-core-com-l1-1-0.dll") |
||||
|
||||
procCoTaskMemFree = modapi_ms_win_core_com_l1_1_0.NewProc("CoTaskMemFree") |
||||
) |
||||
|
||||
func coTaskMemFree(buffer unsafe.Pointer) { |
||||
syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(buffer), 0, 0) |
||||
return |
||||
} |
@ -0,0 +1,23 @@ |
||||
package log |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/sirupsen/logrus" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// G returns a `logrus.Entry` with the `TraceID, SpanID` from `ctx` if `ctx`
|
||||
// contains an OpenCensus `trace.Span`.
|
||||
func G(ctx context.Context) *logrus.Entry { |
||||
span := trace.FromContext(ctx) |
||||
if span != nil { |
||||
sctx := span.SpanContext() |
||||
return logrus.WithFields(logrus.Fields{ |
||||
"traceID": sctx.TraceID.String(), |
||||
"spanID": sctx.SpanID.String(), |
||||
// "parentSpanID": TODO: JTERRY75 - Try to convince OC to export this?
|
||||
}) |
||||
} |
||||
return logrus.NewEntry(logrus.StandardLogger()) |
||||
} |
@ -0,0 +1,32 @@ |
||||
package logfields |
||||
|
||||
const ( |
||||
// Identifiers
|
||||
|
||||
ContainerID = "cid" |
||||
UVMID = "uvm-id" |
||||
ProcessID = "pid" |
||||
|
||||
// Common Misc
|
||||
|
||||
// Timeout represents an operation timeout.
|
||||
Timeout = "timeout" |
||||
JSON = "json" |
||||
|
||||
// Keys/values
|
||||
|
||||
Field = "field" |
||||
OCIAnnotation = "oci-annotation" |
||||
Value = "value" |
||||
|
||||
// Golang type's
|
||||
|
||||
ExpectedType = "expected-type" |
||||
Bool = "bool" |
||||
Uint32 = "uint32" |
||||
Uint64 = "uint64" |
||||
|
||||
// runhcs
|
||||
|
||||
VMShimOperation = "vmshim-op" |
||||
) |
@ -0,0 +1,24 @@ |
||||
package longpath |
||||
|
||||
import ( |
||||
"path/filepath" |
||||
"strings" |
||||
) |
||||
|
||||
// LongAbs makes a path absolute and returns it in NT long path form.
|
||||
func LongAbs(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 |
||||
} |
@ -0,0 +1,52 @@ |
||||
package mergemaps |
||||
|
||||
import "encoding/json" |
||||
|
||||
// Merge 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 Merge(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] = Merge(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 |
||||
} |
||||
|
||||
// MergeJSON merges the contents of a JSON string into an object representation,
|
||||
// returning a new object suitable for translating to JSON.
|
||||
func MergeJSON(object interface{}, additionalJSON []byte) (interface{}, error) { |
||||
if len(additionalJSON) == 0 { |
||||
return object, nil |
||||
} |
||||
objectJSON, err := json.Marshal(object) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
var objectMap, newMap map[string]interface{} |
||||
err = json.Unmarshal(objectJSON, &objectMap) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
err = json.Unmarshal(additionalJSON, &newMap) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return Merge(newMap, objectMap), nil |
||||
} |
@ -0,0 +1,43 @@ |
||||
package oc |
||||
|
||||
import ( |
||||
"github.com/sirupsen/logrus" |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
var _ = (trace.Exporter)(&LogrusExporter{}) |
||||
|
||||
// LogrusExporter is an OpenCensus `trace.Exporter` that exports
|
||||
// `trace.SpanData` to logrus output.
|
||||
type LogrusExporter struct { |
||||
} |
||||
|
||||
// ExportSpan exports `s` based on the the following rules:
|
||||
//
|
||||
// 1. All output will contain `s.Attributes`, `s.TraceID`, `s.SpanID`,
|
||||
// `s.ParentSpanID` for correlation
|
||||
//
|
||||
// 2. Any calls to .Annotate will not be supported.
|
||||
//
|
||||
// 3. The span itself will be written at `logrus.InfoLevel` unless
|
||||
// `s.Status.Code != 0` in which case it will be written at `logrus.ErrorLevel`
|
||||
// providing `s.Status.Message` as the error value.
|
||||
func (le *LogrusExporter) ExportSpan(s *trace.SpanData) { |
||||
// Combine all span annotations with traceID, spanID, parentSpanID
|
||||
baseEntry := logrus.WithFields(logrus.Fields(s.Attributes)) |
||||
baseEntry.Data["traceID"] = s.TraceID.String() |
||||
baseEntry.Data["spanID"] = s.SpanID.String() |
||||
baseEntry.Data["parentSpanID"] = s.ParentSpanID.String() |
||||
baseEntry.Data["startTime"] = s.StartTime |
||||
baseEntry.Data["endTime"] = s.EndTime |
||||
baseEntry.Data["duration"] = s.EndTime.Sub(s.StartTime).String() |
||||
baseEntry.Data["name"] = s.Name |
||||
baseEntry.Time = s.StartTime |
||||
|
||||
level := logrus.InfoLevel |
||||
if s.Status.Code != 0 { |
||||
level = logrus.ErrorLevel |
||||
baseEntry.Data[logrus.ErrorKey] = s.Status.Message |
||||
} |
||||
baseEntry.Log(level, "Span") |
||||
} |
@ -0,0 +1,17 @@ |
||||
package oc |
||||
|
||||
import ( |
||||
"go.opencensus.io/trace" |
||||
) |
||||
|
||||
// SetSpanStatus sets `span.SetStatus` to the proper status depending on `err`. If
|
||||
// `err` is `nil` assumes `trace.StatusCodeOk`.
|
||||
func SetSpanStatus(span *trace.Span, err error) { |
||||
status := trace.Status{} |
||||
if err != nil { |
||||
// TODO: JTERRY75 - Handle errors in a non-generic way
|
||||
status.Code = trace.StatusCodeUnknown |
||||
status.Message = err.Error() |
||||
} |
||||
span.SetStatus(status) |
||||
} |
@ -0,0 +1,375 @@ |
||||
package safefile |
||||
|
||||
import ( |
||||
"errors" |
||||
"io" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
"syscall" |
||||
"unicode/utf16" |
||||
"unsafe" |
||||
|
||||
"github.com/Microsoft/hcsshim/internal/longpath" |
||||
"github.com/Microsoft/hcsshim/internal/winapi" |
||||
|
||||
winio "github.com/Microsoft/go-winio" |
||||
) |
||||
|
||||
func OpenRoot(path string) (*os.File, error) { |
||||
longpath, err := longpath.LongAbs(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 cleanGoStringRelativePath(path string) (string, 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 "", errors.New("path contains invalid character `:`") |
||||
} |
||||
fspath := filepath.FromSlash(path) |
||||
if len(fspath) > 0 && fspath[0] == '\\' { |
||||
return "", errors.New("expected relative path") |
||||
} |
||||
return fspath, nil |
||||
} |
||||
|
||||
func ntRelativePath(path string) ([]uint16, error) { |
||||
fspath, err := cleanGoStringRelativePath(path) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
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 winapi.IOStatusBlock |
||||
oa winapi.ObjectAttributes |
||||
) |
||||
|
||||
cleanRelativePath, err := cleanGoStringRelativePath(path) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
if root == nil || root.Fd() == 0 { |
||||
return nil, errors.New("missing root directory") |
||||
} |
||||
|
||||
pathUnicode, err := winapi.NewUnicodeString(cleanRelativePath) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
oa.Length = unsafe.Sizeof(oa) |
||||
oa.ObjectName = pathUnicode |
||||
oa.RootDirectory = uintptr(root.Fd()) |
||||
oa.Attributes = winapi.OBJ_DONT_REPARSE |
||||
status := winapi.NtCreateFile( |
||||
&h, |
||||
accessMask|syscall.SYNCHRONIZE, |
||||
&oa, |
||||
&iosb, |
||||
nil, |
||||
0, |
||||
shareFlags, |
||||
createDisposition, |
||||
winapi.FILE_OPEN_FOR_BACKUP_INTENT|winapi.FILE_SYNCHRONOUS_IO_NONALERT|flags, |
||||
nil, |
||||
0, |
||||
) |
||||
if status != 0 { |
||||
return nil, winapi.RtlNtStatusToDosError(status) |
||||
} |
||||
|
||||
fullPath, err := longpath.LongAbs(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, |
||||
winapi.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, |
||||
winapi.FILE_OPEN, |
||||
winapi.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: winapi.RtlNtStatusToDosError(winapi.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(winapi.FileLinkInformation{}.FileName)) + len(newbase16)*2 |
||||
linkinfoBuffer := winapi.LocalAlloc(0, size) |
||||
defer winapi.LocalFree(linkinfoBuffer) |
||||
|
||||
linkinfo := (*winapi.FileLinkInformation)(unsafe.Pointer(linkinfoBuffer)) |
||||
linkinfo.RootDirectory = parent.Fd() |
||||
linkinfo.FileNameLength = uint32(len(newbase16) * 2) |
||||
copy(winapi.Uint16BufferToSlice(&linkinfo.FileName[0], len(newbase16)), newbase16) |
||||
|
||||
var iosb winapi.IOStatusBlock |
||||
status := winapi.NtSetInformationFile( |
||||
oldf.Fd(), |
||||
&iosb, |
||||
linkinfoBuffer, |
||||
uint32(size), |
||||
winapi.FileLinkInformationClass, |
||||
) |
||||
if status != 0 { |
||||
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: winapi.RtlNtStatusToDosError(status)} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// deleteOnClose marks a file to be deleted when the handle is closed.
|
||||
func deleteOnClose(f *os.File) error { |
||||
disposition := winapi.FileDispositionInformationEx{Flags: winapi.FILE_DISPOSITION_DELETE} |
||||
var iosb winapi.IOStatusBlock |
||||
status := winapi.NtSetInformationFile( |
||||
f.Fd(), |
||||
&iosb, |
||||
uintptr(unsafe.Pointer(&disposition)), |
||||
uint32(unsafe.Sizeof(disposition)), |
||||
winapi.FileDispositionInformationExClass, |
||||
) |
||||
if status != 0 { |
||||
return winapi.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, |
||||
winapi.FILE_READ_ATTRIBUTES|winapi.FILE_WRITE_ATTRIBUTES|winapi.DELETE, |
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, |
||||
winapi.FILE_OPEN, |
||||
winapi.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, |
||||
winapi.FILE_CREATE, |
||||
winapi.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, |
||||
winapi.FILE_READ_ATTRIBUTES, |
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, |
||||
winapi.FILE_OPEN, |
||||
winapi.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, |
||||
winapi.FILE_OPEN, |
||||
0) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
f.Close() |
||||
return nil |
||||
} |
@ -0,0 +1,250 @@ |
||||
package schema1 |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"time" |
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid" |
||||
hcsschema "github.com/Microsoft/hcsshim/internal/schema2" |
||||
) |
||||
|
||||
// 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 |
||||
// LinuxMetadata - Support added in 1803/RS4+.
|
||||
LinuxMetadata bool `json:",omitempty"` |
||||
} |
||||
|
||||
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"` |
||||
} |
||||
|
||||
// AssignedDevice represents a device that has been directly assigned to a container
|
||||
//
|
||||
// NOTE: Support added in RS5
|
||||
type AssignedDevice struct { |
||||
// InterfaceClassGUID of the device to assign to container.
|
||||
InterfaceClassGUID string `json:"InterfaceClassGuid,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
|
||||
AssignedDevices []AssignedDevice `json:",omitempty"` // Array of devices to assign. NOTE: Support added in RS5
|
||||
} |
||||
|
||||
type ComputeSystemQuery struct { |
||||
IDs []string `json:"Ids,omitempty"` |
||||
Types []string `json:",omitempty"` |
||||
Names []string `json:",omitempty"` |
||||
Owners []string `json:",omitempty"` |
||||
} |
||||
|
||||
type PropertyType string |
||||
|
||||
const ( |
||||
PropertyTypeStatistics PropertyType = "Statistics" // V1 and V2
|
||||
PropertyTypeProcessList PropertyType = "ProcessList" // V1 and V2
|
||||
PropertyTypeMappedVirtualDisk PropertyType = "MappedVirtualDisk" // Not supported in V2 schema call
|
||||
PropertyTypeGuestConnection PropertyType = "GuestConnection" // V1 and V2. Nil return from HCS before RS5
|
||||
) |
||||
|
||||
type PropertyQuery struct { |
||||
PropertyTypes []PropertyType `json:",omitempty"` |
||||
} |
||||
|
||||
// ContainerProperties holds the properties for a container and the processes running in that container
|
||||
type ContainerProperties struct { |
||||
ID string `json:"Id"` |
||||
State string |
||||
Name string |
||||
SystemType string |
||||
RuntimeOSType string `json:"RuntimeOsType,omitempty"` |
||||
Owner string |
||||
SiloGUID string `json:"SiloGuid,omitempty"` |
||||
RuntimeID guid.GUID `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"` |
||||
GuestConnectionInfo GuestConnectionInfo `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"` |
||||
} |
||||
|
||||
// GuestDefinedCapabilities is part of the GuestConnectionInfo returned by a GuestConnection call on a utility VM
|
||||
type GuestDefinedCapabilities struct { |
||||
NamespaceAddRequestSupported bool `json:",omitempty"` |
||||
SignalProcessSupported bool `json:",omitempty"` |
||||
DumpStacksSupported bool `json:",omitempty"` |
||||
DeleteContainerStateSupported bool `json:",omitempty"` |
||||
UpdateContainerSupported bool `json:",omitempty"` |
||||
} |
||||
|
||||
// GuestConnectionInfo is the structure of an iterm return by a GuestConnection call on a utility VM
|
||||
type GuestConnectionInfo struct { |
||||
SupportedSchemaVersions []hcsschema.Version `json:",omitempty"` |
||||
ProtocolVersion uint32 `json:",omitempty"` |
||||
GuestDefinedCapabilities GuestDefinedCapabilities `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"` |
||||
} |
@ -0,0 +1,30 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type Attachment struct { |
||||
Type_ string `json:"Type,omitempty"` |
||||
|
||||
Path string `json:"Path,omitempty"` |
||||
|
||||
IgnoreFlushes bool `json:"IgnoreFlushes,omitempty"` |
||||
|
||||
CachingMode string `json:"CachingMode,omitempty"` |
||||
|
||||
NoWriteHardening bool `json:"NoWriteHardening,omitempty"` |
||||
|
||||
DisableExpansionOptimization bool `json:"DisableExpansionOptimization,omitempty"` |
||||
|
||||
IgnoreRelativeLocator bool `json:"IgnoreRelativeLocator,omitempty"` |
||||
|
||||
CaptureIoAttributionContext bool `json:"CaptureIoAttributionContext,omitempty"` |
||||
|
||||
ReadOnly bool `json:"ReadOnly,omitempty"` |
||||
} |
@ -0,0 +1,13 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type Battery struct { |
||||
} |
@ -0,0 +1,18 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type CacheQueryStatsResponse struct { |
||||
L3OccupancyBytes int32 `json:"L3OccupancyBytes,omitempty"` |
||||
|
||||
L3TotalBwBytes int32 `json:"L3TotalBwBytes,omitempty"` |
||||
|
||||
L3LocalBwBytes int32 `json:"L3LocalBwBytes,omitempty"` |
||||
} |
@ -0,0 +1,27 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type Chipset struct { |
||||
Uefi *Uefi `json:"Uefi,omitempty"` |
||||
|
||||
IsNumLockDisabled bool `json:"IsNumLockDisabled,omitempty"` |
||||
|
||||
BaseBoardSerialNumber string `json:"BaseBoardSerialNumber,omitempty"` |
||||
|
||||
ChassisSerialNumber string `json:"ChassisSerialNumber,omitempty"` |
||||
|
||||
ChassisAssetTag string `json:"ChassisAssetTag,omitempty"` |
||||
|
||||
UseUtc bool `json:"UseUtc,omitempty"` |
||||
|
||||
// LinuxKernelDirect - Added in v2.2 Builds >=181117
|
||||
LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"` |
||||
} |
@ -0,0 +1,14 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type CloseHandle struct { |
||||
Handle string `json:"Handle,omitempty"` |
||||
} |
@ -0,0 +1,17 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
// ComPort specifies the named pipe that will be used for the port, with empty string indicating a disconnected port.
|
||||
type ComPort struct { |
||||
NamedPipe string `json:"NamedPipe,omitempty"` |
||||
|
||||
OptimizeForDebugger bool `json:"OptimizeForDebugger,omitempty"` |
||||
} |
@ -0,0 +1,26 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type ComputeSystem struct { |
||||
Owner string `json:"Owner,omitempty"` |
||||
|
||||
SchemaVersion *Version `json:"SchemaVersion,omitempty"` |
||||
|
||||
HostingSystemId string `json:"HostingSystemId,omitempty"` |
||||
|
||||
HostedSystem interface{} `json:"HostedSystem,omitempty"` |
||||
|
||||
Container *Container `json:"Container,omitempty"` |
||||
|
||||
VirtualMachine *VirtualMachine `json:"VirtualMachine,omitempty"` |
||||
|
||||
ShouldTerminateOnLastHandleClosed bool `json:"ShouldTerminateOnLastHandleClosed,omitempty"` |
||||
} |
@ -0,0 +1,72 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
import ( |
||||
"net/http" |
||||
) |
||||
|
||||
// contextKeys are used to identify the type of value in the context.
|
||||
// Since these are string, it is possible to get a short description of the
|
||||
// context key for logging and debugging using key.String().
|
||||
|
||||
type contextKey string |
||||
|
||||
func (c contextKey) String() string { |
||||
return "auth " + string(c) |
||||
} |
||||
|
||||
var ( |
||||
// ContextOAuth2 takes a oauth2.TokenSource as authentication for the request.
|
||||
ContextOAuth2 = contextKey("token") |
||||
|
||||
// ContextBasicAuth takes BasicAuth as authentication for the request.
|
||||
ContextBasicAuth = contextKey("basic") |
||||
|
||||
// ContextAccessToken takes a string oauth2 access token as authentication for the request.
|
||||
ContextAccessToken = contextKey("accesstoken") |
||||
|
||||
// ContextAPIKey takes an APIKey as authentication for the request
|
||||
ContextAPIKey = contextKey("apikey") |
||||
) |
||||
|
||||
// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth
|
||||
type BasicAuth struct { |
||||
UserName string `json:"userName,omitempty"` |
||||
Password string `json:"password,omitempty"` |
||||
} |
||||
|
||||
// APIKey provides API key based authentication to a request passed via context using ContextAPIKey
|
||||
type APIKey struct { |
||||
Key string |
||||
Prefix string |
||||
} |
||||
|
||||
type Configuration struct { |
||||
BasePath string `json:"basePath,omitempty"` |
||||
Host string `json:"host,omitempty"` |
||||
Scheme string `json:"scheme,omitempty"` |
||||
DefaultHeader map[string]string `json:"defaultHeader,omitempty"` |
||||
UserAgent string `json:"userAgent,omitempty"` |
||||
HTTPClient *http.Client |
||||
} |
||||
|
||||
func NewConfiguration() *Configuration { |
||||
cfg := &Configuration{ |
||||
BasePath: "https://localhost", |
||||
DefaultHeader: make(map[string]string), |
||||
UserAgent: "Swagger-Codegen/2.1.0/go", |
||||
} |
||||
return cfg |
||||
} |
||||
|
||||
func (c *Configuration) AddDefaultHeader(key string, value string) { |
||||
c.DefaultHeader[key] = value |
||||
} |
@ -0,0 +1,16 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type ConsoleSize struct { |
||||
Height int32 `json:"Height,omitempty"` |
||||
|
||||
Width int32 `json:"Width,omitempty"` |
||||
} |
@ -0,0 +1,34 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.1 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type Container struct { |
||||
GuestOs *GuestOs `json:"GuestOs,omitempty"` |
||||
|
||||
Storage *Storage `json:"Storage,omitempty"` |
||||
|
||||
MappedDirectories []MappedDirectory `json:"MappedDirectories,omitempty"` |
||||
|
||||
MappedPipes []MappedPipe `json:"MappedPipes,omitempty"` |
||||
|
||||
Memory *Memory `json:"Memory,omitempty"` |
||||
|
||||
Processor *Processor `json:"Processor,omitempty"` |
||||
|
||||
Networking *Networking `json:"Networking,omitempty"` |
||||
|
||||
HvSocket *HvSocket `json:"HvSocket,omitempty"` |
||||
|
||||
ContainerCredentialGuard *ContainerCredentialGuardState `json:"ContainerCredentialGuard,omitempty"` |
||||
|
||||
RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` |
||||
|
||||
AssignedDevices []Device `json:"AssignedDevices,omitempty"` |
||||
} |
@ -0,0 +1,16 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.4 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type ContainerCredentialGuardAddInstanceRequest struct { |
||||
Id string `json:"Id,omitempty"` |
||||
CredentialSpec string `json:"CredentialSpec,omitempty"` |
||||
Transport string `json:"Transport,omitempty"` |
||||
} |
@ -0,0 +1,15 @@ |
||||
/* |
||||
* HCS API |
||||
* |
||||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
|
||||
* |
||||
* API version: 2.4 |
||||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
*/ |
||||
|
||||
package hcsschema |
||||
|
||||
type ContainerCredentialGuardHvSocketServiceConfig struct { |
||||
ServiceId string `json:"ServiceId,omitempty"` |
||||
ServiceConfig *HvSocketServiceConfig `json:"ServiceConfig,omitempty"` |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue